Bug#1034127: unblock: rhino/1.7.14-2.1

Paul Gevers elbrus at debian.org
Sun Apr 9 21:28:26 BST 2023


Package: release.debian.org
Severity: normal
User: release.debian.org at packages.debian.org
Usertags: unblock
X-Debbugs-Cc: rhino at packages.debian.org
Control: affects -1 + src:rhino

Please unblock package rhino

[ Reason ]
rhino has an RC bug where it causes some reverse build depends to
fails since openjdk-17 (bug 1026639). The maintainer fixed the bug by
uploading a new version, which would have migrated in time if the
autopkgtest of dojo wouldn't have shown that dojo is tightly coupled
to rhino (noticed before but not properly fixed: bug 977027). The
maintainer of rhino claimed that a targeted fix would be very
difficult due to other issues with the package and its age (see bug
1026639) hence I decided we want the new rhino in bookworm. However,
to solve the issue with dojo, in the end I NMU'ed both packages to get
Breaks/versioned Depends right. I don't want to unblock my own
uploads, hence this request.

[ Impact ]
RC bug 1026639 goes unsolved and we ship a 6 year old JavaScript
engine, which given the use of JavaScript doesn't feel great from a
security point of view (although the security tracker doesn't have
anything on rhino).

[ Tests ]
rhino triggers the autopkgtests of several reverse Depends and they
all pass now.

[ Risks ]
This is a new upstream release. This is not a small change. And while
typing this unblock request, I'm getting uncomfortable and wonder if
we want this. But as it's all prepared, let's discuss and pull Markus
in to elaborate a bit too.


[ Checklist ]
  [ ] all changes are documented in the d/changelog
  [ ] I reviewed all changes and I approve them
  [x] attach debdiff against the package in testing

I have attached two files: a filtered debdiff and it's diffstat, filtered with:
-x 'rhino-1.7.14/testsrc/*' -x 'rhino-1.7.14/examples/*' -x 'rhino-1.7.14/docs/*' -x 'rhino-1.7.14/.*' -x 'rhino-1.7.14/benchmarks/*'

reportbug didn't even allow me to append the full debdiff.


unblock rhino/1.7.14-2.1
-------------- next part --------------
diff -Nru rhino-1.7.7.2/apiClasses.properties rhino-1.7.14/apiClasses.properties
--- rhino-1.7.7.2/apiClasses.properties	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/apiClasses.properties	1970-01-01 01:00:00.000000000 +0100
@@ -1,36 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-apiClasses=\
-	src/org/mozilla/javascript/Callable.java,\
-	src/org/mozilla/javascript/ClassCache.java,\
-	src/org/mozilla/javascript/ClassShutter.java,\
-	src/org/mozilla/javascript/CompilerEnvirons.java,\
-	src/org/mozilla/javascript/ContinuationPending.java,\
-	src/org/mozilla/javascript/Context.java,\
-	src/org/mozilla/javascript/ContextAction.java,\
-	src/org/mozilla/javascript/ContextFactory.java,\
-	src/org/mozilla/javascript/GeneratedClassLoader.java,\
-	src/org/mozilla/javascript/EcmaError.java,\
-	src/org/mozilla/javascript/ErrorReporter.java,\
-	src/org/mozilla/javascript/EvaluatorException.java,\
-	src/org/mozilla/javascript/Function.java,\
-	src/org/mozilla/javascript/FunctionObject.java,\
-	src/org/mozilla/javascript/GeneratedClassLoader.java,\
-	src/org/mozilla/javascript/ImporterTopLevel.java,\
-	src/org/mozilla/javascript/JavaScriptException.java,\
-	src/org/mozilla/javascript/RefCallable.java,\
-	src/org/mozilla/javascript/RhinoException.java,\
-	src/org/mozilla/javascript/Script.java,\
-	src/org/mozilla/javascript/Scriptable.java,\
-	src/org/mozilla/javascript/ScriptableObject.java,\
-	src/org/mozilla/javascript/SecurityController.java,\
-	src/org/mozilla/javascript/WrapFactory.java,\
-	src/org/mozilla/javascript/WrappedException.java,\
-	src/org/mozilla/javascript/Wrapper.java,\
-	src/org/mozilla/javascript/Synchronizer.java,\
-	src/org/mozilla/javascript/optimizer/ClassCompiler.java,\
-	src/org/mozilla/javascript/debug/DebuggableScript.java,\
-	src/org/mozilla/javascript/serialize/ScriptableInputStream.java,\
-	src/org/mozilla/javascript/serialize/ScriptableOutputStream.java
diff -Nru rhino-1.7.7.2/build-date rhino-1.7.14/build-date
--- rhino-1.7.7.2/build-date	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/build-date	1970-01-01 01:00:00.000000000 +0100
@@ -1 +0,0 @@
-This version was built on @datestamp at .
diff -Nru rhino-1.7.7.2/build.gradle rhino-1.7.14/build.gradle
--- rhino-1.7.7.2/build.gradle	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/build.gradle	2022-01-06 22:57:21.000000000 +0100
@@ -1,17 +1,25 @@
-apply plugin: 'java'
-apply plugin: 'idea'
-apply plugin: 'maven-publish'
-apply plugin: 'jacoco'
-apply plugin: 'distribution'
-apply plugin: 'checkstyle'
-
-sourceCompatibility = 1.6
-targetCompatibility = 1.6
-
-tasks.withType(JavaCompile) { options.encoding = "UTF-8" }
+plugins {
+    id 'java'
+    id 'idea'
+    id 'eclipse'
+    id 'maven-publish'
+    id 'signing'
+    id 'jacoco'
+    id 'distribution'
+    id 'checkstyle'
+    id 'com.diffplug.spotless' version "5.12.1"
+    id 'com.github.spotbugs' version "4.7.1"
+}
+
+tasks.withType(JavaCompile) {
+    sourceCompatibility = 1.8
+    targetCompatibility = 1.8
+    options.encoding = "UTF-8"
+    options.compilerArgs = [ "-Xlint:deprecation,unchecked" ]
+}
 
-task wrapper(type: Wrapper) {
-    gradleVersion = '2.3'
+compileTestJava {
+    options.compilerArgs = [ ]
 }
 
 repositories {
@@ -39,24 +47,37 @@
             srcDirs "testsrc"
         }
     }
-}
 
-dependencies {
-    testCompile "junit:junit:4.12"
-    testCompile "org.yaml:snakeyaml:1.15"
-    testCompile "net.trajano.caliper:caliper:1.2.1"
-}
-
-if (JavaVersion.current().isJava8Compatible()) {
-    allprojects {
-        tasks.withType(Javadoc) {
-            options.addStringOption('Xdoclint:none', '-quiet')
+    jmh {
+        java {
+            srcDirs "benchmarks"
         }
+        compileClasspath += sourceSets.test.runtimeClasspath
+        runtimeClasspath += sourceSets.test.runtimeClasspath
     }
 }
 
+dependencies {
+    testImplementation "junit:junit:4.13.2"
+    testImplementation "org.yaml:snakeyaml:1.28"
+    testImplementation "javax.xml.soap:javax.xml.soap-api:1.4.0"
+    jmhImplementation project
+    jmhImplementation 'org.openjdk.jmh:jmh-core:1.27'
+    jmhAnnotationProcessor 'org.openjdk.jmh:jmh-generator-annprocess:1.27'
+}
+
 test {
+    useJUnit()
     exclude "**/benchmarks/**"
+
+    jvmArgs += '-Xss1280k'
+
+    if (JavaVersion.current() > JavaVersion.VERSION_1_8) {
+        jvmArgs += ['--add-opens', 'java.desktop/javax.swing.table=ALL-UNNAMED']
+    }
+
+    jacoco.excludes = ['**/testsrc_tests_ecma_3_RegExp_perlstress*']
+
     systemProperty 'java.awt.headless', 'true'
     systemProperty 'mozilla.js.tests', 'testsrc/tests'
     systemProperty 'mozilla.js.tests.timeout', 60000
@@ -64,38 +85,60 @@
     systemProperty 'user.country', 'US'
     systemProperty 'user.timezone', 'America/Los_Angeles'
     systemProperty 'file.encoding', 'UTF-8'
-    maxHeapSize = "256m"
+    if (System.getProperty('quick') != null) {
+        systemProperty 'TEST_OPTLEVEL', -1
+    } else if (System.getProperty('optLevel')) {
+        systemProperty 'TEST_OPTLEVEL', System.getProperty('optLevel')
+    }
+    systemProperty 'test262properties', System.getProperty('test262properties')
+    if (System.getProperty('updateTest262properties') != null) {
+        systemProperty 'updateTest262properties', System.getProperty('updateTest262properties')
+    }
+    maxHeapSize = "1g"
     testLogging.showStandardStreams = true
+    // Many tests do not clean up contexts properly. This makes the tests much
+    // more resilient at the expense of performance.
+    forkEvery = 1
+    maxParallelForks = 64
 }
 
 task sunSpiderBenchmark(type: JavaExec) {
-    main "com.google.caliper.runner.CaliperMain"
-    systemProperty 'rhino.benchmark.report', "${buildDir.absolutePath}"
-    args "-Cresults.upload.class=org.mozilla.javascript.benchmarks.ResultPlotter", "-i", "runtime", "org.mozilla.javascript.benchmarks.CaliperSpiderBenchmark.Spider"
-    classpath sourceSets.test.runtimeClasspath
+    classpath = sourceSets.jmh.runtimeClasspath
+    mainClass = 'org.openjdk.jmh.Main'
+    args '-f', '1', '-bm', 'avgt', '-tu', 'us', 'SunSpider'
 }
 
-task testBenchmark(type: Test) {
-    jacoco {
-        enabled = false
-    }
-    include "**/benchmarks/V8Benchmark*"
-    systemProperty 'rhino.benchmark.report', "${buildDir.absolutePath}"
-    systemProperty 'file.encoding', 'UTF-8'
-    workingDir = file("testsrc/benchmarks")
-    maxHeapSize = "256m"
-    testLogging.showStandardStreams = true
-    forkEvery = 1
+task v8Benchmark(type: JavaExec) {
+    classpath = sourceSets.jmh.runtimeClasspath
+    mainClass = 'org.openjdk.jmh.Main'
+    args '-f', '1', '-i', '10', '-bm', 'avgt', '-tu', 'us', 'V8'
 }
+
+task testBenchmark() {}
 testBenchmark.dependsOn sunSpiderBenchmark
+testBenchmark.dependsOn v8Benchmark
 
-task microBenchmark(type: JavaExec) {
-    main "com.google.caliper.runner.CaliperMain"
-    args "-i", "runtime", "org.mozilla.javascript.benchmarks.CaliperObjectBenchmark.FieldAccess", "-DstringKeys=100,1000", "-DintKeys=0,10,1000"
-    classpath sourceSets.test.runtimeClasspath
+task microBenchmark(type: JavaExec, description: 'JMH micro benchmark') {
+    def benchmark = System.getProperty('benchmark')
+    if (benchmark == null) {
+        benchmark = "MathBenchmark"
+    }
+    classpath = sourceSets.jmh.runtimeClasspath
+    mainClass = 'org.openjdk.jmh.Main'
+    args '-f', '1', '-bm', 'avgt', '-tu', 'ns', '-r', '5', benchmark
 }
 
+task listBenchmarks(type: JavaExec, description: 'JMH benchmarks') {
+    classpath = sourceSets.jmh.runtimeClasspath
+    mainClass = 'org.openjdk.jmh.Main'
+    args '-lp'
+}
 
+task jmhHelp(type: JavaExec, description: 'JMH benchmarks') {
+    classpath = sourceSets.jmh.runtimeClasspath
+    mainClass = 'org.openjdk.jmh.Main'
+    args '-help'
+}
 
 idea {
     module {
@@ -107,8 +150,74 @@
     }
 }
 
+tasks.withType(AbstractArchiveTask).configureEach {
+    // Reproducible jar files
+    preserveFileTimestamps = false
+    reproducibleFileOrder = true
+}
+
+task runtimeJar(type: Jar) {
+    dependsOn compileJava
+    archiveBaseName = 'rhino-runtime'
+    from sourceSets.main.output
+    from ('LICENSE.txt') {
+        into 'META-INF'
+    }
+    from ('NOTICE.txt') {
+        into 'META-INF'
+    }
+    excludes = ["org/mozilla/javascript/tools", "org/mozilla/javascript/engine/**", "META-INF/services/**"]
+    manifest {
+        attributes(
+            "Manifest-Version": "1.0",
+            "Implementation-Version": project.version,
+            "Implementation-Title":  "Mozilla Rhino",
+            "Implementation-Vendor": "Mozilla Foundation",
+            "Implementation-URL": "http://www.mozilla.org/rhino",
+            "Bundle-ManifestVersion": "2",
+            "Bundle-SymbolicName": "org.mozilla.rhino-runtime",
+            "Bundle-Version": project.version.replaceAll("-.*", ""),
+            "Export-Package": "org.mozilla.javascript,org.mozilla.javascript.ast,org.mozilla.javascript.annotations"
+        )
+    }
+}
+
+task engineJar(type: Jar) {
+    dependsOn compileJava
+    archiveBaseName = 'rhino-engine'
+    from (sourceSets.main.output) {
+        include 'org/mozilla/javascript/engine/**'
+        include 'META-INF/services/**'
+    }
+    from ('LICENSE.txt') {
+        into 'META-INF'
+    }
+    manifest {
+        attributes(
+                "Manifest-Version": "1.0",
+                "Implementation-Version": project.version,
+                "Implementation-Title":  "Mozilla Rhino ScriptEngine",
+                "Implementation-Vendor": "Mozilla Foundation",
+                "Implementation-URL": "http://www.mozilla.org/rhino",
+                "Automatic-Module-Name": "org.mozilla.rhino.engine"
+        )
+    }
+}
+
 jar {
-    from "LICENSE.txt"
+    dependsOn compileJava
+    from ('LICENSE.txt') {
+        into 'META-INF'
+    }
+    from ('NOTICE.txt') {
+        into 'META-INF'
+    }
+    from ('NOTICE-tools.txt') {
+        into 'META-INF'
+    }
+    from sourceSets.main.output
+    excludes = ["org/mozilla/javascript/engine/**", "META-INF/services/**"]
+    // Class ImplementationVersion uses 'Implementation-Title'
     manifest {
         attributes(
             "Manifest-Version": "1.0",
@@ -117,25 +226,70 @@
             "Implementation-Title":  "Mozilla Rhino",
             "Implementation-Vendor": "Mozilla Foundation",
             "Implementation-URL": "http://www.mozilla.org/rhino",
-            "Built-Date": new Date().format("yyyy-MM-dd"),
-            "Built-Time": new Date().format("HH:mm:ss")
+            "Automatic-Module-Name": "org.mozilla.rhino",
+            "Bundle-ManifestVersion": "2",
+            "Bundle-SymbolicName": "org.mozilla.rhino",
+            "Bundle-Version": project.version.replaceAll("-.*", ""),
+            "Export-Package": "org.mozilla.javascript,org.mozilla.javascript.ast,org.mozilla.javascript.annotations"
         )
     }
 }
 
+javadoc {
+    options.addBooleanOption("-allow-script-in-comments", true)
+    options.addStringOption('Xdoclint:html', '-quiet')
+}
+
+task javadocJar(type: Jar) {
+    archiveClassifier = 'javadoc'
+    from javadoc
+}
+
+task runtimeJavadocJar(type: Jar) {
+    archiveClassifier = 'javadoc'
+    from javadoc
+    exclude 'org/mozilla/javascript/tools', 'org/mozilla/javascript/engine'
+}
+
+task engineJavadocJar(type: Jar) {
+    archiveClassifier = 'javadoc'
+    from javadoc
+    include 'org/mozilla/javascript/engine/**'
+}
+
 task sourceJar(type: Jar) {
     from sourceSets.main.allJava
+    archiveClassifier = 'sources'
+    from ('LICENSE.txt') {
+        into 'META-INF'
+    }
+    from ('NOTICE.txt') {
+        into 'META-INF'
+    }
+    from ('NOTICE-tools.txt') {
+        into 'META-INF'
+    }
 }
 
-task rhinoJavadoc(type: Javadoc) {
-    source = sourceSets.main.allJava
-    if (JavaVersion.current().isJava8Compatible()) {
-        options.addStringOption('Xdoclint:none', '-quiet')
+task runtimeSourceJar(type: Jar) {
+    archiveClassifier = 'sources'
+    from sourceSets.main.allJava
+    exclude 'org/mozilla/javascript/tools', 'org/mozilla/javascript/engine'
+    from ('LICENSE.txt') {
+        into 'META-INF'
+    }
+    from ('NOTICE.txt') {
+        into 'META-INF'
     }
 }
 
-task rhinoJavadocJar(type: Jar, dependsOn: rhinoJavadoc) {
-    from rhinoJavadoc.destinationDir
+task engineSourceJar(type: Jar) {
+    archiveClassifier = 'sources'
+    from sourceSets.main.allJava
+    include 'org/mozilla/javascript/engine/**'
+    from ('LICENSE.txt') {
+        into 'META-INF'
+    }
 }
 
 publishing {
@@ -143,6 +297,7 @@
         rhino(MavenPublication) {
             groupId 'org.mozilla'
             artifactId 'rhino'
+            artifacts = [jar, sourceJar, javadocJar]
 
             pom.withXml {
                 def root = asNode()
@@ -150,8 +305,9 @@
                 root.appendNode('description', """
     Rhino is an open-source implementation of JavaScript written entirely in Java.
     It is typically embedded into Java applications to provide scripting to end users.
+    Full jar including tools, excluding the JSR-223 Script Engine wrapper.
 """)
-                root.appendNode("url", "https://developer.mozilla.org/en/Rhino")
+                root.appendNode("url", "https://mozilla.github.io/rhino/")
 
                 def p = root.appendNode("parent")
                 p.appendNode("groupId", "org.sonatype.oss")
@@ -172,12 +328,77 @@
                 o.appendNode("url", "http://www.mozilla.org")
 
             }
-            from components.java
-            artifact sourceJar {
-                classifier "sources"
+        }
+
+        rhinoruntime(MavenPublication) {
+            groupId 'org.mozilla'
+            artifactId 'rhino-runtime'
+            artifacts = [runtimeJar, runtimeSourceJar, runtimeJavadocJar]
+
+            pom.withXml {
+                def root = asNode()
+
+                root.appendNode('description', """
+    Rhino JavaScript runtime jar, excludes tools & JSR-223 Script Engine wrapper.
+""")
+                root.appendNode("url", "https://mozilla.github.io/rhino/")
+
+                def p = root.appendNode("parent")
+                p.appendNode("groupId", "org.sonatype.oss")
+                p.appendNode("artifactId", "oss-parent")
+                p.appendNode("version", "7")
+
+                def l = root.appendNode("licenses").appendNode("license")
+                l.appendNode("name", "Mozilla Public License, Version 2.0")
+                l.appendNode("url", "http://www.mozilla.org/MPL/2.0/index.txt")
+
+                def scm = root.appendNode("scm")
+                scm.appendNode("connection", "scm:git:git at github.com:mozilla/rhino.git")
+                scm.appendNode("developerConnection", "scm:git:git at github.com:mozilla/rhino.git")
+                scm.appendNode("url", "git at github.com:mozilla/rhino.git")
+
+                def o = root.appendNode("organization")
+                o.appendNode("name", "The Mozilla Foundation")
+                o.appendNode("url", "http://www.mozilla.org")
             }
-            artifact rhinoJavadocJar {
-                classifier "javadoc"
+        }
+
+        rhinoengine(MavenPublication) {
+            groupId 'org.mozilla'
+            artifactId 'rhino-engine'
+            artifacts = [engineJar, engineSourceJar, engineJavadocJar]
+
+            pom.withXml {
+                def root = asNode()
+
+                root.appendNode('description', """
+    Rhino Javascript JSR-223 Script Engine wrapper.
+""")
+                root.appendNode("url", "https://mozilla.github.io/rhino/")
+
+                def p = root.appendNode("parent")
+                p.appendNode("groupId", "org.sonatype.oss")
+                p.appendNode("artifactId", "oss-parent")
+                p.appendNode("version", "7")
+
+                def l = root.appendNode("licenses").appendNode("license")
+                l.appendNode("name", "Mozilla Public License, Version 2.0")
+                l.appendNode("url", "http://www.mozilla.org/MPL/2.0/index.txt")
+
+                def scm = root.appendNode("scm")
+                scm.appendNode("connection", "scm:git:git at github.com:mozilla/rhino.git")
+                scm.appendNode("developerConnection", "scm:git:git at github.com:mozilla/rhino.git")
+                scm.appendNode("url", "git at github.com:mozilla/rhino.git")
+
+                def o = root.appendNode("organization")
+                o.appendNode("name", "The Mozilla Foundation")
+                o.appendNode("url", "http://www.mozilla.org")
+
+                def deps = root.appendNode("dependencies")
+                def rhino = deps.appendNode("dependency")
+                rhino.appendNode("groupId", "org.mozilla")
+                rhino.appendNode("artifactId", "rhino")
+                rhino.appendNode("version", getVersion())
             }
         }
     }
@@ -199,27 +420,63 @@
     }
 }
 
-jacoco {
-    toolVersion = "0.7.4.201502262128"
+signing {
+    if (project.hasProperty('SIGNINGKEY')) {
+        // Check for ORG_GRADLE_PROJECT_SIGNINGKEY environment variable for use in CI system.
+        // Otherwise, do not sign.
+        def signingKey = getProperty('SIGNINGKEY')
+        def signingPassword = getProperty('SIGNINGPASSWORD')
+        useInMemoryPgpKeys(signingKey, signingPassword)
+        sign publishing.publications.rhino
+        sign publishing.publications.rhinoengine
+        sign publishing.publications.rhinoruntime
+    }
+}
+
+spotbugs {
+    effort = "less"
+    reportLevel = "medium"
+    excludeFilter = file("./spotbugs-exclude.xml")
+}
+
+spotless {
+    ratchetFrom 'code-formatting-required'
+    java {
+        googleJavaFormat().aosp()
+    }
+}
+
+if (JavaVersion.current() > JavaVersion.VERSION_15
+        && (!System.properties.containsKey('org.gradle.jvmargs')
+        || !System.properties.get('org.gradle.jvmargs').contains('com.sun.tools.javac.api'))
+        && (!project.hasProperty('org.gradle.jvmargs')
+        || !project.property('org.gradle.jvmargs').contains('com.sun.tools.javac.api'))) {
+    tasks.named('check') {
+        doFirst {
+            logger.warn('WARNING: spotless plugin removed from check due to bug, ' + \
+                'see README for a workaround when building with Java 16+.')
+        }
+    }
+    spotless.enforceCheck false
 }
 
-jacocoTestReport.dependsOn test
 jacocoTestReport {
+    dependsOn test
     reports {
-        html.destination "${buildDir}/jacocoHtml"
+        csv.required = true
+        html.required = true
     }
 }
 
 checkstyle {
     configFile = file("${projectDir}/checkstyle.xml")
-    sourceSets = [project.sourceSets.main]
+    sourceSets = [ project.sourceSets.main ]
 }
 
 distributions {
     main {
         contents {
             from(sourceSets.main.java) {
-                exclude 'man'
                 into 'rhino' + project.version + '/src'
             }
             from(sourceSets.main.resources) {
@@ -232,13 +489,15 @@
             from(jar.outputs.files) {
                 into 'rhino' + project.version + '/lib'
             }
-            from(sourceSets.main.allSource) {
-                include 'man/*.1'
-                into 'rhino' + project.version
+            from(runtimeJar.outputs.files) {
+                into 'rhino' + project.version + '/lib'
+            }
+            from(engineJar.outputs.files) {
+                into 'rhino' + project.version + '/lib'
             }
             from(file(".")) {
-                include '*.txt', '*.md', 'build.gradle', 'build.properties', 'gradle.properties',
-                        'gradle/**', 'gradlew'
+                include '*.txt', '*.md', 'build.gradle', 'gradle.properties',
+                        'gradle/**', 'gradlew', 'man/*.1'
                 into 'rhino' + project.version 
             }
             into "/"
@@ -246,4 +505,10 @@
     }
 }
 
-distZip.dependsOn javadoc, jar
+distTar {
+    dependsOn javadoc, jar
+    compression = Compression.GZIP
+    archiveExtension = 'tar.gz'
+}
+
+distZip.dependsOn javadoc, jar, sourceJar, runtimeSourceJar
diff -Nru rhino-1.7.7.2/build.properties rhino-1.7.14/build.properties
--- rhino-1.7.7.2/build.properties	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/build.properties	1970-01-01 01:00:00.000000000 +0100
@@ -1,26 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-name: rhino
-Name: Rhino
-version: 1.7.7.2
-# See Context#getImplementationVersion() for format of this!
-implementation.version: Rhino 1.7.7.2 ${implementation.date}
-
-build.dir: build
-rhino.jar: js.jar
-small-rhino.jar: smalljs.jar
-dist.name: rhino${version}
-dist.dir: ${build.dir}/${dist.name}
-
-# compilation destionation
-classes: ${build.dir}/classes
-
-# compilation settings
-debug: on
-target-jvm: 1.6
-source-level: 1.6
-
-# jar generation settings
-jar-compression: true
diff -Nru rhino-1.7.7.2/build.xml rhino-1.7.14/build.xml
--- rhino-1.7.7.2/build.xml	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/build.xml	1970-01-01 01:00:00.000000000 +0100
@@ -1,335 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-
-<!--
-    Build file for Rhino using Ant (see http://jakarta.apache.org/ant/index.html)
-    Requires Ant version 1.2 or later
-
-    Compilation currently requires JDK 1.5 or later. Can cross-compile to
-    support JDK 1.4.
--->
-
-<project name="Rhino" default="help" basedir=".">
-
-  <target name="properties">
-    <!-- Allow user to override default settings from build.properties -->
-    <property file="build.local.properties" />
-    <tstamp>
-      <!-- Specify date part of Context#getImplementationVersion() -->
-      <format property="implementation.date" pattern="yyyy MM dd"/>
-    </tstamp>
-    <property file="build.properties"/>
-
-    <property name="dist.file" value="rhino${version}.zip"/>
-    <property name="dist.source-only-zip" value="rhino${version}-sources.zip"/>
-
-    <property file="apiClasses.properties"/>
-    <property name="xmlimplsrc-build-file"
-              location="xmlimplsrc/build.xml"/>
-
-    <available property="xmlimplsrc-present?"
-               file="${xmlimplsrc-build-file}" />
-
-  </target>
-
-  <target name="init" depends="properties">
-    <mkdir dir="${build.dir}"/>
-    <mkdir dir="${classes}"/>
-    <mkdir dir="${dist.dir}"/>
-  </target>
-
-  <target name="compile" depends="init">
-    <ant antfile="src/build.xml" target="compile"/>
-    <ant antfile="toolsrc/build.xml" target="compile"/>
-    <antcall target="xmlimplsrc-compile" />
-  </target>
-
-  <target name="compile-all" depends="compile"/>
-
-  <target name="graph" depends="init">
-    <ant antfile="src/build.xml" target="graph"/>
-  </target> 
-
-  <target name="shell" depends="compile">
-    <ant antfile="src/build.xml" target="shell"/>
-  </target> 
-
-  <target name="copy-source" depends="init">
-    <ant antfile="src/build.xml" target="copy-source"/>
-    <ant antfile="toolsrc/build.xml" target="copy-source"/>
-    <ant antfile="testsrc/build.xml" target="copy-source"/>
-    <antcall target="xmlimplsrc-copy-source" />
-    <copy todir="${dist.dir}" file="build.xml"/>
-    <copy todir="${dist.dir}" file="build.properties"/>
-    <copy todir="${dist.dir}" file="apiClasses.properties"/>
-    <copy todir="${dist.dir}" file="LICENSE.txt"/>
-  </target>
-
-  <target name="xmlimplsrc-compile" if="xmlimplsrc-present?">
-    <echo>Calling ${xmlimplsrc-build-file}</echo>
-    <!-- Ignore compilation errors under JDK less then 1.4 -->
-    <property name="xmlimpl.compile.failonerror" value="no"/>
-    <ant antfile="${xmlimplsrc-build-file}" target="compile"/>
-  </target>
-
-  <target name="xmlimplsrc-copy-source" if="xmlimplsrc-present?">
-    <echo>Calling ${xmlimplsrc-build-file}</echo>
-    <ant antfile="${xmlimplsrc-build-file}" target="copy-source"/>
-  </target>
-
-  <target name="jar" depends="compile-all">
-    <copy todir="${classes}" file="LICENSE.txt"/>
-    <property name="jarfile" location="${dist.dir}/${rhino.jar}"/>
-    <jar jarfile="${jarfile}"
-         basedir="${classes}"
-         manifest="src/manifest"
-         compress="${jar-compression}"
-     />
-  </target>
-
-  <target name="console" depends="jar">
-    <property name="jarfile" location="${dist.dir}/${rhino.jar}"/>
-    <java jar="${jarfile}"
-          fork="true">
-      <arg line="-version 170"/>
-    </java>
-  </target>
-
-  <target name="smalljar" depends="compile">
-    <property name="smalljarfile" location="${dist.dir}/${small-rhino.jar}"/>
-    <jar basedir="${classes}" destfile="${smalljarfile}"
-         compress="${jar-compression}">
-      <include name="org/mozilla/javascript/*.class"/>
-
-      <include name="org/mozilla/javascript/debug/*.class"/>
-      <include name="org/mozilla/javascript/resources/*.properties"/>
-      <include name="org/mozilla/javascript/xml/*.class"/>
-      <include name="org/mozilla/javascript/continuations/*.class"/>
-      <include name="org/mozilla/javascript/jdk13/*.class"/>
-      <include name="org/mozilla/javascript/ast/*.class"/>
-      <include name="org/mozilla/javascript/json/*.class"/>
-      <include name="org/mozilla/javascript/annotations/*.class"/>
-      <include name="org/mozilla/javascript/v8dtoa/*.class"/>
-
-      <!-- exclude classes that uses class generation library -->
-      <exclude name="org/mozilla/javascript/JavaAdapter*.class"/>
-
-      <include name="org/mozilla/javascript/regexp/*.class"
-               unless="no-regexp"/>
-    </jar>
-  </target>
-
-  <target name="copy-examples" depends="init">
-    <mkdir dir="${dist.dir}/examples"/>
-    <copy todir="${dist.dir}/examples">
-      <fileset dir="examples" includes="**/*.java,**/*.js,**/*.html" />
-    </copy>
-  </target>
-
-  <target name="copy-misc" depends="init">
-    <filter token="datestamp" value="${TODAY}"/>
-    <copy todir="${dist.dir}" filtering="yes">
-      <fileset dir=".">
-        <patternset>
-          <include name="build-date"/>
-        </patternset>
-      </fileset>
-    </copy>
-  </target>
-
-  <target name="copy-all" depends="copy-source,copy-examples,copy-misc">
-  </target>
-
-  <target name="javadoc" depends="init">
-    <mkdir dir="${dist.dir}/javadoc"/>
-    <javadoc sourcefiles="${apiClasses}"
-             sourcepath="src"
-             destdir="${dist.dir}/javadoc"
-             version="true"
-             author="true"
-             windowtitle="${Name}" />
-    <zip destfile="${dist.dir}/javadoc.zip" basedir="${dist.dir}/javadoc"/>
-  </target>
-
-  <target name="dev-javadoc" depends="init">
-    <mkdir dir="${dist.dir}/javadoc"/>
-    <javadoc sourcepath="src"
-             destdir="${dist.dir}/javadoc"
-             version="true"
-             package="true"
-             author="true"
-             windowtitle="${Name}">
-       <fileset
-         dir="."
-         includes="**/*.java"
-         excludes="**/testsrc/**/*.java"
-       />
-    </javadoc>
-  </target>
-
-  <!--
-    Compiles and tests all sources and then creates the distribution file
-  -->
-  <target name="all" depends="deepclean,compile-all,junit-all,dist">
-  </target>
-
-  <target name="dist" depends="deepclean,jar,copy-all,javadoc">
-    <delete file="${dist.file}" />
-    <zip destfile="${dist.file}">
-      <fileset dir="${build.dir}" includes="${dist.name}/**"/>
-    </zip>
-  </target>
-
-  <target name="source-zip" depends="copy-source,copy-examples,javadoc">
-    <delete file="${dist.source-only-zip}" />
-    <zip destfile="${dist.source-only-zip}">
-      <zipfileset prefix="${dist.name}" dir="${dist.dir}">
-        <include name="*src/**"/>
-        <include name="build.xml"/>
-        <include name="*.properties"/>
-        <include name="examples/**"/>
-      </zipfileset>
-    </zip>
-  </target>
-
-  <target name="compile-debugger">
-    <ant antfile="toolsrc/build.xml" target="compile-debugger"/>
-  </target>
-
-  <target name="clean" depends="properties">
-    <delete quiet="true" file="${dist.dir}/${rhino.jar}"/>
-    <delete quiet="true" file="${dist.dir}/${small-rhino.jar}"/>
-    <delete quiet="true" dir="${build.dir}"/>
-  </target>
-
-  <target name="deepclean" depends="properties">
-    <delete quiet="true" dir="${build.dir}"/>
-    <delete quiet="true" file="${dist.file}"/>
-    <delete quiet="true" file="${dist.source-only-zip}"/>
-  </target>
-
-  <!--
-    The next two targets run the JavaScript Test Library tests.  Note that these tests are quite extensive and take a long time
-    to run.  They are not documented in the 'help' target for now.
-  -->
-
-  <!--
-    Run the tests using JUnit.  Beware that if you are using Ant from the command-line, there are some difficulties you may
-    encounter setting this up correctly; see http://ant.apache.org/faq.html#delegating-classloader
-
-    IDEs that use Ant as the build system probably handle this fine.
-  -->
-  <target name="junit-all" depends="compile">
-    <ant antfile="testsrc/build.xml" target="junit-coveragereport"/>
-  </target>
-  <target name="junit" depends="compile">
-    <ant antfile="testsrc/build.xml" target="junit"/>
-  </target>
-
-   <target name="junit-benchmarks" depends="compile">
-    <ant antfile="testsrc/build.xml" target="junit-benchmarks"/>
-  </target>
-
-  <!--
-  Run the tests using the Java port of jsdriver.pl.  Note that running this port
-  from the command-line may be more useful running this Ant target, as running
-  from the command line allows configuration options, such as running with a
-  non-default optimization level, or running only a subset of the tests.
-  -->
-  <target name="jsdriver-run" depends="compile">
-      <ant antfile="testsrc/build.xml" target="jsdriver" />
-  </target>
-
-  <!--
-    Compile the JsDriver test driver.
-  -->
-  <target name="jsdriver" depends="compile">
-      <ant antfile="testsrc/build.xml" target="clean" />
-      <ant antfile="testsrc/build.xml" target="compile" />
-  </target>
-
-  <target name="benchmark-v8-opt-1" depends="jar">
-    <property name="jarfile" location="${dist.dir}/${rhino.jar}"/>
-    <java jar="${jarfile}" dir="testsrc/benchmarks/v8-benchmarks-v5" fork="true">
-      <jvmarg value="-server"/>
-      <arg line="-opt -1 run.js"/>
-    </java>
-  </target>
-
-  <target name="benchmark-v8-opt0" depends="jar">
-    <property name="jarfile" location="${dist.dir}/${rhino.jar}"/>
-    <java jar="${jarfile}" dir="testsrc/benchmarks/v8-benchmarks-v5" fork="true">
-      <jvmarg value="-server"/>
-      <arg line="-opt 0 run.js"/>
-    </java>
-  </target>
-
-  <target name="benchmark-v8-opt9" depends="jar">
-    <property name="jarfile" location="${dist.dir}/${rhino.jar}"/>
-    <java jar="${jarfile}" dir="testsrc/benchmarks/v8-benchmarks-v5" fork="true">
-      <jvmarg value="-server"/>
-      <arg line="-opt 9 run.js"/>
-    </java>
-  </target>
-
-  <target name="benchmark-sunspider-opt-1" depends="jar">
-    <property name="jarfile" location="${dist.dir}/${rhino.jar}"/>
-    <java jar="${jarfile}" dir="testsrc/benchmarks/sunspider-0.9.1" fork="true">
-      <jvmarg value="-server"/>
-      <arg line="-opt -1 run.js"/>
-    </java>
-  </target>
-
-  <target name="benchmark-sunspider-opt0" depends="jar">
-    <property name="jarfile" location="${dist.dir}/${rhino.jar}"/>
-    <java jar="${jarfile}" dir="testsrc/benchmarks/sunspider-0.9.1" fork="true">
-      <jvmarg value="-server"/>
-      <arg line="-opt 0 run.js"/>
-    </java>
-  </target>
-
-  <target name="benchmark-sunspider-opt9" depends="jar">
-    <property name="jarfile" location="${dist.dir}/${rhino.jar}"/>
-    <java jar="${jarfile}" dir="testsrc/benchmarks/sunspider-0.9.1" fork="true">
-      <jvmarg value="-server"/>
-      <arg line="-opt 9 run.js"/>
-    </java>
-  </target>
-
-  <target name="help" depends="properties">
-<echo>The following targets are available with this build file:
-
- clean       remove all compiled classes and copied property files
-
- compile     compile classes and copy all property files
-             into ${classes} directory
-             excluding deprecated code
-
- compile-all compile all classes and copy all property files
-             into ${classes} directory
-             including deprecated code
-
- deepclean   remove all generated files and directories
-
- dist        create ${dist.file} with full Rhino distribution
-
- help        print this help
-
- jar         create ${rhino.jar} in ${dist.dir}
-
- smalljar    create ${small-rhino.jar} in ${dist.dir} with
-             minimalist set of Rhino classes. See footprint.html
-             from the doc directory for details.
-
- javadoc     generate Rhino API documentation
-             in ${dist.dir}/javadoc
-
- source-zip  create ${dist.source-only-zip} with all Rhino
-             source files necessary to recreate ${dist.file}
-</echo>
-  </target>
-
-</project>
diff -Nru rhino-1.7.7.2/checkstyle.xml rhino-1.7.14/checkstyle.xml
--- rhino-1.7.7.2/checkstyle.xml	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/checkstyle.xml	2022-01-06 22:57:21.000000000 +0100
@@ -106,8 +106,7 @@
 
         <!-- Checks for imports                              -->
         <!-- See http://checkstyle.sf.net/config_import.html -->
-        <!-- Unfortunately there's a lot of code to change with star imports... -->
-        <!-- <module name="AvoidStarImport"/> -->
+        <module name="AvoidStarImport"/>
         <module name="IllegalImport"/>
         <!-- defaults to sun.* packages -->
         <module name="RedundantImport"/>
diff -Nru rhino-1.7.7.2/CODE_OF_CONDUCT.md rhino-1.7.14/CODE_OF_CONDUCT.md
--- rhino-1.7.7.2/CODE_OF_CONDUCT.md	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/CODE_OF_CONDUCT.md	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,15 @@
+# Community Participation Guidelines
+
+This repository is governed by Mozilla's code of conduct and etiquette guidelines. 
+For more details, please read the
+[Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/). 
+
+## How to Report
+For more information on how to report violations of the Community Participation Guidelines, please read our '[How to Report](https://www.mozilla.org/about/governance/policies/participation/reporting/)' page.
+
+<!--
+## Project Specific Etiquette
+
+In some cases, there will be additional project etiquette i.e.: (https://bugzilla.mozilla.org/page.cgi?id=etiquette.html).
+Please update for your project.
+-->
diff -Nru rhino-1.7.7.2/debian/changelog rhino-1.7.14/debian/changelog
--- rhino-1.7.7.2/debian/changelog	2021-02-08 11:39:29.000000000 +0100
+++ rhino-1.7.14/debian/changelog	2023-04-06 13:10:20.000000000 +0200
@@ -1,3 +1,41 @@
+rhino (1.7.14-2.1) unstable; urgency=medium
+
+  * Non-maintainer upload
+  * Add versioned Breaks on shrinksafe (Closes: #977027)
+
+ -- Paul Gevers <elbrus at debian.org>  Thu, 06 Apr 2023 13:10:20 +0200
+
+rhino (1.7.14-2) unstable; urgency=medium
+
+  * Team upload.
+  * Update librhino-java.manifest so that it matches the upstream one.
+  * Install the missing bundle resources. This caused a runtime error when
+    running the rhino script. (Closes: #1031432)
+
+ -- Markus Koschany <apo at debian.org>  Sat, 18 Feb 2023 00:46:00 +0100
+
+rhino (1.7.14-1) unstable; urgency=medium
+
+  * Team upload.
+  * New upstream version 1.7.14.
+    - Fix FTBFS with OpenJDK 17. (Closes: #1026639)
+  * Declare compliance with Debian Policy 4.6.2.
+  * d/control: Change homepage field to github repository because
+    the old URL can no longer be found. (Closes: #1029166)
+  * d/rules: Change source level to 8 and target Java 8.
+  * Drop 07_fix-context-implementation-version.patch
+  * Drop 08_fix-jar-version-number.patch
+  * Drop script-engine.patch.
+  * Refresh preserve-backward-compatibility.patch and
+    public_getSourcePositionFromStack.patch because reverse-dependencies in
+    Debian still require them.
+  * Drop librhino-java-doc binary package.
+  * Use javahelper instead of ant to build the jar file.
+  * Add debian/pom.xml.
+  * Install upstream's rhino.1 manpage.
+
+ -- Markus Koschany <apo at debian.org>  Mon, 13 Feb 2023 14:44:17 +0100
+
 rhino (1.7.7.2-3) unstable; urgency=medium
 
   * Team upload.
diff -Nru rhino-1.7.7.2/debian/clean rhino-1.7.14/debian/clean
--- rhino-1.7.7.2/debian/clean	2020-09-03 13:36:47.000000000 +0200
+++ rhino-1.7.14/debian/clean	1970-01-01 01:00:00.000000000 +0100
@@ -1 +0,0 @@
-build/
diff -Nru rhino-1.7.7.2/debian/control rhino-1.7.14/debian/control
--- rhino-1.7.7.2/debian/control	2021-02-08 11:39:26.000000000 +0100
+++ rhino-1.7.14/debian/control	2023-04-06 13:10:20.000000000 +0200
@@ -6,24 +6,22 @@
  Marcus Better <marcus at better.se>,
  Jakub Adam <jakub.adam at ktknet.cz>
 Build-Depends:
- ant,
  debhelper-compat (= 13),
  default-jdk,
  javahelper,
  maven-repo-helper
-Standards-Version: 4.5.1
+Standards-Version: 4.6.2
 Vcs-Git: https://salsa.debian.org/java-team/rhino.git
 Vcs-Browser: https://salsa.debian.org/java-team/rhino
-Homepage: http://www.mozilla.org/rhino/
+Homepage: https://github.com/mozilla/rhino
 
 Package: rhino
 Architecture: all
 Depends:
- default-jre-headless (>= 1:1.6) | java6-runtime-headless,
+ default-jre-headless | java9-runtime-headless,
  libjline-java,
  librhino-java (= ${source:Version}),
  ${misc:Depends}
-Suggests: librhino-java-doc
 Description: JavaScript engine written in Java
  Rhino is an implementation of the JavaScript language written
  entirely in Java. It is typically embedded into Java applications to
@@ -32,9 +30,8 @@
 Package: librhino-java
 Section: java
 Architecture: all
-Replaces: rhino (<< 1.7R3~pre-2)
-Conflicts: rhino (<< 1.7R3~pre-2)
 Depends: ${misc:Depends}
+Breaks: shrinksafe (<< 1.17.2+dfsg1-2.1~)
 Suggests: rhino
 Description: Libraries for rhino Java Script Engine
  Rhino is an implementation of the JavaScript language written
@@ -44,17 +41,3 @@
  This package contains only Java library and no interpreter. For
  Rhino interpreter, you install rhino package.
 
-Package: librhino-java-doc
-Section: doc
-Architecture: all
-Replaces: rhino-doc (<< 1.7R3~pre-2)
-Conflicts: rhino-doc (<< 1.7R3~pre-2)
-Depends: ${misc:Depends}
-Suggests: rhino
-Description: Documentation for rhino Java Script Engine
- Rhino is an implementation of the JavaScript language written
- entirely in Java. It is typically embedded into Java applications to
- provide scripting to end users.
- .
- This is the documentation, including API JavaDoc, for the Rhino
- JavaScript engine.
diff -Nru rhino-1.7.7.2/debian/copyright rhino-1.7.14/debian/copyright
--- rhino-1.7.7.2/debian/copyright	2020-09-03 13:36:47.000000000 +0200
+++ rhino-1.7.14/debian/copyright	2023-04-06 13:10:20.000000000 +0200
@@ -5,7 +5,7 @@
                 gradle/wrapper
 
 Files: *
-Copyright: 1999-2015, Mozilla
+Copyright: 1999-2023, Mozilla
 License: MPL-2.0
 
 Files: src/org/mozilla/javascript/v8dtoa/*
@@ -78,6 +78,7 @@
            2011, Jakub Adam <jakub.adam at ktknet.cz>
            2014, Sylvestre Ledru <sylvestre at debian.org>
            2017, Emmanuel Bourg <ebourg at apache.org>
+           2023, Markus Koschany <apo at debian.org>
 License: MPL-2.0
 
 License: MPL-2.0
diff -Nru rhino-1.7.7.2/debian/librhino-java-doc.doc-base rhino-1.7.14/debian/librhino-java-doc.doc-base
--- rhino-1.7.7.2/debian/librhino-java-doc.doc-base	2020-09-03 13:36:47.000000000 +0200
+++ rhino-1.7.14/debian/librhino-java-doc.doc-base	1970-01-01 01:00:00.000000000 +0100
@@ -1,10 +0,0 @@
-Document: rhino-doc1
-Title: API JavaDoc for Rhino
-Author: The Mozilla Rhino Developers
-Abstract: This is the API JavaDoc of Rhino, a JavaScript 
- engine written in Java.
-Section: Programming
-
-Format: HTML
-Index: /usr/share/doc/rhino/api/index.html
-Files: /usr/share/doc/rhino/api/*.html
diff -Nru rhino-1.7.7.2/debian/librhino-java-doc.examples rhino-1.7.14/debian/librhino-java-doc.examples
--- rhino-1.7.7.2/debian/librhino-java-doc.examples	2020-09-03 13:36:47.000000000 +0200
+++ rhino-1.7.14/debian/librhino-java-doc.examples	1970-01-01 01:00:00.000000000 +0100
@@ -1 +0,0 @@
-examples/*
diff -Nru rhino-1.7.7.2/debian/librhino-java.manifest rhino-1.7.14/debian/librhino-java.manifest
--- rhino-1.7.7.2/debian/librhino-java.manifest	2020-09-03 13:36:47.000000000 +0200
+++ rhino-1.7.14/debian/librhino-java.manifest	2023-04-06 13:10:20.000000000 +0200
@@ -2,6 +2,12 @@
  Bundle-ManifestVersion: 2
  Bundle-Name: Rhino Javascript interpreter
  Bundle-Vendor: Mozilla Foundation
- Bundle-Version: 1.7
- Bundle-SymbolicName: org.mozilla.javascript
- Export-Package: org.mozilla.javascript;version="1.7",org.mozilla.javascript.annotations;version="1.7",org.mozilla.javascript.ast;version="1.7",org.mozilla.javascript;version="1.7",org.mozilla.javascript.commonjs.module;version="1.7",org.mozilla.javascript.commonjs.module.provider;version="1.7",org.mozilla.javascript.debug;version="1.7",org.mozilla.javascript.jdk13;version="1.7",org.mozilla.javascript.json;version="1.7",org.mozilla.javascript.optimizer;version="1.7",org.mozilla.javascript.regexp;version="1.7",org.mozilla.javascript.serialize;version="1.7",org.mozilla.javascript.tools;version="1.7",org.mozilla.javascript.tools.debugger;version="1.7",org.mozilla.javascript.tools.debugger.downloaded;version="1.7",org.mozilla.javascript.tools.debugger.treetable;version="1.7",org.mozilla.javascript.tools.idswitch;version="1.7",org.mozilla.javascript.tools.jsc;version="1.7",org.mozilla.javascript.tools.shell;version="1.7",org.mozilla.javascript.xml;version="1.7",org.mozilla.javascript.xmlimpl;version="1.7",org.mozilla.classfile;version="1.7"
+ Bundle-Version: 1.7.14
+ Bundle-SymbolicName: org.mozilla.rhino
+ Automatic-Module-Name: org.mozilla.rhino
+ Export-Package: org.mozilla.javascript,org.mozilla.javascript.ast,org.mozilla.javascript.annotations
+ Implementation-Title: Mozilla Rhino 1.7.14
+ Implementation-URL: http://www.mozilla.org/rhino
+ Implementation-Vendor: Mozilla Foundation
+ Implementation-Version: 1.7.14
+ Main-Class: org.mozilla.javascript.tools.shell.Main
diff -Nru rhino-1.7.7.2/debian/patches/03_public_getSourcePositionFromStack.patch rhino-1.7.14/debian/patches/03_public_getSourcePositionFromStack.patch
--- rhino-1.7.7.2/debian/patches/03_public_getSourcePositionFromStack.patch	2020-09-03 13:38:39.000000000 +0200
+++ rhino-1.7.14/debian/patches/03_public_getSourcePositionFromStack.patch	1970-01-01 01:00:00.000000000 +0100
@@ -1,18 +0,0 @@
-From: Giovanni Mascellani <gio at debian.org>
-Subject: Make getSourcePositionFromStack() pulic
-Forwarded: no
-Last-Update: 2011-12-08
-
-This is needed by package geogebra.
-
---- a/src/org/mozilla/javascript/Context.java
-+++ b/src/org/mozilla/javascript/Context.java
-@@ -2592,7 +2592,7 @@
-         return (Evaluator)Kit.newInstanceOrNull(interpreterClass);
-     }
- 
--    static String getSourcePositionFromStack(int[] linep)
-+    public static String getSourcePositionFromStack(int[] linep)
-     {
-         Context cx = getCurrentContext();
-         if (cx == null)
diff -Nru rhino-1.7.7.2/debian/patches/05_modify-usage.patch rhino-1.7.14/debian/patches/05_modify-usage.patch
--- rhino-1.7.7.2/debian/patches/05_modify-usage.patch	2020-09-03 13:36:47.000000000 +0200
+++ rhino-1.7.14/debian/patches/05_modify-usage.patch	2023-04-06 13:10:20.000000000 +0200
@@ -1,9 +1,17 @@
-Description: Simplify the usage syntax displayed with the --help option
-Author: Emmanuel Bourg <ebourg at apache.org>
+From: Emmanuel Bourg <ebourg at apache.org>
+Date: Mon, 13 Feb 2023 11:39:55 +0100
+Subject: Simplify the usage syntax displayed with the --help option
+
 Forwarded: not-needed
+---
+ toolsrc/org/mozilla/javascript/tools/resources/Messages.properties | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/toolsrc/org/mozilla/javascript/tools/resources/Messages.properties b/toolsrc/org/mozilla/javascript/tools/resources/Messages.properties
+index 154f1e5..f2b0b6c 100644
 --- a/toolsrc/org/mozilla/javascript/tools/resources/Messages.properties
 +++ b/toolsrc/org/mozilla/javascript/tools/resources/Messages.properties
-@@ -28,7 +28,7 @@
+@@ -28,7 +28,7 @@ msg.shell.invalid =\
      Invalid option "{0}"
  
  msg.shell.usage =\
@@ -12,7 +20,7 @@
      Valid options are:\n\
      \    -?, -help          Displays help messages.\n\
      \    -w                 Enable warnings.\n\
-@@ -110,7 +110,7 @@
+@@ -110,7 +110,7 @@ msg.jsc.bad.usage =\
      For more information, try java {0} -h
  
  msg.jsc.usage =\
diff -Nru rhino-1.7.7.2/debian/patches/06_preserve-backward-compatibility.patch rhino-1.7.14/debian/patches/06_preserve-backward-compatibility.patch
--- rhino-1.7.7.2/debian/patches/06_preserve-backward-compatibility.patch	2020-09-03 13:38:43.000000000 +0200
+++ rhino-1.7.14/debian/patches/06_preserve-backward-compatibility.patch	1970-01-01 01:00:00.000000000 +0100
@@ -1,172 +0,0 @@
-Description: Reintroduces removed classes and methods to preserve the backward compatibility.
- * Commit 9144b81 renamed isGetter/Setter methods, this breaks closure-compiler
- * Commit 71468c9 removed deprecated exceptions still used by htmlunit/2.8-2
-Author: Emmanuel Bourg <ebourg at apache.org>
-Forwarded: not-needed
---- a/src/org/mozilla/javascript/ast/ObjectProperty.java
-+++ b/src/org/mozilla/javascript/ast/ObjectProperty.java
-@@ -69,6 +69,10 @@
-         type = Token.GET;
-     }
- 
-+    public void setIsGetter() {
-+        setIsGetterMethod();
-+    }
-+
-     /**
-      * Returns true if this is a getter function.
-      */
-@@ -76,6 +80,10 @@
-         return type == Token.GET;
-     }
- 
-+    public boolean isGetter() {
-+        return isGetterMethod();
-+    }
-+
-     /**
-      * Marks this node as a "setter" property.
-      */
-@@ -83,6 +91,10 @@
-         type = Token.SET;
-     }
- 
-+    public void setIsSetter() {
-+        setIsSetterMethod();
-+    }
-+
-     /**
-      * Returns true if this is a setter function.
-      */
-@@ -90,6 +102,10 @@
-         return type == Token.SET;
-     }
- 
-+    public boolean isSetter() {
-+        return isSetterMethod();
-+    }
-+
-     public void setIsNormalMethod() {
-         type = Token.METHOD;
-     }
---- a/src/org/mozilla/javascript/ast/FunctionNode.java
-+++ b/src/org/mozilla/javascript/ast/FunctionNode.java
-@@ -330,14 +330,26 @@
-         return functionForm == Form.GETTER || functionForm == Form.SETTER || functionForm == Form.METHOD;
-     }
- 
-+    public boolean isGetterOrSetter() {
-+        return functionForm == Form.GETTER || functionForm == Form.SETTER;
-+    }
-+
-     public boolean isGetterMethod() {
-         return functionForm == Form.GETTER;
-     }
- 
-+    public boolean isGetter() {
-+        return isGetterMethod();
-+    }
-+
-     public boolean isSetterMethod() {
-         return functionForm == Form.SETTER;
-     }
- 
-+    public boolean isSetter() {
-+        return isSetterMethod();
-+    }
-+
-     public boolean isNormalMethod() {
-         return functionForm == Form.METHOD;
-     }
-@@ -346,10 +358,18 @@
-         functionForm = Form.GETTER;
-     }
- 
-+    public void setFunctionIsGetter() {
-+        setFunctionIsGetterMethod();
-+    }
-+
-     public void setFunctionIsSetterMethod() {
-         functionForm = Form.SETTER;
-     }
- 
-+    public void setFunctionIsSetter() {
-+        setFunctionIsSetterMethod();
-+    }
-+
-     public void setFunctionIsNormalMethod() {
-         functionForm = Form.METHOD;
-     }
---- /dev/null
-+++ b/src/org/mozilla/javascript/ClassDefinitionException.java
-@@ -0,0 +1,22 @@
-+
-+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
-+ *
-+ * This Source Code Form is subject to the terms of the Mozilla Public
-+ * License, v. 2.0. If a copy of the MPL was not distributed with this
-+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-+// API class
-+
-+package org.mozilla.javascript;
-+
-+/**
-+ * @deprecated The exception is no longer thrown by Rhino runtime as
-+ * {@link EvaluatorException} is used instead.
-+ */
-+public class ClassDefinitionException extends RuntimeException
-+{
-+    static final long serialVersionUID = -5637830967241712746L;
-+
-+    public ClassDefinitionException(String detail) {
-+        super(detail);
-+    }
-+}
---- /dev/null
-+++ b/src/org/mozilla/javascript/NotAFunctionException.java
-@@ -0,0 +1,20 @@
-+
-+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
-+ *
-+ * This Source Code Form is subject to the terms of the Mozilla Public
-+ * License, v. 2.0. If a copy of the MPL was not distributed with this
-+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-+// API class
-+
-+package org.mozilla.javascript;
-+
-+/**
-+ * @deprecated The exception is no longer thrown by Rhino runtime as
-+ * {@link EvaluatorException} is used instead.
-+ */
-+public class NotAFunctionException extends RuntimeException
-+{
-+    static final long serialVersionUID = 6461524852170711724L;
-+
-+    public NotAFunctionException() { }
-+}
---- /dev/null
-+++ b/src/org/mozilla/javascript/PropertyException.java
-@@ -0,0 +1,22 @@
-+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
-+ *
-+ * This Source Code Form is subject to the terms of the Mozilla Public
-+ * License, v. 2.0. If a copy of the MPL was not distributed with this
-+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-+
-+// API class
-+
-+package org.mozilla.javascript;
-+
-+/**
-+ * @deprecated This exception is no longer thrown by Rhino runtime.
-+ */
-+public class PropertyException extends RuntimeException
-+{
-+    static final long serialVersionUID = -8221564865490676219L;
-+
-+    public PropertyException(String detail) {
-+        super(detail);
-+    }
-+
-+}
diff -Nru rhino-1.7.7.2/debian/patches/07_fix-context-implementation-version.patch rhino-1.7.14/debian/patches/07_fix-context-implementation-version.patch
--- rhino-1.7.7.2/debian/patches/07_fix-context-implementation-version.patch	2020-09-03 13:38:45.000000000 +0200
+++ rhino-1.7.14/debian/patches/07_fix-context-implementation-version.patch	1970-01-01 01:00:00.000000000 +0100
@@ -1,18 +0,0 @@
-Description: Fixes the logic to extract the implementation version from the manifest
- (only when the project is built with Ant, with Gradle the manifest is ok).
-Author: Emmanuel Bourg <ebourg at apache.org>
-Forwarded: https://github.com/mozilla/rhino/commit/f575445cbe5e245#commitcomment-20551124
---- a/src/org/mozilla/javascript/Context.java
-+++ b/src/org/mozilla/javascript/Context.java
-@@ -729,9 +729,9 @@
-                     is = metaUrl.openStream();
-                     Manifest mf = new Manifest(is);
-                     Attributes attrs = mf.getMainAttributes();
--                    if ("Mozilla Rhino".equals(attrs.getValue("Implementation-Title"))) {
-+                    if (attrs.getValue("Implementation-Title") != null && attrs.getValue("Implementation-Title").startsWith("Mozilla Rhino")) {
-                         implementationVersion =
--                            "Rhino " + attrs.getValue("Implementation-Version") + " " + attrs.getValue("Built-Date").replaceAll("-", " ");
-+                            "Rhino " + attrs.getValue("Implementation-Version");
-                         return implementationVersion;
-                     }
-                 } catch (IOException e) {
diff -Nru rhino-1.7.7.2/debian/patches/08_fix-jar-version-number.patch rhino-1.7.14/debian/patches/08_fix-jar-version-number.patch
--- rhino-1.7.7.2/debian/patches/08_fix-jar-version-number.patch	2020-12-16 16:45:16.000000000 +0100
+++ rhino-1.7.14/debian/patches/08_fix-jar-version-number.patch	1970-01-01 01:00:00.000000000 +0100
@@ -1,14 +0,0 @@
-Description: update POM to match upstream version
-Author: tony mancill <tmancill at debian.org>
-
---- a/maven/maven-pom.xml
-+++ b/maven/maven-pom.xml
-@@ -12,7 +12,7 @@
-   <groupId>org.mozilla</groupId>
-   <artifactId>rhino</artifactId>
-   <name>Mozilla Rhino</name>
--  <version>1.7.7.1</version>
-+  <version>1.7.7.2</version>
- 
-   <packaging>jar</packaging>
-   <description>
diff -Nru rhino-1.7.7.2/debian/patches/preserve-backward-compatibility.patch rhino-1.7.14/debian/patches/preserve-backward-compatibility.patch
--- rhino-1.7.7.2/debian/patches/preserve-backward-compatibility.patch	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/debian/patches/preserve-backward-compatibility.patch	2023-04-06 13:10:20.000000000 +0200
@@ -0,0 +1,196 @@
+From: Markus Koschany <apo at debian.org>
+Date: Mon, 13 Feb 2023 11:58:06 +0100
+Subject: preserve-backward-compatibility
+
+Reintroduces removed classes and methods to preserve the backward compatibility.
+ * Commit 9144b81 renamed isGetter/Setter methods, this breaks closure-compiler
+ * Commit 71468c9 removed deprecated exceptions still used by htmlunit/2.8-2
+
+Forwarded: no
+---
+ .../javascript/ClassDefinitionException.java       | 22 ++++++++++++++++++++++
+ .../mozilla/javascript/NotAFunctionException.java  | 20 ++++++++++++++++++++
+ src/org/mozilla/javascript/PropertyException.java  | 22 ++++++++++++++++++++++
+ src/org/mozilla/javascript/ast/FunctionNode.java   | 20 ++++++++++++++++++++
+ src/org/mozilla/javascript/ast/ObjectProperty.java | 16 ++++++++++++++++
+ 5 files changed, 100 insertions(+)
+ create mode 100644 src/org/mozilla/javascript/ClassDefinitionException.java
+ create mode 100644 src/org/mozilla/javascript/NotAFunctionException.java
+ create mode 100644 src/org/mozilla/javascript/PropertyException.java
+
+diff --git a/src/org/mozilla/javascript/ClassDefinitionException.java b/src/org/mozilla/javascript/ClassDefinitionException.java
+new file mode 100644
+index 0000000..e53e12e
+--- /dev/null
++++ b/src/org/mozilla/javascript/ClassDefinitionException.java
+@@ -0,0 +1,22 @@
++
++/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
++ *
++ * This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++// API class
++
++package org.mozilla.javascript;
++
++/**
++ * @deprecated The exception is no longer thrown by Rhino runtime as
++ * {@link EvaluatorException} is used instead.
++ */
++public class ClassDefinitionException extends RuntimeException
++{
++    static final long serialVersionUID = -5637830967241712746L;
++
++    public ClassDefinitionException(String detail) {
++        super(detail);
++    }
++}
+diff --git a/src/org/mozilla/javascript/NotAFunctionException.java b/src/org/mozilla/javascript/NotAFunctionException.java
+new file mode 100644
+index 0000000..0ab2007
+--- /dev/null
++++ b/src/org/mozilla/javascript/NotAFunctionException.java
+@@ -0,0 +1,20 @@
++
++/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
++ *
++ * This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++// API class
++
++package org.mozilla.javascript;
++
++/**
++ * @deprecated The exception is no longer thrown by Rhino runtime as
++ * {@link EvaluatorException} is used instead.
++ */
++public class NotAFunctionException extends RuntimeException
++{
++    static final long serialVersionUID = 6461524852170711724L;
++
++    public NotAFunctionException() { }
++}
+diff --git a/src/org/mozilla/javascript/PropertyException.java b/src/org/mozilla/javascript/PropertyException.java
+new file mode 100644
+index 0000000..a9fcae1
+--- /dev/null
++++ b/src/org/mozilla/javascript/PropertyException.java
+@@ -0,0 +1,22 @@
++/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
++ *
++ * This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++// API class
++
++package org.mozilla.javascript;
++
++/**
++ * @deprecated This exception is no longer thrown by Rhino runtime.
++ */
++public class PropertyException extends RuntimeException
++{
++    static final long serialVersionUID = -8221564865490676219L;
++
++    public PropertyException(String detail) {
++        super(detail);
++    }
++
++}
+diff --git a/src/org/mozilla/javascript/ast/FunctionNode.java b/src/org/mozilla/javascript/ast/FunctionNode.java
+index 14efad9..863b173 100644
+--- a/src/org/mozilla/javascript/ast/FunctionNode.java
++++ b/src/org/mozilla/javascript/ast/FunctionNode.java
+@@ -340,14 +340,26 @@ public class FunctionNode extends ScriptNode {
+         return functionForm == Form.GETTER || functionForm == Form.SETTER || functionForm == Form.METHOD;
+     }
+ 
++    public boolean isGetterOrSetter() {
++        return functionForm == Form.GETTER || functionForm == Form.SETTER;
++    }
++
+     public boolean isGetterMethod() {
+         return functionForm == Form.GETTER;
+     }
+ 
++    public boolean isGetter() {
++        return isGetterMethod();
++    }
++
+     public boolean isSetterMethod() {
+         return functionForm == Form.SETTER;
+     }
+ 
++    public boolean isSetter() {
++        return isSetterMethod();
++    }
++
+     public boolean isNormalMethod() {
+         return functionForm == Form.METHOD;
+     }
+@@ -356,10 +368,18 @@ public class FunctionNode extends ScriptNode {
+         functionForm = Form.GETTER;
+     }
+ 
++    public void setFunctionIsGetter() {
++        setFunctionIsGetterMethod();
++    }
++
+     public void setFunctionIsSetterMethod() {
+         functionForm = Form.SETTER;
+     }
+ 
++    public void setFunctionIsSetter() {
++        setFunctionIsSetterMethod();
++    }
++
+     public void setFunctionIsNormalMethod() {
+         functionForm = Form.METHOD;
+     }
+diff --git a/src/org/mozilla/javascript/ast/ObjectProperty.java b/src/org/mozilla/javascript/ast/ObjectProperty.java
+index 4d06059..d8f894f 100644
+--- a/src/org/mozilla/javascript/ast/ObjectProperty.java
++++ b/src/org/mozilla/javascript/ast/ObjectProperty.java
+@@ -32,6 +32,10 @@ public class ObjectProperty extends InfixExpression {
+         type = Token.COLON;
+     }
+ 
++    public void setIsGetter() {
++        setIsGetterMethod();
++    }
++
+     /**
+      * Sets the node type. Must be one of {@link Token#COLON}, {@link Token#GET}, or {@link
+      * Token#SET}.
+@@ -67,16 +71,28 @@ public class ObjectProperty extends InfixExpression {
+         return type == Token.GET;
+     }
+ 
++    public boolean isGetter() {
++        return isGetterMethod();
++    }
++
+     /** Marks this node as a "setter" property. */
+     public void setIsSetterMethod() {
+         type = Token.SET;
+     }
+ 
++    public void setIsSetter() {
++        setIsSetterMethod();
++    }
++
+     /** Returns true if this is a setter function. */
+     public boolean isSetterMethod() {
+         return type == Token.SET;
+     }
+ 
++    public boolean isSetter() {
++        return isSetterMethod();
++    }
++
+     public void setIsNormalMethod() {
+         type = Token.METHOD;
+     }
diff -Nru rhino-1.7.7.2/debian/patches/public_getSourcePositionFromStack.patch rhino-1.7.14/debian/patches/public_getSourcePositionFromStack.patch
--- rhino-1.7.7.2/debian/patches/public_getSourcePositionFromStack.patch	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/debian/patches/public_getSourcePositionFromStack.patch	2023-04-06 13:10:20.000000000 +0200
@@ -0,0 +1,24 @@
+From: Markus Koschany <apo at debian.org>
+Date: Mon, 13 Feb 2023 11:44:43 +0100
+Subject: public_getSourcePositionFromStack
+
+This is needed by package geogebra.
+
+Forwarded: no
+---
+ src/org/mozilla/javascript/Context.java | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/org/mozilla/javascript/Context.java b/src/org/mozilla/javascript/Context.java
+index 8836e04..ac829e6 100644
+--- a/src/org/mozilla/javascript/Context.java
++++ b/src/org/mozilla/javascript/Context.java
+@@ -2512,7 +2512,7 @@ public class Context implements Closeable {
+         return (Evaluator) Kit.newInstanceOrNull(interpreterClass);
+     }
+ 
+-    static String getSourcePositionFromStack(int[] linep) {
++    public static String getSourcePositionFromStack(int[] linep) {
+         Context cx = getCurrentContext();
+         if (cx == null) return null;
+         if (cx.lastInterpreterFrame != null) {
diff -Nru rhino-1.7.7.2/debian/patches/script-engine.patch rhino-1.7.14/debian/patches/script-engine.patch
--- rhino-1.7.7.2/debian/patches/script-engine.patch	2021-02-08 11:03:36.000000000 +0100
+++ rhino-1.7.14/debian/patches/script-engine.patch	1970-01-01 01:00:00.000000000 +0100
@@ -1,1303 +0,0 @@
-From f195514ffee1b759ba088883732e77b026b3a694 Mon Sep 17 00:00:00 2001
-From: Gregory Brail <gregbrail at google.com>
-Date: Fri, 5 Jun 2020 14:38:28 -0700
-Subject: [PATCH] Implement standard Java ScriptEngine
-
-This is not based on the now-removed JDK code but instead does a
-few things in a more modern way. See the comments for supported
-parameters (you can set language and optimization level via
-properties) and built-in functions (only print is supported right now.)
-
-This is built into a separate JAR called "rhino-engine" because
-otherwise, including it in any Java 8 JDK would break scripts that
-are expecting to see Nashorn instead.
----
- .circleci/config.yml                          |   2 +-
- build.gradle                                  |  86 ++++-
- .../services/javax.script.ScriptEngineFactory |   1 +
- .../javascript/engine/BindingsObject.java     |  60 +++
- .../mozilla/javascript/engine/Builtins.java   |  59 +++
- .../engine/RhinoCompiledScript.java           |  33 ++
- .../engine/RhinoInvocationHandler.java        |  25 ++
- .../javascript/engine/RhinoScriptEngine.java  | 363 ++++++++++++++++++
- .../engine/RhinoScriptEngineFactory.java      | 140 +++++++
- .../tests/scriptengine/BuiltinsTest.java      |  54 +++
- .../tests/scriptengine/FactoryTest.java       |  56 +++
- .../tests/scriptengine/InvocableTest.java     | 158 ++++++++
- .../tests/scriptengine/ScriptEngineTest.java  | 276 +++++++++++++
- 13 files changed, 1305 insertions(+), 8 deletions(-)
- create mode 100644 src/META-INF/services/javax.script.ScriptEngineFactory
- create mode 100644 src/org/mozilla/javascript/engine/BindingsObject.java
- create mode 100644 src/org/mozilla/javascript/engine/Builtins.java
- create mode 100644 src/org/mozilla/javascript/engine/RhinoCompiledScript.java
- create mode 100644 src/org/mozilla/javascript/engine/RhinoInvocationHandler.java
- create mode 100644 src/org/mozilla/javascript/engine/RhinoScriptEngine.java
- create mode 100644 src/org/mozilla/javascript/engine/RhinoScriptEngineFactory.java
- create mode 100644 testsrc/org/mozilla/javascript/tests/scriptengine/BuiltinsTest.java
- create mode 100644 testsrc/org/mozilla/javascript/tests/scriptengine/FactoryTest.java
- create mode 100644 testsrc/org/mozilla/javascript/tests/scriptengine/InvocableTest.java
- create mode 100644 testsrc/org/mozilla/javascript/tests/scriptengine/ScriptEngineTest.java
-
---- /dev/null
-+++ b/src/META-INF/services/javax.script.ScriptEngineFactory
-@@ -0,0 +1 @@
-+org.mozilla.javascript.engine.RhinoScriptEngineFactory
---- /dev/null
-+++ b/src/org/mozilla/javascript/engine/BindingsObject.java
-@@ -0,0 +1,60 @@
-+/* This Source Code Form is subject to the terms of the Mozilla Public
-+ * License, v. 2.0. If a copy of the MPL was not distributed with this
-+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-+
-+package org.mozilla.javascript.engine;
-+
-+import org.mozilla.javascript.Context;
-+import org.mozilla.javascript.Scriptable;
-+import org.mozilla.javascript.ScriptableObject;
-+import javax.script.Bindings;
-+
-+/**
-+ * This class makes the Bindings object into a Scriptable. That way, we can query and modify
-+ * the contents of the Bindings on demand.
-+ */
-+public class BindingsObject
-+  extends ScriptableObject {
-+  private final Bindings bindings;
-+
-+  BindingsObject(Bindings bindings) {
-+    if (bindings == null) {
-+      throw new IllegalArgumentException("Bindings must not be null");
-+    }
-+    this.bindings = bindings;
-+  }
-+
-+  @Override
-+  public String getClassName() {
-+    return "BindingsObject";
-+  }
-+
-+  @Override
-+  public Object get(String name, Scriptable start) {
-+    Object ret = bindings.get(name);
-+    if (ret == null) {
-+      return Scriptable.NOT_FOUND;
-+    }
-+    return Context.jsToJava(ret, Object.class);
-+  }
-+
-+  @Override
-+  public void put(String name, Scriptable start, Object value) {
-+    bindings.put(name, Context.javaToJS(value, start));
-+  }
-+
-+  @Override
-+  public void delete(String name) {
-+    bindings.remove(name);
-+  }
-+
-+  @Override
-+  public boolean has(String name, Scriptable start) {
-+    return bindings.containsKey(name);
-+  }
-+
-+  @Override
-+  public Object[] getIds() {
-+    return bindings.keySet().toArray();
-+  }
-+}
---- /dev/null
-+++ b/src/org/mozilla/javascript/engine/Builtins.java
-@@ -0,0 +1,59 @@
-+/* This Source Code Form is subject to the terms of the Mozilla Public
-+ * License, v. 2.0. If a copy of the MPL was not distributed with this
-+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-+
-+package org.mozilla.javascript.engine;
-+
-+import java.io.IOException;
-+import java.io.OutputStreamWriter;
-+import java.io.Writer;
-+import javax.script.ScriptContext;
-+import org.mozilla.javascript.Context;
-+import org.mozilla.javascript.Function;
-+import org.mozilla.javascript.ScriptRuntime;
-+import org.mozilla.javascript.Scriptable;
-+import org.mozilla.javascript.ScriptableObject;
-+
-+/**
-+ * <p>
-+ * This class defines the following built-in functions for the RhinoScriptEngine.
-+ * </p>
-+ * <ul>
-+ * <li>print(arg, arg, ...): Write each argument, concatenated to the ScriptEngine's
-+ * "standard output" as a string.</li>
-+ * </ul>
-+ */
-+public class Builtins {
-+
-+  static final Object BUILTIN_KEY = new Object();
-+
-+  private Writer stdout;
-+
-+  void register(Context cx, ScriptableObject scope, ScriptContext sc) {
-+    if (sc.getWriter() == null) {
-+      stdout = new OutputStreamWriter(System.out);
-+    } else {
-+      stdout = sc.getWriter();
-+    }
-+
-+    scope.defineFunctionProperties(new String[]{"print"},
-+        Builtins.class,
-+        ScriptableObject.PERMANENT | ScriptableObject.DONTENUM);
-+  }
-+
-+  public static void print(Context cx, Scriptable thisObj, Object[] args, Function f)
-+      throws IOException {
-+    Builtins self = getSelf(thisObj);
-+    for (Object arg : args) {
-+      self.stdout.write(ScriptRuntime.toString(arg));
-+    }
-+    self.stdout.write('\n');
-+  }
-+
-+  private static Builtins getSelf(Scriptable scope) {
-+    // Since this class is invoked as a set of anonymous functions, "this"
-+    // in JavaScript does not point to "this" in Java. We set a key on the
-+    // top-level scope to address this.
-+    return (Builtins) ScriptableObject.getTopScopeValue(scope, BUILTIN_KEY);
-+  }
-+}
---- /dev/null
-+++ b/src/org/mozilla/javascript/engine/RhinoCompiledScript.java
-@@ -0,0 +1,33 @@
-+/* This Source Code Form is subject to the terms of the Mozilla Public
-+ * License, v. 2.0. If a copy of the MPL was not distributed with this
-+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-+
-+package org.mozilla.javascript.engine;
-+
-+import javax.script.CompiledScript;
-+import javax.script.ScriptContext;
-+import javax.script.ScriptEngine;
-+import javax.script.ScriptException;
-+import org.mozilla.javascript.Script;
-+
-+public class RhinoCompiledScript
-+  extends CompiledScript {
-+
-+  private final RhinoScriptEngine engine;
-+  private final Script script;
-+
-+  RhinoCompiledScript(RhinoScriptEngine engine, Script script) {
-+    this.engine = engine;
-+    this.script = script;
-+  }
-+
-+  @Override
-+  public Object eval(ScriptContext context) throws ScriptException {
-+    return engine.eval(script, context);
-+  }
-+
-+  @Override
-+  public ScriptEngine getEngine() {
-+    return engine;
-+  }
-+}
---- /dev/null
-+++ b/src/org/mozilla/javascript/engine/RhinoInvocationHandler.java
-@@ -0,0 +1,25 @@
-+/* This Source Code Form is subject to the terms of the Mozilla Public
-+ * License, v. 2.0. If a copy of the MPL was not distributed with this
-+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-+
-+package org.mozilla.javascript.engine;
-+
-+import java.lang.reflect.InvocationHandler;
-+import java.lang.reflect.Method;
-+
-+public class RhinoInvocationHandler
-+    implements InvocationHandler {
-+
-+  private final Object thiz;
-+  private final RhinoScriptEngine engine;
-+
-+  RhinoInvocationHandler(RhinoScriptEngine engine, Object thiz) {
-+    this.engine = engine;
-+    this.thiz = thiz;
-+  }
-+
-+  @Override
-+  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-+    return engine.invokeMethodRaw(thiz, method.getName(), method.getReturnType(), args);
-+  }
-+}
---- /dev/null
-+++ b/src/org/mozilla/javascript/engine/RhinoScriptEngine.java
-@@ -0,0 +1,355 @@
-+/* This Source Code Form is subject to the terms of the Mozilla Public
-+ * License, v. 2.0. If a copy of the MPL was not distributed with this
-+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-+
-+package org.mozilla.javascript.engine;
-+
-+import java.io.IOException;
-+import java.io.Reader;
-+import java.lang.reflect.Method;
-+import java.lang.reflect.Proxy;
-+import javax.script.AbstractScriptEngine;
-+import javax.script.Bindings;
-+import javax.script.Compilable;
-+import javax.script.CompiledScript;
-+import javax.script.Invocable;
-+import javax.script.ScriptContext;
-+import javax.script.ScriptEngine;
-+import javax.script.ScriptEngineFactory;
-+import javax.script.ScriptException;
-+import javax.script.SimpleBindings;
-+import org.mozilla.javascript.Callable;
-+import org.mozilla.javascript.Context;
-+import org.mozilla.javascript.ContextFactory;
-+import org.mozilla.javascript.RhinoException;
-+import org.mozilla.javascript.Script;
-+import org.mozilla.javascript.Scriptable;
-+import org.mozilla.javascript.ScriptableObject;
-+
-+/**
-+ * <p>
-+ * This is the implementation of the standard ScriptEngine interface for Rhino.
-+ * </p>
-+ * <p>
-+ * An instance of the Rhino ScriptEngine is fully self-contained. Bindings at the GLOBAL_SCOPE may
-+ * be set, but there is nothing special about them -- if both global and ENGINE_SCOPE bindings are
-+ * set then the "engine" bindings override the global ones.
-+ * </p>
-+ * <p>
-+ * The Rhino engine is not thread safe. Rhino does no synchronization of ScriptEngine instances and
-+ * no synchronization of Bindings instances. It is up to the caller to ensure that the ScriptEngine
-+ * and all its Bindings are used by a single thread at a time.
-+ * </p>
-+ * <p>
-+ * The Rhino script engine includes some top-level built-in functions. See the Builtins class for
-+ * more documentation.
-+ * </p>
-+ * <p>
-+ * The engine supports a few configuration parameters that may be set at the "engine scope". Both
-+ * are numbers that may be set to a String or Number object.
-+ * </p>
-+ * <ul>
-+ * <li>javax.script.language_version: The version of the JavaScript language supported,
-+ * which is an integer defined in the Context class. The default is the latest "ES6"
-+ * version, defined as 200.</li>
-+ * <li>org.mozilla.javascript.optimization_level: The level of optimization Rhino performs
-+ * on the generated bytecode. Default is 9, which is the most. Set to -1 to use interpreted
-+ * mode.</li>
-+ * </ul>
-+ */
-+public class RhinoScriptEngine
-+    extends AbstractScriptEngine
-+    implements Compilable, Invocable {
-+
-+  /**
-+   * Reserved key for the Rhino optimization level. Default is "9," for optimized and compiled code.
-+   * Set this to "-1" to run Rhino in interpreted mode -- this is much much slower but the only
-+   * option on platforms like Android that don't support class files.
-+   */
-+  public static final String OPTIMIZATION_LEVEL = "org.mozilla.javascript.optimization_level";
-+
-+  static final int DEFAULT_LANGUAGE_VERSION = Context.VERSION_ES6;
-+  private static final int DEFAULT_OPT = 9;
-+  private static final boolean DEFAULT_DEBUG = true;
-+  private static final String DEFAULT_FILENAME = "eval";
-+
-+  private static final CtxFactory ctxFactory = new CtxFactory();
-+
-+  private final RhinoScriptEngineFactory factory;
-+  private final Builtins builtins;
-+  private ScriptableObject topLevelScope = null;
-+
-+  RhinoScriptEngine(RhinoScriptEngineFactory factory) {
-+    this.factory = factory;
-+    this.builtins = new Builtins();
-+  }
-+
-+  private Scriptable initScope(Context cx, ScriptContext sc) throws ScriptException {
-+    configureContext(cx);
-+
-+    if (topLevelScope == null) {
-+      topLevelScope = cx.initStandardObjects();
-+      // We need to stash this away so that the built in functions can find
-+      // this engine's specific stuff that they need to work.
-+      topLevelScope.associateValue(Builtins.BUILTIN_KEY, builtins);
-+      builtins.register(cx, topLevelScope, sc);
-+    }
-+
-+    Scriptable engineScope = new BindingsObject(
-+        sc.getBindings(ScriptContext.ENGINE_SCOPE));
-+    engineScope.setParentScope(null);
-+    engineScope.setPrototype(topLevelScope);
-+
-+    if (sc.getBindings(ScriptContext.GLOBAL_SCOPE) != null) {
-+      Scriptable globalScope = new BindingsObject(
-+          sc.getBindings(ScriptContext.GLOBAL_SCOPE));
-+      globalScope.setParentScope(null);
-+      globalScope.setPrototype(topLevelScope);
-+      engineScope.setPrototype(globalScope);
-+    }
-+
-+    return engineScope;
-+  }
-+
-+  @Override
-+  public Object eval(String script, ScriptContext context) throws ScriptException {
-+    Context cx = ctxFactory.enterContext();
-+    try {
-+      Scriptable scope = initScope(cx, context);
-+      Object ret = cx.evaluateString(scope, script, getFilename(), 0, null);
-+      return Context.jsToJava(ret, Object.class);
-+    } catch (RhinoException re) {
-+      throw new ScriptException(re.getMessage(), re.sourceName(), re.lineNumber(),
-+          re.columnNumber());
-+    } finally {
-+      Context.exit();
-+    }
-+  }
-+
-+  @Override
-+  public Object eval(Reader reader, ScriptContext context) throws ScriptException {
-+    Context cx = ctxFactory.enterContext();
-+    try {
-+      Scriptable scope = initScope(cx, context);
-+      Object ret = cx.evaluateReader(scope, reader, getFilename(), 0, null);
-+      return Context.jsToJava(ret, Object.class);
-+    } catch (RhinoException re) {
-+      throw new ScriptException(re.getMessage(), re.sourceName(), re.lineNumber(),
-+          re.columnNumber());
-+    } catch (IOException ioe) {
-+      throw new ScriptException(ioe);
-+    } finally {
-+      Context.exit();
-+    }
-+  }
-+
-+  @Override
-+  public CompiledScript compile(String script) throws ScriptException {
-+    Context cx = ctxFactory.enterContext();
-+    try {
-+      configureContext(cx);
-+      Script s =
-+          cx.compileString(script, getFilename(), 1, null);
-+      return new RhinoCompiledScript(this, s);
-+    } catch (RhinoException re) {
-+      throw new ScriptException(re.getMessage(), re.sourceName(), re.lineNumber(),
-+          re.columnNumber());
-+    } finally {
-+      Context.exit();
-+    }
-+  }
-+
-+  @Override
-+  public CompiledScript compile(Reader script) throws ScriptException {
-+    Context cx = ctxFactory.enterContext();
-+    try {
-+      configureContext(cx);
-+      Script s =
-+          cx.compileReader(script, getFilename(), 1, null);
-+      return new RhinoCompiledScript(this, s);
-+    } catch (RhinoException re) {
-+      throw new ScriptException(re.getMessage(), re.sourceName(), re.lineNumber(),
-+          re.columnNumber());
-+    } catch (IOException ioe) {
-+      throw new ScriptException(ioe);
-+    } finally {
-+      Context.exit();
-+    }
-+  }
-+
-+  Object eval(Script script, ScriptContext sc) throws ScriptException {
-+    Context cx = ctxFactory.enterContext();
-+    try {
-+      Scriptable scope = initScope(cx, sc);
-+      Object ret = script.exec(cx, scope);
-+      return Context.jsToJava(ret, Object.class);
-+    } catch (RhinoException re) {
-+      throw new ScriptException(re.getMessage(), re.sourceName(), re.lineNumber(),
-+          re.columnNumber());
-+    } finally {
-+      Context.exit();
-+    }
-+  }
-+
-+  @Override
-+  public Object invokeFunction(String name, Object... args)
-+      throws ScriptException, NoSuchMethodException {
-+    return invokeMethod(null, name, args);
-+  }
-+
-+  @Override
-+  public Object invokeMethod(Object thiz, String name, Object... args)
-+      throws ScriptException, NoSuchMethodException {
-+    return invokeMethodRaw(thiz, name, Object.class, args);
-+  }
-+
-+  Object invokeMethodRaw(Object thiz, String name, Class<?> returnType, Object... args)
-+      throws ScriptException, NoSuchMethodException {
-+    Context cx = ctxFactory.enterContext();
-+    try {
-+      Scriptable scope = initScope(cx, context);
-+
-+      Scriptable localThis;
-+      if (thiz == null) {
-+        localThis = scope;
-+      } else {
-+        localThis = Context.toObject(thiz, scope);
-+      }
-+
-+      Object f = ScriptableObject.getProperty(localThis, name);
-+      if (f == Scriptable.NOT_FOUND) {
-+        throw new NoSuchMethodException(name);
-+      }
-+      if (!(f instanceof Callable)) {
-+        throw new ScriptException("\"" + name + "\" is not a function");
-+      }
-+      Callable func = (Callable) f;
-+
-+      if (args != null) {
-+        for (int i = 0; i < args.length; i++) {
-+          args[i] = Context.javaToJS(args[i], scope);
-+        }
-+      }
-+
-+      Object ret = func.call(cx, scope, localThis, args);
-+      if (returnType == Void.TYPE) {
-+        return null;
-+      }
-+      return Context.jsToJava(ret, returnType);
-+
-+    } catch (RhinoException re) {
-+      throw new ScriptException(re.getMessage(), re.sourceName(), re.lineNumber(),
-+          re.columnNumber());
-+    } finally {
-+      Context.exit();
-+    }
-+  }
-+
-+  @Override
-+  public <T> T getInterface(Class<T> clasz) {
-+    if ((clasz == null) || !clasz.isInterface()) {
-+      throw new IllegalArgumentException("Not an interface");
-+    }
-+    Context cx = ctxFactory.enterContext();
-+    try {
-+      Scriptable scope = initScope(cx, context);
-+      if (methodsMissing(scope, clasz)) {
-+        return null;
-+      }
-+    } catch (ScriptException se) {
-+      return null;
-+    } finally {
-+      Context.exit();
-+    }
-+    return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
-+        new Class<?>[]{clasz}, new RhinoInvocationHandler(this, null));
-+  }
-+
-+  @Override
-+  public <T> T getInterface(Object thiz, Class<T> clasz) {
-+    if ((clasz == null) || !clasz.isInterface()) {
-+      throw new IllegalArgumentException("Not an interface");
-+    }
-+    Context cx = ctxFactory.enterContext();
-+    try {
-+      Scriptable scope = initScope(cx, context);
-+      Scriptable thisObj = Context.toObject(thiz, scope);
-+      if (methodsMissing(thisObj, clasz)) {
-+        return null;
-+      }
-+    } catch (ScriptException se) {
-+      return null;
-+    } finally {
-+      Context.exit();
-+    }
-+    return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
-+        new Class<?>[]{clasz}, new RhinoInvocationHandler(this, thiz));
-+  }
-+
-+  @Override
-+  public Bindings createBindings() {
-+    return new SimpleBindings();
-+  }
-+
-+  @Override
-+  public ScriptEngineFactory getFactory() {
-+    return factory;
-+  }
-+
-+  private void configureContext(Context cx) throws ScriptException {
-+    Object lv = get(ScriptEngine.LANGUAGE_VERSION);
-+    if (lv != null) {
-+      cx.setLanguageVersion(parseInteger(lv));
-+    }
-+    Object ol = get(OPTIMIZATION_LEVEL);
-+    if (ol != null) {
-+      cx.setOptimizationLevel(parseInteger(ol));
-+    }
-+  }
-+
-+  private int parseInteger(Object v) throws ScriptException {
-+    if (v instanceof String) {
-+      try {
-+        return Integer.parseInt((String) v);
-+      } catch (NumberFormatException nfe) {
-+        throw new ScriptException("Invalid number " + v);
-+      }
-+    } else if (v instanceof Integer) {
-+      return (Integer) v;
-+    } else {
-+      throw new ScriptException("Value must be a string or number");
-+    }
-+  }
-+
-+  private String getFilename() {
-+    Object fn = get(ScriptEngine.FILENAME);
-+    if (fn instanceof String) {
-+      return (String) fn;
-+    }
-+    return DEFAULT_FILENAME;
-+  }
-+
-+  private boolean methodsMissing(Scriptable scope, Class<?> clasz) {
-+    for (Method m : clasz.getMethods()) {
-+      if (m.getDeclaringClass() == Object.class) {
-+        continue;
-+      }
-+      Object methodObj = ScriptableObject.getProperty(scope, m.getName());
-+      if (!(methodObj instanceof Callable)) {
-+        return true;
-+      }
-+    }
-+    return false;
-+  }
-+
-+  private static final class CtxFactory
-+      extends ContextFactory {
-+
-+    @Override
-+    protected void onContextCreated(Context cx) {
-+      cx.setLanguageVersion(Context.VERSION_ES6);
-+      cx.setOptimizationLevel(DEFAULT_OPT);
-+      cx.setGeneratingDebug(DEFAULT_DEBUG);
-+    }
-+  }
-+}
---- /dev/null
-+++ b/src/org/mozilla/javascript/engine/RhinoScriptEngineFactory.java
-@@ -0,0 +1,140 @@
-+/* This Source Code Form is subject to the terms of the Mozilla Public
-+ * License, v. 2.0. If a copy of the MPL was not distributed with this
-+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-+
-+package org.mozilla.javascript.engine;
-+
-+import java.util.Arrays;
-+import java.util.Collections;
-+import java.util.List;
-+import javax.script.ScriptEngine;
-+import javax.script.ScriptEngineFactory;
-+import org.mozilla.javascript.Context;
-+
-+/**
-+ * <p>
-+ * This is an implementation of the standard Java "ScriptEngine" for Rhino. If the Rhino engine
-+ * (typically in the form of the "rhino-engine" JAR) is in the classpath, then this script
-+ * engine will be activated.
-+ * </p>
-+ * <p>
-+ * See the list of constants in this class for the list of language names, file extensions, and
-+ * MIME types that this engine supports. This list is essentially the same as the list supported
-+ * in the Nashorn script engine that was included in Java 8.
-+ * </p>
-+ * <p>
-+ * Since this engine and Nashorn support the same language and file extensions, then unless
-+ * you are sure you are running in an environment that has Nashorn, the best way to get this
-+ * engine is to call ScriptEngine.getEngineByName("rhino") to ask for Rhino directly.
-+ * </p>
-+ */
-+public class RhinoScriptEngineFactory
-+  implements ScriptEngineFactory {
-+
-+  public static final String NAME = "rhino";
-+  public static final String LANGUAGE = "javascript";
-+  public static final List<String> NAMES =
-+      Arrays.asList("rhino", "Rhino", "javascript", "JavaScript");
-+  public static final List<String> EXTENSIONS =
-+      Collections.singletonList("js");
-+  public static final List<String> MIME_TYPES =
-+      Arrays.asList("application/javascript", "application/ecmascript",
-+          "text/javascript", "text/ecmascript");
-+  public static final String LANGUAGE_VERSION =
-+      String.valueOf(RhinoScriptEngine.DEFAULT_LANGUAGE_VERSION);
-+
-+  @Override
-+  public String getEngineName() {
-+    return NAME;
-+  }
-+
-+  @Override
-+  public String getEngineVersion() {
-+    Context cx = Context.enter();
-+    try {
-+      String v = cx.getImplementationVersion();
-+      return (v == null ? "unknown" : v);
-+    } finally {
-+      Context.exit();
-+    }
-+  }
-+
-+  @Override
-+  public List<String> getExtensions() {
-+    return EXTENSIONS;
-+  }
-+
-+  @Override
-+  public List<String> getMimeTypes() {
-+    return MIME_TYPES;
-+  }
-+
-+  @Override
-+  public List<String> getNames() {
-+    return NAMES;
-+  }
-+
-+  @Override
-+  public String getLanguageName() {
-+    return LANGUAGE;
-+  }
-+
-+  @Override
-+  public String getLanguageVersion() {
-+    return LANGUAGE_VERSION;
-+  }
-+
-+  @Override
-+  public Object getParameter(String key) {
-+    switch (key) {
-+      case ScriptEngine.ENGINE:
-+        return getEngineName();
-+      case ScriptEngine.ENGINE_VERSION:
-+        return getEngineVersion();
-+      case ScriptEngine.LANGUAGE:
-+        return getLanguageName();
-+      case ScriptEngine.LANGUAGE_VERSION:
-+        return getLanguageVersion();
-+      case ScriptEngine.NAME:
-+        return NAME;
-+      case "THREADING":
-+        // Engines are explicitly not thread-safe
-+        return null;
-+      default:
-+        return null;
-+    }
-+  }
-+
-+  @Override
-+  public String getMethodCallSyntax(String obj, String m, String... args) {
-+    StringBuilder sb = new StringBuilder();
-+    sb.append(obj).append('.').append(m).append('(');
-+    for (int i = 0; i < args.length; i++) {
-+      if (i > 0) {
-+        sb.append(',');
-+      }
-+      sb.append(args[i]);
-+    }
-+    sb.append(");");
-+    return sb.toString();
-+  }
-+
-+  @Override
-+  public String getOutputStatement(String toDisplay) {
-+    return "print('" + toDisplay + "');";
-+  }
-+
-+  @Override
-+  public String getProgram(String... statements) {
-+    StringBuilder sb = new StringBuilder();
-+    for (String stmt : statements) {
-+      sb.append(stmt).append(";\n");
-+    }
-+    return sb.toString();
-+  }
-+
-+  @Override
-+  public ScriptEngine getScriptEngine() {
-+    return new RhinoScriptEngine(this);
-+  }
-+}
---- /dev/null
-+++ b/testsrc/org/mozilla/javascript/tests/scriptengine/BuiltinsTest.java
-@@ -0,0 +1,54 @@
-+package org.mozilla.javascript.tests.scriptengine;
-+
-+import java.io.StringWriter;
-+import javax.script.ScriptContext;
-+import javax.script.ScriptEngine;
-+import javax.script.ScriptEngineManager;
-+import javax.script.ScriptException;
-+import javax.script.SimpleScriptContext;
-+import org.junit.Before;
-+import org.junit.BeforeClass;
-+import org.junit.Test;
-+import org.mozilla.javascript.engine.RhinoScriptEngineFactory;
-+
-+import static org.junit.Assert.*;
-+
-+public class BuiltinsTest {
-+
-+  private static ScriptEngineManager manager;
-+
-+  private ScriptEngine engine;
-+
-+  @BeforeClass
-+  public static void init() {
-+    manager = new ScriptEngineManager();
-+    manager.registerEngineName("rhino", new RhinoScriptEngineFactory());
-+  }
-+
-+  @Before
-+  public void setup() {
-+    engine = manager.getEngineByName("rhino");
-+  }
-+
-+  @Test
-+  public void testPrintStdout() throws ScriptException {
-+    engine.eval("print('Hello, World!');");
-+  }
-+
-+  @Test
-+  public void testPrintWriter() throws ScriptException {
-+    StringWriter sw = new StringWriter();
-+    ScriptContext sc = new SimpleScriptContext();
-+    sc.setWriter(sw);
-+    engine.eval("print('one', 2, true);", sc);
-+    assertEquals(sw.toString(), "one2true\n");
-+  }
-+
-+  @Test
-+  public void testPrintWriterGeneric() throws ScriptException {
-+    StringWriter sw = new StringWriter();
-+    engine.getContext().setWriter(sw);
-+    engine.eval(engine.getFactory().getOutputStatement("Display This!"));
-+    assertEquals(sw.toString(), "Display This!\n");
-+  }
-+}
---- /dev/null
-+++ b/testsrc/org/mozilla/javascript/tests/scriptengine/FactoryTest.java
-@@ -0,0 +1,56 @@
-+package org.mozilla.javascript.tests.scriptengine;
-+
-+import javax.script.ScriptEngine;
-+import javax.script.ScriptEngineFactory;
-+import javax.script.ScriptEngineManager;
-+import org.junit.Test;
-+import org.mozilla.javascript.engine.RhinoScriptEngine;
-+import org.mozilla.javascript.engine.RhinoScriptEngineFactory;
-+
-+import static org.junit.Assert.*;
-+
-+/*
-+ * A series of tests that depend on us having our engine registered with the
-+ * ScriptEngineManager by default.
-+ */
-+public class FactoryTest {
-+
-+  @Test
-+  public void findRhinoFactory() {
-+    ScriptEngineManager manager = new ScriptEngineManager();
-+    for (ScriptEngineFactory factory : manager.getEngineFactories()) {
-+      if (factory instanceof RhinoScriptEngineFactory) {
-+        assertEquals("rhino", factory.getEngineName());
-+        assertEquals("rhino", factory.getParameter(ScriptEngine.ENGINE));
-+        assertEquals("rhino", factory.getParameter(ScriptEngine.NAME));
-+        // This could be "unknown" if we're not running from a regular JAR
-+        assertFalse(factory.getEngineVersion().isEmpty());
-+        assertEquals("javascript", factory.getLanguageName());
-+        assertEquals("javascript", factory.getParameter(ScriptEngine.LANGUAGE));
-+        assertEquals("200", factory.getLanguageVersion());
-+        assertEquals("200", factory.getParameter(ScriptEngine.LANGUAGE_VERSION));
-+        assertNull(factory.getParameter("THREADING"));
-+        assertTrue(factory.getExtensions().contains("js"));
-+        assertTrue(factory.getMimeTypes().contains("application/javascript"));
-+        assertTrue(factory.getMimeTypes().contains("application/ecmascript"));
-+        assertTrue(factory.getMimeTypes().contains("text/javascript"));
-+        assertTrue(factory.getMimeTypes().contains("text/ecmascript"));
-+        assertTrue(factory.getNames().contains("rhino"));
-+        assertTrue(factory.getNames().contains("Rhino"));
-+        assertTrue(factory.getNames().contains("javascript"));
-+        assertTrue(factory.getNames().contains("JavaScript"));
-+        return;
-+      }
-+    }
-+    fail("Expected to find Rhino script engine");
-+  }
-+
-+  @Test
-+  public void testRhinoFactory() {
-+    // This will always uniquely return our engine.
-+    // In Java 8, other ways to find it may return Nashorn.
-+    ScriptEngine engine = new ScriptEngineManager().getEngineByName("rhino");
-+    assertTrue(engine instanceof RhinoScriptEngine);
-+
-+  }
-+}
---- /dev/null
-+++ b/testsrc/org/mozilla/javascript/tests/scriptengine/InvocableTest.java
-@@ -0,0 +1,158 @@
-+package org.mozilla.javascript.tests.scriptengine;
-+
-+import java.io.FileNotFoundException;
-+import java.io.FileReader;
-+import javax.script.Invocable;
-+import javax.script.ScriptEngine;
-+import javax.script.ScriptEngineManager;
-+import javax.script.ScriptException;
-+import org.junit.Before;
-+import org.junit.BeforeClass;
-+import org.junit.Test;
-+import org.mozilla.javascript.engine.RhinoScriptEngineFactory;
-+
-+import static org.junit.Assert.*;
-+
-+public class InvocableTest {
-+
-+  private static ScriptEngineManager manager;
-+
-+  private ScriptEngine engine;
-+  private Invocable iEngine;
-+
-+  @BeforeClass
-+  public static void init() {
-+    manager = new ScriptEngineManager();
-+    manager.registerEngineName("rhino", new RhinoScriptEngineFactory());
-+  }
-+
-+  @Before
-+  public void setup() {
-+    engine = manager.getEngineByName("rhino");
-+    iEngine = (Invocable) engine;
-+  }
-+
-+  @Test
-+  public void invokeFunctionTest() throws ScriptException, NoSuchMethodException {
-+    engine.eval("function foo(a, b) { return a + b; }");
-+    Object result = iEngine.invokeFunction("foo", 2, 2);
-+    assertEquals(result, 4L);
-+  }
-+
-+  @Test
-+  public void invokeScriptFunctionTest() throws ScriptException, NoSuchMethodException {
-+    Object scriptObj = engine.eval("let o = {};\n"
-+        + "o.test = function(x) { return x + 2; }\n"
-+        + "o;");
-+    assertEquals(4L, iEngine.invokeMethod(scriptObj, "test", 2));
-+  }
-+
-+  @Test
-+  public void invokeGenericFunctionTest() throws ScriptException, NoSuchMethodException {
-+    engine.eval("let o = {};\n"
-+        + "o.test = function(x) { return x + 2; }\n");
-+    Object result = engine.eval(engine.getFactory().getMethodCallSyntax("o", "test", "1"));
-+    assertEquals(3L, result);
-+  }
-+
-+  @Test
-+  public void invokeGenericFunctionTest2() throws ScriptException, NoSuchMethodException {
-+    engine.eval("let o = {};\n"
-+        + "o.test = function(x, y) { return x + y; }\n");
-+    Object result = engine.eval(engine.getFactory().getMethodCallSyntax("o", "test", "1", "7"));
-+    assertEquals(8L, result);
-+  }
-+
-+  @Test
-+  public void invokeMethodTest()
-+      throws ScriptException, NoSuchMethodException, FileNotFoundException {
-+    engine.eval(new FileReader("testsrc/assert.js"));
-+    engine.eval("function FooObj() { this.x = 0; }\n"
-+        + "FooObj.prototype.set = function(a, b) { this.x = a + b; }");
-+    engine.eval("let f = new FooObj();\n"
-+        + "assertEquals(f.x, 0);\n"
-+        + "f.set(2, 2);\n"
-+        + "assertEquals(f.x, 4);");
-+
-+    Object fooObj = engine.eval("let y = new FooObj(); y");
-+    assertNotNull(fooObj);
-+    iEngine.invokeMethod(fooObj, "set", 3, 3);
-+    Object result = engine.eval("y.x");
-+    assertEquals(result, 6L);
-+  }
-+
-+  @Test
-+  public void interfaceFunctionTest()
-+      throws ScriptException, FileNotFoundException {
-+    engine.eval(new FileReader("testsrc/assert.js"));
-+    engine.eval("var foo = 'initialized';\n"
-+        + "function setFoo(v) { foo = v; }\n"
-+        + "function getFoo() { return foo; }\n"
-+        + "function addItUp(a, b) { return a + b; }");
-+    I tester = iEngine.getInterface(I.class);
-+    assertEquals(tester.getFoo(), "initialized");
-+    tester.setFoo("tested");
-+    assertEquals(tester.getFoo(), "tested");
-+    assertEquals(tester.addItUp(100, 1), 101);
-+  }
-+
-+  @Test
-+  public void interfaceMethodTest()
-+      throws ScriptException, FileNotFoundException {
-+    engine.eval(new FileReader("testsrc/assert.js"));
-+    Object foo = engine.eval("function Foo() { this.foo = 'initialized' }\n"
-+        + "Foo.prototype.setFoo = function(v) { this.foo = v; };\n"
-+        + "Foo.prototype.getFoo = function() { return this.foo; };\n"
-+        + "Foo.prototype.addItUp = function(a, b) { return a + b; };\n"
-+        + "new Foo();");
-+    I tester = iEngine.getInterface(foo, I.class);
-+    assertEquals(tester.getFoo(), "initialized");
-+    tester.setFoo("tested");
-+    assertEquals(tester.getFoo(), "tested");
-+    assertEquals(tester.addItUp(100, 1), 101);
-+  }
-+
-+  @Test
-+  public void interfaceFunctionMissingTest() {
-+    I tester = iEngine.getInterface(I.class);
-+    assertNull(tester);
-+  }
-+
-+  @Test
-+  public void interfaceMethodMissingTest()
-+      throws ScriptException {
-+    // Functions defined, but not on the right object
-+    Object foo = engine.eval("var foo = 'initialized';\n"
-+        + "function setFoo(v) { foo = v; }\n"
-+        + "function getFoo() { return foo; }\n"
-+        + "function addItUp(a, b) { return a + b; }\n"
-+        + "function Foo() {}\n"
-+        + "new Foo();");
-+    I tester = iEngine.getInterface(foo, I.class);
-+    assertNull(tester);
-+  }
-+
-+  @Test
-+  public void invokeNotFoundTest() {
-+    assertThrows(NoSuchMethodException.class, () -> {
-+      iEngine.invokeFunction("foo", 2, 2);
-+    });
-+  }
-+
-+  @Test
-+  public void invokeNotFunctionTest() {
-+    assertThrows(ScriptException.class, () -> {
-+      engine.eval("foo = 'bar';");
-+      iEngine.invokeFunction("foo", 2, 2);
-+    });
-+  }
-+
-+  interface I {
-+
-+    void setFoo(String v);
-+
-+    String getFoo();
-+
-+    int addItUp(int a, int b);
-+  }
-+}
---- /dev/null
-+++ b/testsrc/org/mozilla/javascript/tests/scriptengine/ScriptEngineTest.java
-@@ -0,0 +1,276 @@
-+package org.mozilla.javascript.tests.scriptengine;
-+
-+import java.io.File;
-+import java.io.FileReader;
-+import java.io.IOException;
-+import java.io.StringReader;
-+import javax.script.Bindings;
-+import javax.script.Compilable;
-+import javax.script.CompiledScript;
-+import javax.script.ScriptContext;
-+import javax.script.ScriptEngine;
-+import javax.script.ScriptEngineFactory;
-+import javax.script.ScriptEngineManager;
-+import javax.script.ScriptException;
-+import javax.script.SimpleBindings;
-+import javax.script.SimpleScriptContext;
-+import org.junit.Before;
-+import org.junit.BeforeClass;
-+import org.junit.Test;
-+import org.mozilla.javascript.engine.RhinoScriptEngine;
-+import org.mozilla.javascript.engine.RhinoScriptEngineFactory;
-+
-+import static org.junit.Assert.*;
-+
-+public class ScriptEngineTest {
-+
-+  private static ScriptEngineManager manager;
-+  private ScriptEngine engine;
-+  private Compilable cEngine;
-+
-+  @BeforeClass
-+  public static void initManager() {
-+    manager = new ScriptEngineManager();
-+    manager.registerEngineName("rhino", new RhinoScriptEngineFactory());
-+  }
-+
-+  @Before
-+  public void init() {
-+    engine = manager.getEngineByName("rhino");
-+    cEngine = (Compilable) engine;
-+  }
-+
-+  @Test
-+  public void testHello() throws ScriptException {
-+    Object result = engine.eval("'Hello, World!';");
-+    assertEquals(result, "Hello, World!");
-+  }
-+
-+  @Test
-+  public void testHelloInterpreted() throws ScriptException {
-+    engine.put(RhinoScriptEngine.OPTIMIZATION_LEVEL, -1);
-+    Object result = engine.eval("'Hello, World!';");
-+    assertEquals(result, "Hello, World!");
-+  }
-+
-+
-+  @Test
-+  public void testHelloReader() throws ScriptException {
-+    String src = "1 + 1;";
-+    StringReader sr = new StringReader(src);
-+    Object result = engine.eval(sr);
-+    assertEquals(result, 2L);
-+  }
-+
-+  @Test
-+  public void testGenericStatements() throws ScriptException {
-+    Object result = engine.eval(engine.getFactory().getProgram(
-+        "let x = 1;",
-+        "let y = 2",
-+        "x + y"
-+    ));
-+    assertEquals(3L, result);
-+  }
-+
-+  @Test
-+  public void testThrows() {
-+    assertThrows(ScriptException.class, () -> {
-+      engine.eval("throw 'This is an error'");
-+    });
-+  }
-+
-+  @Test
-+  public void testEngineBindings() throws IOException, ScriptException {
-+    engine.put("string", "Hello");
-+    engine.put("integer", 123);
-+    engine.put("a", "a");
-+    engine.put("b", "b");
-+    engine.put("c", "c");
-+
-+    // Ensure that stuff we just stuck in bindings made it to a global
-+    engine.eval(new FileReader("testsrc/assert.js"));
-+    engine.eval("assertEquals(string, 'Hello');\n"
-+        + "assertEquals(integer, 123);\n"
-+        + "string = 'Goodbye';\n"
-+        + "assertEquals(string, 'Goodbye');");
-+    assertEquals(engine.get("string"), "Goodbye");
-+
-+    // Make sure we can delete
-+    engine.getBindings(ScriptContext.ENGINE_SCOPE).remove("string");
-+    // This will throw because string is undefined
-+    assertThrows(ScriptException.class, () -> {
-+      engine.eval("let failing = string + '123';");
-+    });
-+  }
-+
-+  @Test
-+  public void testEngineScope() throws IOException, ScriptException {
-+    engine.put("string", "Hello");
-+    engine.put("integer", 123);
-+    engine.eval(new FileReader("testsrc/assert.js"));
-+    engine.eval("assertEquals(string, 'Hello');"
-+        + "assertEquals(integer, 123);");
-+
-+    // Additional things added to the context but old stuff still there
-+    engine.put("second", true);
-+    engine.put("integer", 99);
-+    engine.eval("assertEquals(string, 'Hello');"
-+        + "assertEquals(integer, 99);"
-+        + "assertTrue(second);");
-+  }
-+
-+  @Test
-+  public void testScopedBindings() throws IOException, ScriptException {
-+    ScriptContext sc = new SimpleScriptContext();
-+
-+    // We treat engine and global scope the same -- if the user actually
-+    // uses both, then engine scope overrides global scope.
-+    Bindings eb = new SimpleBindings();
-+    sc.setBindings(eb, ScriptContext.ENGINE_SCOPE);
-+    eb.put("engine", Boolean.TRUE);
-+    eb.put("level", 2);
-+
-+    Bindings gb = new SimpleBindings();
-+    sc.setBindings(gb, ScriptContext.GLOBAL_SCOPE);
-+    gb.put("global", Boolean.TRUE);
-+    gb.put("level", 0);
-+
-+    engine.eval(new FileReader("testsrc/assert.js"), sc);
-+    engine.eval("assertTrue(engine);"
-+        + "assertTrue(global);"
-+        + "assertEquals(level, 2);", sc);
-+  }
-+
-+  @Test
-+  public void testReservedBindings() throws ScriptException {
-+    engine.put(ScriptEngine.ENGINE, "engine");
-+    engine.put(ScriptEngine.ENGINE_VERSION, "123");
-+    engine.put(ScriptEngine.LANGUAGE, "foo");
-+    engine.put(ScriptEngine.NAME, "nothing");
-+
-+    // Can't actually test for those invalid property names -- but
-+    // at least they didn't break the script.
-+    assertEquals(engine.eval("'success'"), "success");
-+  }
-+
-+  @Test
-+  public void testCompiled() throws ScriptException, IOException {
-+    CompiledScript asserts =
-+        cEngine.compile(new FileReader("testsrc/assert.js"));
-+    CompiledScript tests =
-+        cEngine.compile("assertEquals(compiled, true);");
-+
-+    // Fails because asserts have not been loaded
-+    assertThrows(ScriptException.class, tests::eval);
-+
-+    asserts.eval();
-+    // Fails because value has not been set
-+    assertThrows(ScriptException.class, tests::eval);
-+
-+    engine.put("compiled", Boolean.TRUE);
-+    tests.eval();
-+  }
-+
-+  @Test
-+  public void testCompiled2() throws ScriptException, IOException {
-+    CompiledScript asserts =
-+        cEngine.compile(new FileReader("testsrc/assert.js"));
-+    CompiledScript init =
-+        cEngine.compile("value = 0;");
-+    CompiledScript tests =
-+        cEngine.compile("assertEquals(value, expectedValue);"
-+            + "value += 1;");
-+
-+    asserts.eval();
-+    init.eval();
-+    for (int i = 0; i <= 10; i++) {
-+      engine.put("expectedValue", i);
-+      tests.eval();
-+    }
-+  }
-+
-+  @Test
-+  public void testCompiledThrows() throws ScriptException {
-+    engine.put(ScriptEngine.FILENAME, "throws1.js");
-+    CompiledScript throw1 = cEngine.compile("throw 'one';");
-+    engine.put(ScriptEngine.FILENAME, "throws2.js");
-+    CompiledScript throw2 = cEngine.compile("throw 'two';");
-+
-+    try {
-+      throw1.eval();
-+      fail("Expected a throw");
-+    } catch (ScriptException se) {
-+      assertTrue(se.getMessage().startsWith("one"));
-+      assertEquals("throws1.js", se.getFileName());
-+      assertEquals(1, se.getLineNumber());
-+    }
-+
-+    try {
-+      throw2.eval();
-+      fail("Expected a throw");
-+    } catch (ScriptException se) {
-+      assertTrue(se.getMessage().startsWith("two"));
-+      assertEquals("throws2.js", se.getFileName());
-+      assertEquals(1, se.getLineNumber());
-+    }
-+  }
-+
-+  @Test
-+  public void testCantCompile() {
-+    assertThrows(ScriptException.class, () -> {
-+      cEngine.compile("This is not JavaScript at all!");
-+    });
-+  }
-+
-+  @Test
-+  public void testLanguageVersion() throws ScriptException {
-+    // Default language version is modernish
-+    ScriptEngine newEngine = manager.getEngineByName("rhino");
-+    assertEquals(newEngine.eval("Symbol() == Symbol()"), Boolean.FALSE);
-+
-+    // Older language versions
-+    ScriptEngine oldEngine = manager.getEngineByName("rhino");
-+    oldEngine.put(ScriptEngine.LANGUAGE_VERSION, 120);
-+    assertThrows(ScriptException.class, () -> {
-+      oldEngine.eval("Symbol() == Symbol()");
-+    });
-+
-+    // The same with a string
-+    ScriptEngine olderEngine = manager.getEngineByName("rhino");
-+    olderEngine.put(ScriptEngine.LANGUAGE_VERSION, "100");
-+    assertThrows(ScriptException.class, () -> {
-+      olderEngine.eval("Symbol() == Symbol()");
-+    });
-+  }
-+
-+  @Test
-+  public void testBadLanguageVersion() {
-+    assertThrows(ScriptException.class, () -> {
-+      engine.put(ScriptEngine.LANGUAGE_VERSION, "Not a number");
-+      engine.eval("print('Hi!');");
-+    });
-+    assertThrows(ScriptException.class, () -> {
-+      engine.put(ScriptEngine.LANGUAGE_VERSION, 3.14);
-+      engine.eval("print('Hi!');");
-+    });
-+  }
-+
-+  @Test
-+  public void testFilename() {
-+    engine.put(ScriptEngine.FILENAME, "test.js");
-+    try {
-+      engine.eval("throw 'This is an exception';");
-+    } catch (ScriptException se) {
-+      assertEquals(se.getFileName(), "test.js");
-+    }
-+  }
-+
-+  @Test
-+  public void testJavaObject() throws ScriptException {
-+    File f = new File("testsrc/assert.js");
-+    String absVal = f.getAbsolutePath();
-+    engine.put("file", f);
-+    Object result = engine.eval("file.getAbsolutePath();");
-+    assertEquals(absVal, result);
-+  }
-+}
---- a/build.xml
-+++ b/build.xml
-@@ -87,7 +87,9 @@
-          basedir="${classes}"
-          manifest="src/manifest"
-          compress="${jar-compression}"
--     />
-+    >
-+      <fileset dir="src" includes="META-INF/**"/>
-+    </jar>
-   </target>
- 
-   <target name="console" depends="jar">
diff -Nru rhino-1.7.7.2/debian/patches/series rhino-1.7.14/debian/patches/series
--- rhino-1.7.7.2/debian/patches/series	2021-02-08 10:51:49.000000000 +0100
+++ rhino-1.7.14/debian/patches/series	2023-04-06 13:10:20.000000000 +0200
@@ -1,7 +1,3 @@
-03_public_getSourcePositionFromStack.patch
 05_modify-usage.patch
-06_preserve-backward-compatibility.patch
-07_fix-context-implementation-version.patch
-08_fix-jar-version-number.patch
-script-engine.patch
-
+public_getSourcePositionFromStack.patch
+preserve-backward-compatibility.patch
diff -Nru rhino-1.7.7.2/debian/pom.xml rhino-1.7.14/debian/pom.xml
--- rhino-1.7.7.2/debian/pom.xml	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/debian/pom.xml	2023-04-06 13:10:20.000000000 +0200
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.mozilla</groupId>
+  <artifactId>rhino</artifactId>
+  <version>1.7.14</version>
+  <description>
+    Rhino is an open-source implementation of JavaScript written entirely in Java.
+    It is typically embedded into Java applications to provide scripting to end users.
+    Full jar including tools, excluding the JSR-223 Script Engine wrapper.
+</description>
+  <url>https://mozilla.github.io/rhino/</url>
+  <parent>
+    <groupId>org.sonatype.oss</groupId>
+    <artifactId>oss-parent</artifactId>
+    <version>7</version>
+  </parent>
+  <licenses>
+    <license>
+      <name>Mozilla Public License, Version 2.0</name>
+      <url>http://www.mozilla.org/MPL/2.0/index.txt</url>
+    </license>
+  </licenses>
+  <scm>
+    <connection>scm:git:git at github.com:mozilla/rhino.git</connection>
+    <developerConnection>scm:git:git at github.com:mozilla/rhino.git</developerConnection>
+    <url>git at github.com:mozilla/rhino.git</url>
+  </scm>
+  <organization>
+    <name>The Mozilla Foundation</name>
+    <url>http://www.mozilla.org</url>
+  </organization>
+</project>
\ No newline at end of file
diff -Nru rhino-1.7.7.2/debian/rhino.1 rhino-1.7.14/debian/rhino.1
--- rhino-1.7.7.2/debian/rhino.1	2020-09-03 13:36:47.000000000 +0200
+++ rhino-1.7.14/debian/rhino.1	1970-01-01 01:00:00.000000000 +0100
@@ -1,112 +0,0 @@
-.\"                                      Hey, EMACS: -*- nroff -*-
-.\" First parameter, NAME, should be all caps
-.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
-.\" other parameters are allowed: see man(7), man(1)
-.TH RHINO 1
-.\" Please adjust this date whenever revising the manpage.
-.\"
-.\" Some roff macros, for reference:
-.\" .nh        disable hyphenation
-.\" .hy        enable hyphenation
-.\" .ad l      left justify
-.\" .ad b      justify to both left and right margins
-.\" .nf        disable filling
-.\" .fi        enable filling
-.\" .br        insert line break
-.\" .sp <n>    insert n+1 empty lines
-.\" for manpage-specific macros, see man(7)
-.SH NAME
-
-rhino \- invokes the JavaScript shell for running scripts in batch mode or interactive
-
-.SH SYNOPSIS
-
-.B rhino
-.I [options]
-.I script_filename_or_url
-.I [script_arguments]
-
-.SH DESCRIPTION
-
-This manual page documents briefly the
-.B rhino
-command.
-This manual page was written for the Debian distribution because the original
-program does not have a manual page. It is written according to the html documentation.
-.PP
-.\" TeX users may be more comfortable with the \fB<whatever>\fP and
-.\" \fI<whatever>\fP escape sequences to invode bold face and italics,
-.\" respectively.
-\fBrhino\fP is a start script for the rhino JavaScript shell which provides a simple way
-to run scripts in batch mode or an interactive environment for exploratory programming.
-
-.SH OPTIONS
-
-.IP -w
-Enable warnings.
-.IP -version\ \fIversionNumber\fP
-Specifies the language version to compile with. The string versionNumber must be one of 100, 110, 120, 130, 140, 150, 160, 170, 180 or 200. See JavaScript Language Versions for more information on language versions.
-.IP -opt,\ -O\ \fIoptLevel\fP
-Optimizes at level optLevel, which must be an integer between -1 and 9. -1 means Interpretive mode is always used. 0 means no optimizations whereas 9 means all optimizations are performed.
-.IP -f\ \fIscript_filename_or_url\fP
-Reads script_filename_or_url content and execute it as a JavaScript script.
-.IP -e\ \fIscript_source\fP
-Executes script_source as a JavaScript script.
-.IP -modules\ \fIuri\fP
-Add a single path or URL element to the CommonJS module search path. (implies -require)
-.IP -require
-Enable CommonJS module support.
-.IP -sandbox
-Enable CommonJS sandbox mode. (implies -require)
-.IP -debug
-Generate debug code.
-.IP -strict
-Enable strict mode.
-.IP -fatal-warnings
-Treat warnings as errors.
-.IP -encoding\ \fIcharset\fP
-Use specified character encoding as default when reading scripts.
-
-.SH PREDEFINED PROPERTIES
-
-Scripts executing in the shell have access to some additional properties of the top-level object.
-
-.IP arguments
-The arguments object is an array containing the strings of all the arguments given at the command line when the shell was invoked.
-.IP help()
-Executing the help function will print usage and help messages.
-.IP defineClass(\fIclassName\fP)
-Define an extension using the Java class named with the string argument className. Uses ScriptableObject.defineClass() to define the extension.
-.IP deserialize(\fIfilename\fP)
-Restore from the specified file an object previously written by a call to serialize.
-.IP load(\fI[filename,\&.\&.\&.]\fP)
-Load JavaScript source files named by string arguments. If multiple arguments are given, each file is read in and executed in turn.
-.IP loadClass(\fIclassName\fP)
-Load and execute the class named by the string argument className. The class must be a class that implements the Script interface, as will any script compiled by jsc.
-.IP print(\fI[expr\&.\&.\&.]\fP)
-Evaluate and print expressions. Evaluates each expression, converts the result to a string, and prints it.
-.IP readFile(\fIpath[,characterCoding]\fP)
-Read given file and convert its bytes to a string using the specified character coding or default character coding if explicit coding argument is not given.
-.IP readUrl(\fIurl[,characterCoding]\fP)
-Open an input connection to the given string url, read all its bytes and convert them to a string using the specified character coding or default character coding if explicit coding argument is not given.
-.IP runCommand(\fIcommandName,[arg,\&.\&.\&.][options]\fP)
-Execute the specified command with the given argument and options as a separate process and return the exit status of the process. For details, see JavaDoc for org.mozilla.javascript.tools.shell.Global#runCommand.
-.IP serialize(\fIobject,filename\fP)
-Serialize the given object to the specified file.
-.IP spawn(\fIfunctionOrScript\fP)
-Run the given function or script in a different thread.
-.IP sync(\fIfunction\fP)
-creates a synchronized function (in the sense of a Java synchronized method) from an existing function. The new function synchronizes on the this object of its invocation.
-.IP quit()
-Quit shell. The shell will also quit in interactive mode if an end-of-file character is typed at the prompt.
-.IP version(\fI[number]\fP)
-Get or set JavaScript version number. If no argument is supplied, the current version number is returned. If an argument is supplied, it is expected to be one of 100, 110, 120, 130, or 140 to indicate JavaScript version 1.0, 1.1, 1.2, 1.3, or 1.4 respectively.
-
-.SH SEE ALSO
-
-The online documentation under https://developer.mozilla.org/en-US/docs/Mozilla/Projects/Rhino/Shell
-
-.SH AUTHOR
-
-This manual page was written by Wolfgang Baer <WBaer at gmx.de>,
-for the Debian project (but may be used by others).
diff -Nru rhino-1.7.7.2/debian/rhino.manpages rhino-1.7.14/debian/rhino.manpages
--- rhino-1.7.7.2/debian/rhino.manpages	2020-09-03 13:36:47.000000000 +0200
+++ rhino-1.7.14/debian/rhino.manpages	2023-04-06 13:10:20.000000000 +0200
@@ -1,3 +1,3 @@
 debian/rhino-debugger.1
 debian/rhino-jsc.1
-debian/rhino.1
+man/rhino.1
diff -Nru rhino-1.7.7.2/debian/rules rhino-1.7.14/debian/rules
--- rhino-1.7.7.2/debian/rules	2021-02-08 11:39:26.000000000 +0100
+++ rhino-1.7.14/debian/rules	2023-04-06 13:10:20.000000000 +0200
@@ -1,23 +1,26 @@
 #!/usr/bin/make -f
 
-VERSION  = $(shell dpkg-parsechangelog --show-field Version | sed -rne 's/([^-]+).*/\1/p')
-BUILDDIR = build/rhino$(VERSION)
+export JH_JAR_EXTRA=META-INF/services/javax.script.ScriptEngineFactory org/mozilla/javascript
 
 %:
 	dh $@ --with javahelper --with maven-repo-helper
 
 override_dh_auto_build:
-	dh_auto_build -- jar javadoc -Dsource-level=7 -Dtarget-jvm=7
+	mkdir -p META-INF/services
+	mkdir -p org/mozilla/javascript/tools/resources/
+	mkdir -p org/mozilla/javascript/tools/debugger/
+	cp src/META-INF/services/javax.script.ScriptEngineFactory META-INF/services/
+	cp -r src/org/mozilla/javascript/resources org/mozilla/javascript/
+	cp toolsrc/org/mozilla/javascript/tools/resources/*.properties org/mozilla/javascript/tools/resources/
+	cp toolsrc/org/mozilla/javascript/tools/debugger/test.js org/mozilla/javascript/tools/debugger/
+	jh_build --javacopts='--release 8' --no-javadoc js.jar src toolsrc xmlimplsrc
+	$(RM) -r META-INF/services
+	$(RM) -r org/mozilla/javascript
 
 override_dh_installchangelogs:
 	dh_installchangelogs -- RELEASE-NOTES.md
 
 override_mh_install:
-	mh_installpom -plibrhino-java maven/maven-pom.xml --no-parent --relocate=rhino:js
-	mh_installjar -plibrhino-java -l maven/maven-pom.xml --usj-name=js $(BUILDDIR)/js.jar
+	mh_installpom -plibrhino-java debian/pom.xml --no-parent --relocate=rhino:js
+	mh_installjar -plibrhino-java -l debian/pom.xml --usj-name=js $(CURDIR)/js.jar
 
-override_dh_install:
-	dh_install
-
-	mv $(BUILDDIR)/javadoc $(BUILDDIR)/api
-	dh_install -plibrhino-java-doc $(BUILDDIR)/api /usr/share/doc/rhino/
diff -Nru rhino-1.7.7.2/debian/source/lintian-overrides rhino-1.7.14/debian/source/lintian-overrides
--- rhino-1.7.7.2/debian/source/lintian-overrides	2020-09-03 13:36:47.000000000 +0200
+++ rhino-1.7.14/debian/source/lintian-overrides	2023-04-06 13:10:20.000000000 +0200
@@ -1,4 +1,2 @@
 # The tests contain non obfuscated JS files with really long lines
-rhino source: source-contains-prebuilt-javascript-object testsrc/*
-rhino source: source-is-missing testsrc/*
-rhino source: insane-line-length-in-source-file testsrc/*
+source-is-missing
Binary files /tmp/GTc5eQkff5/rhino-1.7.7.2/docs/compat/favico.ico and /tmp/mPzyKKlCwi/rhino-1.7.14/docs/compat/favico.ico differ
diff -Nru rhino-1.7.7.2/gradle.properties rhino-1.7.14/gradle.properties
--- rhino-1.7.7.2/gradle.properties	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/gradle.properties	2022-01-06 22:57:21.000000000 +0100
@@ -1,7 +1,8 @@
-#Sun, 26 Apr 2015 10:43:55 +0300
 rootProject.name=rhino
 group=org.mozilla
-version=1.7.7.2
+version=1.7.14
 buildDir=buildGradle
 mavenSnapshotRepo=https://oss.sonatype.org/content/repositories/snapshots
-mavenReleaseRepo=https://oss.sonatype.org/service/local/staging/deploy/maven
+mavenReleaseRepo=https://oss.sonatype.org/service/local/staging/deploy/maven2/
+org.gradle.caching=true
+org.gradle.parallel=true
diff -Nru rhino-1.7.7.2/man/rhino.1 rhino-1.7.14/man/rhino.1
--- rhino-1.7.7.2/man/rhino.1	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/man/rhino.1	2022-01-06 22:57:21.000000000 +0100
@@ -2,7 +2,7 @@
 .\" First parameter, NAME, should be all caps
 .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
 .\" other parameters are allowed: see man(7), man(1)
-.TH RHINO 1 "February  12, 2005"
+.TH RHINO 1 "December  11, 2021"
 .\" Please adjust this date whenever revising the manpage.
 .\"
 .\" Some roff macros, for reference:
@@ -37,14 +37,34 @@
 
 .SH OPTIONS
 
+.IP -debug\fP
+Generate debug information. This will set the optimization level to zero.
 .IP -e\ \fIscript_source\fP
 Executes script_source as a JavaScript script.
+.IP -encoding\ \fIcharacterEncoding\fP
+Set the character encoding to apply in case it could not be detected.
 .IP -f\ \fIscript_filename_or_url\fP
 Reads script_filename_or_url content and execute it as a JavaScript script.
+.IP -fatal-warnings\fP
+Set warnings as errors.
+.IP -help,\ -?\fP
+Display help.
+.IP -modules\ \fImodule\fP
+Add a module to the path.
 .IP -opt,\ -O\ \fIoptLevel\fP
-Optimizes at level optLevel, which must be an integer between 0 and 9. 
+Optimizes at level optLevel, which must be an integer between 0 and 9.
+.IP -require\fP
+Use require().
+.IP -sandbox\fP
+The created require() instances will be sandboxed.
+.IP -sealedlib\fP
+Use a sealed standard library.
+.IP -strict\fP
+Set strict mode.
 .IP -version\ \fIversionNumber\fP
-Specifies the language version to compile with. The string versionNumber must be one of 100, 110, 120, 130, or 140. See JavaScript Language Versions for more information on language versions. 
+Specifies the language version to compile with. It must be one of 100, 110, 120, 130, 140, 150, 160, 170, 180 or 200.
+.IP -w\fP
+Report warnings.
 
 .SH PREDEFINED PROPERTIES
 
@@ -70,6 +90,10 @@
 Open an input connection to the given string url, read all its bytes and convert them to a string using the specified character coding or default character coding if explicit coding argument is not given.
 .IP runCommand(\fIcommandName,[arg,\&.\&.\&.][options]\fP)
 Execute the specified command with the given argument and options as a separate process and return the exit status of the process. For details, see JavaDoc for org.mozilla.javascript.tools.shell.Global#runCommand.
+.IP setTimeout(\fIfunction[,delay,arg,\&.\&.\&.]\fP)
+Execute "function" after "delay" milliseconds. (If "delay" is not set, then it is set to 0.) Pass any trailing parameters as arguments. Timeouts run after the current function is done executing, and in the interactive shell, only after the shell is exited. Return an ID that may be passed to "clearTimeout".
+.IP clearTimeout(\fIid\fP)
+Cancel a timeout set using "setTimeout".
 .IP serialize(\fIobject,filename\fP)
 Serialize the given object to the specified file.
 .IP spawn(\fIfunctionOrScript\fP)
@@ -79,13 +103,15 @@
 .IP quit()
 Quit shell. The shell will also quit in interactive mode if an end-of-file character is typed at the prompt.
 .IP version(\fI[number]\fP)
-Get or set JavaScript version number. If no argument is supplied, the current version number is returned. If an argument is supplied, it is expected to be one of 100, 110, 120, 130, or 140 to indicate JavaScript version 1.0, 1.1, 1.2, 1.3, or 1.4 respectively.
+Get or set JavaScript version number. If no argument is supplied, the current version number is returned. If an argument is supplied, it is expected to be one of 100, 110, 120, 130, 150, 160, 170, 180 or 200 to indicate JavaScript version 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8 or ECMAScript 6 respectively.
+.IP write(\fI[expr\&.\&.\&.]\fP)
+Evaluate and print expressions like in "print", but without the trailing newline.
 
 .SH SEE ALSO
-The online documentation under
-.UR http://www.mozilla.org/rhino/shell.html
-.I http://www.mozilla.org/rhino/shell.html
+The archived online documentation under
+.UR https://web.archive.org/web/20210507045220/https://developer.mozilla.org/en-US/docs/Mozilla/Projects/Rhino/Shell
+.I https://developer.mozilla.org/en-US/docs/Mozilla/Projects/Rhino/Shell
 .UE
 
 .SH AUTHOR
-This manual page was written by Wolfgang Baer <WBaer at gmx.de>.
+This manual page was created by Wolfgang Baer <WBaer at gmx.de>, see Git history for later modifications.
diff -Nru rhino-1.7.7.2/maven/maven-pom.xml rhino-1.7.14/maven/maven-pom.xml
--- rhino-1.7.7.2/maven/maven-pom.xml	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/maven/maven-pom.xml	1970-01-01 01:00:00.000000000 +0100
@@ -1,41 +0,0 @@
-<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
-         xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-  <modelVersion>4.0.0</modelVersion>
-
-  <parent>
-    <groupId>org.sonatype.oss</groupId>
-    <artifactId>oss-parent</artifactId>
-    <version>7</version>
-  </parent>
-
-  <groupId>org.mozilla</groupId>
-  <artifactId>rhino</artifactId>
-  <name>Mozilla Rhino</name>
-  <version>1.7.7.1</version>
-
-  <packaging>jar</packaging>
-  <description>
-      Rhino is an open-source implementation of JavaScript written entirely in Java. It is typically
-      embedded into Java applications to provide scripting to end users.
-  </description>
-  <url>https://developer.mozilla.org/en/Rhino</url>
-
-  <licenses>
-    <license>
-      <name>Mozilla Public License, Version 2.0</name>
-      <url>http://www.mozilla.org/MPL/2.0/index.txt</url>
-    </license>
-  </licenses>
-
-  <scm>
-    <connection>scm:git:git at github.com:mozilla/rhino.git</connection>
-    <developerConnection>scm:git:git at github.com:mozilla/rhino.git</developerConnection>
-    <url>git at github.com:mozilla/rhino.git</url>
-  </scm>
-
-  <organization>
-    <name>The Mozilla Foundation</name>
-    <url>http://www.mozilla.org</url>
-  </organization>
-</project>
diff -Nru rhino-1.7.7.2/maven/maven-snapshot-deploy.sh rhino-1.7.14/maven/maven-snapshot-deploy.sh
--- rhino-1.7.7.2/maven/maven-snapshot-deploy.sh	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/maven/maven-snapshot-deploy.sh	1970-01-01 01:00:00.000000000 +0100
@@ -1,15 +0,0 @@
-#!/bin/sh
-
-deployFile=`ls ../buildGradle/libs/rhino*.jar`
-
-if [ ! -f $deployFile ]
-then
-  echo "File cannot be found in $deployFile"
-  exit 2
-fi
-
-mvn deploy:deploy-file \
-  -Dfile=${deployFile} \
-  -DpomFile=maven-pom.xml \
-  -DrepositoryId=sonatype-nexus-snapshots \
-  -Durl=https://oss.sonatype.org/content/repositories/snapshots/
diff -Nru rhino-1.7.7.2/maven/maven-staging-deploy.sh rhino-1.7.14/maven/maven-staging-deploy.sh
--- rhino-1.7.7.2/maven/maven-staging-deploy.sh	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/maven/maven-staging-deploy.sh	1970-01-01 01:00:00.000000000 +0100
@@ -1,51 +0,0 @@
-#!/bin/sh
-
-vers=`egrep '^version:' ../build.properties | awk '{print $2}'`
-
-echo "Deploying ${vers}"
-
-pom=maven-pom.xml
-jsjar=../buildGradle/libs/rhino-${vers}.jar
-echo "Installing ${jsjar}"
-srczip=../buildGradle/libs/rhino-${vers}-sources.jar
-echo "Sources are ${srczip}"
-doczip=../buildGradle/libs/rhino-${vers}-javadoc.jar
-echo "Javadoc is ${doczip}"
-
-if [ ! -f $jsjar ]
-then
-  echo "Missing js.jar"
-  exit 1
-fi
-
-if [ ! -f $srczip ]
-then
-  echo "Missing rhino-${vers}-sources.zip. Run \"ant source-zip\"."
-  exit 2
-fi
-
-if [ ! -f $doczip ]
-then
-  echo "Missing javadoc.zip. Run \"ant javadoc\"."
-  exit 3
-fi
-
-mvn gpg:sign-and-deploy-file \
-  -Dfile=${jsjar} \
-  -DpomFile=${pom} \
-  -DrepositoryId=sonatype-nexus-staging \
-  -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ 
-
-mvn gpg:sign-and-deploy-file \
-  -Dfile=${srczip} \
-  -DpomFile=${pom} \
-  -DrepositoryId=sonatype-nexus-staging \
-  -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/  \
-  -Dclassifier=sources
-
-mvn gpg:sign-and-deploy-file \
-  -Dfile=${doczip} \
-  -DpomFile=${pom} \
-  -DrepositoryId=sonatype-nexus-staging \
-  -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/  \
-  -Dclassifier=javadoc
diff -Nru rhino-1.7.7.2/NOTICE-tools.txt rhino-1.7.14/NOTICE-tools.txt
--- rhino-1.7.7.2/NOTICE-tools.txt	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/NOTICE-tools.txt	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,37 @@
+Rhino is licensed subject to the terms of the Mozilla Public License, v. 2.0.
+See "LICENSE.txt" for the text of the license.
+
+The files in toolsrc/org/mozilla/javascript/tools/debugger/treetable (runtime
+package org.mozilla.javascript.tools.debugger.treetable) are available under
+the following license:
+
+----
+
+Copyright 1997, 1998 Sun Microsystems, Inc.  All Rights Reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+  - Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+  - Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+  - Neither the name of Sun Microsystems nor the names of its
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff -Nru rhino-1.7.7.2/README.md rhino-1.7.14/README.md
--- rhino-1.7.7.2/README.md	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/README.md	2022-01-06 22:57:21.000000000 +0100
@@ -1,6 +1,6 @@
 # Rhino: JavaScript in Java
 
-![Rhino](https://developer.mozilla.org/@api/deki/files/832/=Rhino.jpg)
+<a title="Rodrigo J De Marco, CC0, via Wikimedia Commons" href="https://commons.wikimedia.org/wiki/File:Rhino_(234581759).jpeg"><img width="384" alt="Rhino (234581759)" src="https://upload.wikimedia.org/wikipedia/commons/thumb/4/4f/Rhino_%28234581759%29.jpeg/512px-Rhino_%28234581759%29.jpeg"></a>
 
 Rhino is an implementation of JavaScript in Java.
 
@@ -15,23 +15,34 @@
 <tr><td><a href="https://github.com/mozilla/rhino/releases/tag/Rhino1_7_6_RELEASE">Rhino 1.7.6</a></td><td>April 15, 2015</td></tr>
 <tr><td><a href="https://github.com/mozilla/rhino/releases/tag/Rhino1_7_7_RELEASE">Rhino 1.7.7</a></td><td>June 17, 2015</td></tr>
 <tr><td><a href="https://github.com/mozilla/rhino/releases/tag/Rhino1_7_7_1_RELEASE">Rhino 1.7.7.1</a></td><td>February 2, 2016</td></tr>
-<tr><td><a href="https://github.com/mozilla/rhino/releases/tag/Rhino1_7_7_2_RELEASE">Rhino 1.7.7.2</a></td><td>August 24, 2017</td></tr>
+<tr><td><a href="https://github.com/mozilla/rhino/releases/tag/Rhino1_7_7_2_Release">Rhino 1.7.7.2</a></td><td>August 24, 2017</td></tr>
+<tr><td><a href="https://github.com/mozilla/rhino/releases/tag/Rhino1_7_8_Release">Rhino 1.7.8</a></td><td>January 22, 2018</td></tr>
+<tr><td><a href="https://github.com/mozilla/rhino/releases/tag/Rhino1_7_9_Release">Rhino 1.7.9</a></td><td>March 15, 2018</td></tr>
+<tr><td><a href="https://github.com/mozilla/rhino/releases/tag/Rhino1_7_10_Release">Rhino 1.7.10</a></td><td>April 9, 2018</td></tr>
+<tr><td><a href="https://github.com/mozilla/rhino/releases/tag/Rhino1_7_11_Release">Rhino 1.7.11</a></td><td>May 30, 2019</td></tr>
+<tr><td><a href="https://github.com/mozilla/rhino/releases/tag/Rhino1_7_12_Release">Rhino 1.7.12</a></td><td>January 13, 2020</td></tr>
+<tr><td><a href="https://github.com/mozilla/rhino/releases/tag/Rhino1_7_13_Release">Rhino 1.7.13</a></td><td>September 2, 2020</td></tr>
+<tr><td><a href="https://github.com/mozilla/rhino/releases/tag/Rhino1_7_14_Release">Rhino 1.7.14</a></td><td>January 6, 2022</td></tr>
 </table>
 
 [Release Notes](./RELEASE-NOTES.md) for recent releases.
 
-[Compatability table](http://mozilla.github.io/rhino/compat/engines.html) which shows which advanced JavaScript
-features from ES5, 6, and 7 are implemented in Rhino.
+[Compatibility table](https://mozilla.github.io/rhino/compat/engines.html) which shows which advanced JavaScript
+features from ES6, and ES2016+ are implemented in Rhino.
+
+[![GitHub Action Status](https://github.com/mozilla/rhino/actions/workflows/gradle.yml/badge.svg)](https://github.com/mozilla/rhino/actions/workflows/gradle.yml)
+
+[![Mozilla](https://circleci.com/gh/mozilla/rhino.svg?style=shield)](https://app.circleci.com/pipelines/github/mozilla/rhino)
 
 ## Documentation
 
 Information for script builders and embedders:
 
-[https://developer.mozilla.org/en-US/docs/Rhino_documentation](https://developer.mozilla.org/en-US/docs/Rhino_documentation)
+[Archived](http://web.archive.org/web/20210304081342/https://developer.mozilla.org/en-US/docs/Mozilla/Projects/Rhino/Documentation)
 
 JavaDoc for all the APIs:
 
-[http://mozilla.github.io/rhino/javadoc/index.html](http://mozilla.github.io/rhino/javadoc/index.html)
+[https://javadoc.io/doc/org.mozilla/rhino](https://javadoc.io/doc/org.mozilla/rhino)
 
 More resources if you get stuck:
 
@@ -45,11 +56,14 @@
 ```
 ./gradlew jar
 ```
-Build and create `Rhino` jar in the `build/libs` directory.
+Build and create `Rhino` jar in the `buildGradle/libs` directory.
 ```
+git submodule init
+git submodule update
 ./gradlew test
 ```
-Build and run all the tests.
+Build and run all the tests, including the official [ECMAScript Test Suite](https://github.com/tc39/test262).
+See [Running tests](testsrc/README.md) for more detailed info about running tests.
 ```
 ./gradlew testBenchmark
 ```
@@ -73,29 +87,80 @@
 ```
 5. Increase version and add `-SNAPSHOT` to it in `gradle.properties` in project root folder.
 6. Push `gradle.properties` to `GitHub`
-   
+
 ## Running
 
 Rhino can run as a stand-alone interpreter from the command line:
 ```
-java -jar buildGradle/libs/rhino-1.7.7.2.jar
-Rhino 1.7.7.2 2017 08 24
+java -jar buildGradle/libs/rhino-1.7.12.jar -debug -version 200
+Rhino 1.7.9 2018 03 15
 js> print('Hello, World!');
 Hello, World!
 js>
 ```
+There is also a "rhino" package for many Linux distributions as well as Homebrew for the Mac.
+
 You can also embed it, as most people do. See below for more docs.
 
+### Java 16 and later
+
+If you are using a modular JDK that disallows the reflective access to
+non-public fields (16 and later), you may need to configure the JVM with the
+[`--add-opens`](https://docs.oracle.com/en/java/javase/17/migrate/migrating-jdk-8-later-jdk-releases.html#GUID-12F945EB-71D6-46AF-8C3D-D354FD0B1781)
+option to authorize the packages that your scripts shall use, for example:
+```
+--add-opens java.desktop/javax.swing.table=ALL-UNNAMED
+```
+
 ## Issues
 
 Most issues are managed on GitHub:
 
 [https://github.com/mozilla/rhino/issues](https://github.com/mozilla/rhino/issues)
 
+## Contributing PRs
+
+To submit a new PR, please use the following process:
+
+* Ensure that your entire build passes "./gradlew check". This will include
+code formatting and style checks and runs the tests.
+* Please write tests for what you fixed, unless you can show us that existing
+tests cover the changes. Use existing tests, such as those in
+"testsrc/org/mozilla/javascript/tests", as a guide.
+* If you fixed ECMAScript spec compatibility, take a look at test262.properties and see
+if you can un-disable some tests.
+* Push your change to GitHub and open a pull request.
+* Please be patient as Rhino is only maintained by volunteers and we may need
+some time to get back to you.
+* Thank you for contributing!
+
+### Code Formatting
+
+Code formatting was introduced in 2021. The "spotless" plugin will fail your
+build if you have changed any files that have not yet been reformatted.
+Please use "spotlessApply" to reformat the necessary files.
+
+If you are the first person to touch a big file that spotless wants to make
+hundreds of lines of changes to, please try to put the reformatting changes
+alone into a single Git commit so that we can separate reformatting changes
+from more substantive changes.
+
+> **Warning:** If you build with Java 16 or later, you need to apply a
+> workaround for a "spotless" issue. Otherwise, the task will be disabled
+> and your PR may fail.
+> 
+> The following must be added to your `gradle.properties`.
+> ```
+> org.gradle.jvmargs=--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \
+>  --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \
+>  --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \
+>  --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \
+>  --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
+> ```
+> For more details, see https://github.com/diffplug/spotless/issues/834#issuecomment-819118761
+
 ## More Help
 
 The Google group is the best place to go with questions:
 
 [https://groups.google.com/forum/#!forum/mozilla-rhino](https://groups.google.com/forum/#!forum/mozilla-rhino)
-
-
diff -Nru rhino-1.7.7.2/RELEASE-NOTES.md rhino-1.7.14/RELEASE-NOTES.md
--- rhino-1.7.7.2/RELEASE-NOTES.md	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/RELEASE-NOTES.md	2022-01-06 22:57:21.000000000 +0100
@@ -1,5 +1,586 @@
+# Rhino 1.7.14
+## January 6, 2022
+
+# Highlights
+## Features
+#### ECMAScript features
+* #160 Promise support (@gbrail)
+* #837 BigInt support (@tuchida)
+* #243 Template Literal support (@p-bakker)
+* #879 String.raw (@tonygermano)
+* #977 JSON superset (@tuchida)
+* #932 globalThis (@p-bakker)
+* #838 Exponential operator (@tuchida)
+* #853 Short-hand property names (@tuchida)
+* #902 Object.values / Object.entries / Object.fromEntries (@rPraml)
+* #883 Number.EPSILON (@tonygermano)
+
+#### Non-ECMAScript features
+* #153 stack property on Error Constructor (@gbrail)
+* #888 support for Mozilla-styled Stack formatting (@rbri)
+
+[All features](https://github.com/mozilla/rhino/issues?q=milestone%3A%22Release+1.7.14%22+label%3Afeature+is%3Aclosed)
+
+## Bugs
+[All bug fixes](https://github.com/mozilla/rhino/issues?q=milestone%3A%22Release+1.7.14%22+label%3Abug)
+
+## Performance
+[All performance enhancements](https://github.com/mozilla/rhino/issues?q=milestone%3A%22Release+1.7.14%22+label%3APerformance)
+
+## Java Interop
+* #839 JavaScript for-of loop support for Java Iterables (@tuchida)
+* #860 / #857 JSON.stringify support on Java Objects (@tonygermano / @rPraml)
+* #1031 delete operator and .length setting support in JavaScript on Java Lists (@rPraml)
+* #901 java.util.subList() support on JavaScript Arrays in Java (@rPraml)
+* #889 Automatically increase size of Java List instances on .put(...) if required (@rPraml)
+
+[All Java Interop related cases](https://github.com/mozilla/rhino/issues?q=milestone%3A%22Release+1.7.14%22+label%3A%22Java+Interop%22)
+
+## Embedding Rhino
+* #864 Context now implements Closable (@gbrail)
+* #865 Introduction of LambdaFunction and LambdaConstructor, to be used to represent Java lambda functions as native JavaScript functions and also can be used to construct an entire class out of lambdas (@gbrail)
+* #911 Throw InternalError instead of wrapped JavaException if thrown Java Exception class is not visible due to class shutter (@youngj)
+
+[All Rhino embedding related cases](https://github.com/mozilla/rhino/issues?q=milestone%3A%22Release+1.7.14%22+label%3A%22embedding+Rhino%22+)
+
+## Test262 suite
+* Running against a much newer version of Test262 suite
+* Improved documentation for running the Test262 suite + more options to make running the tests easier & faster
+* #930 Support added for automatically regenerating the test262.properties file based on actual passage of tests
+* #930 Improved feedback about reason of test failures
+
+## Distribution
+* #873 Automatic module names
+
+## Internals
+* #878 Removed idSwitch
+* #896 SlotMap and Slot refactoring
+* #922 Started extracting logic related to Abstract Operations as defined by the ECMAScript specification 
+
+## Misc.
+* #661 Rhino now listed in the [kangax ES6 Compatibility table](https://kangax.github.io/compat-table/es6) (must select the `Show obsolete platforms` in the upperleft corner)
+* #661 Rhino now available as a compilation target in Babel through @babel/preset-env:
+```
+{
+  "targets": {
+    "rhino": "1.7.13"
+  }
+}
+```
+* Introduced Java Code Formatting through spotless
+* Moved to CircleCI (instead of Travis)
+* Enabled Gitlab CI, running tests on multiple Java versions'
+  
+## Thanks!
+
+This release contains more than 350 commits from 23 contributors. Thanks to everyone who helped!
+
+# Rhino 1.7.13
+## September 2, 2020
+
+### Script Engine support
+
+Now that Nashorn has been deprecated, a number of people have asked about using
+Rhino with the standard Java "ScriptEngine" interface. This release supports that.
+
+However, in order to avoid breaking existing code, the script engine is
+shipped in a separate JAR. Use the "rhino-engine" jar along with the
+standard "rhino" jar to include this feature.
+
+### Generator Support
+
+This release supports generators based on the ES6-standard "function *"
+syntaxt.
+
+### Other important changes
+
+This release also includes a number of quality and consistency fixes from five contributors.
+As always, check out the [compatibility table](http://mozilla.github.io/rhino/compat/engines.html)
+to see where Rhino stands today.
+
+Gregory Brail (18):
+*     Start on 1.7.13.
+*     Add a build config for CircleCi.
+*     Upgrade Gradle version to 6.5.
+*     Update max workers.
+*     Add support for ES6 generators.
+*     Make "GeneratorFunction" pattern work in interpreted mode.
+*     Complete implementation of GeneratorFunction.
+*     Diagnostics to discover test timeouts.
+*     Implement standard Java ScriptEngine
+*     Change MozillaSuiteBenchmark to not fork threads.
+*     Try to improve performance of MozillaSuiteTest
+*     Disable some very slow tests
+*     Start using JMH for benchmarks.
+*     Many small fixes suggested by FindBugs and other linters
+*     Turn off all the Mozilla tests that use the "BigO" function.
+*     Move "BodyCodegen" into a file with the appropriate name.
+*     Add feature flag for changes to Function.__proto__
+*     Make __proto__ more closely match the spec
+
+Karl Tauber (2):
+*     Debugger fixes for FlatLaf (https://github.com/JFormDesigner/FlatLaf): - make renderer tree row height same as table row height - increase monospaced font size in script source and evaluation view if L&F uses larger font - remove renderer tree border if L&F sets one (built in L&F do not)
+*     Debugger: fix NPE in variables view when expanding "CallSite"
+
+Sylvain Jermini (7):
+*     improve java.util.{List,Map} interop
+*     travis: switch from trusty to xenial + set explicit -Xss in tests
+*     try to fix circle, increase Xss
+*     Fix failing string.trim.doctest in java11.
+*     NativeDate: DateFormat, use explicit pattern, has the default has changed from java8 to 9. See https://stackoverflow.com/q/53317365
+*     add java11 to travis test matrix
+*     various fixes so the javadoc linter is happy
+
+hjx??? (2):
+*     Add String.fromCodePoint()
+*     fromCharCode optimize
+
+ian4hu (5):
+*     Add String.prototype.trimStart String.prototype.strimEnd
+*     style: code style
+*     test: string test with hex code instead of literal
+*     remove unused StringBuilder
+*     fix tests in test262/built-ins/String/fromCodePoint/*
+
+leela52452 (1):
+*     fix OWASP Cheat Sheet markdown format
+
+RBRi (48):
+*     switch value and done
+*     make some method protected to support rhino-external implementations
+*     NativeArrayBuffer slice() length is 2
+*     fix String.indexOf and String.includes when searching for an empty st? (#747)
+*     fix string.split with limit 0
+*     fix for issue #665 (maybe we have to adjust the version switch to version 1_6)
+*     fix for the recursion detection when converting an array into a string
+*     fix #670
+*     add testcase for issue #656
+*     Symbol.length is 0 fixes #648
+*     add testcase for issue #651
+*     fix type o the expected value
+*     improve seal() and freeze() processing; fixes #174
+*     An error should be thrown when defining a property for a read-only variable in strict mode fixes 573
+*     code cleanup
+*     Do not save/share an instance of NativeArrayBuffer in a static variable. This introduces really strange side effects, because the instance is available (and changeable) from javascript code. These changes are 'persistent' in a way that starting a fresh rhino instance still uses this changed object.
+*     various fixes for array calls using this pattern Array.prototype.foo.call(null, ....);
+*     fix issue #648
+*     fix Object.getOwnPropertyDescriptor for index properties on native strings
+*     Function.__proto__ ignores write access
+*     improved regexp parser based on commit 2164382abe078ea2024b9dff7fe416a78e3a668f from anba
+*     fix handling of undefined parameter value in String.normalize()
+*     it should not be possible to change the [[Prototype]]  of a non-extensible object; some cleanup
+*     add version guard
+*     fix all this-value-not-obj-coercible.js tests for string
+*     checkstyle fixes
+*     fix test suite setup
+*     use the RangeError construction helper
+*     improved handling of negative ArrayBuffer size fixes #708
+*     in ES6 TypedArray constructors are only callable via new
+*     avoid some auto boxing use Double.valueOf instead of new some cleanup try to optimize the code a bit to avoid unnecessary conversations and Double object creation make some methods static
+*     regular expressions are not functions in the context of string replace fixes #726
+*     improved regex range handling
+*     do not inherit strict mode when parsing a function body
+*     code style fix
+*     fix wrong start object for getter in Object.assign
+*     use Undefined.isUndefined()
+*     String.prototype[Symbol.iterator].call(undefined) has to throw because undefined is not coercible
+*     enable more test cases
+*     reduce auto boxing to be able to better control this and avoid boxing if possible
+*     make a bunch  of methods static
+*     code cleanup
+*     make the inner class static (this makes also SpotBugs happy)
+*     Object.setPrototypeOf() arg[0] has to be coercible
+*     fix one more case
+*     match
+*     search
+*     throw if the lastIndex prop of an regex is readonly
+
+# Rhino 1.7.12
+## January 13, 2020
+
+### XML external entities disabled by default
+
+As of this release, Rhino makes "XML external entity injections" more difficult
+by disabling fetching of external DTDs and stylesheets by default,
+as recommended in the [OWASP Cheat Sheet](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.md). 
+Even though this may break some existing projects, the fact that this
+vulnerability is in the OWASP top 10 makes it important enough to change
+the default.
+
+Developers who still need this old capability can re-enable it by setting the
+Context feature flag FEATURE_ENABLE_XML_SECURE_PARSING to false. (The default
+is true.)
+
+### New JAR for embedding use cases
+
+This release also includes a second JAR artifact, "rhino-runtime.jar". This is
+simply the existing Rhino JAR with the "tools" source directory excluded. 
+This directory includes the Rhino shell as well as the default "Global" 
+object, which includes capabilities to load and process external source
+code.
+
+Since some automated source-scanning tools mark these capabilties as insecure,
+this new JAR provides a way to only include the parts of Rhino that embedders
+typically need without pulling in additional capabilities.
+
+Developers who typically embed "rhino.jar" might consider embedding "rhino-runtime.jar"
+instead if they do not need all this.
+
+Thanks to the following developers for the contributions below!
+
+Aditya Pal (1):
+*     Fix syntax error for comments in array (#607)
+
+Chris Smith (1):
+*     Adding secure configuration for XML parsers (#600)
+
+Gregory Brail (12):
+*     Update versions for 1.7.12 release.
+*     Fix a code generation bug for generators.
+*     Fix "fall through" comment.
+*     Fix static analysis around NaN values.
+*     More isNaN fixes and one rounding bug.
+*     Make XML processor configuration more robust.
+*     Enable SpotBugs plugin.
+*     Fix minor static analysis findings.
+*     Increase Travis timeout.
+*     Disable more flaky "BigO" tests.
+*     Fix handling of "return" in iterators.
+*     Undo setting some members "final".
+
+Ivan Di Francesco (1):
+*     Fix warnings (#596)
+
+Roland Praml (2):
+*     FIX: NativeJavaObject.getDefaultValue recognizes numbers correctly
+*     #511 fixing InterfaceAdapter abstract name lookup.
+
+Stijn Kliemesch (7):
+*     Private static method ScriptRuntime.enumInitOrder(Context,IdEnumeration) no longer expects given IdEnumeration's property obj to be of type ScriptableObject specifically, only of type SymbolScriptable.
+*     Added testclass IterableTest to test iterable implementations, currently with one testcase for a host object, specifically one that uses Array Iterator.
+*     Added more tests to IterableTest.
+*     Fix for #616 (#617)
+*     Fixes for calling several Object.prototype members.
+*     Fixed dynamic scoping for implementations of Object.create and Object.defineProperties
+*     Testcase for dynamic scoping and Object.create.
+
+nename0 (2):
+*     Fix Array.include return a wrapped Boolean
+*     implement Array.includes to align to specs
+
+RBRi (20):
+*     fix for Map/Set working with ConsString as key also; closes #583
+*     fix propertyIsEnumerable when using an index to access string; closes #582
+*     ignore surplus search/match/replace parameters; closes #581
+*     add support for setPrototypeOf
+*     fixed imports
+*     RangeError should be throw if the argument of Number.prototype.toFixed is less than 0 fixes #587
+*     fix interpreter fallback when using streams (fixes #592)
+*     Parser already always reads the reader into a string. Move this reader handling to the Context to be able to fall back to the interpreter in all cases.
+*     fix imports
+*     functions declared as var f = function f() {...} within a function should not impact higher scope variable with the same name
+*     functions declared as var f = function f() {...} within a function should not impact higher scope variable with the same name
+*     fix Boolean(document.all)
+*     many more tests are passing already and some cleanup
+*     add tests for built-ins/ThrowTypeError and built-ins/TypedArray
+*     add tests for built-ins/TypedArrays
+*     fix BYTES_PER_ELEMENT property
+*     fix BYTES_PER_ELEMENT prototype property
+*     fix TypedArray constructor arity
+*     Fix issue with parseInt's handling of leading zeroes
+*     #529 (#628)
+
+# Rhino 1.7.11
+## May 30, 2019
+
+This release includes implementations of a number of missing JavaScript language features,
+including:
+
+* Improvement to the accuracy and reliability of the parser and its associated AST.
+* The Map, Set, WeakMap, and WeakSet classes.
+* More Array functions, including from, of, fill, keys, values, entries, and copyWithin.
+* Many more Math methods.
+* More Object functions, including seal and freeze.
+* Many other bug fixes, as shown below.
+
+In general, Rhino's philosophy for new features and changes is to
+follow the ECMAScript spec, but to use the "language version" on the Context class
+when backward compatibility would be broken.
+
+For example, the Array.prototype.concat function in older versions of Rhino would treat
+any input value as "spreadable" if it has the same constructor as Array. ECMAScript now says
+clearly that this should only happen if the "isConcatSpreadable" symbol is present. In
+this release, the old behavior is disable when the language level is at least the
+"ES6" level (Context.VERSION_ES6, or 200).
+
+Developers working on new code will be happier if they set the language level to
+CONTEXT.VERSION_ES6, or use the "-version 200" flag to the command line tool.
+
+A future release will change the default language version of the command line tool.
+
+This release contains contributions from 15 developers. Thanks for all your hard work!
+
+Attila Szegedi (7):
+*     Improvements to MemberBox (#438)
+*     Labmdify usages of ContextAction
+*     Make ContextAction generic.
+*     API for comparing continuation implementations
+*     Algorithm for structural equality comparison of object graphs
+*     Use structural equality as the equality algorithm for Interpreter.CallFrame, which serves as the NativeContinuation implementation.
+*     Add workarounds for #437 and #449
+
+Dimitar Nestorov (1):
+*     Update README.md
+
+Dirk Hogan (1):
+*     431 update expiry of cached commonjs entity if no change on filesystem
+
+Gregory Brail (18):
+*     Prepare for next iteration.
+*     Support replacing prototype functions of native objects.
+*     Fix NullPointerException in __defineGetter__
+*     Fix a problem with standard objects that have Symbols in their  prototypes.
+*     Implement the built-in Set and Map classes for ES6.
+*     Add WeakMap and WeakSet on top of the Map and Set work.
+*     Upgrade max heap for Gradle tests to 1 GB.
+*     Test cases and a small fix for native arrays.
+*     Implement @@isConcatSpreadable and make Java arrays spreadable
+*     Code review comments for @@isConcatSpreadable.
+*     Make the "Sorting" helper class a proper singleton.
+*     Un-do recent addition to the Scriptable interface.
+*     Update Gradle wrapper version.
+*     Fix flag tests that assume Context is available.
+*     Fix a parser bug with missing semicolon warnings.
+*     Fix a regression introduced recently to MemberBox.
+*     More compatibility fixes for Array.prototype.concat.
+*     Work on Array.of, from, and copyWithin.
+*     Fix a parsing regression.
+
+Igor Kuzmanenko (2):
+*     fixes XmlMemberGet toSource implementation (#483)
+*     fixes position for ParenthesizedExpression nodes (#129)
+
+Markus Sunela (1):
+*     Add manual OSGi manifest
+
+Mozilla-GitHub-Standards (1):
+*     Add Mozilla Code of Conduct file
+
+Nedelcho Delchev (1):
+*     Update README.md
+
+RBRi (2):
+*     some fixes/enhancements for the typed array support (#436)
+*     Array fixes (#467)
+*     fix all javadoc errors and all javadoc html warnings
+*     method Global#version(xxx) should return the new version identifier if the version was updated
+*     add more info to the error message
+*     us the right method name if available
+*     two minor improvements from HtmlUnit code * window list is sorted * Command 'Go to line' added
+*     avoid npe if no file window is available
+*     improve the design for flexible subclassing
+*     remove duplicated check
+*     fix the isSymbol detection to no longer detect the prototype as symbol
+*     we have many more 262 tests passing already - i think we have to use as many tests as possible to check our quality
+*     and some more; now we are at 51093
+*     Use as many test262 tests as possible to check our quality
+*     implement missing Math functions
+*     disable some slow tests
+*     Support for `arguments` object as TypedArray constructor argument this is the same as #297 but includes a simple test.
+*     fix issue 437
+*     use the system line separator for code generation
+*     remove work around for 437
+*     fix #449 also and remove the work around from EqualObjectGraphs
+*     add @Override and some more cleanup
+*     fix ctor called with date arg
+*     valueOf has to be called without any args
+*     fix remaining utc constructor case
+*     minor cleanup
+*     fix native array constructor called with null and same for the setter
+*     code cleanup based on eclipse Photon suggestions
+*     add more delegate methods to MemberBox; name all delegate method using the name of the delegated method
+*     avoid star imports across the codebase
+*     fall back to the interpreter if the byte code generation fails
+*     fix serialization for NativeMap, NativeSet, NativeWeakMap, and NativeWeakSet
+*     scope is only undefined in strict mode; fix special null entries for maps
+*     more config cleanup - use files for excluding; again enable a bunch of tests already running
+*     another VMBridge cleanup step (JDK 1.8 is the minimum at the moment)
+*     another map fix
+*     more detailed tests hack to dump already passing tests exclude one more class of tests more tests are passing
+*     next try to make the travis build pass
+*     array.fill
+*     array.keys, array.entries, array.values
+*     fixes for DataView, including enabling more test cases.
+*     fix freeze/preventExtensions/seal/isFrozen/isExtensible/isSealed for ES6
+*     add padStart and padEnd
+*     make serialVersionUID private
+*     make the test pass on my machine (also from inside eclipse)
+*     fix null/undefined handling add first array includes impl
+*     fix some array length handling border cases
+*     NativeArray cleanup more error output for Test262SuiteTest
+*     fix for #531 - using a symbol as array index crashes with a class cast exception
+*     Update Jacoco version
+*     functions are valid keys for WeakMap/WeakSet
+*     use valueOf
+*     cleanup vm bridge; since we are at java 8 there is no need to check for iterator availability
+*     cleanup member; we are using the executable type instead of member
+*     fix unused import
+*     another jdk check no longer required
+*     fix build
+*     add (modified) test case from #135
+*     first simple version of copyWithin
+*     first array.of impl
+
+Rapha?l Jakse (1):
+*     Test function arity and length properties
+
+Ravi Kishore (1):
+*     Retain of comments and their position in the actual code after parsing. (#465)
+
+Stijn Kliemesch (1):
+*     Added testcase for #510
+
+S?bastien Doeraene (2):
+*     Fix #448: Correctly wrap the result of Math.imul as an Int32.
+*     Fix the conversions in typedarrays.Conversions.
+
+Travis Haagen (2):
+*     Fix bug that caused modified JavaScript files to never be reloaded
+*     Created UrlModuleSourceProviderTest
+
+nabice (2):
+*     Fix #533 AstNode.setParent() causes a position error
+*     test for #533
+
+raphj (1):
+*     Override getArity
+
+stijnkliemesch (1):
+*     Fixes Parser.throwStatement()
+
+# Rhino 1.7.10
+## April 9, 2018
+
+This release fixes a regression introduced in version 1.7.7.2 that caused the
+"propertyIsEnumerable" to throw an exception when used with String and typed array objects,
+and possibly with custom user-written objects as well.
+
+[Issue 415](https://github.com/mozilla/rhino/issues/415)
+
+It contains a few other fixes:
+
+Attila Szegedi (2):
+*     Make as many CallFrame fields as possible final, initialize them in constructor
+*     frame.debuggerFrame != null || frame.idata.itsNeedActivation is identical to frame.useActivation.
+
+Jeremy Whitlock (1):
+*     Missing properties are not enumerable when checking enumerability
+
+# Rhino 1.7.9
+## March 15, 2018
+
+This release fixes a [potential ArrayIndexOutOfBoundsException](https://github.com/mozilla/rhino/issues/390)
+that was introduced in 1.7.8. Since it's potentially pretty serious, projects currently using 1.7.8
+should switch to this new release.
+
+[Issue 390](https://github.com/mozilla/rhino/issues/390)
+
+In addition, there is a new flag on Context called "FEATURE_INTEGER_WITHOUT_DECIMAL_PLACE."
+If set, Rhino will work harder to display numbers in integer form rather than in floating-point
+form. This feature is currently disabled by default, although if it proves popular than we can
+consider enabling it in the future.
+
+[PR 398](https://github.com/mozilla/rhino/pull/398)
+
+At language level "ES6" and above, ToNumber conversion is now more compliant to the spec. (This
+change is disabled for older language levels to prevent a problem with backward compatibility.)
+
+[PR 383](https://github.com/mozilla/rhino/pull/383)
+
+Finally, there are a number of other fixes.
+
+Thanks to all who contributed, both with issues and with code!
+
+Attila Szegedi:
+*     Fix a JavaDoc warning
+
+Ivan Vyshnevskyi:
+*     Make ToNumber(String) conversion more spec-compliant
+*     Report parsing error for default values in destructuring assignments
+
+Michael[tm] Smith:
+*     Add addError(String messageId, int c) method
+*     Add ?illegal character? test to ParserTest
+*     Show word in ?identifier is a reserved word? error
+*     Add ?identifier is a reserved word? test
+
+Oleksandr Maksymenko:
+*     changes to process integer object as integer and long as long, not as double
+
+RBRi:
+*     cleanup the code an try to make it faster (#373)
+
+jhertel:
+*     Correction: Compatability ? Compatibility
+
+# Rhino 1.7.8
+## January 22, 2018
+
+Most important changes in this release:
+
+* JavaScript objects are no longer (somewhat) thread-safe by default
+* Rhino is resistant to "hash flooding" attacks
+* Rhino is only supported for Java 8 and up
+* Rhino only builds with Gradle.
+
+The primary change in this release is that the object storage format has changed
+for objects derived from ScriptableObject (which is nearly all objects).
+
+First, objects are no longer thread-safe by default. (They were thread-safe previously, but
+not in a way that we could prove was 100 percent correct in all cases.) We do not believe
+that the vast majority of Rhino code depended on this capability. 
+
+The feature flag Context.FEATURE_THREAD_SAFE_OBJECTS may be used to enable locking on all
+objects by default in case it is needed. Furthermore, the built-in "sync" function is still
+supported, which can be used to wrap a function in a similar way to the "synchronized" keyword
+in Java.
+
+Second, when an object grows to a large number of properties, the native hash table implementation
+is replaced with java.util.HashMap. This more complex (but slightly slower) hash implementation
+is resistant to hash collisions. This makes Rhino in general resistant to "hash flooding" attacks.
+
+Rhino now depends on Java 8. It also works on Java 9, although a few tests are currently breaking
+around Date parsing and UTF-8 encoding.
+
+Additional changes:
+
+[Issue 290](https://github.com/mozilla/rhino/issues/290) Resist hash flooding attacks
+[Issue 303](https://github.com/mozilla/rhino/issues/303) Arrow function position set error
+[Issue 323](https://github.com/mozilla/rhino/issues/323) Possible OutOfMemory due to
+infinite loop on parsing
+[Issue 341](https://github.com/mozilla/rhino/issues/341) Objects are only thread-safe when
+feature is enabled
+[Issue 351](https://github.com/mozilla/rhino/issues/351) Function-level use-strict breaks
+backward compatibility
+[Issue 357](https://github.com/mozilla/rhino/issues/357) Array.sort() can throw
+ArrayIndexOutOfBoundsException
+[Issue 295](https://github.com/mozilla/rhino/issues/295) Change WrapFactory to only wrap
+"true" primitive types and not subclasses.
+[Issue 377](https://github.com/mozilla/rhino/issues/377) Context initialization in 
+"sealed" mode failed for ES6 language level.
+[PR102](https://github.com/mozilla/rhino/pull/102) Fix regexp parsing for "/0{0/"
+[PR108](https://github.com/mozilla/rhino/pull/108) Attach jsdoc nodes to function params.
+[PR 169](https://github.com/mozilla/rhino/pull/169) Enable calling default method
+on Java 8.
+[PR322](https://github.com/mozilla/rhino/pull/322) Fix static array functions
+[PR353](https://github.com/mozilla/rhino/pull/353) Member box call error.
+[PR355](https://github.com/mozilla/rhino/pull/358) Support array-like parameters to 
+Function.prototype.apply().
+[PR372](https://github.com/mozilla/rhino/pull/372) Improve test262 integration and enable
+many more tests.
+
 # Rhino 1.7.7.2
-## February 1, 2016
+## August 24, 2017
 
 This release contains fixes for a few important bugs that have caught Rhino users out in the
 field.
@@ -24,7 +605,7 @@
 The next release is likely to be 1.7.8.
 
 # Rhino 1.7.7.1
-## February 1, 2016
+## February 2, 2016
 
 This release fixes a few critical bugs that were affecting code in the field:
 
diff -Nru rhino-1.7.7.2/RELEASE-STEPS.md rhino-1.7.14/RELEASE-STEPS.md
--- rhino-1.7.7.2/RELEASE-STEPS.md	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/RELEASE-STEPS.md	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,76 @@
+# Rhino Release Process
+
+## Prepare the repository
+
+Update the version in gradle.properties to the new release version.
+
+Update RELEASE_NOTES.md to include at least a summary of the major features
+of the release. (This is historical, but many find it helpful to have a single
+file in the repo that lists the major changes.)
+
+Update README.md to add a row to the table of releases that points to the
+GitHub release that we'll create in the next step (just follow the pattern
+that's there).
+
+Now might be a good time to run "./gradlew publishToMavenLocal" and use the
+published JARs as a sanity check.
+
+## Update Compatibility Table
+
+The offial Kangax "compat table" now supports Rhino, but it's convenient
+to have our own that shows progress across all releases. Here's how to
+update it:
+
+    git clone -b gh-pages https://github.com/gbrail/node-compat-table.git
+    cd node-compat-table
+    
+Now, edit "rhinoall.sh" to include the new release -- it includes a series
+of lines that fetch old releases, and use your local build of the new one.
+Then, update the table:
+
+    ./rhinoall.sh
+
+The resulting "index.html" can be copied into "docs/compat/engines.html" in 
+this repo.
+
+## Push the Release to GitHub
+
+At this point, the current contents of your directory correspond to the 
+new release. Prepare a pull request containing the changes, submit it,
+and merge it -- the result will be that the head of the "master" branch
+will build your new release.
+
+Update to that branch and create a tag for the release, where XX is a number
+like "1_7_14":
+
+    git pull origin master
+    git tag Rhino_XX_Release
+    git push origin Rhino_XX_Release
+
+Now, on the Rhino "Releases" tab in GitHub, create a release that corresponds
+to the new tag. Include the following:
+
+* A cut and paste of the part of RELEASE_NOTES.md added for the release
+* The three JARs created by "./gradlew.jar"
+* The ZIP file created by "./gradlew distZip"
+* A ZIP of the source will be included automatically by GitHub
+
+## Push the release to Maven Central
+
+The "Publish to Maven Central" action on GitHub Actions will automatically
+build the release, sign the JARs, and push it to oss.sonatype.org in the
+"org.mozilla" area. Log in to oss.sonatype.org, verify that all the checks
+that happen there were successful, and "close" the release. It will appear
+on Maven Central a few hours later.
+
+## Update Homebrew
+
+The Homebrew team for Mac does not necessarily pick up Rhino releases 
+automatically. It may be necessary to submit a PR to the "homebrew/homebrew"
+repo in GitHub for a change to the file "Library/Formula/rhino.rb".
+
+## Prepare for Next Release
+
+Now it's time to move to the next "SNAPSHOT" release. Update gradle.properties,
+create a PR, and push the new PR. Now development can proceeed anew!
+
diff -Nru rhino-1.7.7.2/release-steps.txt rhino-1.7.14/release-steps.txt
--- rhino-1.7.7.2/release-steps.txt	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/release-steps.txt	1970-01-01 01:00:00.000000000 +0100
@@ -1,37 +0,0 @@
-Update version in:
-	src/manifest
-	build.properties
-	gradle.properties
-	maven/maven-pom.xml
-
-Paste release notes into RELEASE_NOTES.md:
-	git shortlog LAST_TAG.. | sed 's/^ /*/'
-
-Paste release info into README.md
-	Copy README into gh-pages
-	Extract build/*/javadoc.zip to gh-pages/javadoc
-
-Re-run compat-table "engine.js"
-	Copy HTML to gh-pages/compat
-
-./gradlew publishToMavenLocal distZip 
-cd maven
-./maven-staging-deploy.sh
-
-Go to oss.sonatype.org
-	Find "org.mozilla" staging repo, close, release, and drop.
-
-git tag for new release
-
-Update release in GitHub
-	Paste release notes, upload "rhinoXXX.zip" and "js.jar"
-
-Update to new snapshot version in:
-	src/manifest
-	build.properties
-	gradle.properties
-	maven/maven-pom.xml
-
-Update the Homebrew formula. Submit a PR for:
-	Homebrew/homebrew
-		Library/Formula/rhino.rb
diff -Nru rhino-1.7.7.2/spotbugs-exclude.xml rhino-1.7.14/spotbugs-exclude.xml
--- rhino-1.7.7.2/spotbugs-exclude.xml	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/spotbugs-exclude.xml	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<FindBugsFilter>
+  <!-- In general, do not run this on tests or generated benchmark classes -->
+  <Match>
+    <Source name="~.*\/testsrc\/.*"/>
+  </Match>
+  <Match>
+    <Source name="~.*\\testsrc\\.*"/>
+  </Match>
+  <Match>
+    <Source name="~.*\/examples\/.*"/>
+  </Match>
+  <Match>
+    <Source name="~.*\\examples\\.*"/>
+  </Match>
+  <Match>
+    <Class name="~org\.mozilla\.javascript\.benchmarks\.jmh_generated.*"/>
+  </Match>
+
+  <!-- Things below are legit exemptions -->
+  <Match>
+    <!-- Existing "Token" constants have values to 255 -->
+    <Class name="org.mozilla.javascript.Interpreter" />
+    <Bug pattern="INT_BAD_COMPARISON_WITH_SIGNED_BYTE" />
+  </Match>
+  <Match>
+    <!-- Let this class do its job -->
+    <Class name="org.mozilla.javascript.DToA" />
+    <Bug pattern="FE_FLOATING_POINT_EQUALITY" />
+  </Match>
+  <Match>
+    <!-- A bigger task to take this on across the codebase -->
+    <Bug pattern="DM_DEFAULT_ENCODING" />
+  </Match>
+  <Match>
+    <!-- SymbolKey and NativeSymbol objects may be compared directly. -->
+    <Class name="org.mozilla.javascript.SymbolKey"/>
+    <Bug pattern="EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS" />
+  </Match>
+  <Match>
+    <!-- SymbolKey and NativeSymbol objects may be compared directly. -->
+    <Class name="org.mozilla.javascript.NativeSymbol"/>
+    <Bug pattern="EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS" />
+  </Match>
+  <Match>
+    <!-- Legacy code depends on being able to reassign values in Main -->
+    <Class name="org.mozilla.javascript.tools.shell.Main"/>
+    <Bug pattern="MS_SHOULD_BE_FINAL"/>
+  </Match>
+  <Match>
+    <!-- Legacy code depends on being able to reassign values in Main -->
+    <Class name="org.mozilla.javascript.tools.shell.Main"/>
+    <Bug pattern="MS_PKGPROTECT"/>
+  </Match>
+  <Match>
+    <!-- GUI code just loves to swallow exceptions -->
+    <Class name="org.mozilla.javascript.tools.debugger.SwingGui"/>
+    <Bug pattern="DE_MIGHT_IGNORE"/>
+  </Match>
+  <Match>
+    <!-- In general, the tools need to exit -->
+    <Class name="~org\.mozilla\.javascript\.tools.*"/>
+    <Bug pattern="DM_EXIT"/>
+  </Match>
+  <Match>
+    <!-- Fixing this would break our public API -->
+    <Bug pattern="EI_EXPOSE_REP"/>
+  </Match>
+  <Match>
+    <!-- We do this in a few places for performance in legacy code -->
+    <Bug pattern="EI_EXPOSE_REP2"/>
+  </Match>
+  <Match>
+    <!-- We check for this in checkstyle using a different annotation -->
+    <Bug pattern="SF_SWITCH_FALLTHROUGH"/>
+  </Match>
+  <Match>
+    <!-- We check for this in checkstyle using a different annotation -->
+    <Bug pattern="SF_SWITCH_NO_DEFAULT"/>
+  </Match>
+  <Match>
+    <!-- Fixing this would break "DEBUG" -->
+    <Class name="org.mozilla.javascript.optimizer.Block"/>
+    <Bug pattern="UUF_UNUSED_FIELD"/>
+  </Match>
+  <Match>
+    <!-- Fixing this would break "DEBUG" -->
+    <Class name="org.mozilla.javascript.regexp.NativeRegExp"/>
+    <Bug pattern="DLS_DEAD_LOCAL_STORE"/>
+  </Match>
+  <Match>
+    <!-- This is kind of a weird thing that's necessary for the security manager -->
+    <Class name="org.mozilla.javascript.tools.shell.JavaPolicySecurity"/>
+    <Bug pattern="RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT"/>
+  </Match>
+  <Match>
+    <!-- And this is a weird thing related to maybe multithreading the GUI -->
+    <Class name="org.mozilla.javascript.tools.debugger.Dim"/>
+    <Bug pattern="RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE"/>
+  </Match>
+  <Match>
+    <!-- Referenced in generated code -->
+    <Class name="org.mozilla.javascript.optimizer.OptRuntime$GeneratorState"/>
+    <Bug pattern="UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD"/>
+  </Match>
+  <Match>
+    <!-- Referenced in generated code -->
+    <Class name="org.mozilla.javascript.optimizer.OptRuntime$GeneratorState"/>
+    <Bug pattern="URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD"/>
+  </Match>
+  <!-- Complex legacy stuff -->
+  <!--<Match>
+    <Class name="org.mozilla.javascript.regexp.RegExpImpl"/>
+    <Bug pattern="RC_REF_COMPARISON_BAD_PRACTICE_BOOLEAN"/>
+  </Match>-->
+
+  <!-- Things below are things that we aspire to fix! -->
+  <Match>
+    <!-- We are planning to eliminate serialization -->
+    <Bug pattern="SE_BAD_FIELD"/>
+  </Match>
+  <Match>
+    <!-- We are planning to eliminate serialization -->
+    <Bug pattern="SE_TRANSIENT_FIELD_NOT_RESTORED"/>
+  </Match>
+  <Match>
+    <!-- We are planning to get rid of synchronization -->
+    <Bug pattern="DC_DOUBLECHECK"/>
+  </Match>
+  <Match>
+    <!-- We are planning to get rid of synchronization -->
+    <Bug pattern="IS2_INCONSISTENT_SYNC"/>
+  </Match>
+  <Match>
+    <!-- We are planning to get rid of synchronization -->
+    <Bug pattern="UG_SYNC_SET_UNSYNC_GET"/>
+  </Match>
+</FindBugsFilter>
\ No newline at end of file
diff -Nru rhino-1.7.7.2/src/build.xml rhino-1.7.14/src/build.xml
--- rhino-1.7.7.2/src/build.xml	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/build.xml	1970-01-01 01:00:00.000000000 +0100
@@ -1,76 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-
-<!--
-Build file for Rhino using Ant (see http://jakarta.apache.org/ant/index.html)
-Requires Ant version 1.2
--->
-<project name="src" default="compile" basedir="..">
-
-  <property file="build.properties"/>
-
-  <available property="jdk15"
-             classname="java.lang.reflect.ParameterizedType" />
-
-  <target name="compile" depends="compile-most,compile-jdk15">
-  </target>
-
-  <target name="shell" depends="compile">
-    <java classname="org.mozilla.javascript.tools.shell.Main"
-          classpath="${classes}"
-          fork="true">
-      <arg line="-version 170"/>
-    </java>
-  </target>
-
-  <target name="compile-most">
-    <javac srcdir="src"
-           destdir="${classes}"
-           includes="org/**/*.java"
-           excludes="org/**/jdk15/*.java"
-           deprecation="on"
-           debug="${debug}"
-           includeAntRuntime="false"
-           encoding="UTF-8"
-           target="${target-jvm}"
-           source="${source-level}" />
-    <copy todir="${classes}">
-      <fileset dir="src" includes="org/**/*.properties" />
-      <filterset>
-      <filter token="IMPLEMENTATION.VERSION"
-              value="${implementation.version}"/>
-      </filterset>
-    </copy>
-  </target>
-
-  <target name="compile-jdk15" if="jdk15">
-    <javac srcdir="src"
-           destdir="${classes}"
-           includes="org/**/jdk15/*.java"
-           deprecation="on"
-           debug="${debug}"
-           includeAntRuntime="false"
-           encoding="UTF-8"
-           target="${target-jvm}"
-           source="${source-level}" />
-  </target>
-
-  <target name="copy-source">
-    <mkdir dir="${dist.dir}/src"/>
-    <copy todir="${dist.dir}/src">
-      <fileset dir="src"
-               includes="**/*.java,**/*.properties,**/*.xml,manifest"/>
-    </copy>
-  </target>
-
-  <target name="clean">
-    <delete includeEmptyDirs="true">
-      <fileset dir="${classes}"
-               excludes="org/mozilla/javascript/tools/**"/>
-    </delete>
-  </target>
-
-</project>
diff -Nru rhino-1.7.7.2/src/manifest rhino-1.7.14/src/manifest
--- rhino-1.7.7.2/src/manifest	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/manifest	1970-01-01 01:00:00.000000000 +0100
@@ -1,6 +0,0 @@
-Manifest-Version: 1.0
-Main-Class: org.mozilla.javascript.tools.shell.Main
-Implementation-Version: 1.7.7.2
-Implementation-Title: Mozilla Rhino 1.7.7.2
-Implementation-Vendor: Mozilla Foundation
-Implementation-URL: http://www.mozilla.org/rhino
diff -Nru rhino-1.7.7.2/src/META-INF/services/javax.script.ScriptEngineFactory rhino-1.7.14/src/META-INF/services/javax.script.ScriptEngineFactory
--- rhino-1.7.7.2/src/META-INF/services/javax.script.ScriptEngineFactory	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/META-INF/services/javax.script.ScriptEngineFactory	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1 @@
+org.mozilla.javascript.engine.RhinoScriptEngineFactory
diff -Nru rhino-1.7.7.2/src/org/mozilla/classfile/ByteCode.java rhino-1.7.14/src/org/mozilla/classfile/ByteCode.java
--- rhino-1.7.7.2/src/org/mozilla/classfile/ByteCode.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/classfile/ByteCode.java	2022-01-06 22:57:21.000000000 +0100
@@ -205,6 +205,7 @@
         INVOKESPECIAL = 0xB7,
         INVOKESTATIC = 0xB8,
         INVOKEINTERFACE = 0xB9,
+        INVOKEDYNAMIC = 0xBA,
         NEW = 0xBB,
         NEWARRAY = 0xBC,
         ANEWARRAY = 0xBD,
@@ -222,21 +223,33 @@
         JSR_W = 0xC9,
         BREAKPOINT = 0xCA,
 
-        IMPDEP1 = 0xFE,
+    IMPDEP1 = 0xFE,
         IMPDEP2 = 0xFF;
 
-        /**
-         * Types for the NEWARRAY opcode.
-         */
-        public static final byte
-            T_BOOLEAN = 4,
-            T_CHAR = 5,
-            T_FLOAT = 6,
-            T_DOUBLE = 7,
-            T_BYTE = 8,
-            T_SHORT = 9,
-            T_INT = 10,
-            T_LONG = 11;
+    /**
+     * Types for the NEWARRAY opcode.
+     */
+    public static final byte
+        T_BOOLEAN = 4,
+        T_CHAR = 5,
+        T_FLOAT = 6,
+        T_DOUBLE = 7,
+        T_BYTE = 8,
+        T_SHORT = 9,
+        T_INT = 10,
+        T_LONG = 11;
 
-
-}
+    /*
+     * Types for MethodHandle
+     */
+    public static final byte
+        MH_GETFIELD = 1,
+        MH_GETSTATIC = 2,
+        MH_PUTFIELD = 3,
+        MH_PUTSTATIC = 4,
+        MH_INVOKEVIRTUAL = 5,
+        MH_INVOKESTATIC = 6,
+        MH_INVOKESPECIAL = 7,
+        MH_NEWINVOKESPECIAL = 8,
+        MH_INVOKEINTERFACE = 9;
+}
\ No newline at end of file
diff -Nru rhino-1.7.7.2/src/org/mozilla/classfile/ClassFileField.java rhino-1.7.14/src/org/mozilla/classfile/ClassFileField.java
--- rhino-1.7.7.2/src/org/mozilla/classfile/ClassFileField.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/classfile/ClassFileField.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,63 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.classfile;
+
+final class ClassFileField
+{
+  ClassFileField(short nameIndex, short typeIndex, short flags)
+  {
+    itsNameIndex = nameIndex;
+    itsTypeIndex = typeIndex;
+    itsFlags = flags;
+    itsHasAttributes = false;
+  }
+
+  void setAttributes(short attr1, short attr2, short attr3, int index)
+  {
+    itsHasAttributes = true;
+    itsAttr1 = attr1;
+    itsAttr2 = attr2;
+    itsAttr3 = attr3;
+    itsIndex = index;
+  }
+
+  int write(byte[] data, int offset)
+  {
+    offset = ClassFileWriter.putInt16(itsFlags, data, offset);
+    offset = ClassFileWriter.putInt16(itsNameIndex, data, offset);
+    offset = ClassFileWriter.putInt16(itsTypeIndex, data, offset);
+    if (!itsHasAttributes) {
+      // write 0 attributes
+      offset = ClassFileWriter.putInt16(0, data, offset);
+    } else {
+      offset = ClassFileWriter.putInt16(1, data, offset);
+      offset = ClassFileWriter.putInt16(itsAttr1, data, offset);
+      offset = ClassFileWriter.putInt16(itsAttr2, data, offset);
+      offset = ClassFileWriter.putInt16(itsAttr3, data, offset);
+      offset = ClassFileWriter.putInt16(itsIndex, data, offset);
+    }
+    return offset;
+  }
+
+  int getWriteSize()
+  {
+    int size = 2 * 3;
+    if (!itsHasAttributes) {
+      size += 2;
+    } else {
+      size += 2 + 2 * 4;
+    }
+    return size;
+  }
+
+  private short itsNameIndex;
+  private short itsTypeIndex;
+  private short itsFlags;
+  private boolean itsHasAttributes;
+  private short itsAttr1, itsAttr2, itsAttr3;
+  private int itsIndex;
+}
\ No newline at end of file
diff -Nru rhino-1.7.7.2/src/org/mozilla/classfile/ClassFileMethod.java rhino-1.7.14/src/org/mozilla/classfile/ClassFileMethod.java
--- rhino-1.7.7.2/src/org/mozilla/classfile/ClassFileMethod.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/classfile/ClassFileMethod.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,66 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.classfile;
+
+final class ClassFileMethod
+{
+  ClassFileMethod(String name, short nameIndex, String type, short typeIndex,
+      short flags)
+  {
+    itsName = name;
+    itsNameIndex = nameIndex;
+    itsType = type;
+    itsTypeIndex = typeIndex;
+    itsFlags = flags;
+  }
+
+  void setCodeAttribute(byte codeAttribute[])
+  {
+    itsCodeAttribute = codeAttribute;
+  }
+
+  int write(byte[] data, int offset)
+  {
+    offset = ClassFileWriter.putInt16(itsFlags, data, offset);
+    offset = ClassFileWriter.putInt16(itsNameIndex, data, offset);
+    offset = ClassFileWriter.putInt16(itsTypeIndex, data, offset);
+    // Code attribute only
+    offset = ClassFileWriter.putInt16(1, data, offset);
+    System.arraycopy(itsCodeAttribute, 0, data, offset,
+        itsCodeAttribute.length);
+    offset += itsCodeAttribute.length;
+    return offset;
+  }
+
+  int getWriteSize()
+  {
+    return 2 * 4 + itsCodeAttribute.length;
+  }
+
+  String getName()
+  {
+    return itsName;
+  }
+
+  String getType()
+  {
+    return itsType;
+  }
+
+  short getFlags()
+  {
+    return itsFlags;
+  }
+
+  private String itsName;
+  private String itsType;
+  private short itsNameIndex;
+  private short itsTypeIndex;
+  private short itsFlags;
+  private byte[] itsCodeAttribute;
+
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/classfile/ClassFileWriter.java rhino-1.7.14/src/org/mozilla/classfile/ClassFileWriter.java
--- rhino-1.7.7.2/src/org/mozilla/classfile/ClassFileWriter.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/classfile/ClassFileWriter.java	2022-01-06 22:57:21.000000000 +0100
@@ -9,27 +9,24 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import org.mozilla.javascript.ObjToIntMap;
+import java.util.Arrays;
+import org.mozilla.javascript.Kit;
 import org.mozilla.javascript.ObjArray;
 import org.mozilla.javascript.UintMap;
 
-import java.util.Arrays;
-
 /**
  * ClassFileWriter
  *
- * A ClassFileWriter is used to write a Java class file. Methods are
- * provided to create fields and methods, and within methods to write
- * Java bytecodes.
+ * <p>A ClassFileWriter is used to write a Java class file. Methods are provided to create fields
+ * and methods, and within methods to write Java bytecodes.
  *
  * @author Roger Lawrence
  */
 public class ClassFileWriter {
 
     /**
-     * Thrown for cases where the error in generating the class file is
-     * due to a program size constraints rather than a likely bug in the
-     * compiler.
+     * Thrown for cases where the error in generating the class file is due to a program size
+     * constraints rather than a likely bug in the compiler.
      */
     public static class ClassFileFormatException extends RuntimeException {
 
@@ -43,17 +40,13 @@
     /**
      * Construct a ClassFileWriter for a class.
      *
-     * @param className the name of the class to write, including
-     *        full package qualification.
-     * @param superClassName the name of the superclass of the class
-     *        to write, including full package qualification.
-     * @param sourceFileName the name of the source file to use for
-     *        producing debug information, or null if debug information
-     *        is not desired
-     */
-    public ClassFileWriter(String className, String superClassName,
-                           String sourceFileName)
-    {
+     * @param className the name of the class to write, including full package qualification.
+     * @param superClassName the name of the superclass of the class to write, including full
+     *     package qualification.
+     * @param sourceFileName the name of the source file to use for producing debug information, or
+     *     null if debug information is not desired
+     */
+    public ClassFileWriter(String className, String superClassName, String sourceFileName) {
         generatedClassName = className;
         itsConstantPool = new ConstantPool(this);
         itsThisClassIndex = itsConstantPool.addClass(className);
@@ -66,68 +59,56 @@
         itsFlags = ACC_PUBLIC | ACC_SUPER;
     }
 
-    public final String getClassName()
-    {
+    public final String getClassName() {
         return generatedClassName;
     }
 
     /**
      * Add an interface implemented by this class.
      *
-     * This method may be called multiple times for classes that
-     * implement multiple interfaces.
+     * <p>This method may be called multiple times for classes that implement multiple interfaces.
      *
-     * @param interfaceName a name of an interface implemented
-     *        by the class being written, including full package
-     *        qualification.
+     * @param interfaceName a name of an interface implemented by the class being written, including
+     *     full package qualification.
      */
     public void addInterface(String interfaceName) {
         short interfaceIndex = itsConstantPool.addClass(interfaceName);
         itsInterfaces.add(Short.valueOf(interfaceIndex));
     }
 
-    public static final short
-        ACC_PUBLIC = 0x0001,
-        ACC_PRIVATE = 0x0002,
-        ACC_PROTECTED = 0x0004,
-        ACC_STATIC = 0x0008,
-        ACC_FINAL = 0x0010,
-        ACC_SUPER = 0x0020,
-        ACC_SYNCHRONIZED = 0x0020,
-        ACC_VOLATILE = 0x0040,
-        ACC_TRANSIENT = 0x0080,
-        ACC_NATIVE = 0x0100,
-        ACC_ABSTRACT = 0x0400;
+    public static final short ACC_PUBLIC = 0x0001,
+            ACC_PRIVATE = 0x0002,
+            ACC_PROTECTED = 0x0004,
+            ACC_STATIC = 0x0008,
+            ACC_FINAL = 0x0010,
+            ACC_SUPER = 0x0020,
+            ACC_SYNCHRONIZED = 0x0020,
+            ACC_VOLATILE = 0x0040,
+            ACC_TRANSIENT = 0x0080,
+            ACC_NATIVE = 0x0100,
+            ACC_ABSTRACT = 0x0400;
 
     /**
      * Set the class's flags.
      *
-     * Flags must be a set of the following flags, bitwise or'd
-     * together:
-     *      ACC_PUBLIC
-     *      ACC_PRIVATE
-     *      ACC_PROTECTED
-     *      ACC_FINAL
-     *      ACC_ABSTRACT
-     * TODO: check that this is the appropriate set
+     * <p>Flags must be a set of the following flags, bitwise or'd together: ACC_PUBLIC ACC_PRIVATE
+     * ACC_PROTECTED ACC_FINAL ACC_ABSTRACT TODO: check that this is the appropriate set
+     *
      * @param flags the set of class flags to set
      */
     public void setFlags(short flags) {
         itsFlags = flags;
     }
 
-    static String getSlashedForm(String name)
-    {
+    static String getSlashedForm(String name) {
         return name.replace('.', '/');
     }
 
     /**
-     * Convert Java class name in dot notation into
-     * "Lname-with-dots-replaced-by-slashes;" form suitable for use as
-     * JVM type signatures.
+     * Convert Java class name in dot notation into "Lname-with-dots-replaced-by-slashes;" form
+     * suitable for use as JVM type signatures.
      */
-    public static String classNameToSignature(String name)
-    {
+    public static String classNameToSignature(String name) {
         int nameLength = name.length();
         int colonPos = 1 + nameLength;
         char[] buf = new char[colonPos + 1];
@@ -147,8 +128,7 @@
      *
      * @param fieldName the name of the field
      * @param type the type of the field using ...
-     * @param flags the attributes of the field, such as ACC_PUBLIC, etc.
-     *        bitwise or'd together
+     * @param flags the attributes of the field, such as ACC_PUBLIC, etc. bitwise or'd together
      */
     public void addField(String fieldName, String type, short flags) {
         short fieldNameIndex = itsConstantPool.addUtf8(fieldName);
@@ -161,21 +141,18 @@
      *
      * @param fieldName the name of the field
      * @param type the type of the field using ...
-     * @param flags the attributes of the field, such as ACC_PUBLIC, etc.
-     *        bitwise or'd together
+     * @param flags the attributes of the field, such as ACC_PUBLIC, etc. bitwise or'd together
      * @param value an initial integral value
      */
-    public void addField(String fieldName, String type, short flags,
-                         int value)
-    {
+    public void addField(String fieldName, String type, short flags, int value) {
         short fieldNameIndex = itsConstantPool.addUtf8(fieldName);
         short typeIndex = itsConstantPool.addUtf8(type);
-        ClassFileField field = new ClassFileField(fieldNameIndex, typeIndex,
-                                                  flags);
-        field.setAttributes(itsConstantPool.addUtf8("ConstantValue"),
-                            (short)0,
-                            (short)0,
-                            itsConstantPool.addConstant(value));
+        ClassFileField field = new ClassFileField(fieldNameIndex, typeIndex, flags);
+        field.setAttributes(
+                itsConstantPool.addUtf8("ConstantValue"),
+                (short) 0,
+                (short) 0,
+                itsConstantPool.addConstant(value));
         itsFields.add(field);
     }
 
@@ -184,21 +161,18 @@
      *
      * @param fieldName the name of the field
      * @param type the type of the field using ...
-     * @param flags the attributes of the field, such as ACC_PUBLIC, etc.
-     *        bitwise or'd together
+     * @param flags the attributes of the field, such as ACC_PUBLIC, etc. bitwise or'd together
      * @param value an initial long value
      */
-    public void addField(String fieldName, String type, short flags,
-                         long value)
-    {
+    public void addField(String fieldName, String type, short flags, long value) {
         short fieldNameIndex = itsConstantPool.addUtf8(fieldName);
         short typeIndex = itsConstantPool.addUtf8(type);
-        ClassFileField field = new ClassFileField(fieldNameIndex, typeIndex,
-                                                  flags);
-        field.setAttributes(itsConstantPool.addUtf8("ConstantValue"),
-                            (short)0,
-                            (short)2,
-                            itsConstantPool.addConstant(value));
+        ClassFileField field = new ClassFileField(fieldNameIndex, typeIndex, flags);
+        field.setAttributes(
+                itsConstantPool.addUtf8("ConstantValue"),
+                (short) 0,
+                (short) 2,
+                itsConstantPool.addConstant(value));
         itsFields.add(field);
     }
 
@@ -207,40 +181,35 @@
      *
      * @param fieldName the name of the field
      * @param type the type of the field using ...
-     * @param flags the attributes of the field, such as ACC_PUBLIC, etc.
-     *        bitwise or'd together
+     * @param flags the attributes of the field, such as ACC_PUBLIC, etc. bitwise or'd together
      * @param value an initial double value
      */
-    public void addField(String fieldName, String type, short flags,
-                         double value)
-    {
+    public void addField(String fieldName, String type, short flags, double value) {
         short fieldNameIndex = itsConstantPool.addUtf8(fieldName);
         short typeIndex = itsConstantPool.addUtf8(type);
-        ClassFileField field = new ClassFileField(fieldNameIndex, typeIndex,
-                                                  flags);
-        field.setAttributes(itsConstantPool.addUtf8("ConstantValue"),
-                            (short)0,
-                            (short)2,
-                            itsConstantPool.addConstant(value));
+        ClassFileField field = new ClassFileField(fieldNameIndex, typeIndex, flags);
+        field.setAttributes(
+                itsConstantPool.addUtf8("ConstantValue"),
+                (short) 0,
+                (short) 2,
+                itsConstantPool.addConstant(value));
         itsFields.add(field);
     }
 
     /**
-     * Add Information about java variable to use when generating the local
-     * variable table.
+     * Add Information about java variable to use when generating the local variable table.
      *
      * @param name variable name.
      * @param type variable type as bytecode descriptor string.
-     * @param startPC the starting bytecode PC where this variable is live,
-     *                 or -1 if it does not have a Java register.
-     * @param register the Java register number of variable
-     *                 or -1 if it does not have a Java register.
+     * @param startPC the starting bytecode PC where this variable is live, or -1 if it does not
+     *     have a Java register.
+     * @param register the Java register number of variable or -1 if it does not have a Java
+     *     register.
      */
-    public void addVariableDescriptor(String name, String type, int startPC, int register)
-    {
+    public void addVariableDescriptor(String name, String type, int startPC, int register) {
         int nameIndex = itsConstantPool.addUtf8(name);
         int descriptorIndex = itsConstantPool.addUtf8(type);
-        int [] chunk = { nameIndex, descriptorIndex, startPC, register };
+        int[] chunk = {nameIndex, descriptorIndex, startPC, register};
         if (itsVarDescriptors == null) {
             itsVarDescriptors = new ObjArray();
         }
@@ -250,19 +219,17 @@
     /**
      * Add a method and begin adding code.
      *
-     * This method must be called before other methods for adding code,
-     * exception tables, etc. can be invoked.
+     * <p>This method must be called before other methods for adding code, exception tables, etc.
+     * can be invoked.
      *
      * @param methodName the name of the method
      * @param type a string representing the type
-     * @param flags the attributes of the field, such as ACC_PUBLIC, etc.
-     *        bitwise or'd together
+     * @param flags the attributes of the field, such as ACC_PUBLIC, etc. bitwise or'd together
      */
     public void startMethod(String methodName, String type, short flags) {
         short methodNameIndex = itsConstantPool.addUtf8(methodName);
         short typeIndex = itsConstantPool.addUtf8(type);
-        itsCurrentMethod = new ClassFileMethod(methodName, methodNameIndex,
-                                               type, typeIndex, flags);
+        itsCurrentMethod = new ClassFileMethod(methodName, methodNameIndex, type, typeIndex, flags);
         itsJumpFroms = new UintMap();
         itsMethods.add(itsCurrentMethod);
         addSuperBlockStart(0);
@@ -271,15 +238,14 @@
     /**
      * Complete generation of the method.
      *
-     * After this method is called, no more code can be added to the
-     * method begun with <code>startMethod</code>.
+     * <p>After this method is called, no more code can be added to the method begun with <code>
+     * startMethod</code>.
      *
-     * @param maxLocals the maximum number of local variable slots
-     *        (a.k.a. Java registers) used by the method
+     * @param maxLocals the maximum number of local variable slots (a.k.a. Java registers) used by
+     *     the method
      */
     public void stopMethod(short maxLocals) {
-        if (itsCurrentMethod == null)
-            throw new IllegalStateException("No method to stop");
+        if (itsCurrentMethod == null) throw new IllegalStateException("No method to stop");
 
         fixLabelGotos();
 
@@ -316,79 +282,79 @@
             }
         }
 
-        int attrLength = 2 +                    // attribute_name_index
-                         4 +                    // attribute_length
-                         2 +                    // max_stack
-                         2 +                    // max_locals
-                         4 +                    // code_length
-                         itsCodeBufferTop +
-                         2 +                    // exception_table_length
-                         (itsExceptionTableTop * 8) +
-                         2 +                    // attributes_count
-                         lineNumberTableLength +
-                         variableTableLength +
-                         stackMapTableLength;
+        int attrLength =
+                2
+                        + // attribute_name_index
+                        4
+                        + // attribute_length
+                        2
+                        + // max_stack
+                        2
+                        + // max_locals
+                        4
+                        + // code_length
+                        itsCodeBufferTop
+                        + 2
+                        + // exception_table_length
+                        (itsExceptionTableTop * 8)
+                        + 2
+                        + // attributes_count
+                        lineNumberTableLength
+                        + variableTableLength
+                        + stackMapTableLength;
 
         if (attrLength > 65536) {
             // See http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html,
             // section 4.10, "The amount of code per non-native, non-abstract
             // method is limited to 65536 bytes...
-            throw new ClassFileFormatException(
-                "generated bytecode for method exceeds 64K limit.");
+            throw new ClassFileFormatException("generated bytecode for method exceeds 64K limit.");
         }
         byte[] codeAttribute = new byte[attrLength];
         int index = 0;
         int codeAttrIndex = itsConstantPool.addUtf8("Code");
         index = putInt16(codeAttrIndex, codeAttribute, index);
-        attrLength -= 6;                 // discount the attribute header
+        attrLength -= 6; // discount the attribute header
         index = putInt32(attrLength, codeAttribute, index);
         index = putInt16(itsMaxStack, codeAttribute, index);
         index = putInt16(itsMaxLocals, codeAttribute, index);
         index = putInt32(itsCodeBufferTop, codeAttribute, index);
-        System.arraycopy(itsCodeBuffer, 0, codeAttribute, index,
-                         itsCodeBufferTop);
+        System.arraycopy(itsCodeBuffer, 0, codeAttribute, index, itsCodeBufferTop);
         index += itsCodeBufferTop;
 
         if (itsExceptionTableTop > 0) {
             index = putInt16(itsExceptionTableTop, codeAttribute, index);
             for (int i = 0; i < itsExceptionTableTop; i++) {
                 ExceptionTableEntry ete = itsExceptionTable[i];
-                short startPC = (short)getLabelPC(ete.itsStartLabel);
-                short endPC = (short)getLabelPC(ete.itsEndLabel);
-                short handlerPC = (short)getLabelPC(ete.itsHandlerLabel);
+                int startPC = getLabelPC(ete.itsStartLabel);
+                int endPC = getLabelPC(ete.itsEndLabel);
+                int handlerPC = getLabelPC(ete.itsHandlerLabel);
                 short catchType = ete.itsCatchType;
-                if (startPC == -1)
-                    throw new IllegalStateException("start label not defined");
-                if (endPC == -1)
-                    throw new IllegalStateException("end label not defined");
-                if (handlerPC == -1)
-                    throw new IllegalStateException(
-                        "handler label not defined");
+                if (startPC == -1) throw new IllegalStateException("start label not defined");
+                if (endPC == -1) throw new IllegalStateException("end label not defined");
+                if (handlerPC == -1) throw new IllegalStateException("handler label not defined");
 
+                // no need to cast to short here, the putInt16 uses only
+                // the short part of the int
                 index = putInt16(startPC, codeAttribute, index);
                 index = putInt16(endPC, codeAttribute, index);
                 index = putInt16(handlerPC, codeAttribute, index);
                 index = putInt16(catchType, codeAttribute, index);
             }
-        }
-        else {
+        } else {
             // write 0 as exception table length
             index = putInt16(0, codeAttribute, index);
         }
 
         int attributeCount = 0;
-        if (itsLineNumberTable != null)
-            attributeCount++;
-        if (itsVarDescriptors != null)
-            attributeCount++;
+        if (itsLineNumberTable != null) attributeCount++;
+        if (itsVarDescriptors != null) attributeCount++;
         if (stackMapTableLength > 0) {
             attributeCount++;
         }
         index = putInt16(attributeCount, codeAttribute, index);
 
         if (itsLineNumberTable != null) {
-            int lineNumberTableAttrIndex
-                    = itsConstantPool.addUtf8("LineNumberTable");
+            int lineNumberTableAttrIndex = itsConstantPool.addUtf8("LineNumberTable");
             index = putInt16(lineNumberTableAttrIndex, codeAttribute, index);
             int tableAttrLength = 2 + (itsLineNumberTableTop * 4);
             index = putInt32(tableAttrLength, codeAttribute, index);
@@ -399,19 +365,18 @@
         }
 
         if (itsVarDescriptors != null) {
-            int variableTableAttrIndex
-                    = itsConstantPool.addUtf8("LocalVariableTable");
+            int variableTableAttrIndex = itsConstantPool.addUtf8("LocalVariableTable");
             index = putInt16(variableTableAttrIndex, codeAttribute, index);
             int varCount = itsVarDescriptors.size();
             int tableAttrLength = 2 + (varCount * 10);
             index = putInt32(tableAttrLength, codeAttribute, index);
             index = putInt16(varCount, codeAttribute, index);
             for (int i = 0; i < varCount; i++) {
-                int[] chunk = (int[])itsVarDescriptors.get(i);
-                int nameIndex       = chunk[0];
+                int[] chunk = (int[]) itsVarDescriptors.get(i);
+                int nameIndex = chunk[0];
                 int descriptorIndex = chunk[1];
-                int startPC         = chunk[2];
-                int register        = chunk[3];
+                int startPC = chunk[2];
+                int register = chunk[3];
                 int length = itsCodeBufferTop - startPC;
 
                 index = putInt16(startPC, codeAttribute, index);
@@ -423,9 +388,7 @@
         }
 
         if (stackMapTableLength > 0) {
-            int stackMapTableAttrIndex =
-                    itsConstantPool.addUtf8("StackMapTable");
-            int start = index;
+            int stackMapTableAttrIndex = itsConstantPool.addUtf8("StackMapTable");
             index = putInt16(stackMapTableAttrIndex, codeAttribute, index);
             index = stackMap.write(codeAttribute, index);
         }
@@ -453,18 +416,15 @@
      * @param theOpCode the opcode of the bytecode
      */
     public void add(int theOpCode) {
-        if (opcodeCount(theOpCode) != 0)
-            throw new IllegalArgumentException("Unexpected operands");
+        if (opcodeCount(theOpCode) != 0) throw new IllegalArgumentException("Unexpected operands");
         int newStack = itsStackTop + stackChange(theOpCode);
         if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
-        if (DEBUGCODE)
-            System.out.println("Add " + bytecodeStr(theOpCode));
+        if (DEBUGCODE) System.out.println("Add " + bytecodeStr(theOpCode));
         addToCodeBuffer(theOpCode);
-        itsStackTop = (short)newStack;
-        if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
+        itsStackTop = (short) newStack;
+        if (newStack > itsMaxStack) itsMaxStack = (short) newStack;
         if (DEBUGSTACK) {
-            System.out.println("After "+bytecodeStr(theOpCode)
-                               +" stack = "+itsStackTop);
+            System.out.println("After " + bytecodeStr(theOpCode) + " stack = " + itsStackTop);
         }
         if (theOpCode == ByteCode.ATHROW) {
             addSuperBlockStart(itsCodeBufferTop);
@@ -479,65 +439,67 @@
      */
     public void add(int theOpCode, int theOperand) {
         if (DEBUGCODE) {
-            System.out.println("Add "+bytecodeStr(theOpCode)
-                               +", "+Integer.toHexString(theOperand));
+            System.out.println(
+                    "Add " + bytecodeStr(theOpCode) + ", " + Integer.toHexString(theOperand));
         }
         int newStack = itsStackTop + stackChange(theOpCode);
         if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
 
         switch (theOpCode) {
-            case ByteCode.GOTO :
+            case ByteCode.GOTO:
                 // This is necessary because dead code is seemingly being
                 // generated and Sun's verifier is expecting type state to be
                 // placed even at dead blocks of code.
                 addSuperBlockStart(itsCodeBufferTop + 3);
-                // fallthru...
-            case ByteCode.IFEQ :
-            case ByteCode.IFNE :
-            case ByteCode.IFLT :
-            case ByteCode.IFGE :
-            case ByteCode.IFGT :
-            case ByteCode.IFLE :
-            case ByteCode.IF_ICMPEQ :
-            case ByteCode.IF_ICMPNE :
-            case ByteCode.IF_ICMPLT :
-            case ByteCode.IF_ICMPGE :
-            case ByteCode.IF_ICMPGT :
-            case ByteCode.IF_ICMPLE :
-            case ByteCode.IF_ACMPEQ :
-            case ByteCode.IF_ACMPNE :
-            case ByteCode.JSR :
-            case ByteCode.IFNULL :
-            case ByteCode.IFNONNULL : {
+                // fall through...
+            case ByteCode.IFEQ:
+            case ByteCode.IFNE:
+            case ByteCode.IFLT:
+            case ByteCode.IFGE:
+            case ByteCode.IFGT:
+            case ByteCode.IFLE:
+            case ByteCode.IF_ICMPEQ:
+            case ByteCode.IF_ICMPNE:
+            case ByteCode.IF_ICMPLT:
+            case ByteCode.IF_ICMPGE:
+            case ByteCode.IF_ICMPGT:
+            case ByteCode.IF_ICMPLE:
+            case ByteCode.IF_ACMPEQ:
+            case ByteCode.IF_ACMPNE:
+            case ByteCode.JSR:
+            case ByteCode.IFNULL:
+            case ByteCode.IFNONNULL:
+                {
                     if ((theOperand & 0x80000000) != 0x80000000) {
                         if ((theOperand < 0) || (theOperand > 65535))
-                            throw new IllegalArgumentException(
-                                "Bad label for branch");
+                            throw new IllegalArgumentException("Bad label for branch");
                     }
                     int branchPC = itsCodeBufferTop;
                     addToCodeBuffer(theOpCode);
                     if ((theOperand & 0x80000000) != 0x80000000) {
-                            // hard displacement
+                        // hard displacement
                         addToCodeInt16(theOperand);
                         int target = theOperand + branchPC;
                         addSuperBlockStart(target);
                         itsJumpFroms.put(target, branchPC);
-                    }
-                    else {  // a label
+                    } else { // a label
                         int targetPC = getLabelPC(theOperand);
                         if (DEBUGLABELS) {
                             int theLabel = theOperand & 0x7FFFFFFF;
-                            System.out.println("Fixing branch to " +
-                                               theLabel + " at " + targetPC +
-                                               " from " + branchPC);
+                            System.out.println(
+                                    "Fixing branch to "
+                                            + theLabel
+                                            + " at "
+                                            + targetPC
+                                            + " from "
+                                            + branchPC);
                         }
                         if (targetPC != -1) {
                             int offset = targetPC - branchPC;
                             addToCodeInt16(offset);
                             addSuperBlockStart(targetPC);
                             itsJumpFroms.put(targetPC, branchPC);
-                        }
-                        else {
+                        } else {
                             addLabelFixup(theOperand, branchPC + 1);
                             addToCodeInt16(0);
                         }
@@ -545,44 +507,43 @@
                 }
                 break;
 
-            case ByteCode.BIPUSH :
-                if ((byte)theOperand != theOperand)
+            case ByteCode.BIPUSH:
+                if ((byte) theOperand != theOperand)
                     throw new IllegalArgumentException("out of range byte");
                 addToCodeBuffer(theOpCode);
-                addToCodeBuffer((byte)theOperand);
+                addToCodeBuffer((byte) theOperand);
                 break;
 
-            case ByteCode.SIPUSH :
-                if ((short)theOperand != theOperand)
+            case ByteCode.SIPUSH:
+                if ((short) theOperand != theOperand)
                     throw new IllegalArgumentException("out of range short");
                 addToCodeBuffer(theOpCode);
-                   addToCodeInt16(theOperand);
+                addToCodeInt16(theOperand);
                 break;
 
-            case ByteCode.NEWARRAY :
+            case ByteCode.NEWARRAY:
                 if (!(0 <= theOperand && theOperand < 256))
                     throw new IllegalArgumentException("out of range index");
                 addToCodeBuffer(theOpCode);
                 addToCodeBuffer(theOperand);
                 break;
 
-            case ByteCode.GETFIELD :
-            case ByteCode.PUTFIELD :
+            case ByteCode.GETFIELD:
+            case ByteCode.PUTFIELD:
                 if (!(0 <= theOperand && theOperand < 65536))
                     throw new IllegalArgumentException("out of range field");
                 addToCodeBuffer(theOpCode);
                 addToCodeInt16(theOperand);
                 break;
 
-            case ByteCode.LDC :
-            case ByteCode.LDC_W :
-            case ByteCode.LDC2_W :
+            case ByteCode.LDC:
+            case ByteCode.LDC_W:
+            case ByteCode.LDC2_W:
                 if (!(0 <= theOperand && theOperand < 65536))
-                    throw new IllegalArgumentException("out of range index");
+                    throw new ClassFileFormatException("out of range index");
                 if (theOperand >= 256
-                    || theOpCode == ByteCode.LDC_W
-                    || theOpCode == ByteCode.LDC2_W)
-                {
+                        || theOpCode == ByteCode.LDC_W
+                        || theOpCode == ByteCode.LDC2_W) {
                     if (theOpCode == ByteCode.LDC) {
                         addToCodeBuffer(ByteCode.LDC_W);
                     } else {
@@ -595,40 +556,37 @@
                 }
                 break;
 
-            case ByteCode.RET :
-            case ByteCode.ILOAD :
-            case ByteCode.LLOAD :
-            case ByteCode.FLOAD :
-            case ByteCode.DLOAD :
-            case ByteCode.ALOAD :
-            case ByteCode.ISTORE :
-            case ByteCode.LSTORE :
-            case ByteCode.FSTORE :
-            case ByteCode.DSTORE :
-            case ByteCode.ASTORE :
-                if (!(0 <= theOperand && theOperand < 65536))
+            case ByteCode.RET:
+            case ByteCode.ILOAD:
+            case ByteCode.LLOAD:
+            case ByteCode.FLOAD:
+            case ByteCode.DLOAD:
+            case ByteCode.ALOAD:
+            case ByteCode.ISTORE:
+            case ByteCode.LSTORE:
+            case ByteCode.FSTORE:
+            case ByteCode.DSTORE:
+            case ByteCode.ASTORE:
+                if (theOperand < 0 || 65536 <= theOperand)
                     throw new ClassFileFormatException("out of range variable");
                 if (theOperand >= 256) {
                     addToCodeBuffer(ByteCode.WIDE);
                     addToCodeBuffer(theOpCode);
                     addToCodeInt16(theOperand);
-                }
-                else {
+                } else {
                     addToCodeBuffer(theOpCode);
                     addToCodeBuffer(theOperand);
                 }
                 break;
 
-            default :
-                throw new IllegalArgumentException(
-                    "Unexpected opcode for 1 operand");
+            default:
+                throw new IllegalArgumentException("Unexpected opcode for 1 operand");
         }
 
-        itsStackTop = (short)newStack;
-        if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
+        itsStackTop = (short) newStack;
+        if (newStack > itsMaxStack) itsMaxStack = (short) newStack;
         if (DEBUGSTACK) {
-            System.out.println("After "+bytecodeStr(theOpCode)
-                               +" stack = "+itsStackTop);
+            System.out.println("After " + bytecodeStr(theOpCode) + " stack = " + itsStackTop);
         }
     }
 
@@ -639,12 +597,24 @@
      */
     public void addLoadConstant(int k) {
         switch (k) {
-            case 0: add(ByteCode.ICONST_0); break;
-            case 1: add(ByteCode.ICONST_1); break;
-            case 2: add(ByteCode.ICONST_2); break;
-            case 3: add(ByteCode.ICONST_3); break;
-            case 4: add(ByteCode.ICONST_4); break;
-            case 5: add(ByteCode.ICONST_5); break;
+            case 0:
+                add(ByteCode.ICONST_0);
+                break;
+            case 1:
+                add(ByteCode.ICONST_1);
+                break;
+            case 2:
+                add(ByteCode.ICONST_2);
+                break;
+            case 3:
+                add(ByteCode.ICONST_3);
+                break;
+            case 4:
+                add(ByteCode.ICONST_4);
+                break;
+            case 5:
+                add(ByteCode.ICONST_5);
+                break;
             default:
                 add(ByteCode.LDC, itsConstantPool.addConstant(k));
                 break;
@@ -696,32 +666,34 @@
      */
     public void add(int theOpCode, int theOperand1, int theOperand2) {
         if (DEBUGCODE) {
-            System.out.println("Add "+bytecodeStr(theOpCode)
-                               +", "+Integer.toHexString(theOperand1)
-                               +", "+Integer.toHexString(theOperand2));
+            System.out.println(
+                    "Add "
+                            + bytecodeStr(theOpCode)
+                            + ", "
+                            + Integer.toHexString(theOperand1)
+                            + ", "
+                            + Integer.toHexString(theOperand2));
         }
         int newStack = itsStackTop + stackChange(theOpCode);
         if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
 
         if (theOpCode == ByteCode.IINC) {
-            if (!(0 <= theOperand1 && theOperand1 < 65536))
+            if (theOperand1 < 0 || 65536 <= theOperand1)
                 throw new ClassFileFormatException("out of range variable");
-            if (!(0 <= theOperand2 && theOperand2 < 65536))
+            if (theOperand2 < 0 || 65536 <= theOperand2)
                 throw new ClassFileFormatException("out of range increment");
 
-            if (theOperand1 > 255 || theOperand2 < -128 || theOperand2 > 127) {
+            if (theOperand1 > 255 || theOperand2 > 127) {
                 addToCodeBuffer(ByteCode.WIDE);
                 addToCodeBuffer(ByteCode.IINC);
                 addToCodeInt16(theOperand1);
                 addToCodeInt16(theOperand2);
-            }
-            else {
+            } else {
                 addToCodeBuffer(ByteCode.IINC);
                 addToCodeBuffer(theOperand1);
                 addToCodeBuffer(theOperand2);
             }
-        }
-        else if (theOpCode == ByteCode.MULTIANEWARRAY) {
+        } else if (theOpCode == ByteCode.MULTIANEWARRAY) {
             if (!(0 <= theOperand1 && theOperand1 < 65536))
                 throw new IllegalArgumentException("out of range index");
             if (!(0 <= theOperand2 && theOperand2 < 256))
@@ -730,138 +702,176 @@
             addToCodeBuffer(ByteCode.MULTIANEWARRAY);
             addToCodeInt16(theOperand1);
             addToCodeBuffer(theOperand2);
+        } else {
+            throw new IllegalArgumentException("Unexpected opcode for 2 operands");
         }
-        else {
-            throw new IllegalArgumentException(
-                "Unexpected opcode for 2 operands");
-        }
-        itsStackTop = (short)newStack;
-        if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
+        itsStackTop = (short) newStack;
+        if (newStack > itsMaxStack) itsMaxStack = (short) newStack;
         if (DEBUGSTACK) {
-            System.out.println("After "+bytecodeStr(theOpCode)
-                               +" stack = "+itsStackTop);
+            System.out.println("After " + bytecodeStr(theOpCode) + " stack = " + itsStackTop);
         }
-
     }
 
     public void add(int theOpCode, String className) {
         if (DEBUGCODE) {
-            System.out.println("Add "+bytecodeStr(theOpCode)
-                               +", "+className);
+            System.out.println("Add " + bytecodeStr(theOpCode) + ", " + className);
         }
         int newStack = itsStackTop + stackChange(theOpCode);
         if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
         switch (theOpCode) {
-            case ByteCode.NEW :
-            case ByteCode.ANEWARRAY :
-            case ByteCode.CHECKCAST :
-            case ByteCode.INSTANCEOF : {
-                short classIndex = itsConstantPool.addClass(className);
-                addToCodeBuffer(theOpCode);
-                addToCodeInt16(classIndex);
-            }
-            break;
+            case ByteCode.NEW:
+            case ByteCode.ANEWARRAY:
+            case ByteCode.CHECKCAST:
+            case ByteCode.INSTANCEOF:
+                {
+                    short classIndex = itsConstantPool.addClass(className);
+                    addToCodeBuffer(theOpCode);
+                    addToCodeInt16(classIndex);
+                }
+                break;
 
-            default :
-                throw new IllegalArgumentException(
-                    "bad opcode for class reference");
+            default:
+                throw new IllegalArgumentException("bad opcode for class reference");
         }
-        itsStackTop = (short)newStack;
-        if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
+        itsStackTop = (short) newStack;
+        if (newStack > itsMaxStack) itsMaxStack = (short) newStack;
         if (DEBUGSTACK) {
-            System.out.println("After "+bytecodeStr(theOpCode)
-                               +" stack = "+itsStackTop);
+            System.out.println("After " + bytecodeStr(theOpCode) + " stack = " + itsStackTop);
         }
     }
 
-
-    public void add(int theOpCode, String className, String fieldName,
-                    String fieldType)
-    {
+    public void add(int theOpCode, String className, String fieldName, String fieldType) {
         if (DEBUGCODE) {
-            System.out.println("Add "+bytecodeStr(theOpCode)
-                               +", "+className+", "+fieldName+", "+fieldType);
+            System.out.println(
+                    "Add "
+                            + bytecodeStr(theOpCode)
+                            + ", "
+                            + className
+                            + ", "
+                            + fieldName
+                            + ", "
+                            + fieldType);
         }
         int newStack = itsStackTop + stackChange(theOpCode);
         char fieldTypeChar = fieldType.charAt(0);
-        int fieldSize = (fieldTypeChar == 'J' || fieldTypeChar == 'D')
-                        ? 2 : 1;
+        int fieldSize = (fieldTypeChar == 'J' || fieldTypeChar == 'D') ? 2 : 1;
         switch (theOpCode) {
-            case ByteCode.GETFIELD :
-            case ByteCode.GETSTATIC :
+            case ByteCode.GETFIELD:
+            case ByteCode.GETSTATIC:
                 newStack += fieldSize;
                 break;
-            case ByteCode.PUTSTATIC :
-            case ByteCode.PUTFIELD :
+            case ByteCode.PUTSTATIC:
+            case ByteCode.PUTFIELD:
                 newStack -= fieldSize;
                 break;
-            default :
-                throw new IllegalArgumentException(
-                    "bad opcode for field reference");
+            default:
+                throw new IllegalArgumentException("bad opcode for field reference");
         }
         if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
-        short fieldRefIndex = itsConstantPool.addFieldRef(className,
-                                             fieldName, fieldType);
+        short fieldRefIndex = itsConstantPool.addFieldRef(className, fieldName, fieldType);
         addToCodeBuffer(theOpCode);
         addToCodeInt16(fieldRefIndex);
 
-        itsStackTop = (short)newStack;
-        if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
+        itsStackTop = (short) newStack;
+        if (newStack > itsMaxStack) itsMaxStack = (short) newStack;
         if (DEBUGSTACK) {
-            System.out.println("After "+bytecodeStr(theOpCode)
-                               +" stack = "+itsStackTop);
+            System.out.println("After " + bytecodeStr(theOpCode) + " stack = " + itsStackTop);
         }
     }
 
-    public void addInvoke(int theOpCode, String className, String methodName,
-                          String methodType)
-    {
+    public void addInvoke(int theOpCode, String className, String methodName, String methodType) {
         if (DEBUGCODE) {
-            System.out.println("Add "+bytecodeStr(theOpCode)
-                               +", "+className+", "+methodName+", "
-                               +methodType);
+            System.out.println(
+                    "Add "
+                            + bytecodeStr(theOpCode)
+                            + ", "
+                            + className
+                            + ", "
+                            + methodName
+                            + ", "
+                            + methodType);
         }
         int parameterInfo = sizeOfParameters(methodType);
         int parameterCount = parameterInfo >>> 16;
-        int stackDiff = (short)parameterInfo;
+        int stackDiff = (short) parameterInfo;
 
         int newStack = itsStackTop + stackDiff;
-        newStack += stackChange(theOpCode);     // adjusts for 'this'
+        newStack += stackChange(theOpCode); // adjusts for 'this'
         if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
 
         switch (theOpCode) {
-            case ByteCode.INVOKEVIRTUAL :
-            case ByteCode.INVOKESPECIAL :
-            case ByteCode.INVOKESTATIC :
-            case ByteCode.INVOKEINTERFACE : {
+            case ByteCode.INVOKEVIRTUAL:
+            case ByteCode.INVOKESPECIAL:
+            case ByteCode.INVOKESTATIC:
+            case ByteCode.INVOKEINTERFACE:
+                {
                     addToCodeBuffer(theOpCode);
                     if (theOpCode == ByteCode.INVOKEINTERFACE) {
-                        short ifMethodRefIndex
-                                    = itsConstantPool.addInterfaceMethodRef(
-                                               className, methodName,
-                                               methodType);
+                        short ifMethodRefIndex =
+                                itsConstantPool.addInterfaceMethodRef(
+                                        className, methodName, methodType);
                         addToCodeInt16(ifMethodRefIndex);
                         addToCodeBuffer(parameterCount + 1);
                         addToCodeBuffer(0);
-                    }
-                    else {
-                        short methodRefIndex = itsConstantPool.addMethodRef(
-                                               className, methodName,
-                                               methodType);
+                    } else {
+                        short methodRefIndex =
+                                itsConstantPool.addMethodRef(className, methodName, methodType);
                         addToCodeInt16(methodRefIndex);
                     }
                 }
                 break;
 
-            default :
-                throw new IllegalArgumentException(
-                    "bad opcode for method reference");
+            default:
+                throw new IllegalArgumentException("bad opcode for method reference");
         }
-        itsStackTop = (short)newStack;
-        if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
+        itsStackTop = (short) newStack;
+        if (newStack > itsMaxStack) itsMaxStack = (short) newStack;
         if (DEBUGSTACK) {
-            System.out.println("After "+bytecodeStr(theOpCode)
-                               +" stack = "+itsStackTop);
+            System.out.println("After " + bytecodeStr(theOpCode) + " stack = " + itsStackTop);
+        }
+    }
+
+    public void addInvokeDynamic(
+            String methodName, String methodType, MHandle bsm, Object... bsmArgs) {
+        if (DEBUGCODE) {
+            System.out.println("Add invokedynamic, " + methodName + ", " + methodType);
+        }
+        // JDK 1.7 major class file version is required for invokedynamic
+        if (MajorVersion < 51) {
+            throw new RuntimeException(
+                    "Please build and run with JDK 1.7 for invokedynamic support");
+        }
+
+        int parameterInfo = sizeOfParameters(methodType);
+        // int parameterCount = parameterInfo >>> 16;
+        int stackDiff = (short) parameterInfo;
+
+        int newStack = itsStackTop + stackDiff;
+        if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
+
+        BootstrapEntry bsmEntry = new BootstrapEntry(bsm, bsmArgs);
+
+        if (itsBootstrapMethods == null) {
+            itsBootstrapMethods = new ObjArray();
+        }
+        int bootstrapIndex = itsBootstrapMethods.indexOf(bsmEntry);
+        if (bootstrapIndex == -1) {
+            bootstrapIndex = itsBootstrapMethods.size();
+            itsBootstrapMethods.add(bsmEntry);
+            itsBootstrapMethodsLength += bsmEntry.code.length;
+        }
+
+        short invokedynamicIndex =
+                itsConstantPool.addInvokeDynamic(methodName, methodType, bootstrapIndex);
+
+        addToCodeBuffer(ByteCode.INVOKEDYNAMIC);
+        addToCodeInt16(invokedynamicIndex);
+        addToCodeInt16(0);
+
+        itsStackTop = (short) newStack;
+        if (newStack > itsMaxStack) itsMaxStack = (short) newStack;
+        if (DEBUGSTACK) {
+            System.out.println("After invokedynamic stack = " + itsStackTop);
         }
     }
 
@@ -870,25 +880,23 @@
      *
      * @param k the constant
      */
-    public void addPush(int k)
-    {
-        if ((byte)k == k) {
+    public void addPush(int k) {
+        if ((byte) k == k) {
             if (k == -1) {
                 add(ByteCode.ICONST_M1);
             } else if (0 <= k && k <= 5) {
-                add((byte)(ByteCode.ICONST_0 + k));
+                add((byte) (ByteCode.ICONST_0 + k));
             } else {
-                add(ByteCode.BIPUSH, (byte)k);
+                add(ByteCode.BIPUSH, (byte) k);
             }
-        } else if ((short)k == k) {
-            add(ByteCode.SIPUSH, (short)k);
+        } else if ((short) k == k) {
+            add(ByteCode.SIPUSH, (short) k);
         } else {
             addLoadConstant(k);
         }
     }
 
-    public void addPush(boolean k)
-    {
+    public void addPush(boolean k) {
         add(k ? ByteCode.ICONST_1 : ByteCode.ICONST_0);
     }
 
@@ -897,9 +905,8 @@
      *
      * @param k the constant
      */
-    public void addPush(long k)
-    {
-        int ik = (int)k;
+    public void addPush(long k) {
+        int ik = (int) k;
         if (ik == k) {
             addPush(ik);
             add(ByteCode.I2L);
@@ -913,8 +920,7 @@
      *
      * @param k the constant
      */
-    public void addPush(double k)
-    {
+    public void addPush(double k) {
         if (k == 0.0) {
             // zero
             add(ByteCode.DCONST_0);
@@ -933,8 +939,8 @@
     }
 
     /**
-     * Generate the code to leave on stack the given string even if the
-     * string encoding exeeds the class file limit for single string constant
+     * Generate the code to leave on stack the given string even if the string encoding exeeds the
+     * class file limit for single string constant
      *
      * @param k the constant
      */
@@ -957,12 +963,15 @@
         addPush(length);
         addInvoke(ByteCode.INVOKESPECIAL, SB, "<init>", "(I)V");
         int cursor = 0;
-        for (;;) {
+        for (; ; ) {
             add(ByteCode.DUP);
             String s = k.substring(cursor, limit);
             addLoadConstant(s);
-            addInvoke(ByteCode.INVOKEVIRTUAL, SB, "append",
-                      "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
+            addInvoke(
+                    ByteCode.INVOKEVIRTUAL,
+                    SB,
+                    "append",
+                    "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
             add(ByteCode.POP);
             if (limit == length) {
                 break;
@@ -970,18 +979,15 @@
             cursor = limit;
             limit = itsConstantPool.getUtfEncodingLimit(k, limit, length);
         }
-        addInvoke(ByteCode.INVOKEVIRTUAL, SB, "toString",
-                  "()Ljava/lang/String;");
+        addInvoke(ByteCode.INVOKEVIRTUAL, SB, "toString", "()Ljava/lang/String;");
     }
 
     /**
-     * Check if k fits limit on string constant size imposed by class file
-     * format.
+     * Check if k fits limit on string constant size imposed by class file format.
      *
      * @param k the string constant
      */
-    public boolean isUnderStringSizeLimit(String k)
-    {
+    public boolean isUnderStringSizeLimit(String k) {
         return itsConstantPool.isUnderUtfEncodingLimit(k);
     }
 
@@ -990,8 +996,7 @@
      *
      * @param local number of local register
      */
-    public void addIStore(int local)
-    {
+    public void addIStore(int local) {
         xop(ByteCode.ISTORE_0, ByteCode.ISTORE, local);
     }
 
@@ -1000,8 +1005,7 @@
      *
      * @param local number of local register
      */
-    public void addLStore(int local)
-    {
+    public void addLStore(int local) {
         xop(ByteCode.LSTORE_0, ByteCode.LSTORE, local);
     }
 
@@ -1010,8 +1014,7 @@
      *
      * @param local number of local register
      */
-    public void addFStore(int local)
-    {
+    public void addFStore(int local) {
         xop(ByteCode.FSTORE_0, ByteCode.FSTORE, local);
     }
 
@@ -1020,8 +1023,7 @@
      *
      * @param local number of local register
      */
-    public void addDStore(int local)
-    {
+    public void addDStore(int local) {
         xop(ByteCode.DSTORE_0, ByteCode.DSTORE, local);
     }
 
@@ -1030,8 +1032,7 @@
      *
      * @param local number of local register
      */
-    public void addAStore(int local)
-    {
+    public void addAStore(int local) {
         xop(ByteCode.ASTORE_0, ByteCode.ASTORE, local);
     }
 
@@ -1040,8 +1041,7 @@
      *
      * @param local number of local register
      */
-    public void addILoad(int local)
-    {
+    public void addILoad(int local) {
         xop(ByteCode.ILOAD_0, ByteCode.ILOAD, local);
     }
 
@@ -1050,8 +1050,7 @@
      *
      * @param local number of local register
      */
-    public void addLLoad(int local)
-    {
+    public void addLLoad(int local) {
         xop(ByteCode.LLOAD_0, ByteCode.LLOAD, local);
     }
 
@@ -1060,8 +1059,7 @@
      *
      * @param local number of local register
      */
-    public void addFLoad(int local)
-    {
+    public void addFLoad(int local) {
         xop(ByteCode.FLOAD_0, ByteCode.FLOAD, local);
     }
 
@@ -1070,8 +1068,7 @@
      *
      * @param local number of local register
      */
-    public void addDLoad(int local)
-    {
+    public void addDLoad(int local) {
         xop(ByteCode.DLOAD_0, ByteCode.DLOAD, local);
     }
 
@@ -1080,47 +1077,39 @@
      *
      * @param local number of local register
      */
-    public void addALoad(int local)
-    {
+    public void addALoad(int local) {
         xop(ByteCode.ALOAD_0, ByteCode.ALOAD, local);
     }
 
-    /**
-     * Load "this" into stack.
-     */
-    public void addLoadThis()
-    {
+    /** Load "this" into stack. */
+    public void addLoadThis() {
         add(ByteCode.ALOAD_0);
     }
 
-    private void xop(int shortOp, int op, int local)
-    {
+    private void xop(int shortOp, int op, int local) {
         switch (local) {
-          case 0:
-            add(shortOp);
-            break;
-          case 1:
-            add(shortOp + 1);
-            break;
-          case 2:
-            add(shortOp + 2);
-            break;
-          case 3:
-            add(shortOp + 3);
-            break;
-          default:
-            add(op, local);
+            case 0:
+                add(shortOp);
+                break;
+            case 1:
+                add(shortOp + 1);
+                break;
+            case 2:
+                add(shortOp + 2);
+                break;
+            case 3:
+                add(shortOp + 3);
+                break;
+            default:
+                add(op, local);
         }
     }
 
-    public int addTableSwitch(int low, int high)
-    {
+    public int addTableSwitch(int low, int high) {
         if (DEBUGCODE) {
-            System.out.println("Add "+bytecodeStr(ByteCode.TABLESWITCH)
-                               +" "+low+" "+high);
+            System.out.println("Add " + bytecodeStr(ByteCode.TABLESWITCH) + " " + low + " " + high);
         }
-        if (low > high)
-            throw new ClassFileFormatException("Bad bounds: "+low+' '+ high);
+        if (low > high) throw new ClassFileFormatException("Bad bounds: " + low + ' ' + high);
 
         int newStack = itsStackTop + stackChange(ByteCode.TABLESWITCH);
         if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
@@ -1130,7 +1119,7 @@
 
         int N = addReservedCodeSpace(1 + padSize + 4 * (1 + 2 + entryCount));
         int switchStart = N;
-        itsCodeBuffer[N++] = (byte)ByteCode.TABLESWITCH;
+        itsCodeBuffer[N++] = (byte) ByteCode.TABLESWITCH;
         while (padSize != 0) {
             itsCodeBuffer[N++] = 0;
             --padSize;
@@ -1139,52 +1128,45 @@
         N = putInt32(low, itsCodeBuffer, N);
         putInt32(high, itsCodeBuffer, N);
 
-        itsStackTop = (short)newStack;
-        if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
+        itsStackTop = (short) newStack;
+        if (newStack > itsMaxStack) itsMaxStack = (short) newStack;
         if (DEBUGSTACK) {
-            System.out.println("After "+bytecodeStr(ByteCode.TABLESWITCH)
-                               +" stack = "+itsStackTop);
+            System.out.println(
+                    "After " + bytecodeStr(ByteCode.TABLESWITCH) + " stack = " + itsStackTop);
         }
 
         return switchStart;
     }
 
-    public final void markTableSwitchDefault(int switchStart)
-    {
+    public final void markTableSwitchDefault(int switchStart) {
         addSuperBlockStart(itsCodeBufferTop);
         itsJumpFroms.put(itsCodeBufferTop, switchStart);
         setTableSwitchJump(switchStart, -1, itsCodeBufferTop);
     }
 
-    public final void markTableSwitchCase(int switchStart, int caseIndex)
-    {
+    public final void markTableSwitchCase(int switchStart, int caseIndex) {
         addSuperBlockStart(itsCodeBufferTop);
         itsJumpFroms.put(itsCodeBufferTop, switchStart);
         setTableSwitchJump(switchStart, caseIndex, itsCodeBufferTop);
     }
 
-    public final void markTableSwitchCase(int switchStart, int caseIndex,
-                                          int stackTop)
-    {
+    public final void markTableSwitchCase(int switchStart, int caseIndex, int stackTop) {
         if (!(0 <= stackTop && stackTop <= itsMaxStack))
-            throw new IllegalArgumentException("Bad stack index: "+stackTop);
-        itsStackTop = (short)stackTop;
+            throw new IllegalArgumentException("Bad stack index: " + stackTop);
+        itsStackTop = (short) stackTop;
         addSuperBlockStart(itsCodeBufferTop);
         itsJumpFroms.put(itsCodeBufferTop, switchStart);
         setTableSwitchJump(switchStart, caseIndex, itsCodeBufferTop);
     }
 
     /**
-     * Set a jump case for a tableswitch instruction. The jump target should
-     * be marked as a super block start for stack map generation.
+     * Set a jump case for a tableswitch instruction. The jump target should be marked as a super
+     * block start for stack map generation.
      */
-    public void setTableSwitchJump(int switchStart, int caseIndex,
-                                   int jumpTarget)
-    {
-        if (!(0 <= jumpTarget && jumpTarget <= itsCodeBufferTop))
-            throw new IllegalArgumentException("Bad jump target: "+jumpTarget);
-        if (!(caseIndex >= -1))
-            throw new IllegalArgumentException("Bad case index: "+caseIndex);
+    public void setTableSwitchJump(int switchStart, int caseIndex, int jumpTarget) {
+        if (jumpTarget < 0 || itsCodeBufferTop < jumpTarget)
+            throw new IllegalArgumentException("Bad jump target: " + jumpTarget);
+        if (caseIndex < -1) throw new IllegalArgumentException("Bad case index: " + caseIndex);
 
         int padSize = 3 & ~switchStart; // == 3 - switchStart % 4
         int caseOffset;
@@ -1194,34 +1176,31 @@
         } else {
             caseOffset = switchStart + 1 + padSize + 4 * (3 + caseIndex);
         }
-        if (!(0 <= switchStart
-              && switchStart <= itsCodeBufferTop - 4 * 4 - padSize - 1))
-        {
+        if (switchStart < 0 || itsCodeBufferTop - 4 * 4 - padSize - 1 < switchStart) {
             throw new IllegalArgumentException(
-                switchStart+" is outside a possible range of tableswitch"
-                +" in already generated code");
+                    switchStart
+                            + " is outside a possible range of tableswitch"
+                            + " in already generated code");
         }
         if ((0xFF & itsCodeBuffer[switchStart]) != ByteCode.TABLESWITCH) {
             throw new IllegalArgumentException(
-                switchStart+" is not offset of tableswitch statement");
+                    switchStart + " is not offset of tableswitch statement");
         }
-        if (!(0 <= caseOffset && caseOffset + 4 <= itsCodeBufferTop)) {
+        if (caseOffset < 0 || itsCodeBufferTop < caseOffset + 4) {
             // caseIndex >= -1 does not guarantee that caseOffset >= 0 due
             // to a possible overflow.
-            throw new ClassFileFormatException(
-                "Too big case index: "+caseIndex);
+            throw new ClassFileFormatException("Too big case index: " + caseIndex);
         }
         // ALERT: perhaps check against case bounds?
         putInt32(jumpTarget - switchStart, itsCodeBuffer, caseOffset);
     }
 
-    public int acquireLabel()
-    {
+    public int acquireLabel() {
         int top = itsLabelTableTop;
         if (itsLabelTable == null || top == itsLabelTable.length) {
             if (itsLabelTable == null) {
                 itsLabelTable = new int[MIN_LABEL_TABLE_SIZE];
-            }else {
+            } else {
                 int[] tmp = new int[itsLabelTable.length * 2];
                 System.arraycopy(itsLabelTable, 0, tmp, 0, top);
                 itsLabelTable = tmp;
@@ -1232,14 +1211,11 @@
         return top | 0x80000000;
     }
 
-    public void markLabel(int label)
-    {
-        if (!(label < 0))
-            throw new IllegalArgumentException("Bad label, no biscuit");
+    public void markLabel(int label) {
+        if (!(label < 0)) throw new IllegalArgumentException("Bad label, no biscuit");
 
         label &= 0x7FFFFFFF;
-        if (label > itsLabelTableTop)
-            throw new IllegalArgumentException("Bad label");
+        if (label > itsLabelTableTop) throw new IllegalArgumentException("Bad label");
 
         if (itsLabelTable[label] != -1) {
             throw new IllegalStateException("Can only mark label once");
@@ -1248,8 +1224,7 @@
         itsLabelTable[label] = itsCodeBufferTop;
     }
 
-    public void markLabel(int label, short stackTop)
-    {
+    public void markLabel(int label, short stackTop) {
         markLabel(label);
         itsStackTop = stackTop;
     }
@@ -1259,59 +1234,51 @@
         markLabel(theLabel);
     }
 
-    public int getLabelPC(int label)
-    {
-        if (!(label < 0))
-            throw new IllegalArgumentException("Bad label, no biscuit");
+    public int getLabelPC(int label) {
+        if (!(label < 0)) throw new IllegalArgumentException("Bad label, no biscuit");
         label &= 0x7FFFFFFF;
-        if (!(label < itsLabelTableTop))
-            throw new IllegalArgumentException("Bad label");
+        if (!(label < itsLabelTableTop)) throw new IllegalArgumentException("Bad label");
         return itsLabelTable[label];
     }
 
-    private void addLabelFixup(int label, int fixupSite)
-    {
-        if (!(label < 0))
-            throw new IllegalArgumentException("Bad label, no biscuit");
+    private void addLabelFixup(int label, int fixupSite) {
+        if (!(label < 0)) throw new IllegalArgumentException("Bad label, no biscuit");
         label &= 0x7FFFFFFF;
-        if (!(label < itsLabelTableTop))
-            throw new IllegalArgumentException("Bad label");
+        if (!(label < itsLabelTableTop)) throw new IllegalArgumentException("Bad label");
         int top = itsFixupTableTop;
         if (itsFixupTable == null || top == itsFixupTable.length) {
             if (itsFixupTable == null) {
                 itsFixupTable = new long[MIN_FIXUP_TABLE_SIZE];
-            }else {
+            } else {
                 long[] tmp = new long[itsFixupTable.length * 2];
                 System.arraycopy(itsFixupTable, 0, tmp, 0, top);
                 itsFixupTable = tmp;
             }
         }
         itsFixupTableTop = top + 1;
-        itsFixupTable[top] = ((long)label << 32) | fixupSite;
+        itsFixupTable[top] = ((long) label << 32) | fixupSite;
     }
 
-    private  void fixLabelGotos()
-    {
+    private void fixLabelGotos() {
         byte[] codeBuffer = itsCodeBuffer;
         for (int i = 0; i < itsFixupTableTop; i++) {
             long fixup = itsFixupTable[i];
-            int label = (int)(fixup >> 32);
-            int fixupSite = (int)fixup;
+            int label = (int) (fixup >> 32);
+            int fixupSite = (int) fixup;
             int pc = itsLabelTable[label];
             if (pc == -1) {
                 // Unlocated label
-                throw new RuntimeException();
+                throw new RuntimeException("unlocated label");
             }
             // -1 to get delta from instruction start
             addSuperBlockStart(pc);
             itsJumpFroms.put(pc, fixupSite - 1);
             int offset = pc - (fixupSite - 1);
-            if ((short)offset != offset) {
-                throw new ClassFileFormatException
-                    ("Program too complex: too big jump offset");
+            if ((short) offset != offset) {
+                throw new ClassFileFormatException("Program too complex: too big jump offset");
             }
-            codeBuffer[fixupSite] = (byte)(offset >> 8);
-            codeBuffer[fixupSite + 1] = (byte)offset;
+            codeBuffer[fixupSite] = (byte) (offset >> 8);
+            codeBuffer[fixupSite + 1] = (byte) offset;
         }
         itsFixupTableTop = 0;
     }
@@ -1336,35 +1303,33 @@
     public void adjustStackTop(int delta) {
         int newStack = itsStackTop + delta;
         if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
-        itsStackTop = (short)newStack;
-        if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
+        itsStackTop = (short) newStack;
+        if (newStack > itsMaxStack) itsMaxStack = (short) newStack;
         if (DEBUGSTACK) {
-            System.out.println("After "+"adjustStackTop("+delta+")"
-                               +" stack = "+itsStackTop);
+            System.out.println(
+                    "After " + "adjustStackTop(" + delta + ")" + " stack = " + itsStackTop);
         }
     }
 
-    private void addToCodeBuffer(int b)
-    {
+    private void addToCodeBuffer(int b) {
         int N = addReservedCodeSpace(1);
-        itsCodeBuffer[N] = (byte)b;
+        itsCodeBuffer[N] = (byte) b;
     }
 
-    private void addToCodeInt16(int value)
-    {
+    private void addToCodeInt16(int value) {
         int N = addReservedCodeSpace(2);
         putInt16(value, itsCodeBuffer, N);
     }
 
-    private int addReservedCodeSpace(int size)
-    {
-        if (itsCurrentMethod == null)
-            throw new IllegalArgumentException("No method to add to");
+    private int addReservedCodeSpace(int size) {
+        if (itsCurrentMethod == null) throw new IllegalArgumentException("No method to add to");
         int oldTop = itsCodeBufferTop;
         int newTop = oldTop + size;
         if (newTop > itsCodeBuffer.length) {
             int newSize = itsCodeBuffer.length * 2;
-            if (newTop > newSize) { newSize = newTop; }
+            if (newTop > newSize) {
+                newSize = newTop;
+            }
             byte[] tmp = new byte[newSize];
             System.arraycopy(itsCodeBuffer, 0, tmp, 0, oldTop);
             itsCodeBuffer = tmp;
@@ -1373,9 +1338,8 @@
         return oldTop;
     }
 
-    public void addExceptionHandler(int startLabel, int endLabel,
-                                    int handlerLabel, String catchClassName)
-    {
+    public void addExceptionHandler(
+            int startLabel, int endLabel, int handlerLabel, String catchClassName) {
         if ((startLabel & 0x80000000) != 0x80000000)
             throw new IllegalArgumentException("Bad startLabel");
         if ((endLabel & 0x80000000) != 0x80000000)
@@ -1388,14 +1352,10 @@
          * means catch everything.  (Even when the verifier has let you throw
          * something other than a Throwable.)
          */
-        short catch_type_index = (catchClassName == null)
-                                 ? 0
-                                 : itsConstantPool.addClass(catchClassName);
-        ExceptionTableEntry newEntry = new ExceptionTableEntry(
-                                           startLabel,
-                                           endLabel,
-                                           handlerLabel,
-                                           catch_type_index);
+        short catch_type_index =
+                (catchClassName == null) ? 0 : itsConstantPool.addClass(catchClassName);
+        ExceptionTableEntry newEntry =
+                new ExceptionTableEntry(startLabel, endLabel, handlerLabel, catch_type_index);
         int N = itsExceptionTableTop;
         if (N == 0) {
             itsExceptionTable = new ExceptionTableEntry[ExceptionTableSize];
@@ -1406,12 +1366,10 @@
         }
         itsExceptionTable[N] = newEntry;
         itsExceptionTableTop = N + 1;
-
     }
 
     public void addLineNumberEntry(short lineNumber) {
-        if (itsCurrentMethod == null)
-            throw new IllegalArgumentException("No method to stop");
+        if (itsCurrentMethod == null) throw new IllegalArgumentException("No method to stop");
         int N = itsLineNumberTableTop;
         if (N == 0) {
             itsLineNumberTable = new int[LineNumberTableSize];
@@ -1425,13 +1383,13 @@
     }
 
     /**
-     * A stack map table is a code attribute introduced in Java 6 that
-     * gives type information at key points in the method body (namely, at
-     * the beginning of each super block after the first). Each frame of a
-     * stack map table contains the state of local variable and operand stack
-     * for a given super block.
+     * A stack map table is a code attribute introduced in Java 6 that gives type information at key
+     * points in the method body (namely, at the beginning of each super block after the first).
+     * Each frame of a stack map table contains the state of local variable and operand stack for a
+     * given super block.
      */
     final class StackMapTable {
+
         StackMapTable() {
             superBlocks = null;
             locals = stack = null;
@@ -1461,16 +1419,18 @@
 
             if (DEBUGSTACKMAP) {
                 System.out.println("super blocks: ");
-                for (int i = 0;
-                     i < superBlocks.length && superBlocks[i] != null; i++) {
-                    System.out.println("sb " + i + ": [" +
-                                       superBlocks[i].getStart() + ", " +
-                                       superBlocks[i].getEnd() + ")");
+                for (int i = 0; i < superBlocks.length && superBlocks[i] != null; i++) {
+                    System.out.println(
+                            "sb "
+                                    + i
+                                    + ": ["
+                                    + superBlocks[i].getStart()
+                                    + ", "
+                                    + superBlocks[i].getEnd()
+                                    + ")");
                 }
             }
 
-            superBlockDeps = getSuperBlockDependencies();
-
             verify();
 
             if (DEBUGSTACKMAP) {
@@ -1478,27 +1438,33 @@
                 for (int i = 0; i < superBlocks.length; i++) {
                     SuperBlock sb = superBlocks[i];
                     System.out.println("sb " + i + ":");
-                    TypeInfo.print(sb.getLocals(), sb.getStack(),
-                                   itsConstantPool);
+                    TypeInfo.print(sb.getLocals(), sb.getStack(), itsConstantPool);
                 }
             }
         }
 
         private SuperBlock getSuperBlockFromOffset(int offset) {
-            for (int i = 0; i < superBlocks.length; i++) {
-                SuperBlock sb = superBlocks[i];
-                if (sb == null) {
-                    break;
-                } else if (offset >= sb.getStart() && offset < sb.getEnd()) {
-                    return sb;
-                }
+            int startIdx =
+                    Arrays.binarySearch(itsSuperBlockStarts, 0, itsSuperBlockStartsTop, offset);
+
+            if (startIdx < 0) {
+                // if offset was not found, insertion point is returned (See
+                // Arrays.binarySearch)
+                // we convert it back to the matching superblock.
+                startIdx = -startIdx - 2;
+            }
+            if (startIdx < itsSuperBlockStartsTop) {
+                SuperBlock sb = superBlocks[startIdx];
+                // check, if it is really the matching one
+                if (offset < sb.getStart() || offset >= sb.getEnd()) Kit.codeBug();
+                return sb;
             }
             throw new IllegalArgumentException("bad offset: " + offset);
         }
 
         /**
-         * Determine whether or not an opcode is an actual end to a super
-         * block. This includes any returns or unconditional jumps.
+         * Determine whether or not an opcode is an actual end to a super block. This includes any
+         * returns or unconditional jumps.
          */
         private boolean isSuperBlockEnd(int opcode) {
             switch (opcode) {
@@ -1519,35 +1485,6 @@
         }
 
         /**
-         * Calculate partial dependencies for super blocks.
-         *
-         * This is used as a workaround for dead code that is generated. Only
-         * one dependency per super block is given.
-         */
-        private SuperBlock[] getSuperBlockDependencies() {
-            SuperBlock[] deps = new SuperBlock[superBlocks.length];
-
-            for (int i = 0; i < itsExceptionTableTop; i++) {
-                ExceptionTableEntry ete = itsExceptionTable[i];
-                short startPC = (short) getLabelPC(ete.itsStartLabel);
-                short handlerPC = (short) getLabelPC(ete.itsHandlerLabel);
-                SuperBlock handlerSB = getSuperBlockFromOffset(handlerPC);
-                SuperBlock dep = getSuperBlockFromOffset(startPC);
-                deps[handlerSB.getIndex()] = dep;
-            }
-            int[] targetPCs = itsJumpFroms.getKeys();
-            for (int i = 0; i < targetPCs.length; i++) {
-                int targetPC = targetPCs[i];
-                int branchPC = itsJumpFroms.getInt(targetPC, -1);
-                SuperBlock branchSB = getSuperBlockFromOffset(branchPC);
-                SuperBlock targetSB = getSuperBlockFromOffset(targetPC);
-                deps[targetSB.getIndex()] = branchSB;
-            }
-
-            return deps;
-        }
-
-        /**
          * Get the target super block of a branch instruction.
          *
          * @param bci the index of the branch instruction in the code buffer
@@ -1562,10 +1499,7 @@
             return getSuperBlockFromOffset(target);
         }
 
-        /**
-         * Determine whether or not an opcode is a conditional or unconditional
-         * jump.
-         */
+        /** Determine whether or not an opcode is a conditional or unconditional jump. */
         private boolean isBranch(int opcode) {
             switch (opcode) {
                 case ByteCode.GOTO:
@@ -1599,7 +1533,7 @@
         /**
          * Extract a logical operand from the byte code.
          *
-         * This is used, for example, to get branch offsets.
+         * <p>This is used, for example, to get branch offsets.
          */
         private int getOperand(int start, int size) {
             int result = 0;
@@ -1613,17 +1547,16 @@
         }
 
         /**
-         * Calculate initial local variable and op stack types for each super
-         * block in the method.
+         * Calculate initial local variable and op stack types for each super block in the method.
          */
         private void verify() {
             int[] initialLocals = createInitialLocals();
-            superBlocks[0].merge(initialLocals, initialLocals.length,
-                                 new int[0], 0, itsConstantPool);
+            superBlocks[0].merge(
+                    initialLocals, initialLocals.length, new int[0], 0, itsConstantPool);
 
             // Start from the top of the method and queue up block dependencies
             // as they come along.
-            workList = new SuperBlock[] { superBlocks[0] };
+            workList = new SuperBlock[] {superBlocks[0]};
             workListTop = 1;
             executeWorkList();
 
@@ -1640,41 +1573,51 @@
         /**
          * Replace the contents of a super block with no-ops.
          *
-         * The above description is not strictly true; the last instruction is
-         * an athrow instruction. This technique is borrowed from ASM's
-         * developer guide: http://asm.ow2.org/doc/developer-guide.html#deadcode
+         * <p>The above description is not strictly true; the last instruction is an athrow
+         * instruction. This technique is borrowed from ASM's developer guide:
+         * http://asm.ow2.org/doc/developer-guide.html#deadcode
          *
-         * The proposed algorithm fills a block with nop, ending it with an
-         * athrow. The stack map generated would be empty locals with an
-         * exception on the stack. In theory, it shouldn't matter what the
-         * locals are, as long as the stack has an exception for the athrow bit.
-         * However, it turns out that if the code being modified falls into an
-         * exception handler, it causes problems. Therefore, if it does, then
-         * we steal the locals from the exception block.
+         * <p>The proposed algorithm fills a block with nop, ending it with an athrow. The stack map
+         * generated would be empty locals with an exception on the stack. In theory, it shouldn't
+         * matter what the locals are, as long as the stack has an exception for the athrow bit.
+         * However, it turns out that if the code being modified falls into an exception handler, it
+         * causes problems. Therefore, if it does, then we steal the locals from the exception
+         * block.
          *
-         * If the block itself is an exception handler, we remove it from the
-         * exception table to simplify block dependencies.
+         * <p>If the block itself is an exception handler, we remove it from the exception table to
+         * simplify block dependencies.
          */
         private void killSuperBlock(SuperBlock sb) {
             int[] locals = new int[0];
-            int[] stack = new int[] { TypeInfo.OBJECT("java/lang/Throwable",
-                                                      itsConstantPool) };
+            int[] stack = new int[] {TypeInfo.OBJECT("java/lang/Throwable", itsConstantPool)};
 
             // If the super block is handled by any exception handler, use its
             // locals as the killed block's locals. Ignore uninitialized
             // handlers, because they will also be killed and removed from the
             // exception table.
+            int sbStart = sb.getStart();
             for (int i = 0; i < itsExceptionTableTop; i++) {
                 ExceptionTableEntry ete = itsExceptionTable[i];
                 int eteStart = getLabelPC(ete.itsStartLabel);
-                int eteEnd = getLabelPC(ete.itsEndLabel);
-                int handlerPC = getLabelPC(ete.itsHandlerLabel);
-                SuperBlock handlerSB = getSuperBlockFromOffset(handlerPC);
-                if ((sb.getStart() > eteStart && sb.getStart() < eteEnd) ||
-                    (eteStart > sb.getStart() && eteStart < sb.getEnd()) &&
-                    handlerSB.isInitialized()) {
+                // this is "hot" code and it has been optimized so that
+                // there are not too many function calls
+                if (sbStart > eteStart && sbStart < getLabelPC(ete.itsEndLabel)) {
+                    int handlerPC = getLabelPC(ete.itsHandlerLabel);
+                    SuperBlock handlerSB = getSuperBlockFromOffset(handlerPC);
                     locals = handlerSB.getLocals();
-                    break;
+                    if (handlerSB.isInitialized()) {
+                        break;
+                    } else {
+                        continue;
+                    }
+                }
+                if (eteStart > sbStart && eteStart < sb.getEnd()) {
+                    int handlerPC = getLabelPC(ete.itsHandlerLabel);
+                    SuperBlock handlerSB = getSuperBlockFromOffset(handlerPC);
+                    if (handlerSB.isInitialized()) {
+                        locals = handlerSB.getLocals();
+                        break;
+                    }
                 }
             }
 
@@ -1684,7 +1627,7 @@
             for (int i = 0; i < itsExceptionTableTop; i++) {
                 ExceptionTableEntry ete = itsExceptionTable[i];
                 int eteStart = getLabelPC(ete.itsStartLabel);
-                if (eteStart == sb.getStart()) {
+                if (eteStart == sb.getStart() || getLabelPC(ete.itsHandlerLabel) == sb.getStart()) {
                     for (int j = i + 1; j < itsExceptionTableTop; j++) {
                         itsExceptionTable[j - 1] = itsExceptionTable[j];
                     }
@@ -1693,8 +1636,7 @@
                 }
             }
 
-            sb.merge(locals, locals.length, stack, stack.length,
-                     itsConstantPool);
+            sb.merge(locals, locals.length, stack, stack.length, itsConstantPool);
 
             int end = sb.getEnd() - 1;
             itsCodeBuffer[end] = (byte) ByteCode.ATHROW;
@@ -1715,9 +1657,7 @@
             }
         }
 
-        /**
-         * Simulate the local variable and op stack for a super block.
-         */
+        /** Simulate the local variable and op stack for a super block. */
         private void executeBlock(SuperBlock work) {
             int bc = 0;
             int next = 0;
@@ -1725,8 +1665,43 @@
             if (DEBUGSTACKMAP) {
                 System.out.println("working on sb " + work.getIndex());
                 System.out.println("initial type state:");
-                TypeInfo.print(locals, localsTop, stack, stackTop,
-                               itsConstantPool);
+                TypeInfo.print(locals, localsTop, stack, stackTop, itsConstantPool);
+            }
+
+            int etStart = 0;
+            int etEnd = itsExceptionTableTop;
+            if (itsExceptionTableTop > 1) {
+                // determine the relevant search range in the exception table.
+                // this will reduce the search time if we have many exception
+                // blocks. There may be false positives in the range, but in
+                // most cases, this code does a good job, which leads in to
+                // fewer checks in the double-for-loop.
+                etStart = Integer.MAX_VALUE;
+                etEnd = 0;
+                for (int i = 0; i < itsExceptionTableTop; i++) {
+                    ExceptionTableEntry ete = itsExceptionTable[i];
+                    // we have found an entry, that overlaps with our work block
+                    if (work.getEnd() >= getLabelPC(ete.itsStartLabel)
+                            && work.getStart() < getLabelPC(ete.itsEndLabel)) {
+                        etStart = Math.min(etStart, i);
+                        etEnd = Math.max(etEnd, i + 1);
+                    }
+                }
+                if (DEBUGSTACK) {
+                    if (etStart == 0 && etEnd == itsExceptionTableTop) {
+                        System.out.println(
+                                "lookup size " + itsExceptionTableTop + ": could not be reduced");
+                    } else if (etStart < 0) {
+                        System.out.println(
+                                "lookup size " + itsExceptionTableTop + ": reduced completely");
+                    } else {
+                        System.out.println(
+                                "lookup size "
+                                        + itsExceptionTableTop
+                                        + ": reduced to "
+                                        + (etEnd - etStart));
+                    }
+                }
             }
 
             for (int bci = work.getStart(); bci < work.getEnd(); bci += next) {
@@ -1741,30 +1716,34 @@
                 if (isBranch(bc)) {
                     SuperBlock targetSB = getBranchTarget(bci);
                     if (DEBUGSTACKMAP) {
-                        System.out.println("sb " + work.getIndex() +
-                                           " points to sb " +
-                                           targetSB.getIndex() +
-                                           " (offset " + bci + " -> " +
-                                           targetSB.getStart() + ")");
+                        System.out.println(
+                                "sb "
+                                        + work.getIndex()
+                                        + " points to sb "
+                                        + targetSB.getIndex()
+                                        + " (offset "
+                                        + bci
+                                        + " -> "
+                                        + targetSB.getStart()
+                                        + ")");
                         System.out.println("type state at " + bci + ":");
-                        TypeInfo.print(locals, localsTop, stack, stackTop,
-                                       itsConstantPool);
+                        TypeInfo.print(locals, localsTop, stack, stackTop, itsConstantPool);
                     }
                     flowInto(targetSB);
                     if (DEBUGSTACKMAP) {
-                        System.out.println("type state of " + targetSB +
-                                           " after merge:");
-                        TypeInfo.print(targetSB.getLocals(),
-                                       targetSB.getStack(), itsConstantPool);
+                        System.out.println("type state of " + targetSB + " after merge:");
+                        TypeInfo.print(targetSB.getLocals(), targetSB.getStack(), itsConstantPool);
                     }
                 } else if (bc == ByteCode.TABLESWITCH) {
                     int switchStart = bci + 1 + (3 & ~bci); // 3 - bci % 4
                     int defaultOffset = getOperand(switchStart, 4);
-                    SuperBlock targetSB =
-                            getSuperBlockFromOffset(bci + defaultOffset);
+                    SuperBlock targetSB = getSuperBlockFromOffset(bci + defaultOffset);
                     if (DEBUGSTACK) {
-                        System.out.println("merging sb " + work.getIndex() +
-                                           " with sb " + targetSB.getIndex());
+                        System.out.println(
+                                "merging sb "
+                                        + work.getIndex()
+                                        + " with sb "
+                                        + targetSB.getIndex());
                     }
                     flowInto(targetSB);
                     int low = getOperand(switchStart + 4, 4);
@@ -1775,42 +1754,41 @@
                         int label = bci + getOperand(caseBase + 4 * i, 4);
                         targetSB = getSuperBlockFromOffset(label);
                         if (DEBUGSTACKMAP) {
-                            System.out.println("merging sb " +
-                                               work.getIndex() + " with sb " +
-                                               targetSB.getIndex());
+                            System.out.println(
+                                    "merging sb "
+                                            + work.getIndex()
+                                            + " with sb "
+                                            + targetSB.getIndex());
                         }
                         flowInto(targetSB);
                     }
                 }
 
-                for (int i = 0; i < itsExceptionTableTop; i++) {
+                for (int i = etStart; i < etEnd; i++) {
                     ExceptionTableEntry ete = itsExceptionTable[i];
-                    short startPC = (short) getLabelPC(ete.itsStartLabel);
-                    short endPC = (short) getLabelPC(ete.itsEndLabel);
+                    int startPC = getLabelPC(ete.itsStartLabel);
+                    int endPC = getLabelPC(ete.itsEndLabel);
                     if (bci < startPC || bci >= endPC) {
                         continue;
                     }
-                    short handlerPC =
-                            (short) getLabelPC(ete.itsHandlerLabel);
+                    int handlerPC = getLabelPC(ete.itsHandlerLabel);
                     SuperBlock sb = getSuperBlockFromOffset(handlerPC);
                     int exceptionType;
 
                     if (ete.itsCatchType == 0) {
-                        exceptionType = TypeInfo.OBJECT(
-                            itsConstantPool.addClass("java/lang/Throwable"));
+                        exceptionType =
+                                TypeInfo.OBJECT(itsConstantPool.addClass("java/lang/Throwable"));
                     } else {
                         exceptionType = TypeInfo.OBJECT(ete.itsCatchType);
                     }
-                    sb.merge(locals, localsTop, new int[] { exceptionType }, 1,
-                             itsConstantPool);
+                    sb.merge(locals, localsTop, new int[] {exceptionType}, 1, itsConstantPool);
                     addToWorkList(sb);
                 }
             }
 
             if (DEBUGSTACKMAP) {
                 System.out.println("end of sb " + work.getIndex() + ":");
-                TypeInfo.print(locals, localsTop, stack, stackTop,
-                               itsConstantPool);
+                TypeInfo.print(locals, localsTop, stack, stackTop, itsConstantPool);
             }
 
             // Check the last instruction to see if it is a true end of a
@@ -1820,9 +1798,8 @@
                 int nextIndex = work.getIndex() + 1;
                 if (nextIndex < superBlocks.length) {
                     if (DEBUGSTACKMAP) {
-                        System.out.println("continuing from sb " +
-                                           work.getIndex() + " into sb " +
-                                           nextIndex);
+                        System.out.println(
+                                "continuing from sb " + work.getIndex() + " into sb " + nextIndex);
                     }
                     flowInto(superBlocks[nextIndex]);
                 }
@@ -1830,8 +1807,8 @@
         }
 
         /**
-         * Perform a merge of type state and add the super block to the work
-         * list if the merge changed anything.
+         * Perform a merge of type state and add the super block to the work list if the merge
+         * changed anything.
          */
         private void flowInto(SuperBlock sb) {
             if (sb.merge(locals, localsTop, stack, stackTop, itsConstantPool)) {
@@ -1885,7 +1862,7 @@
                 case ByteCode.CASTORE:
                 case ByteCode.SASTORE:
                     pop();
-                    // fallthru
+                    // fall through
                 case ByteCode.PUTFIELD: // pop; pop
                 case ByteCode.IF_ICMPEQ:
                 case ByteCode.IF_ICMPNE:
@@ -1896,7 +1873,7 @@
                 case ByteCode.IF_ACMPEQ:
                 case ByteCode.IF_ACMPNE:
                     pop();
-                    // fallthru
+                    // fall through
                 case ByteCode.IFEQ: // pop
                 case ByteCode.IFNE:
                 case ByteCode.IFLT:
@@ -1938,7 +1915,7 @@
                 case ByteCode.DCMPL:
                 case ByteCode.DCMPG:
                     pop();
-                    // fallthru
+                    // fall through
                 case ByteCode.INEG: // pop; push(INTEGER)
                 case ByteCode.L2I:
                 case ByteCode.F2I:
@@ -1949,7 +1926,7 @@
                 case ByteCode.ARRAYLENGTH:
                 case ByteCode.INSTANCEOF:
                     pop();
-                    // fallthru
+                    // fall through
                 case ByteCode.ICONST_M1: // push(INTEGER)
                 case ByteCode.ICONST_0:
                 case ByteCode.ICONST_1:
@@ -1979,13 +1956,13 @@
                 case ByteCode.LOR:
                 case ByteCode.LXOR:
                     pop();
-                    // fallthru
+                    // fall through
                 case ByteCode.LNEG: // pop; push(LONG)
                 case ByteCode.I2L:
                 case ByteCode.F2L:
                 case ByteCode.D2L:
                     pop();
-                    // fallthru
+                    // fall through
                 case ByteCode.LCONST_0: // push(LONG)
                 case ByteCode.LCONST_1:
                 case ByteCode.LLOAD:
@@ -2002,13 +1979,13 @@
                 case ByteCode.FDIV:
                 case ByteCode.FREM:
                     pop();
-                    // fallthru
+                    // fall through
                 case ByteCode.FNEG: // pop; push(FLOAT)
                 case ByteCode.I2F:
                 case ByteCode.L2F:
                 case ByteCode.D2F:
                     pop();
-                    // fallthru
+                    // fall through
                 case ByteCode.FCONST_0: // push(FLOAT)
                 case ByteCode.FCONST_1:
                 case ByteCode.FCONST_2:
@@ -2026,13 +2003,13 @@
                 case ByteCode.DDIV:
                 case ByteCode.DREM:
                     pop();
-                    // fallthru
+                    // fall through
                 case ByteCode.DNEG: // pop; push(DOUBLE)
                 case ByteCode.I2D:
                 case ByteCode.L2D:
                 case ByteCode.F2D:
                     pop();
-                    // fallthru
+                    // fall through
                 case ByteCode.DCONST_0: // push(DOUBLE)
                 case ByteCode.DCONST_1:
                 case ByteCode.DLOAD:
@@ -2138,12 +2115,10 @@
                             push(TypeInfo.INTEGER);
                             break;
                         case ConstantPool.CONSTANT_String:
-                            push(TypeInfo.OBJECT("java/lang/String",
-                                                 itsConstantPool));
+                            push(TypeInfo.OBJECT("java/lang/String", itsConstantPool));
                             break;
                         default:
-                            throw new IllegalArgumentException(
-                                "bad const type " + constType);
+                            throw new IllegalArgumentException("bad const type " + constType);
                     }
                     break;
                 case ByteCode.NEW:
@@ -2151,8 +2126,7 @@
                     break;
                 case ByteCode.NEWARRAY:
                     pop();
-                    char componentType =
-                            arrayTypeToName(itsCodeBuffer[bci + 1]);
+                    char componentType = arrayTypeToName(itsCodeBuffer[bci + 1]);
                     index = itsConstantPool.addClass("[" + componentType);
                     push(TypeInfo.OBJECT((short) index));
                     break;
@@ -2160,16 +2134,14 @@
                     index = getOperand(bci + 1, 2);
                     className = (String) itsConstantPool.getConstantData(index);
                     pop();
-                    push(TypeInfo.OBJECT("[L" + className + ';',
-                                         itsConstantPool));
+                    push(TypeInfo.OBJECT("[L" + className + ';', itsConstantPool));
                     break;
                 case ByteCode.INVOKEVIRTUAL:
                 case ByteCode.INVOKESPECIAL:
                 case ByteCode.INVOKESTATIC:
                 case ByteCode.INVOKEINTERFACE:
                     index = getOperand(bci + 1, 2);
-                    FieldOrMethodRef m = (FieldOrMethodRef)
-                            itsConstantPool.getConstantData(index);
+                    FieldOrMethodRef m = (FieldOrMethodRef) itsConstantPool.getConstantData(index);
                     String methodType = m.getType();
                     String methodName = m.getName();
                     int parameterCount = sizeOfParameters(methodType) >>> 16;
@@ -2179,11 +2151,15 @@
                     if (bc != ByteCode.INVOKESTATIC) {
                         int instType = pop();
                         int tag = TypeInfo.getTag(instType);
-                        if (tag == TypeInfo.UNINITIALIZED_VARIABLE(0) ||
-                            tag == TypeInfo.UNINITIALIZED_THIS) {
+                        if (tag == TypeInfo.UNINITIALIZED_VARIABLE(0)
+                                || tag == TypeInfo.UNINITIALIZED_THIS) {
                             if ("<init>".equals(methodName)) {
-                                int newType =
-                                        TypeInfo.OBJECT(itsThisClassIndex);
+                                int newType;
+                                if (tag == TypeInfo.UNINITIALIZED_VARIABLE(0)) {
+                                    newType = TypeInfo.OBJECT(m.getClassName(), itsConstantPool);
+                                } else {
+                                    newType = TypeInfo.OBJECT(itsThisClassIndex);
+                                }
                                 initializeTypeInfo(instType, newType);
                             } else {
                                 throw new IllegalStateException("bad instance");
@@ -2197,13 +2173,26 @@
                         push(TypeInfo.fromType(returnType, itsConstantPool));
                     }
                     break;
+                case ByteCode.INVOKEDYNAMIC:
+                    index = getOperand(bci + 1, 2);
+                    methodType = (String) itsConstantPool.getConstantData(index);
+                    parameterCount = sizeOfParameters(methodType) >>> 16;
+                    for (int i = 0; i < parameterCount; i++) {
+                        pop();
+                    }
+                    rParen = methodType.indexOf(')');
+                    returnType = methodType.substring(rParen + 1);
+                    returnType = descriptorToInternalName(returnType);
+                    if (!returnType.equals("V")) {
+                        push(TypeInfo.fromType(returnType, itsConstantPool));
+                    }
+                    break;
                 case ByteCode.GETFIELD:
                     pop();
-                    // fallthru
+                    // fall through
                 case ByteCode.GETSTATIC:
                     index = getOperand(bci + 1, 2);
-                    FieldOrMethodRef f = (FieldOrMethodRef)
-                            itsConstantPool.getConstantData(index);
+                    FieldOrMethodRef f = (FieldOrMethodRef) itsConstantPool.getConstantData(index);
                     String fieldType = descriptorToInternalName(f.getType());
                     push(TypeInfo.fromType(fieldType, itsConstantPool));
                     break;
@@ -2255,8 +2244,7 @@
                 case ByteCode.AALOAD:
                     pop();
                     int typeIndex = pop() >>> 8;
-                    className =
-                            (String) itsConstantPool.getConstantData(typeIndex);
+                    className = (String) itsConstantPool.getConstantData(typeIndex);
                     String arrayType = className;
                     if (arrayType.charAt(0) != '[') {
                         throw new IllegalStateException("bad array type");
@@ -2292,15 +2280,14 @@
         private void executeALoad(int localIndex) {
             int type = getLocal(localIndex);
             int tag = TypeInfo.getTag(type);
-            if (tag == TypeInfo.OBJECT_TAG ||
-                tag == TypeInfo.UNINITIALIZED_THIS ||
-                tag == TypeInfo.UNINITIALIZED_VAR_TAG ||
-                tag == TypeInfo.NULL) {
+            if (tag == TypeInfo.OBJECT_TAG
+                    || tag == TypeInfo.UNINITIALIZED_THIS
+                    || tag == TypeInfo.UNINITIALIZED_VAR_TAG
+                    || tag == TypeInfo.NULL) {
                 push(type);
             } else {
-                throw new IllegalStateException("bad local variable type: " +
-                                                type + " at index: " +
-                                                localIndex);
+                throw new IllegalStateException(
+                        "bad local variable type: " + type + " at index: " + localIndex);
             }
         }
 
@@ -2314,17 +2301,15 @@
         }
 
         /**
-         * Change an UNINITIALIZED_OBJECT or UNINITIALIZED_THIS to the proper
-         * type of the object. This occurs when the proper constructor is
-         * invoked.
+         * Change an UNINITIALIZED_OBJECT or UNINITIALIZED_THIS to the proper type of the object.
+         * This occurs when the proper constructor is invoked.
          */
         private void initializeTypeInfo(int prevType, int newType) {
             initializeTypeInfo(prevType, newType, locals, localsTop);
             initializeTypeInfo(prevType, newType, stack, stackTop);
         }
 
-        private void initializeTypeInfo(int prevType, int newType, int[] data,
-                                        int dataTop) {
+        private void initializeTypeInfo(int prevType, int newType, int[] data, int dataTop) {
             for (int i = 0; i < dataTop; i++) {
                 if (data[i] == prevType) {
                     data[i] = newType;
@@ -2335,9 +2320,8 @@
         private int getLocal(int localIndex) {
             if (localIndex < localsTop) {
                 return locals[localIndex];
-            } else {
-                return TypeInfo.TOP;
             }
+            return TypeInfo.TOP;
         }
 
         private void setLocal(int localIndex, int typeInfo) {
@@ -2366,8 +2350,8 @@
         /**
          * Push two words onto the op stack.
          *
-         * This is only meant to be used as a complement to pop2(), and both
-         * methods are helpers for the more complex DUP operations.
+         * <p>This is only meant to be used as a complement to pop2(), and both methods are helpers
+         * for the more complex DUP operations.
          */
         private void push2(long typeInfo) {
             push((int) (typeInfo & 0xFFFFFF));
@@ -2380,18 +2364,16 @@
         /**
          * Pop two words from the op stack.
          *
-         * If the top of the stack is a DOUBLE or LONG, then the bottom 32 bits
-         * reflects the appropriate type and the top 32 bits are 0. Otherwise,
-         * the top 32 bits are the first word on the stack and the lower 32
-         * bits are the second word on the stack.
+         * <p>If the top of the stack is a DOUBLE or LONG, then the bottom 32 bits reflects the
+         * appropriate type and the top 32 bits are 0. Otherwise, the top 32 bits are the first word
+         * on the stack and the lower 32 bits are the second word on the stack.
          */
         private long pop2() {
             long type = pop();
             if (TypeInfo.isTwoWords((int) type)) {
                 return type;
-            } else {
-                return type << 32 | (pop() & 0xFFFFFF);
             }
+            return type << 32 | (pop() & 0xFFFFFF);
         }
 
         private void clearStack() {
@@ -2401,10 +2383,9 @@
         /**
          * Compute the output size of the stack map table.
          *
-         * Because this would share much in common with actual writing of the
-         * stack map table, we instead just write the stack map table to a
-         * buffer and return the size from it. The buffer is later used in
-         * the actual writing of bytecode.
+         * <p>Because this would share much in common with actual writing of the stack map table, we
+         * instead just write the stack map table to a buffer and return the size from it. The
+         * buffer is later used in the actual writing of bytecode.
          */
         int computeWriteSize() {
             // Allocate a buffer that can handle the worst case size of the
@@ -2422,9 +2403,7 @@
             return offset + rawStackMapTop;
         }
 
-        /**
-         * Compute a space-optimal stack map table.
-         */
+        /** Compute a space-optimal stack map table. */
         private void computeRawStackMap() {
             SuperBlock prev = superBlocks[0];
             int[] prevLocals = prev.getTrimmedLocals();
@@ -2436,10 +2415,11 @@
                 int offsetDelta = current.getStart() - prevOffset - 1;
 
                 if (currentStack.length == 0) {
-                    int last = prevLocals.length > currentLocals.length ?
-                            currentLocals.length : prevLocals.length;
-                    int delta = Math.abs(prevLocals.length -
-                                         currentLocals.length);
+                    int last =
+                            prevLocals.length > currentLocals.length
+                                    ? currentLocals.length
+                                    : prevLocals.length;
+                    int delta = Math.abs(prevLocals.length - currentLocals.length);
                     int j;
                     // Compare locals until one is different or the end of a
                     // local variable array is reached
@@ -2451,7 +2431,7 @@
                     if (j == currentLocals.length && delta == 0) {
                         // All of the compared locals are equal and the local
                         // arrays are of equal size
-                        writeSameFrame(currentLocals, offsetDelta);
+                        writeSameFrame(offsetDelta);
                     } else if (j == currentLocals.length && delta <= 3) {
                         // All of the compared locals are equal and the current
                         // frame has less locals than the previous frame
@@ -2463,19 +2443,15 @@
                     } else {
                         // Not all locals were compared were equal, so a full
                         // frame is necessary
-                        writeFullFrame(currentLocals, currentStack,
-                                       offsetDelta);
+                        writeFullFrame(currentLocals, currentStack, offsetDelta);
                     }
                 } else if (currentStack.length == 1) {
                     if (Arrays.equals(prevLocals, currentLocals)) {
-                       writeSameLocalsOneStackItemFrame(currentLocals,
-                                                        currentStack,
-                                                        offsetDelta);
+                        writeSameLocalsOneStackItemFrame(currentStack, offsetDelta);
                     } else {
                         // Output a full frame, since no other frame types have
                         // one operand stack item.
-                        writeFullFrame(currentLocals, currentStack,
-                                       offsetDelta);
+                        writeFullFrame(currentLocals, currentStack, offsetDelta);
                     }
                 } else {
                     // Any stack map frame that has more than one operand stack
@@ -2493,16 +2469,14 @@
         /**
          * Get the worst case write size of the stack map table.
          *
-         * This computes how much full frames would take, if each full frame
-         * contained the maximum number of locals and stack operands, and each
-         * verification type was 3 bytes.
+         * <p>This computes how much full frames would take, if each full frame contained the
+         * maximum number of locals and stack operands, and each verification type was 3 bytes.
          */
         private int getWorstCaseWriteSize() {
-            return (superBlocks.length - 1) * (7 + itsMaxLocals * 3 +
-                                               itsMaxStack * 3);
+            return (superBlocks.length - 1) * (7 + itsMaxLocals * 3 + itsMaxStack * 3);
         }
 
-        private void writeSameFrame(int[] locals, int offsetDelta) {
+        private void writeSameFrame(int offsetDelta) {
             if (offsetDelta <= 63) {
                 // Output a same_frame frame. Despite the name,
                 // the operand stack may differ, but the current
@@ -2512,14 +2486,11 @@
                 // Output a same_frame_extended frame. Similar to
                 // the above, except with a larger offset delta.
                 rawStackMap[rawStackMapTop++] = (byte) 251;
-                rawStackMapTop = putInt16(offsetDelta, rawStackMap,
-                                          rawStackMapTop);
+                rawStackMapTop = putInt16(offsetDelta, rawStackMap, rawStackMapTop);
             }
         }
 
-        private void writeSameLocalsOneStackItemFrame(int[] locals,
-                                                      int[] stack,
-                                                      int offsetDelta) {
+        private void writeSameLocalsOneStackItemFrame(int[] stack, int offsetDelta) {
             if (offsetDelta <= 63) {
                 // Output a same_locals_1_stack_item frame. Similar
                 // to same_frame, only with one item on the operand
@@ -2530,26 +2501,21 @@
                 // Similar to same_frame_extended, only with one
                 // item on the operand stack instead of zero.
                 rawStackMap[rawStackMapTop++] = (byte) 247;
-                rawStackMapTop = putInt16(offsetDelta, rawStackMap,
-                                          rawStackMapTop);
+                rawStackMapTop = putInt16(offsetDelta, rawStackMap, rawStackMapTop);
             }
             writeType(stack[0]);
         }
 
-        private void writeFullFrame(int[] locals, int[] stack,
-                                    int offsetDelta) {
+        private void writeFullFrame(int[] locals, int[] stack, int offsetDelta) {
             rawStackMap[rawStackMapTop++] = (byte) 255;
             rawStackMapTop = putInt16(offsetDelta, rawStackMap, rawStackMapTop);
-            rawStackMapTop = putInt16(locals.length, rawStackMap,
-                                      rawStackMapTop);
-            rawStackMapTop = writeTypes(locals);
-            rawStackMapTop = putInt16(stack.length, rawStackMap,
-                                      rawStackMapTop);
-            rawStackMapTop = writeTypes(stack);
+            rawStackMapTop = putInt16(locals.length, rawStackMap, rawStackMapTop);
+            writeTypes(locals);
+            rawStackMapTop = putInt16(stack.length, rawStackMap, rawStackMapTop);
+            writeTypes(stack);
         }
 
-        private void writeAppendFrame(int[] locals, int localsDelta,
-                                      int offsetDelta) {
+        private void writeAppendFrame(int[] locals, int localsDelta, int offsetDelta) {
             int start = locals.length - localsDelta;
             rawStackMap[rawStackMapTop++] = (byte) (251 + localsDelta);
             rawStackMapTop = putInt16(offsetDelta, rawStackMap, rawStackMapTop);
@@ -2566,7 +2532,6 @@
         }
 
         private int writeTypes(int[] types, int start) {
-            int startOffset = rawStackMapTop;
             for (int i = start; i < types.length; i++) {
                 rawStackMapTop = writeType(types[i]);
             }
@@ -2576,10 +2541,8 @@
         private int writeType(int type) {
             int tag = type & 0xFF;
             rawStackMap[rawStackMapTop++] = (byte) tag;
-            if (tag == TypeInfo.OBJECT_TAG ||
-                tag == TypeInfo.UNINITIALIZED_VAR_TAG) {
-                rawStackMapTop = putInt16(type >>> 8, rawStackMap,
-                                          rawStackMapTop);
+            if (tag == TypeInfo.OBJECT_TAG || tag == TypeInfo.UNINITIALIZED_VAR_TAG) {
+                rawStackMapTop = putInt16(type >>> 8, rawStackMap, rawStackMapTop);
             }
             return rawStackMapTop;
         }
@@ -2597,7 +2560,6 @@
         private int workListTop;
 
         private SuperBlock[] superBlocks;
-        private SuperBlock[] superBlockDeps;
 
         private byte[] rawStackMap;
         private int rawStackMapTop;
@@ -2607,9 +2569,7 @@
         static final boolean DEBUGSTACKMAP = false;
     }
 
-    /**
-     * Convert a newarray operand into an internal type.
-     */
+    /** Convert a newarray operand into an internal type. */
     private static char arrayTypeToName(int type) {
         switch (type) {
             case ByteCode.T_BOOLEAN:
@@ -2636,7 +2596,7 @@
     /**
      * Convert a class descriptor into an internal name.
      *
-     * For example, descriptor Ljava/lang/Object; becomes java/lang/Object.
+     * <p>For example, descriptor Ljava/lang/Object; becomes java/lang/Object.
      */
     private static String classDescriptorToInternalName(String descriptor) {
         return descriptor.substring(1, descriptor.length() - 1);
@@ -2663,16 +2623,15 @@
             case 'L':
                 return classDescriptorToInternalName(descriptor);
             default:
-                throw new IllegalArgumentException("bad descriptor:" +
-                                                   descriptor);
+                throw new IllegalArgumentException("bad descriptor:" + descriptor);
         }
     }
 
     /**
      * Compute the initial local variable array for the current method.
      *
-     * Creates an array of the size of the method's max locals, regardless of
-     * the number of parameters in the method.
+     * <p>Creates an array of the size of the method's max locals, regardless of the number of
+     * parameters in the method.
      */
     private int[] createInitialLocals() {
         int[] initialLocals = new int[itsMaxLocals];
@@ -2722,8 +2681,7 @@
                     ++start;
                     continue;
             }
-            String internalType =
-                    descriptorToInternalName(paramType.toString());
+            String internalType = descriptorToInternalName(paramType.toString());
             int typeInfo = TypeInfo.fromType(internalType, itsConstantPool);
             initialLocals[localsTop++] = typeInfo;
             if (TypeInfo.isTwoWords(typeInfo)) {
@@ -2740,66 +2698,74 @@
      * @param oStream the stream to write to
      * @throws IOException if writing to the stream produces an exception
      */
-    public void write(OutputStream oStream)
-        throws IOException
-    {
+    public void write(OutputStream oStream) throws IOException {
         byte[] array = toByteArray();
         oStream.write(array);
     }
 
-    private int getWriteSize()
-    {
+    private int getWriteSize() {
         int size = 0;
 
         if (itsSourceFileNameIndex != 0) {
             itsConstantPool.addUtf8("SourceFile");
         }
 
-        size += 8; //writeLong(FileHeaderConstant);
+        size += 8; // writeLong(FileHeaderConstant);
         size += itsConstantPool.getWriteSize();
-        size += 2; //writeShort(itsFlags);
-        size += 2; //writeShort(itsThisClassIndex);
-        size += 2; //writeShort(itsSuperClassIndex);
-        size += 2; //writeShort(itsInterfaces.size());
+        size += 2; // writeShort(itsFlags);
+        size += 2; // writeShort(itsThisClassIndex);
+        size += 2; // writeShort(itsSuperClassIndex);
+        size += 2; // writeShort(itsInterfaces.size());
         size += 2 * itsInterfaces.size();
 
-        size += 2; //writeShort(itsFields.size());
+        size += 2; // writeShort(itsFields.size());
         for (int i = 0; i < itsFields.size(); i++) {
-            size += ((ClassFileField)(itsFields.get(i))).getWriteSize();
+            size += ((ClassFileField) (itsFields.get(i))).getWriteSize();
         }
 
-        size += 2; //writeShort(itsMethods.size());
+        size += 2; // writeShort(itsMethods.size());
         for (int i = 0; i < itsMethods.size(); i++) {
-            size += ((ClassFileMethod)(itsMethods.get(i))).getWriteSize();
+            size += ((ClassFileMethod) (itsMethods.get(i))).getWriteSize();
         }
 
+        size += 2; // writeShort(1);  attributes count, could be zero
         if (itsSourceFileNameIndex != 0) {
-            size += 2; //writeShort(1);  attributes count
-            size += 2; //writeShort(sourceFileAttributeNameIndex);
-            size += 4; //writeInt(2);
-            size += 2; //writeShort(itsSourceFileNameIndex);
-        }else {
-            size += 2; //out.writeShort(0);  no attributes
+            size += 2; // writeShort(sourceFileAttributeNameIndex);
+            size += 4; // writeInt(2);
+            size += 2; // writeShort(itsSourceFileNameIndex);
+        }
+        if (itsBootstrapMethods != null) {
+            size += 2; // writeShort(bootstrapMethodsAttrNameIndex);
+            size += 4; // writeInt(itsBootstrapMethodsLength);
+            size += 2; // writeShort(bootstrapMethods.size());
+            size += itsBootstrapMethodsLength;
         }
 
         return size;
     }
 
-    /**
-     * Get the class file as array of bytesto the OutputStream.
-     */
-    public byte[] toByteArray()
-    {
-        int dataSize = getWriteSize();
-        byte[] data = new byte[dataSize];
-        int offset = 0;
+    /** Get the class file as array of bytesto the OutputStream. */
+    public byte[] toByteArray() {
+        short bootstrapMethodsAttrNameIndex = 0;
+        int attributeCount = 0;
 
         short sourceFileAttributeNameIndex = 0;
+        if (itsBootstrapMethods != null) {
+            ++attributeCount;
+            bootstrapMethodsAttrNameIndex = itsConstantPool.addUtf8("BootstrapMethods");
+        }
+
         if (itsSourceFileNameIndex != 0) {
-            sourceFileAttributeNameIndex = itsConstantPool.addUtf8(
-                                               "SourceFile");
+            ++attributeCount;
+            sourceFileAttributeNameIndex = itsConstantPool.addUtf8("SourceFile");
         }
 
+        // Don't calculate the data size until we know how many bootstrap
+        // methods there will be.
+        int offset = 0;
+        int dataSize = getWriteSize();
+        byte[] data = new byte[dataSize];
+
         offset = putInt32(FileHeaderConstant, data, offset);
         offset = putInt16(MinorVersion, data, offset);
         offset = putInt16(MajorVersion, data, offset);
@@ -2809,26 +2775,34 @@
         offset = putInt16(itsSuperClassIndex, data, offset);
         offset = putInt16(itsInterfaces.size(), data, offset);
         for (int i = 0; i < itsInterfaces.size(); i++) {
-            int interfaceIndex = ((Short)(itsInterfaces.get(i))).shortValue();
+            int interfaceIndex = ((Short) (itsInterfaces.get(i))).shortValue();
             offset = putInt16(interfaceIndex, data, offset);
         }
         offset = putInt16(itsFields.size(), data, offset);
         for (int i = 0; i < itsFields.size(); i++) {
-            ClassFileField field = (ClassFileField)itsFields.get(i);
+            ClassFileField field = (ClassFileField) itsFields.get(i);
             offset = field.write(data, offset);
         }
         offset = putInt16(itsMethods.size(), data, offset);
         for (int i = 0; i < itsMethods.size(); i++) {
-            ClassFileMethod method = (ClassFileMethod)itsMethods.get(i);
+            ClassFileMethod method = (ClassFileMethod) itsMethods.get(i);
             offset = method.write(data, offset);
         }
+        offset = putInt16(attributeCount, data, offset); // attributes count
+        if (itsBootstrapMethods != null) {
+            offset = putInt16(bootstrapMethodsAttrNameIndex, data, offset);
+            offset = putInt32(itsBootstrapMethodsLength + 2, data, offset);
+            offset = putInt16(itsBootstrapMethods.size(), data, offset);
+            for (int i = 0; i < itsBootstrapMethods.size(); i++) {
+                BootstrapEntry entry = (BootstrapEntry) itsBootstrapMethods.get(i);
+                System.arraycopy(entry.code, 0, data, offset, entry.code.length);
+                offset += entry.code.length;
+            }
+        }
         if (itsSourceFileNameIndex != 0) {
-            offset = putInt16(1, data, offset); // attributes count
             offset = putInt16(sourceFileAttributeNameIndex, data, offset);
             offset = putInt32(2, data, offset);
             offset = putInt16(itsSourceFileNameIndex, data, offset);
-        } else {
-            offset = putInt16(0, data, offset); // no attributes
         }
 
         if (offset != dataSize) {
@@ -2839,17 +2813,18 @@
         return data;
     }
 
-    static int putInt64(long value, byte[] array, int offset)
-    {
-        offset = putInt32((int)(value >>> 32), array, offset);
-        return putInt32((int)value, array, offset);
+    static int putInt64(long value, byte[] array, int offset) {
+        offset = putInt32((int) (value >>> 32), array, offset);
+        return putInt32((int) value, array, offset);
     }
 
-    private static void badStack(int value)
-    {
+    private static void badStack(int value) {
         String s;
-        if (value < 0) { s = "Stack underflow: "+value; }
-        else { s = "Too big stack: "+value; }
+        if (value < 0) {
+            s = "Stack underflow: " + value;
+        } else {
+            s = "Too big stack: " + value;
+        }
         throw new IllegalStateException(s);
     }
 
@@ -2861,39 +2836,38 @@
         If Java really supported references we wouldn't have to be this
         perverted.
     */
-    private static int sizeOfParameters(String pString)
-    {
+    private static int sizeOfParameters(String pString) {
         int length = pString.length();
         int rightParenthesis = pString.lastIndexOf(')');
         if (3 <= length /* minimal signature takes at least 3 chars: ()V */
-            && pString.charAt(0) == '('
-            && 1 <= rightParenthesis && rightParenthesis + 1 < length)
-        {
+                && pString.charAt(0) == '('
+                && 1 <= rightParenthesis
+                && rightParenthesis + 1 < length) {
             boolean ok = true;
             int index = 1;
             int stackDiff = 0;
             int count = 0;
-        stringLoop:
+            stringLoop:
             while (index != rightParenthesis) {
                 switch (pString.charAt(index)) {
                     default:
                         ok = false;
                         break stringLoop;
-                    case 'J' :
-                    case 'D' :
+                    case 'J':
+                    case 'D':
                         --stackDiff;
-                        // fallthru
-                    case 'B' :
-                    case 'S' :
-                    case 'C' :
-                    case 'I' :
-                    case 'Z' :
-                    case 'F' :
+                        // fall through
+                    case 'B':
+                    case 'S':
+                    case 'C':
+                    case 'I':
+                    case 'Z':
+                    case 'F':
                         --stackDiff;
                         ++count;
                         ++index;
                         continue;
-                    case '[' :
+                    case '[':
                         ++index;
                         int c = pString.charAt(index);
                         while (c == '[') {
@@ -2904,36 +2878,35 @@
                             default:
                                 ok = false;
                                 break stringLoop;
-                            case 'J' :
-                            case 'D' :
-                            case 'B' :
-                            case 'S' :
-                            case 'C' :
-                            case 'I' :
-                            case 'Z' :
-                            case 'F' :
+                            case 'J':
+                            case 'D':
+                            case 'B':
+                            case 'S':
+                            case 'C':
+                            case 'I':
+                            case 'Z':
+                            case 'F':
                                 --stackDiff;
                                 ++count;
                                 ++index;
                                 continue;
                             case 'L':
-                                // fallthru
+                                // fall through
                         }
-                          // fallthru
-                    case 'L' : {
-                        --stackDiff;
-                        ++count;
-                        ++index;
-                        int semicolon = pString.indexOf(';',  index);
-                        if (!(index + 1 <= semicolon
-                            && semicolon < rightParenthesis))
+                        // fall through
+                    case 'L':
                         {
-                            ok = false;
-                            break stringLoop;
+                            --stackDiff;
+                            ++count;
+                            ++index;
+                            int semicolon = pString.indexOf(';', index);
+                            if (!(index + 1 <= semicolon && semicolon < rightParenthesis)) {
+                                ok = false;
+                                break stringLoop;
+                            }
+                            index = semicolon + 1;
+                            continue;
                         }
-                        index = semicolon + 1;
-                        continue;
-                    }
                 }
             }
             if (ok) {
@@ -2941,21 +2914,21 @@
                     default:
                         ok = false;
                         break;
-                    case 'J' :
-                    case 'D' :
+                    case 'J':
+                    case 'D':
                         ++stackDiff;
-                        // fallthru
-                    case 'B' :
-                    case 'S' :
-                    case 'C' :
-                    case 'I' :
-                    case 'Z' :
-                    case 'F' :
-                    case 'L' :
-                    case '[' :
+                        // fall through
+                    case 'B':
+                    case 'S':
+                    case 'C':
+                    case 'I':
+                    case 'Z':
+                    case 'F':
+                    case 'L':
+                    case '[':
                         ++stackDiff;
-                        // fallthru
-                    case 'V' :
+                        // fall through
+                    case 'V':
                         break;
                 }
                 if (ok) {
@@ -2963,33 +2936,29 @@
                 }
             }
         }
-        throw new IllegalArgumentException(
-            "Bad parameter signature: "+pString);
+        throw new IllegalArgumentException("Bad parameter signature: " + pString);
     }
 
-    static int putInt16(int value, byte[] array, int offset)
-    {
-        array[offset + 0] = (byte)(value >>> 8);
-        array[offset + 1] = (byte)value;
+    static int putInt16(int value, byte[] array, int offset) {
+        array[offset + 0] = (byte) (value >>> 8);
+        array[offset + 1] = (byte) value;
         return offset + 2;
     }
 
-    static int putInt32(int value, byte[] array, int offset)
-    {
-        array[offset + 0] = (byte)(value >>> 24);
-        array[offset + 1] = (byte)(value >>> 16);
-        array[offset + 2] = (byte)(value >>> 8);
-        array[offset + 3] = (byte)value;
+    static int putInt32(int value, byte[] array, int offset) {
+        array[offset + 0] = (byte) (value >>> 24);
+        array[offset + 1] = (byte) (value >>> 16);
+        array[offset + 2] = (byte) (value >>> 8);
+        array[offset + 3] = (byte) value;
         return offset + 4;
     }
 
     /**
      * Size of a bytecode instruction, counting the opcode and its operands.
      *
-     * This is different from opcodeCount, since opcodeCount counts logical
-     * operands.
+     * <p>This is different from opcodeCount, since opcodeCount counts logical operands.
      */
-    static int opcodeLength(int opcode, boolean wide) {
+    private static int opcodeLength(int opcode, boolean wide) {
         switch (opcode) {
             case ByteCode.AALOAD:
             case ByteCode.AASTORE:
@@ -3202,23 +3171,21 @@
 
             case ByteCode.GOTO_W:
             case ByteCode.INVOKEINTERFACE:
+            case ByteCode.INVOKEDYNAMIC:
             case ByteCode.JSR_W:
                 return 5;
 
-            /*
-            case ByteCode.LOOKUPSWITCH:
-            case ByteCode.TABLESWITCH:
-                return -1;
-            */
+                /*
+                case ByteCode.LOOKUPSWITCH:
+                case ByteCode.TABLESWITCH:
+                    return -1;
+                */
         }
         throw new IllegalArgumentException("Bad opcode: " + opcode);
     }
 
-    /**
-     * Number of operands accompanying the opcode.
-     */
-    static int opcodeCount(int opcode)
-    {
+    /** Number of operands accompanying the opcode. */
+    private static int opcodeCount(int opcode) {
         switch (opcode) {
             case ByteCode.AALOAD:
             case ByteCode.AASTORE:
@@ -3431,14 +3398,11 @@
             case ByteCode.TABLESWITCH:
                 return -1;
         }
-        throw new IllegalArgumentException("Bad opcode: "+opcode);
+        throw new IllegalArgumentException("Bad opcode: " + opcode);
     }
 
-    /**
-     *  The effect on the operand stack of a given opcode.
-     */
-    static int stackChange(int opcode)
-    {
+    /** The effect on the operand stack of a given opcode. */
+    private static int stackChange(int opcode) {
         // For INVOKE... accounts only for popping this (unless static),
         // ignoring parameters and return type
         switch (opcode) {
@@ -3533,9 +3497,9 @@
             case ByteCode.IFNONNULL:
             case ByteCode.IFNULL:
             case ByteCode.IMUL:
-            case ByteCode.INVOKEINTERFACE:       //
-            case ByteCode.INVOKESPECIAL:         // but needs to account for
-            case ByteCode.INVOKEVIRTUAL:         // pops 'this' (unless static)
+            case ByteCode.INVOKEINTERFACE: //
+            case ByteCode.INVOKESPECIAL: // but needs to account for
+            case ByteCode.INVOKEVIRTUAL: // pops 'this' (unless static)
             case ByteCode.IOR:
             case ByteCode.IREM:
             case ByteCode.IRETURN:
@@ -3585,6 +3549,7 @@
             case ByteCode.INEG:
             case ByteCode.INSTANCEOF:
             case ByteCode.INVOKESTATIC:
+            case ByteCode.INVOKEDYNAMIC:
             case ByteCode.L2D:
             case ByteCode.LALOAD:
             case ByteCode.LNEG:
@@ -3660,457 +3625,663 @@
             case ByteCode.LLOAD_3:
                 return 2;
         }
-        throw new IllegalArgumentException("Bad opcode: "+opcode);
+        throw new IllegalArgumentException("Bad opcode: " + opcode);
     }
 
-        /*
-         * Number of bytes of operands generated after the opcode.
-         * Not in use currently.
-         */
-/*
-    int extra(int opcode)
-    {
-        switch (opcode) {
-            case ByteCode.AALOAD:
-            case ByteCode.AASTORE:
-            case ByteCode.ACONST_NULL:
-            case ByteCode.ALOAD_0:
-            case ByteCode.ALOAD_1:
-            case ByteCode.ALOAD_2:
-            case ByteCode.ALOAD_3:
-            case ByteCode.ARETURN:
-            case ByteCode.ARRAYLENGTH:
-            case ByteCode.ASTORE_0:
-            case ByteCode.ASTORE_1:
-            case ByteCode.ASTORE_2:
-            case ByteCode.ASTORE_3:
-            case ByteCode.ATHROW:
-            case ByteCode.BALOAD:
-            case ByteCode.BASTORE:
-            case ByteCode.BREAKPOINT:
-            case ByteCode.CALOAD:
-            case ByteCode.CASTORE:
-            case ByteCode.D2F:
-            case ByteCode.D2I:
-            case ByteCode.D2L:
-            case ByteCode.DADD:
-            case ByteCode.DALOAD:
-            case ByteCode.DASTORE:
-            case ByteCode.DCMPG:
-            case ByteCode.DCMPL:
-            case ByteCode.DCONST_0:
-            case ByteCode.DCONST_1:
-            case ByteCode.DDIV:
-            case ByteCode.DLOAD_0:
-            case ByteCode.DLOAD_1:
-            case ByteCode.DLOAD_2:
-            case ByteCode.DLOAD_3:
-            case ByteCode.DMUL:
-            case ByteCode.DNEG:
-            case ByteCode.DREM:
-            case ByteCode.DRETURN:
-            case ByteCode.DSTORE_0:
-            case ByteCode.DSTORE_1:
-            case ByteCode.DSTORE_2:
-            case ByteCode.DSTORE_3:
-            case ByteCode.DSUB:
-            case ByteCode.DUP2:
-            case ByteCode.DUP2_X1:
-            case ByteCode.DUP2_X2:
-            case ByteCode.DUP:
-            case ByteCode.DUP_X1:
-            case ByteCode.DUP_X2:
-            case ByteCode.F2D:
-            case ByteCode.F2I:
-            case ByteCode.F2L:
-            case ByteCode.FADD:
-            case ByteCode.FALOAD:
-            case ByteCode.FASTORE:
-            case ByteCode.FCMPG:
-            case ByteCode.FCMPL:
-            case ByteCode.FCONST_0:
-            case ByteCode.FCONST_1:
-            case ByteCode.FCONST_2:
-            case ByteCode.FDIV:
-            case ByteCode.FLOAD_0:
-            case ByteCode.FLOAD_1:
-            case ByteCode.FLOAD_2:
-            case ByteCode.FLOAD_3:
-            case ByteCode.FMUL:
-            case ByteCode.FNEG:
-            case ByteCode.FREM:
-            case ByteCode.FRETURN:
-            case ByteCode.FSTORE_0:
-            case ByteCode.FSTORE_1:
-            case ByteCode.FSTORE_2:
-            case ByteCode.FSTORE_3:
-            case ByteCode.FSUB:
-            case ByteCode.I2B:
-            case ByteCode.I2C:
-            case ByteCode.I2D:
-            case ByteCode.I2F:
-            case ByteCode.I2L:
-            case ByteCode.I2S:
-            case ByteCode.IADD:
-            case ByteCode.IALOAD:
-            case ByteCode.IAND:
-            case ByteCode.IASTORE:
-            case ByteCode.ICONST_0:
-            case ByteCode.ICONST_1:
-            case ByteCode.ICONST_2:
-            case ByteCode.ICONST_3:
-            case ByteCode.ICONST_4:
-            case ByteCode.ICONST_5:
-            case ByteCode.ICONST_M1:
-            case ByteCode.IDIV:
-            case ByteCode.ILOAD_0:
-            case ByteCode.ILOAD_1:
-            case ByteCode.ILOAD_2:
-            case ByteCode.ILOAD_3:
-            case ByteCode.IMPDEP1:
-            case ByteCode.IMPDEP2:
-            case ByteCode.IMUL:
-            case ByteCode.INEG:
-            case ByteCode.IOR:
-            case ByteCode.IREM:
-            case ByteCode.IRETURN:
-            case ByteCode.ISHL:
-            case ByteCode.ISHR:
-            case ByteCode.ISTORE_0:
-            case ByteCode.ISTORE_1:
-            case ByteCode.ISTORE_2:
-            case ByteCode.ISTORE_3:
-            case ByteCode.ISUB:
-            case ByteCode.IUSHR:
-            case ByteCode.IXOR:
-            case ByteCode.L2D:
-            case ByteCode.L2F:
-            case ByteCode.L2I:
-            case ByteCode.LADD:
-            case ByteCode.LALOAD:
-            case ByteCode.LAND:
-            case ByteCode.LASTORE:
-            case ByteCode.LCMP:
-            case ByteCode.LCONST_0:
-            case ByteCode.LCONST_1:
-            case ByteCode.LDIV:
-            case ByteCode.LLOAD_0:
-            case ByteCode.LLOAD_1:
-            case ByteCode.LLOAD_2:
-            case ByteCode.LLOAD_3:
-            case ByteCode.LMUL:
-            case ByteCode.LNEG:
-            case ByteCode.LOR:
-            case ByteCode.LREM:
-            case ByteCode.LRETURN:
-            case ByteCode.LSHL:
-            case ByteCode.LSHR:
-            case ByteCode.LSTORE_0:
-            case ByteCode.LSTORE_1:
-            case ByteCode.LSTORE_2:
-            case ByteCode.LSTORE_3:
-            case ByteCode.LSUB:
-            case ByteCode.LUSHR:
-            case ByteCode.LXOR:
-            case ByteCode.MONITORENTER:
-            case ByteCode.MONITOREXIT:
-            case ByteCode.NOP:
-            case ByteCode.POP2:
-            case ByteCode.POP:
-            case ByteCode.RETURN:
-            case ByteCode.SALOAD:
-            case ByteCode.SASTORE:
-            case ByteCode.SWAP:
-            case ByteCode.WIDE:
-                return 0;
+    /*
+     * Number of bytes of operands generated after the opcode.
+     * Not in use currently.
+     */
+    /*
+        int extra(int opcode)
+        {
+            switch (opcode) {
+                case ByteCode.AALOAD:
+                case ByteCode.AASTORE:
+                case ByteCode.ACONST_NULL:
+                case ByteCode.ALOAD_0:
+                case ByteCode.ALOAD_1:
+                case ByteCode.ALOAD_2:
+                case ByteCode.ALOAD_3:
+                case ByteCode.ARETURN:
+                case ByteCode.ARRAYLENGTH:
+                case ByteCode.ASTORE_0:
+                case ByteCode.ASTORE_1:
+                case ByteCode.ASTORE_2:
+                case ByteCode.ASTORE_3:
+                case ByteCode.ATHROW:
+                case ByteCode.BALOAD:
+                case ByteCode.BASTORE:
+                case ByteCode.BREAKPOINT:
+                case ByteCode.CALOAD:
+                case ByteCode.CASTORE:
+                case ByteCode.D2F:
+                case ByteCode.D2I:
+                case ByteCode.D2L:
+                case ByteCode.DADD:
+                case ByteCode.DALOAD:
+                case ByteCode.DASTORE:
+                case ByteCode.DCMPG:
+                case ByteCode.DCMPL:
+                case ByteCode.DCONST_0:
+                case ByteCode.DCONST_1:
+                case ByteCode.DDIV:
+                case ByteCode.DLOAD_0:
+                case ByteCode.DLOAD_1:
+                case ByteCode.DLOAD_2:
+                case ByteCode.DLOAD_3:
+                case ByteCode.DMUL:
+                case ByteCode.DNEG:
+                case ByteCode.DREM:
+                case ByteCode.DRETURN:
+                case ByteCode.DSTORE_0:
+                case ByteCode.DSTORE_1:
+                case ByteCode.DSTORE_2:
+                case ByteCode.DSTORE_3:
+                case ByteCode.DSUB:
+                case ByteCode.DUP2:
+                case ByteCode.DUP2_X1:
+                case ByteCode.DUP2_X2:
+                case ByteCode.DUP:
+                case ByteCode.DUP_X1:
+                case ByteCode.DUP_X2:
+                case ByteCode.F2D:
+                case ByteCode.F2I:
+                case ByteCode.F2L:
+                case ByteCode.FADD:
+                case ByteCode.FALOAD:
+                case ByteCode.FASTORE:
+                case ByteCode.FCMPG:
+                case ByteCode.FCMPL:
+                case ByteCode.FCONST_0:
+                case ByteCode.FCONST_1:
+                case ByteCode.FCONST_2:
+                case ByteCode.FDIV:
+                case ByteCode.FLOAD_0:
+                case ByteCode.FLOAD_1:
+                case ByteCode.FLOAD_2:
+                case ByteCode.FLOAD_3:
+                case ByteCode.FMUL:
+                case ByteCode.FNEG:
+                case ByteCode.FREM:
+                case ByteCode.FRETURN:
+                case ByteCode.FSTORE_0:
+                case ByteCode.FSTORE_1:
+                case ByteCode.FSTORE_2:
+                case ByteCode.FSTORE_3:
+                case ByteCode.FSUB:
+                case ByteCode.I2B:
+                case ByteCode.I2C:
+                case ByteCode.I2D:
+                case ByteCode.I2F:
+                case ByteCode.I2L:
+                case ByteCode.I2S:
+                case ByteCode.IADD:
+                case ByteCode.IALOAD:
+                case ByteCode.IAND:
+                case ByteCode.IASTORE:
+                case ByteCode.ICONST_0:
+                case ByteCode.ICONST_1:
+                case ByteCode.ICONST_2:
+                case ByteCode.ICONST_3:
+                case ByteCode.ICONST_4:
+                case ByteCode.ICONST_5:
+                case ByteCode.ICONST_M1:
+                case ByteCode.IDIV:
+                case ByteCode.ILOAD_0:
+                case ByteCode.ILOAD_1:
+                case ByteCode.ILOAD_2:
+                case ByteCode.ILOAD_3:
+                case ByteCode.IMPDEP1:
+                case ByteCode.IMPDEP2:
+                case ByteCode.IMUL:
+                case ByteCode.INEG:
+                case ByteCode.IOR:
+                case ByteCode.IREM:
+                case ByteCode.IRETURN:
+                case ByteCode.ISHL:
+                case ByteCode.ISHR:
+                case ByteCode.ISTORE_0:
+                case ByteCode.ISTORE_1:
+                case ByteCode.ISTORE_2:
+                case ByteCode.ISTORE_3:
+                case ByteCode.ISUB:
+                case ByteCode.IUSHR:
+                case ByteCode.IXOR:
+                case ByteCode.L2D:
+                case ByteCode.L2F:
+                case ByteCode.L2I:
+                case ByteCode.LADD:
+                case ByteCode.LALOAD:
+                case ByteCode.LAND:
+                case ByteCode.LASTORE:
+                case ByteCode.LCMP:
+                case ByteCode.LCONST_0:
+                case ByteCode.LCONST_1:
+                case ByteCode.LDIV:
+                case ByteCode.LLOAD_0:
+                case ByteCode.LLOAD_1:
+                case ByteCode.LLOAD_2:
+                case ByteCode.LLOAD_3:
+                case ByteCode.LMUL:
+                case ByteCode.LNEG:
+                case ByteCode.LOR:
+                case ByteCode.LREM:
+                case ByteCode.LRETURN:
+                case ByteCode.LSHL:
+                case ByteCode.LSHR:
+                case ByteCode.LSTORE_0:
+                case ByteCode.LSTORE_1:
+                case ByteCode.LSTORE_2:
+                case ByteCode.LSTORE_3:
+                case ByteCode.LSUB:
+                case ByteCode.LUSHR:
+                case ByteCode.LXOR:
+                case ByteCode.MONITORENTER:
+                case ByteCode.MONITOREXIT:
+                case ByteCode.NOP:
+                case ByteCode.POP2:
+                case ByteCode.POP:
+                case ByteCode.RETURN:
+                case ByteCode.SALOAD:
+                case ByteCode.SASTORE:
+                case ByteCode.SWAP:
+                case ByteCode.WIDE:
+                    return 0;
 
-            case ByteCode.ALOAD:
-            case ByteCode.ASTORE:
-            case ByteCode.BIPUSH:
-            case ByteCode.DLOAD:
-            case ByteCode.DSTORE:
-            case ByteCode.FLOAD:
-            case ByteCode.FSTORE:
-            case ByteCode.ILOAD:
-            case ByteCode.ISTORE:
-            case ByteCode.LDC:
-            case ByteCode.LLOAD:
-            case ByteCode.LSTORE:
-            case ByteCode.NEWARRAY:
-            case ByteCode.RET:
-                return 1;
+                case ByteCode.ALOAD:
+                case ByteCode.ASTORE:
+                case ByteCode.BIPUSH:
+                case ByteCode.DLOAD:
+                case ByteCode.DSTORE:
+                case ByteCode.FLOAD:
+                case ByteCode.FSTORE:
+                case ByteCode.ILOAD:
+                case ByteCode.ISTORE:
+                case ByteCode.LDC:
+                case ByteCode.LLOAD:
+                case ByteCode.LSTORE:
+                case ByteCode.NEWARRAY:
+                case ByteCode.RET:
+                    return 1;
 
-            case ByteCode.ANEWARRAY:
-            case ByteCode.CHECKCAST:
-            case ByteCode.GETFIELD:
-            case ByteCode.GETSTATIC:
-            case ByteCode.GOTO:
-            case ByteCode.IFEQ:
-            case ByteCode.IFGE:
-            case ByteCode.IFGT:
-            case ByteCode.IFLE:
-            case ByteCode.IFLT:
-            case ByteCode.IFNE:
-            case ByteCode.IFNONNULL:
-            case ByteCode.IFNULL:
-            case ByteCode.IF_ACMPEQ:
-            case ByteCode.IF_ACMPNE:
-            case ByteCode.IF_ICMPEQ:
-            case ByteCode.IF_ICMPGE:
-            case ByteCode.IF_ICMPGT:
-            case ByteCode.IF_ICMPLE:
-            case ByteCode.IF_ICMPLT:
-            case ByteCode.IF_ICMPNE:
-            case ByteCode.IINC:
-            case ByteCode.INSTANCEOF:
-            case ByteCode.INVOKEINTERFACE:
-            case ByteCode.INVOKESPECIAL:
-            case ByteCode.INVOKESTATIC:
-            case ByteCode.INVOKEVIRTUAL:
-            case ByteCode.JSR:
-            case ByteCode.LDC2_W:
-            case ByteCode.LDC_W:
-            case ByteCode.NEW:
-            case ByteCode.PUTFIELD:
-            case ByteCode.PUTSTATIC:
-            case ByteCode.SIPUSH:
-                return 2;
+                case ByteCode.ANEWARRAY:
+                case ByteCode.CHECKCAST:
+                case ByteCode.GETFIELD:
+                case ByteCode.GETSTATIC:
+                case ByteCode.GOTO:
+                case ByteCode.IFEQ:
+                case ByteCode.IFGE:
+                case ByteCode.IFGT:
+                case ByteCode.IFLE:
+                case ByteCode.IFLT:
+                case ByteCode.IFNE:
+                case ByteCode.IFNONNULL:
+                case ByteCode.IFNULL:
+                case ByteCode.IF_ACMPEQ:
+                case ByteCode.IF_ACMPNE:
+                case ByteCode.IF_ICMPEQ:
+                case ByteCode.IF_ICMPGE:
+                case ByteCode.IF_ICMPGT:
+                case ByteCode.IF_ICMPLE:
+                case ByteCode.IF_ICMPLT:
+                case ByteCode.IF_ICMPNE:
+                case ByteCode.IINC:
+                case ByteCode.INSTANCEOF:
+                case ByteCode.INVOKEINTERFACE:
+                case ByteCode.INVOKESPECIAL:
+                case ByteCode.INVOKESTATIC:
+                case ByteCode.INVOKEVIRTUAL:
+                case ByteCode.JSR:
+                case ByteCode.LDC2_W:
+                case ByteCode.LDC_W:
+                case ByteCode.NEW:
+                case ByteCode.PUTFIELD:
+                case ByteCode.PUTSTATIC:
+                case ByteCode.SIPUSH:
+                    return 2;
 
-            case ByteCode.MULTIANEWARRAY:
-                return 3;
+                case ByteCode.MULTIANEWARRAY:
+                    return 3;
 
-            case ByteCode.GOTO_W:
-            case ByteCode.JSR_W:
-                return 4;
+                case ByteCode.GOTO_W:
+                case ByteCode.JSR_W:
+                    return 4;
 
-            case ByteCode.LOOKUPSWITCH:    // depends on alignment
-            case ByteCode.TABLESWITCH: // depends on alignment
-                return -1;
+                case ByteCode.LOOKUPSWITCH:    // depends on alignment
+                case ByteCode.TABLESWITCH: // depends on alignment
+                    return -1;
+            }
+            throw new IllegalArgumentException("Bad opcode: "+opcode);
         }
-        throw new IllegalArgumentException("Bad opcode: "+opcode);
-    }
-*/
+    */
 
     @SuppressWarnings("unused")
-    private static String bytecodeStr(int code)
-    {
+    private static String bytecodeStr(int code) {
         if (DEBUGSTACK || DEBUGCODE) {
             switch (code) {
-                case ByteCode.NOP:              return "nop";
-                case ByteCode.ACONST_NULL:      return "aconst_null";
-                case ByteCode.ICONST_M1:        return "iconst_m1";
-                case ByteCode.ICONST_0:         return "iconst_0";
-                case ByteCode.ICONST_1:         return "iconst_1";
-                case ByteCode.ICONST_2:         return "iconst_2";
-                case ByteCode.ICONST_3:         return "iconst_3";
-                case ByteCode.ICONST_4:         return "iconst_4";
-                case ByteCode.ICONST_5:         return "iconst_5";
-                case ByteCode.LCONST_0:         return "lconst_0";
-                case ByteCode.LCONST_1:         return "lconst_1";
-                case ByteCode.FCONST_0:         return "fconst_0";
-                case ByteCode.FCONST_1:         return "fconst_1";
-                case ByteCode.FCONST_2:         return "fconst_2";
-                case ByteCode.DCONST_0:         return "dconst_0";
-                case ByteCode.DCONST_1:         return "dconst_1";
-                case ByteCode.BIPUSH:           return "bipush";
-                case ByteCode.SIPUSH:           return "sipush";
-                case ByteCode.LDC:              return "ldc";
-                case ByteCode.LDC_W:            return "ldc_w";
-                case ByteCode.LDC2_W:           return "ldc2_w";
-                case ByteCode.ILOAD:            return "iload";
-                case ByteCode.LLOAD:            return "lload";
-                case ByteCode.FLOAD:            return "fload";
-                case ByteCode.DLOAD:            return "dload";
-                case ByteCode.ALOAD:            return "aload";
-                case ByteCode.ILOAD_0:          return "iload_0";
-                case ByteCode.ILOAD_1:          return "iload_1";
-                case ByteCode.ILOAD_2:          return "iload_2";
-                case ByteCode.ILOAD_3:          return "iload_3";
-                case ByteCode.LLOAD_0:          return "lload_0";
-                case ByteCode.LLOAD_1:          return "lload_1";
-                case ByteCode.LLOAD_2:          return "lload_2";
-                case ByteCode.LLOAD_3:          return "lload_3";
-                case ByteCode.FLOAD_0:          return "fload_0";
-                case ByteCode.FLOAD_1:          return "fload_1";
-                case ByteCode.FLOAD_2:          return "fload_2";
-                case ByteCode.FLOAD_3:          return "fload_3";
-                case ByteCode.DLOAD_0:          return "dload_0";
-                case ByteCode.DLOAD_1:          return "dload_1";
-                case ByteCode.DLOAD_2:          return "dload_2";
-                case ByteCode.DLOAD_3:          return "dload_3";
-                case ByteCode.ALOAD_0:          return "aload_0";
-                case ByteCode.ALOAD_1:          return "aload_1";
-                case ByteCode.ALOAD_2:          return "aload_2";
-                case ByteCode.ALOAD_3:          return "aload_3";
-                case ByteCode.IALOAD:           return "iaload";
-                case ByteCode.LALOAD:           return "laload";
-                case ByteCode.FALOAD:           return "faload";
-                case ByteCode.DALOAD:           return "daload";
-                case ByteCode.AALOAD:           return "aaload";
-                case ByteCode.BALOAD:           return "baload";
-                case ByteCode.CALOAD:           return "caload";
-                case ByteCode.SALOAD:           return "saload";
-                case ByteCode.ISTORE:           return "istore";
-                case ByteCode.LSTORE:           return "lstore";
-                case ByteCode.FSTORE:           return "fstore";
-                case ByteCode.DSTORE:           return "dstore";
-                case ByteCode.ASTORE:           return "astore";
-                case ByteCode.ISTORE_0:         return "istore_0";
-                case ByteCode.ISTORE_1:         return "istore_1";
-                case ByteCode.ISTORE_2:         return "istore_2";
-                case ByteCode.ISTORE_3:         return "istore_3";
-                case ByteCode.LSTORE_0:         return "lstore_0";
-                case ByteCode.LSTORE_1:         return "lstore_1";
-                case ByteCode.LSTORE_2:         return "lstore_2";
-                case ByteCode.LSTORE_3:         return "lstore_3";
-                case ByteCode.FSTORE_0:         return "fstore_0";
-                case ByteCode.FSTORE_1:         return "fstore_1";
-                case ByteCode.FSTORE_2:         return "fstore_2";
-                case ByteCode.FSTORE_3:         return "fstore_3";
-                case ByteCode.DSTORE_0:         return "dstore_0";
-                case ByteCode.DSTORE_1:         return "dstore_1";
-                case ByteCode.DSTORE_2:         return "dstore_2";
-                case ByteCode.DSTORE_3:         return "dstore_3";
-                case ByteCode.ASTORE_0:         return "astore_0";
-                case ByteCode.ASTORE_1:         return "astore_1";
-                case ByteCode.ASTORE_2:         return "astore_2";
-                case ByteCode.ASTORE_3:         return "astore_3";
-                case ByteCode.IASTORE:          return "iastore";
-                case ByteCode.LASTORE:          return "lastore";
-                case ByteCode.FASTORE:          return "fastore";
-                case ByteCode.DASTORE:          return "dastore";
-                case ByteCode.AASTORE:          return "aastore";
-                case ByteCode.BASTORE:          return "bastore";
-                case ByteCode.CASTORE:          return "castore";
-                case ByteCode.SASTORE:          return "sastore";
-                case ByteCode.POP:              return "pop";
-                case ByteCode.POP2:             return "pop2";
-                case ByteCode.DUP:              return "dup";
-                case ByteCode.DUP_X1:           return "dup_x1";
-                case ByteCode.DUP_X2:           return "dup_x2";
-                case ByteCode.DUP2:             return "dup2";
-                case ByteCode.DUP2_X1:          return "dup2_x1";
-                case ByteCode.DUP2_X2:          return "dup2_x2";
-                case ByteCode.SWAP:             return "swap";
-                case ByteCode.IADD:             return "iadd";
-                case ByteCode.LADD:             return "ladd";
-                case ByteCode.FADD:             return "fadd";
-                case ByteCode.DADD:             return "dadd";
-                case ByteCode.ISUB:             return "isub";
-                case ByteCode.LSUB:             return "lsub";
-                case ByteCode.FSUB:             return "fsub";
-                case ByteCode.DSUB:             return "dsub";
-                case ByteCode.IMUL:             return "imul";
-                case ByteCode.LMUL:             return "lmul";
-                case ByteCode.FMUL:             return "fmul";
-                case ByteCode.DMUL:             return "dmul";
-                case ByteCode.IDIV:             return "idiv";
-                case ByteCode.LDIV:             return "ldiv";
-                case ByteCode.FDIV:             return "fdiv";
-                case ByteCode.DDIV:             return "ddiv";
-                case ByteCode.IREM:             return "irem";
-                case ByteCode.LREM:             return "lrem";
-                case ByteCode.FREM:             return "frem";
-                case ByteCode.DREM:             return "drem";
-                case ByteCode.INEG:             return "ineg";
-                case ByteCode.LNEG:             return "lneg";
-                case ByteCode.FNEG:             return "fneg";
-                case ByteCode.DNEG:             return "dneg";
-                case ByteCode.ISHL:             return "ishl";
-                case ByteCode.LSHL:             return "lshl";
-                case ByteCode.ISHR:             return "ishr";
-                case ByteCode.LSHR:             return "lshr";
-                case ByteCode.IUSHR:            return "iushr";
-                case ByteCode.LUSHR:            return "lushr";
-                case ByteCode.IAND:             return "iand";
-                case ByteCode.LAND:             return "land";
-                case ByteCode.IOR:              return "ior";
-                case ByteCode.LOR:              return "lor";
-                case ByteCode.IXOR:             return "ixor";
-                case ByteCode.LXOR:             return "lxor";
-                case ByteCode.IINC:             return "iinc";
-                case ByteCode.I2L:              return "i2l";
-                case ByteCode.I2F:              return "i2f";
-                case ByteCode.I2D:              return "i2d";
-                case ByteCode.L2I:              return "l2i";
-                case ByteCode.L2F:              return "l2f";
-                case ByteCode.L2D:              return "l2d";
-                case ByteCode.F2I:              return "f2i";
-                case ByteCode.F2L:              return "f2l";
-                case ByteCode.F2D:              return "f2d";
-                case ByteCode.D2I:              return "d2i";
-                case ByteCode.D2L:              return "d2l";
-                case ByteCode.D2F:              return "d2f";
-                case ByteCode.I2B:              return "i2b";
-                case ByteCode.I2C:              return "i2c";
-                case ByteCode.I2S:              return "i2s";
-                case ByteCode.LCMP:             return "lcmp";
-                case ByteCode.FCMPL:            return "fcmpl";
-                case ByteCode.FCMPG:            return "fcmpg";
-                case ByteCode.DCMPL:            return "dcmpl";
-                case ByteCode.DCMPG:            return "dcmpg";
-                case ByteCode.IFEQ:             return "ifeq";
-                case ByteCode.IFNE:             return "ifne";
-                case ByteCode.IFLT:             return "iflt";
-                case ByteCode.IFGE:             return "ifge";
-                case ByteCode.IFGT:             return "ifgt";
-                case ByteCode.IFLE:             return "ifle";
-                case ByteCode.IF_ICMPEQ:        return "if_icmpeq";
-                case ByteCode.IF_ICMPNE:        return "if_icmpne";
-                case ByteCode.IF_ICMPLT:        return "if_icmplt";
-                case ByteCode.IF_ICMPGE:        return "if_icmpge";
-                case ByteCode.IF_ICMPGT:        return "if_icmpgt";
-                case ByteCode.IF_ICMPLE:        return "if_icmple";
-                case ByteCode.IF_ACMPEQ:        return "if_acmpeq";
-                case ByteCode.IF_ACMPNE:        return "if_acmpne";
-                case ByteCode.GOTO:             return "goto";
-                case ByteCode.JSR:              return "jsr";
-                case ByteCode.RET:              return "ret";
-                case ByteCode.TABLESWITCH:      return "tableswitch";
-                case ByteCode.LOOKUPSWITCH:     return "lookupswitch";
-                case ByteCode.IRETURN:          return "ireturn";
-                case ByteCode.LRETURN:          return "lreturn";
-                case ByteCode.FRETURN:          return "freturn";
-                case ByteCode.DRETURN:          return "dreturn";
-                case ByteCode.ARETURN:          return "areturn";
-                case ByteCode.RETURN:           return "return";
-                case ByteCode.GETSTATIC:        return "getstatic";
-                case ByteCode.PUTSTATIC:        return "putstatic";
-                case ByteCode.GETFIELD:         return "getfield";
-                case ByteCode.PUTFIELD:         return "putfield";
-                case ByteCode.INVOKEVIRTUAL:    return "invokevirtual";
-                case ByteCode.INVOKESPECIAL:    return "invokespecial";
-                case ByteCode.INVOKESTATIC:     return "invokestatic";
-                case ByteCode.INVOKEINTERFACE:  return "invokeinterface";
-                case ByteCode.NEW:              return "new";
-                case ByteCode.NEWARRAY:         return "newarray";
-                case ByteCode.ANEWARRAY:        return "anewarray";
-                case ByteCode.ARRAYLENGTH:      return "arraylength";
-                case ByteCode.ATHROW:           return "athrow";
-                case ByteCode.CHECKCAST:        return "checkcast";
-                case ByteCode.INSTANCEOF:       return "instanceof";
-                case ByteCode.MONITORENTER:     return "monitorenter";
-                case ByteCode.MONITOREXIT:      return "monitorexit";
-                case ByteCode.WIDE:             return "wide";
-                case ByteCode.MULTIANEWARRAY:   return "multianewarray";
-                case ByteCode.IFNULL:           return "ifnull";
-                case ByteCode.IFNONNULL:        return "ifnonnull";
-                case ByteCode.GOTO_W:           return "goto_w";
-                case ByteCode.JSR_W:            return "jsr_w";
-                case ByteCode.BREAKPOINT:       return "breakpoint";
-
-                case ByteCode.IMPDEP1:          return "impdep1";
-                case ByteCode.IMPDEP2:          return "impdep2";
+                case ByteCode.NOP:
+                    return "nop";
+                case ByteCode.ACONST_NULL:
+                    return "aconst_null";
+                case ByteCode.ICONST_M1:
+                    return "iconst_m1";
+                case ByteCode.ICONST_0:
+                    return "iconst_0";
+                case ByteCode.ICONST_1:
+                    return "iconst_1";
+                case ByteCode.ICONST_2:
+                    return "iconst_2";
+                case ByteCode.ICONST_3:
+                    return "iconst_3";
+                case ByteCode.ICONST_4:
+                    return "iconst_4";
+                case ByteCode.ICONST_5:
+                    return "iconst_5";
+                case ByteCode.LCONST_0:
+                    return "lconst_0";
+                case ByteCode.LCONST_1:
+                    return "lconst_1";
+                case ByteCode.FCONST_0:
+                    return "fconst_0";
+                case ByteCode.FCONST_1:
+                    return "fconst_1";
+                case ByteCode.FCONST_2:
+                    return "fconst_2";
+                case ByteCode.DCONST_0:
+                    return "dconst_0";
+                case ByteCode.DCONST_1:
+                    return "dconst_1";
+                case ByteCode.BIPUSH:
+                    return "bipush";
+                case ByteCode.SIPUSH:
+                    return "sipush";
+                case ByteCode.LDC:
+                    return "ldc";
+                case ByteCode.LDC_W:
+                    return "ldc_w";
+                case ByteCode.LDC2_W:
+                    return "ldc2_w";
+                case ByteCode.ILOAD:
+                    return "iload";
+                case ByteCode.LLOAD:
+                    return "lload";
+                case ByteCode.FLOAD:
+                    return "fload";
+                case ByteCode.DLOAD:
+                    return "dload";
+                case ByteCode.ALOAD:
+                    return "aload";
+                case ByteCode.ILOAD_0:
+                    return "iload_0";
+                case ByteCode.ILOAD_1:
+                    return "iload_1";
+                case ByteCode.ILOAD_2:
+                    return "iload_2";
+                case ByteCode.ILOAD_3:
+                    return "iload_3";
+                case ByteCode.LLOAD_0:
+                    return "lload_0";
+                case ByteCode.LLOAD_1:
+                    return "lload_1";
+                case ByteCode.LLOAD_2:
+                    return "lload_2";
+                case ByteCode.LLOAD_3:
+                    return "lload_3";
+                case ByteCode.FLOAD_0:
+                    return "fload_0";
+                case ByteCode.FLOAD_1:
+                    return "fload_1";
+                case ByteCode.FLOAD_2:
+                    return "fload_2";
+                case ByteCode.FLOAD_3:
+                    return "fload_3";
+                case ByteCode.DLOAD_0:
+                    return "dload_0";
+                case ByteCode.DLOAD_1:
+                    return "dload_1";
+                case ByteCode.DLOAD_2:
+                    return "dload_2";
+                case ByteCode.DLOAD_3:
+                    return "dload_3";
+                case ByteCode.ALOAD_0:
+                    return "aload_0";
+                case ByteCode.ALOAD_1:
+                    return "aload_1";
+                case ByteCode.ALOAD_2:
+                    return "aload_2";
+                case ByteCode.ALOAD_3:
+                    return "aload_3";
+                case ByteCode.IALOAD:
+                    return "iaload";
+                case ByteCode.LALOAD:
+                    return "laload";
+                case ByteCode.FALOAD:
+                    return "faload";
+                case ByteCode.DALOAD:
+                    return "daload";
+                case ByteCode.AALOAD:
+                    return "aaload";
+                case ByteCode.BALOAD:
+                    return "baload";
+                case ByteCode.CALOAD:
+                    return "caload";
+                case ByteCode.SALOAD:
+                    return "saload";
+                case ByteCode.ISTORE:
+                    return "istore";
+                case ByteCode.LSTORE:
+                    return "lstore";
+                case ByteCode.FSTORE:
+                    return "fstore";
+                case ByteCode.DSTORE:
+                    return "dstore";
+                case ByteCode.ASTORE:
+                    return "astore";
+                case ByteCode.ISTORE_0:
+                    return "istore_0";
+                case ByteCode.ISTORE_1:
+                    return "istore_1";
+                case ByteCode.ISTORE_2:
+                    return "istore_2";
+                case ByteCode.ISTORE_3:
+                    return "istore_3";
+                case ByteCode.LSTORE_0:
+                    return "lstore_0";
+                case ByteCode.LSTORE_1:
+                    return "lstore_1";
+                case ByteCode.LSTORE_2:
+                    return "lstore_2";
+                case ByteCode.LSTORE_3:
+                    return "lstore_3";
+                case ByteCode.FSTORE_0:
+                    return "fstore_0";
+                case ByteCode.FSTORE_1:
+                    return "fstore_1";
+                case ByteCode.FSTORE_2:
+                    return "fstore_2";
+                case ByteCode.FSTORE_3:
+                    return "fstore_3";
+                case ByteCode.DSTORE_0:
+                    return "dstore_0";
+                case ByteCode.DSTORE_1:
+                    return "dstore_1";
+                case ByteCode.DSTORE_2:
+                    return "dstore_2";
+                case ByteCode.DSTORE_3:
+                    return "dstore_3";
+                case ByteCode.ASTORE_0:
+                    return "astore_0";
+                case ByteCode.ASTORE_1:
+                    return "astore_1";
+                case ByteCode.ASTORE_2:
+                    return "astore_2";
+                case ByteCode.ASTORE_3:
+                    return "astore_3";
+                case ByteCode.IASTORE:
+                    return "iastore";
+                case ByteCode.LASTORE:
+                    return "lastore";
+                case ByteCode.FASTORE:
+                    return "fastore";
+                case ByteCode.DASTORE:
+                    return "dastore";
+                case ByteCode.AASTORE:
+                    return "aastore";
+                case ByteCode.BASTORE:
+                    return "bastore";
+                case ByteCode.CASTORE:
+                    return "castore";
+                case ByteCode.SASTORE:
+                    return "sastore";
+                case ByteCode.POP:
+                    return "pop";
+                case ByteCode.POP2:
+                    return "pop2";
+                case ByteCode.DUP:
+                    return "dup";
+                case ByteCode.DUP_X1:
+                    return "dup_x1";
+                case ByteCode.DUP_X2:
+                    return "dup_x2";
+                case ByteCode.DUP2:
+                    return "dup2";
+                case ByteCode.DUP2_X1:
+                    return "dup2_x1";
+                case ByteCode.DUP2_X2:
+                    return "dup2_x2";
+                case ByteCode.SWAP:
+                    return "swap";
+                case ByteCode.IADD:
+                    return "iadd";
+                case ByteCode.LADD:
+                    return "ladd";
+                case ByteCode.FADD:
+                    return "fadd";
+                case ByteCode.DADD:
+                    return "dadd";
+                case ByteCode.ISUB:
+                    return "isub";
+                case ByteCode.LSUB:
+                    return "lsub";
+                case ByteCode.FSUB:
+                    return "fsub";
+                case ByteCode.DSUB:
+                    return "dsub";
+                case ByteCode.IMUL:
+                    return "imul";
+                case ByteCode.LMUL:
+                    return "lmul";
+                case ByteCode.FMUL:
+                    return "fmul";
+                case ByteCode.DMUL:
+                    return "dmul";
+                case ByteCode.IDIV:
+                    return "idiv";
+                case ByteCode.LDIV:
+                    return "ldiv";
+                case ByteCode.FDIV:
+                    return "fdiv";
+                case ByteCode.DDIV:
+                    return "ddiv";
+                case ByteCode.IREM:
+                    return "irem";
+                case ByteCode.LREM:
+                    return "lrem";
+                case ByteCode.FREM:
+                    return "frem";
+                case ByteCode.DREM:
+                    return "drem";
+                case ByteCode.INEG:
+                    return "ineg";
+                case ByteCode.LNEG:
+                    return "lneg";
+                case ByteCode.FNEG:
+                    return "fneg";
+                case ByteCode.DNEG:
+                    return "dneg";
+                case ByteCode.ISHL:
+                    return "ishl";
+                case ByteCode.LSHL:
+                    return "lshl";
+                case ByteCode.ISHR:
+                    return "ishr";
+                case ByteCode.LSHR:
+                    return "lshr";
+                case ByteCode.IUSHR:
+                    return "iushr";
+                case ByteCode.LUSHR:
+                    return "lushr";
+                case ByteCode.IAND:
+                    return "iand";
+                case ByteCode.LAND:
+                    return "land";
+                case ByteCode.IOR:
+                    return "ior";
+                case ByteCode.LOR:
+                    return "lor";
+                case ByteCode.IXOR:
+                    return "ixor";
+                case ByteCode.LXOR:
+                    return "lxor";
+                case ByteCode.IINC:
+                    return "iinc";
+                case ByteCode.I2L:
+                    return "i2l";
+                case ByteCode.I2F:
+                    return "i2f";
+                case ByteCode.I2D:
+                    return "i2d";
+                case ByteCode.L2I:
+                    return "l2i";
+                case ByteCode.L2F:
+                    return "l2f";
+                case ByteCode.L2D:
+                    return "l2d";
+                case ByteCode.F2I:
+                    return "f2i";
+                case ByteCode.F2L:
+                    return "f2l";
+                case ByteCode.F2D:
+                    return "f2d";
+                case ByteCode.D2I:
+                    return "d2i";
+                case ByteCode.D2L:
+                    return "d2l";
+                case ByteCode.D2F:
+                    return "d2f";
+                case ByteCode.I2B:
+                    return "i2b";
+                case ByteCode.I2C:
+                    return "i2c";
+                case ByteCode.I2S:
+                    return "i2s";
+                case ByteCode.LCMP:
+                    return "lcmp";
+                case ByteCode.FCMPL:
+                    return "fcmpl";
+                case ByteCode.FCMPG:
+                    return "fcmpg";
+                case ByteCode.DCMPL:
+                    return "dcmpl";
+                case ByteCode.DCMPG:
+                    return "dcmpg";
+                case ByteCode.IFEQ:
+                    return "ifeq";
+                case ByteCode.IFNE:
+                    return "ifne";
+                case ByteCode.IFLT:
+                    return "iflt";
+                case ByteCode.IFGE:
+                    return "ifge";
+                case ByteCode.IFGT:
+                    return "ifgt";
+                case ByteCode.IFLE:
+                    return "ifle";
+                case ByteCode.IF_ICMPEQ:
+                    return "if_icmpeq";
+                case ByteCode.IF_ICMPNE:
+                    return "if_icmpne";
+                case ByteCode.IF_ICMPLT:
+                    return "if_icmplt";
+                case ByteCode.IF_ICMPGE:
+                    return "if_icmpge";
+                case ByteCode.IF_ICMPGT:
+                    return "if_icmpgt";
+                case ByteCode.IF_ICMPLE:
+                    return "if_icmple";
+                case ByteCode.IF_ACMPEQ:
+                    return "if_acmpeq";
+                case ByteCode.IF_ACMPNE:
+                    return "if_acmpne";
+                case ByteCode.GOTO:
+                    return "goto";
+                case ByteCode.JSR:
+                    return "jsr";
+                case ByteCode.RET:
+                    return "ret";
+                case ByteCode.TABLESWITCH:
+                    return "tableswitch";
+                case ByteCode.LOOKUPSWITCH:
+                    return "lookupswitch";
+                case ByteCode.IRETURN:
+                    return "ireturn";
+                case ByteCode.LRETURN:
+                    return "lreturn";
+                case ByteCode.FRETURN:
+                    return "freturn";
+                case ByteCode.DRETURN:
+                    return "dreturn";
+                case ByteCode.ARETURN:
+                    return "areturn";
+                case ByteCode.RETURN:
+                    return "return";
+                case ByteCode.GETSTATIC:
+                    return "getstatic";
+                case ByteCode.PUTSTATIC:
+                    return "putstatic";
+                case ByteCode.GETFIELD:
+                    return "getfield";
+                case ByteCode.PUTFIELD:
+                    return "putfield";
+                case ByteCode.INVOKEVIRTUAL:
+                    return "invokevirtual";
+                case ByteCode.INVOKESPECIAL:
+                    return "invokespecial";
+                case ByteCode.INVOKESTATIC:
+                    return "invokestatic";
+                case ByteCode.INVOKEINTERFACE:
+                    return "invokeinterface";
+                case ByteCode.INVOKEDYNAMIC:
+                    return "invokedynamic";
+                case ByteCode.NEW:
+                    return "new";
+                case ByteCode.NEWARRAY:
+                    return "newarray";
+                case ByteCode.ANEWARRAY:
+                    return "anewarray";
+                case ByteCode.ARRAYLENGTH:
+                    return "arraylength";
+                case ByteCode.ATHROW:
+                    return "athrow";
+                case ByteCode.CHECKCAST:
+                    return "checkcast";
+                case ByteCode.INSTANCEOF:
+                    return "instanceof";
+                case ByteCode.MONITORENTER:
+                    return "monitorenter";
+                case ByteCode.MONITOREXIT:
+                    return "monitorexit";
+                case ByteCode.WIDE:
+                    return "wide";
+                case ByteCode.MULTIANEWARRAY:
+                    return "multianewarray";
+                case ByteCode.IFNULL:
+                    return "ifnull";
+                case ByteCode.IFNONNULL:
+                    return "ifnonnull";
+                case ByteCode.GOTO_W:
+                    return "goto_w";
+                case ByteCode.JSR_W:
+                    return "jsr_w";
+                case ByteCode.BREAKPOINT:
+                    return "breakpoint";
+
+                case ByteCode.IMPDEP1:
+                    return "impdep1";
+                case ByteCode.IMPDEP2:
+                    return "impdep2";
             }
         }
         return "";
     }
 
-    final char[] getCharBuffer(int minimalSize)
-    {
+    final char[] getCharBuffer(int minimalSize) {
         if (minimalSize > tmpCharBuffer.length) {
             int newSize = tmpCharBuffer.length * 2;
-            if (minimalSize > newSize) { newSize = minimalSize; }
+            if (minimalSize > newSize) {
+                newSize = minimalSize;
+            }
             tmpCharBuffer = new char[newSize];
         }
         return tmpCharBuffer;
@@ -4119,11 +4290,9 @@
     /**
      * Add a pc as the start of super block.
      *
-     * A pc is the beginning of a super block if:
-     * - pc == 0
-     * - it is the target of a branch instruction
-     * - it is the beginning of an exception handler
-     * - it is directly after an unconditional jump
+     * <p>A pc is the beginning of a super block if: - pc == 0 - it is the target of a branch
+     * instruction - it is the beginning of an exception handler - it is directly after an
+     * unconditional jump
      */
     private void addSuperBlockStart(int pc) {
         if (GenerateStackMap) {
@@ -4131,8 +4300,7 @@
                 itsSuperBlockStarts = new int[SuperBlockStartsSize];
             } else if (itsSuperBlockStarts.length == itsSuperBlockStartsTop) {
                 int[] tmp = new int[itsSuperBlockStartsTop * 2];
-                System.arraycopy(itsSuperBlockStarts, 0, tmp, 0,
-                                 itsSuperBlockStartsTop);
+                System.arraycopy(itsSuperBlockStarts, 0, tmp, 0, itsSuperBlockStartsTop);
                 itsSuperBlockStarts = tmp;
             }
             itsSuperBlockStarts[itsSuperBlockStartsTop++] = pc;
@@ -4142,14 +4310,14 @@
     /**
      * Sort the list of recorded super block starts and remove duplicates.
      *
-     * Also adds exception handling blocks as block starts, since there is no
-     * explicit control flow to these. Used for stack map table generation.
+     * <p>Also adds exception handling blocks as block starts, since there is no explicit control
+     * flow to these. Used for stack map table generation.
      */
     private void finalizeSuperBlockStarts() {
         if (GenerateStackMap) {
             for (int i = 0; i < itsExceptionTableTop; i++) {
                 ExceptionTableEntry ete = itsExceptionTable[i];
-                short handlerPC = (short) getLabelPC(ete.itsHandlerLabel);
+                int handlerPC = getLabelPC(ete.itsHandlerLabel);
                 addSuperBlockStart(handlerPC);
             }
             Arrays.sort(itsSuperBlockStarts, 0, itsSuperBlockStartsTop);
@@ -4205,8 +4373,9 @@
         try {
             is = ClassFileWriter.class.getResourceAsStream("ClassFileWriter.class");
             if (is == null) {
-                is = ClassLoader.getSystemResourceAsStream(
-                    "org/mozilla/classfile/ClassFileWriter.class");
+                is =
+                        ClassLoader.getSystemResourceAsStream(
+                                "org/mozilla/classfile/ClassFileWriter.class");
             }
             byte[] header = new byte[8];
             // read loop is required since JDK7 will only provide 2 bytes
@@ -4234,7 +4403,73 @@
         }
     }
 
-    private final static int FileHeaderConstant = 0xCAFEBABE;
+    final class BootstrapEntry {
+
+        final byte[] code;
+
+        BootstrapEntry(ClassFileWriter.MHandle bsm, Object... bsmArgs) {
+            int length = 2 + 2 + bsmArgs.length * 2;
+            code = new byte[length];
+            putInt16(itsConstantPool.addMethodHandle(bsm), code, 0);
+            putInt16(bsmArgs.length, code, 2);
+            for (int i = 0; i < bsmArgs.length; i++) {
+                putInt16(itsConstantPool.addConstant(bsmArgs[i]), code, 4 + i * 2);
+            }
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            return obj instanceof BootstrapEntry
+                    && Arrays.equals(code, ((BootstrapEntry) obj).code);
+        }
+
+        @Override
+        public int hashCode() {
+            return ~Arrays.hashCode(code);
+        }
+    }
+
+    public static final class MHandle {
+
+        final byte tag;
+        final String owner;
+        final String name;
+        final String desc;
+
+        public MHandle(byte tag, String owner, String name, String desc) {
+            this.tag = tag;
+            this.owner = owner;
+            this.name = name;
+            this.desc = desc;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == this) {
+                return true;
+            }
+            if (!(obj instanceof MHandle)) {
+                return false;
+            }
+            MHandle mh = (MHandle) obj;
+            return tag == mh.tag
+                    && owner.equals(mh.owner)
+                    && name.equals(mh.name)
+                    && desc.equals(mh.desc);
+        }
+
+        @Override
+        public int hashCode() {
+            return tag + owner.hashCode() * name.hashCode() * desc.hashCode();
+        }
+
+        @Override
+        public String toString() {
+            return owner + '.' + name + desc + " (" + tag + ')';
+        }
+    }
+
+    private static final int FileHeaderConstant = 0xCAFEBABE;
     // Set DEBUG flags to true to get better checking and progress info.
     private static final boolean DEBUGSTACK = false;
     private static final boolean DEBUGLABELS = false;
@@ -4242,10 +4477,11 @@
 
     private String generatedClassName;
 
-    private ExceptionTableEntry itsExceptionTable[];
+    private ExceptionTableEntry[] itsExceptionTable;
+
     private int itsExceptionTableTop;
 
-    private int itsLineNumberTable[];   // pack start_pc & line_number together
+    private int[] itsLineNumberTable; // pack start_pc & line_number together
     private int itsLineNumberTableTop;
 
     private byte[] itsCodeBuffer = new byte[256];
@@ -4272,927 +4508,13 @@
     private int[] itsLabelTable;
     private int itsLabelTableTop;
 
-// itsFixupTable[i] = (label_index << 32) | fixup_site
+    // itsFixupTable[i] = (label_index << 32) | fixup_site
     private static final int MIN_FIXUP_TABLE_SIZE = 40;
     private long[] itsFixupTable;
     private int itsFixupTableTop;
     private ObjArray itsVarDescriptors;
+    private ObjArray itsBootstrapMethods;
+    private int itsBootstrapMethodsLength = 0;
 
     private char[] tmpCharBuffer = new char[64];
 }
-
-final class ExceptionTableEntry
-{
-
-    ExceptionTableEntry(int startLabel, int endLabel,
-                        int handlerLabel, short catchType)
-    {
-        itsStartLabel = startLabel;
-        itsEndLabel = endLabel;
-        itsHandlerLabel = handlerLabel;
-        itsCatchType = catchType;
-    }
-
-    int itsStartLabel;
-    int itsEndLabel;
-    int itsHandlerLabel;
-    short itsCatchType;
-}
-
-final class ClassFileField
-{
-
-    ClassFileField(short nameIndex, short typeIndex, short flags)
-    {
-        itsNameIndex = nameIndex;
-        itsTypeIndex = typeIndex;
-        itsFlags = flags;
-        itsHasAttributes = false;
-    }
-
-    void setAttributes(short attr1, short attr2, short attr3, int index)
-    {
-        itsHasAttributes = true;
-        itsAttr1 = attr1;
-        itsAttr2 = attr2;
-        itsAttr3 = attr3;
-        itsIndex = index;
-    }
-
-    int write(byte[] data, int offset)
-    {
-        offset = ClassFileWriter.putInt16(itsFlags, data, offset);
-        offset = ClassFileWriter.putInt16(itsNameIndex, data, offset);
-        offset = ClassFileWriter.putInt16(itsTypeIndex, data, offset);
-        if (!itsHasAttributes) {
-            // write 0 attributes
-            offset = ClassFileWriter.putInt16(0, data, offset);
-        } else {
-            offset = ClassFileWriter.putInt16(1, data, offset);
-            offset = ClassFileWriter.putInt16(itsAttr1, data, offset);
-            offset = ClassFileWriter.putInt16(itsAttr2, data, offset);
-            offset = ClassFileWriter.putInt16(itsAttr3, data, offset);
-            offset = ClassFileWriter.putInt16(itsIndex, data, offset);
-        }
-        return offset;
-    }
-
-    int getWriteSize()
-    {
-        int size = 2 * 3;
-        if (!itsHasAttributes) {
-            size += 2;
-        } else {
-            size += 2 + 2 * 4;
-        }
-        return size;
-    }
-
-    private short itsNameIndex;
-    private short itsTypeIndex;
-    private short itsFlags;
-    private boolean itsHasAttributes;
-    private short itsAttr1, itsAttr2, itsAttr3;
-    private int itsIndex;
-}
-
-final class ClassFileMethod
-{
-
-    ClassFileMethod(String name, short nameIndex, String type, short typeIndex,
-                    short flags)
-    {
-        itsName = name;
-        itsNameIndex = nameIndex;
-        itsType = type;
-        itsTypeIndex = typeIndex;
-        itsFlags = flags;
-    }
-
-    void setCodeAttribute(byte codeAttribute[])
-    {
-        itsCodeAttribute = codeAttribute;
-    }
-
-    int write(byte[] data, int offset)
-    {
-        offset = ClassFileWriter.putInt16(itsFlags, data, offset);
-        offset = ClassFileWriter.putInt16(itsNameIndex, data, offset);
-        offset = ClassFileWriter.putInt16(itsTypeIndex, data, offset);
-        // Code attribute only
-        offset = ClassFileWriter.putInt16(1, data, offset);
-        System.arraycopy(itsCodeAttribute, 0, data, offset,
-                         itsCodeAttribute.length);
-        offset += itsCodeAttribute.length;
-        return offset;
-    }
-
-    int getWriteSize()
-    {
-        return 2 * 4 + itsCodeAttribute.length;
-    }
-
-    String getName()
-    {
-        return itsName;
-    }
-
-    String getType()
-    {
-        return itsType;
-    }
-
-    short getFlags()
-    {
-        return itsFlags;
-    }
-
-    private String itsName;
-    private String itsType;
-    private short itsNameIndex;
-    private short itsTypeIndex;
-    private short itsFlags;
-    private byte[] itsCodeAttribute;
-
-}
-
-final class ConstantPool
-{
-
-    ConstantPool(ClassFileWriter cfw)
-    {
-        this.cfw = cfw;
-        itsTopIndex = 1;       // the zero'th entry is reserved
-        itsPool = new byte[ConstantPoolSize];
-        itsTop = 0;
-    }
-
-    private static final int ConstantPoolSize = 256;
-    static final byte
-        CONSTANT_Class = 7,
-        CONSTANT_Fieldref = 9,
-        CONSTANT_Methodref = 10,
-        CONSTANT_InterfaceMethodref = 11,
-        CONSTANT_String = 8,
-        CONSTANT_Integer = 3,
-        CONSTANT_Float = 4,
-        CONSTANT_Long = 5,
-        CONSTANT_Double = 6,
-        CONSTANT_NameAndType = 12,
-        CONSTANT_Utf8 = 1;
-
-    int write(byte[] data, int offset)
-    {
-        offset = ClassFileWriter.putInt16((short)itsTopIndex, data, offset);
-        System.arraycopy(itsPool, 0, data, offset, itsTop);
-        offset += itsTop;
-        return offset;
-    }
-
-    int getWriteSize()
-    {
-        return 2 + itsTop;
-    }
-
-    int addConstant(int k)
-    {
-        ensure(5);
-        itsPool[itsTop++] = CONSTANT_Integer;
-        itsTop = ClassFileWriter.putInt32(k, itsPool, itsTop);
-        itsPoolTypes.put(itsTopIndex, CONSTANT_Integer);
-        return (short)(itsTopIndex++);
-    }
-
-    int addConstant(long k)
-    {
-        ensure(9);
-        itsPool[itsTop++] = CONSTANT_Long;
-        itsTop = ClassFileWriter.putInt64(k, itsPool, itsTop);
-        int index = itsTopIndex;
-        itsTopIndex += 2;
-        itsPoolTypes.put(index, CONSTANT_Long);
-        return index;
-    }
-
-    int addConstant(float k)
-    {
-        ensure(5);
-        itsPool[itsTop++] = CONSTANT_Float;
-        int bits = Float.floatToIntBits(k);
-        itsTop = ClassFileWriter.putInt32(bits, itsPool, itsTop);
-        itsPoolTypes.put(itsTopIndex, CONSTANT_Float);
-        return itsTopIndex++;
-    }
-
-    int addConstant(double k)
-    {
-        ensure(9);
-        itsPool[itsTop++] = CONSTANT_Double;
-        long bits = Double.doubleToLongBits(k);
-        itsTop = ClassFileWriter.putInt64(bits, itsPool, itsTop);
-        int index = itsTopIndex;
-        itsTopIndex += 2;
-        itsPoolTypes.put(index, CONSTANT_Double);
-        return index;
-    }
-
-    int addConstant(String k)
-    {
-        int utf8Index = 0xFFFF & addUtf8(k);
-        int theIndex = itsStringConstHash.getInt(utf8Index, -1);
-        if (theIndex == -1) {
-            theIndex = itsTopIndex++;
-            ensure(3);
-            itsPool[itsTop++] = CONSTANT_String;
-            itsTop = ClassFileWriter.putInt16(utf8Index, itsPool, itsTop);
-            itsStringConstHash.put(utf8Index, theIndex);
-        }
-        itsPoolTypes.put(theIndex, CONSTANT_String);
-        return theIndex;
-    }
-
-    boolean isUnderUtfEncodingLimit(String s)
-    {
-        int strLen = s.length();
-        if (strLen * 3 <= MAX_UTF_ENCODING_SIZE) {
-            return true;
-        } else if (strLen > MAX_UTF_ENCODING_SIZE) {
-            return false;
-        }
-        return strLen == getUtfEncodingLimit(s, 0, strLen);
-    }
-
-    /**
-     * Get maximum i such that <tt>start <= i <= end</tt> and
-     * <tt>s.substring(start, i)</tt> fits JVM UTF string encoding limit.
-     */
-    int getUtfEncodingLimit(String s, int start, int end)
-    {
-        if ((end - start) * 3 <= MAX_UTF_ENCODING_SIZE) {
-            return end;
-        }
-        int limit = MAX_UTF_ENCODING_SIZE;
-        for (int i = start; i != end; i++) {
-            int c = s.charAt(i);
-            if (0 != c && c <= 0x7F) {
-                --limit;
-            } else if (c < 0x7FF) {
-                limit -= 2;
-            } else {
-                limit -= 3;
-            }
-            if (limit < 0) {
-                return i;
-            }
-        }
-        return end;
-    }
-
-    short addUtf8(String k)
-    {
-        int theIndex = itsUtf8Hash.get(k, -1);
-        if (theIndex == -1) {
-            int strLen = k.length();
-            boolean tooBigString;
-            if (strLen > MAX_UTF_ENCODING_SIZE) {
-                tooBigString = true;
-            } else {
-                tooBigString = false;
-                // Ask for worst case scenario buffer when each char takes 3
-                // bytes
-                ensure(1 + 2 + strLen * 3);
-                int top = itsTop;
-
-                itsPool[top++] = CONSTANT_Utf8;
-                top += 2; // skip length
-
-                char[] chars = cfw.getCharBuffer(strLen);
-                k.getChars(0, strLen, chars, 0);
-
-                for (int i = 0; i != strLen; i++) {
-                    int c = chars[i];
-                    if (c != 0 && c <= 0x7F) {
-                        itsPool[top++] = (byte)c;
-                    } else if (c > 0x7FF) {
-                        itsPool[top++] = (byte)(0xE0 | (c >> 12));
-                        itsPool[top++] = (byte)(0x80 | ((c >> 6) & 0x3F));
-                        itsPool[top++] = (byte)(0x80 | (c & 0x3F));
-                    } else {
-                        itsPool[top++] = (byte)(0xC0 | (c >> 6));
-                        itsPool[top++] = (byte)(0x80 | (c & 0x3F));
-                    }
-                }
-
-                int utfLen = top - (itsTop + 1 + 2);
-                if (utfLen > MAX_UTF_ENCODING_SIZE) {
-                    tooBigString = true;
-                } else {
-                    // Write back length
-                    itsPool[itsTop + 1] = (byte)(utfLen >>> 8);
-                    itsPool[itsTop + 2] = (byte)utfLen;
-
-                    itsTop = top;
-                    theIndex = itsTopIndex++;
-                    itsUtf8Hash.put(k, theIndex);
-                }
-            }
-            if (tooBigString) {
-                throw new IllegalArgumentException("Too big string");
-            }
-        }
-        setConstantData(theIndex, k);
-        itsPoolTypes.put(theIndex, CONSTANT_Utf8);
-        return (short)theIndex;
-    }
-
-    private short addNameAndType(String name, String type)
-    {
-        short nameIndex = addUtf8(name);
-        short typeIndex = addUtf8(type);
-        ensure(5);
-        itsPool[itsTop++] = CONSTANT_NameAndType;
-        itsTop = ClassFileWriter.putInt16(nameIndex, itsPool, itsTop);
-        itsTop = ClassFileWriter.putInt16(typeIndex, itsPool, itsTop);
-        itsPoolTypes.put(itsTopIndex, CONSTANT_NameAndType);
-        return (short)(itsTopIndex++);
-    }
-
-    short addClass(String className)
-    {
-        int theIndex = itsClassHash.get(className, -1);
-        if (theIndex == -1) {
-            String slashed = className;
-            if (className.indexOf('.') > 0) {
-                slashed = ClassFileWriter.getSlashedForm(className);
-                theIndex = itsClassHash.get(slashed, -1);
-                if (theIndex != -1) {
-                    itsClassHash.put(className, theIndex);
-                }
-            }
-            if (theIndex == -1) {
-                int utf8Index = addUtf8(slashed);
-                ensure(3);
-                itsPool[itsTop++] = CONSTANT_Class;
-                itsTop = ClassFileWriter.putInt16(utf8Index, itsPool, itsTop);
-                theIndex = itsTopIndex++;
-                itsClassHash.put(slashed, theIndex);
-                if (className != slashed) {
-                    itsClassHash.put(className, theIndex);
-                }
-            }
-        }
-        setConstantData(theIndex, className);
-        itsPoolTypes.put(theIndex, CONSTANT_Class);
-        return (short)theIndex;
-    }
-
-    short addFieldRef(String className, String fieldName, String fieldType)
-    {
-        FieldOrMethodRef ref = new FieldOrMethodRef(className, fieldName,
-                                                    fieldType);
-
-        int theIndex = itsFieldRefHash.get(ref, -1);
-        if (theIndex == -1) {
-            short ntIndex = addNameAndType(fieldName, fieldType);
-            short classIndex = addClass(className);
-            ensure(5);
-            itsPool[itsTop++] = CONSTANT_Fieldref;
-            itsTop = ClassFileWriter.putInt16(classIndex, itsPool, itsTop);
-            itsTop = ClassFileWriter.putInt16(ntIndex, itsPool, itsTop);
-            theIndex = itsTopIndex++;
-            itsFieldRefHash.put(ref, theIndex);
-        }
-        setConstantData(theIndex, ref);
-        itsPoolTypes.put(theIndex, CONSTANT_Fieldref);
-        return (short)theIndex;
-    }
-
-    short addMethodRef(String className, String methodName,
-                       String methodType)
-    {
-        FieldOrMethodRef ref = new FieldOrMethodRef(className, methodName,
-                                                    methodType);
-
-        int theIndex = itsMethodRefHash.get(ref, -1);
-        if (theIndex == -1) {
-            short ntIndex = addNameAndType(methodName, methodType);
-            short classIndex = addClass(className);
-            ensure(5);
-            itsPool[itsTop++] = CONSTANT_Methodref;
-            itsTop = ClassFileWriter.putInt16(classIndex, itsPool, itsTop);
-            itsTop = ClassFileWriter.putInt16(ntIndex, itsPool, itsTop);
-            theIndex = itsTopIndex++;
-            itsMethodRefHash.put(ref, theIndex);
-        }
-        setConstantData(theIndex, ref);
-        itsPoolTypes.put(theIndex, CONSTANT_Methodref);
-        return (short)theIndex;
-    }
-
-    short addInterfaceMethodRef(String className,
-                                String methodName, String methodType)
-    {
-        short ntIndex = addNameAndType(methodName, methodType);
-        short classIndex = addClass(className);
-        ensure(5);
-        itsPool[itsTop++] = CONSTANT_InterfaceMethodref;
-        itsTop = ClassFileWriter.putInt16(classIndex, itsPool, itsTop);
-        itsTop = ClassFileWriter.putInt16(ntIndex, itsPool, itsTop);
-        FieldOrMethodRef r = new FieldOrMethodRef(className, methodName,
-                                                  methodType);
-        setConstantData(itsTopIndex, r);
-        itsPoolTypes.put(itsTopIndex, CONSTANT_InterfaceMethodref);
-        return (short)(itsTopIndex++);
-    }
-
-    Object getConstantData(int index)
-    {
-        return itsConstantData.getObject(index);
-    }
-
-    void setConstantData(int index, Object data)
-    {
-        itsConstantData.put(index, data);
-    }
-
-    byte getConstantType(int index)
-    {
-        return (byte) itsPoolTypes.getInt(index, 0);
-    }
-
-    void ensure(int howMuch)
-    {
-        if (itsTop + howMuch > itsPool.length) {
-            int newCapacity = itsPool.length * 2;
-            if (itsTop + howMuch > newCapacity) {
-                newCapacity = itsTop + howMuch;
-            }
-            byte[] tmp = new byte[newCapacity];
-            System.arraycopy(itsPool, 0, tmp, 0, itsTop);
-            itsPool = tmp;
-        }
-    }
-
-    private ClassFileWriter cfw;
-
-    private static final int MAX_UTF_ENCODING_SIZE = 65535;
-
-    private UintMap itsStringConstHash = new UintMap();
-    private ObjToIntMap itsUtf8Hash = new ObjToIntMap();
-    private ObjToIntMap itsFieldRefHash = new ObjToIntMap();
-    private ObjToIntMap itsMethodRefHash = new ObjToIntMap();
-    private ObjToIntMap itsClassHash = new ObjToIntMap();
-
-    private int itsTop;
-    private int itsTopIndex;
-    private UintMap itsConstantData = new UintMap();
-    private UintMap itsPoolTypes = new UintMap();
-    private byte itsPool[];
-}
-
-final class FieldOrMethodRef
-{
-    FieldOrMethodRef(String className, String name, String type)
-    {
-        this.className = className;
-        this.name = name;
-        this.type = type;
-    }
-
-    public String getClassName()
-    {
-        return className;
-    }
-
-    public String getName()
-    {
-        return name;
-    }
-
-    public String getType()
-    {
-        return type;
-    }
-
-    @Override
-    public boolean equals(Object obj)
-    {
-        if (!(obj instanceof FieldOrMethodRef)) { return false; }
-        FieldOrMethodRef x = (FieldOrMethodRef)obj;
-        return className.equals(x.className)
-            && name.equals(x.name)
-            && type.equals(x.type);
-    }
-
-    @Override
-    public int hashCode()
-    {
-        if (hashCode == -1) {
-            int h1 = className.hashCode();
-            int h2 = name.hashCode();
-            int h3 = type.hashCode();
-            hashCode = h1 ^ h2 ^ h3;
-        }
-        return hashCode;
-    }
-
-    private String className;
-    private String name;
-    private String type;
-    private int hashCode = -1;
-}
-
-/**
- * A super block is defined as a contiguous chunk of code with a single entry
- * point and multiple exit points (therefore ending in an unconditional jump
- * or the end of the method). This is used to emulate OpenJDK's compiler, which
- * outputs stack map frames at the start of every super block except the method
- * start.
- */
-final class SuperBlock {
-    SuperBlock(int index, int start, int end, int[] initialLocals) {
-        this.index = index;
-        this.start = start;
-        this.end = end;
-        locals = new int[initialLocals.length];
-        System.arraycopy(initialLocals, 0, locals, 0, initialLocals.length);
-        stack = new int[0];
-        isInitialized = false;
-        isInQueue = false;
-    }
-
-    int getIndex() {
-        return index;
-    }
-
-    int[] getLocals() {
-        int[] copy = new int[locals.length];
-        System.arraycopy(locals, 0, copy, 0, locals.length);
-        return copy;
-    }
-
-    /**
-     * Get a copy of the super block's locals without any trailing TOP types.
-     *
-     * This is useful for actual writing stack maps; during the computation of
-     * stack map types, all local arrays have the same size; the max locals for
-     * the method. In addition, DOUBLE and LONG types have trailing TOP types
-     * because they occupy two words. For writing purposes, these are not
-     * useful.
-     */
-    int[] getTrimmedLocals() {
-        int last = locals.length - 1;
-        // Exclude all of the trailing TOPs not bound to a DOUBLE/LONG
-        while (last >= 0 && locals[last] == TypeInfo.TOP &&
-               !TypeInfo.isTwoWords(locals[last - 1])) {
-            last--;
-        }
-        last++;
-        // Exclude trailing TOPs following a DOUBLE/LONG
-        int size = last;
-        for (int i = 0; i < last; i++) {
-            if (TypeInfo.isTwoWords(locals[i])) {
-                size--;
-            }
-        }
-        int[] copy = new int[size];
-        for (int i = 0, j = 0; i < size; i++, j++) {
-            copy[i] = locals[j];
-            if (TypeInfo.isTwoWords(locals[j])) {
-                j++;
-            }
-        }
-        return copy;
-    }
-
-    int[] getStack() {
-        int[] copy = new int[stack.length];
-        System.arraycopy(stack, 0, copy, 0, stack.length);
-        return copy;
-    }
-
-    boolean merge(int[] locals, int localsTop, int[] stack, int stackTop,
-                  ConstantPool pool) {
-        if (!isInitialized) {
-            System.arraycopy(locals, 0, this.locals, 0, localsTop);
-            this.stack = new int[stackTop];
-            System.arraycopy(stack, 0, this.stack, 0, stackTop);
-            isInitialized = true;
-            return true;
-        } else if (this.locals.length == localsTop &&
-                   this.stack.length == stackTop) {
-            boolean localsChanged = mergeState(this.locals, locals, localsTop,
-                                               pool);
-            boolean stackChanged = mergeState(this.stack, stack, stackTop,
-                                              pool);
-            return localsChanged || stackChanged;
-        } else {
-            if (ClassFileWriter.StackMapTable.DEBUGSTACKMAP) {
-                System.out.println("bad merge");
-                System.out.println("current type state:");
-                TypeInfo.print(this.locals, this.stack, pool);
-                System.out.println("incoming type state:");
-                TypeInfo.print(locals, localsTop, stack, stackTop, pool);
-            }
-            throw new IllegalArgumentException("bad merge attempt");
-        }
-    }
-
-    /**
-     * Merge an operand stack or local variable array with incoming state.
-     *
-     * They are treated the same way; by this point, it should already be
-     * ensured that the array sizes are the same, which is the only additional
-     * constraint that is imposed on merging operand stacks (the local variable
-     * array is always the same size).
-     */
-    private boolean mergeState(int[] current, int[] incoming, int size,
-                               ConstantPool pool) {
-        boolean changed = false;
-        for (int i = 0; i < size; i++) {
-            int currentType = current[i];
-
-            current[i] = TypeInfo.merge(current[i], incoming[i], pool);
-            if (currentType != current[i]) {
-                changed = true;
-            }
-        }
-        return changed;
-    }
-
-    int getStart() {
-        return start;
-    }
-
-    int getEnd() {
-        return end;
-    }
-
-    @Override
-    public String toString() {
-        return "sb " + index;
-    }
-
-    boolean isInitialized() {
-        return isInitialized;
-    }
-
-    void setInitialized(boolean b) {
-        isInitialized = b;
-    }
-
-    boolean isInQueue() {
-        return isInQueue;
-    }
-
-    void setInQueue(boolean b) {
-        isInQueue = b;
-    }
-
-    private int index;
-    private int start;
-    private int end;
-    private int[] locals;
-    private int[] stack;
-    private boolean isInitialized;
-    private boolean isInQueue;
-}
-
-/**
- * Helper class for internal representations of type information. In most
- * cases, type information can be represented by a constant, but in some
- * cases, a payload is included. Despite the payload coming after the type
- * tag in the output, we store it in bits 8-23 for uniformity; the tag is
- * always in bits 0-7.
- */
-final class TypeInfo {
-    private TypeInfo() { }
-
-    static final int TOP = 0;
-    static final int INTEGER = 1;
-    static final int FLOAT = 2;
-    static final int DOUBLE = 3;
-    static final int LONG = 4;
-    static final int NULL = 5;
-    static final int UNINITIALIZED_THIS = 6;
-    static final int OBJECT_TAG = 7;
-    static final int UNINITIALIZED_VAR_TAG = 8;
-
-    static final int OBJECT(int constantPoolIndex) {
-        return ((constantPoolIndex & 0xFFFF) << 8) | OBJECT_TAG;
-    }
-
-    static final int OBJECT(String type, ConstantPool pool) {
-        return OBJECT(pool.addClass(type));
-    }
-
-    static final int UNINITIALIZED_VARIABLE(int bytecodeOffset) {
-        return ((bytecodeOffset & 0xFFFF) << 8) | UNINITIALIZED_VAR_TAG;
-    }
-
-    static final int getTag(int typeInfo) {
-        return typeInfo & 0xFF;
-    }
-
-    static final int getPayload(int typeInfo) {
-        return typeInfo >>> 8;
-    }
-
-    /**
-     * Treat the result of getPayload as a constant pool index and fetch the
-     * corresponding String mapped to it.
-     *
-     * Only works on OBJECT types.
-     */
-    static final String getPayloadAsType(int typeInfo, ConstantPool pool) {
-        if (getTag(typeInfo) == OBJECT_TAG) {
-            return (String) pool.getConstantData(getPayload(typeInfo));
-        }
-        throw new IllegalArgumentException("expecting object type");
-    }
-
-    /**
-     * Create type information from an internal type.
-     */
-    static final int fromType(String type, ConstantPool pool) {
-        if (type.length() == 1) {
-            switch (type.charAt(0)) {
-                case 'B': // sbyte
-                case 'C': // unicode char
-                case 'S': // short
-                case 'Z': // boolean
-                case 'I': // all of the above are verified as integers
-                    return INTEGER;
-                case 'D':
-                    return DOUBLE;
-                case 'F':
-                    return FLOAT;
-                case 'J':
-                    return LONG;
-                default:
-                    throw new IllegalArgumentException("bad type");
-            }
-        }
-        return TypeInfo.OBJECT(type, pool);
-    }
-
-    static boolean isTwoWords(int type) {
-        return type == DOUBLE || type == LONG;
-    }
-
-    /**
-     * Merge two verification types.
-     *
-     * In most cases, the verification types must be the same. For example,
-     * INTEGER and DOUBLE cannot be merged and an exception will be thrown.
-     * The basic rules are:
-     *
-     * - If the types are equal, simply return one.
-     * - If either type is TOP, return TOP.
-     * - If either type is NULL, return the other type.
-     * - If both types are objects, find the lowest common ancestor in the
-     *   class hierarchy.
-     *
-     * This method uses reflection to traverse the class hierarchy. Therefore,
-     * it is assumed that the current class being generated is never the target
-     * of a full object-object merge, which would need to load the current
-     * class reflectively.
-     */
-    static int merge(int current, int incoming, ConstantPool pool) {
-        int currentTag = getTag(current);
-        int incomingTag = getTag(incoming);
-        boolean currentIsObject = currentTag == TypeInfo.OBJECT_TAG;
-        boolean incomingIsObject = incomingTag == TypeInfo.OBJECT_TAG;
-
-        if (current == incoming || (currentIsObject && incoming == NULL)) {
-            return current;
-        } else if (currentTag == TypeInfo.TOP ||
-            incomingTag == TypeInfo.TOP) {
-            return TypeInfo.TOP;
-        } else if (current == NULL && incomingIsObject) {
-            return incoming;
-        } else if (currentIsObject && incomingIsObject) {
-            String currentName = getPayloadAsType(current, pool);
-            String incomingName = getPayloadAsType(incoming, pool);
-            // The class file always has the class and super names in the same
-            // spot. The constant order is: class_data, class_name, super_data,
-            // super_name.
-            String currentlyGeneratedName = (String) pool.getConstantData(2);
-            String currentlyGeneratedSuperName =
-                    (String) pool.getConstantData(4);
-
-            // If any of the merged types are the class that's currently being
-            // generated, automatically start at the super class instead. At
-            // this point, we already know the classes are different, so we
-            // don't need to handle that case.
-            if (currentName.equals(currentlyGeneratedName)) {
-                currentName = currentlyGeneratedSuperName;
-            }
-            if (incomingName.equals(currentlyGeneratedName)) {
-                incomingName = currentlyGeneratedSuperName;
-            }
-
-            Class<?> currentClass = getClassFromInternalName(currentName);
-            Class<?> incomingClass = getClassFromInternalName(incomingName);
-
-            if (currentClass.isAssignableFrom(incomingClass)) {
-                return current;
-            } else if (incomingClass.isAssignableFrom(currentClass)) {
-                return incoming;
-            } else if (incomingClass.isInterface() ||
-                       currentClass.isInterface()) {
-                // For verification purposes, Sun specifies that interfaces are
-                // subtypes of Object. Therefore, we know that the merge result
-                // involving interfaces where one is not assignable to the
-                // other results in Object.
-                return OBJECT("java/lang/Object", pool);
-            } else {
-                Class<?> commonClass = incomingClass.getSuperclass();
-                while (commonClass != null) {
-                    if (commonClass.isAssignableFrom(currentClass)) {
-                        String name = commonClass.getName();
-                        name = ClassFileWriter.getSlashedForm(name);
-                        return OBJECT(name, pool);
-                    }
-                    commonClass = commonClass.getSuperclass();
-                }
-            }
-        }
-        throw new IllegalArgumentException("bad merge attempt between " +
-                                           toString(current, pool) + " and " +
-                                           toString(incoming, pool));
-    }
-
-    static String toString(int type, ConstantPool pool) {
-        int tag = getTag(type);
-        switch (tag) {
-            case TypeInfo.TOP:
-                return "top";
-            case TypeInfo.INTEGER:
-                return "int";
-            case TypeInfo.FLOAT:
-                return "float";
-            case TypeInfo.DOUBLE:
-                return "double";
-            case TypeInfo.LONG:
-                return "long";
-            case TypeInfo.NULL:
-                return "null";
-            case TypeInfo.UNINITIALIZED_THIS:
-                return "uninitialized_this";
-            default:
-                if (tag == TypeInfo.OBJECT_TAG) {
-                    return getPayloadAsType(type, pool);
-                } else if (tag == TypeInfo.UNINITIALIZED_VAR_TAG) {
-                    return "uninitialized";
-                } else {
-                    throw new IllegalArgumentException("bad type");
-                }
-        }
-    }
-
-    /**
-     * Take an internal name and return a java.lang.Class instance that
-     * represents it.
-     *
-     * For example, given "java/lang/Object", returns the equivalent of
-     * Class.forName("java.lang.Object"), but also handles exceptions.
-     */
-    static Class<?> getClassFromInternalName(String internalName) {
-        try {
-            return Class.forName(internalName.replace('/', '.'));
-        } catch (ClassNotFoundException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    static String toString(int[] types, ConstantPool pool) {
-        return toString(types, types.length, pool);
-    }
-
-    static String toString(int[] types, int typesTop, ConstantPool pool) {
-        StringBuilder sb = new StringBuilder();
-        sb.append("[");
-        for (int i = 0; i < typesTop; i++) {
-            if (i > 0) {
-                sb.append(", ");
-            }
-            sb.append(toString(types[i], pool));
-        }
-        sb.append("]");
-        return sb.toString();
-    }
-
-    static void print(int[] locals, int[] stack, ConstantPool pool) {
-        print(locals, locals.length, stack, stack.length, pool);
-    }
-
-    static void print(int[] locals, int localsTop, int[] stack, int stackTop,
-                       ConstantPool pool) {
-        System.out.print("locals: ");
-        System.out.println(toString(locals, localsTop, pool));
-        System.out.print("stack: ");
-        System.out.println(toString(stack, stackTop, pool));
-        System.out.println();
-    }
-}
diff -Nru rhino-1.7.7.2/src/org/mozilla/classfile/ConstantEntry.java rhino-1.7.14/src/org/mozilla/classfile/ConstantEntry.java
--- rhino-1.7.7.2/src/org/mozilla/classfile/ConstantEntry.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/classfile/ConstantEntry.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,57 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.classfile;
+
+final class ConstantEntry {
+  private int type;
+  private int intval;
+  private long longval;
+  private String str1;
+  private String str2;
+  private int hashcode;
+
+  ConstantEntry(int type, int intval, String str1, String str2) {
+    this.type = type;
+    this.intval = intval;
+    this.str1 = str1;
+    this.str2 = str2;
+    hashcode = type ^ intval + str1.hashCode() * str2.hashCode();
+  }
+
+  @Override
+  public int hashCode() {
+    return hashcode;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (!(obj instanceof ConstantEntry)) {
+      return false;
+    }
+    ConstantEntry entry = (ConstantEntry) obj;
+    if (type != entry.type) {
+      return false;
+    }
+    switch (type) {
+      case ConstantPool.CONSTANT_Integer:
+      case ConstantPool.CONSTANT_Float:
+        return intval == entry.intval;
+      case ConstantPool.CONSTANT_Long:
+      case ConstantPool.CONSTANT_Double:
+        return longval == entry.longval;
+      case ConstantPool.CONSTANT_NameAndType:
+        return str1.equals(entry.str1) && str2.equals(entry.str2);
+      case ConstantPool.CONSTANT_InvokeDynamic:
+        return intval == entry.intval
+            && str1.equals(entry.str1)
+            && str2.equals(entry.str2);
+      default:
+        throw new RuntimeException("unsupported constant type");
+
+    }
+  }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/classfile/ConstantPool.java rhino-1.7.14/src/org/mozilla/classfile/ConstantPool.java
--- rhino-1.7.7.2/src/org/mozilla/classfile/ConstantPool.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/classfile/ConstantPool.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,418 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.classfile;
+
+import org.mozilla.javascript.ObjToIntMap;
+import org.mozilla.javascript.UintMap;
+
+final class ConstantPool
+{
+  ConstantPool(ClassFileWriter cfw)
+  {
+    this.cfw = cfw;
+    itsTopIndex = 1;       // the zero'th entry is reserved
+    itsPool = new byte[ConstantPoolSize];
+    itsTop = 0;
+  }
+
+  private static final int ConstantPoolSize = 256;
+  static final byte
+      CONSTANT_Class = 7,
+      CONSTANT_Fieldref = 9,
+      CONSTANT_Methodref = 10,
+      CONSTANT_InterfaceMethodref = 11,
+      CONSTANT_String = 8,
+      CONSTANT_Integer = 3,
+      CONSTANT_Float = 4,
+      CONSTANT_Long = 5,
+      CONSTANT_Double = 6,
+      CONSTANT_NameAndType = 12,
+      CONSTANT_Utf8 = 1,
+      CONSTANT_MethodType = 16,
+      CONSTANT_MethodHandle = 15,
+      CONSTANT_InvokeDynamic = 18;
+
+  int write(byte[] data, int offset)
+  {
+    offset = ClassFileWriter.putInt16((short)itsTopIndex, data, offset);
+    System.arraycopy(itsPool, 0, data, offset, itsTop);
+    offset += itsTop;
+    return offset;
+  }
+
+  int getWriteSize()
+  {
+    return 2 + itsTop;
+  }
+
+  int addConstant(int k)
+  {
+    ensure(5);
+    itsPool[itsTop++] = CONSTANT_Integer;
+    itsTop = ClassFileWriter.putInt32(k, itsPool, itsTop);
+    itsPoolTypes.put(itsTopIndex, CONSTANT_Integer);
+    return (short)(itsTopIndex++);
+  }
+
+  int addConstant(long k)
+  {
+    ensure(9);
+    itsPool[itsTop++] = CONSTANT_Long;
+    itsTop = ClassFileWriter.putInt64(k, itsPool, itsTop);
+    int index = itsTopIndex;
+    itsTopIndex += 2;
+    itsPoolTypes.put(index, CONSTANT_Long);
+    return index;
+  }
+
+  int addConstant(float k)
+  {
+    ensure(5);
+    itsPool[itsTop++] = CONSTANT_Float;
+    int bits = Float.floatToIntBits(k);
+    itsTop = ClassFileWriter.putInt32(bits, itsPool, itsTop);
+    itsPoolTypes.put(itsTopIndex, CONSTANT_Float);
+    return itsTopIndex++;
+  }
+
+  int addConstant(double k)
+  {
+    ensure(9);
+    itsPool[itsTop++] = CONSTANT_Double;
+    long bits = Double.doubleToLongBits(k);
+    itsTop = ClassFileWriter.putInt64(bits, itsPool, itsTop);
+    int index = itsTopIndex;
+    itsTopIndex += 2;
+    itsPoolTypes.put(index, CONSTANT_Double);
+    return index;
+  }
+
+  int addConstant(String k)
+  {
+    int utf8Index = 0xFFFF & addUtf8(k);
+    int theIndex = itsStringConstHash.getInt(utf8Index, -1);
+    if (theIndex == -1) {
+      theIndex = itsTopIndex++;
+      ensure(3);
+      itsPool[itsTop++] = CONSTANT_String;
+      itsTop = ClassFileWriter.putInt16(utf8Index, itsPool, itsTop);
+      itsStringConstHash.put(utf8Index, theIndex);
+    }
+    itsPoolTypes.put(theIndex, CONSTANT_String);
+    return theIndex;
+  }
+
+  int addConstant(Object value) {
+    if (value instanceof Integer || value instanceof Byte
+        || value instanceof Short) {
+      return addConstant(((Number) value).intValue());
+    } else if (value instanceof Character) {
+      return addConstant(((Character) value).charValue());
+    } else if (value instanceof Boolean) {
+      return addConstant(((Boolean) value).booleanValue() ? 1 : 0);
+    } else if (value instanceof Float) {
+      return addConstant(((Float) value).floatValue());
+    } else if (value instanceof Long) {
+      return addConstant(((Long) value).longValue());
+    } else if (value instanceof Double) {
+      return addConstant(((Double) value).doubleValue());
+    } else if (value instanceof String) {
+      return addConstant((String) value);
+      //} else if (value instanceof ClassFileWriter.MethodType) {
+      //    return addMethodType((ClassFileWriter.MethodType) value);
+    } else if (value instanceof ClassFileWriter.MHandle) {
+      return addMethodHandle((ClassFileWriter.MHandle) value);
+    } else {
+      throw new IllegalArgumentException("value " + value);
+    }
+  }
+
+
+  boolean isUnderUtfEncodingLimit(String s)
+  {
+    int strLen = s.length();
+    if (strLen * 3 <= MAX_UTF_ENCODING_SIZE) {
+      return true;
+    } else if (strLen > MAX_UTF_ENCODING_SIZE) {
+      return false;
+    }
+    return strLen == getUtfEncodingLimit(s, 0, strLen);
+  }
+
+  /**
+   * Get maximum i such that <code>start <= i <= end</code> and
+   * <code>s.substring(start, i)</code> fits JVM UTF string encoding limit.
+   */
+  int getUtfEncodingLimit(String s, int start, int end)
+  {
+    if ((end - start) * 3 <= MAX_UTF_ENCODING_SIZE) {
+      return end;
+    }
+    int limit = MAX_UTF_ENCODING_SIZE;
+    for (int i = start; i != end; i++) {
+      int c = s.charAt(i);
+      if (0 != c && c <= 0x7F) {
+        --limit;
+      } else if (c < 0x7FF) {
+        limit -= 2;
+      } else {
+        limit -= 3;
+      }
+      if (limit < 0) {
+        return i;
+      }
+    }
+    return end;
+  }
+
+  short addUtf8(String k)
+  {
+    int theIndex = itsUtf8Hash.get(k, -1);
+    if (theIndex == -1) {
+      int strLen = k.length();
+      boolean tooBigString;
+      if (strLen > MAX_UTF_ENCODING_SIZE) {
+        tooBigString = true;
+      } else {
+        tooBigString = false;
+        // Ask for worst case scenario buffer when each char takes 3
+        // bytes
+        ensure(1 + 2 + strLen * 3);
+        int top = itsTop;
+
+        itsPool[top++] = CONSTANT_Utf8;
+        top += 2; // skip length
+
+        char[] chars = cfw.getCharBuffer(strLen);
+        k.getChars(0, strLen, chars, 0);
+
+        for (int i = 0; i != strLen; i++) {
+          int c = chars[i];
+          if (c != 0 && c <= 0x7F) {
+            itsPool[top++] = (byte)c;
+          } else if (c > 0x7FF) {
+            itsPool[top++] = (byte)(0xE0 | (c >> 12));
+            itsPool[top++] = (byte)(0x80 | ((c >> 6) & 0x3F));
+            itsPool[top++] = (byte)(0x80 | (c & 0x3F));
+          } else {
+            itsPool[top++] = (byte)(0xC0 | (c >> 6));
+            itsPool[top++] = (byte)(0x80 | (c & 0x3F));
+          }
+        }
+
+        int utfLen = top - (itsTop + 1 + 2);
+        if (utfLen > MAX_UTF_ENCODING_SIZE) {
+          tooBigString = true;
+        } else {
+          // Write back length
+          itsPool[itsTop + 1] = (byte)(utfLen >>> 8);
+          itsPool[itsTop + 2] = (byte)utfLen;
+
+          itsTop = top;
+          theIndex = itsTopIndex++;
+          itsUtf8Hash.put(k, theIndex);
+        }
+      }
+      if (tooBigString) {
+        throw new IllegalArgumentException("Too big string");
+      }
+    }
+    setConstantData(theIndex, k);
+    itsPoolTypes.put(theIndex, CONSTANT_Utf8);
+    return (short)theIndex;
+  }
+
+  private short addNameAndType(String name, String type)
+  {
+    short nameIndex = addUtf8(name);
+    short typeIndex = addUtf8(type);
+    ensure(5);
+    itsPool[itsTop++] = CONSTANT_NameAndType;
+    itsTop = ClassFileWriter.putInt16(nameIndex, itsPool, itsTop);
+    itsTop = ClassFileWriter.putInt16(typeIndex, itsPool, itsTop);
+    itsPoolTypes.put(itsTopIndex, CONSTANT_NameAndType);
+    return (short)(itsTopIndex++);
+  }
+
+  short addClass(String className)
+  {
+    int theIndex = itsClassHash.get(className, -1);
+    if (theIndex == -1) {
+      String slashed = className;
+      if (className.indexOf('.') > 0) {
+        slashed = ClassFileWriter.getSlashedForm(className);
+        theIndex = itsClassHash.get(slashed, -1);
+        if (theIndex != -1) {
+          itsClassHash.put(className, theIndex);
+        }
+      }
+      if (theIndex == -1) {
+        int utf8Index = addUtf8(slashed);
+        ensure(3);
+        itsPool[itsTop++] = CONSTANT_Class;
+        itsTop = ClassFileWriter.putInt16(utf8Index, itsPool, itsTop);
+        theIndex = itsTopIndex++;
+        itsClassHash.put(slashed, theIndex);
+        if (!className.equals(slashed)) {
+          itsClassHash.put(className, theIndex);
+        }
+      }
+    }
+    setConstantData(theIndex, className);
+    itsPoolTypes.put(theIndex, CONSTANT_Class);
+    return (short)theIndex;
+  }
+
+  short addFieldRef(String className, String fieldName, String fieldType)
+  {
+    FieldOrMethodRef ref = new FieldOrMethodRef(className, fieldName,
+        fieldType);
+
+    int theIndex = itsFieldRefHash.get(ref, -1);
+    if (theIndex == -1) {
+      short ntIndex = addNameAndType(fieldName, fieldType);
+      short classIndex = addClass(className);
+      ensure(5);
+      itsPool[itsTop++] = CONSTANT_Fieldref;
+      itsTop = ClassFileWriter.putInt16(classIndex, itsPool, itsTop);
+      itsTop = ClassFileWriter.putInt16(ntIndex, itsPool, itsTop);
+      theIndex = itsTopIndex++;
+      itsFieldRefHash.put(ref, theIndex);
+    }
+    setConstantData(theIndex, ref);
+    itsPoolTypes.put(theIndex, CONSTANT_Fieldref);
+    return (short)theIndex;
+  }
+
+  short addMethodRef(String className, String methodName,
+      String methodType)
+  {
+    FieldOrMethodRef ref = new FieldOrMethodRef(className, methodName,
+        methodType);
+
+    int theIndex = itsMethodRefHash.get(ref, -1);
+    if (theIndex == -1) {
+      short ntIndex = addNameAndType(methodName, methodType);
+      short classIndex = addClass(className);
+      ensure(5);
+      itsPool[itsTop++] = CONSTANT_Methodref;
+      itsTop = ClassFileWriter.putInt16(classIndex, itsPool, itsTop);
+      itsTop = ClassFileWriter.putInt16(ntIndex, itsPool, itsTop);
+      theIndex = itsTopIndex++;
+      itsMethodRefHash.put(ref, theIndex);
+    }
+    setConstantData(theIndex, ref);
+    itsPoolTypes.put(theIndex, CONSTANT_Methodref);
+    return (short)theIndex;
+  }
+
+  short addInterfaceMethodRef(String className,
+      String methodName, String methodType)
+  {
+    short ntIndex = addNameAndType(methodName, methodType);
+    short classIndex = addClass(className);
+    ensure(5);
+    itsPool[itsTop++] = CONSTANT_InterfaceMethodref;
+    itsTop = ClassFileWriter.putInt16(classIndex, itsPool, itsTop);
+    itsTop = ClassFileWriter.putInt16(ntIndex, itsPool, itsTop);
+    FieldOrMethodRef r = new FieldOrMethodRef(className, methodName,
+        methodType);
+    setConstantData(itsTopIndex, r);
+    itsPoolTypes.put(itsTopIndex, CONSTANT_InterfaceMethodref);
+    return (short)(itsTopIndex++);
+  }
+
+  short addInvokeDynamic(String methodName, String methodType, int bootstrapIndex)
+  {
+    ConstantEntry entry = new ConstantEntry(CONSTANT_InvokeDynamic,
+        bootstrapIndex, methodName, methodType);
+    int theIndex = itsConstantHash.get(entry, -1);
+
+    if (theIndex == -1) {
+      short nameTypeIndex = addNameAndType(methodName, methodType);
+      ensure(5);
+      itsPool[itsTop++] = CONSTANT_InvokeDynamic;
+      itsTop = ClassFileWriter.putInt16(bootstrapIndex, itsPool, itsTop);
+      itsTop = ClassFileWriter.putInt16(nameTypeIndex, itsPool, itsTop);
+      theIndex = itsTopIndex++;
+      itsConstantHash.put(entry, theIndex);
+      setConstantData(theIndex, methodType);
+      itsPoolTypes.put(theIndex, CONSTANT_InvokeDynamic);
+    }
+    return (short)(theIndex);
+  }
+
+  short addMethodHandle(ClassFileWriter.MHandle mh)
+  {
+    int theIndex = itsConstantHash.get(mh, -1);
+
+    if (theIndex == -1) {
+      short ref;
+      if (mh.tag <= ByteCode.MH_PUTSTATIC) {
+        ref = addFieldRef(mh.owner, mh.name, mh.desc);
+      } else if (mh.tag == ByteCode.MH_INVOKEINTERFACE) {
+        ref = addInterfaceMethodRef(mh.owner, mh.name, mh.desc);
+      } else {
+        ref = addMethodRef(mh.owner, mh.name, mh.desc);
+      }
+
+      ensure(4);
+      itsPool[itsTop++] = CONSTANT_MethodHandle;
+      itsPool[itsTop++] = mh.tag;
+      itsTop = ClassFileWriter.putInt16(ref, itsPool, itsTop);
+      theIndex = itsTopIndex++;
+      itsConstantHash.put(mh, theIndex);
+      itsPoolTypes.put(theIndex, CONSTANT_MethodHandle);
+    }
+    return (short)(theIndex);
+  }
+
+  Object getConstantData(int index)
+  {
+    return itsConstantData.getObject(index);
+  }
+
+  void setConstantData(int index, Object data)
+  {
+    itsConstantData.put(index, data);
+  }
+
+  byte getConstantType(int index)
+  {
+    return (byte) itsPoolTypes.getInt(index, 0);
+  }
+
+  private void ensure(int howMuch)
+  {
+    if (itsTop + howMuch > itsPool.length) {
+      int newCapacity = itsPool.length * 2;
+      if (itsTop + howMuch > newCapacity) {
+        newCapacity = itsTop + howMuch;
+      }
+      byte[] tmp = new byte[newCapacity];
+      System.arraycopy(itsPool, 0, tmp, 0, itsTop);
+      itsPool = tmp;
+    }
+  }
+
+  private ClassFileWriter cfw;
+
+  private static final int MAX_UTF_ENCODING_SIZE = 65535;
+
+  private UintMap itsStringConstHash = new UintMap();
+  private ObjToIntMap itsUtf8Hash = new ObjToIntMap();
+  private ObjToIntMap itsFieldRefHash = new ObjToIntMap();
+  private ObjToIntMap itsMethodRefHash = new ObjToIntMap();
+  private ObjToIntMap itsClassHash = new ObjToIntMap();
+  private ObjToIntMap itsConstantHash = new ObjToIntMap();
+
+  private int itsTop;
+  private int itsTopIndex;
+  private UintMap itsConstantData = new UintMap();
+  private UintMap itsPoolTypes = new UintMap();
+  private byte itsPool[];
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/classfile/ExceptionTableEntry.java rhino-1.7.14/src/org/mozilla/classfile/ExceptionTableEntry.java
--- rhino-1.7.7.2/src/org/mozilla/classfile/ExceptionTableEntry.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/classfile/ExceptionTableEntry.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,24 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.classfile;
+
+final class ExceptionTableEntry
+{
+  ExceptionTableEntry(int startLabel, int endLabel,
+      int handlerLabel, short catchType)
+  {
+    itsStartLabel = startLabel;
+    itsEndLabel = endLabel;
+    itsHandlerLabel = handlerLabel;
+    itsCatchType = catchType;
+  }
+
+  int itsStartLabel;
+  int itsEndLabel;
+  int itsHandlerLabel;
+  short itsCatchType;
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/classfile/FieldOrMethodRef.java rhino-1.7.14/src/org/mozilla/classfile/FieldOrMethodRef.java
--- rhino-1.7.7.2/src/org/mozilla/classfile/FieldOrMethodRef.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/classfile/FieldOrMethodRef.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,59 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.classfile;
+
+final class FieldOrMethodRef
+{
+  FieldOrMethodRef(String className, String name, String type)
+  {
+    this.className = className;
+    this.name = name;
+    this.type = type;
+  }
+
+  public String getClassName()
+  {
+    return className;
+  }
+
+  public String getName()
+  {
+    return name;
+  }
+
+  public String getType()
+  {
+    return type;
+  }
+
+  @Override
+  public boolean equals(Object obj)
+  {
+    if (!(obj instanceof FieldOrMethodRef)) { return false; }
+    FieldOrMethodRef x = (FieldOrMethodRef)obj;
+    return className.equals(x.className)
+        && name.equals(x.name)
+        && type.equals(x.type);
+  }
+
+  @Override
+  public int hashCode()
+  {
+    if (hashCode == -1) {
+      int h1 = className.hashCode();
+      int h2 = name.hashCode();
+      int h3 = type.hashCode();
+      hashCode = h1 ^ h2 ^ h3;
+    }
+    return hashCode;
+  }
+
+  private String className;
+  private String name;
+  private String type;
+  private int hashCode = -1;
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/classfile/SuperBlock.java rhino-1.7.14/src/org/mozilla/classfile/SuperBlock.java
--- rhino-1.7.7.2/src/org/mozilla/classfile/SuperBlock.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/classfile/SuperBlock.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,163 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.classfile;
+
+/**
+ * A super block is defined as a contiguous chunk of code with a single entry
+ * point and multiple exit points (therefore ending in an unconditional jump
+ * or the end of the method). This is used to emulate OpenJDK's compiler, which
+ * outputs stack map frames at the start of every super block except the method
+ * start.
+ */
+final class SuperBlock {
+  SuperBlock(int index, int start, int end, int[] initialLocals) {
+    this.index = index;
+    this.start = start;
+    this.end = end;
+    locals = new int[initialLocals.length];
+    System.arraycopy(initialLocals, 0, locals, 0, initialLocals.length);
+    stack = new int[0];
+    isInitialized = false;
+    isInQueue = false;
+  }
+
+  int getIndex() {
+    return index;
+  }
+
+  int[] getLocals() {
+    int[] copy = new int[locals.length];
+    System.arraycopy(locals, 0, copy, 0, locals.length);
+    return copy;
+  }
+
+  /**
+   * Get a copy of the super block's locals without any trailing TOP types.
+   *
+   * This is useful for actual writing stack maps; during the computation of
+   * stack map types, all local arrays have the same size; the max locals for
+   * the method. In addition, DOUBLE and LONG types have trailing TOP types
+   * because they occupy two words. For writing purposes, these are not
+   * useful.
+   */
+  int[] getTrimmedLocals() {
+    int last = locals.length - 1;
+    // Exclude all of the trailing TOPs not bound to a DOUBLE/LONG
+    while (last >= 0 && locals[last] == TypeInfo.TOP &&
+        !TypeInfo.isTwoWords(locals[last - 1])) {
+      last--;
+    }
+    last++;
+    // Exclude trailing TOPs following a DOUBLE/LONG
+    int size = last;
+    for (int i = 0; i < last; i++) {
+      if (TypeInfo.isTwoWords(locals[i])) {
+        size--;
+      }
+    }
+    int[] copy = new int[size];
+    for (int i = 0, j = 0; i < size; i++, j++) {
+      copy[i] = locals[j];
+      if (TypeInfo.isTwoWords(locals[j])) {
+        j++;
+      }
+    }
+    return copy;
+  }
+
+  int[] getStack() {
+    int[] copy = new int[stack.length];
+    System.arraycopy(stack, 0, copy, 0, stack.length);
+    return copy;
+  }
+
+  boolean merge(int[] locals, int localsTop, int[] stack, int stackTop,
+      ConstantPool pool) {
+    if (!isInitialized) {
+      System.arraycopy(locals, 0, this.locals, 0, localsTop);
+      this.stack = new int[stackTop];
+      System.arraycopy(stack, 0, this.stack, 0, stackTop);
+      isInitialized = true;
+      return true;
+    } else if (this.locals.length == localsTop &&
+        this.stack.length == stackTop) {
+      boolean localsChanged = mergeState(this.locals, locals, localsTop,
+          pool);
+      boolean stackChanged = mergeState(this.stack, stack, stackTop,
+          pool);
+      return localsChanged || stackChanged;
+    } else {
+      if (ClassFileWriter.StackMapTable.DEBUGSTACKMAP) {
+        System.out.println("bad merge");
+        System.out.println("current type state:");
+        TypeInfo.print(this.locals, this.stack, pool);
+        System.out.println("incoming type state:");
+        TypeInfo.print(locals, localsTop, stack, stackTop, pool);
+      }
+      throw new IllegalArgumentException("bad merge attempt");
+    }
+  }
+
+  /**
+   * Merge an operand stack or local variable array with incoming state.
+   *
+   * They are treated the same way; by this point, it should already be
+   * ensured that the array sizes are the same, which is the only additional
+   * constraint that is imposed on merging operand stacks (the local variable
+   * array is always the same size).
+   */
+  private static boolean mergeState(int[] current, int[] incoming, int size,
+      ConstantPool pool) {
+    boolean changed = false;
+    for (int i = 0; i < size; i++) {
+      int currentType = current[i];
+
+      current[i] = TypeInfo.merge(current[i], incoming[i], pool);
+      if (currentType != current[i]) {
+        changed = true;
+      }
+    }
+    return changed;
+  }
+
+  int getStart() {
+    return start;
+  }
+
+  int getEnd() {
+    return end;
+  }
+
+  @Override
+  public String toString() {
+    return "sb " + index;
+  }
+
+  boolean isInitialized() {
+    return isInitialized;
+  }
+
+  void setInitialized(boolean b) {
+    isInitialized = b;
+  }
+
+  boolean isInQueue() {
+    return isInQueue;
+  }
+
+  void setInQueue(boolean b) {
+    isInQueue = b;
+  }
+
+  private int index;
+  private int start;
+  private int end;
+  private int[] locals;
+  private int[] stack;
+  private boolean isInitialized;
+  private boolean isInQueue;
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/classfile/TypeInfo.java rhino-1.7.14/src/org/mozilla/classfile/TypeInfo.java
--- rhino-1.7.7.2/src/org/mozilla/classfile/TypeInfo.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/classfile/TypeInfo.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,235 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.classfile;
+
+/**
+ * Helper class for internal representations of type information. In most cases, type information
+ * can be represented by a constant, but in some cases, a payload is included. Despite the payload
+ * coming after the type tag in the output, we store it in bits 8-23 for uniformity; the tag is
+ * always in bits 0-7.
+ */
+final class TypeInfo {
+    private TypeInfo() {}
+
+    static final int TOP = 0;
+    static final int INTEGER = 1;
+    static final int FLOAT = 2;
+    static final int DOUBLE = 3;
+    static final int LONG = 4;
+    static final int NULL = 5;
+    static final int UNINITIALIZED_THIS = 6;
+    static final int OBJECT_TAG = 7;
+    static final int UNINITIALIZED_VAR_TAG = 8;
+
+    static final int OBJECT(int constantPoolIndex) {
+        return ((constantPoolIndex & 0xFFFF) << 8) | OBJECT_TAG;
+    }
+
+    static final int OBJECT(String type, ConstantPool pool) {
+        return OBJECT(pool.addClass(type));
+    }
+
+    static final int UNINITIALIZED_VARIABLE(int bytecodeOffset) {
+        return ((bytecodeOffset & 0xFFFF) << 8) | UNINITIALIZED_VAR_TAG;
+    }
+
+    static final int getTag(int typeInfo) {
+        return typeInfo & 0xFF;
+    }
+
+    static final int getPayload(int typeInfo) {
+        return typeInfo >>> 8;
+    }
+
+    /**
+     * Treat the result of getPayload as a constant pool index and fetch the corresponding String
+     * mapped to it.
+     *
+     * <p>Only works on OBJECT types.
+     */
+    static final String getPayloadAsType(int typeInfo, ConstantPool pool) {
+        if (getTag(typeInfo) == OBJECT_TAG) {
+            return (String) pool.getConstantData(getPayload(typeInfo));
+        }
+        throw new IllegalArgumentException("expecting object type");
+    }
+
+    /** Create type information from an internal type. */
+    static final int fromType(String type, ConstantPool pool) {
+        if (type.length() == 1) {
+            switch (type.charAt(0)) {
+                case 'B': // sbyte
+                case 'C': // unicode char
+                case 'S': // short
+                case 'Z': // boolean
+                case 'I': // all of the above are verified as integers
+                    return INTEGER;
+                case 'D':
+                    return DOUBLE;
+                case 'F':
+                    return FLOAT;
+                case 'J':
+                    return LONG;
+                default:
+                    throw new IllegalArgumentException("bad type");
+            }
+        }
+        return TypeInfo.OBJECT(type, pool);
+    }
+
+    static boolean isTwoWords(int type) {
+        return type == DOUBLE || type == LONG;
+    }
+
+    /**
+     * Merge two verification types.
+     *
+     * <p>In most cases, the verification types must be the same. For example, INTEGER and DOUBLE
+     * cannot be merged and an exception will be thrown. The basic rules are:
+     *
+     * <p>- If the types are equal, simply return one. - If either type is TOP, return TOP. - If
+     * either type is NULL, return the other type. - If both types are objects, find the lowest
+     * common ancestor in the class hierarchy.
+     *
+     * <p>This method uses reflection to traverse the class hierarchy. Therefore, it is assumed that
+     * the current class being generated is never the target of a full object-object merge, which
+     * would need to load the current class reflectively.
+     */
+    static int merge(int current, int incoming, ConstantPool pool) {
+        if (current == incoming) {
+            return current;
+        }
+        int currentTag = getTag(current);
+        int incomingTag = getTag(incoming);
+        boolean currentIsObject = currentTag == TypeInfo.OBJECT_TAG;
+        boolean incomingIsObject = incomingTag == TypeInfo.OBJECT_TAG;
+
+        if (currentIsObject && incoming == NULL) {
+            return current;
+        } else if (currentTag == TypeInfo.TOP || incomingTag == TypeInfo.TOP) {
+            return TypeInfo.TOP;
+        } else if (current == NULL && incomingIsObject) {
+            return incoming;
+        } else if (currentIsObject && incomingIsObject) {
+            String currentName = getPayloadAsType(current, pool);
+            String incomingName = getPayloadAsType(incoming, pool);
+            // The class file always has the class and super names in the same
+            // spot. The constant order is: class_data, class_name, super_data,
+            // super_name.
+            String currentlyGeneratedName = (String) pool.getConstantData(2);
+            String currentlyGeneratedSuperName = (String) pool.getConstantData(4);
+
+            // If any of the merged types are the class that's currently being
+            // generated, automatically start at the super class instead. At
+            // this point, we already know the classes are different, so we
+            // don't need to handle that case.
+            if (currentName.equals(currentlyGeneratedName)) {
+                currentName = currentlyGeneratedSuperName;
+            }
+            if (incomingName.equals(currentlyGeneratedName)) {
+                incomingName = currentlyGeneratedSuperName;
+            }
+
+            Class<?> currentClass = getClassFromInternalName(currentName);
+            Class<?> incomingClass = getClassFromInternalName(incomingName);
+
+            if (currentClass.isAssignableFrom(incomingClass)) {
+                return current;
+            } else if (incomingClass.isAssignableFrom(currentClass)) {
+                return incoming;
+            } else if (incomingClass.isInterface() || currentClass.isInterface()) {
+                // For verification purposes, Sun specifies that interfaces are
+                // subtypes of Object. Therefore, we know that the merge result
+                // involving interfaces where one is not assignable to the
+                // other results in Object.
+                return OBJECT("java/lang/Object", pool);
+            } else {
+                Class<?> commonClass = incomingClass.getSuperclass();
+                while (commonClass != null) {
+                    if (commonClass.isAssignableFrom(currentClass)) {
+                        String name = commonClass.getName();
+                        name = ClassFileWriter.getSlashedForm(name);
+                        return OBJECT(name, pool);
+                    }
+                    commonClass = commonClass.getSuperclass();
+                }
+            }
+        }
+        throw new IllegalArgumentException(
+                "bad merge attempt between "
+                        + toString(current, pool)
+                        + " and "
+                        + toString(incoming, pool));
+    }
+
+    static String toString(int type, ConstantPool pool) {
+        int tag = getTag(type);
+        switch (tag) {
+            case TypeInfo.TOP:
+                return "top";
+            case TypeInfo.INTEGER:
+                return "int";
+            case TypeInfo.FLOAT:
+                return "float";
+            case TypeInfo.DOUBLE:
+                return "double";
+            case TypeInfo.LONG:
+                return "long";
+            case TypeInfo.NULL:
+                return "null";
+            case TypeInfo.UNINITIALIZED_THIS:
+                return "uninitialized_this";
+            default:
+                if (tag == TypeInfo.OBJECT_TAG) {
+                    return getPayloadAsType(type, pool);
+                } else if (tag == TypeInfo.UNINITIALIZED_VAR_TAG) {
+                    return "uninitialized";
+                } else {
+                    throw new IllegalArgumentException("bad type");
+                }
+        }
+    }
+
+    /**
+     * Take an internal name and return a java.lang.Class instance that represents it.
+     *
+     * <p>For example, given "java/lang/Object", returns the equivalent of
+     * Class.forName("java.lang.Object"), but also handles exceptions.
+     */
+    private static Class<?> getClassFromInternalName(String internalName) {
+        try {
+            return Class.forName(internalName.replace('/', '.'));
+        } catch (ClassNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static String toString(int[] types, int typesTop, ConstantPool pool) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("[");
+        for (int i = 0; i < typesTop; i++) {
+            if (i > 0) {
+                sb.append(", ");
+            }
+            sb.append(toString(types[i], pool));
+        }
+        sb.append("]");
+        return sb.toString();
+    }
+
+    static void print(int[] locals, int[] stack, ConstantPool pool) {
+        print(locals, locals.length, stack, stack.length, pool);
+    }
+
+    static void print(int[] locals, int localsTop, int[] stack, int stackTop, ConstantPool pool) {
+        System.out.print("locals: ");
+        System.out.println(toString(locals, localsTop, pool));
+        System.out.print("stack: ");
+        System.out.println(toString(stack, stackTop, pool));
+        System.out.println();
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java rhino-1.7.14/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,172 @@
+package org.mozilla.javascript;
+
+/**
+ * Abstract Object Operations as defined by EcmaScript
+ *
+ * @see <a href="https://262.ecma-international.org/11.0/#sec-operations-on-objects">Abstract
+ *     Operations - Operations on Objects</a>
+ *     <p>Notes
+ *     <ul>
+ *       <li>all methods are to deviate from the method signature defined in the EcmaScript
+ *           specification, by taking an additional 1st parameter of type Context: (downstream)
+ *           methods may need the Context object to read flags and we want to avoid having to look
+ *           up the current context (for performance reasons)
+ *       <li>all methods that implement an Abstract Operation as defined by EcmaScript are to be
+ *           package-scopes methods, to prevent them from being used directly by 3rd party code,
+ *           which would hamper evolving them over time to adept to newer EcmaScript specifications
+ *       <li>a link to the method specification of the specific (EcmaScript) version implemented
+ *           will be put in the JavaDoc of each method that implements an Abstract Operations
+ *     </ul>
+ */
+class AbstractEcmaObjectOperations {
+    enum INTEGRITY_LEVEL {
+        FROZEN,
+        SEALED
+    }
+
+    /**
+     * Implementation of Abstract Object operation testIntegrityLevel as defined by EcmaScript
+     *
+     * @param cx
+     * @param o
+     * @param level
+     * @return boolean
+     * @see <a
+     *     href="https://262.ecma-international.org/11.0/#sec-testintegritylevel">TestIntegrityLevel</a>
+     */
+    static boolean testIntegrityLevel(Context cx, Object o, INTEGRITY_LEVEL level) {
+        ScriptableObject obj = ScriptableObject.ensureScriptableObject(o);
+
+        if (obj.isExtensible()) return false;
+
+        for (Object name : obj.getIds(true, true)) {
+            ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, name);
+            if (Boolean.TRUE.equals(desc.get("configurable"))) return false;
+
+            if (level == INTEGRITY_LEVEL.FROZEN
+                    && desc.isDataDescriptor(desc)
+                    && Boolean.TRUE.equals(desc.get("writable"))) return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Implementation of Abstract Object operation setIntegrityLevel as defined by EcmaScript
+     *
+     * @param cx
+     * @param o
+     * @param level
+     * @return boolean
+     * @see <a
+     *     href="https://262.ecma-international.org/11.0/#sec-setintegritylevel">SetIntegrityLevel</a>
+     */
+    static boolean setIntegrityLevel(Context cx, Object o, INTEGRITY_LEVEL level) {
+        /*
+           1. Assert: Type(O) is Object.
+           2. Assert: level is either sealed or frozen.
+           3. Let status be ? O.[[PreventExtensions]]().
+           4. If status is false, return false.
+           5. Let keys be ? O.[[OwnPropertyKeys]]().
+           6. If level is sealed, then
+               a. For each element k of keys, do
+                   i. Perform ? DefinePropertyOrThrow(O, k, PropertyDescriptor { [[Configurable]]: false }).
+           7. Else,
+               a. Assert: level is frozen.
+               b. For each element k of keys, do
+                   i. Let currentDesc be ? O.[[GetOwnProperty]](k).
+                   ii. If currentDesc is not undefined, then
+                       1. If IsAccessorDescriptor(currentDesc) is true, then
+                           a. Let desc be the PropertyDescriptor { [[Configurable]]: false }.
+                       2. Else,
+                           a. Let desc be the PropertyDescriptor { [[Configurable]]: false, [[Writable]]: false }.
+                       3. Perform ? DefinePropertyOrThrow(O, k, desc).
+           8. Return true.
+
+           NOTES
+           - While steps 6.a.i and 7.b.ii.3 call for the Abstract DefinePropertyOrThrow operation,
+             the conditions under which a throw would occur aren't applicable when freezing or sealing an object,
+             see https://262.ecma-international.org/11.0/#sec-validateandapplypropertydescriptor:
+             1. n/a
+             2. current cannot be undefined, because the logic operates only on existing properties
+             3. n/a
+             4. this code doesn't ever set configurable to true or modifies the enumerable property
+             5. n/a
+             6. as current and desc start out the same and the writable property is set only after checking if isDataDescriptor == true, this condition cannot occur
+             7. both conditions under which false would be returned cannot occur here
+             8. both conditions under which false would be returned cannot occur here
+        */
+        ScriptableObject obj = ScriptableObject.ensureScriptableObject(o);
+
+        // TODO check .preventExtensions() return value once implemented and act accordingly to spec
+        obj.preventExtensions();
+
+        for (Object key : obj.getIds(true, true)) {
+            ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, key);
+
+            if (level == INTEGRITY_LEVEL.SEALED) {
+                if (Boolean.TRUE.equals(desc.get("configurable"))) {
+                    desc.put("configurable", desc, false);
+
+                    obj.defineOwnProperty(cx, key, desc, false);
+                }
+            } else {
+                if (obj.isDataDescriptor(desc) && Boolean.TRUE.equals(desc.get("writable"))) {
+                    desc.put("writable", desc, false);
+                }
+                if (Boolean.TRUE.equals(desc.get("configurable"))) {
+                    desc.put("configurable", desc, false);
+                }
+                obj.defineOwnProperty(cx, key, desc, false);
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Implement the ECMAScript abstract operation "SpeciesConstructor" defined in section 7.2.33 of
+     * ECMA262.
+     *
+     * @param cx context
+     * @param s the object for which we will find the "species constructor" as per the spec
+     * @param defaultConstructor as per the spec, the value that will be returned if there is no
+     *     constructor on "s" or if the "species" symbol is not set.
+     * @see <a href="https://tc39.es/ecma262/#sec-speciesconstructor"></a>
+     */
+    public static Constructable speciesConstructor(
+            Context cx, Scriptable s, Constructable defaultConstructor) {
+        /*
+        The abstract operation SpeciesConstructor takes arguments O (an Object) and
+        defaultConstructor (a constructor). It is used to retrieve the constructor that should
+        be used to create new objects that are derived from O. defaultConstructor is the
+        constructor to use if a constructor @@species property cannot be found starting from O.
+        It performs the following steps when called:
+
+        1. Assert: Type(O) is Object.
+        2. Let C be ? Get(O, "constructor").
+        3. If C is undefined, return defaultConstructor.
+        4. If Type(C) is not Object, throw a TypeError exception.
+        5. Let S be ? Get(C, @@species).
+        6. If S is either undefined or null, return defaultConstructor.
+        7. If IsConstructor(S) is true, return S.
+        8. Throw a TypeError exception.
+         */
+        Object constructor = ScriptableObject.getProperty(s, "constructor");
+        if (constructor == Scriptable.NOT_FOUND || Undefined.isUndefined(constructor)) {
+            return defaultConstructor;
+        }
+        if (!ScriptRuntime.isObject(constructor)) {
+            throw ScriptRuntime.typeErrorById(
+                    "msg.arg.not.object", ScriptRuntime.typeof(constructor));
+        }
+        Object species = ScriptableObject.getProperty((Scriptable) constructor, SymbolKey.SPECIES);
+        if (species == Scriptable.NOT_FOUND || species == null || Undefined.isUndefined(species)) {
+            return defaultConstructor;
+        }
+        if (!(species instanceof Constructable)) {
+            throw ScriptRuntime.typeErrorById("msg.not.ctor", ScriptRuntime.typeof(species));
+        }
+        return (Constructable) species;
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/AccessorSlot.java rhino-1.7.14/src/org/mozilla/javascript/AccessorSlot.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/AccessorSlot.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/AccessorSlot.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,202 @@
+package org.mozilla.javascript;
+
+/**
+ * This is a specialization of Slot to store various types of values that are retrieved dynamically
+ * using Java and JavaScript functions. Unlike LambdaSlot, the fact that these values are accessed
+ * and mutated by functions is visible via the slot's property descriptor.
+ */
+public class AccessorSlot extends Slot {
+    private static final long serialVersionUID = 1677840254177335827L;
+
+    AccessorSlot(Slot oldSlot) {
+        super(oldSlot);
+    }
+
+    // The Getter and Setter may each be of a different type (JavaScript function, Java
+    // function, or neither). So, use an abstraction to distinguish them.
+    transient Getter getter;
+    transient Setter setter;
+
+    @Override
+    boolean isValueSlot() {
+        return false;
+    }
+
+    @Override
+    boolean isSetterSlot() {
+        return true;
+    }
+
+    @Override
+    ScriptableObject getPropertyDescriptor(Context cx, Scriptable scope) {
+        // It sounds logical that this would be the same as the logic for a normal Slot,
+        // but the spec is super pedantic about things like the order of properties here,
+        // so we need special support here.
+        ScriptableObject desc = (ScriptableObject) cx.newObject(scope);
+        desc.setCommonDescriptorProperties(getAttributes(), getter == null && setter == null);
+        String fName = name == null ? "f" : name.toString();
+        if (getter != null) {
+            Function f = getter.asGetterFunction(fName, scope);
+            desc.defineProperty("get", f == null ? Undefined.instance : f, ScriptableObject.EMPTY);
+        }
+        if (setter != null) {
+            Function f = setter.asSetterFunction(fName, scope);
+            desc.defineProperty("set", f == null ? Undefined.instance : f, ScriptableObject.EMPTY);
+        }
+        return desc;
+    }
+
+    @Override
+    public boolean setValue(Object value, Scriptable owner, Scriptable start) {
+        if (setter == null) {
+            if (getter != null) {
+                throwNoSetterException(start, value);
+                return true;
+            }
+        } else {
+            return setter.setValue(value, owner, start);
+        }
+        return super.setValue(value, owner, start);
+    }
+
+    @Override
+    public Object getValue(Scriptable start) {
+        if (getter != null) {
+            return getter.getValue(start);
+        }
+        return super.getValue(start);
+    }
+
+    @Override
+    Function getSetterFunction(String name, Scriptable scope) {
+        if (setter == null) {
+            return null;
+        }
+        return setter.asSetterFunction(name, scope);
+    }
+
+    @Override
+    Function getGetterFunction(String name, Scriptable scope) {
+        if (getter == null) {
+            return null;
+        }
+        return getter.asGetterFunction(name, scope);
+    }
+
+    interface Getter {
+        Object getValue(Scriptable start);
+
+        Function asGetterFunction(final String name, final Scriptable scope);
+    }
+
+    /** This is a Getter that delegates to a Java function via a MemberBox. */
+    static final class MemberBoxGetter implements Getter {
+        final MemberBox member;
+
+        MemberBoxGetter(MemberBox member) {
+            this.member = member;
+        }
+
+        @Override
+        public Object getValue(Scriptable start) {
+            if (member.delegateTo == null) {
+                return member.invoke(start, ScriptRuntime.emptyArgs);
+            }
+            return member.invoke(member.delegateTo, new Object[] {start});
+        }
+
+        @Override
+        public Function asGetterFunction(String name, Scriptable scope) {
+            return member.asGetterFunction(name, scope);
+        }
+    }
+
+    /** This is a getter that delegates to a JavaScript function. */
+    static final class FunctionGetter implements Getter {
+        // The value of the function might actually be Undefined, so we need an Object here.
+        final Object target;
+
+        FunctionGetter(Object target) {
+            this.target = target;
+        }
+
+        @Override
+        public Object getValue(Scriptable start) {
+            if (target instanceof Function) {
+                Function t = (Function) target;
+                Context cx = Context.getContext();
+                return t.call(cx, t.getParentScope(), start, ScriptRuntime.emptyArgs);
+            }
+            return Undefined.instance;
+        }
+
+        @Override
+        public Function asGetterFunction(String name, Scriptable scope) {
+            return target instanceof Function ? (Function) target : null;
+        }
+    }
+
+    interface Setter {
+        boolean setValue(Object value, Scriptable owner, Scriptable start);
+
+        Function asSetterFunction(final String name, final Scriptable scope);
+    }
+
+    /** Invoke the setter on this slot via reflection using MemberBox. */
+    static final class MemberBoxSetter implements Setter {
+        final MemberBox member;
+
+        MemberBoxSetter(MemberBox member) {
+            this.member = member;
+        }
+
+        @Override
+        public boolean setValue(Object value, Scriptable owner, Scriptable start) {
+            Context cx = Context.getContext();
+            Class<?>[] pTypes = member.argTypes;
+            // XXX: cache tag since it is already calculated in
+            // defineProperty ?
+            Class<?> valueType = pTypes[pTypes.length - 1];
+            int tag = FunctionObject.getTypeTag(valueType);
+            Object actualArg = FunctionObject.convertArg(cx, start, value, tag);
+
+            if (member.delegateTo == null) {
+                member.invoke(start, new Object[] {actualArg});
+            } else {
+                member.invoke(member.delegateTo, new Object[] {start, actualArg});
+            }
+            return true;
+        }
+
+        @Override
+        public Function asSetterFunction(String name, Scriptable scope) {
+            return member.asSetterFunction(name, scope);
+        }
+    }
+
+    /**
+     * Invoke the setter as a JavaScript function, taking care that it might actually be Undefined.
+     */
+    static final class FunctionSetter implements Setter {
+        final Object target;
+
+        FunctionSetter(Object target) {
+            this.target = target;
+        }
+
+        @Override
+        public boolean setValue(Object value, Scriptable owner, Scriptable start) {
+            if (target instanceof Function) {
+                Function t = (Function) target;
+                Context cx = Context.getContext();
+                t.call(cx, t.getParentScope(), start, new Object[] {value});
+            }
+            return true;
+        }
+
+        @Override
+        public Function asSetterFunction(String name, Scriptable scope) {
+            return target instanceof Function ? (Function) target : null;
+        }
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/annotations/JSConstructor.java rhino-1.7.14/src/org/mozilla/javascript/annotations/JSConstructor.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/annotations/JSConstructor.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/annotations/JSConstructor.java	2022-01-06 22:57:21.000000000 +0100
@@ -4,7 +4,11 @@
 
 package org.mozilla.javascript.annotations;
 
-import java.lang.annotation.*;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
 /**
  * An annotation that marks a Java method as JavaScript constructor. This can
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/annotations/JSFunction.java rhino-1.7.14/src/org/mozilla/javascript/annotations/JSFunction.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/annotations/JSFunction.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/annotations/JSFunction.java	2022-01-06 22:57:21.000000000 +0100
@@ -4,7 +4,11 @@
 
 package org.mozilla.javascript.annotations;
 
-import java.lang.annotation.*;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
 /**
  * An annotation that marks a Java method as JavaScript function. This can
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/annotations/JSGetter.java rhino-1.7.14/src/org/mozilla/javascript/annotations/JSGetter.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/annotations/JSGetter.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/annotations/JSGetter.java	2022-01-06 22:57:21.000000000 +0100
@@ -4,7 +4,11 @@
 
 package org.mozilla.javascript.annotations;
 
-import java.lang.annotation.*;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
 /**
  * An annotation that marks a Java method as JavaScript getter. This can
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/annotations/JSSetter.java rhino-1.7.14/src/org/mozilla/javascript/annotations/JSSetter.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/annotations/JSSetter.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/annotations/JSSetter.java	2022-01-06 22:57:21.000000000 +0100
@@ -4,7 +4,11 @@
 
 package org.mozilla.javascript.annotations;
 
-import java.lang.annotation.*;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
 /**
  * An annotation that marks a Java method as JavaScript setter. This can
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/annotations/JSStaticFunction.java rhino-1.7.14/src/org/mozilla/javascript/annotations/JSStaticFunction.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/annotations/JSStaticFunction.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/annotations/JSStaticFunction.java	2022-01-06 22:57:21.000000000 +0100
@@ -4,7 +4,11 @@
 
 package org.mozilla.javascript.annotations;
 
-import java.lang.annotation.*;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
 /**
  * An annotation that marks a Java method as JavaScript static function. This can
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/Arguments.java rhino-1.7.14/src/org/mozilla/javascript/Arguments.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/Arguments.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/Arguments.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,22 +6,22 @@
 
 package org.mozilla.javascript;
 
+import org.mozilla.javascript.NativeArrayIterator.ARRAY_ITERATOR_TYPE;
+
 /**
  * This class implements the "arguments" object.
  *
- * See ECMA 10.1.8
+ * <p>See ECMA 10.1.8
  *
  * @see org.mozilla.javascript.NativeCall
  * @author Norris Boyd
  */
-final class Arguments extends IdScriptableObject
-{
-    static final long serialVersionUID = 4275508002492040609L;
+final class Arguments extends IdScriptableObject {
+    private static final long serialVersionUID = 4275508002492040609L;
 
     private static final String FTAG = "Arguments";
 
-    public Arguments(NativeCall activation)
-    {
+    public Arguments(NativeCall activation) {
         this.activation = activation;
 
         Scriptable parent = activation.getParentScope();
@@ -35,9 +35,7 @@
         calleeObj = f;
 
         int version = f.getLanguageVersion();
-        if (version <= Context.VERSION_1_3
-            && version != Context.VERSION_DEFAULT)
-        {
+        if (version <= Context.VERSION_1_3 && version != Context.VERSION_DEFAULT) {
             callerObj = null;
         } else {
             callerObj = NOT_FOUND;
@@ -47,14 +45,13 @@
     }
 
     @Override
-    public String getClassName()
-    {
+    public String getClassName() {
         return FTAG;
     }
 
     private Object arg(int index) {
-      if (index < 0 || args.length <= index) return NOT_FOUND;
-      return args[index];
+        if (index < 0 || args.length <= index) return NOT_FOUND;
+        return args[index];
     }
 
     // the following helper methods assume that 0 < index < args.length
@@ -70,56 +67,51 @@
     }
 
     private void replaceArg(int index, Object value) {
-      if (sharedWithActivation(index)) {
-        putIntoActivation(index, value);
-      }
-      synchronized (this) {
-        if (args == activation.originalArgs) {
-          args = args.clone();
+        if (sharedWithActivation(index)) {
+            putIntoActivation(index, value);
+        }
+        synchronized (this) {
+            if (args == activation.originalArgs) {
+                args = args.clone();
+            }
+            args[index] = value;
         }
-        args[index] = value;
-      }
     }
 
     private void removeArg(int index) {
-      synchronized (this) {
-        if (args[index] != NOT_FOUND) {
-          if (args == activation.originalArgs) {
-            args = args.clone();
-          }
-          args[index] = NOT_FOUND;
+        synchronized (this) {
+            if (args[index] != NOT_FOUND) {
+                if (args == activation.originalArgs) {
+                    args = args.clone();
+                }
+                args[index] = NOT_FOUND;
+            }
         }
-      }
     }
 
     // end helpers
 
     @Override
-    public boolean has(int index, Scriptable start)
-    {
+    public boolean has(int index, Scriptable start) {
         if (arg(index) != NOT_FOUND) {
-          return true;
+            return true;
         }
         return super.has(index, start);
     }
 
     @Override
-    public Object get(int index, Scriptable start)
-    {
-      final Object value = arg(index);
-      if (value == NOT_FOUND) {
-        return super.get(index, start);
-      } else {
+    public Object get(int index, Scriptable start) {
+        final Object value = arg(index);
+        if (value == NOT_FOUND) {
+            return super.get(index, start);
+        }
         if (sharedWithActivation(index)) {
-          return getFromActivation(index);
-        } else {
-          return value;
+            return getFromActivation(index);
         }
-      }
+        return value;
     }
 
-    private boolean sharedWithActivation(int index)
-    {
+    private boolean sharedWithActivation(int index) {
         Context cx = Context.getContext();
         if (cx.isStrictMode()) {
             return false;
@@ -143,62 +135,54 @@
     }
 
     @Override
-    public void put(int index, Scriptable start, Object value)
-    {
+    public void put(int index, Scriptable start, Object value) {
         if (arg(index) == NOT_FOUND) {
-          super.put(index, start, value);
+            super.put(index, start, value);
         } else {
-          replaceArg(index, value);
+            replaceArg(index, value);
         }
     }
 
     @Override
-    public void put(String name, Scriptable start, Object value)
-    {
+    public void put(String name, Scriptable start, Object value) {
         super.put(name, start, value);
     }
 
     @Override
-    public void delete(int index)
-    {
+    public void delete(int index) {
         if (0 <= index && index < args.length) {
-          removeArg(index);
+            removeArg(index);
         }
         super.delete(index);
     }
 
-// #string_id_map#
-
-    private static final int
-        Id_callee           = 1,
-        Id_length           = 2,
-        Id_caller           = 3,
-
-        MAX_INSTANCE_ID     = Id_caller;
+    private static final int Id_callee = 1,
+            Id_length = 2,
+            Id_caller = 3,
+            MAX_INSTANCE_ID = Id_caller;
 
     @Override
-    protected int getMaxInstanceId()
-    {
+    protected int getMaxInstanceId() {
         return MAX_INSTANCE_ID;
     }
 
     @Override
-    protected int findInstanceIdInfo(String s)
-    {
+    protected int findInstanceIdInfo(String s) {
         int id;
-// #generated# Last update: 2010-01-06 05:48:21 ARST
-        L0: { id = 0; String X = null; int c;
-            int s_length = s.length();
-            if (s_length==6) {
-                c=s.charAt(5);
-                if (c=='e') { X="callee";id=Id_callee; }
-                else if (c=='h') { X="length";id=Id_length; }
-                else if (c=='r') { X="caller";id=Id_caller; }
-            }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-            break L0;
+        switch (s) {
+            case "callee":
+                id = Id_callee;
+                break;
+            case "length":
+                id = Id_length;
+                break;
+            case "caller":
+                id = Id_caller;
+                break;
+            default:
+                id = 0;
+                break;
         }
-// #/generated#
         Context cx = Context.getContext();
         if (cx.isStrictMode()) {
             if (id == Id_callee || id == Id_caller) {
@@ -206,65 +190,71 @@
             }
         }
 
-
         if (id == 0) return super.findInstanceIdInfo(s);
 
         int attr;
         switch (id) {
-          case Id_callee:
-            attr = calleeAttr;
-            break;
-          case Id_caller:
-            attr = callerAttr;
-            break;
-          case Id_length:
-            attr = lengthAttr;
-            break;
-          default: throw new IllegalStateException();
+            case Id_callee:
+                attr = calleeAttr;
+                break;
+            case Id_caller:
+                attr = callerAttr;
+                break;
+            case Id_length:
+                attr = lengthAttr;
+                break;
+            default:
+                throw new IllegalStateException();
         }
         return instanceIdInfo(attr, id);
     }
 
-// #/string_id_map#
-
     @Override
-    protected String getInstanceIdName(int id)
-    {
+    protected String getInstanceIdName(int id) {
         switch (id) {
-            case Id_callee: return "callee";
-            case Id_length: return "length";
-            case Id_caller: return "caller";
+            case Id_callee:
+                return "callee";
+            case Id_length:
+                return "length";
+            case Id_caller:
+                return "caller";
         }
         return null;
     }
 
     @Override
-    protected Object getInstanceIdValue(int id)
-    {
+    protected Object getInstanceIdValue(int id) {
         switch (id) {
-            case Id_callee: return calleeObj;
-            case Id_length: return lengthObj;
-            case Id_caller: {
-                Object value = callerObj;
-                if (value == UniqueTag.NULL_VALUE) { value = null; }
-                else if (value == null) {
-                    NativeCall caller = activation.parentActivationCall;
-                    if (caller != null) {
-                        value = caller.get("arguments", caller);
+            case Id_callee:
+                return calleeObj;
+            case Id_length:
+                return lengthObj;
+            case Id_caller:
+                {
+                    Object value = callerObj;
+                    if (value == UniqueTag.NULL_VALUE) {
+                        value = null;
+                    } else if (value == null) {
+                        NativeCall caller = activation.parentActivationCall;
+                        if (caller != null) {
+                            value = caller.get("arguments", caller);
+                        }
                     }
+                    return value;
                 }
-                return value;
-            }
         }
         return super.getInstanceIdValue(id);
     }
 
     @Override
-    protected void setInstanceIdValue(int id, Object value)
-    {
+    protected void setInstanceIdValue(int id, Object value) {
         switch (id) {
-            case Id_callee: calleeObj = value; return;
-            case Id_length: lengthObj = value; return;
+            case Id_callee:
+                calleeObj = value;
+                return;
+            case Id_length:
+                lengthObj = value;
+                return;
             case Id_caller:
                 callerObj = (value != null) ? value : UniqueTag.NULL_VALUE;
                 return;
@@ -273,19 +263,23 @@
     }
 
     @Override
-    protected void setInstanceIdAttributes(int id, int attr)
-    {
+    protected void setInstanceIdAttributes(int id, int attr) {
         switch (id) {
-            case Id_callee: calleeAttr = attr; return;
-            case Id_length: lengthAttr = attr; return;
-            case Id_caller: callerAttr = attr; return;
+            case Id_callee:
+                calleeAttr = attr;
+                return;
+            case Id_length:
+                lengthAttr = attr;
+                return;
+            case Id_caller:
+                callerAttr = attr;
+                return;
         }
         super.setInstanceIdAttributes(id, attr);
     }
 
     @Override
-    Object[] getIds(boolean getNonEnumerable, boolean getSymbols)
-    {
+    Object[] getIds(boolean getNonEnumerable, boolean getSymbols) {
         Object[] ids = super.getIds(getNonEnumerable, getSymbols);
         if (args.length != 0) {
             boolean[] present = new boolean[args.length];
@@ -293,7 +287,7 @@
             for (int i = 0; i != ids.length; ++i) {
                 Object id = ids[i];
                 if (id instanceof Integer) {
-                    int index = ((Integer)id).intValue();
+                    int index = ((Integer) id).intValue();
                     if (0 <= index && index < args.length) {
                         if (!present[index]) {
                             present[index] = true;
@@ -303,12 +297,12 @@
                 }
             }
             if (!getNonEnumerable) { // avoid adding args which were redefined to non-enumerable
-              for (int i = 0; i < present.length; i++) {
-                if (!present[i] && super.has(i, this)) {
-                  present[i] = true;
-                  extraCount--;
+                for (int i = 0; i < present.length; i++) {
+                    if (!present[i] && super.has(i, this)) {
+                        present[i] = true;
+                        extraCount--;
+                    }
                 }
-              }
             }
             if (extraCount != 0) {
                 Object[] tmp = new Object[extraCount + ids.length];
@@ -316,7 +310,7 @@
                 ids = tmp;
                 int offset = 0;
                 for (int i = 0; i != args.length; ++i) {
-                    if (present == null || !present[i]) {
+                    if (!present[i]) {
                         ids[offset] = Integer.valueOf(i);
                         ++offset;
                     }
@@ -329,65 +323,69 @@
 
     @Override
     protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) {
-        if (id instanceof Scriptable) {
-           return super.getOwnPropertyDescriptor(cx, id);
+        if (ScriptRuntime.isSymbol(id) || id instanceof Scriptable) {
+            return super.getOwnPropertyDescriptor(cx, id);
+        }
+
+        double d = ScriptRuntime.toNumber(id);
+        int index = (int) d;
+        if (d != index) {
+            return super.getOwnPropertyDescriptor(cx, id);
+        }
+        Object value = arg(index);
+        if (value == NOT_FOUND) {
+            return super.getOwnPropertyDescriptor(cx, id);
+        }
+        if (sharedWithActivation(index)) {
+            value = getFromActivation(index);
+        }
+        if (super.has(index, this)) { // the descriptor has been redefined
+            ScriptableObject desc = super.getOwnPropertyDescriptor(cx, id);
+            desc.put("value", desc, value);
+            return desc;
         }
-      double d = ScriptRuntime.toNumber(id);
-      int index = (int) d;
-      if (d != index) {
-        return super.getOwnPropertyDescriptor(cx, id);
-      }
-      Object value = arg(index);
-      if (value == NOT_FOUND) {
-        return super.getOwnPropertyDescriptor(cx, id);
-      }
-      if (sharedWithActivation(index)) {
-        value = getFromActivation(index);
-      }
-      if (super.has(index, this)) { // the descriptor has been redefined
-        ScriptableObject desc = super.getOwnPropertyDescriptor(cx, id);
-        desc.put("value", desc, value);
-        return desc;
-      } else {
         Scriptable scope = getParentScope();
         if (scope == null) scope = this;
         return buildDataDescriptor(scope, value, EMPTY);
-      }
     }
 
     @Override
-    protected void defineOwnProperty(Context cx, Object id,
-                                     ScriptableObject desc,
-                                     boolean checkValid) {
-      super.defineOwnProperty(cx, id, desc, checkValid);
+    protected void defineOwnProperty(
+            Context cx, Object id, ScriptableObject desc, boolean checkValid) {
+        super.defineOwnProperty(cx, id, desc, checkValid);
+        if (ScriptRuntime.isSymbol(id)) {
+            return;
+        }
 
-      double d = ScriptRuntime.toNumber(id);
-      int index = (int) d;
-      if (d != index) return;
+        double d = ScriptRuntime.toNumber(id);
+        int index = (int) d;
+        if (d != index) return;
 
-      Object value = arg(index);
-      if (value == NOT_FOUND) return;
+        Object value = arg(index);
+        if (value == NOT_FOUND) return;
 
-      if (isAccessorDescriptor(desc)) {
-        removeArg(index);
-        return;
-      }
+        if (isAccessorDescriptor(desc)) {
+            removeArg(index);
+            return;
+        }
 
-      Object newValue = getProperty(desc, "value");
-      if (newValue == NOT_FOUND) return;
+        Object newValue = getProperty(desc, "value");
+        if (newValue == NOT_FOUND) return;
 
-      replaceArg(index, newValue);
+        replaceArg(index, newValue);
 
-      if (isFalse(getProperty(desc, "writable"))) {
-        removeArg(index);
-      }
+        if (isFalse(getProperty(desc, "writable"))) {
+            removeArg(index);
+        }
     }
 
     // ECMAScript2015
     // 9.4.4.6 CreateUnmappedArgumentsObject(argumentsList)
-    //   8. Perform DefinePropertyOrThrow(obj, "caller", PropertyDescriptor {[[Get]]: %ThrowTypeError%,
+    //   8. Perform DefinePropertyOrThrow(obj, "caller", PropertyDescriptor {[[Get]]:
+    // %ThrowTypeError%,
     //      [[Set]]: %ThrowTypeError%, [[Enumerable]]: false, [[Configurable]]: false}).
-    //   9. Perform DefinePropertyOrThrow(obj, "callee", PropertyDescriptor {[[Get]]: %ThrowTypeError%,
+    //   9. Perform DefinePropertyOrThrow(obj, "callee", PropertyDescriptor {[[Get]]:
+    // %ThrowTypeError%,
     //      [[Set]]: %ThrowTypeError%, [[Enumerable]]: false, [[Configurable]]: false}).
     void defineAttributesForStrictMode() {
         Context cx = Context.getContext();
@@ -404,19 +402,24 @@
         calleeObj = null;
     }
 
-    private static BaseFunction iteratorMethod = new BaseFunction() {
-        @Override
-        public Object call(Context cx, Scriptable scope, Scriptable thisObj,
-                           Object[] args) {
-            // TODO : call %ArrayProto_values%
-            // 9.4.4.6 CreateUnmappedArgumentsObject(argumentsList)
-            //  1. Perform DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor {[[Value]]:%ArrayProto_values%,
-            //     [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}).
-            return new NativeArrayIterator(scope, thisObj);
-        }
-    };
+    private static BaseFunction iteratorMethod =
+            new BaseFunction() {
+                private static final long serialVersionUID = 4239122318596177391L;
+
+                @Override
+                public Object call(
+                        Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+                    // TODO : call %ArrayProto_values%
+                    // 9.4.4.6 CreateUnmappedArgumentsObject(argumentsList)
+                    //  1. Perform DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor
+                    // {[[Value]]:%ArrayProto_values%,
+                    //     [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}).
+                    return new NativeArrayIterator(scope, thisObj, ARRAY_ITERATOR_TYPE.VALUES);
+                }
+            };
 
     private static class ThrowTypeError extends BaseFunction {
+        private static final long serialVersionUID = -744615873947395749L;
         private String propertyName;
 
         ThrowTypeError(String propertyName) {
@@ -425,15 +428,15 @@
 
         @Override
         public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
-            throw ScriptRuntime.typeError1("msg.arguments.not.access.strict", propertyName);
+            throw ScriptRuntime.typeErrorById("msg.arguments.not.access.strict", propertyName);
         }
     }
 
-// Fields to hold caller, callee and length properties,
-// where NOT_FOUND value tags deleted properties.
-// In addition if callerObj == NULL_VALUE, it tags null for scripts, as
-// initial callerObj == null means access to caller arguments available
-// only in JS <= 1.3 scripts
+    // Fields to hold caller, callee and length properties,
+    // where NOT_FOUND value tags deleted properties.
+    // In addition if callerObj == NULL_VALUE, it tags null for scripts, as
+    // initial callerObj == null means access to caller arguments available
+    // only in JS <= 1.3 scripts
     private Object callerObj;
     private Object calleeObj;
     private Object lengthObj;
@@ -444,8 +447,8 @@
 
     private NativeCall activation;
 
-// Initially args holds activation.getOriginalArgs(), but any modification
-// of its elements triggers creation of a copy. If its element holds NOT_FOUND,
-// it indicates deleted index, in which case super class is queried.
+    // Initially args holds activation.getOriginalArgs(), but any modification
+    // of its elements triggers creation of a copy. If its element holds NOT_FOUND,
+    // it indicates deleted index, in which case super class is queried.
     private Object[] args;
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ArrowFunction.java rhino-1.7.14/src/org/mozilla/javascript/ArrowFunction.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ArrowFunction.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ArrowFunction.java	2022-01-06 22:57:21.000000000 +0100
@@ -11,9 +11,9 @@
  * EcmaScript 6 Rev 14, March 8, 2013 Draft spec , 13.2
  */
 public class ArrowFunction extends BaseFunction {
-    
-    static final long serialVersionUID = -7377989503697220633L;
-    
+
+    private static final long serialVersionUID = -7377989503697220633L;
+
     private final Callable targetFunction;
     private final Scriptable boundThis;
 
@@ -24,12 +24,12 @@
 
         ScriptRuntime.setFunctionProtoAndParent(this, scope);
 
-        Function thrower = ScriptRuntime.typeErrorThrower();
+        Function thrower = ScriptRuntime.typeErrorThrower(cx);
         NativeObject throwing = new NativeObject();
         throwing.put("get", throwing, thrower);
         throwing.put("set", throwing, thrower);
-        throwing.put("enumerable", throwing, false);
-        throwing.put("configurable", throwing, false);
+        throwing.put("enumerable", throwing, Boolean.FALSE);
+        throwing.put("configurable", throwing, Boolean.FALSE);
         throwing.preventExtensions();
 
         this.defineOwnProperty(cx, "caller", throwing, false);
@@ -45,7 +45,7 @@
 
     @Override
     public Scriptable construct(Context cx, Scriptable scope, Object[] args) {
-        throw ScriptRuntime.typeError1("msg.not.ctor", decompile(0, 0));
+        throw ScriptRuntime.typeErrorById("msg.not.ctor", decompile(0, 0));
     }
 
     @Override
@@ -53,7 +53,7 @@
         if (targetFunction instanceof Function) {
             return ((Function) targetFunction).hasInstance(instance);
         }
-        throw ScriptRuntime.typeError0("msg.not.ctor");
+        throw ScriptRuntime.typeErrorById("msg.not.ctor");
     }
 
     @Override
@@ -65,6 +65,11 @@
     }
 
     @Override
+    public int getArity() {
+        return getLength();
+    }
+
+    @Override
     String decompile(int indent, int flags)
     {
         if (targetFunction instanceof BaseFunction) {
@@ -72,4 +77,8 @@
         }
         return super.decompile(indent, flags);
     }
+
+    static boolean equalObjectGraphs(ArrowFunction f1, ArrowFunction f2, EqualObjectGraphs eq) {
+        return  eq.equalGraphs(f1.boundThis, f2.boundThis) && eq.equalGraphs(f1.targetFunction, f2.targetFunction);
+    }
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/ArrayComprehension.java rhino-1.7.14/src/org/mozilla/javascript/ast/ArrayComprehension.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/ArrayComprehension.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/ArrayComprehension.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,14 +6,14 @@
 
 package org.mozilla.javascript.ast;
 
-import org.mozilla.javascript.Token;
-
 import java.util.ArrayList;
 import java.util.List;
 
+import org.mozilla.javascript.Token;
+
 /**
  * AST node for a JavaScript 1.7 Array comprehension.
- * Node type is {@link Token#ARRAYCOMP}.<p>
+ * Node type is {@link Token#ARRAYCOMP}.
  */
 public class ArrayComprehension extends Scope {
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/ArrayComprehensionLoop.java rhino-1.7.14/src/org/mozilla/javascript/ast/ArrayComprehensionLoop.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/ArrayComprehensionLoop.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/ArrayComprehensionLoop.java	2022-01-06 22:57:21.000000000 +0100
@@ -10,9 +10,9 @@
 
 /**
  * AST node for a single 'for (foo in bar)' loop construct in a JavaScript 1.7
- * Array comprehension.  This node type is almost equivalent to a
+ * Array comprehension. This node type is almost equivalent to a
  * {@link ForInLoop}, except that it has no body statement.
- * Node type is {@link Token#FOR}.<p>
+ * Node type is {@link Token#FOR}.
  */
 public class ArrayComprehensionLoop extends ForInLoop {
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/ArrayLiteral.java rhino-1.7.14/src/org/mozilla/javascript/ast/ArrayLiteral.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/ArrayLiteral.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/ArrayLiteral.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,18 +6,18 @@
 
 package org.mozilla.javascript.ast;
 
-import org.mozilla.javascript.Token;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
+import org.mozilla.javascript.Token;
+
 /**
  * AST node for an Array literal.  The elements list will always be
  * non-{@code null}, although the list will have no elements if the Array literal
- * is empty.<p>
+ * is empty.
  *
- * Node type is {@link Token#ARRAYLIT}.<p>
+ * <p>Node type is {@link Token#ARRAYLIT}.
  *
  * <pre><i>ArrayLiteral</i> :
  *        <b>[</b> Elisionopt <b>]</b>
@@ -153,6 +153,7 @@
      * in a context such as {@code for ([a, b] in ...)} where it's the
      * target of a destructuring assignment.
      */
+    @Override
     public void setIsDestructuring(boolean destructuring) {
         isDestructuring = destructuring;
     }
@@ -162,6 +163,7 @@
      * a function parameter, the target of a variable initializer, the
      * iterator of a for..in loop, etc.
      */
+    @Override
     public boolean isDestructuring() {
         return isDestructuring;
     }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/AstNode.java rhino-1.7.14/src/org/mozilla/javascript/ast/AstNode.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/AstNode.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/AstNode.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,16 +6,16 @@
 
 package org.mozilla.javascript.ast;
 
-import org.mozilla.javascript.Kit;
-import org.mozilla.javascript.Node;
-import org.mozilla.javascript.Token;
-
 import java.io.Serializable;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.mozilla.javascript.Kit;
+import org.mozilla.javascript.Node;
+import org.mozilla.javascript.Token;
+
 /**
  * Base class for AST node types.  The goal of the AST is to represent the
  * physical source code, to make it useful for code-processing tools such
@@ -59,16 +59,28 @@
  *
  * This hierarchy does not have separate branches for expressions and
  * statements, as the distinction in JavaScript is not as clear-cut as in
- * Java or C++. <p>
+ * Java or C++.
  */
 public abstract class AstNode extends Node implements Comparable<AstNode> {
 
     protected int position = -1;
     protected int length = 1;
     protected AstNode parent;
-
-    private static Map<Integer,String> operatorNames =
-            new HashMap<Integer,String>();
+    /*
+     * Holds comments that are on same line as of actual statement e.g.
+     * For a for loop
+     *      1) for(var i=0; i<10; i++) //test comment { }
+     *      2) for(var i=0; i<10; i++)
+     *          //test comment
+     *          //test comment 2
+     *          { }
+     * For If Statement
+     *      1) if (x == 2) //test if comment
+     *             a = 3 + 4; //then comment
+     * and so on
+     */
+    protected AstNode inlineComment;
+    private static Map<Integer, String> operatorNames = new HashMap<Integer, String>();
 
     static {
         operatorNames.put(Token.IN, "in");
@@ -98,6 +110,7 @@
         operatorNames.put(Token.MUL, "*");
         operatorNames.put(Token.DIV, "/");
         operatorNames.put(Token.MOD, "%");
+        operatorNames.put(Token.EXP, "**");
         operatorNames.put(Token.NOT, "!");
         operatorNames.put(Token.BITNOT, "~");
         operatorNames.put(Token.POS, "+");
@@ -116,17 +129,18 @@
         operatorNames.put(Token.ASSIGN_DIV, "/=");
         operatorNames.put(Token.ASSIGN_MOD, "%=");
         operatorNames.put(Token.ASSIGN_BITXOR, "^=");
+        operatorNames.put(Token.ASSIGN_EXP, "**=");
         operatorNames.put(Token.VOID, "void");
     }
 
     public static class PositionComparator implements Comparator<AstNode>, Serializable {
         private static final long serialVersionUID = 1L;
-
-        /**
+       /**
          * Sorts nodes by (relative) start position.  The start positions are
          * relative to their parent, so this comparator is only meaningful for
          * comparing siblings.
          */
+        @Override
         public int compare(AstNode n1, AstNode n2) {
             return n1.position - n2.position;
         }
@@ -239,12 +253,12 @@
 
         // Convert position back to absolute.
         if (this.parent != null) {
-            setRelative(-this.parent.getPosition());
+            setRelative(-this.parent.getAbsolutePosition());
         }
 
         this.parent = parent;
         if (parent != null) {
-            setRelative(parent.getPosition());
+            setRelative(parent.getAbsolutePosition());
         }
     }
 
@@ -425,6 +439,7 @@
           case Token.WITH:
           case Token.WITHEXPR:
           case Token.YIELD:
+          case Token.YIELD_STAR:
             return true;
 
           default:
@@ -521,6 +536,7 @@
      * {@code other}'s length.  If the lengths are equal, sorts abitrarily
      * on hashcode unless the nodes are the same per {@link #equals}.
      */
+    @Override
     public int compareTo(AstNode other) {
         if (this.equals(other)) return 0;
         int abs1 = this.getAbsolutePosition();
@@ -553,13 +569,16 @@
         public String toString() {
             return buffer.toString();
         }
-        private String makeIndent(int depth) {
+
+        private static String makeIndent(int depth) {
             StringBuilder sb = new StringBuilder(DEBUG_INDENT * depth);
             for (int i = 0; i < (DEBUG_INDENT * depth); i++) {
                 sb.append(" ");
             }
             return sb.toString();
         }
+
+        @Override
         public boolean visit(AstNode node) {
             int tt = node.getType();
             String name = Token.typeToName(tt);
@@ -570,6 +589,8 @@
             buffer.append(node.getLength());
             if (tt == Token.NAME) {
                 buffer.append(" ").append(((Name)node).getIdentifier());
+            } else if(tt == Token.STRING) {
+                buffer.append(" ").append(((StringLiteral)node).getValue(true));
             }
             buffer.append("\n");
             return true;  // process kids
@@ -601,4 +622,12 @@
         visit(dpv);
         return dpv.toString();
     }
+
+    public AstNode getInlineComment() {
+        return inlineComment;
+    }
+
+    public void setInlineComment(AstNode inlineComment) {
+        this.inlineComment = inlineComment;
+    }
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/AstRoot.java rhino-1.7.14/src/org/mozilla/javascript/ast/AstRoot.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/AstRoot.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/AstRoot.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,18 +6,18 @@
 
 package org.mozilla.javascript.ast;
 
-import org.mozilla.javascript.Node;
-import org.mozilla.javascript.Token;
-
 import java.util.SortedSet;
 import java.util.TreeSet;
 
+import org.mozilla.javascript.Node;
+import org.mozilla.javascript.Token;
+
 /**
  * Node for the root of a parse tree.  It contains the statements and functions
  * in the script, and a list of {@link Comment} nodes associated with the script
- * as a whole.  Node type is {@link Token#SCRIPT}. <p>
+ * as a whole.  Node type is {@link Token#SCRIPT}.
  *
- * Note that the tree itself does not store errors.  To collect the parse errors
+ * <p>Note that the tree itself does not store errors. To collect the parse errors
  * and warnings, pass an {@link org.mozilla.javascript.ErrorReporter} to the
  * {@link org.mozilla.javascript.Parser} via the
  * {@link org.mozilla.javascript.CompilerEnvirons}.
@@ -107,6 +107,9 @@
         StringBuilder sb = new StringBuilder();
         for (Node node : this) {
             sb.append(((AstNode)node).toSource(depth));
+            if(node.getType() == Token.COMMENT) {
+                sb.append("\n");
+            }
         }
         return sb.toString();
     }
@@ -128,6 +131,7 @@
      */
     public void checkParentLinks() {
         this.visit(new NodeVisitor() {
+            @Override
             public boolean visit(AstNode node) {
                 int type = node.getType();
                 if (type == Token.SCRIPT)
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/BigIntLiteral.java rhino-1.7.14/src/org/mozilla/javascript/ast/BigIntLiteral.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/BigIntLiteral.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/BigIntLiteral.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,82 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript.ast;
+
+import java.math.BigInteger;
+import org.mozilla.javascript.Token;
+
+/** AST node for a BigInt literal. Node type is {@link Token#BIGINT}. */
+public class BigIntLiteral extends AstNode {
+
+    private String value;
+    private BigInteger bigInt;
+
+    {
+        type = Token.BIGINT;
+    }
+
+    public BigIntLiteral() {}
+
+    public BigIntLiteral(int pos) {
+        super(pos);
+    }
+
+    public BigIntLiteral(int pos, int len) {
+        super(pos, len);
+    }
+
+    /** Constructor. Sets the length to the length of the {@code value} string. */
+    public BigIntLiteral(int pos, String value) {
+        super(pos);
+        setValue(value);
+        setLength(value.length());
+    }
+
+    /** Constructor. Sets the length to the length of the {@code value} string. */
+    public BigIntLiteral(int pos, String value, BigInteger bigInt) {
+        this(pos, value);
+        setBigInt(bigInt);
+    }
+
+    /** Returns the node's string value (the original source token) */
+    public String getValue() {
+        return value;
+    }
+
+    /**
+     * Sets the node's value
+     *
+     * @throws IllegalArgumentException} if value is {@code null}
+     */
+    public void setValue(String value) {
+        assertNotNull(value);
+        this.value = value;
+    }
+
+    /** Gets the {@code BigInteger} value. */
+    @Override
+    public BigInteger getBigInt() {
+        return bigInt;
+    }
+
+    /** Sets the node's {@code BigInteger} value. */
+    @Override
+    public void setBigInt(BigInteger value) {
+        bigInt = value;
+    }
+
+    @Override
+    public String toSource(int depth) {
+        return makeIndent(depth) + (bigInt == null ? "<null>" : bigInt.toString());
+    }
+
+    /** Visits this node. There are no children to visit. */
+    @Override
+    public void visit(NodeVisitor v) {
+        v.visit(this);
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/Block.java rhino-1.7.14/src/org/mozilla/javascript/ast/Block.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/Block.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/Block.java	2022-01-06 22:57:21.000000000 +0100
@@ -47,10 +47,18 @@
         sb.append(makeIndent(depth));
         sb.append("{\n");
         for (Node kid : this) {
-            sb.append(((AstNode)kid).toSource(depth+1));
+            AstNode astNodeKid = (AstNode)kid;
+            sb.append(astNodeKid.toSource(depth+1));
+            if(astNodeKid.getType() == Token.COMMENT) {
+                sb.append("\n");
+            }
         }
         sb.append(makeIndent(depth));
-        sb.append("}\n");
+        sb.append("}");
+        if(this.getInlineComment() != null) {
+            sb.append(this.getInlineComment().toSource(depth));
+        }
+        sb.append("\n");
         return sb.toString();
     }
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/BreakStatement.java rhino-1.7.14/src/org/mozilla/javascript/ast/BreakStatement.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/BreakStatement.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/BreakStatement.java	2022-01-06 22:57:21.000000000 +0100
@@ -9,7 +9,7 @@
 import org.mozilla.javascript.Token;
 
 /**
- * A break statement.  Node type is {@link Token#BREAK}.<p>
+ * A break statement.  Node type is {@link Token#BREAK}.
  *
  * <pre><i>BreakStatement</i> :
  *   <b>break</b> [<i>no LineTerminator here</i>] [Identifier] ;</pre>
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/Comment.java rhino-1.7.14/src/org/mozilla/javascript/ast/Comment.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/Comment.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/Comment.java	2022-01-06 22:57:21.000000000 +0100
@@ -10,13 +10,13 @@
 
 /**
  * Node representing comments.
- * Node type is {@link Token#COMMENT}.<p>
+ * Node type is {@link Token#COMMENT}.
  *
  * <p>JavaScript effectively has five comment types:
  *   <ol>
  *     <li>// line comments</li>
- *     <li>/<span class="none">* block comments *\/</li>
- *     <li>/<span class="none">** jsdoc comments *\/</li>
+ *     <li>/* block comments *\/</li>
+ *     <li>/** jsdoc comments *\/</li>
  *     <li><!-- html-open line comments</li>
  *     <li>^\\s*--> html-close line comments</li>
  *   </ol>
@@ -24,7 +24,7 @@
  * <p>The first three should be familiar to Java programmers.  JsDoc comments
  * are really just block comments with some conventions about the formatting
  * within the comment delimiters.  Line and block comments are described in the
- * Ecma-262 specification. <p>
+ * Ecma-262 specification.
  *
  * <p>SpiderMonkey and Rhino also support HTML comment syntax, but somewhat
  * counterintuitively, the syntax does not produce a block comment.  Instead,
@@ -32,9 +32,9 @@
  * a comment, and if the token --> is the first non-whitespace on the line,
  * then the line is considered a line comment.  This is to support parsing
  * JavaScript in <script> HTML tags that has been "hidden" from very old
- * browsers by surrounding it with HTML comment delimiters. <p>
+ * browsers by surrounding it with HTML comment delimiters.
  *
- * Note the node start position for Comment nodes is still relative to the
+ * <p>Note the node start position for Comment nodes is still relative to the
  * parent, but Comments are always stored directly in the AstRoot node, so
  * they are also effectively absolute offsets.
  */
@@ -83,11 +83,23 @@
         return value;
     }
 
+    /**
+     * Set the comment Value with the new commentString. and updates the length with new Length.
+     * @param commentString
+     */
+    public void setValue(String commentString) {
+        this.value = commentString;
+        this.setLength(this.value.length());
+    }
+
     @Override
     public String toSource(int depth) {
         StringBuilder sb = new StringBuilder(getLength() + 10);
         sb.append(makeIndent(depth));
         sb.append(value);
+        if(Token.CommentType.BLOCK_COMMENT == this.getCommentType()) {
+            sb.append("\n");
+        }
         return sb.toString();
     }
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/ConditionalExpression.java rhino-1.7.14/src/org/mozilla/javascript/ast/ConditionalExpression.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/ConditionalExpression.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/ConditionalExpression.java	2022-01-06 22:57:21.000000000 +0100
@@ -15,7 +15,7 @@
  * <pre><i>ConditionalExpression</i> :
  *        LogicalORExpression
  *        LogicalORExpression ? AssignmentExpression
- *                            : AssignmentExpression</pre>
+ *                            : AssignmentExpression
  *
  * <i>ConditionalExpressionNoIn</i> :
  *        LogicalORExpressionNoIn
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/ContinueStatement.java rhino-1.7.14/src/org/mozilla/javascript/ast/ContinueStatement.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/ContinueStatement.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/ContinueStatement.java	2022-01-06 22:57:21.000000000 +0100
@@ -10,7 +10,7 @@
 
 /**
  * A continue statement.
- * Node type is {@link Token#CONTINUE}.<p>
+ * Node type is {@link Token#CONTINUE}.
  *
  * <pre><i>ContinueStatement</i> :
  *   <b>continue</b> [<i>no LineTerminator here</i>] [Identifier] ;</pre>
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/DoLoop.java rhino-1.7.14/src/org/mozilla/javascript/ast/DoLoop.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/DoLoop.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/DoLoop.java	2022-01-06 22:57:21.000000000 +0100
@@ -9,7 +9,7 @@
 import org.mozilla.javascript.Token;
 
 /**
- * Do statement.  Node type is {@link Token#DO}.<p>
+ * Do statement.  Node type is {@link Token#DO}.
  *
  * <pre><i>DoLoop</i>:
  * <b>do</b> Statement <b>while</b> <b>(</b> Expression <b>)</b> <b>;</b></pre>
@@ -70,6 +70,9 @@
         StringBuilder sb = new StringBuilder();
         sb.append(makeIndent(depth));
         sb.append("do ");
+        if(this.getInlineComment() != null) {
+            sb.append(this.getInlineComment().toSource(depth + 1)).append("\n");
+        }
         sb.append(body.toSource(depth).trim());
         sb.append(" while (");
         sb.append(condition.toSource(0));
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/EmptyStatement.java rhino-1.7.14/src/org/mozilla/javascript/ast/EmptyStatement.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/EmptyStatement.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/EmptyStatement.java	2022-01-06 22:57:21.000000000 +0100
@@ -9,7 +9,7 @@
 import org.mozilla.javascript.Token;
 
 /**
- * AST node for an empty statement.  Node type is {@link Token#EMPTY}.<p>
+ * AST node for an empty statement.  Node type is {@link Token#EMPTY}.
  *
  */
 public class EmptyStatement extends AstNode {
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/ErrorCollector.java rhino-1.7.14/src/org/mozilla/javascript/ast/ErrorCollector.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/ErrorCollector.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/ErrorCollector.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,11 +6,11 @@
 
 package org.mozilla.javascript.ast;
 
-import org.mozilla.javascript.EvaluatorException;
-
 import java.util.ArrayList;
 import java.util.List;
 
+import org.mozilla.javascript.EvaluatorException;
+
 /**
  * An error reporter that gathers the errors and warnings for later display.
  * This a useful {@link org.mozilla.javascript.ErrorReporter} when the
@@ -28,14 +28,16 @@
      * {@link #warning(String,String,int,int)} is used instead.
      * @throws UnsupportedOperationException
      */
+    @Override
     public void warning(String message, String sourceName, int line,
                         String lineSource, int lineOffset) {
         throw new UnsupportedOperationException();
     }
 
     /**
-     * @inheritDoc
+     * {@inheritDoc}
      */
+    @Override
     public void warning(String message, String sourceName, int offset, int length)
     {
         errors.add(new ParseProblem(ParseProblem.Type.Warning,
@@ -48,6 +50,7 @@
      * {@link #warning(String,String,int,int)} is used instead.
      * @throws UnsupportedOperationException
      */
+    @Override
     public void error(String message, String sourceName, int line,
                       String lineSource, int lineOffset)
     {
@@ -55,8 +58,9 @@
     }
 
     /**
-     * @inheritDoc
+     * {@inheritDoc}
      */
+    @Override
     public void error(String message, String sourceName,
                       int fileOffset, int length)
     {
@@ -66,8 +70,9 @@
     }
 
     /**
-     * @inheritDoc
+     * {@inheritDoc}
      */
+    @Override
     public EvaluatorException runtimeError(String message, String sourceName,
                                            int line, String lineSource,
                                            int lineOffset)
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/ErrorNode.java rhino-1.7.14/src/org/mozilla/javascript/ast/ErrorNode.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/ErrorNode.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/ErrorNode.java	2022-01-06 22:57:21.000000000 +0100
@@ -10,7 +10,7 @@
 
 /**
  * AST node representing a parse error or a warning.  Node type is
- * {@link Token#ERROR}.<p>
+ * {@link Token#ERROR}.
  */
 public class ErrorNode extends AstNode {
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/ExpressionStatement.java rhino-1.7.14/src/org/mozilla/javascript/ast/ExpressionStatement.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/ExpressionStatement.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/ExpressionStatement.java	2022-01-06 22:57:21.000000000 +0100
@@ -107,7 +107,11 @@
     public String toSource(int depth) {
         StringBuilder sb = new StringBuilder();
         sb.append(expr.toSource(depth));
-        sb.append(";\n");
+        sb.append(";");
+        if(this.getInlineComment() != null) {
+            sb.append(this.getInlineComment().toSource(depth));
+        }
+        sb.append("\n");
         return sb.toString();
     }
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/ForInLoop.java rhino-1.7.14/src/org/mozilla/javascript/ast/ForInLoop.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/ForInLoop.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/ForInLoop.java	2022-01-06 22:57:21.000000000 +0100
@@ -9,7 +9,7 @@
 import org.mozilla.javascript.Token;
 
 /**
- * For-in or for-each-in or for-of statement.  Node type is {@link Token#FOR}.<p>
+ * For-in or for-each-in or for-of statement.  Node type is {@link Token#FOR}.
  *
  * <pre><b>for</b> [<b>each</b>] ( LeftHandSideExpression <b>in</b> Expression ) Statement</pre>
  * <pre><b>for</b> [<b>each</b>] ( <b>var</b> VariableDeclarationNoIn <b>in</b> Expression ) Statement</pre>
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/ForLoop.java rhino-1.7.14/src/org/mozilla/javascript/ast/ForLoop.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/ForLoop.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/ForLoop.java	2022-01-06 22:57:21.000000000 +0100
@@ -10,7 +10,7 @@
 
 /**
  * C-style for-loop statement.
- * Node type is {@link Token#FOR}.<p>
+ * Node type is {@link Token#FOR}.
  *
  * <pre><b>for</b> ( ExpressionNoInopt; Expressionopt ; Expressionopt ) Statement</pre>
  * <pre><b>for</b> ( <b>var</b> VariableDeclarationListNoIn; Expressionopt ; Expressionopt ) Statement</pre>
@@ -110,10 +110,20 @@
         sb.append("; ");
         sb.append(increment.toSource(0));
         sb.append(") ");
+        if(this.getInlineComment() != null) {
+            sb.append(this.getInlineComment().toSource()).append("\n");
+        }
         if (body.getType() == Token.BLOCK) {
-            sb.append(body.toSource(depth).trim()).append("\n");
+            String bodySource = body.toSource(depth);
+            if(this.getInlineComment() == null) {
+                bodySource = bodySource.trim();
+            }
+            sb.append(bodySource).append("\n");
         } else {
-            sb.append("\n").append(body.toSource(depth+1));
+            if(this.getInlineComment() == null) {
+                sb.append("\n");
+            }
+            sb.append(body.toSource(depth+1));
         }
         return sb.toString();
     }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/FunctionCall.java rhino-1.7.14/src/org/mozilla/javascript/ast/FunctionCall.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/FunctionCall.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/FunctionCall.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,14 +6,14 @@
 
 package org.mozilla.javascript.ast;
 
-import org.mozilla.javascript.Token;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
+import org.mozilla.javascript.Token;
+
 /**
- * AST node for a function call.  Node type is {@link Token#CALL}.<p>
+ * AST node for a function call.  Node type is {@link Token#CALL}.
  */
 public class FunctionCall extends AstNode {
 
@@ -146,6 +146,9 @@
             printList(arguments, sb);
         }
         sb.append(")");
+        if(this.getInlineComment() != null) {
+            sb.append(this.getInlineComment().toSource(depth)).append("\n");
+        }
         return sb.toString();
     }
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/FunctionNode.java rhino-1.7.14/src/org/mozilla/javascript/ast/FunctionNode.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/FunctionNode.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/FunctionNode.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,18 +6,18 @@
 
 package org.mozilla.javascript.ast;
 
-import org.mozilla.javascript.Node;
-import org.mozilla.javascript.Token;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.mozilla.javascript.Node;
+import org.mozilla.javascript.Token;
+
 /**
- * A JavaScript function declaration or expression.<p>
- * Node type is {@link Token#FUNCTION}.<p>
+ * A JavaScript function declaration or expression.
+ * <p>Node type is {@link Token#FUNCTION}.</p>
  *
  * <pre><i>FunctionDeclaration</i> :
  *        <b>function</b> Identifier ( FormalParameterListopt ) { FunctionBody }
@@ -58,7 +58,7 @@
      * top-level expression in an expression statement.<p>
      *
      * The three types of functions have different treatment and must be
-     * distinguished.<p>
+     * distinguished.
      */
     public static final int FUNCTION_STATEMENT            = 1;
     public static final int FUNCTION_EXPRESSION           = 2;
@@ -82,6 +82,7 @@
     private int functionType;
     private boolean needsActivation;
     private boolean isGenerator;
+    private boolean isES6Generator;
     private List<Node> generatorResumePoints;
     private Map<Node,int[]> liveLocals;
     private AstNode memberExprNode;
@@ -286,6 +287,15 @@
         isGenerator = true;
     }
 
+    public boolean isES6Generator() {
+        return isES6Generator;
+    }
+
+    public void setIsES6Generator() {
+        isES6Generator = true;
+        isGenerator = true;
+    }
+
     public void addResumptionPoint(Node target) {
         if (generatorResumePoints == null)
             generatorResumePoints = new ArrayList<Node>();
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/GeneratorExpression.java rhino-1.7.14/src/org/mozilla/javascript/ast/GeneratorExpression.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/GeneratorExpression.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/GeneratorExpression.java	2022-01-06 22:57:21.000000000 +0100
@@ -8,6 +8,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+
 import org.mozilla.javascript.Token;
 
 /**
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/IfStatement.java rhino-1.7.14/src/org/mozilla/javascript/ast/IfStatement.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/IfStatement.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/IfStatement.java	2022-01-06 22:57:21.000000000 +0100
@@ -9,7 +9,7 @@
 import org.mozilla.javascript.Token;
 
 /**
- * If-else statement.  Node type is {@link Token#IF}.<p>
+ * If-else statement.  Node type is {@link Token#IF}.
  *
  * <pre><i>IfStatement</i> :
  *       <b>if</b> ( Expression ) Statement <b>else</b> Statement
@@ -21,6 +21,7 @@
     private AstNode thenPart;
     private int elsePosition = -1;
     private AstNode elsePart;
+    private AstNode elseKeyWordInlineComment;
     private int lp = -1;
     private int rp = -1;
 
@@ -149,9 +150,15 @@
         sb.append("if (");
         sb.append(condition.toSource(0));
         sb.append(") ");
-        if (thenPart.getType() != Token.BLOCK) {
-            sb.append("\n").append(makeIndent(depth + 1));
+        if(this.getInlineComment() != null) {
+            sb.append("    ").append(this.getInlineComment().toSource()).append("\n");
         }
+        if (thenPart.getType() != Token.BLOCK) {
+            if(this.getInlineComment() == null) {
+                sb.append("\n");
+            }
+            sb.append(makeIndent(depth + 1));
+        } 
         sb.append(thenPart.toSource(depth).trim());
         if (elsePart != null) {
             if (thenPart.getType() != Token.BLOCK) {
@@ -159,9 +166,15 @@
             } else {
                 sb.append(" else ");
             }
+            if(this.getElseKeyWordInlineComment() != null) {
+                sb.append("    ").append(this.getElseKeyWordInlineComment().toSource()).append("\n");
+            }
             if (elsePart.getType() != Token.BLOCK
                     && elsePart.getType() != Token.IF) {
-                sb.append("\n").append(makeIndent(depth + 1));
+                if(this.getElseKeyWordInlineComment() == null) {
+                    sb.append("\n");
+                }
+                sb.append(makeIndent(depth + 1));
             }
             sb.append(elsePart.toSource(depth).trim());
         }
@@ -183,4 +196,13 @@
             }
         }
     }
+
+    public AstNode getElseKeyWordInlineComment() {
+        return elseKeyWordInlineComment;
+    }
+
+    public void setElseKeyWordInlineComment(AstNode elseKeyWordInlineComment) {
+        this.elseKeyWordInlineComment = elseKeyWordInlineComment;
+    }
+    
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/LabeledStatement.java rhino-1.7.14/src/org/mozilla/javascript/ast/LabeledStatement.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/LabeledStatement.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/LabeledStatement.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,17 +6,17 @@
 
 package org.mozilla.javascript.ast;
 
-import org.mozilla.javascript.Token;
-
 import java.util.ArrayList;
 import java.util.List;
 
+import org.mozilla.javascript.Token;
+
 /**
  * A labeled statement.  A statement can have more than one label.  In
  * this AST representation, all labels for a statement are collapsed into
- * the "labels" list of a single {@link LabeledStatement} node. <p>
+ * the "labels" list of a single {@link LabeledStatement} node.
  *
- * Node type is {@link Token#EXPR_VOID}. <p>
+ * <p>Node type is {@link Token#EXPR_VOID}.</p>
  */
 public class LabeledStatement extends AstNode {
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/Label.java rhino-1.7.14/src/org/mozilla/javascript/ast/Label.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/Label.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/Label.java	2022-01-06 22:57:21.000000000 +0100
@@ -11,7 +11,7 @@
 /**
  * AST node representing a label.  It is a distinct node type so it can
  * record its length and position for code-processing tools.
- * Node type is {@link Token#LABEL}.<p>
+ * Node type is {@link Token#LABEL}.
  */
 public class Label extends Jump {
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/LetNode.java rhino-1.7.14/src/org/mozilla/javascript/ast/LetNode.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/LetNode.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/LetNode.java	2022-01-06 22:57:21.000000000 +0100
@@ -10,7 +10,7 @@
 
 /**
  * AST node for let statements and expressions.
- * Node type is {@link Token#LET} or {@link Token#LETEXPR}.<p>
+ * Node type is {@link Token#LET} or {@link Token#LETEXPR}.
  *
  * <pre> <i>LetStatement</i>:
  *     <b>let</b> ( VariableDeclarationList ) Block
@@ -20,7 +20,7 @@
  * Note that standalone let-statements with no parens or body block,
  * such as {@code let x=6, y=7;}, are represented as a
  * {@link VariableDeclaration} node of type {@code Token.LET},
- * wrapped with an {@link ExpressionStatement}.<p>
+ * wrapped with an {@link ExpressionStatement}.
  */
 public class LetNode extends Scope {
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/NewExpression.java rhino-1.7.14/src/org/mozilla/javascript/ast/NewExpression.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/NewExpression.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/NewExpression.java	2022-01-06 22:57:21.000000000 +0100
@@ -9,7 +9,7 @@
 import org.mozilla.javascript.Token;
 
 /**
- * New expression. Node type is {@link Token#NEW}.<p>
+ * New expression. Node type is {@link Token#NEW}.
  *
  * <pre><i>NewExpression</i> :
  *      MemberExpression
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/NumberLiteral.java rhino-1.7.14/src/org/mozilla/javascript/ast/NumberLiteral.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/NumberLiteral.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/NumberLiteral.java	2022-01-06 22:57:21.000000000 +0100
@@ -9,7 +9,7 @@
 import org.mozilla.javascript.Token;
 
 /**
- * AST node for a Number literal. Node type is {@link Token#NUMBER}.<p>
+ * AST node for a Number literal. Node type is {@link Token#NUMBER}.
  */
 public class NumberLiteral extends AstNode {
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/ObjectLiteral.java rhino-1.7.14/src/org/mozilla/javascript/ast/ObjectLiteral.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/ObjectLiteral.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/ObjectLiteral.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,18 +6,18 @@
 
 package org.mozilla.javascript.ast;
 
-import org.mozilla.javascript.Token;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
+import org.mozilla.javascript.Token;
+
 /**
  * AST node for an Object literal (also called an Object initialiser in
  * Ecma-262).  The elements list will always be non-{@code null}, although
- * the list will have no elements if the Object literal is empty.<p>
+ * the list will have no elements if the Object literal is empty.
  *
- * Node type is {@link Token#OBJECTLIT}.<p>
+ * <p>Node type is {@link Token#OBJECTLIT}.</p>
  *
  * <pre><i>ObjectLiteral</i> :
  *       <b>{}</b>
@@ -96,6 +96,7 @@
      * in a context such as {@code for ([a, b] in ...)} where it's the
      * target of a destructuring assignment.
      */
+    @Override
     public void setIsDestructuring(boolean destructuring) {
         isDestructuring = destructuring;
     }
@@ -105,6 +106,7 @@
      * a function parameter, the target of a variable initializer, the
      * iterator of a for..in loop, etc.
      */
+    @Override
     public boolean isDestructuring() {
         return isDestructuring;
     }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/ObjectProperty.java rhino-1.7.14/src/org/mozilla/javascript/ast/ObjectProperty.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/ObjectProperty.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/ObjectProperty.java	2022-01-06 22:57:21.000000000 +0100
@@ -9,26 +9,22 @@
 import org.mozilla.javascript.Token;
 
 /**
- * AST node for a single name:value entry in an Object literal.
- * For simple entries, the node type is {@link Token#COLON}, and
- * the name (left side expression) is either a {@link Name}, a
- * {@link StringLiteral} or a {@link NumberLiteral}.<p>
+ * AST node for a single name:value entry in an Object literal. For simple entries, the node type is
+ * {@link Token#COLON}, and the name (left side expression) is either a {@link Name}, a {@link
+ * StringLiteral}, a {@link NumberLiteral} or a {@link BigIntLiteral}.
  *
- * This node type is also used for getter/setter properties in object
- * literals.  In this case the node bounds include the "get" or "set"
- * keyword.  The left-hand expression in this case is always a
- * {@link Name}, and the overall node type is {@link Token#GET} or
- * {@link Token#SET}, as appropriate.<p>
- *
- * The {@code operatorPosition} field is meaningless if the node is
- * a getter or setter.<p>
+ * <p>This node type is also used for getter/setter properties in object literals. In this case the
+ * node bounds include the "get" or "set" keyword. The left-hand expression in this case is always a
+ * {@link Name}, and the overall node type is {@link Token#GET} or {@link Token#SET}, as
+ * appropriate. The {@code operatorPosition} field is meaningless if the node is a getter or setter.
  *
  * <pre><i>ObjectProperty</i> :
  *       PropertyName <b>:</b> AssignmentExpression
  * <i>PropertyName</i> :
  *       Identifier
  *       StringLiteral
- *       NumberLiteral</pre>
+ *       NumberLiteral
+ *       BigIntLiteral</pre>
  */
 public class ObjectProperty extends InfixExpression {
 
@@ -37,22 +33,21 @@
     }
 
     /**
-     * Sets the node type.  Must be one of
-     * {@link Token#COLON}, {@link Token#GET}, or {@link Token#SET}.
+     * Sets the node type. Must be one of {@link Token#COLON}, {@link Token#GET}, or {@link
+     * Token#SET}.
+     *
      * @throws IllegalArgumentException if {@code nodeType} is invalid
      */
     public void setNodeType(int nodeType) {
         if (nodeType != Token.COLON
-            && nodeType != Token.GET
-            && nodeType != Token.SET
-            && nodeType != Token.METHOD)
-            throw new IllegalArgumentException("invalid node type: "
-                                               + nodeType);
+                && nodeType != Token.GET
+                && nodeType != Token.SET
+                && nodeType != Token.METHOD)
+            throw new IllegalArgumentException("invalid node type: " + nodeType);
         setType(nodeType);
     }
 
-    public ObjectProperty() {
-    }
+    public ObjectProperty() {}
 
     public ObjectProperty(int pos) {
         super(pos);
@@ -62,30 +57,22 @@
         super(pos, len);
     }
 
-    /**
-     * Marks this node as a "getter" property.
-     */
+    /** Marks this node as a "getter" property. */
     public void setIsGetterMethod() {
         type = Token.GET;
     }
 
-    /**
-     * Returns true if this is a getter function.
-     */
+    /** Returns true if this is a getter function. */
     public boolean isGetterMethod() {
         return type == Token.GET;
     }
 
-    /**
-     * Marks this node as a "setter" property.
-     */
+    /** Marks this node as a "setter" property. */
     public void setIsSetterMethod() {
         type = Token.SET;
     }
 
-    /**
-     * Returns true if this is a setter function.
-     */
+    /** Returns true if this is a setter function. */
     public boolean isSetterMethod() {
         return type == Token.SET;
     }
@@ -106,17 +93,17 @@
     public String toSource(int depth) {
         StringBuilder sb = new StringBuilder();
         sb.append("\n");
-        sb.append(makeIndent(depth+1));
+        sb.append(makeIndent(depth + 1));
         if (isGetterMethod()) {
             sb.append("get ");
         } else if (isSetterMethod()) {
             sb.append("set ");
         }
-        sb.append(left.toSource(getType()==Token.COLON ? 0 : depth));
+        sb.append(left.toSource(getType() == Token.COLON ? 0 : depth));
         if (type == Token.COLON) {
             sb.append(": ");
         }
-        sb.append(right.toSource(getType()==Token.COLON ? 0 : depth+1));
+        sb.append(right.toSource(getType() == Token.COLON ? 0 : depth + 1));
         return sb.toString();
     }
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/ParenthesizedExpression.java rhino-1.7.14/src/org/mozilla/javascript/ast/ParenthesizedExpression.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/ParenthesizedExpression.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/ParenthesizedExpression.java	2022-01-06 22:57:21.000000000 +0100
@@ -10,7 +10,7 @@
 
 /**
  * AST node for a parenthesized expression.
- * Node type is {@link Token#LP}.<p>
+ * Node type is {@link Token#LP}.
  */
 public class ParenthesizedExpression extends AstNode {
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/RegExpLiteral.java rhino-1.7.14/src/org/mozilla/javascript/ast/RegExpLiteral.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/RegExpLiteral.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/RegExpLiteral.java	2022-01-06 22:57:21.000000000 +0100
@@ -10,7 +10,7 @@
 
 /**
  * AST node for a RegExp literal.
- * Node type is {@link Token#REGEXP}.<p>
+ * Node type is {@link Token#REGEXP}.
  */
 public class RegExpLiteral extends AstNode {
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/ReturnStatement.java rhino-1.7.14/src/org/mozilla/javascript/ast/ReturnStatement.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/ReturnStatement.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/ReturnStatement.java	2022-01-06 22:57:21.000000000 +0100
@@ -9,7 +9,7 @@
 import org.mozilla.javascript.Token;
 
 /**
- * Return statement.  Node type is {@link Token#RETURN}.<p>
+ * Return statement.  Node type is {@link Token#RETURN}.
  *
  * <pre><i>ReturnStatement</i> :
  *      <b>return</b> [<i>no LineTerminator here</i>] [Expression] ;</pre>
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/Scope.java rhino-1.7.14/src/org/mozilla/javascript/ast/Scope.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/Scope.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/Scope.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,15 +6,15 @@
 
 package org.mozilla.javascript.ast;
 
-import org.mozilla.javascript.Node;
-import org.mozilla.javascript.Token;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.mozilla.javascript.Node;
+import org.mozilla.javascript.Token;
+
 /**
  * Represents a scope in the lexical scope chain.  Base type for
  * all {@link AstNode} implementations that can introduce a new scope.
@@ -237,7 +237,11 @@
         sb.append(makeIndent(depth));
         sb.append("{\n");
         for (Node kid : this) {
-            sb.append(((AstNode)kid).toSource(depth+1));
+            AstNode astNodeKid = (AstNode) kid;
+            sb.append(astNodeKid.toSource(depth+1));
+            if(astNodeKid.getType() == Token.COMMENT) {
+                sb.append("\n");
+            }
         }
         sb.append(makeIndent(depth));
         sb.append("}\n");
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/ScriptNode.java rhino-1.7.14/src/org/mozilla/javascript/ast/ScriptNode.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/ScriptNode.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/ScriptNode.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,16 +6,15 @@
 
 package org.mozilla.javascript.ast;
 
-import org.mozilla.javascript.Node;
-import org.mozilla.javascript.Token;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import org.mozilla.javascript.Node;
+import org.mozilla.javascript.Token;
 
 /**
- * Base type for {@link AstRoot} and {@link FunctionNode} nodes, which need to
- * collect much of the same information.
+ * Base type for {@link AstRoot} and {@link FunctionNode} nodes, which need to collect much of the
+ * same information.
  */
 public class ScriptNode extends Scope {
 
@@ -27,6 +26,7 @@
 
     private List<FunctionNode> functions;
     private List<RegExpLiteral> regexps;
+    private List<TemplateLiteral> templateLiterals;
     private List<FunctionNode> EMPTY_LIST = Collections.emptyList();
 
     private List<Symbol> symbols = new ArrayList<Symbol>(4);
@@ -44,32 +44,29 @@
         this.type = Token.SCRIPT;
     }
 
-    public ScriptNode() {
-    }
+    public ScriptNode() {}
 
     public ScriptNode(int pos) {
         super(pos);
     }
 
     /**
-     * Returns the URI, path or descriptive text indicating the origin
-     * of this script's source code.
+     * Returns the URI, path or descriptive text indicating the origin of this script's source code.
      */
     public String getSourceName() {
         return sourceName;
     }
 
     /**
-     * Sets the URI, path or descriptive text indicating the origin
-     * of this script's source code.
+     * Sets the URI, path or descriptive text indicating the origin of this script's source code.
      */
     public void setSourceName(String sourceName) {
         this.sourceName = sourceName;
     }
 
     /**
-     * Returns the start offset of the encoded source.
-     * Only valid if {@link #getEncodedSource} returns non-{@code null}.
+     * Returns the start offset of the encoded source. Only valid if {@link #getEncodedSource}
+     * returns non-{@code null}.
      */
     public int getEncodedSourceStart() {
         return encodedSourceStart;
@@ -77,6 +74,7 @@
 
     /**
      * Used by code generator.
+     *
      * @see #getEncodedSource
      */
     public void setEncodedSourceStart(int start) {
@@ -84,8 +82,8 @@
     }
 
     /**
-     * Returns the end offset of the encoded source.
-     * Only valid if {@link #getEncodedSource} returns non-{@code null}.
+     * Returns the end offset of the encoded source. Only valid if {@link #getEncodedSource} returns
+     * non-{@code null}.
      */
     public int getEncodedSourceEnd() {
         return encodedSourceEnd;
@@ -93,6 +91,7 @@
 
     /**
      * Used by code generator.
+     *
      * @see #getEncodedSource
      */
     public void setEncodedSourceEnd(int end) {
@@ -101,6 +100,7 @@
 
     /**
      * Used by code generator.
+     *
      * @see #getEncodedSource
      */
     public void setEncodedSourceBounds(int start, int end) {
@@ -110,6 +110,7 @@
 
     /**
      * Used by the code generator.
+     *
      * @see #getEncodedSource
      */
     public void setEncodedSource(String encodedSource) {
@@ -117,17 +118,14 @@
     }
 
     /**
-     * Returns a canonical version of the source for this script or function,
-     * for use in implementing the {@code Object.toSource} method of
-     * JavaScript objects.  This source encoding is only recorded during code
-     * generation.  It must be passed back to
-     * {@link org.mozilla.javascript.Decompiler#decompile} to construct the
-     * human-readable source string.<p>
+     * Returns a canonical version of the source for this script or function, for use in
+     * implementing the {@code Object.toSource} method of JavaScript objects. This source encoding
+     * is only recorded during code generation. It must be passed back to {@link
+     * org.mozilla.javascript.Decompiler#decompile} to construct the human-readable source string.
      *
-     * Given a parsed AST, you can always convert it to source code using the
-     * {@link AstNode#toSource} method, although it's not guaranteed to produce
-     * exactly the same results as {@code Object.toSource} with respect to
-     * formatting, parenthesization and other details.
+     * <p>Given a parsed AST, you can always convert it to source code using the {@link
+     * AstNode#toSource} method, although it's not guaranteed to produce exactly the same results as
+     * {@code Object.toSource} with respect to formatting, parenthesization and other details.
      *
      * @return the encoded source, or {@code null} if it was not recorded.
      */
@@ -140,9 +138,8 @@
     }
 
     /**
-     * Sets base (starting) line number for this script or function.
-     * This is a one-time operation, and throws an exception if the
-     * line number has already been set.
+     * Sets base (starting) line number for this script or function. This is a one-time operation,
+     * and throws an exception if the line number has already been set.
      */
     public void setBaseLineno(int lineno) {
         if (lineno < 0 || this.lineno >= 0) codeBug();
@@ -172,14 +169,14 @@
     }
 
     /**
-     * Adds a {@link FunctionNode} to the functions table for codegen.
-     * Does not set the parent of the node.
+     * Adds a {@link FunctionNode} to the functions table for codegen. Does not set the parent of
+     * the node.
+     *
      * @return the index of the function within its parent
      */
     public int addFunction(FunctionNode fnNode) {
         if (fnNode == null) codeBug();
-        if (functions == null)
-            functions = new ArrayList<FunctionNode>();
+        if (functions == null) functions = new ArrayList<FunctionNode>();
         functions.add(fnNode);
         return functions.size() - 1;
     }
@@ -196,23 +193,37 @@
         return regexps.get(index).getFlags();
     }
 
-    /**
-     * Called by IRFactory to add a RegExp to the regexp table.
-     */
+    /** Called by IRFactory to add a RegExp to the regexp table. */
     public void addRegExp(RegExpLiteral re) {
         if (re == null) codeBug();
-        if (regexps == null)
-            regexps = new ArrayList<RegExpLiteral>();
+        if (regexps == null) regexps = new ArrayList<RegExpLiteral>();
         regexps.add(re);
         re.putIntProp(REGEXP_PROP, regexps.size() - 1);
     }
 
+    public int getTemplateLiteralCount() {
+        return templateLiterals == null ? 0 : templateLiterals.size();
+    }
+
+    public List<TemplateCharacters> getTemplateLiteralStrings(int index) {
+        return templateLiterals.get(index).getTemplateStrings();
+    }
+
+    /** Called by IRFactory to add a Template Literal to the templateLiterals table. */
+    public void addTemplateLiteral(TemplateLiteral templateLiteral) {
+        if (templateLiteral == null) codeBug();
+        if (templateLiterals == null) templateLiterals = new ArrayList<TemplateLiteral>();
+        templateLiterals.add(templateLiteral);
+        templateLiteral.putIntProp(TEMPLATE_LITERAL_PROP, templateLiterals.size() - 1);
+    }
+
     public int getIndexForNameNode(Node nameNode) {
         if (variableNames == null) codeBug();
         Scope node = nameNode.getScope();
-        Symbol symbol = node == null
-            ? null
-            : node.getSymbol(((Name)nameNode).getIdentifier());
+        Symbol symbol = null;
+        if (node != null && nameNode instanceof Name) {
+            symbol = node.getSymbol(((Name) nameNode).getIdentifier());
+        }
         return (symbol == null) ? -1 : symbol.getIndex();
     }
 
@@ -257,12 +268,11 @@
     }
 
     /**
-     * Assign every symbol a unique integer index. Generate arrays of variable
-     * names and constness that can be indexed by those indices.
+     * Assign every symbol a unique integer index. Generate arrays of variable names and constness
+     * that can be indexed by those indices.
      *
-     * @param flattenAllTables if true, flatten all symbol tables,
-     * included nested block scope symbol tables. If false, just flatten the
-     * script's or function's symbol table.
+     * @param flattenAllTables if true, flatten all symbol tables, included nested block scope
+     *     symbol tables. If false, just flatten the script's or function's symbol table.
      */
     public void flattenSymbolTable(boolean flattenAllTables) {
         if (!flattenAllTables) {
@@ -297,8 +307,7 @@
     public void setCompilerData(Object data) {
         assertNotNull(data);
         // Can only call once
-        if (compilerData != null)
-            throw new IllegalStateException();
+        if (compilerData != null) throw new IllegalStateException();
         compilerData = data;
     }
 
@@ -318,7 +327,7 @@
     public void visit(NodeVisitor v) {
         if (v.visit(this)) {
             for (Node kid : this) {
-                ((AstNode)kid).visit(v);
+                ((AstNode) kid).visit(v);
             }
         }
     }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/StringLiteral.java rhino-1.7.14/src/org/mozilla/javascript/ast/StringLiteral.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/StringLiteral.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/StringLiteral.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,12 +6,12 @@
 
 package org.mozilla.javascript.ast;
 
-import org.mozilla.javascript.Token;
 import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.Token;
 
 /**
  * AST node for a single- or double-quoted string literal.
- * Node type is {@link Token#STRING}.<p>
+ * Node type is {@link Token#STRING}.
  */
 public class StringLiteral extends AstNode {
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/SwitchCase.java rhino-1.7.14/src/org/mozilla/javascript/ast/SwitchCase.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/SwitchCase.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/SwitchCase.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,15 +6,15 @@
 
 package org.mozilla.javascript.ast;
 
-import org.mozilla.javascript.Token;
-
 import java.util.ArrayList;
 import java.util.List;
 
+import org.mozilla.javascript.Token;
+
 /**
  * Switch-case AST node type.  The switch case is always part of a
  * switch statement.
- * Node type is {@link Token#CASE}.<p>
+ * Node type is {@link Token#CASE}.
  *
  * <pre><i>CaseBlock</i> :
  *        { [CaseClauses] }
@@ -124,11 +124,18 @@
         } else {
             sb.append("case ");
             sb.append(expression.toSource(0));
-            sb.append(":\n");
+            sb.append(":");
+            if(this.getInlineComment() != null) {
+                sb.append(this.getInlineComment().toSource(depth + 1));
+            }
+            sb.append("\n");
         }
         if (statements != null) {
             for (AstNode s : statements) {
                 sb.append(s.toSource(depth+1));
+                if(s.getType() == Token.COMMENT && ((Comment)s).getCommentType() == Token.CommentType.LINE) {
+                    sb.append("\n");
+                }
             }
         }
         return sb.toString();
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/SwitchStatement.java rhino-1.7.14/src/org/mozilla/javascript/ast/SwitchStatement.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/SwitchStatement.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/SwitchStatement.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,15 +6,15 @@
 
 package org.mozilla.javascript.ast;
 
-import org.mozilla.javascript.Token;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
+import org.mozilla.javascript.Token;
+
 /**
  * Switch statement AST node type.
- * Node type is {@link Token#SWITCH}.<p>
+ * Node type is {@link Token#SWITCH}.
  *
  * <pre><i>SwitchStatement</i> :
  *        <b>switch</b> ( Expression ) CaseBlock
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/TaggedTemplateLiteral.java rhino-1.7.14/src/org/mozilla/javascript/ast/TaggedTemplateLiteral.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/TaggedTemplateLiteral.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/TaggedTemplateLiteral.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,66 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript.ast;
+
+import org.mozilla.javascript.Token;
+
+/**
+ * AST node for a Tagged Template Literal.
+ *
+ * <p>Node type is {@link Token#TAGGED_TEMPLATE_LITERAL}.
+ */
+public class TaggedTemplateLiteral extends AstNode {
+
+    private AstNode target;
+    private AstNode templateLiteral;
+
+    {
+        type = Token.TAGGED_TEMPLATE_LITERAL;
+    }
+
+    public TaggedTemplateLiteral() {}
+
+    public TaggedTemplateLiteral(int pos) {
+        super(pos);
+    }
+
+    public TaggedTemplateLiteral(int pos, int len) {
+        super(pos, len);
+    }
+
+    public AstNode getTarget() {
+        return target;
+    }
+
+    public void setTarget(AstNode target) {
+        this.target = target;
+    }
+
+    public AstNode getTemplateLiteral() {
+        return templateLiteral;
+    }
+
+    public void setTemplateLiteral(AstNode templateLiteral) {
+        this.templateLiteral = templateLiteral;
+    }
+
+    @Override
+    public String toSource(int depth) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(makeIndent(depth));
+        sb.append(target.toSource(0));
+        sb.append(templateLiteral.toSource(0));
+        return sb.toString();
+    }
+
+    /** Visits this node. */
+    @Override
+    public void visit(NodeVisitor v) {
+        if (v.visit(this)) {
+            target.visit(v);
+            templateLiteral.visit(v);
+        }
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/TemplateCharacters.java rhino-1.7.14/src/org/mozilla/javascript/ast/TemplateCharacters.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/TemplateCharacters.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/TemplateCharacters.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,82 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript.ast;
+
+import org.mozilla.javascript.Token;
+
+/**
+ * AST node for Template Literal Characters.
+ *
+ * <p>Node type is {@link Token#TEMPLATE_CHARS}.
+ */
+public class TemplateCharacters extends AstNode {
+
+    private String value;
+    private String rawValue;
+
+    {
+        type = Token.TEMPLATE_CHARS;
+    }
+
+    public TemplateCharacters() {}
+
+    public TemplateCharacters(int pos) {
+        super(pos);
+    }
+
+    public TemplateCharacters(int pos, int len) {
+        super(pos, len);
+    }
+
+    /**
+     * Returns the node's value: the parsed template-literal-value (QV)
+     *
+     * @return the node's value
+     */
+    public String getValue() {
+        return value;
+    }
+
+    /**
+     * Sets the node's value. Can be null in case of illegal escape sequences, which are allowed in
+     * Template Literals but will have an undefined cooked value
+     *
+     * @param value the node's value
+     */
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    /**
+     * Returns the node's raw-value: the parsed template-literal-raw-value (QRV)
+     *
+     * @return the node's raw-value
+     */
+    public String getRawValue() {
+        return rawValue;
+    }
+
+    /**
+     * Sets the node's raw-value.
+     *
+     * @param rawValue the node's raw-value
+     * @throws IllegalArgumentException} if rawValue is {@code null}
+     */
+    public void setRawValue(String rawValue) {
+        assertNotNull(rawValue);
+        this.rawValue = rawValue;
+    }
+
+    @Override
+    public String toSource(int depth) {
+        return new StringBuilder(makeIndent(depth)).append(rawValue).toString();
+    }
+
+    /** Visits this node. There are no children to visit. */
+    @Override
+    public void visit(NodeVisitor v) {
+        v.visit(this);
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/TemplateLiteral.java rhino-1.7.14/src/org/mozilla/javascript/ast/TemplateLiteral.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/TemplateLiteral.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/TemplateLiteral.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,140 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript.ast;
+
+import static java.util.Collections.emptyList;
+import static java.util.Collections.unmodifiableList;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.mozilla.javascript.Token;
+
+/**
+ * AST node for a Template literal.
+ *
+ * <p>Node type is {@link Token#TEMPLATE_LITERAL}.
+ */
+public class TemplateLiteral extends AstNode {
+
+    private List<AstNode> elements;
+
+    {
+        type = Token.TEMPLATE_LITERAL;
+    }
+
+    public TemplateLiteral() {}
+
+    public TemplateLiteral(int pos) {
+        super(pos);
+    }
+
+    public TemplateLiteral(int pos, int len) {
+        super(pos, len);
+    }
+
+    /** Returns a list of all literal sections of this template literal */
+    public List<TemplateCharacters> getTemplateStrings() {
+        if (elements == null) return emptyList();
+        List<TemplateCharacters> strings = new ArrayList<TemplateCharacters>();
+        for (AstNode e : elements) {
+            if (e.getType() == Token.TEMPLATE_CHARS) {
+                strings.add((TemplateCharacters) e);
+            }
+        }
+        return unmodifiableList(strings);
+    }
+
+    /** Returns a list of all substitutions of this template literal */
+    public List<AstNode> getSubstitutions() {
+        if (elements == null) return emptyList();
+        List<AstNode> subs = new ArrayList<AstNode>();
+        for (AstNode e : elements) {
+            if (e.getType() != Token.TEMPLATE_CHARS) {
+                subs.add(e);
+            }
+        }
+        return unmodifiableList(subs);
+    }
+
+    /**
+     * Returns the element list
+     *
+     * @return the element list. If there are no elements, returns an immutable empty list.
+     */
+    public List<AstNode> getElements() {
+        if (elements == null) return emptyList();
+        return elements;
+    }
+
+    /**
+     * Sets the element list, and sets each element's parent to this node.
+     *
+     * @param elements the element list. Can be {@code null}.
+     */
+    public void setElements(List<AstNode> elements) {
+        if (elements == null) {
+            this.elements = null;
+        } else {
+            if (this.elements != null) this.elements.clear();
+            for (AstNode e : elements) addElement(e);
+        }
+    }
+
+    /**
+     * Adds an element to the list, and sets its parent to this node.
+     *
+     * @param element the element to add
+     * @throws IllegalArgumentException if element is {@code null}.
+     */
+    public void addElement(AstNode element) {
+        assertNotNull(element);
+        if (elements == null) elements = new ArrayList<AstNode>();
+        elements.add(element);
+        element.setParent(this);
+    }
+
+    /** Returns the number of elements in this {@code TemplateLiteral} literal. */
+    public int getSize() {
+        return elements == null ? 0 : elements.size();
+    }
+
+    /**
+     * Returns element at specified index.
+     *
+     * @param index the index of the element to retrieve
+     * @return the element
+     * @throws IndexOutOfBoundsException if the index is invalid
+     */
+    public AstNode getElement(int index) {
+        if (elements == null) throw new IndexOutOfBoundsException("no elements");
+        return elements.get(index);
+    }
+
+    @Override
+    public String toSource(int depth) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(makeIndent(depth));
+        sb.append("`");
+        for (AstNode e : getElements()) {
+            if (e.getType() == Token.TEMPLATE_CHARS) {
+                sb.append(e.toSource(0));
+            } else {
+                sb.append("${").append(e.toSource(0)).append("}");
+            }
+        }
+        sb.append("`");
+        return sb.toString();
+    }
+
+    /** Visits this node. */
+    @Override
+    public void visit(NodeVisitor v) {
+        if (v.visit(this)) {
+            for (AstNode e : getElements()) {
+                e.visit(v);
+            }
+        }
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/ThrowStatement.java rhino-1.7.14/src/org/mozilla/javascript/ast/ThrowStatement.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/ThrowStatement.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/ThrowStatement.java	2022-01-06 22:57:21.000000000 +0100
@@ -9,7 +9,7 @@
 import org.mozilla.javascript.Token;
 
 /**
- * Throw statement.  Node type is {@link Token#THROW}.<p>
+ * Throw statement.  Node type is {@link Token#THROW}.
  *
  * <pre><i>ThrowStatement</i> :
  *      <b>throw</b> [<i>no LineTerminator here</i>] Expression ;</pre>
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/TryStatement.java rhino-1.7.14/src/org/mozilla/javascript/ast/TryStatement.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/TryStatement.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/TryStatement.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,14 +6,14 @@
 
 package org.mozilla.javascript.ast;
 
-import org.mozilla.javascript.Token;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
+import org.mozilla.javascript.Token;
+
 /**
- * Try/catch/finally statement.  Node type is {@link Token#TRY}.<p>
+ * Try/catch/finally statement.  Node type is {@link Token#TRY}.
  *
  * <pre><i>TryStatement</i> :
  *        <b>try</b> Block Catch
@@ -138,6 +138,9 @@
         StringBuilder sb = new StringBuilder(250);
         sb.append(makeIndent(depth));
         sb.append("try ");
+        if(this.getInlineComment() != null) {
+            sb.append(this.getInlineComment().toSource(depth + 1)).append("\n");
+        }
         sb.append(tryBlock.toSource(depth).trim());
         for (CatchClause cc : getCatchClauses()) {
             sb.append(cc.toSource(depth));
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/UnaryExpression.java rhino-1.7.14/src/org/mozilla/javascript/ast/UnaryExpression.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/UnaryExpression.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/UnaryExpression.java	2022-01-06 22:57:21.000000000 +0100
@@ -9,11 +9,9 @@
 import org.mozilla.javascript.Token;
 
 /**
- * AST node representing unary operators such as {@code ++},
- * {@code ~}, {@code typeof} and {@code delete}.  The type field
- * is set to the appropriate Token type for the operator.  The node length spans
- * from the operator to the end of the operand (for prefix operators) or from
- * the start of the operand to the operator (for postfix).<p>
+ * AST node representing unary operators such as {@code typeof} and {@code delete}.
+ * The type field is set to the appropriate Token type for the operator.
+ * The node length spans from the operator to the end of the operand.<p>
  *
  * The {@code default xml namespace = <expr>} statement in E4X
  * (JavaScript 1.6) is represented as a {@code UnaryExpression} of node
@@ -23,7 +21,6 @@
 public class UnaryExpression extends AstNode {
 
     private AstNode operand;
-    private boolean isPostfix;
 
     public UnaryExpression() {
     }
@@ -33,42 +30,29 @@
     }
 
     /**
-     * Constructs a new postfix UnaryExpression
+     * Constructs a new UnaryExpression
      */
     public UnaryExpression(int pos, int len) {
         super(pos, len);
     }
 
     /**
-     * Constructs a new prefix UnaryExpression.
-     */
-    public UnaryExpression(int operator, int operatorPosition,
-                           AstNode operand) {
-        this(operator, operatorPosition, operand, false);
-    }
-
-    /**
      * Constructs a new UnaryExpression with the specified operator
      * and operand.  It sets the parent of the operand, and sets its own bounds
      * to encompass the operator and operand.
      * @param operator the node type
      * @param operatorPosition the absolute position of the operator.
      * @param operand the operand expression
-     * @param postFix true if the operator follows the operand.  Int
      * @throws IllegalArgumentException} if {@code operand} is {@code null}
      */
-    public UnaryExpression(int operator, int operatorPosition,
-                           AstNode operand, boolean postFix) {
+    public UnaryExpression(int operator, int operatorPosition, AstNode operand) {
         assertNotNull(operand);
-        int beg = postFix ? operand.getPosition() : operatorPosition;
+        int beg = operand.getPosition();
         // JavaScript only has ++ and -- postfix operators, so length is 2
-        int end = postFix
-                  ? operatorPosition + 2
-                  : operand.getPosition() + operand.getLength();
+        int end = operand.getPosition() + operand.getLength();
         setBounds(beg, end);
         setOperator(operator);
         setOperand(operand);
-        isPostfix = postFix;
     }
 
     /**
@@ -104,42 +88,17 @@
         operand.setParent(this);
     }
 
-    /**
-     * Returns whether the operator is postfix
-     */
-    public boolean isPostfix() {
-        return isPostfix;
-    }
-
-    /**
-     * Returns whether the operator is prefix
-     */
-    public boolean isPrefix() {
-        return !isPostfix;
-    }
-
-    /**
-     * Sets whether the operator is postfix
-     */
-    public void setIsPostfix(boolean isPostfix) {
-        this.isPostfix = isPostfix;
-    }
-
     @Override
     public String toSource(int depth) {
         StringBuilder sb = new StringBuilder();
         sb.append(makeIndent(depth));
         int type = getType();
-        if (!isPostfix) {
-            sb.append(operatorToString(type));
-            if (type == Token.TYPEOF || type == Token.DELPROP || type == Token.VOID) {
-                sb.append(" ");
-            }
+        sb.append(operatorToString(type));
+        if (type == Token.TYPEOF || type == Token.DELPROP || type == Token.VOID) {
+            sb.append(" ");
         }
         sb.append(operand.toSource());
-        if (isPostfix) {
-            sb.append(operatorToString(type));
-        }
+
         return sb.toString();
     }
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/UpdateExpression.java rhino-1.7.14/src/org/mozilla/javascript/ast/UpdateExpression.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/UpdateExpression.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/UpdateExpression.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,128 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript.ast;
+
+import org.mozilla.javascript.Token;
+
+/**
+ * AST node representing update operators such as {@code ++}. The type field is set to the
+ * appropriate Token type for the operator. The node length spans from the operator to the end of
+ * the operand (for prefix operators) or from the start of the operand to the operator (for
+ * postfix).
+ */
+public class UpdateExpression extends AstNode {
+
+    private AstNode operand;
+    private boolean isPostfix;
+
+    public UpdateExpression() {}
+
+    public UpdateExpression(int pos) {
+        super(pos);
+    }
+
+    /** Constructs a new postfix UpdateExpression. */
+    public UpdateExpression(int pos, int len) {
+        super(pos, len);
+    }
+
+    /** Constructs a new prefix UpdateExpression. */
+    public UpdateExpression(int operator, int operatorPosition, AstNode operand) {
+        this(operator, operatorPosition, operand, false);
+    }
+
+    /**
+     * Constructs a new UpdateExpression with the specified operator and operand. It sets the parent
+     * of the operand, and sets its own bounds to encompass the operator and operand.
+     *
+     * @param operator the node type
+     * @param operatorPosition the absolute position of the operator.
+     * @param operand the operand expression
+     * @param postFix true if the operator follows the operand. Int
+     * @throws IllegalArgumentException} if {@code operand} is {@code null}
+     */
+    public UpdateExpression(int operator, int operatorPosition, AstNode operand, boolean postFix) {
+        assertNotNull(operand);
+        int beg = postFix ? operand.getPosition() : operatorPosition;
+        // JavaScript only has ++ and -- postfix operators, so length is 2
+        int end = postFix ? operatorPosition + 2 : operand.getPosition() + operand.getLength();
+        setBounds(beg, end);
+        setOperator(operator);
+        setOperand(operand);
+        isPostfix = postFix;
+    }
+
+    /** Returns operator token – alias for {@link #getType} */
+    public int getOperator() {
+        return type;
+    }
+
+    /**
+     * Sets operator – same as {@link #setType}, but throws an exception if the operator is
+     * invalid
+     *
+     * @throws IllegalArgumentException if operator is not a valid Token code
+     */
+    public void setOperator(int operator) {
+        if (!Token.isValidToken(operator))
+            throw new IllegalArgumentException("Invalid token: " + operator);
+        setType(operator);
+    }
+
+    public AstNode getOperand() {
+        return operand;
+    }
+
+    /**
+     * Sets the operand, and sets its parent to be this node.
+     *
+     * @throws IllegalArgumentException} if {@code operand} is {@code null}
+     */
+    public void setOperand(AstNode operand) {
+        assertNotNull(operand);
+        this.operand = operand;
+        operand.setParent(this);
+    }
+
+    /** Returns whether the operator is postfix */
+    public boolean isPostfix() {
+        return isPostfix;
+    }
+
+    /** Returns whether the operator is prefix */
+    public boolean isPrefix() {
+        return !isPostfix;
+    }
+
+    /** Sets whether the operator is postfix */
+    public void setIsPostfix(boolean isPostfix) {
+        this.isPostfix = isPostfix;
+    }
+
+    @Override
+    public String toSource(int depth) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(makeIndent(depth));
+        int type = getType();
+        if (!isPostfix) {
+            sb.append(operatorToString(type));
+        }
+        sb.append(operand.toSource());
+        if (isPostfix) {
+            sb.append(operatorToString(type));
+        }
+        return sb.toString();
+    }
+
+    /** Visits this node, then the operand. */
+    @Override
+    public void visit(NodeVisitor v) {
+        if (v.visit(this)) {
+            operand.visit(v);
+        }
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/VariableDeclaration.java rhino-1.7.14/src/org/mozilla/javascript/ast/VariableDeclaration.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/VariableDeclaration.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/VariableDeclaration.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,11 +6,11 @@
 
 package org.mozilla.javascript.ast;
 
-import org.mozilla.javascript.Token;
-
 import java.util.ArrayList;
 import java.util.List;
 
+import org.mozilla.javascript.Token;
+
 /**
  * A list of one or more var, const or let declarations.
  * Node type is {@link Token#VAR}, {@link Token#CONST} or
@@ -136,7 +136,12 @@
         sb.append(" ");
         printList(variables, sb);
         if (isStatement()) {
-            sb.append(";\n");
+            sb.append(";");
+        }
+        if(this.getInlineComment() != null) {
+            sb.append(this.getInlineComment().toSource(depth)).append("\n");
+        } else if (isStatement()) {
+            sb.append("\n");
         }
         return sb.toString();
     }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/VariableInitializer.java rhino-1.7.14/src/org/mozilla/javascript/ast/VariableInitializer.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/VariableInitializer.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/VariableInitializer.java	2022-01-06 22:57:21.000000000 +0100
@@ -11,10 +11,10 @@
 /**
  * A variable declaration or initializer, part of a {@link VariableDeclaration}
  * expression.  The variable "target" can be a simple name or a destructuring
- * form.  The initializer, if present, can be any expression.<p>
+ * form.  The initializer, if present, can be any expression.<br>
  *
  * Node type is one of {@link Token#VAR}, {@link Token#CONST}, or
- * {@link Token#LET}.<p>
+ * {@link Token#LET}.
  */
 public class VariableInitializer extends AstNode {
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/WhileLoop.java rhino-1.7.14/src/org/mozilla/javascript/ast/WhileLoop.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/WhileLoop.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/WhileLoop.java	2022-01-06 22:57:21.000000000 +0100
@@ -9,7 +9,7 @@
 import org.mozilla.javascript.Token;
 
 /**
- * While statement.  Node type is {@link Token#WHILE}.<p>
+ * While statement.  Node type is {@link Token#WHILE}.
  *
  * <pre><i>WhileStatement</i>:
  *     <b>while</b> <b>(</b> Expression <b>)</b> Statement</pre>
@@ -57,11 +57,17 @@
         sb.append("while (");
         sb.append(condition.toSource(0));
         sb.append(") ");
+        if(this.getInlineComment() != null) {
+            sb.append(this.getInlineComment().toSource(depth + 1)).append("\n");
+        }
         if (body.getType() == Token.BLOCK) {
             sb.append(body.toSource(depth).trim());
             sb.append("\n");
         } else {
-            sb.append("\n").append(body.toSource(depth+1));
+            if(this.getInlineComment() == null) {
+                sb.append("\n");
+            }
+            sb.append(body.toSource(depth+1));
         }
         return sb.toString();
     }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/WithStatement.java rhino-1.7.14/src/org/mozilla/javascript/ast/WithStatement.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/WithStatement.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/WithStatement.java	2022-01-06 22:57:21.000000000 +0100
@@ -9,7 +9,7 @@
 import org.mozilla.javascript.Token;
 
 /**
- * With statement.  Node type is {@link Token#WITH}.<p>
+ * With statement.  Node type is {@link Token#WITH}.
  *
  * <pre><i>WithStatement</i> :
  *      <b>with</b> ( Expression ) Statement ;</pre>
@@ -113,7 +113,13 @@
         sb.append("with (");
         sb.append(expression.toSource(0));
         sb.append(") ");
+        if(this.getInlineComment() != null) {
+            sb.append(this.getInlineComment().toSource(depth + 1));
+        }
         if (statement.getType() == Token.BLOCK) {
+            if(this.getInlineComment() != null) {
+                sb.append("\n");
+            }
             sb.append(statement.toSource(depth).trim());
             sb.append("\n");
         } else {
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/XmlElemRef.java rhino-1.7.14/src/org/mozilla/javascript/ast/XmlElemRef.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/XmlElemRef.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/XmlElemRef.java	2022-01-06 22:57:21.000000000 +0100
@@ -12,8 +12,7 @@
  * AST node for an E4X XML {@code [expr]} member-ref expression.
  * The node type is {@link Token#REF_MEMBER}.<p>
  *
- * Syntax:<p>
- *
+ * Syntax:
  * <pre> @<i><sub>opt</sub></i> ns:: <i><sub>opt</sub></i> [ expr ]</pre>
  *
  * Examples include {@code ns::[expr]}, {@code @ns::[expr]}, {@code @[expr]},
@@ -29,7 +28,7 @@
  * The node starts at the {@code @} token, if present.  Otherwise it starts
  * at the namespace name.  The node bounds extend through the closing
  * right-bracket, or if it is missing due to a syntax error, through the
- * end of the index expression.<p>
+ * end of the index expression.
  */
 public class XmlElemRef extends XmlRef {
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/XmlFragment.java rhino-1.7.14/src/org/mozilla/javascript/ast/XmlFragment.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/XmlFragment.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/XmlFragment.java	2022-01-06 22:57:21.000000000 +0100
@@ -10,7 +10,7 @@
 
 /**
  * Abstract base type for components that comprise an {@link XmlLiteral}
- * object. Node type is {@link Token#XML}.<p>
+ * object. Node type is {@link Token#XML}.
  */
 public abstract class XmlFragment extends AstNode {
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/XmlLiteral.java rhino-1.7.14/src/org/mozilla/javascript/ast/XmlLiteral.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/XmlLiteral.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/XmlLiteral.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,17 +6,17 @@
 
 package org.mozilla.javascript.ast;
 
-import org.mozilla.javascript.Token;
-
 import java.util.ArrayList;
 import java.util.List;
 
+import org.mozilla.javascript.Token;
+
 /**
  * AST node for an E4X (Ecma-357) embedded XML literal.  Node type is
  * {@link Token#XML}.  The parser generates a simple list of strings and
  * expressions.  In the future we may parse the XML and produce a richer set of
  * nodes, but for now it's just a set of expressions evaluated to produce a
- * string to pass to the {@code XML} constructor function.<p>
+ * string to pass to the {@code XML} constructor function.
  */
 public class XmlLiteral extends AstNode {
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/XmlMemberGet.java rhino-1.7.14/src/org/mozilla/javascript/ast/XmlMemberGet.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/XmlMemberGet.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/XmlMemberGet.java	2022-01-06 22:57:21.000000000 +0100
@@ -87,8 +87,24 @@
         StringBuilder sb = new StringBuilder();
         sb.append(makeIndent(depth));
         sb.append(getLeft().toSource(0));
-        sb.append(operatorToString(getType()));
+        sb.append(dotsToString());
         sb.append(getRight().toSource(0));
         return sb.toString();
     }
+
+    /**
+     * Gives string representation of inner dots token.
+     * @return  String representation of inner dots token (e.g. '.' or '..')
+     * @throws IllegalArgumentException on unexpected token type
+     */
+    private String dotsToString() {
+        switch (getType()) {
+            case Token.DOT:
+                return ".";
+            case Token.DOTDOT:
+                return "..";
+            default:
+                throw new IllegalArgumentException("Invalid type of XmlMemberGet: " + getType());
+        }
+    }
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/XmlPropRef.java rhino-1.7.14/src/org/mozilla/javascript/ast/XmlPropRef.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/XmlPropRef.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/XmlPropRef.java	2022-01-06 22:57:21.000000000 +0100
@@ -10,9 +10,9 @@
 
 /**
  * AST node for an E4X XML {@code [expr]} property-ref expression.
- * The node type is {@link Token#REF_NAME}.<p>
+ * The node type is {@link Token#REF_NAME}.<br>
  *
- * Syntax:<p>
+ * Syntax:
  *
  * <pre> @<i><sub>opt</sub></i> ns:: <i><sub>opt</sub></i> name</pre>
  *
@@ -23,7 +23,7 @@
  * The node starts at the {@code @} token, if present.  Otherwise it starts
  * at the namespace name.  The node bounds extend through the closing
  * right-bracket, or if it is missing due to a syntax error, through the
- * end of the index expression.<p>
+ * end of the index expression.
  */
 public class XmlPropRef extends XmlRef {
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/XmlRef.java rhino-1.7.14/src/org/mozilla/javascript/ast/XmlRef.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/XmlRef.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/XmlRef.java	2022-01-06 22:57:21.000000000 +0100
@@ -9,10 +9,9 @@
 /**
  * Base class for E4X XML attribute-access or property-get expressions.
  * Such expressions can take a variety of forms. The general syntax has
- * three parts:<p>
- *
+ * three parts:
  * <ol>
- *  <li>optional: an {@code @}</li>  (specifying an attribute access)</li>
+ *  <li>optional: an {@code @} (specifying an attribute access)</li>
  *  <li>optional: a namespace (a {@code Name}) and double-colon</li>
  *  <li>required:  either a {@code Name} or a bracketed [expression]</li>
  * </ol>
@@ -29,7 +28,7 @@
  * is valid in certain expression contexts such as
  * {@code company..employee.(@id < 100)} - in this case, the {@code @id}
  * is an {@code XmlRef} that is part of an infix '<' expression
- * whose parent is an {@code XmlDotQuery} node.<p>
+ * whose parent is an {@code XmlDotQuery} node.
  */
 public abstract class XmlRef extends AstNode {
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ast/Yield.java rhino-1.7.14/src/org/mozilla/javascript/ast/Yield.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ast/Yield.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ast/Yield.java	2022-01-06 22:57:21.000000000 +0100
@@ -10,7 +10,7 @@
 
 /**
  * AST node for JavaScript 1.7 {@code yield} expression or statement.
- * Node type is {@link Token#YIELD}.<p>
+ * Node type is {@link Token#YIELD}.
  *
  * <pre><i>Yield</i> :
  *   <b>yield</b> [<i>no LineTerminator here</i>] [non-paren Expression] ;</pre>
@@ -19,23 +19,23 @@
 
     private AstNode value;
 
-    {
-        type = Token.YIELD;
-    }
-
     public Yield() {
+        type = Token.YIELD;
     }
 
     public Yield(int pos) {
         super(pos);
+        type = Token.YIELD;
     }
 
     public Yield(int pos, int len) {
         super(pos, len);
+        type = Token.YIELD;
     }
 
-    public Yield(int pos, int len, AstNode value) {
+    public Yield(int pos, int len, AstNode value, boolean isStar) {
         super(pos, len);
+        type = isStar ? Token.YIELD_STAR : Token.YIELD;
         setValue(value);
     }
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/BaseFunction.java rhino-1.7.14/src/org/mozilla/javascript/BaseFunction.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/BaseFunction.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/BaseFunction.java	2022-01-06 22:57:21.000000000 +0100
@@ -7,171 +7,174 @@
 package org.mozilla.javascript;
 
 /**
- * The base class for Function objects
- * See ECMA 15.3.
+ * The base class for Function objects. That is one of two purposes. It is also the prototype for
+ * every "function" defined except those that are used as GeneratorFunctions via the ES6 "function
+ * *" syntax.
+ *
+ * <p>See ECMA 15.3.
+ *
  * @author Norris Boyd
  */
-public class BaseFunction extends IdScriptableObject implements Function
-{
-
-    static final long serialVersionUID = 5311394446546053859L;
+public class BaseFunction extends IdScriptableObject implements Function {
+    private static final long serialVersionUID = 5311394446546053859L;
 
     private static final Object FUNCTION_TAG = "Function";
+    private static final String FUNCTION_CLASS = "Function";
+    static final String GENERATOR_FUNCTION_CLASS = "__GeneratorFunction";
 
-    static void init(Scriptable scope, boolean sealed)
-    {
+    static void init(Scriptable scope, boolean sealed) {
         BaseFunction obj = new BaseFunction();
         // Function.prototype attributes: see ECMA 15.3.3.1
         obj.prototypePropertyAttributes = DONTENUM | READONLY | PERMANENT;
         obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
     }
 
-    public BaseFunction()
-    {
+    static Object initAsGeneratorFunction(Scriptable scope, boolean sealed) {
+        BaseFunction obj = new BaseFunction(true);
+        // Function.prototype attributes: see ECMA 15.3.3.1
+        obj.prototypePropertyAttributes = READONLY | PERMANENT;
+        obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
+        // The "GeneratorFunction" name actually never appears in the global scope.
+        // Return it here so it can be cached as a "builtin"
+        return ScriptableObject.getProperty(scope, GENERATOR_FUNCTION_CLASS);
+    }
+
+    public BaseFunction() {}
+
+    public BaseFunction(boolean isGenerator) {
+        this.isGeneratorFunction = isGenerator;
     }
 
-    public BaseFunction(Scriptable scope, Scriptable prototype)
-    {
+    public BaseFunction(Scriptable scope, Scriptable prototype) {
         super(scope, prototype);
     }
 
     @Override
     public String getClassName() {
-        return "Function";
+        return isGeneratorFunction() ? GENERATOR_FUNCTION_CLASS : FUNCTION_CLASS;
+    }
+
+    // Generated code will override this
+    protected boolean isGeneratorFunction() {
+        return isGeneratorFunction;
     }
 
     /**
      * Gets the value returned by calling the typeof operator on this object.
+     *
      * @see org.mozilla.javascript.ScriptableObject#getTypeOf()
-     * @return "function" or "undefined" if {@link #avoidObjectDetection()} returns <code>true</code>
+     * @return "function" or "undefined" if {@link #avoidObjectDetection()} returns <code>true
+     *     </code>
      */
     @Override
-    public String getTypeOf()
-    {
+    public String getTypeOf() {
         return avoidObjectDetection() ? "undefined" : "function";
     }
 
     /**
      * Implements the instanceof operator for JavaScript Function objects.
-     * <p>
-     * <code>
+     *
+     * <p><code>
      * foo = new Foo();<br>
      * foo instanceof Foo;  // true<br>
      * </code>
      *
-     * @param instance The value that appeared on the LHS of the instanceof
-     *              operator
-     * @return true if the "prototype" property of "this" appears in
-     *              value's prototype chain
-     *
+     * @param instance The value that appeared on the LHS of the instanceof operator
+     * @return true if the "prototype" property of "this" appears in value's prototype chain
      */
     @Override
-    public boolean hasInstance(Scriptable instance)
-    {
+    public boolean hasInstance(Scriptable instance) {
         Object protoProp = ScriptableObject.getProperty(this, "prototype");
         if (protoProp instanceof Scriptable) {
-            return ScriptRuntime.jsDelegatesTo(instance, (Scriptable)protoProp);
+            return ScriptRuntime.jsDelegatesTo(instance, (Scriptable) protoProp);
         }
-        throw ScriptRuntime.typeError1("msg.instanceof.bad.prototype",
-                                       getFunctionName());
+        throw ScriptRuntime.typeErrorById("msg.instanceof.bad.prototype", getFunctionName());
     }
 
-// #string_id_map#
-
-    private static final int
-        Id_length       = 1,
-        Id_arity        = 2,
-        Id_name         = 3,
-        Id_prototype    = 4,
-        Id_arguments    = 5,
-
-        MAX_INSTANCE_ID = 5;
+    private static final int Id_length = 1,
+            Id_arity = 2,
+            Id_name = 3,
+            Id_prototype = 4,
+            Id_arguments = 5,
+            MAX_INSTANCE_ID = 5;
 
     @Override
-    protected int getMaxInstanceId()
-    {
+    protected int getMaxInstanceId() {
         return MAX_INSTANCE_ID;
     }
 
     @Override
-    protected int findInstanceIdInfo(String s)
-    {
-        int id;
-// #generated# Last update: 2007-05-09 08:15:15 EDT
-        L0: { id = 0; String X = null; int c;
-            L: switch (s.length()) {
-            case 4: X="name";id=Id_name; break L;
-            case 5: X="arity";id=Id_arity; break L;
-            case 6: X="length";id=Id_length; break L;
-            case 9: c=s.charAt(0);
-                if (c=='a') { X="arguments";id=Id_arguments; }
-                else if (c=='p') { X="prototype";id=Id_prototype; }
-                break L;
-            }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-            break L0;
+    protected int findInstanceIdInfo(String s) {
+        switch (s) {
+            case "length":
+                if (lengthPropertyAttributes >= 0) {
+                    return instanceIdInfo(lengthPropertyAttributes, Id_length);
+                }
+                break;
+            case "arity":
+                if (arityPropertyAttributes >= 0) {
+                    return instanceIdInfo(arityPropertyAttributes, Id_arity);
+                }
+                break;
+            case "name":
+                if (namePropertyAttributes >= 0) {
+                    return instanceIdInfo(namePropertyAttributes, Id_name);
+                }
+                break;
+            case "prototype":
+                if (hasPrototypeProperty()) {
+                    return instanceIdInfo(prototypePropertyAttributes, Id_prototype);
+                }
+                break;
+            case "arguments":
+                return instanceIdInfo(argumentsAttributes, Id_arguments);
+            default:
+                break;
         }
-// #/generated#
-// #/string_id_map#
 
-        if (id == 0) return super.findInstanceIdInfo(s);
-
-        int attr;
-        switch (id) {
-          case Id_length:
-          case Id_arity:
-          case Id_name:
-            attr = DONTENUM | READONLY | PERMANENT;
-            break;
-          case Id_prototype:
-            // some functions such as built-ins don't have a prototype property
-            if (!hasPrototypeProperty()) {
-                return 0;
-            }
-            attr = prototypePropertyAttributes;
-            break;
-          case Id_arguments:
-            attr = argumentsAttributes;
-            break;
-          default: throw new IllegalStateException();
-        }
-        return instanceIdInfo(attr, id);
+        return super.findInstanceIdInfo(s);
     }
 
     @Override
-    protected String getInstanceIdName(int id)
-    {
+    protected String getInstanceIdName(int id) {
         switch (id) {
-            case Id_length:       return "length";
-            case Id_arity:        return "arity";
-            case Id_name:         return "name";
-            case Id_prototype:    return "prototype";
-            case Id_arguments:    return "arguments";
+            case Id_length:
+                return "length";
+            case Id_arity:
+                return "arity";
+            case Id_name:
+                return "name";
+            case Id_prototype:
+                return "prototype";
+            case Id_arguments:
+                return "arguments";
         }
         return super.getInstanceIdName(id);
     }
 
     @Override
-    protected Object getInstanceIdValue(int id)
-    {
+    protected Object getInstanceIdValue(int id) {
         switch (id) {
-          case Id_length:    return ScriptRuntime.wrapInt(getLength());
-          case Id_arity:     return ScriptRuntime.wrapInt(getArity());
-          case Id_name:      return getFunctionName();
-          case Id_prototype: return getPrototypeProperty();
-          case Id_arguments: return getArguments();
+            case Id_length:
+                return lengthPropertyAttributes >= 0 ? getLength() : NOT_FOUND;
+            case Id_arity:
+                return arityPropertyAttributes >= 0 ? getArity() : NOT_FOUND;
+            case Id_name:
+                return namePropertyAttributes >= 0 ? getFunctionName() : NOT_FOUND;
+            case Id_prototype:
+                return getPrototypeProperty();
+            case Id_arguments:
+                return getArguments();
         }
         return super.getInstanceIdValue(id);
     }
 
     @Override
-    protected void setInstanceIdValue(int id, Object value)
-    {
+    protected void setInstanceIdValue(int id, Object value) {
         switch (id) {
             case Id_prototype:
                 if ((prototypePropertyAttributes & READONLY) == 0) {
-                    prototypeProperty = (value != null)
-                                        ? value : UniqueTag.NULL_VALUE;
+                    prototypeProperty = (value != null) ? value : UniqueTag.NULL_VALUE;
                 }
                 return;
             case Id_arguments:
@@ -186,16 +189,26 @@
                 }
                 return;
             case Id_name:
+                if (value == NOT_FOUND) {
+                    namePropertyAttributes = -1;
+                }
+                return;
             case Id_arity:
+                if (value == NOT_FOUND) {
+                    arityPropertyAttributes = -1;
+                }
+                return;
             case Id_length:
+                if (value == NOT_FOUND) {
+                    lengthPropertyAttributes = -1;
+                }
                 return;
         }
         super.setInstanceIdValue(id, value);
     }
 
     @Override
-    protected void setInstanceIdAttributes(int id, int attr)
-    {
+    protected void setInstanceIdAttributes(int id, int attr) {
         switch (id) {
             case Id_prototype:
                 prototypePropertyAttributes = attr;
@@ -203,13 +216,21 @@
             case Id_arguments:
                 argumentsAttributes = attr;
                 return;
+            case Id_arity:
+                arityPropertyAttributes = attr;
+                return;
+            case Id_name:
+                namePropertyAttributes = attr;
+                return;
+            case Id_length:
+                lengthPropertyAttributes = attr;
+                return;
         }
         super.setInstanceIdAttributes(id, attr);
     }
 
     @Override
-    protected void fillConstructorProperties(IdFunctionObject ctor)
-    {
+    protected void fillConstructorProperties(IdFunctionObject ctor) {
         // Fix up bootstrapping problem: getPrototype of the IdFunctionObject
         // can not return Function.prototype because Function object is not
         // yet defined.
@@ -218,18 +239,36 @@
     }
 
     @Override
-    protected void initPrototypeId(int id)
-    {
+    protected void initPrototypeId(int id) {
         String s;
         int arity;
         switch (id) {
-          case Id_constructor: arity=1; s="constructor"; break;
-          case Id_toString:    arity=0; s="toString";    break;
-          case Id_toSource:    arity=1; s="toSource";    break;
-          case Id_apply:       arity=2; s="apply";       break;
-          case Id_call:        arity=1; s="call";        break;
-          case Id_bind:        arity=1; s="bind";        break;
-          default: throw new IllegalArgumentException(String.valueOf(id));
+            case Id_constructor:
+                arity = 1;
+                s = "constructor";
+                break;
+            case Id_toString:
+                arity = 0;
+                s = "toString";
+                break;
+            case Id_toSource:
+                arity = 1;
+                s = "toSource";
+                break;
+            case Id_apply:
+                arity = 2;
+                s = "apply";
+                break;
+            case Id_call:
+                arity = 1;
+                s = "call";
+                break;
+            case Id_bind:
+                arity = 1;
+                s = "bind";
+                break;
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
         }
         initPrototypeMethod(FUNCTION_TAG, id, s, arity);
     }
@@ -239,8 +278,8 @@
     }
 
     static boolean isApplyOrCall(IdFunctionObject f) {
-        if(f.hasTag(FUNCTION_TAG)) {
-            switch(f.methodId()) {
+        if (f.hasTag(FUNCTION_TAG)) {
+            switch (f.methodId()) {
                 case Id_apply:
                 case Id_call:
                     return true;
@@ -250,83 +289,77 @@
     }
 
     @Override
-    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
-                             Scriptable thisObj, Object[] args)
-    {
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         if (!f.hasTag(FUNCTION_TAG)) {
             return super.execIdCall(f, cx, scope, thisObj, args);
         }
         int id = f.methodId();
         switch (id) {
-          case Id_constructor:
-            return jsConstructor(cx, scope, args);
+            case Id_constructor:
+                return jsConstructor(cx, scope, args);
 
-          case Id_toString: {
-            BaseFunction realf = realFunction(thisObj, f);
-            int indent = ScriptRuntime.toInt32(args, 0);
-            return realf.decompile(indent, 0);
-          }
-
-          case Id_toSource: {
-            BaseFunction realf = realFunction(thisObj, f);
-            int indent = 0;
-            int flags = Decompiler.TO_SOURCE_FLAG;
-            if (args.length != 0) {
-                indent = ScriptRuntime.toInt32(args[0]);
-                if (indent >= 0) {
-                    flags = 0;
-                } else {
-                    indent = 0;
+            case Id_toString:
+                {
+                    BaseFunction realf = realFunction(thisObj, f);
+                    int indent = ScriptRuntime.toInt32(args, 0);
+                    return realf.decompile(indent, 0);
                 }
-            }
-            return realf.decompile(indent, flags);
-          }
 
-          case Id_apply:
-          case Id_call:
-            return ScriptRuntime.applyOrCall(id == Id_apply,
-                                             cx, scope, thisObj, args);
-
-          case Id_bind:
-            if ( !(thisObj instanceof Callable) ) {
-              throw ScriptRuntime.notFunctionError(thisObj);
-            }
-            Callable targetFunction = (Callable) thisObj;
-            int argc = args.length;
-            final Scriptable boundThis;
-            final Object[] boundArgs;
-            if (argc > 0) {
-              boundThis = ScriptRuntime.toObjectOrNull(cx, args[0], scope);
-              boundArgs = new Object[argc-1];
-              System.arraycopy(args, 1, boundArgs, 0, argc-1);
-            } else {
-              boundThis = null;
-              boundArgs = ScriptRuntime.emptyArgs;
-            }
-            return new BoundFunction(cx, scope, targetFunction, boundThis, boundArgs);
+            case Id_toSource:
+                {
+                    BaseFunction realf = realFunction(thisObj, f);
+                    int indent = 0;
+                    int flags = Decompiler.TO_SOURCE_FLAG;
+                    if (args.length != 0) {
+                        indent = ScriptRuntime.toInt32(args[0]);
+                        if (indent >= 0) {
+                            flags = 0;
+                        } else {
+                            indent = 0;
+                        }
+                    }
+                    return realf.decompile(indent, flags);
+                }
+
+            case Id_apply:
+            case Id_call:
+                return ScriptRuntime.applyOrCall(id == Id_apply, cx, scope, thisObj, args);
+
+            case Id_bind:
+                if (!(thisObj instanceof Callable)) {
+                    throw ScriptRuntime.notFunctionError(thisObj);
+                }
+                Callable targetFunction = (Callable) thisObj;
+                int argc = args.length;
+                final Scriptable boundThis;
+                final Object[] boundArgs;
+                if (argc > 0) {
+                    boundThis = ScriptRuntime.toObjectOrNull(cx, args[0], scope);
+                    boundArgs = new Object[argc - 1];
+                    System.arraycopy(args, 1, boundArgs, 0, argc - 1);
+                } else {
+                    boundThis = null;
+                    boundArgs = ScriptRuntime.emptyArgs;
+                }
+                return new BoundFunction(cx, scope, targetFunction, boundThis, boundArgs);
         }
         throw new IllegalArgumentException(String.valueOf(id));
     }
 
-    private BaseFunction realFunction(Scriptable thisObj, IdFunctionObject f)
-    {
+    private static BaseFunction realFunction(Scriptable thisObj, IdFunctionObject f) {
+        if (thisObj == null) {
+            throw ScriptRuntime.notFunctionError(null);
+        }
         Object x = thisObj.getDefaultValue(ScriptRuntime.FunctionClass);
         if (x instanceof Delegator) {
-            x = ((Delegator)x).getDelegee();
+            x = ((Delegator) x).getDelegee();
         }
-        if (x instanceof BaseFunction) {
-            return (BaseFunction)x;
-        }
-        throw ScriptRuntime.typeError1("msg.incompat.call",
-                                       f.getFunctionName());
+        return ensureType(x, BaseFunction.class, f);
     }
 
-    /**
-     * Make value as DontEnum, DontDelete, ReadOnly
-     * prototype property of this Function object
-     */
-    public void setImmunePrototypeProperty(Object value)
-    {
+    /** Make value as DontEnum, DontDelete, ReadOnly prototype property of this Function object */
+    public void setImmunePrototypeProperty(Object value) {
         if ((prototypePropertyAttributes & READONLY) != 0) {
             throw new IllegalStateException();
         }
@@ -334,8 +367,7 @@
         prototypePropertyAttributes = DONTENUM | PERMANENT | READONLY;
     }
 
-    protected Scriptable getClassPrototype()
-    {
+    protected Scriptable getClassPrototype() {
         Object protoVal = getPrototypeProperty();
         if (protoVal instanceof Scriptable) {
             return (Scriptable) protoVal;
@@ -343,22 +375,19 @@
         return ScriptableObject.getObjectPrototype(this);
     }
 
-    /**
-     * Should be overridden.
-     */
-    public Object call(Context cx, Scriptable scope, Scriptable thisObj,
-                       Object[] args)
-    {
+    /** Should be overridden. */
+    @Override
+    public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         return Undefined.instance;
     }
 
-    public Scriptable construct(Context cx, Scriptable scope, Object[] args)
-    {
+    @Override
+    public Scriptable construct(Context cx, Scriptable scope, Object[] args) {
         Scriptable result = createObject(cx, scope);
         if (result != null) {
             Object val = call(cx, scope, result, args);
             if (val instanceof Scriptable) {
-                result = (Scriptable)val;
+                result = (Scriptable) val;
             }
         } else {
             Object val = call(cx, scope, null, args);
@@ -366,10 +395,12 @@
                 // It is program error not to return Scriptable from
                 // the call method if createObject returns null.
                 throw new IllegalStateException(
-                    "Bad implementaion of call as constructor, name="
-                    +getFunctionName()+" in "+getClass().getName());
+                        "Bad implementation of call as constructor, name="
+                                + getFunctionName()
+                                + " in "
+                                + getClass().getName());
             }
-            result = (Scriptable)val;
+            result = (Scriptable) val;
             if (result.getPrototype() == null) {
                 Scriptable proto = getClassPrototype();
                 if (result != proto) {
@@ -387,17 +418,13 @@
     }
 
     /**
-     * Creates new script object.
-     * The default implementation of {@link #construct} uses the method to
-     * to get the value for <tt>thisObj</tt> argument when invoking
-     * {@link #call}.
-     * The methos is allowed to return <tt>null</tt> to indicate that
-     * {@link #call} will create a new object itself. In this case
-     * {@link #construct} will set scope and prototype on the result
+     * Creates new script object. The default implementation of {@link #construct} uses the method
+     * to to get the value for <code>thisObj</code> argument when invoking {@link #call}. The methos
+     * is allowed to return <code>null</code> to indicate that {@link #call} will create a new
+     * object itself. In this case {@link #construct} will set scope and prototype on the result
      * {@link #call} unless they are already set.
      */
-    public Scriptable createObject(Context cx, Scriptable scope)
-    {
+    public Scriptable createObject(Context cx, Scriptable scope) {
         Scriptable newInstance = new NativeObject();
         newInstance.setPrototype(getClassPrototype());
         newInstance.setParentScope(getParentScope());
@@ -405,15 +432,12 @@
     }
 
     /**
-     * Decompile the source information associated with this js
-     * function/script back into a string.
+     * Decompile the source information associated with this js function/script back into a string.
      *
      * @param indent How much to indent the decompiled result.
-     *
      * @param flags Flags specifying format of decompilation output.
      */
-    String decompile(int indent, int flags)
-    {
+    String decompile(int indent, int flags) {
         StringBuilder sb = new StringBuilder();
         boolean justbody = (0 != (flags & Decompiler.ONLY_BODY_FLAG));
         if (!justbody) {
@@ -430,14 +454,32 @@
         return sb.toString();
     }
 
-    public int getArity() { return 0; }
+    public int getArity() {
+        return 0;
+    }
 
-    public int getLength() { return 0; }
+    public int getLength() {
+        return 0;
+    }
 
     public String getFunctionName() {
         return "";
     }
 
+    /**
+     * Sets the attributes of the "name", "length", and "arity" properties, which differ for many
+     * native objects.
+     */
+    public void setStandardPropertyAttributes(int attributes) {
+        namePropertyAttributes = attributes;
+        lengthPropertyAttributes = attributes;
+        arityPropertyAttributes = attributes;
+    }
+
+    public void setPrototypePropertyAttributes(int attributes) {
+        prototypePropertyAttributes = attributes;
+    }
+
     protected boolean hasPrototypeProperty() {
         return prototypeProperty != null || this instanceof NativeFunction;
     }
@@ -458,13 +500,16 @@
         return result;
     }
 
-    private synchronized Object setupDefaultPrototype() {
+    protected void setPrototypeProperty(Object prototype) {
+        this.prototypeProperty = prototype;
+    }
+
+    protected synchronized Object setupDefaultPrototype() {
         if (prototypeProperty != null) {
             return prototypeProperty;
         }
         NativeObject obj = new NativeObject();
-        final int attr = ScriptableObject.DONTENUM;
-        obj.defineProperty("constructor", this, attr);
+        obj.defineProperty("constructor", this, DONTENUM);
         // put the prototype property into the object now, then in the
         // wacky case of a user defining a function Object(), we don't
         // get an infinite loop trying to find the prototype.
@@ -477,34 +522,32 @@
         return obj;
     }
 
-    private Object getArguments()
-    {
-      // <Function name>.arguments is deprecated, so we use a slow
-      // way of getting it that doesn't add to the invocation cost.
-      // TODO: add warning, error based on version
-      Object value = defaultHas("arguments") ? defaultGet("arguments") : argumentsObj;
-      if (value != NOT_FOUND) {
-          // Should after changing <Function name>.arguments its
-          // activation still be available during Function call?
-          // This code assumes it should not:
-          // defaultGet("arguments") != NOT_FOUND
-          // means assigned arguments
-          return value;
-      }
-      Context cx = Context.getContext();
-      NativeCall activation = ScriptRuntime.findFunctionActivation(cx, this);
-      return (activation == null)
-             ? null
-             : activation.get("arguments", activation);
-    }
-
-    private static Object jsConstructor(Context cx, Scriptable scope,
-                                        Object[] args)
-    {
+    private Object getArguments() {
+        // <Function name>.arguments is deprecated, so we use a slow
+        // way of getting it that doesn't add to the invocation cost.
+        // TODO: add warning, error based on version
+        Object value = defaultHas("arguments") ? defaultGet("arguments") : argumentsObj;
+        if (value != NOT_FOUND) {
+            // Should after changing <Function name>.arguments its
+            // activation still be available during Function call?
+            // This code assumes it should not:
+            // defaultGet("arguments") != NOT_FOUND
+            // means assigned arguments
+            return value;
+        }
+        Context cx = Context.getContext();
+        NativeCall activation = ScriptRuntime.findFunctionActivation(cx, this);
+        return (activation == null) ? null : activation.get("arguments", activation);
+    }
+
+    private Object jsConstructor(Context cx, Scriptable scope, Object[] args) {
         int arglen = args.length;
         StringBuilder sourceBuf = new StringBuilder();
 
         sourceBuf.append("function ");
+        if (isGeneratorFunction()) {
+            sourceBuf.append("* ");
+        }
         /* version != 1.2 Function constructor behavior -
          * print 'anonymous' as the function name if the
          * version (under which the function was compiled) is
@@ -539,8 +582,7 @@
             linep[0] = 1;
         }
 
-        String sourceURI = ScriptRuntime.
-            makeUrlForGeneratedScript(false, filename, linep[0]);
+        String sourceURI = ScriptRuntime.makeUrlForGeneratedScript(false, filename, linep[0]);
 
         Scriptable global = ScriptableObject.getTopLevelScope(scope);
 
@@ -549,61 +591,61 @@
 
         Evaluator evaluator = Context.createInterpreter();
         if (evaluator == null) {
-            throw new JavaScriptException("Interpreter not present",
-                    filename, linep[0]);
+            throw new JavaScriptException("Interpreter not present", filename, linep[0]);
         }
 
         // Compile with explicit interpreter instance to force interpreter
         // mode.
-        return cx.compileFunction(global, source, evaluator, reporter,
-                                  sourceURI, 1, null);
+        return cx.compileFunction(global, source, evaluator, reporter, sourceURI, 1, null);
     }
 
     @Override
-    protected int findPrototypeId(String s)
-    {
+    protected int findPrototypeId(String s) {
         int id;
-// #string_id_map#
-// #generated# Last update: 2009-07-24 16:00:52 EST
-        L0: { id = 0; String X = null; int c;
-            L: switch (s.length()) {
-            case 4: c=s.charAt(0);
-                if (c=='b') { X="bind";id=Id_bind; }
-                else if (c=='c') { X="call";id=Id_call; }
-                break L;
-            case 5: X="apply";id=Id_apply; break L;
-            case 8: c=s.charAt(3);
-                if (c=='o') { X="toSource";id=Id_toSource; }
-                else if (c=='t') { X="toString";id=Id_toString; }
-                break L;
-            case 11: X="constructor";id=Id_constructor; break L;
-            }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-            break L0;
+        switch (s) {
+            case "constructor":
+                id = Id_constructor;
+                break;
+            case "toString":
+                id = Id_toString;
+                break;
+            case "toSource":
+                id = Id_toSource;
+                break;
+            case "apply":
+                id = Id_apply;
+                break;
+            case "call":
+                id = Id_call;
+                break;
+            case "bind":
+                id = Id_bind;
+                break;
+            default:
+                id = 0;
+                break;
         }
-// #/generated#
         return id;
     }
 
-    private static final int
-        Id_constructor    = 1,
-        Id_toString       = 2,
-        Id_toSource       = 3,
-        Id_apply          = 4,
-        Id_call           = 5,
-        Id_bind           = 6,
-
-        MAX_PROTOTYPE_ID  = Id_bind;
-
-// #/string_id_map#
+    private static final int Id_constructor = 1,
+            Id_toString = 2,
+            Id_toSource = 3,
+            Id_apply = 4,
+            Id_call = 5,
+            Id_bind = 6,
+            MAX_PROTOTYPE_ID = Id_bind;
 
     private Object prototypeProperty;
     private Object argumentsObj = NOT_FOUND;
+    private boolean isGeneratorFunction = false;
 
     // For function object instances, attributes are
     //  {configurable:false, enumerable:false};
     // see ECMA 15.3.5.2
-    private int prototypePropertyAttributes = PERMANENT|DONTENUM;
-    private int argumentsAttributes = PERMANENT|DONTENUM;
+    private int prototypePropertyAttributes = PERMANENT | DONTENUM;
+    private int argumentsAttributes = PERMANENT | DONTENUM;
+    private int arityPropertyAttributes = PERMANENT | READONLY | DONTENUM;
+    private int namePropertyAttributes = PERMANENT | READONLY | DONTENUM;
+    private int lengthPropertyAttributes = PERMANENT | READONLY | DONTENUM;
 }
-
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/BoundFunction.java rhino-1.7.14/src/org/mozilla/javascript/BoundFunction.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/BoundFunction.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/BoundFunction.java	2022-01-06 22:57:21.000000000 +0100
@@ -12,9 +12,9 @@
  * @author Raphael Speyer
  */
 public class BoundFunction extends BaseFunction {
-    
-  static final long serialVersionUID = 2118137342826470729L;
-    
+
+  private static final long serialVersionUID = 2118137342826470729L;
+
   private final Callable targetFunction;
   private final Scriptable boundThis;
   private final Object[] boundArgs;
@@ -38,8 +38,8 @@
     NativeObject throwing = new NativeObject();
     throwing.put("get", throwing, thrower);
     throwing.put("set", throwing, thrower);
-    throwing.put("enumerable", throwing, false);
-    throwing.put("configurable", throwing, false);
+    throwing.put("enumerable", throwing, Boolean.FALSE);
+    throwing.put("configurable", throwing, Boolean.FALSE);
     throwing.preventExtensions();
 
     this.defineOwnProperty(cx, "caller", throwing, false);
@@ -58,7 +58,7 @@
     if (targetFunction instanceof Function) {
       return ((Function) targetFunction).construct(cx, scope, concat(boundArgs, extraArgs));
     }
-    throw ScriptRuntime.typeError0("msg.not.ctor");
+    throw ScriptRuntime.typeErrorById("msg.not.ctor");
   }
 
   @Override
@@ -66,7 +66,7 @@
     if (targetFunction instanceof Function) {
       return ((Function) targetFunction).hasInstance(instance);
     }
-    throw ScriptRuntime.typeError0("msg.not.ctor");
+    throw ScriptRuntime.typeErrorById("msg.not.ctor");
   }
 
   @Override
@@ -74,10 +74,14 @@
     return length;
   }
 
-  private Object[] concat(Object[] first, Object[] second) {
+  private static Object[] concat(Object[] first, Object[] second) {
     Object[] args = new Object[first.length + second.length];
     System.arraycopy(first, 0, args, 0, first.length);
     System.arraycopy(second, 0, args, first.length, second.length);
     return args;
   }
+
+  static boolean equalObjectGraphs(BoundFunction f1, BoundFunction f2, EqualObjectGraphs eq) {
+      return  eq.equalGraphs(f1.boundThis, f2.boundThis) && eq.equalGraphs(f1.targetFunction, f2.targetFunction) && eq.equalGraphs(f1.boundArgs, f2.boundArgs);
+  }
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/Callable.java rhino-1.7.14/src/org/mozilla/javascript/Callable.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/Callable.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/Callable.java	2022-01-06 22:57:21.000000000 +0100
@@ -21,7 +21,6 @@
      * @param args the array of arguments
      * @return the result of the call
      */
-    public Object call(Context cx, Scriptable scope, Scriptable thisObj,
-                       Object[] args);
+    public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args);
 }
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ClassCache.java rhino-1.7.14/src/org/mozilla/javascript/ClassCache.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ClassCache.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ClassCache.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,66 +6,86 @@
 
 package org.mozilla.javascript;
 
+import java.io.Serializable;
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
-import java.io.Serializable;
 
 /**
- * Cache of generated classes and data structures to access Java runtime
- * from JavaScript.
+ * Cache of generated classes and data structures to access Java runtime from JavaScript.
  *
  * @author Igor Bukanov
- *
  * @since Rhino 1.5 Release 5
  */
-public class ClassCache implements Serializable
-{
+public class ClassCache implements Serializable {
+
     private static final long serialVersionUID = -8866246036237312215L;
     private static final Object AKEY = "ClassCache";
     private volatile boolean cachingIsEnabled = true;
-    private transient Map<Class<?>,JavaMembers> classTable;
-    private transient Map<JavaAdapter.JavaAdapterSignature,Class<?>> classAdapterCache;
-    private transient Map<Class<?>,Object> interfaceAdapterCache;
+    private transient Map<CacheKey, JavaMembers> classTable;
+    private transient Map<JavaAdapter.JavaAdapterSignature, Class<?>> classAdapterCache;
+    private transient Map<Class<?>, Object> interfaceAdapterCache;
     private int generatedClassSerial;
     private Scriptable associatedScope;
 
     /**
-     * Search for ClassCache object in the given scope.
-     * The method first calls
-     * {@link ScriptableObject#getTopLevelScope(Scriptable scope)}
-     * to get the top most scope and then tries to locate associated
-     * ClassCache object in the prototype chain of the top scope.
+     * CacheKey is a combination of class and securityContext. This is required when classes are
+     * loaded from different security contexts
+     */
+    static class CacheKey {
+        final Class<?> cls;
+        final Object sec;
+        /** Constructor. */
+        public CacheKey(Class<?> cls, Object securityContext) {
+            this.cls = cls;
+            this.sec = securityContext;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = cls.hashCode();
+            if (sec != null) {
+                result = sec.hashCode() * 31;
+            }
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            return (obj instanceof CacheKey)
+                    && Objects.equals(this.cls, ((CacheKey) obj).cls)
+                    && Objects.equals(this.sec, ((CacheKey) obj).sec);
+        }
+    }
+
+    /**
+     * Search for ClassCache object in the given scope. The method first calls {@link
+     * ScriptableObject#getTopLevelScope(Scriptable scope)} to get the top most scope and then tries
+     * to locate associated ClassCache object in the prototype chain of the top scope.
      *
      * @param scope scope to search for ClassCache object.
-     * @return previously associated ClassCache object or a new instance of
-     *         ClassCache if no ClassCache object was found.
-     *
+     * @return previously associated ClassCache object or a new instance of ClassCache if no
+     *     ClassCache object was found.
      * @see #associate(ScriptableObject topScope)
      */
-    public static ClassCache get(Scriptable scope)
-    {
-        ClassCache cache = (ClassCache)
-                ScriptableObject.getTopScopeValue(scope, AKEY);
+    public static ClassCache get(Scriptable scope) {
+        ClassCache cache = (ClassCache) ScriptableObject.getTopScopeValue(scope, AKEY);
         if (cache == null) {
-            throw new RuntimeException("Can't find top level scope for " +
-                    "ClassCache.get");
+            throw new RuntimeException("Can't find top level scope for " + "ClassCache.get");
         }
         return cache;
     }
 
     /**
-     * Associate ClassCache object with the given top-level scope.
-     * The ClassCache object can only be associated with the given scope once.
+     * Associate ClassCache object with the given top-level scope. The ClassCache object can only be
+     * associated with the given scope once.
      *
      * @param topScope scope to associate this ClassCache object with.
-     * @return true if no previous ClassCache objects were embedded into
-     *         the scope and this ClassCache were successfully associated
-     *         or false otherwise.
-     *
+     * @return true if no previous ClassCache objects were embedded into the scope and this
+     *     ClassCache were successfully associated or false otherwise.
      * @see #get(Scriptable scope)
      */
-    public boolean associate(ScriptableObject topScope)
-    {
+    public boolean associate(ScriptableObject topScope) {
         if (topScope.getParentScope() != null) {
             // Can only associate cache with top level scope
             throw new IllegalArgumentException();
@@ -77,118 +97,92 @@
         return false;
     }
 
-    /**
-     * Empty caches of generated Java classes and Java reflection information.
-     */
-    public synchronized void clearCaches()
-    {
+    /** Empty caches of generated Java classes and Java reflection information. */
+    public synchronized void clearCaches() {
         classTable = null;
         classAdapterCache = null;
         interfaceAdapterCache = null;
     }
 
-    /**
-     * Check if generated Java classes and Java reflection information
-     * is cached.
-     */
-    public final boolean isCachingEnabled()
-    {
+    /** Check if generated Java classes and Java reflection information is cached. */
+    public final boolean isCachingEnabled() {
         return cachingIsEnabled;
     }
 
-     /**
+    /**
      * Set whether to cache some values.
-     * <p>
-     * By default, the engine will cache the results of
-     * <tt>Class.getMethods()</tt> and similar calls.
-     * This can speed execution dramatically, but increases the memory
-     * footprint. Also, with caching enabled, references may be held to
-     * objects past the lifetime of any real usage.
-     * <p>
-     * If caching is enabled and this method is called with a
-     * <code>false</code> argument, the caches will be emptied.
-     * <p>
-     * Caching is enabled by default.
      *
-     * @param enabled if true, caching is enabled
+     * <p>By default, the engine will cache the results of <code>Class.getMethods()</code> and
+     * similar calls. This can speed execution dramatically, but increases the memory footprint.
+     * Also, with caching enabled, references may be held to objects past the lifetime of any real
+     * usage.
      *
+     * <p>If caching is enabled and this method is called with a <code>false</code> argument, the
+     * caches will be emptied.
+     *
+     * <p>Caching is enabled by default.
+     *
+     * @param enabled if true, caching is enabled
      * @see #clearCaches()
      */
-    public synchronized void setCachingEnabled(boolean enabled)
-    {
-        if (enabled == cachingIsEnabled)
-            return;
-        if (!enabled)
-            clearCaches();
+    public synchronized void setCachingEnabled(boolean enabled) {
+        if (enabled == cachingIsEnabled) return;
+        if (!enabled) clearCaches();
         cachingIsEnabled = enabled;
     }
 
-    /**
-     * @return a map from classes to associated JavaMembers objects
-     */
-    Map<Class<?>,JavaMembers> getClassCacheMap() {
+    /** @return a map from classes to associated JavaMembers objects */
+    Map<CacheKey, JavaMembers> getClassCacheMap() {
         if (classTable == null) {
             // Use 1 as concurrency level here and for other concurrent hash maps
             // as we don't expect high levels of sustained concurrent writes.
-            classTable = new ConcurrentHashMap<Class<?>,JavaMembers>(16, 0.75f, 1);
+            classTable = new ConcurrentHashMap<CacheKey, JavaMembers>(16, 0.75f, 1);
         }
         return classTable;
     }
 
-    Map<JavaAdapter.JavaAdapterSignature,Class<?>> getInterfaceAdapterCacheMap()
-    {
+    Map<JavaAdapter.JavaAdapterSignature, Class<?>> getInterfaceAdapterCacheMap() {
         if (classAdapterCache == null) {
-            classAdapterCache = new ConcurrentHashMap<JavaAdapter.JavaAdapterSignature,Class<?>>(16, 0.75f, 1);
+            classAdapterCache =
+                    new ConcurrentHashMap<JavaAdapter.JavaAdapterSignature, Class<?>>(16, 0.75f, 1);
         }
         return classAdapterCache;
     }
 
     /**
-     * @deprecated
-     * The method always returns false.
+     * @deprecated The method always returns false.
      * @see #setInvokerOptimizationEnabled(boolean enabled)
      */
     @Deprecated
-    public boolean isInvokerOptimizationEnabled()
-    {
+    public boolean isInvokerOptimizationEnabled() {
         return false;
     }
 
     /**
-     * @deprecated
-     * The method does nothing.
-     * Invoker optimization is no longer used by Rhino.
-     * On modern JDK like 1.4 or 1.5 the disadvantages of the optimization
-     * like increased memory usage or longer initialization time overweight
-     * small speed increase that can be gained using generated proxy class
-     * to replace reflection.
+     * @deprecated The method does nothing. Invoker optimization is no longer used by Rhino. On
+     *     modern JDK like 1.4 or 1.5 the disadvantages of the optimization like increased memory
+     *     usage or longer initialization time overweight small speed increase that can be gained
+     *     using generated proxy class to replace reflection.
      */
     @Deprecated
-    public synchronized void setInvokerOptimizationEnabled(boolean enabled)
-    {
-    }
+    public synchronized void setInvokerOptimizationEnabled(boolean enabled) {}
 
     /**
-     * Internal engine method to return serial number for generated classes
-     * to ensure name uniqueness.
+     * Internal engine method to return serial number for generated classes to ensure name
+     * uniqueness.
      */
-    public final synchronized int newClassSerialNumber()
-    {
+    public final synchronized int newClassSerialNumber() {
         return ++generatedClassSerial;
     }
 
-    Object getInterfaceAdapter(Class<?> cl)
-    {
-        return interfaceAdapterCache == null
-                    ? null
-                    : interfaceAdapterCache.get(cl);
+    Object getInterfaceAdapter(Class<?> cl) {
+        return interfaceAdapterCache == null ? null : interfaceAdapterCache.get(cl);
     }
 
-    synchronized void cacheInterfaceAdapter(Class<?> cl, Object iadapter)
-    {
+    synchronized void cacheInterfaceAdapter(Class<?> cl, Object iadapter) {
         if (cachingIsEnabled) {
             if (interfaceAdapterCache == null) {
-                interfaceAdapterCache = new ConcurrentHashMap<Class<?>,Object>(16, 0.75f, 1);
+                interfaceAdapterCache = new ConcurrentHashMap<Class<?>, Object>(16, 0.75f, 1);
             }
             interfaceAdapterCache.put(cl, iadapter);
         }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/CodeGenerator.java rhino-1.7.14/src/org/mozilla/javascript/CodeGenerator.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/CodeGenerator.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/CodeGenerator.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,13 +6,19 @@
 
 package org.mozilla.javascript;
 
-import org.mozilla.javascript.ast.ScriptNode;
-import org.mozilla.javascript.ast.Jump;
+import java.math.BigInteger;
+import java.util.List;
+import org.mozilla.javascript.ast.AstNode;
+import org.mozilla.javascript.ast.AstRoot;
+import org.mozilla.javascript.ast.Block;
 import org.mozilla.javascript.ast.FunctionNode;
+import org.mozilla.javascript.ast.Jump;
+import org.mozilla.javascript.ast.Scope;
+import org.mozilla.javascript.ast.ScriptNode;
+import org.mozilla.javascript.ast.TemplateCharacters;
+import org.mozilla.javascript.ast.VariableInitializer;
 
-/**
- * Generates bytecode for the Interpreter.
- */
+/** Generates bytecode for the Interpreter. */
 class CodeGenerator extends Icode {
 
     private static final int MIN_LABEL_TABLE_SIZE = 32;
@@ -32,6 +38,7 @@
     private int doubleTableTop;
 
     private ObjToIntMap strings = new ObjToIntMap(20);
+    private ObjToIntMap bigInts = new ObjToIntMap(20);
     private int localTop;
     private int[] labelTable;
     private int labelTableTop;
@@ -46,11 +53,11 @@
     // ECF_ or Expression Context Flags constants: for now only TAIL
     private static final int ECF_TAIL = 1 << 0;
 
-    public InterpreterData compile(CompilerEnvirons compilerEnv,
-                                   ScriptNode tree,
-                                   String encodedSource,
-                                   boolean returnFunction)
-    {
+    public InterpreterData compile(
+            CompilerEnvirons compilerEnv,
+            ScriptNode tree,
+            String encodedSource,
+            boolean returnFunction) {
         this.compilerEnv = compilerEnv;
 
         if (Token.printTrees) {
@@ -58,7 +65,7 @@
             System.out.println(tree.toStringTree(tree));
         }
 
-        new NodeTransformer().transform(tree);
+        new NodeTransformer().transform(tree, compilerEnv);
 
         if (Token.printTrees) {
             System.out.println("after transform:");
@@ -71,10 +78,12 @@
             scriptOrFn = tree;
         }
 
-        itsData = new InterpreterData(compilerEnv.getLanguageVersion(),
-                                      scriptOrFn.getSourceName(),
-                                      encodedSource,
-                                      scriptOrFn.isInStrictMode());
+        itsData =
+                new InterpreterData(
+                        compilerEnv.getLanguageVersion(),
+                        scriptOrFn.getSourceName(),
+                        encodedSource,
+                        scriptOrFn.isInStrictMode());
         itsData.topLevel = true;
 
         if (returnFunction) {
@@ -85,11 +94,10 @@
         return itsData;
     }
 
-    private void generateFunctionICode()
-    {
+    private void generateFunctionICode() {
         itsInFunctionFlag = true;
 
-        FunctionNode theFunction = (FunctionNode)scriptOrFn;
+        FunctionNode theFunction = (FunctionNode) scriptOrFn;
 
         itsData.itsFunctionType = theFunction.getFunctionType();
         itsData.itsNeedsActivation = theFunction.requiresActivation();
@@ -97,22 +105,28 @@
             itsData.itsName = theFunction.getName();
         }
         if (theFunction.isGenerator()) {
-          addIcode(Icode_GENERATOR);
-          addUint16(theFunction.getBaseLineno() & 0xFFFF);
+            addIcode(Icode_GENERATOR);
+            addUint16(theFunction.getBaseLineno() & 0xFFFF);
         }
         if (theFunction.isInStrictMode()) {
             itsData.isStrict = true;
         }
+        if (theFunction.isES6Generator()) {
+            itsData.isES6Generator = true;
+        }
+
+        itsData.declaredAsVar = (theFunction.getParent() instanceof VariableInitializer);
 
         generateICodeFromTree(theFunction.getLastChild());
     }
 
-    private void generateICodeFromTree(Node tree)
-    {
+    private void generateICodeFromTree(Node tree) {
         generateNestedFunctions();
 
         generateRegExpLiterals();
 
+        generateTemplateLiterals();
+
         visitStatement(tree, 0);
         fixLabelGotos();
         // add RETURN_RESULT only to scripts as function always ends with RETURN
@@ -133,7 +147,7 @@
             itsData.itsStringTable = new String[strings.size()];
             ObjToIntMap.Iterator iter = strings.newIterator();
             for (iter.start(); !iter.done(); iter.next()) {
-                String str = (String)iter.getKey();
+                String str = (String) iter.getKey();
                 int index = iter.getValue();
                 if (itsData.itsStringTable[index] != null) Kit.codeBug();
                 itsData.itsStringTable[index] = str;
@@ -143,25 +157,31 @@
             itsData.itsDoubleTable = null;
         } else if (itsData.itsDoubleTable.length != doubleTableTop) {
             double[] tmp = new double[doubleTableTop];
-            System.arraycopy(itsData.itsDoubleTable, 0, tmp, 0,
-                             doubleTableTop);
+            System.arraycopy(itsData.itsDoubleTable, 0, tmp, 0, doubleTableTop);
             itsData.itsDoubleTable = tmp;
         }
-        if (exceptionTableTop != 0
-            && itsData.itsExceptionTable.length != exceptionTableTop)
-        {
+        if (bigInts.size() == 0) {
+            itsData.itsBigIntTable = null;
+        } else {
+            itsData.itsBigIntTable = new BigInteger[bigInts.size()];
+            ObjToIntMap.Iterator iter = bigInts.newIterator();
+            for (iter.start(); !iter.done(); iter.next()) {
+                BigInteger bigInt = (BigInteger) iter.getKey();
+                int index = iter.getValue();
+                if (itsData.itsBigIntTable[index] != null) Kit.codeBug();
+                itsData.itsBigIntTable[index] = bigInt;
+            }
+        }
+        if (exceptionTableTop != 0 && itsData.itsExceptionTable.length != exceptionTableTop) {
             int[] tmp = new int[exceptionTableTop];
-            System.arraycopy(itsData.itsExceptionTable, 0, tmp, 0,
-                             exceptionTableTop);
+            System.arraycopy(itsData.itsExceptionTable, 0, tmp, 0, exceptionTableTop);
             itsData.itsExceptionTable = tmp;
         }
 
         itsData.itsMaxVars = scriptOrFn.getParamAndVarCount();
         // itsMaxFrameArray: interpret method needs this amount for its
         // stack and sDbl arrays
-        itsData.itsMaxFrameArray = itsData.itsMaxVars
-                                   + itsData.itsMaxLocals
-                                   + itsData.itsMaxStack;
+        itsData.itsMaxFrameArray = itsData.itsMaxVars + itsData.itsMaxLocals + itsData.itsMaxStack;
 
         itsData.argNames = scriptOrFn.getParamAndVarNames();
         itsData.argIsConst = scriptOrFn.getParamAndVarConst();
@@ -177,8 +197,7 @@
         if (Token.printICode) Interpreter.dumpICode(itsData);
     }
 
-    private void generateNestedFunctions()
-    {
+    private void generateNestedFunctions() {
         int functionCount = scriptOrFn.getFunctionCount();
         if (functionCount == 0) return;
 
@@ -191,12 +210,18 @@
             gen.itsData = new InterpreterData(itsData);
             gen.generateFunctionICode();
             array[i] = gen.itsData;
+
+            final AstNode fnParent = fn.getParent();
+            if (!(fnParent instanceof AstRoot
+                    || fnParent instanceof Scope
+                    || fnParent instanceof Block)) {
+                gen.itsData.declaredAsFunctionExpression = true;
+            }
         }
         itsData.itsNestedFunctions = array;
     }
 
-    private void generateRegExpLiterals()
-    {
+    private void generateRegExpLiterals() {
         int N = scriptOrFn.getRegexpCount();
         if (N == 0) return;
 
@@ -211,8 +236,25 @@
         itsData.itsRegExpLiterals = array;
     }
 
-    private void updateLineNumber(Node node)
-    {
+    private void generateTemplateLiterals() {
+        int N = scriptOrFn.getTemplateLiteralCount();
+        if (N == 0) return;
+
+        Object[] array = new Object[N];
+        for (int i = 0; i != N; i++) {
+            List<TemplateCharacters> strings = scriptOrFn.getTemplateLiteralStrings(i);
+            int j = 0;
+            String[] values = new String[strings.size() * 2];
+            for (TemplateCharacters s : strings) {
+                values[j++] = s.getValue();
+                values[j++] = s.getRawValue();
+            }
+            array[i] = values;
+        }
+        itsData.itsTemplateLiterals = array;
+    }
+
+    private void updateLineNumber(Node node) {
         int lineno = node.getLineno();
         if (lineno != lineNumber && lineno >= 0) {
             if (itsData.firstLinePC < 0) {
@@ -224,271 +266,283 @@
         }
     }
 
-    private RuntimeException badTree(Node node)
-    {
+    private static RuntimeException badTree(Node node) {
         throw new RuntimeException(node.toString());
     }
 
-    private void visitStatement(Node node, int initialStackDepth)
-    {
+    private void visitStatement(Node node, int initialStackDepth) {
         int type = node.getType();
         Node child = node.getFirstChild();
         switch (type) {
-
-          case Token.FUNCTION:
-            {
-                int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
-                int fnType = scriptOrFn.getFunctionNode(fnIndex).
-                                 getFunctionType();
-                // Only function expressions or function expression
-                // statements need closure code creating new function
-                // object on stack as function statements are initialized
-                // at script/function start.
-                // In addition, function expressions can not be present here
-                // at statement level, they must only be present as expressions.
-                if (fnType == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) {
-                    addIndexOp(Icode_CLOSURE_STMT, fnIndex);
-                } else {
-                    if (fnType != FunctionNode.FUNCTION_STATEMENT) {
-                        throw Kit.codeBug();
+            case Token.FUNCTION:
+                {
+                    int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
+                    int fnType = scriptOrFn.getFunctionNode(fnIndex).getFunctionType();
+                    // Only function expressions or function expression
+                    // statements need closure code creating new function
+                    // object on stack as function statements are initialized
+                    // at script/function start.
+                    // In addition, function expressions can not be present here
+                    // at statement level, they must only be present as expressions.
+                    if (fnType == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) {
+                        addIndexOp(Icode_CLOSURE_STMT, fnIndex);
+                    } else {
+                        if (fnType != FunctionNode.FUNCTION_STATEMENT) {
+                            throw Kit.codeBug();
+                        }
+                    }
+                    // For function statements or function expression statements
+                    // in scripts, we need to ensure that the result of the script
+                    // is the function if it is the last statement in the script.
+                    // For example, eval("function () {}") should return a
+                    // function, not undefined.
+                    if (!itsInFunctionFlag) {
+                        addIndexOp(Icode_CLOSURE_EXPR, fnIndex);
+                        stackChange(1);
+                        addIcode(Icode_POP_RESULT);
+                        stackChange(-1);
                     }
                 }
-                // For function statements or function expression statements
-                // in scripts, we need to ensure that the result of the script
-                // is the function if it is the last statement in the script.
-                // For example, eval("function () {}") should return a
-                // function, not undefined.
-                if (!itsInFunctionFlag) {
-                    addIndexOp(Icode_CLOSURE_EXPR, fnIndex);
-                    stackChange(1);
-                    addIcode(Icode_POP_RESULT);
-                    stackChange(-1);
-                }
-            }
-            break;
+                break;
 
-          case Token.LABEL:
-          case Token.LOOP:
-          case Token.BLOCK:
-          case Token.EMPTY:
-          case Token.WITH:
-            updateLineNumber(node);
-            // fallthru
-          case Token.SCRIPT:
-            while (child != null) {
-                visitStatement(child, initialStackDepth);
-                child = child.getNext();
-            }
-            break;
-
-          case Token.ENTERWITH:
-            visitExpression(child, 0);
-            addToken(Token.ENTERWITH);
-            stackChange(-1);
-            break;
-
-          case Token.LEAVEWITH:
-            addToken(Token.LEAVEWITH);
-            break;
-
-          case Token.LOCAL_BLOCK:
-            {
-                int local = allocLocal();
-                node.putIntProp(Node.LOCAL_PROP, local);
+            case Token.LABEL:
+            case Token.LOOP:
+            case Token.BLOCK:
+            case Token.EMPTY:
+            case Token.WITH:
                 updateLineNumber(node);
+                // fall through
+            case Token.SCRIPT:
                 while (child != null) {
                     visitStatement(child, initialStackDepth);
                     child = child.getNext();
                 }
-                addIndexOp(Icode_LOCAL_CLEAR, local);
-                releaseLocal(local);
-            }
-            break;
+                break;
 
-          case Token.DEBUGGER:
-            addIcode(Icode_DEBUGGER);
-            break;
-
-          case Token.SWITCH:
-            updateLineNumber(node);
-            // See comments in IRFactory.createSwitch() for description
-            // of SWITCH node
-            {
+            case Token.ENTERWITH:
                 visitExpression(child, 0);
-                for (Jump caseNode = (Jump)child.getNext();
-                     caseNode != null;
-                     caseNode = (Jump)caseNode.getNext())
-                {
-                    if (caseNode.getType() != Token.CASE)
-                        throw badTree(caseNode);
-                    Node test = caseNode.getFirstChild();
-                    addIcode(Icode_DUP);
-                    stackChange(1);
-                    visitExpression(test, 0);
-                    addToken(Token.SHEQ);
-                    stackChange(-1);
-                    // If true, Icode_IFEQ_POP will jump and remove case
-                    // value from stack
-                    addGoto(caseNode.target, Icode_IFEQ_POP);
-                    stackChange(-1);
-                }
-                addIcode(Icode_POP);
+                addToken(Token.ENTERWITH);
                 stackChange(-1);
-            }
-            break;
+                break;
 
-          case Token.TARGET:
-            markTargetLabel(node);
-            break;
-
-          case Token.IFEQ :
-          case Token.IFNE :
-            {
-                Node target = ((Jump)node).target;
-                visitExpression(child, 0);
-                addGoto(target, type);
-                stackChange(-1);
-            }
-            break;
+            case Token.LEAVEWITH:
+                addToken(Token.LEAVEWITH);
+                break;
 
-          case Token.GOTO:
-            {
-                Node target = ((Jump)node).target;
-                addGoto(target, type);
-            }
-            break;
+            case Token.LOCAL_BLOCK:
+                {
+                    int local = allocLocal();
+                    node.putIntProp(Node.LOCAL_PROP, local);
+                    updateLineNumber(node);
+                    while (child != null) {
+                        visitStatement(child, initialStackDepth);
+                        child = child.getNext();
+                    }
+                    addIndexOp(Icode_LOCAL_CLEAR, local);
+                    releaseLocal(local);
+                }
+                break;
 
-          case Token.JSR:
-            {
-                Node target = ((Jump)node).target;
-                addGoto(target, Icode_GOSUB);
-            }
-            break;
+            case Token.DEBUGGER:
+                addIcode(Icode_DEBUGGER);
+                break;
 
-          case Token.FINALLY:
-            {
-                // Account for incomming GOTOSUB address
-                stackChange(1);
-                int finallyRegister = getLocalBlockRef(node);
-                addIndexOp(Icode_STARTSUB, finallyRegister);
-                stackChange(-1);
-                while (child != null) {
-                    visitStatement(child, initialStackDepth);
-                    child = child.getNext();
+            case Token.SWITCH:
+                updateLineNumber(node);
+                // See comments in IRFactory.createSwitch() for description
+                // of SWITCH node
+                {
+                    visitExpression(child, 0);
+                    for (Jump caseNode = (Jump) child.getNext();
+                            caseNode != null;
+                            caseNode = (Jump) caseNode.getNext()) {
+                        if (caseNode.getType() != Token.CASE) throw badTree(caseNode);
+                        Node test = caseNode.getFirstChild();
+                        addIcode(Icode_DUP);
+                        stackChange(1);
+                        visitExpression(test, 0);
+                        addToken(Token.SHEQ);
+                        stackChange(-1);
+                        // If true, Icode_IFEQ_POP will jump and remove case
+                        // value from stack
+                        addGoto(caseNode.target, Icode_IFEQ_POP);
+                        stackChange(-1);
+                    }
+                    addIcode(Icode_POP);
+                    stackChange(-1);
                 }
-                addIndexOp(Icode_RETSUB, finallyRegister);
-            }
-            break;
+                break;
 
-          case Token.EXPR_VOID:
-          case Token.EXPR_RESULT:
-            updateLineNumber(node);
-            visitExpression(child, 0);
-            addIcode((type == Token.EXPR_VOID) ? Icode_POP : Icode_POP_RESULT);
-            stackChange(-1);
-            break;
+            case Token.TARGET:
+                markTargetLabel(node);
+                break;
 
-          case Token.TRY:
-            {
-                Jump tryNode = (Jump)node;
-                int exceptionObjectLocal = getLocalBlockRef(tryNode);
-                int scopeLocal = allocLocal();
-
-                addIndexOp(Icode_SCOPE_SAVE, scopeLocal);
-
-                int tryStart = iCodeTop;
-                boolean savedFlag = itsInTryFlag;
-                itsInTryFlag = true;
-                while (child != null) {
-                    visitStatement(child, initialStackDepth);
-                    child = child.getNext();
+            case Token.IFEQ:
+            case Token.IFNE:
+                {
+                    Node target = ((Jump) node).target;
+                    visitExpression(child, 0);
+                    addGoto(target, type);
+                    stackChange(-1);
                 }
-                itsInTryFlag = savedFlag;
+                break;
 
-                Node catchTarget = tryNode.target;
-                if (catchTarget != null) {
-                    int catchStartPC
-                        = labelTable[getTargetLabel(catchTarget)];
-                    addExceptionHandler(
-                        tryStart, catchStartPC, catchStartPC,
-                        false, exceptionObjectLocal, scopeLocal);
-                }
-                Node finallyTarget = tryNode.getFinally();
-                if (finallyTarget != null) {
-                    int finallyStartPC
-                        = labelTable[getTargetLabel(finallyTarget)];
-                    addExceptionHandler(
-                        tryStart, finallyStartPC, finallyStartPC,
-                        true, exceptionObjectLocal, scopeLocal);
+            case Token.GOTO:
+                {
+                    Node target = ((Jump) node).target;
+                    addGoto(target, type);
                 }
+                break;
 
-                addIndexOp(Icode_LOCAL_CLEAR, scopeLocal);
-                releaseLocal(scopeLocal);
-            }
-            break;
+            case Token.JSR:
+                {
+                    Node target = ((Jump) node).target;
+                    addGoto(target, Icode_GOSUB);
+                }
+                break;
 
-          case Token.CATCH_SCOPE:
-            {
-                int localIndex = getLocalBlockRef(node);
-                int scopeIndex = node.getExistingIntProp(Node.CATCH_SCOPE_PROP);
-                String name = child.getString();
-                child = child.getNext();
-                visitExpression(child, 0); // load expression object
-                addStringPrefix(name);
-                addIndexPrefix(localIndex);
-                addToken(Token.CATCH_SCOPE);
-                addUint8(scopeIndex != 0 ? 1 : 0);
+            case Token.FINALLY:
+                {
+                    // Account for incomming GOTOSUB address
+                    stackChange(1);
+                    int finallyRegister = getLocalBlockRef(node);
+                    addIndexOp(Icode_STARTSUB, finallyRegister);
+                    stackChange(-1);
+                    while (child != null) {
+                        visitStatement(child, initialStackDepth);
+                        child = child.getNext();
+                    }
+                    addIndexOp(Icode_RETSUB, finallyRegister);
+                }
+                break;
+
+            case Token.EXPR_VOID:
+            case Token.EXPR_RESULT:
+                updateLineNumber(node);
+                visitExpression(child, 0);
+                addIcode((type == Token.EXPR_VOID) ? Icode_POP : Icode_POP_RESULT);
                 stackChange(-1);
-            }
-            break;
+                break;
 
-          case Token.THROW:
-            updateLineNumber(node);
-            visitExpression(child, 0);
-            addToken(Token.THROW);
-            addUint16(lineNumber & 0xFFFF);
-            stackChange(-1);
-            break;
+            case Token.TRY:
+                {
+                    Jump tryNode = (Jump) node;
+                    int exceptionObjectLocal = getLocalBlockRef(tryNode);
+                    int scopeLocal = allocLocal();
+
+                    addIndexOp(Icode_SCOPE_SAVE, scopeLocal);
+
+                    int tryStart = iCodeTop;
+                    boolean savedFlag = itsInTryFlag;
+                    itsInTryFlag = true;
+                    while (child != null) {
+                        visitStatement(child, initialStackDepth);
+                        child = child.getNext();
+                    }
+                    itsInTryFlag = savedFlag;
 
-          case Token.RETHROW:
-            updateLineNumber(node);
-            addIndexOp(Token.RETHROW, getLocalBlockRef(node));
-            break;
-
-          case Token.RETURN:
-            updateLineNumber(node);
-            if (node.getIntProp(Node.GENERATOR_END_PROP, 0) != 0) {
-                // We're in a generator, so change RETURN to GENERATOR_END
-                addIcode(Icode_GENERATOR_END);
+                    Node catchTarget = tryNode.target;
+                    if (catchTarget != null) {
+                        int catchStartPC = labelTable[getTargetLabel(catchTarget)];
+                        addExceptionHandler(
+                                tryStart,
+                                catchStartPC,
+                                catchStartPC,
+                                false,
+                                exceptionObjectLocal,
+                                scopeLocal);
+                    }
+                    Node finallyTarget = tryNode.getFinally();
+                    if (finallyTarget != null) {
+                        int finallyStartPC = labelTable[getTargetLabel(finallyTarget)];
+                        addExceptionHandler(
+                                tryStart,
+                                finallyStartPC,
+                                finallyStartPC,
+                                true,
+                                exceptionObjectLocal,
+                                scopeLocal);
+                    }
+
+                    addIndexOp(Icode_LOCAL_CLEAR, scopeLocal);
+                    releaseLocal(scopeLocal);
+                }
+                break;
+
+            case Token.CATCH_SCOPE:
+                {
+                    int localIndex = getLocalBlockRef(node);
+                    int scopeIndex = node.getExistingIntProp(Node.CATCH_SCOPE_PROP);
+                    String name = child.getString();
+                    child = child.getNext();
+                    visitExpression(child, 0); // load expression object
+                    addStringPrefix(name);
+                    addIndexPrefix(localIndex);
+                    addToken(Token.CATCH_SCOPE);
+                    addUint8(scopeIndex != 0 ? 1 : 0);
+                    stackChange(-1);
+                }
+                break;
+
+            case Token.THROW:
+                updateLineNumber(node);
+                visitExpression(child, 0);
+                addToken(Token.THROW);
                 addUint16(lineNumber & 0xFFFF);
-            } else if (child != null) {
-                visitExpression(child, ECF_TAIL);
-                addToken(Token.RETURN);
                 stackChange(-1);
-            } else {
-                addIcode(Icode_RETUNDEF);
-            }
-            break;
+                break;
 
-          case Token.RETURN_RESULT:
-            updateLineNumber(node);
-            addToken(Token.RETURN_RESULT);
-            break;
+            case Token.RETHROW:
+                updateLineNumber(node);
+                addIndexOp(Token.RETHROW, getLocalBlockRef(node));
+                break;
 
-          case Token.ENUM_INIT_KEYS:
-          case Token.ENUM_INIT_VALUES:
-          case Token.ENUM_INIT_ARRAY:
-          case Token.ENUM_INIT_VALUES_IN_ORDER:
-            visitExpression(child, 0);
-            addIndexOp(type, getLocalBlockRef(node));
-            stackChange(-1);
-            break;
+            case Token.RETURN:
+                updateLineNumber(node);
+                if (node.getIntProp(Node.GENERATOR_END_PROP, 0) != 0) {
+                    if ((child == null)
+                            || (compilerEnv.getLanguageVersion() < Context.VERSION_ES6)) {
+                        // End generator function with no result, or old language version
+                        // in which generators never return a result.
+                        addIcode(Icode_GENERATOR_END);
+                        addUint16(lineNumber & 0xFFFF);
+                    } else {
+                        visitExpression(child, ECF_TAIL);
+                        addIcode(Icode_GENERATOR_RETURN);
+                        addUint16(lineNumber & 0xFFFF);
+                        stackChange(-1);
+                    }
 
-          case Icode_GENERATOR:
-            break;
+                } else {
+                    if (child == null) {
+                        addIcode(Icode_RETUNDEF);
+                    } else {
+                        visitExpression(child, ECF_TAIL);
+                        addToken(Token.RETURN);
+                        stackChange(-1);
+                    }
+                }
+                break;
 
-          default:
-            throw badTree(node);
+            case Token.RETURN_RESULT:
+                updateLineNumber(node);
+                addToken(Token.RETURN_RESULT);
+                break;
+
+            case Token.ENUM_INIT_KEYS:
+            case Token.ENUM_INIT_VALUES:
+            case Token.ENUM_INIT_ARRAY:
+            case Token.ENUM_INIT_VALUES_IN_ORDER:
+                visitExpression(child, 0);
+                addIndexOp(type, getLocalBlockRef(node));
+                stackChange(-1);
+                break;
+
+            case Icode_GENERATOR:
+                break;
+
+            default:
+                throw badTree(node);
         }
 
         if (stackDepth != initialStackDepth) {
@@ -496,577 +550,595 @@
         }
     }
 
-    private void visitExpression(Node node, int contextFlags)
-    {
+    private void visitExpression(Node node, int contextFlags) {
         int type = node.getType();
         Node child = node.getFirstChild();
         int savedStackDepth = stackDepth;
         switch (type) {
+            case Token.FUNCTION:
+                {
+                    int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
+                    FunctionNode fn = scriptOrFn.getFunctionNode(fnIndex);
+                    // See comments in visitStatement for Token.FUNCTION case
+                    if (fn.getFunctionType() != FunctionNode.FUNCTION_EXPRESSION
+                            && fn.getFunctionType() != FunctionNode.ARROW_FUNCTION) {
+                        throw Kit.codeBug();
+                    }
+                    addIndexOp(Icode_CLOSURE_EXPR, fnIndex);
+                    stackChange(1);
+                }
+                break;
 
-          case Token.FUNCTION:
-            {
-                int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
-                FunctionNode fn = scriptOrFn.getFunctionNode(fnIndex);
-                // See comments in visitStatement for Token.FUNCTION case
-                if (fn.getFunctionType() != FunctionNode.FUNCTION_EXPRESSION &&
-                    fn.getFunctionType() != FunctionNode.ARROW_FUNCTION) {
-                    throw Kit.codeBug();
+            case Token.LOCAL_LOAD:
+                {
+                    int localIndex = getLocalBlockRef(node);
+                    addIndexOp(Token.LOCAL_LOAD, localIndex);
+                    stackChange(1);
                 }
-                addIndexOp(Icode_CLOSURE_EXPR, fnIndex);
-                stackChange(1);
-            }
-            break;
+                break;
 
-          case Token.LOCAL_LOAD:
-            {
-                int localIndex = getLocalBlockRef(node);
-                addIndexOp(Token.LOCAL_LOAD, localIndex);
+            case Token.COMMA:
+                {
+                    Node lastChild = node.getLastChild();
+                    while (child != lastChild) {
+                        visitExpression(child, 0);
+                        addIcode(Icode_POP);
+                        stackChange(-1);
+                        child = child.getNext();
+                    }
+                    // Preserve tail context flag if any
+                    visitExpression(child, contextFlags & ECF_TAIL);
+                }
+                break;
+
+            case Token.USE_STACK:
+                // Indicates that stack was modified externally,
+                // like placed catch object
                 stackChange(1);
-            }
-            break;
+                break;
 
-          case Token.COMMA:
-            {
-                Node lastChild = node.getLastChild();
-                while (child != lastChild) {
+            case Token.REF_CALL:
+            case Token.CALL:
+            case Token.NEW:
+                {
+                    if (type == Token.NEW) {
+                        visitExpression(child, 0);
+                    } else {
+                        generateCallFunAndThis(child);
+                    }
+                    int argCount = 0;
+                    while ((child = child.getNext()) != null) {
+                        visitExpression(child, 0);
+                        ++argCount;
+                    }
+                    int callType = node.getIntProp(Node.SPECIALCALL_PROP, Node.NON_SPECIALCALL);
+                    if (type != Token.REF_CALL && callType != Node.NON_SPECIALCALL) {
+                        // embed line number and source filename
+                        addIndexOp(Icode_CALLSPECIAL, argCount);
+                        addUint8(callType);
+                        addUint8(type == Token.NEW ? 1 : 0);
+                        addUint16(lineNumber & 0xFFFF);
+                    } else {
+                        // Only use the tail call optimization if we're not in a try
+                        // or we're not generating debug info (since the
+                        // optimization will confuse the debugger)
+                        if (type == Token.CALL
+                                && (contextFlags & ECF_TAIL) != 0
+                                && !compilerEnv.isGenerateDebugInfo()
+                                && !itsInTryFlag) {
+                            type = Icode_TAIL_CALL;
+                        }
+                        addIndexOp(type, argCount);
+                    }
+                    // adjust stack
+                    if (type == Token.NEW) {
+                        // new: f, args -> result
+                        stackChange(-argCount);
+                    } else {
+                        // call: f, thisObj, args -> result
+                        // ref_call: f, thisObj, args -> ref
+                        stackChange(-1 - argCount);
+                    }
+                    if (argCount > itsData.itsMaxCalleeArgs) {
+                        itsData.itsMaxCalleeArgs = argCount;
+                    }
+                }
+                break;
+
+            case Token.AND:
+            case Token.OR:
+                {
                     visitExpression(child, 0);
+                    addIcode(Icode_DUP);
+                    stackChange(1);
+                    int afterSecondJumpStart = iCodeTop;
+                    int jump = (type == Token.AND) ? Token.IFNE : Token.IFEQ;
+                    addGotoOp(jump);
+                    stackChange(-1);
                     addIcode(Icode_POP);
                     stackChange(-1);
                     child = child.getNext();
+                    // Preserve tail context flag if any
+                    visitExpression(child, contextFlags & ECF_TAIL);
+                    resolveForwardGoto(afterSecondJumpStart);
                 }
-                // Preserve tail context flag if any
-                visitExpression(child, contextFlags & ECF_TAIL);
-            }
-            break;
+                break;
 
-          case Token.USE_STACK:
-            // Indicates that stack was modified externally,
-            // like placed catch object
-            stackChange(1);
-            break;
-
-          case Token.REF_CALL:
-          case Token.CALL:
-          case Token.NEW:
-            {
-                if (type == Token.NEW) {
-                    visitExpression(child, 0);
-                } else {
-                    generateCallFunAndThis(child);
-                }
-                int argCount = 0;
-                while ((child = child.getNext()) != null) {
+            case Token.HOOK:
+                {
+                    Node ifThen = child.getNext();
+                    Node ifElse = ifThen.getNext();
                     visitExpression(child, 0);
-                    ++argCount;
-                }
-                int callType = node.getIntProp(Node.SPECIALCALL_PROP,
-                                               Node.NON_SPECIALCALL);
-                if (type != Token.REF_CALL && callType != Node.NON_SPECIALCALL) {
-                    // embed line number and source filename
-                    addIndexOp(Icode_CALLSPECIAL, argCount);
-                    addUint8(callType);
-                    addUint8(type == Token.NEW ? 1 : 0);
-                    addUint16(lineNumber & 0xFFFF);
-                } else {
-                    // Only use the tail call optimization if we're not in a try
-                    // or we're not generating debug info (since the
-                    // optimization will confuse the debugger)
-                    if (type == Token.CALL && (contextFlags & ECF_TAIL) != 0 &&
-                        !compilerEnv.isGenerateDebugInfo() && !itsInTryFlag)
-                    {
-                        type = Icode_TAIL_CALL;
-                    }
-                    addIndexOp(type, argCount);
-                }
-                // adjust stack
-                if (type == Token.NEW) {
-                    // new: f, args -> result
-                    stackChange(-argCount);
-                } else {
-                    // call: f, thisObj, args -> result
-                    // ref_call: f, thisObj, args -> ref
-                    stackChange(-1 - argCount);
-                }
-                if (argCount > itsData.itsMaxCalleeArgs) {
-                    itsData.itsMaxCalleeArgs = argCount;
+                    int elseJumpStart = iCodeTop;
+                    addGotoOp(Token.IFNE);
+                    stackChange(-1);
+                    // Preserve tail context flag if any
+                    visitExpression(ifThen, contextFlags & ECF_TAIL);
+                    int afterElseJumpStart = iCodeTop;
+                    addGotoOp(Token.GOTO);
+                    resolveForwardGoto(elseJumpStart);
+                    stackDepth = savedStackDepth;
+                    // Preserve tail context flag if any
+                    visitExpression(ifElse, contextFlags & ECF_TAIL);
+                    resolveForwardGoto(afterElseJumpStart);
                 }
-            }
-            break;
+                break;
 
-          case Token.AND:
-          case Token.OR:
-            {
+            case Token.GETPROP:
+            case Token.GETPROPNOWARN:
                 visitExpression(child, 0);
-                addIcode(Icode_DUP);
-                stackChange(1);
-                int afterSecondJumpStart = iCodeTop;
-                int jump = (type == Token.AND) ? Token.IFNE : Token.IFEQ;
-                addGotoOp(jump);
-                stackChange(-1);
-                addIcode(Icode_POP);
-                stackChange(-1);
                 child = child.getNext();
-                // Preserve tail context flag if any
-                visitExpression(child, contextFlags & ECF_TAIL);
-                resolveForwardGoto(afterSecondJumpStart);
-            }
-            break;
+                addStringOp(type, child.getString());
+                break;
 
-          case Token.HOOK:
-            {
-                Node ifThen = child.getNext();
-                Node ifElse = ifThen.getNext();
+            case Token.DELPROP:
+                boolean isName = child.getType() == Token.BINDNAME;
                 visitExpression(child, 0);
-                int elseJumpStart = iCodeTop;
-                addGotoOp(Token.IFNE);
+                child = child.getNext();
+                visitExpression(child, 0);
+                if (isName) {
+                    // special handling for delete name
+                    addIcode(Icode_DELNAME);
+                } else {
+                    addToken(Token.DELPROP);
+                }
                 stackChange(-1);
-                // Preserve tail context flag if any
-                visitExpression(ifThen, contextFlags & ECF_TAIL);
-                int afterElseJumpStart = iCodeTop;
-                addGotoOp(Token.GOTO);
-                resolveForwardGoto(elseJumpStart);
-                stackDepth = savedStackDepth;
-                // Preserve tail context flag if any
-                visitExpression(ifElse, contextFlags & ECF_TAIL);
-                resolveForwardGoto(afterElseJumpStart);
-            }
-            break;
-
-          case Token.GETPROP:
-          case Token.GETPROPNOWARN:
-            visitExpression(child, 0);
-            child = child.getNext();
-            addStringOp(type, child.getString());
-            break;
+                break;
 
-          case Token.DELPROP:
-            boolean isName = child.getType() == Token.BINDNAME;
-            visitExpression(child, 0);
-            child = child.getNext();
-            visitExpression(child, 0);
-            if (isName) {
-                // special handling for delete name
-                addIcode(Icode_DELNAME);
-            } else {
-                addToken(Token.DELPROP);
-            }
-            stackChange(-1);
-            break;
+            case Token.GETELEM:
+            case Token.BITAND:
+            case Token.BITOR:
+            case Token.BITXOR:
+            case Token.LSH:
+            case Token.RSH:
+            case Token.URSH:
+            case Token.ADD:
+            case Token.SUB:
+            case Token.MOD:
+            case Token.DIV:
+            case Token.MUL:
+            case Token.EXP:
+            case Token.EQ:
+            case Token.NE:
+            case Token.SHEQ:
+            case Token.SHNE:
+            case Token.IN:
+            case Token.INSTANCEOF:
+            case Token.LE:
+            case Token.LT:
+            case Token.GE:
+            case Token.GT:
+                visitExpression(child, 0);
+                child = child.getNext();
+                visitExpression(child, 0);
+                addToken(type);
+                stackChange(-1);
+                break;
 
-          case Token.GETELEM:
-          case Token.BITAND:
-          case Token.BITOR:
-          case Token.BITXOR:
-          case Token.LSH:
-          case Token.RSH:
-          case Token.URSH:
-          case Token.ADD:
-          case Token.SUB:
-          case Token.MOD:
-          case Token.DIV:
-          case Token.MUL:
-          case Token.EQ:
-          case Token.NE:
-          case Token.SHEQ:
-          case Token.SHNE:
-          case Token.IN:
-          case Token.INSTANCEOF:
-          case Token.LE:
-          case Token.LT:
-          case Token.GE:
-          case Token.GT:
-            visitExpression(child, 0);
-            child = child.getNext();
-            visitExpression(child, 0);
-            addToken(type);
-            stackChange(-1);
-            break;
+            case Token.POS:
+            case Token.NEG:
+            case Token.NOT:
+            case Token.BITNOT:
+            case Token.TYPEOF:
+            case Token.VOID:
+                visitExpression(child, 0);
+                if (type == Token.VOID) {
+                    addIcode(Icode_POP);
+                    addIcode(Icode_UNDEF);
+                } else {
+                    addToken(type);
+                }
+                break;
 
-          case Token.POS:
-          case Token.NEG:
-          case Token.NOT:
-          case Token.BITNOT:
-          case Token.TYPEOF:
-          case Token.VOID:
-            visitExpression(child, 0);
-            if (type == Token.VOID) {
-                addIcode(Icode_POP);
-                addIcode(Icode_UNDEF);
-            } else {
+            case Token.GET_REF:
+            case Token.DEL_REF:
+                visitExpression(child, 0);
                 addToken(type);
-            }
-            break;
+                break;
 
-          case Token.GET_REF:
-          case Token.DEL_REF:
-            visitExpression(child, 0);
-            addToken(type);
-            break;
-
-          case Token.SETPROP:
-          case Token.SETPROP_OP:
-            {
+            case Token.SETPROP:
+            case Token.SETPROP_OP:
+                {
+                    visitExpression(child, 0);
+                    child = child.getNext();
+                    String property = child.getString();
+                    child = child.getNext();
+                    if (type == Token.SETPROP_OP) {
+                        addIcode(Icode_DUP);
+                        stackChange(1);
+                        addStringOp(Token.GETPROP, property);
+                        // Compensate for the following USE_STACK
+                        stackChange(-1);
+                    }
+                    visitExpression(child, 0);
+                    addStringOp(Token.SETPROP, property);
+                    stackChange(-1);
+                }
+                break;
+
+            case Token.SETELEM:
+            case Token.SETELEM_OP:
                 visitExpression(child, 0);
                 child = child.getNext();
-                String property = child.getString();
+                visitExpression(child, 0);
                 child = child.getNext();
-                if (type == Token.SETPROP_OP) {
-                    addIcode(Icode_DUP);
-                    stackChange(1);
-                    addStringOp(Token.GETPROP, property);
+                if (type == Token.SETELEM_OP) {
+                    addIcode(Icode_DUP2);
+                    stackChange(2);
+                    addToken(Token.GETELEM);
+                    stackChange(-1);
                     // Compensate for the following USE_STACK
                     stackChange(-1);
                 }
                 visitExpression(child, 0);
-                addStringOp(Token.SETPROP, property);
-                stackChange(-1);
-            }
-            break;
-
-          case Token.SETELEM:
-          case Token.SETELEM_OP:
-            visitExpression(child, 0);
-            child = child.getNext();
-            visitExpression(child, 0);
-            child = child.getNext();
-            if (type == Token.SETELEM_OP) {
-                addIcode(Icode_DUP2);
-                stackChange(2);
-                addToken(Token.GETELEM);
-                stackChange(-1);
-                // Compensate for the following USE_STACK
-                stackChange(-1);
-            }
-            visitExpression(child, 0);
-            addToken(Token.SETELEM);
-            stackChange(-2);
-            break;
-
-          case Token.SET_REF:
-          case Token.SET_REF_OP:
-            visitExpression(child, 0);
-            child = child.getNext();
-            if (type == Token.SET_REF_OP) {
-                addIcode(Icode_DUP);
-                stackChange(1);
-                addToken(Token.GET_REF);
-                // Compensate for the following USE_STACK
-                stackChange(-1);
-            }
-            visitExpression(child, 0);
-            addToken(Token.SET_REF);
-            stackChange(-1);
-            break;
+                addToken(Token.SETELEM);
+                stackChange(-2);
+                break;
 
-          case Token.STRICT_SETNAME:
-          case Token.SETNAME:
-            {
-                String name = child.getString();
+            case Token.SET_REF:
+            case Token.SET_REF_OP:
                 visitExpression(child, 0);
                 child = child.getNext();
+                if (type == Token.SET_REF_OP) {
+                    addIcode(Icode_DUP);
+                    stackChange(1);
+                    addToken(Token.GET_REF);
+                    // Compensate for the following USE_STACK
+                    stackChange(-1);
+                }
                 visitExpression(child, 0);
-                addStringOp(type, name);
+                addToken(Token.SET_REF);
                 stackChange(-1);
-            }
-            break;
+                break;
 
-          case Token.SETCONST:
-            {
-                String name = child.getString();
-                visitExpression(child, 0);
-                child = child.getNext();
-                visitExpression(child, 0);
-                addStringOp(Icode_SETCONST, name);
-                stackChange(-1);
-            }
-            break;
+            case Token.STRICT_SETNAME:
+            case Token.SETNAME:
+                {
+                    String name = child.getString();
+                    visitExpression(child, 0);
+                    child = child.getNext();
+                    visitExpression(child, 0);
+                    addStringOp(type, name);
+                    stackChange(-1);
+                }
+                break;
 
-          case Token.TYPEOFNAME:
-            {
-                int index = -1;
-                // use typeofname if an activation frame exists
-                // since the vars all exist there instead of in jregs
-                if (itsInFunctionFlag && !itsData.itsNeedsActivation)
-                    index = scriptOrFn.getIndexForNameNode(node);
-                if (index == -1) {
-                    addStringOp(Icode_TYPEOFNAME, node.getString());
-                    stackChange(1);
-                } else {
-                    addVarOp(Token.GETVAR, index);
-                    stackChange(1);
-                    addToken(Token.TYPEOF);
+            case Token.SETCONST:
+                {
+                    String name = child.getString();
+                    visitExpression(child, 0);
+                    child = child.getNext();
+                    visitExpression(child, 0);
+                    addStringOp(Icode_SETCONST, name);
+                    stackChange(-1);
                 }
-            }
-            break;
+                break;
 
-          case Token.BINDNAME:
-          case Token.NAME:
-          case Token.STRING:
-            addStringOp(type, node.getString());
-            stackChange(1);
-            break;
-
-          case Token.INC:
-          case Token.DEC:
-            visitIncDec(node, child);
-            break;
-
-          case Token.NUMBER:
-            {
-                double num = node.getDouble();
-                int inum = (int)num;
-                if (inum == num) {
-                    if (inum == 0) {
-                        addIcode(Icode_ZERO);
-                        // Check for negative zero
-                        if (1.0 / num < 0.0) {
-                            addToken(Token.NEG);
+            case Token.TYPEOFNAME:
+                {
+                    int index = -1;
+                    // use typeofname if an activation frame exists
+                    // since the vars all exist there instead of in jregs
+                    if (itsInFunctionFlag && !itsData.itsNeedsActivation)
+                        index = scriptOrFn.getIndexForNameNode(node);
+                    if (index == -1) {
+                        addStringOp(Icode_TYPEOFNAME, node.getString());
+                        stackChange(1);
+                    } else {
+                        addVarOp(Token.GETVAR, index);
+                        stackChange(1);
+                        addToken(Token.TYPEOF);
+                    }
+                }
+                break;
+
+            case Token.BINDNAME:
+            case Token.NAME:
+            case Token.STRING:
+                addStringOp(type, node.getString());
+                stackChange(1);
+                break;
+
+            case Token.INC:
+            case Token.DEC:
+                visitIncDec(node, child);
+                break;
+
+            case Token.NUMBER:
+                {
+                    double num = node.getDouble();
+                    int inum = (int) num;
+                    if (inum == num) {
+                        if (inum == 0) {
+                            addIcode(Icode_ZERO);
+                            // Check for negative zero
+                            if (1.0 / num < 0.0) {
+                                addToken(Token.NEG);
+                            }
+                        } else if (inum == 1) {
+                            addIcode(Icode_ONE);
+                        } else if ((short) inum == inum) {
+                            addIcode(Icode_SHORTNUMBER);
+                            // write short as uin16 bit pattern
+                            addUint16(inum & 0xFFFF);
+                        } else {
+                            addIcode(Icode_INTNUMBER);
+                            addInt(inum);
                         }
-                    } else if (inum == 1) {
-                        addIcode(Icode_ONE);
-                    } else if ((short)inum == inum) {
-                        addIcode(Icode_SHORTNUMBER);
-                        // write short as uin16 bit pattern
-                        addUint16(inum & 0xFFFF);
                     } else {
-                        addIcode(Icode_INTNUMBER);
-                        addInt(inum);
+                        int index = getDoubleIndex(num);
+                        addIndexOp(Token.NUMBER, index);
                     }
-                } else {
-                    int index = getDoubleIndex(num);
-                    addIndexOp(Token.NUMBER, index);
+                    stackChange(1);
+                }
+                break;
+
+            case Token.GETVAR:
+                {
+                    if (itsData.itsNeedsActivation) Kit.codeBug();
+                    int index = scriptOrFn.getIndexForNameNode(node);
+                    addVarOp(Token.GETVAR, index);
+                    stackChange(1);
+                }
+                break;
+
+            case Token.SETVAR:
+                {
+                    if (itsData.itsNeedsActivation) Kit.codeBug();
+                    int index = scriptOrFn.getIndexForNameNode(child);
+                    child = child.getNext();
+                    visitExpression(child, 0);
+                    addVarOp(Token.SETVAR, index);
+                }
+                break;
+
+            case Token.SETCONSTVAR:
+                {
+                    if (itsData.itsNeedsActivation) Kit.codeBug();
+                    int index = scriptOrFn.getIndexForNameNode(child);
+                    child = child.getNext();
+                    visitExpression(child, 0);
+                    addVarOp(Token.SETCONSTVAR, index);
                 }
+                break;
+
+            case Token.NULL:
+            case Token.THIS:
+            case Token.THISFN:
+            case Token.FALSE:
+            case Token.TRUE:
+                addToken(type);
                 stackChange(1);
-            }
-            break;
+                break;
 
-          case Token.GETVAR:
-            {
-                if (itsData.itsNeedsActivation) Kit.codeBug();
-                int index = scriptOrFn.getIndexForNameNode(node);
-                addVarOp(Token.GETVAR, index);
+            case Token.ENUM_NEXT:
+            case Token.ENUM_ID:
+                addIndexOp(type, getLocalBlockRef(node));
                 stackChange(1);
-            }
-            break;
+                break;
 
-          case Token.SETVAR:
-            {
-                if (itsData.itsNeedsActivation) Kit.codeBug();
-                int index = scriptOrFn.getIndexForNameNode(child);
-                child = child.getNext();
-                visitExpression(child, 0);
-                addVarOp(Token.SETVAR, index);
-            }
-            break;
+            case Token.BIGINT:
+                addBigInt(node.getBigInt());
+                stackChange(1);
+                break;
 
-          case Token.SETCONSTVAR:
-            {
-                if (itsData.itsNeedsActivation) Kit.codeBug();
-                int index = scriptOrFn.getIndexForNameNode(child);
-                child = child.getNext();
+            case Token.REGEXP:
+                {
+                    int index = node.getExistingIntProp(Node.REGEXP_PROP);
+                    addIndexOp(Token.REGEXP, index);
+                    stackChange(1);
+                }
+                break;
+
+            case Token.ARRAYLIT:
+            case Token.OBJECTLIT:
+                visitLiteral(node, child);
+                break;
+
+            case Token.ARRAYCOMP:
+                visitArrayComprehension(node, child, child.getNext());
+                break;
+
+            case Token.REF_SPECIAL:
                 visitExpression(child, 0);
-                addVarOp(Token.SETCONSTVAR, index);
-            }
-            break;
+                addStringOp(type, (String) node.getProp(Node.NAME_PROP));
+                break;
 
-          case Token.NULL:
-          case Token.THIS:
-          case Token.THISFN:
-          case Token.FALSE:
-          case Token.TRUE:
-            addToken(type);
-            stackChange(1);
-            break;
-
-          case Token.ENUM_NEXT:
-          case Token.ENUM_ID:
-            addIndexOp(type, getLocalBlockRef(node));
-            stackChange(1);
-            break;
-
-          case Token.REGEXP:
-            {
-                int index = node.getExistingIntProp(Node.REGEXP_PROP);
-                addIndexOp(Token.REGEXP, index);
-                stackChange(1);
-            }
-            break;
+            case Token.REF_MEMBER:
+            case Token.REF_NS_MEMBER:
+            case Token.REF_NAME:
+            case Token.REF_NS_NAME:
+                {
+                    int memberTypeFlags = node.getIntProp(Node.MEMBER_TYPE_PROP, 0);
+                    // generate possible target, possible namespace and member
+                    int childCount = 0;
+                    do {
+                        visitExpression(child, 0);
+                        ++childCount;
+                        child = child.getNext();
+                    } while (child != null);
+                    addIndexOp(type, memberTypeFlags);
+                    stackChange(1 - childCount);
+                }
+                break;
 
-          case Token.ARRAYLIT:
-          case Token.OBJECTLIT:
-            visitLiteral(node, child);
-            break;
-
-          case Token.ARRAYCOMP:
-            visitArrayComprehension(node, child, child.getNext());
-            break;
-
-          case Token.REF_SPECIAL:
-            visitExpression(child, 0);
-            addStringOp(type, (String)node.getProp(Node.NAME_PROP));
-            break;
-
-          case Token.REF_MEMBER:
-          case Token.REF_NS_MEMBER:
-          case Token.REF_NAME:
-          case Token.REF_NS_NAME:
-            {
-                int memberTypeFlags = node.getIntProp(Node.MEMBER_TYPE_PROP, 0);
-                // generate possible target, possible namespace and member
-                int childCount = 0;
-                do {
+            case Token.DOTQUERY:
+                {
+                    int queryPC;
+                    updateLineNumber(node);
                     visitExpression(child, 0);
-                    ++childCount;
-                    child = child.getNext();
-                } while (child != null);
-                addIndexOp(type, memberTypeFlags);
-                stackChange(1 - childCount);
-            }
-            break;
+                    addIcode(Icode_ENTERDQ);
+                    stackChange(-1);
+                    queryPC = iCodeTop;
+                    visitExpression(child.getNext(), 0);
+                    addBackwardGoto(Icode_LEAVEDQ, queryPC);
+                }
+                break;
 
-          case Token.DOTQUERY:
-            {
-                int queryPC;
-                updateLineNumber(node);
+            case Token.DEFAULTNAMESPACE:
+            case Token.ESCXMLATTR:
+            case Token.ESCXMLTEXT:
                 visitExpression(child, 0);
-                addIcode(Icode_ENTERDQ);
-                stackChange(-1);
-                queryPC = iCodeTop;
-                visitExpression(child.getNext(), 0);
-                addBackwardGoto(Icode_LEAVEDQ, queryPC);
-            }
-            break;
+                addToken(type);
+                break;
+
+            case Token.YIELD:
+            case Token.YIELD_STAR:
+                if (child != null) {
+                    visitExpression(child, 0);
+                } else {
+                    addIcode(Icode_UNDEF);
+                    stackChange(1);
+                }
+                if (type == Token.YIELD) {
+                    addToken(Token.YIELD);
+                } else {
+                    addIcode(Icode_YIELD_STAR);
+                }
+                addUint16(node.getLineno() & 0xFFFF);
+                break;
 
-          case Token.DEFAULTNAMESPACE :
-          case Token.ESCXMLATTR :
-          case Token.ESCXMLTEXT :
-            visitExpression(child, 0);
-            addToken(type);
-            break;
+            case Token.WITHEXPR:
+                {
+                    Node enterWith = node.getFirstChild();
+                    Node with = enterWith.getNext();
+                    visitExpression(enterWith.getFirstChild(), 0);
+                    addToken(Token.ENTERWITH);
+                    stackChange(-1);
+                    visitExpression(with.getFirstChild(), 0);
+                    addToken(Token.LEAVEWITH);
+                    break;
+                }
 
-          case Token.YIELD:
-            if (child != null) {
-                visitExpression(child, 0);
-            } else {
-                addIcode(Icode_UNDEF);
-                stackChange(1);
-            }
-            addToken(Token.YIELD);
-            addUint16(node.getLineno() & 0xFFFF);
-            break;
-
-          case Token.WITHEXPR: {
-            Node enterWith = node.getFirstChild();
-            Node with = enterWith.getNext();
-            visitExpression(enterWith.getFirstChild(), 0);
-            addToken(Token.ENTERWITH);
-            stackChange(-1);
-            visitExpression(with.getFirstChild(), 0);
-            addToken(Token.LEAVEWITH);
-            break;
-          }
+            case Token.TEMPLATE_LITERAL:
+                visitTemplateLiteral(node);
+                break;
 
-          default:
-            throw badTree(node);
+            default:
+                throw badTree(node);
         }
         if (savedStackDepth + 1 != stackDepth) {
             Kit.codeBug();
         }
     }
 
-    private void generateCallFunAndThis(Node left)
-    {
+    private void generateCallFunAndThis(Node left) {
         // Generate code to place on stack function and thisObj
         int type = left.getType();
         switch (type) {
-          case Token.NAME: {
-            String name = left.getString();
-            // stack: ... -> ... function thisObj
-            addStringOp(Icode_NAME_AND_THIS, name);
-            stackChange(2);
-            break;
-          }
-          case Token.GETPROP:
-          case Token.GETELEM: {
-            Node target = left.getFirstChild();
-            visitExpression(target, 0);
-            Node id = target.getNext();
-            if (type == Token.GETPROP) {
-                String property = id.getString();
-                // stack: ... target -> ... function thisObj
-                addStringOp(Icode_PROP_AND_THIS, property);
+            case Token.NAME:
+                {
+                    String name = left.getString();
+                    // stack: ... -> ... function thisObj
+                    addStringOp(Icode_NAME_AND_THIS, name);
+                    stackChange(2);
+                    break;
+                }
+            case Token.GETPROP:
+            case Token.GETELEM:
+                {
+                    Node target = left.getFirstChild();
+                    visitExpression(target, 0);
+                    Node id = target.getNext();
+                    if (type == Token.GETPROP) {
+                        String property = id.getString();
+                        // stack: ... target -> ... function thisObj
+                        addStringOp(Icode_PROP_AND_THIS, property);
+                        stackChange(1);
+                    } else {
+                        visitExpression(id, 0);
+                        // stack: ... target id -> ... function thisObj
+                        addIcode(Icode_ELEM_AND_THIS);
+                    }
+                    break;
+                }
+            default:
+                // Including Token.GETVAR
+                visitExpression(left, 0);
+                // stack: ... value -> ... function thisObj
+                addIcode(Icode_VALUE_AND_THIS);
                 stackChange(1);
-            } else {
-                visitExpression(id, 0);
-                // stack: ... target id -> ... function thisObj
-                addIcode(Icode_ELEM_AND_THIS);
-            }
-            break;
-          }
-          default:
-            // Including Token.GETVAR
-            visitExpression(left, 0);
-            // stack: ... value -> ... function thisObj
-            addIcode(Icode_VALUE_AND_THIS);
-            stackChange(1);
-            break;
+                break;
         }
     }
 
-
-    private void visitIncDec(Node node, Node child)
-    {
+    private void visitIncDec(Node node, Node child) {
         int incrDecrMask = node.getExistingIntProp(Node.INCRDECR_PROP);
         int childType = child.getType();
         switch (childType) {
-          case Token.GETVAR : {
-            if (itsData.itsNeedsActivation) Kit.codeBug();
-            int i = scriptOrFn.getIndexForNameNode(child);
-            addVarOp(Icode_VAR_INC_DEC, i);
-            addUint8(incrDecrMask);
-            stackChange(1);
-            break;
-          }
-          case Token.NAME : {
-            String name = child.getString();
-            addStringOp(Icode_NAME_INC_DEC, name);
-            addUint8(incrDecrMask);
-            stackChange(1);
-            break;
-          }
-          case Token.GETPROP : {
-            Node object = child.getFirstChild();
-            visitExpression(object, 0);
-            String property = object.getNext().getString();
-            addStringOp(Icode_PROP_INC_DEC, property);
-            addUint8(incrDecrMask);
-            break;
-          }
-          case Token.GETELEM : {
-            Node object = child.getFirstChild();
-            visitExpression(object, 0);
-            Node index = object.getNext();
-            visitExpression(index, 0);
-            addIcode(Icode_ELEM_INC_DEC);
-            addUint8(incrDecrMask);
-            stackChange(-1);
-            break;
-          }
-          case Token.GET_REF : {
-            Node ref = child.getFirstChild();
-            visitExpression(ref, 0);
-            addIcode(Icode_REF_INC_DEC);
-            addUint8(incrDecrMask);
-            break;
-          }
-          default : {
-            throw badTree(node);
-          }
+            case Token.GETVAR:
+                {
+                    if (itsData.itsNeedsActivation) Kit.codeBug();
+                    int i = scriptOrFn.getIndexForNameNode(child);
+                    addVarOp(Icode_VAR_INC_DEC, i);
+                    addUint8(incrDecrMask);
+                    stackChange(1);
+                    break;
+                }
+            case Token.NAME:
+                {
+                    String name = child.getString();
+                    addStringOp(Icode_NAME_INC_DEC, name);
+                    addUint8(incrDecrMask);
+                    stackChange(1);
+                    break;
+                }
+            case Token.GETPROP:
+                {
+                    Node object = child.getFirstChild();
+                    visitExpression(object, 0);
+                    String property = object.getNext().getString();
+                    addStringOp(Icode_PROP_INC_DEC, property);
+                    addUint8(incrDecrMask);
+                    break;
+                }
+            case Token.GETELEM:
+                {
+                    Node object = child.getFirstChild();
+                    visitExpression(object, 0);
+                    Node index = object.getNext();
+                    visitExpression(index, 0);
+                    addIcode(Icode_ELEM_INC_DEC);
+                    addUint8(incrDecrMask);
+                    stackChange(-1);
+                    break;
+                }
+            case Token.GET_REF:
+                {
+                    Node ref = child.getFirstChild();
+                    visitExpression(ref, 0);
+                    addIcode(Icode_REF_INC_DEC);
+                    addUint8(incrDecrMask);
+                    break;
+                }
+            default:
+                {
+                    throw badTree(node);
+                }
         }
     }
 
-    private void visitLiteral(Node node, Node child)
-    {
+    private void visitLiteral(Node node, Node child) {
         int type = node.getType();
         int count;
         Object[] propertyIds = null;
@@ -1076,8 +1148,8 @@
                 ++count;
             }
         } else if (type == Token.OBJECTLIT) {
-            propertyIds = (Object[])node.getProp(Node.OBJECT_IDS_PROP);
-            count = propertyIds.length;
+            propertyIds = (Object[]) node.getProp(Node.OBJECT_IDS_PROP);
+            count = propertyIds == null ? 0 : propertyIds.length;
         } else {
             throw badTree(node);
         }
@@ -1102,7 +1174,7 @@
             child = child.getNext();
         }
         if (type == Token.ARRAYLIT) {
-            int[] skipIndexes = (int[])node.getProp(Node.SKIP_INDEXES_PROP);
+            int[] skipIndexes = (int[]) node.getProp(Node.SKIP_INDEXES_PROP);
             if (skipIndexes == null) {
                 addToken(Token.ARRAYLIT);
             } else {
@@ -1118,8 +1190,13 @@
         stackChange(-1);
     }
 
-    private void visitArrayComprehension(Node node, Node initStmt, Node expr)
-    {
+    private void visitTemplateLiteral(Node node) {
+        int index = node.getExistingIntProp(Node.TEMPLATE_LITERAL_PROP);
+        addIndexOp(Icode_TEMPLATE_LITERAL_CALLSITE, index);
+        stackChange(1);
+    }
+
+    private void visitArrayComprehension(Node node, Node initStmt, Node expr) {
         // A bit of a hack: array comprehensions are implemented using
         // statement nodes for the iteration, yet they appear in an
         // expression context. So we pass the current stack depth to
@@ -1129,14 +1206,12 @@
         visitExpression(expr, 0);
     }
 
-    private int getLocalBlockRef(Node node)
-    {
-        Node localBlock = (Node)node.getProp(Node.LOCAL_BLOCK_PROP);
+    private static int getLocalBlockRef(Node node) {
+        Node localBlock = (Node) node.getProp(Node.LOCAL_BLOCK_PROP);
         return localBlock.getExistingIntProp(Node.LOCAL_PROP);
     }
 
-    private int getTargetLabel(Node target)
-    {
+    private int getTargetLabel(Node target) {
         int label = target.labelId();
         if (label != -1) {
             return label;
@@ -1145,7 +1220,7 @@
         if (labelTable == null || label == labelTable.length) {
             if (labelTable == null) {
                 labelTable = new int[MIN_LABEL_TABLE_SIZE];
-            }else {
+            } else {
                 int[] tmp = new int[labelTable.length * 2];
                 System.arraycopy(labelTable, 0, tmp, 0, label);
                 labelTable = tmp;
@@ -1158,8 +1233,7 @@
         return label;
     }
 
-    private void markTargetLabel(Node target)
-    {
+    private void markTargetLabel(Node target) {
         int label = getTargetLabel(target);
         if (labelTable[label] != -1) {
             // Can mark label only once
@@ -1168,8 +1242,7 @@
         labelTable[label] = iCodeTop;
     }
 
-    private void addGoto(Node target, int gotoOp)
-    {
+    private void addGoto(Node target, int gotoOp) {
         int label = getTargetLabel(target);
         if (!(label < labelTableTop)) Kit.codeBug();
         int targetPC = labelTable[label];
@@ -1190,16 +1263,15 @@
                 }
             }
             fixupTableTop = top + 1;
-            fixupTable[top] = ((long)label << 32) | gotoPC;
+            fixupTable[top] = ((long) label << 32) | gotoPC;
         }
     }
 
-    private void fixLabelGotos()
-    {
+    private void fixLabelGotos() {
         for (int i = 0; i < fixupTableTop; i++) {
             long fixup = fixupTable[i];
-            int label = (int)(fixup >> 32);
-            int jumpSource = (int)fixup;
+            int label = (int) (fixup >> 32);
+            int jumpSource = (int) fixup;
             int pc = labelTable[label];
             if (pc == -1) {
                 // Unlocated label
@@ -1210,8 +1282,7 @@
         fixupTableTop = 0;
     }
 
-    private void addBackwardGoto(int gotoOp, int jumpPC)
-    {
+    private void addBackwardGoto(int gotoOp, int jumpPC) {
         int fromPC = iCodeTop;
         // Ensure that this is a jump backward
         if (fromPC <= jumpPC) throw Kit.codeBug();
@@ -1219,20 +1290,18 @@
         resolveGoto(fromPC, jumpPC);
     }
 
-    private void resolveForwardGoto(int fromPC)
-    {
+    private void resolveForwardGoto(int fromPC) {
         // Ensure that forward jump skips at least self bytecode
         if (iCodeTop < fromPC + 3) throw Kit.codeBug();
         resolveGoto(fromPC, iCodeTop);
     }
 
-    private void resolveGoto(int fromPC, int jumpPC)
-    {
+    private void resolveGoto(int fromPC, int jumpPC) {
         int offset = jumpPC - fromPC;
         // Ensure that jumps do not overlap
         if (0 <= offset && offset <= 2) throw Kit.codeBug();
         int offsetSite = fromPC + 1;
-        if (offset != (short)offset) {
+        if (offset != (short) offset) {
             if (itsData.longJumps == null) {
                 itsData.longJumps = new UintMap();
             }
@@ -1240,64 +1309,58 @@
             offset = 0;
         }
         byte[] array = itsData.itsICode;
-        array[offsetSite] = (byte)(offset >> 8);
-        array[offsetSite + 1] = (byte)offset;
+        array[offsetSite] = (byte) (offset >> 8);
+        array[offsetSite + 1] = (byte) offset;
     }
 
-    private void addToken(int token)
-    {
+    private void addToken(int token) {
         if (!Icode.validTokenCode(token)) throw Kit.codeBug();
         addUint8(token);
     }
 
-    private void addIcode(int icode)
-    {
+    private void addIcode(int icode) {
         if (!Icode.validIcode(icode)) throw Kit.codeBug();
         // Write negative icode as uint8 bits
         addUint8(icode & 0xFF);
     }
 
-    private void addUint8(int value)
-    {
+    private void addUint8(int value) {
         if ((value & ~0xFF) != 0) throw Kit.codeBug();
         byte[] array = itsData.itsICode;
         int top = iCodeTop;
         if (top == array.length) {
             array = increaseICodeCapacity(1);
         }
-        array[top] = (byte)value;
+        array[top] = (byte) value;
         iCodeTop = top + 1;
     }
 
-    private void addUint16(int value)
-    {
+    private void addUint16(int value) {
         if ((value & ~0xFFFF) != 0) throw Kit.codeBug();
         byte[] array = itsData.itsICode;
         int top = iCodeTop;
         if (top + 2 > array.length) {
             array = increaseICodeCapacity(2);
         }
-        array[top] = (byte)(value >>> 8);
-        array[top + 1] = (byte)value;
+        array[top] = (byte) (value >>> 8);
+        array[top + 1] = (byte) value;
         iCodeTop = top + 2;
     }
 
-    private void addInt(int i)
-    {
+    private void addInt(int i) {
         byte[] array = itsData.itsICode;
         int top = iCodeTop;
         if (top + 4 > array.length) {
             array = increaseICodeCapacity(4);
         }
-        array[top] = (byte)(i >>> 24);
-        array[top + 1] = (byte)(i >>> 16);
-        array[top + 2] = (byte)(i >>> 8);
-        array[top + 3] = (byte)i;
+        array[top] = (byte) (i >>> 24);
+        array[top + 1] = (byte) (i >>> 16);
+        array[top + 2] = (byte) (i >>> 8);
+        array[top + 3] = (byte) i;
         iCodeTop = top + 4;
     }
 
-    private int getDoubleIndex(double num)
-    {
+    private int getDoubleIndex(double num) {
         int index = doubleTableTop;
         if (index == 0) {
             itsData.itsDoubleTable = new double[64];
@@ -1311,46 +1374,43 @@
         return index;
     }
 
-    private void addGotoOp(int gotoOp)
-    {
+    private void addGotoOp(int gotoOp) {
         byte[] array = itsData.itsICode;
         int top = iCodeTop;
         if (top + 3 > array.length) {
             array = increaseICodeCapacity(3);
         }
-        array[top] = (byte)gotoOp;
+        array[top] = (byte) gotoOp;
         // Offset would written later
         iCodeTop = top + 1 + 2;
     }
 
-    private void addVarOp(int op, int varIndex)
-    {
+    private void addVarOp(int op, int varIndex) {
         switch (op) {
-          case Token.SETCONSTVAR:
-            if (varIndex < 128) {
-                addIcode(Icode_SETCONSTVAR1);
-                addUint8(varIndex);
+            case Token.SETCONSTVAR:
+                if (varIndex < 128) {
+                    addIcode(Icode_SETCONSTVAR1);
+                    addUint8(varIndex);
+                    return;
+                }
+                addIndexOp(Icode_SETCONSTVAR, varIndex);
                 return;
-            }
-            addIndexOp(Icode_SETCONSTVAR, varIndex);
-            return;
-          case Token.GETVAR:
-          case Token.SETVAR:
-            if (varIndex < 128) {
-                addIcode(op == Token.GETVAR ? Icode_GETVAR1 : Icode_SETVAR1);
-                addUint8(varIndex);
+            case Token.GETVAR:
+            case Token.SETVAR:
+                if (varIndex < 128) {
+                    addIcode(op == Token.GETVAR ? Icode_GETVAR1 : Icode_SETVAR1);
+                    addUint8(varIndex);
+                    return;
+                }
+                // fallthrough
+            case Icode_VAR_INC_DEC:
+                addIndexOp(op, varIndex);
                 return;
-            }
-            // fallthrough
-          case Icode_VAR_INC_DEC:
-            addIndexOp(op, varIndex);
-            return;
         }
         throw Kit.codeBug();
     }
 
-    private void addStringOp(int op, String str)
-    {
+    private void addStringOp(int op, String str) {
         addStringPrefix(str);
         if (Icode.validIcode(op)) {
             addIcode(op);
@@ -1359,8 +1419,7 @@
         }
     }
 
-    private void addIndexOp(int op, int index)
-    {
+    private void addIndexOp(int op, int index) {
         addIndexPrefix(index);
         if (Icode.validIcode(op)) {
             addIcode(op);
@@ -1369,8 +1428,7 @@
         }
     }
 
-    private void addStringPrefix(String str)
-    {
+    private void addStringPrefix(String str) {
         int index = strings.get(str, -1);
         if (index == -1) {
             index = strings.size();
@@ -1381,36 +1439,59 @@
         } else if (index <= 0xFF) {
             addIcode(Icode_REG_STR1);
             addUint8(index);
-         } else if (index <= 0xFFFF) {
+        } else if (index <= 0xFFFF) {
             addIcode(Icode_REG_STR2);
             addUint16(index);
-         } else {
+        } else {
             addIcode(Icode_REG_STR4);
             addInt(index);
         }
     }
 
-    private void addIndexPrefix(int index)
-    {
+    private void addBigInt(BigInteger n) {
+        int index = bigInts.get(n, -1);
+        if (index == -1) {
+            index = bigInts.size();
+            bigInts.put(n, index);
+        }
+        if (index < 4) {
+            addIcode(Icode_REG_BIGINT_C0 - index);
+        } else if (index <= 0xFF) {
+            addIcode(Icode_REG_BIGINT1);
+            addUint8(index);
+        } else if (index <= 0xFFFF) {
+            addIcode(Icode_REG_BIGINT2);
+            addUint16(index);
+        } else {
+            addIcode(Icode_REG_BIGINT4);
+            addInt(index);
+        }
+        addToken(Token.BIGINT);
+    }
+
+    private void addIndexPrefix(int index) {
         if (index < 0) Kit.codeBug();
         if (index < 6) {
             addIcode(Icode_REG_IND_C0 - index);
         } else if (index <= 0xFF) {
             addIcode(Icode_REG_IND1);
             addUint8(index);
-         } else if (index <= 0xFFFF) {
+        } else if (index <= 0xFFFF) {
             addIcode(Icode_REG_IND2);
             addUint16(index);
-         } else {
+        } else {
             addIcode(Icode_REG_IND4);
             addInt(index);
         }
     }
 
-    private void addExceptionHandler(int icodeStart, int icodeEnd,
-                                     int handlerStart, boolean isFinally,
-                                     int exceptionObjectLocal, int scopeLocal)
-    {
+    private void addExceptionHandler(
+            int icodeStart,
+            int icodeEnd,
+            int handlerStart,
+            boolean isFinally,
+            int exceptionObjectLocal,
+            int scopeLocal) {
         int top = exceptionTableTop;
         int[] table = itsData.itsExceptionTable;
         if (table == null) {
@@ -1422,18 +1503,17 @@
             System.arraycopy(itsData.itsExceptionTable, 0, table, 0, top);
             itsData.itsExceptionTable = table;
         }
-        table[top + Interpreter.EXCEPTION_TRY_START_SLOT]  = icodeStart;
-        table[top + Interpreter.EXCEPTION_TRY_END_SLOT]    = icodeEnd;
-        table[top + Interpreter.EXCEPTION_HANDLER_SLOT]    = handlerStart;
-        table[top + Interpreter.EXCEPTION_TYPE_SLOT]     = isFinally ? 1 : 0;
-        table[top + Interpreter.EXCEPTION_LOCAL_SLOT]    = exceptionObjectLocal;
-        table[top + Interpreter.EXCEPTION_SCOPE_SLOT]    = scopeLocal;
+        table[top + Interpreter.EXCEPTION_TRY_START_SLOT] = icodeStart;
+        table[top + Interpreter.EXCEPTION_TRY_END_SLOT] = icodeEnd;
+        table[top + Interpreter.EXCEPTION_HANDLER_SLOT] = handlerStart;
+        table[top + Interpreter.EXCEPTION_TYPE_SLOT] = isFinally ? 1 : 0;
+        table[top + Interpreter.EXCEPTION_LOCAL_SLOT] = exceptionObjectLocal;
+        table[top + Interpreter.EXCEPTION_SCOPE_SLOT] = scopeLocal;
 
         exceptionTableTop = top + Interpreter.EXCEPTION_SLOT_SIZE;
     }
 
-    private byte[] increaseICodeCapacity(int extraSize)
-    {
+    private byte[] increaseICodeCapacity(int extraSize) {
         int capacity = itsData.itsICode.length;
         int top = iCodeTop;
         if (top + extraSize <= capacity) throw Kit.codeBug();
@@ -1447,8 +1527,7 @@
         return array;
     }
 
-    private void stackChange(int change)
-    {
+    private void stackChange(int change) {
         if (change <= 0) {
             stackDepth += change;
         } else {
@@ -1460,8 +1539,7 @@
         }
     }
 
-    private int allocLocal()
-    {
+    private int allocLocal() {
         int localSlot = localTop;
         ++localTop;
         if (localTop > itsData.itsMaxLocals) {
@@ -1470,8 +1548,7 @@
         return localSlot;
     }
 
-    private void releaseLocal(int localSlot)
-    {
+    private void releaseLocal(int localSlot) {
         --localTop;
         if (localSlot != localTop) Kit.codeBug();
     }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/ModuleScope.java rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/ModuleScope.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/ModuleScope.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/ModuleScope.java	2022-01-06 22:57:21.000000000 +0100
@@ -4,20 +4,18 @@
 
 package org.mozilla.javascript.commonjs.module;
 
+import java.net.URI;
+
 import org.mozilla.javascript.Scriptable;
 import org.mozilla.javascript.TopLevel;
 
-import java.net.URI;
-
 /**
  * A top-level module scope. This class provides methods to retrieve the
  * module's source and base URIs in order to resolve relative module IDs
  * and check sandbox constraints.
  */
 public class ModuleScope extends TopLevel {
-
     private static final long serialVersionUID = 1L;
-
     private final URI uri;
     private final URI base;
 
@@ -25,7 +23,7 @@
         this.uri = uri;
         this.base = base;
         setPrototype(prototype);
-        cacheBuiltins();
+        cacheBuiltins(prototype, false);
     }
 
     public URI getUri() {
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/ModuleScript.java rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/ModuleScript.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/ModuleScript.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/ModuleScript.java	2022-01-06 22:57:21.000000000 +0100
@@ -16,10 +16,8 @@
  * @author Attila Szegedi
  * @version $Id: ModuleScript.java,v 1.3 2011/04/07 20:26:11 hannes%helma.at Exp $
  */
-public class ModuleScript implements Serializable
-{
+public class ModuleScript implements Serializable {
     private static final long serialVersionUID = 1L;
-
     private final Script script;
     private final URI uri;
     private final URI base;
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/ModuleScriptProvider.java rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/ModuleScriptProvider.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/ModuleScriptProvider.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/ModuleScriptProvider.java	2022-01-06 22:57:21.000000000 +0100
@@ -4,11 +4,11 @@
 
 package org.mozilla.javascript.commonjs.module;
 
+import java.net.URI;
+
 import org.mozilla.javascript.Context;
 import org.mozilla.javascript.Scriptable;
 
-import java.net.URI;
-
 /**
  * Should be implemented by Rhino embeddings to allow the require() function to
  * obtain {@link ModuleScript} objects. We provide two default implementations,
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/provider/CachingModuleScriptProviderBase.java rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/CachingModuleScriptProviderBase.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/provider/CachingModuleScriptProviderBase.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/CachingModuleScriptProviderBase.java	2022-01-06 22:57:21.000000000 +0100
@@ -23,13 +23,9 @@
  * @author Attila Szegedi
  * @version $Id: CachingModuleScriptProviderBase.java,v 1.3 2011/04/07 20:26:12 hannes%helma.at Exp $
  */
-public abstract class CachingModuleScriptProviderBase
-implements ModuleScriptProvider, Serializable
-{
-    private static final long serialVersionUID = 1L;
-
-    private static final int loadConcurrencyLevel =
-        Runtime.getRuntime().availableProcessors() * 8;
+public abstract class CachingModuleScriptProviderBase implements ModuleScriptProvider, Serializable {
+    private static final long serialVersionUID = -1L;
+    private static final int loadConcurrencyLevel = Runtime.getRuntime().availableProcessors() * 8;
     private static final int loadLockShift;
     private static final int loadLockMask;
     private static final int loadLockCount;
@@ -61,6 +57,7 @@
         this.moduleSourceProvider = moduleSourceProvider;
     }
 
+    @Override
     public ModuleScript getModuleScript(Context cx, String moduleId,
             URI moduleUri, URI baseUri, Scriptable paths) throws Exception
     {
@@ -75,8 +72,7 @@
         if(moduleSource == null) {
             return null;
         }
-        final Reader reader = moduleSource.getReader();
-        try {
+        try (Reader reader = moduleSource.getReader()) {
             final int idHash = moduleId.hashCode();
             synchronized(loadLocks[(idHash >>> loadLockShift) & loadLockMask]) {
                 final CachedModuleScript cachedModule2 = getLoadedModule(moduleId);
@@ -95,9 +91,6 @@
                 return moduleScript;
             }
         }
-        finally {
-            reader.close();
-        }
     }
 
     /**
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/provider/DefaultUrlConnectionExpiryCalculator.java rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/DefaultUrlConnectionExpiryCalculator.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/provider/DefaultUrlConnectionExpiryCalculator.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/DefaultUrlConnectionExpiryCalculator.java	2022-01-06 22:57:21.000000000 +0100
@@ -15,11 +15,8 @@
  * @author Attila Szegedi
  * @version $Id: DefaultUrlConnectionExpiryCalculator.java,v 1.3 2011/04/07 20:26:12 hannes%helma.at Exp $
  */
-public class DefaultUrlConnectionExpiryCalculator
-implements UrlConnectionExpiryCalculator, Serializable
-{
+public class DefaultUrlConnectionExpiryCalculator implements UrlConnectionExpiryCalculator, Serializable {
     private static final long serialVersionUID = 1L;
-
     private final long relativeExpiry;
 
     /**
@@ -41,6 +38,7 @@
         this.relativeExpiry = relativeExpiry;
     }
 
+    @Override
     public long calculateExpiry(URLConnection urlConnection) {
         return System.currentTimeMillis() + relativeExpiry;
     }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/provider/ModuleSource.java rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/ModuleSource.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/provider/ModuleSource.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/ModuleSource.java	2022-01-06 22:57:21.000000000 +0100
@@ -30,10 +30,8 @@
  * @author Attila Szegedi
  * @version $Id: ModuleSource.java,v 1.3 2011/04/07 20:26:12 hannes%helma.at Exp $
  */
-public class ModuleSource implements Serializable
-{
+public class ModuleSource implements Serializable {
     private static final long serialVersionUID = 1L;
-
     private final Reader reader;
     private final Object securityDomain;
     private final URI uri;
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/provider/ModuleSourceProviderBase.java rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/ModuleSourceProviderBase.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/provider/ModuleSourceProviderBase.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/ModuleSourceProviderBase.java	2022-01-06 22:57:21.000000000 +0100
@@ -26,11 +26,10 @@
  * @author Attila Szegedi
  * @version $Id: ModuleSourceProviderBase.java,v 1.3 2011/04/07 20:26:12 hannes%helma.at Exp $
  */
-public abstract class ModuleSourceProviderBase implements
-        ModuleSourceProvider, Serializable
-{
+public abstract class ModuleSourceProviderBase implements ModuleSourceProvider, Serializable {
     private static final long serialVersionUID = 1L;
 
+    @Override
     public ModuleSource loadSource(String moduleId, Scriptable paths,
             Object validator) throws IOException, URISyntaxException
     {
@@ -38,14 +37,12 @@
             return NOT_MODIFIED;
         }
 
-        ModuleSource moduleSource = loadFromPrivilegedLocations(
-                moduleId, validator);
+        ModuleSource moduleSource = loadFromPrivilegedLocations(moduleId, validator);
         if(moduleSource != null) {
             return moduleSource;
         }
         if(paths != null) {
-            moduleSource = loadFromPathArray(moduleId, paths,
-                    validator);
+            moduleSource = loadFromPathArray(moduleId, paths, validator);
             if(moduleSource != null) {
                 return moduleSource;
             }
@@ -53,6 +50,7 @@
         return loadFromFallbackLocations(moduleId, validator);
     }
 
+    @Override
     public ModuleSource loadSource(URI uri, URI base, Object validator)
             throws IOException, URISyntaxException {
         return loadFromUri(uri, base, validator);
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/provider/MultiModuleScriptProvider.java rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/MultiModuleScriptProvider.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/provider/MultiModuleScriptProvider.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/MultiModuleScriptProvider.java	2022-01-06 22:57:21.000000000 +0100
@@ -35,6 +35,7 @@
         this.providers = l.toArray(new ModuleScriptProvider[l.size()]);
     }
 
+    @Override
     public ModuleScript getModuleScript(Context cx, String moduleId, URI uri,
                                         URI base, Scriptable paths) throws Exception {
         for (ModuleScriptProvider provider : providers) {
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/provider/ParsedContentType.java rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/ParsedContentType.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/provider/ParsedContentType.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/ParsedContentType.java	2022-01-06 22:57:21.000000000 +0100
@@ -13,10 +13,8 @@
  * @author Attila Szegedi
  * @version $Id: ParsedContentType.java,v 1.3 2011/04/07 20:26:12 hannes%helma.at Exp $
  */
-public final class ParsedContentType implements Serializable
-{
+public final class ParsedContentType implements Serializable {
     private static final long serialVersionUID = 1L;
-
     private final String contentType;
     private final String encoding;
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/provider/SoftCachingModuleScriptProvider.java rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/SoftCachingModuleScriptProvider.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/provider/SoftCachingModuleScriptProvider.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/SoftCachingModuleScriptProvider.java	2022-01-06 22:57:21.000000000 +0100
@@ -29,16 +29,10 @@
  * @author Attila Szegedi
  * @version $Id: SoftCachingModuleScriptProvider.java,v 1.3 2011/04/07 20:26:12 hannes%helma.at Exp $
  */
-public class SoftCachingModuleScriptProvider extends CachingModuleScriptProviderBase
-{
+public class SoftCachingModuleScriptProvider extends CachingModuleScriptProviderBase {
     private static final long serialVersionUID = 1L;
-
-    private transient ReferenceQueue<Script> scriptRefQueue =
-        new ReferenceQueue<Script>();
-
-    private transient ConcurrentMap<String, ScriptReference> scripts =
-        new ConcurrentHashMap<String, ScriptReference>(16, .75f,
-                getConcurrencyLevel());
+    private transient ReferenceQueue<Script> scriptRefQueue = new ReferenceQueue<Script>();
+    private transient ConcurrentMap<String, ScriptReference> scripts = new ConcurrentHashMap<String, ScriptReference>(16, .75f, getConcurrencyLevel());
 
     /**
      * Creates a new module provider with the specified module source provider.
@@ -111,6 +105,7 @@
         }
     }
 
+    @SuppressWarnings("unchecked")
     private void readObject(ObjectInputStream in) throws IOException,
     ClassNotFoundException
     {
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/provider/StrongCachingModuleScriptProvider.java rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/StrongCachingModuleScriptProvider.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/provider/StrongCachingModuleScriptProvider.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/StrongCachingModuleScriptProvider.java	2022-01-06 22:57:21.000000000 +0100
@@ -17,12 +17,10 @@
  * @author Attila Szegedi
  * @version $Id: StrongCachingModuleScriptProvider.java,v 1.3 2011/04/07 20:26:12 hannes%helma.at Exp $
  */
-public class StrongCachingModuleScriptProvider extends CachingModuleScriptProviderBase
-{
-    private static final long serialVersionUID = 1L;
+public class StrongCachingModuleScriptProvider extends CachingModuleScriptProviderBase {
 
-    private final Map<String, CachedModuleScript> modules =
-        new ConcurrentHashMap<String, CachedModuleScript>(16, .75f, getConcurrencyLevel());
+    private static final long serialVersionUID = 1L;
+    private final Map<String, CachedModuleScript> modules = new ConcurrentHashMap<String, CachedModuleScript>(16, .75f, getConcurrencyLevel());
 
     /**
      * Creates a new module provider with the specified module source provider.
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/provider/UrlModuleSourceProvider.java rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/UrlModuleSourceProvider.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/provider/UrlModuleSourceProvider.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/UrlModuleSourceProvider.java	2022-01-06 22:57:21.000000000 +0100
@@ -31,14 +31,11 @@
  * @author Attila Szegedi
  * @version $Id: UrlModuleSourceProvider.java,v 1.4 2011/04/07 20:26:12 hannes%helma.at Exp $
  */
-public class UrlModuleSourceProvider extends ModuleSourceProviderBase
-{
+public class UrlModuleSourceProvider extends ModuleSourceProviderBase {
     private static final long serialVersionUID = 1L;
-
     private final Iterable<URI> privilegedUris;
     private final Iterable<URI> fallbackUris;
-    private final UrlConnectionSecurityDomainProvider
-        urlConnectionSecurityDomainProvider;
+    private final UrlConnectionSecurityDomainProvider urlConnectionSecurityDomainProvider;
     private final UrlConnectionExpiryCalculator urlConnectionExpiryCalculator;
 
     /**
@@ -152,7 +149,7 @@
         try {
             urlConnection.connect();
             if(applicableValidator != null &&
-                    applicableValidator.updateValidator(urlConnection,
+                    !applicableValidator.updateValidator(urlConnection,
                             request_time, urlConnectionExpiryCalculator))
             {
                 close(urlConnection);
@@ -195,9 +192,7 @@
         if(contentType != null && contentType.startsWith("text/")) {
             return "8859_1";
         }
-        else {
-            return "utf-8";
-        }
+        return "utf-8";
     }
 
     private Object getSecurityDomain(URLConnection urlConnection) {
@@ -243,22 +238,18 @@
     }
 
     private static class URLValidator implements Serializable {
-        private static final long serialVersionUID = 1L;
-
-        private final URI uri;
-        private final long lastModified;
-        private final String entityTags;
-        private long expiry;
-
-        public URLValidator(URI uri, URLConnection urlConnection,
-                long request_time, UrlConnectionExpiryCalculator
-                urlConnectionExpiryCalculator) {
-            this.uri = uri;
-            this.lastModified = urlConnection.getLastModified();
-            this.entityTags = getEntityTags(urlConnection);
-            expiry = calculateExpiry(urlConnection, request_time,
-                    urlConnectionExpiryCalculator);
-        }
+    private static final long serialVersionUID = 1L;
+    private final URI uri;
+    private final long lastModified;
+    private final String entityTags;
+    private long expiry;
+
+    public URLValidator(URI uri, URLConnection urlConnection, long request_time, UrlConnectionExpiryCalculator urlConnectionExpiryCalculator) {
+        this.uri = uri;
+        this.lastModified = urlConnection.getLastModified();
+        this.entityTags = getEntityTags(urlConnection);
+        expiry = calculateExpiry(urlConnection, request_time, urlConnectionExpiryCalculator);
+    }
 
         boolean updateValidator(URLConnection urlConnection, long request_time,
                 UrlConnectionExpiryCalculator urlConnectionExpiryCalculator)
@@ -278,10 +269,10 @@
                 return ((HttpURLConnection)urlConnection).getResponseCode() ==
                     HttpURLConnection.HTTP_NOT_MODIFIED;
             }
-            return lastModified == urlConnection.getLastModified();
+            return lastModified != urlConnection.getLastModified();
         }
 
-        private long calculateExpiry(URLConnection urlConnection,
+        private static long calculateExpiry(URLConnection urlConnection,
                 long request_time, UrlConnectionExpiryCalculator
                 urlConnectionExpiryCalculator)
         {
@@ -318,7 +309,7 @@
                 urlConnectionExpiryCalculator.calculateExpiry(urlConnection);
         }
 
-        private int getMaxAge(String cacheControl) {
+        private static int getMaxAge(String cacheControl) {
             final int maxAgeIndex = cacheControl.indexOf("max-age");
             if(maxAgeIndex == -1) {
                 return -1;
@@ -343,7 +334,7 @@
             }
         }
 
-        private String getEntityTags(URLConnection urlConnection) {
+        private static String getEntityTags(URLConnection urlConnection) {
             final List<String> etags = urlConnection.getHeaderFields().get("ETag");
             if(etags == null || etags.isEmpty()) {
                 return null;
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/RequireBuilder.java rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/RequireBuilder.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/RequireBuilder.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/RequireBuilder.java	2022-01-06 22:57:21.000000000 +0100
@@ -20,10 +20,8 @@
  * @author Attila Szegedi
  * @version $Id: RequireBuilder.java,v 1.4 2011/04/07 20:26:11 hannes%helma.at Exp $
  */
-public class RequireBuilder implements Serializable
-{
+public class RequireBuilder implements Serializable {
     private static final long serialVersionUID = 1L;
-
     private boolean sandboxed = true;
     private ModuleScriptProvider moduleScriptProvider;
     private Script preExec;
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/Require.java rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/Require.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/Require.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/Require.java	2022-01-06 22:57:21.000000000 +0100
@@ -38,10 +38,8 @@
  * @author Attila Szegedi
  * @version $Id: Require.java,v 1.4 2011/04/07 20:26:11 hannes%helma.at Exp $
  */
-public class Require extends BaseFunction
-{
+public class Require extends BaseFunction {
     private static final long serialVersionUID = 1L;
-
     private final ModuleScriptProvider moduleScriptProvider;
     private final Scriptable nativeScope;
     private final Scriptable paths;
@@ -210,9 +208,8 @@
                     if (sandboxed) {
                         throw ScriptRuntime.throwError(cx, scope,
                             "Module \"" + id + "\" is not contained in sandbox.");
-                    } else {
-                        id = uri.toString();
                     }
+                    id = uri.toString();
                 }
             }
         }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/CompilerEnvirons.java rhino-1.7.14/src/org/mozilla/javascript/CompilerEnvirons.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/CompilerEnvirons.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/CompilerEnvirons.java	2022-01-06 22:57:21.000000000 +0100
@@ -266,7 +266,7 @@
         env.setRecordingComments(true);
         env.setStrictMode(true);
         env.setWarnTrailingComma(true);
-        env.setLanguageVersion(170);
+        env.setLanguageVersion(Context.VERSION_1_7);
         env.setReservedKeywordAsIdentifier(true);
         env.setIdeMode(true);
         env.setErrorReporter(new ErrorCollector());
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ConsString.java rhino-1.7.14/src/org/mozilla/javascript/ConsString.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ConsString.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ConsString.java	2022-01-06 22:57:21.000000000 +0100
@@ -7,7 +7,7 @@
 package org.mozilla.javascript;
 
 import java.io.Serializable;
-import java.util.ArrayList;
+import java.util.ArrayDeque;
 
 /**
  * <p>This class represents a string composed of two components, each of which
@@ -29,21 +29,15 @@
 
     private static final long serialVersionUID = -8432806714471372570L;
 
-    private CharSequence s1, s2;
+    private CharSequence left, right;
     private final int length;
-    private int depth;
+    private boolean isFlat;
 
     public ConsString(CharSequence str1, CharSequence str2) {
-        s1 = str1;
-        s2 = str2;
-        length = str1.length() + str2.length();
-        depth = 1;
-        if (str1 instanceof ConsString) {
-            depth += ((ConsString)str1).depth;
-        }
-        if (str2 instanceof ConsString) {
-            depth += ((ConsString)str2).depth;
-        }
+        left = str1;
+        right = str2;
+        length = left.length() + right.length();
+        isFlat = false;
     }
 
     // Replace with string representation when serializing
@@ -53,44 +47,57 @@
     
     @Override
     public String toString() {
-        return depth == 0 ? (String)s1 : flatten();
+        return isFlat ? (String)left : flatten();
     }
 
     private synchronized String flatten() {
-        if (depth > 0) {
-            StringBuilder b = new StringBuilder(length);
-            ArrayList<CharSequence> buffer = new ArrayList<CharSequence>();
-            buffer.add(s2);
-            buffer.add(s1);
-            while(!buffer.isEmpty()) {
-                CharSequence next = buffer.remove(buffer.size() - 1);
+        if (!isFlat) {
+            final char[] chars = new char[length];
+            int charPos = length;
+
+            ArrayDeque<CharSequence> stack = new ArrayDeque<CharSequence>();
+            stack.addFirst(left);
+
+            CharSequence next = right;
+            do {
                 if (next instanceof ConsString) {
                     ConsString casted = (ConsString) next;
-                    buffer.add(casted.s2);
-                    buffer.add(casted.s1);
-                } else {
-                    b.append(next);
+                    if (casted.isFlat) {
+                        next = casted.left;
+                    } else {
+                        stack.addFirst(casted.left);
+                        next = casted.right;
+                        continue;
+                    }
                 }
-            }
-            s1 = b.toString();
-            s2 = "";
-            depth = 0;
+
+                final String str = (String) next;
+                charPos -= str.length();
+                str.getChars(0, str.length(), chars, charPos);
+                next = stack.isEmpty() ? null : stack.removeFirst();
+            } while (next != null);
+
+            left = new String(chars);
+            right = "";
+            isFlat = true;
         }
-        return (String)s1;
+        return (String)left;
     }
 
+    @Override
     public int length() {
         return length;
     }
 
+    @Override
     public char charAt(int index) {
-        String str = depth == 0 ? (String)s1 : flatten();
+        String str = isFlat ? (String)left : flatten();
         return str.charAt(index);
     }
 
+    @Override
     public CharSequence subSequence(int start, int end) {
-        String str = depth == 0 ? (String)s1 : flatten();
+        String str = isFlat ? (String)left : flatten();
         return str.substring(start, end);
     }
-
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ConstProperties.java rhino-1.7.14/src/org/mozilla/javascript/ConstProperties.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ConstProperties.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ConstProperties.java	2022-01-06 22:57:21.000000000 +0100
@@ -29,7 +29,7 @@
      * object's responsibility to modify <i>o</i>. <p>
      * This design allows properties to be defined in prototypes and implemented
      * in terms of getters and setters of Java values without consuming slots
-     * in each instance.<p>
+     * in each instance.
      * <p>
      * The values that may be set are limited to the following:
      * <UL>
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/Constructable.java rhino-1.7.14/src/org/mozilla/javascript/Constructable.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/Constructable.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/Constructable.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,19 @@
+package org.mozilla.javascript;
+
+/** An interface that can be used to implement a constructor function as a lambda. */
+public interface Constructable {
+
+    /**
+     * Call the function as a constructor.
+     *
+     * <p>This method is invoked by the runtime in order to satisfy a use of the JavaScript <code>
+     * new</code> operator. This method is expected to create a new object and return it.
+     *
+     * @param cx the current Context for this thread
+     * @param scope an enclosing scope of the caller except when the function is called from a
+     *     closure.
+     * @param args the array of arguments
+     * @return the allocated object
+     */
+    Scriptable construct(Context cx, Scriptable scope, Object[] args);
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ContextAction.java rhino-1.7.14/src/org/mozilla/javascript/ContextAction.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ContextAction.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ContextAction.java	2022-01-06 22:57:21.000000000 +0100
@@ -11,16 +11,17 @@
 /**
  * Interface to represent arbitrary action that requires to have Context
  * object associated with the current thread for its execution.
+ * @param T the type of the return value of action execution
  */
-public interface ContextAction
+public interface ContextAction<T>
 {
     /**
      * Execute action using the supplied Context instance.
-     * When Rhino runtime calls the method, <tt>cx</tt> will be associated
+     * When Rhino runtime calls the method, <code>cx</code> will be associated
      * with the current thread as active context.
      *
      * @see ContextFactory#call(ContextAction)
      */
-    public Object run(Context cx);
+    public T run(Context cx);
 }
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ContextFactory.java rhino-1.7.14/src/org/mozilla/javascript/ContextFactory.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ContextFactory.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ContextFactory.java	2022-01-06 22:57:21.000000000 +0100
@@ -12,22 +12,21 @@
 import java.security.PrivilegedAction;
 
 /**
- * Factory class that Rhino runtime uses to create new {@link Context}
- * instances.  A <code>ContextFactory</code> can also notify listeners
- * about context creation and release.
- * <p>
- * When the Rhino runtime needs to create new {@link Context} instance during
- * execution of {@link Context#enter()} or {@link Context}, it will call
- * {@link #makeContext()} of the current global ContextFactory.
- * See {@link #getGlobal()} and {@link #initGlobal(ContextFactory)}.
- * <p>
- * It is also possible to use explicit ContextFactory instances for Context
- * creation. This is useful to have a set of independent Rhino runtime
- * instances under single JVM. See {@link #call(ContextAction)}.
- * <p>
- * The following example demonstrates Context customization to terminate
- * scripts running more then 10 seconds and to provide better compatibility
- * with JavaScript code using MSIE-specific features.
+ * Factory class that Rhino runtime uses to create new {@link Context} instances. A <code>
+ * ContextFactory</code> can also notify listeners about context creation and release.
+ *
+ * <p>When the Rhino runtime needs to create new {@link Context} instance during execution of {@link
+ * Context#enter()} or {@link Context}, it will call {@link #makeContext()} of the current global
+ * ContextFactory. See {@link #getGlobal()} and {@link #initGlobal(ContextFactory)}.
+ *
+ * <p>It is also possible to use explicit ContextFactory instances for Context creation. This is
+ * useful to have a set of independent Rhino runtime instances under single JVM. See {@link
+ * #call(ContextAction)}.
+ *
+ * <p>The following example demonstrates Context customization to terminate scripts running more
+ * then 10 seconds and to provide better compatibility with JavaScript code using MSIE-specific
+ * features.
+ *
  * <pre>
  * import org.mozilla.javascript.*;
  *
@@ -80,7 +79,7 @@
  *     {
  *         MyContext mcx = (MyContext)cx;
  *         long currentTime = System.currentTimeMillis();
- *         if (currentTime - mcx.startTime > 10*1000) {
+ *         if (currentTime - mcx.startTime > 10*1000) {
  *             // More then 10 seconds from Context creation time:
  *             // it is time to stop the script.
  *             // Throw Error instance to ensure that script will never
@@ -90,8 +89,8 @@
  *     }
  *
  *     // Override {@link #doTopCall(Callable,
-                               Context, Scriptable,
-                               Scriptable, Object[])}
+ * Context, Scriptable,
+ * Scriptable, Object[])}
  *     protected Object doTopCall(Callable callable,
  *                                Context cx, Scriptable scope,
  *                                Scriptable thisObj, Object[] args)
@@ -103,12 +102,9 @@
  *     }
  *
  * }
- *
  * </pre>
  */
-
-public class ContextFactory
-{
+public class ContextFactory {
     private static volatile boolean hasCustomGlobal;
     private static ContextFactory global = new ContextFactory();
 
@@ -119,19 +115,14 @@
     private boolean disabledListening;
     private ClassLoader applicationClassLoader;
 
-    /**
-     * Listener of {@link Context} creation and release events.
-     */
-    public interface Listener
-    {
-        /**
-         * Notify about newly created {@link Context} object.
-         */
+    /** Listener of {@link Context} creation and release events. */
+    public interface Listener {
+        /** Notify about newly created {@link Context} object. */
         public void contextCreated(Context cx);
 
         /**
-         * Notify that the specified {@link Context} instance is no longer
-         * associated with the current thread.
+         * Notify that the specified {@link Context} instance is no longer associated with the
+         * current thread.
          */
         public void contextReleased(Context cx);
     }
@@ -142,34 +133,29 @@
      * @see #hasExplicitGlobal()
      * @see #initGlobal(ContextFactory)
      */
-    public static ContextFactory getGlobal()
-    {
+    public static ContextFactory getGlobal() {
         return global;
     }
 
     /**
-     * Check if global factory was set.
-     * Return true to indicate that {@link #initGlobal(ContextFactory)} was
-     * already called and false to indicate that the global factory was not
-     * explicitly set.
+     * Check if global factory was set. Return true to indicate that {@link
+     * #initGlobal(ContextFactory)} was already called and false to indicate that the global factory
+     * was not explicitly set.
      *
      * @see #getGlobal()
      * @see #initGlobal(ContextFactory)
      */
-    public static boolean hasExplicitGlobal()
-    {
+    public static boolean hasExplicitGlobal() {
         return hasCustomGlobal;
     }
 
     /**
-     * Set global ContextFactory.
-     * The method can only be called once.
+     * Set global ContextFactory. The method can only be called once.
      *
      * @see #getGlobal()
      * @see #hasExplicitGlobal()
      */
-    public synchronized static void initGlobal(ContextFactory factory)
-    {
+    public static synchronized void initGlobal(ContextFactory factory) {
         if (factory == null) {
             throw new IllegalArgumentException();
         }
@@ -182,18 +168,22 @@
 
     public interface GlobalSetter {
         public void setContextFactoryGlobal(ContextFactory factory);
+
         public ContextFactory getContextFactoryGlobal();
     }
 
-    public synchronized static GlobalSetter getGlobalSetter() {
+    public static synchronized GlobalSetter getGlobalSetter() {
         if (hasCustomGlobal) {
             throw new IllegalStateException();
         }
         hasCustomGlobal = true;
         class GlobalSetterImpl implements GlobalSetter {
+            @Override
             public void setContextFactoryGlobal(ContextFactory factory) {
                 global = factory == null ? new ContextFactory() : factory;
             }
+
+            @Override
             public ContextFactory getContextFactoryGlobal() {
                 return global;
             }
@@ -202,104 +192,113 @@
     }
 
     /**
-     * Create new {@link Context} instance to be associated with the current
-     * thread.
-     * This is a callback method used by Rhino to create {@link Context}
-     * instance when it is necessary to associate one with the current
-     * execution thread. <tt>makeContext()</tt> is allowed to call
-     * {@link Context#seal(Object)} on the result to prevent
-     * {@link Context} changes by hostile scripts or applets.
+     * Create new {@link Context} instance to be associated with the current thread. This is a
+     * callback method used by Rhino to create {@link Context} instance when it is necessary to
+     * associate one with the current execution thread. <code>makeContext()</code> is allowed to
+     * call {@link Context#seal(Object)} on the result to prevent {@link Context} changes by hostile
+     * scripts or applets.
      */
-    protected Context makeContext()
-    {
+    protected Context makeContext() {
         return new Context(this);
     }
 
     /**
-     * Implementation of {@link Context#hasFeature(int featureIndex)}.
-     * This can be used to customize {@link Context} without introducing
-     * additional subclasses.
+     * Implementation of {@link Context#hasFeature(int featureIndex)}. This can be used to customize
+     * {@link Context} without introducing additional subclasses.
      */
-    protected boolean hasFeature(Context cx, int featureIndex)
-    {
+    protected boolean hasFeature(Context cx, int featureIndex) {
         int version;
         switch (featureIndex) {
-          case Context.FEATURE_NON_ECMA_GET_YEAR:
-           /*
-            * During the great date rewrite of 1.3, we tried to track the
-            * evolving ECMA standard, which then had a definition of
-            * getYear which always subtracted 1900.  Which we
-            * implemented, not realizing that it was incompatible with
-            * the old behavior...  now, rather than thrash the behavior
-            * yet again, we've decided to leave it with the - 1900
-            * behavior and point people to the getFullYear method.  But
-            * we try to protect existing scripts that have specified a
-            * version...
-            */
-            version = cx.getLanguageVersion();
-            return (version == Context.VERSION_1_0
-                    || version == Context.VERSION_1_1
-                    || version == Context.VERSION_1_2);
+            case Context.FEATURE_NON_ECMA_GET_YEAR:
+                /*
+                 * During the great date rewrite of 1.3, we tried to track the
+                 * evolving ECMA standard, which then had a definition of
+                 * getYear which always subtracted 1900.  Which we
+                 * implemented, not realizing that it was incompatible with
+                 * the old behavior...  now, rather than thrash the behavior
+                 * yet again, we've decided to leave it with the - 1900
+                 * behavior and point people to the getFullYear method.  But
+                 * we try to protect existing scripts that have specified a
+                 * version...
+                 */
+                version = cx.getLanguageVersion();
+                return (version == Context.VERSION_1_0
+                        || version == Context.VERSION_1_1
+                        || version == Context.VERSION_1_2);
 
-          case Context.FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME:
-            return false;
+            case Context.FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME:
+                return false;
 
-          case Context.FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER:
-            return true;
+            case Context.FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER:
+                return true;
 
-          case Context.FEATURE_TO_STRING_AS_SOURCE:
-            version = cx.getLanguageVersion();
-            return version == Context.VERSION_1_2;
+            case Context.FEATURE_TO_STRING_AS_SOURCE:
+                version = cx.getLanguageVersion();
+                return version == Context.VERSION_1_2;
 
-          case Context.FEATURE_PARENT_PROTO_PROPERTIES:
-            return true;
+            case Context.FEATURE_PARENT_PROTO_PROPERTIES:
+                return true;
 
-          case Context.FEATURE_E4X:
-            version = cx.getLanguageVersion();
-            return (version == Context.VERSION_DEFAULT
-                    || version >= Context.VERSION_1_6);
+            case Context.FEATURE_E4X:
+                version = cx.getLanguageVersion();
+                return (version == Context.VERSION_DEFAULT || version >= Context.VERSION_1_6);
 
-          case Context.FEATURE_DYNAMIC_SCOPE:
-            return false;
+            case Context.FEATURE_DYNAMIC_SCOPE:
+                return false;
 
-          case Context.FEATURE_STRICT_VARS:
-            return false;
+            case Context.FEATURE_STRICT_VARS:
+                return false;
 
-          case Context.FEATURE_STRICT_EVAL:
-            return false;
+            case Context.FEATURE_STRICT_EVAL:
+                return false;
 
-          case Context.FEATURE_LOCATION_INFORMATION_IN_ERROR:
-            return false;
+            case Context.FEATURE_LOCATION_INFORMATION_IN_ERROR:
+                return false;
 
-          case Context.FEATURE_STRICT_MODE:
-            return false;
+            case Context.FEATURE_STRICT_MODE:
+                return false;
 
-          case Context.FEATURE_WARNING_AS_ERROR:
-            return false;
+            case Context.FEATURE_WARNING_AS_ERROR:
+                return false;
 
-          case Context.FEATURE_ENHANCED_JAVA_ACCESS:
-            return false;
+            case Context.FEATURE_ENHANCED_JAVA_ACCESS:
+                return false;
 
-          case Context.FEATURE_V8_EXTENSIONS:
-            return true;
+            case Context.FEATURE_V8_EXTENSIONS:
+                return true;
+
+            case Context.FEATURE_OLD_UNDEF_NULL_THIS:
+                return cx.getLanguageVersion() <= Context.VERSION_1_7;
+
+            case Context.FEATURE_ENUMERATE_IDS_FIRST:
+                return cx.getLanguageVersion() >= Context.VERSION_ES6;
 
-          case Context.FEATURE_OLD_UNDEF_NULL_THIS:
-              return cx.getLanguageVersion() <= Context.VERSION_1_7;
+            case Context.FEATURE_THREAD_SAFE_OBJECTS:
+                return false;
 
-          case Context.FEATURE_ENUMERATE_IDS_FIRST:
-              return cx.getLanguageVersion() >= Context.VERSION_ES6;
+            case Context.FEATURE_INTEGER_WITHOUT_DECIMAL_PLACE:
+                return false;
+
+            case Context.FEATURE_LITTLE_ENDIAN:
+                return false;
+
+            case Context.FEATURE_ENABLE_XML_SECURE_PARSING:
+                return true;
+
+            case Context.FEATURE_ENABLE_JAVA_MAP_ACCESS:
+                return false;
         }
         // It is a bug to call the method with unknown featureIndex
         throw new IllegalArgumentException(String.valueOf(featureIndex));
     }
 
-    private boolean isDom3Present() {
+    private static boolean isDom3Present() {
         Class<?> nodeClass = Kit.classOrNull("org.w3c.dom.Node");
         if (nodeClass == null) return false;
         // Check to see whether DOM3 is present; use a new method defined in
         // DOM3 that is vital to our implementation
         try {
-            nodeClass.getMethod("getUserData", new Class<?>[] { String.class });
+            nodeClass.getMethod("getUserData", new Class<?>[] {String.class});
             return true;
         } catch (NoSuchMethodException e) {
             return false;
@@ -307,19 +306,15 @@
     }
 
     /**
-     * Provides a default
-     * {@link org.mozilla.javascript.xml.XMLLib.Factory XMLLib.Factory}
-     * to be used by the <code>Context</code> instances produced by this
-     * factory. See {@link Context#getE4xImplementationFactory} for details.
-     *
-     * May return null, in which case E4X functionality is not supported in
-     * Rhino.
-     *
-     * The default implementation now prefers the DOM3 E4X implementation.
-     */
-    protected org.mozilla.javascript.xml.XMLLib.Factory
-        getE4xImplementationFactory()
-    {
+     * Provides a default {@link org.mozilla.javascript.xml.XMLLib.Factory XMLLib.Factory} to be
+     * used by the <code>Context</code> instances produced by this factory. See {@link
+     * Context#getE4xImplementationFactory} for details.
+     *
+     * <p>May return null, in which case E4X functionality is not supported in Rhino.
+     *
+     * <p>The default implementation now prefers the DOM3 E4X implementation.
+     */
+    protected org.mozilla.javascript.xml.XMLLib.Factory getE4xImplementationFactory() {
         // Must provide default implementation, rather than abstract method,
         // so that past implementors of ContextFactory do not fail at runtime
         // upon invocation of this method.
@@ -328,39 +323,33 @@
 
         if (isDom3Present()) {
             return org.mozilla.javascript.xml.XMLLib.Factory.create(
-                "org.mozilla.javascript.xmlimpl.XMLLibImpl"
-            );
-        } else {
-            return null;
+                    "org.mozilla.javascript.xmlimpl.XMLLibImpl");
         }
+        return null;
     }
 
-
     /**
-     * Create class loader for generated classes.
-     * This method creates an instance of the default implementation
-     * of {@link GeneratedClassLoader}. Rhino uses this interface to load
-     * generated JVM classes when no {@link SecurityController}
-     * is installed.
-     * Application can override the method to provide custom class loading.
+     * Create class loader for generated classes. This method creates an instance of the default
+     * implementation of {@link GeneratedClassLoader}. Rhino uses this interface to load generated
+     * JVM classes when no {@link SecurityController} is installed. Application can override the
+     * method to provide custom class loading.
      */
-    protected GeneratedClassLoader createClassLoader(final ClassLoader parent)
-    {
-        return AccessController.doPrivileged(new PrivilegedAction<DefiningClassLoader>() {
-            public DefiningClassLoader run(){
-                return new DefiningClassLoader(parent);
-            }
-        });
+    protected GeneratedClassLoader createClassLoader(final ClassLoader parent) {
+        return AccessController.doPrivileged(
+                new PrivilegedAction<DefiningClassLoader>() {
+                    @Override
+                    public DefiningClassLoader run() {
+                        return new DefiningClassLoader(parent);
+                    }
+                });
     }
 
     /**
-     * Get ClassLoader to use when searching for Java classes.
-     * Unless it was explicitly initialized with
-     * {@link #initApplicationClassLoader(ClassLoader)} the method returns
-     * null to indicate that Thread.getContextClassLoader() should be used.
+     * Get ClassLoader to use when searching for Java classes. Unless it was explicitly initialized
+     * with {@link #initApplicationClassLoader(ClassLoader)} the method returns null to indicate
+     * that Thread.getContextClassLoader() should be used.
      */
-    public final ClassLoader getApplicationClassLoader()
-    {
+    public final ClassLoader getApplicationClassLoader() {
         return applicationClassLoader;
     }
 
@@ -369,70 +358,54 @@
      *
      * @see #getApplicationClassLoader()
      */
-    public final void initApplicationClassLoader(ClassLoader loader)
-    {
-        if (loader == null)
-            throw new IllegalArgumentException("loader is null");
+    public final void initApplicationClassLoader(ClassLoader loader) {
+        if (loader == null) throw new IllegalArgumentException("loader is null");
         if (!Kit.testIfCanLoadRhinoClasses(loader))
-            throw new IllegalArgumentException(
-                "Loader can not resolve Rhino classes");
+            throw new IllegalArgumentException("Loader can not resolve Rhino classes");
 
         if (this.applicationClassLoader != null)
-            throw new IllegalStateException(
-                "applicationClassLoader can only be set once");
+            throw new IllegalStateException("applicationClassLoader can only be set once");
         checkNotSealed();
 
         this.applicationClassLoader = loader;
     }
 
     /**
-     * Execute top call to script or function.
-     * When the runtime is about to execute a script or function that will
-     * create the first stack frame with scriptable code, it calls this method
-     * to perform the real call. In this way execution of any script
-     * happens inside this function.
-     */
-    protected Object doTopCall(Callable callable,
-                               Context cx, Scriptable scope,
-                               Scriptable thisObj, Object[] args)
-    {
+     * Execute top call to script or function. When the runtime is about to execute a script or
+     * function that will create the first stack frame with scriptable code, it calls this method to
+     * perform the real call. In this way execution of any script happens inside this function.
+     */
+    protected Object doTopCall(
+            Callable callable, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         Object result = callable.call(cx, scope, thisObj, args);
         return result instanceof ConsString ? result.toString() : result;
     }
 
     /**
-     * Implementation of
-     * {@link Context#observeInstructionCount(int instructionCount)}.
-     * This can be used to customize {@link Context} without introducing
-     * additional subclasses.
+     * Implementation of {@link Context#observeInstructionCount(int instructionCount)}. This can be
+     * used to customize {@link Context} without introducing additional subclasses.
      */
-    protected void observeInstructionCount(Context cx, int instructionCount) {
-    }
+    protected void observeInstructionCount(Context cx, int instructionCount) {}
 
-    protected void onContextCreated(Context cx)
-    {
+    protected void onContextCreated(Context cx) {
         Object listeners = this.listeners;
         for (int i = 0; ; ++i) {
-            Listener l = (Listener)Kit.getListener(listeners, i);
-            if (l == null)
-                break;
+            Listener l = (Listener) Kit.getListener(listeners, i);
+            if (l == null) break;
             l.contextCreated(cx);
         }
     }
 
-    protected void onContextReleased(Context cx)
-    {
+    protected void onContextReleased(Context cx) {
         Object listeners = this.listeners;
         for (int i = 0; ; ++i) {
-            Listener l = (Listener)Kit.getListener(listeners, i);
-            if (l == null)
-                break;
+            Listener l = (Listener) Kit.getListener(listeners, i);
+            if (l == null) break;
             l.contextReleased(cx);
         }
     }
 
-    public final void addListener(Listener listener)
-    {
+    public final void addListener(Listener listener) {
         checkNotSealed();
         synchronized (listenersLock) {
             if (disabledListening) {
@@ -442,8 +415,7 @@
         }
     }
 
-    public final void removeListener(Listener listener)
-    {
+    public final void removeListener(Listener listener) {
         checkNotSealed();
         synchronized (listenersLock) {
             if (disabledListening) {
@@ -453,12 +425,8 @@
         }
     }
 
-    /**
-     * The method is used only to implement
-     * Context.disableStaticContextListening()
-     */
-    final void disableContextListening()
-    {
+    /** The method is used only to implement Context.disableStaticContextListening() */
+    final void disableContextListening() {
         checkNotSealed();
         synchronized (listenersLock) {
             disabledListening = true;
@@ -468,59 +436,52 @@
 
     /**
      * Checks if this is a sealed ContextFactory.
+     *
      * @see #seal()
      */
-    public final boolean isSealed()
-    {
+    public final boolean isSealed() {
         return sealed;
     }
 
     /**
-     * Seal this ContextFactory so any attempt to modify it like to add or
-     * remove its listeners will throw an exception.
+     * Seal this ContextFactory so any attempt to modify it like to add or remove its listeners will
+     * throw an exception.
+     *
      * @see #isSealed()
      */
-    public final void seal()
-    {
+    public final void seal() {
         checkNotSealed();
         sealed = true;
     }
 
-    protected final void checkNotSealed()
-    {
+    protected final void checkNotSealed() {
         if (sealed) throw new IllegalStateException();
     }
 
     /**
-     * Call {@link ContextAction#run(Context cx)}
-     * using the {@link Context} instance associated with the current thread.
-     * If no Context is associated with the thread, then
-     * {@link #makeContext()} will be called to construct
-     * new Context instance. The instance will be temporary associated
+     * Call {@link ContextAction#run(Context cx)} using the {@link Context} instance associated with
+     * the current thread. If no Context is associated with the thread, then {@link #makeContext()}
+     * will be called to construct new Context instance. The instance will be temporary associated
      * with the thread during call to {@link ContextAction#run(Context)}.
      *
      * @see ContextFactory#call(ContextAction)
-     * @see Context#call(ContextFactory factory, Callable callable,
-     *                   Scriptable scope, Scriptable thisObj,
-     *                   Object[] args)
+     * @see Context#call(ContextFactory factory, Callable callable, Scriptable scope, Scriptable
+     *     thisObj, Object[] args)
      */
-    public final Object call(ContextAction action)
-    {
+    public final <T> T call(ContextAction<T> action) {
         return Context.call(this, action);
     }
 
     /**
-     * Get a context associated with the current thread, creating one if need
-     * be. The Context stores the execution state of the JavaScript engine, so
-     * it is required that the context be entered before execution may begin.
-     * Once a thread has entered a Context, then getCurrentContext() may be
-     * called to find the context that is associated with the current thread.
-     * <p>
-     * Calling <code>enterContext()</code> will return either the Context
-     * currently associated with the thread, or will create a new context and
-     * associate it with the current thread. Each call to
-     * <code>enterContext()</code> must have a matching call to
-     * {@link Context#exit()}.
+     * Get a context associated with the current thread, creating one if need be. The Context stores
+     * the execution state of the JavaScript engine, so it is required that the context be entered
+     * before execution may begin. Once a thread has entered a Context, then getCurrentContext() may
+     * be called to find the context that is associated with the current thread.
+     *
+     * <p>Calling <code>enterContext()</code> will return either the Context currently associated
+     * with the thread, or will create a new context and associate it with the current thread. Each
+     * call to <code>enterContext()</code> must have a matching call to {@link Context#exit()}.
+     *
      * <pre>
      *      Context cx = contextFactory.enterContext();
      *      try {
@@ -530,10 +491,11 @@
      *          Context.exit();
      *      }
      * </pre>
-     * Instead of using <tt>enterContext()</tt>, <tt>exit()</tt> pair consider
-     * using {@link #call(ContextAction)} which guarantees proper association
-     * of Context instances with the current thread.
-     * With this method the above example becomes:
+     *
+     * Instead of using <code>enterContext()</code>, <code>exit()</code> pair consider using {@link
+     * #call(ContextAction)} which guarantees proper association of Context instances with the
+     * current thread. With this method the above example becomes:
+     *
      * <pre>
      *      ContextFactory.call(new ContextAction() {
      *          public Object run(Context cx) {
@@ -543,13 +505,13 @@
      *          }
      *      });
      * </pre>
+     *
      * @return a Context associated with the current thread
      * @see Context#getCurrentContext()
      * @see Context#exit()
      * @see #call(ContextAction)
      */
-    public Context enterContext()
-    {
+    public Context enterContext() {
         return enterContext(null);
     }
 
@@ -558,37 +520,31 @@
      * @return a Context associated with the current thread
      */
     @Deprecated
-    public final Context enter()
-    {
+    public final Context enter() {
         return enterContext(null);
     }
 
-    /**
-     * @deprecated Use {@link Context#exit()} instead.
-     */
+    /** @deprecated Use {@link Context#exit()} instead. */
     @Deprecated
-    public final void exit()
-    {
+    public final void exit() {
         Context.exit();
     }
 
     /**
-     * Get a Context associated with the current thread, using the given
-     * Context if need be.
-     * <p>
-     * The same as <code>enterContext()</code> except that <code>cx</code>
-     * is associated with the current thread and returned if the current thread
-     * has no associated context and <code>cx</code> is not associated with any
-     * other thread.
+     * Get a Context associated with the current thread, using the given Context if need be.
+     *
+     * <p>The same as <code>enterContext()</code> except that <code>cx</code> is associated with the
+     * current thread and returned if the current thread has no associated context and <code>cx
+     * </code> is not associated with any other thread.
+     *
      * @param cx a Context to associate with the thread if possible
      * @return a Context associated with the current thread
      * @see #enterContext()
      * @see #call(ContextAction)
-     * @throws IllegalStateException if <code>cx</code> is already associated
-     * with a different thread
+     * @throws IllegalStateException if <code>cx</code> is already associated with a different
+     *     thread
      */
-    public final Context enterContext(Context cx)
-    {
+    public final Context enterContext(Context cx) {
         return Context.enter(cx, this);
     }
-}
\ No newline at end of file
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/Context.java rhino-1.7.14/src/org/mozilla/javascript/Context.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/Context.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/Context.java	2022-01-06 22:57:21.000000000 +0100
@@ -10,24 +10,22 @@
 
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
+import java.io.Closeable;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.PrintWriter;
 import java.io.Reader;
 import java.io.StringWriter;
 import java.io.Writer;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
-import java.net.URL;
-import java.util.Enumeration;
+import java.util.ArrayDeque;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
-import java.util.jar.Attributes;
-import java.util.jar.Manifest;
-
+import java.util.function.UnaryOperator;
+import org.mozilla.classfile.ClassFileWriter.ClassFileFormatException;
 import org.mozilla.javascript.ast.AstRoot;
 import org.mozilla.javascript.ast.ScriptNode;
 import org.mozilla.javascript.debug.DebuggableScript;
@@ -37,325 +35,345 @@
 /**
  * This class represents the runtime context of an executing script.
  *
- * Before executing a script, an instance of Context must be created
- * and associated with the thread that will be executing the script.
- * The Context will be used to store information about the executing
- * of the script such as the call stack. Contexts are associated with
- * the current thread  using the {@link #call(ContextAction)}
- * or {@link #enter()} methods.<p>
+ * <p>Before executing a script, an instance of Context must be created and associated with the
+ * thread that will be executing the script. The Context will be used to store information about the
+ * executing of the script such as the call stack. Contexts are associated with the current thread
+ * using the {@link #call(ContextAction)} or {@link #enter()} methods.
  *
- * Different forms of script execution are supported. Scripts may be
- * evaluated from the source directly, or first compiled and then later
- * executed. Interactive execution is also supported.<p>
+ * <p>Different forms of script execution are supported. Scripts may be evaluated from the source
+ * directly, or first compiled and then later executed. Interactive execution is also supported.
  *
- * Some aspects of script execution, such as type conversions and
- * object creation, may be accessed directly through methods of
- * Context.
+ * <p>Some aspects of script execution, such as type conversions and object creation, may be
+ * accessed directly through methods of Context.
  *
  * @see Scriptable
  * @author Norris Boyd
  * @author Brendan Eich
  */
-
-public class Context
-{
+public class Context implements Closeable {
     /**
      * Language versions.
      *
-     * All integral values are reserved for future version numbers.
+     * <p>All integral values are reserved for future version numbers.
      */
 
     /**
      * The unknown version.
+     *
+     * <p>Be aware, this version will not support many of the newer language features and will not
+     * change in the future.
+     *
+     * <p>Please use one of the other constants like VERSION_ES6 to get support for recent language
+     * features.
      */
-    public static final int VERSION_UNKNOWN =   -1;
+    public static final int VERSION_UNKNOWN = -1;
 
-    /**
-     * The default version.
-     */
-    public static final int VERSION_DEFAULT =    0;
+    /** The default version. */
+    public static final int VERSION_DEFAULT = 0;
 
-    /**
-     * JavaScript 1.0
-     */
-    public static final int VERSION_1_0 =      100;
+    /** JavaScript 1.0 */
+    public static final int VERSION_1_0 = 100;
 
-    /**
-     * JavaScript 1.1
-     */
-    public static final int VERSION_1_1 =      110;
+    /** JavaScript 1.1 */
+    public static final int VERSION_1_1 = 110;
 
-    /**
-     * JavaScript 1.2
-     */
-    public static final int VERSION_1_2 =      120;
+    /** JavaScript 1.2 */
+    public static final int VERSION_1_2 = 120;
 
-    /**
-     * JavaScript 1.3
-     */
-    public static final int VERSION_1_3 =      130;
+    /** JavaScript 1.3 */
+    public static final int VERSION_1_3 = 130;
 
-    /**
-     * JavaScript 1.4
-     */
-    public static final int VERSION_1_4 =      140;
+    /** JavaScript 1.4 */
+    public static final int VERSION_1_4 = 140;
 
-    /**
-     * JavaScript 1.5
-     */
-    public static final int VERSION_1_5 =      150;
+    /** JavaScript 1.5 */
+    public static final int VERSION_1_5 = 150;
 
-    /**
-     * JavaScript 1.6
-     */
-    public static final int VERSION_1_6 =      160;
+    /** JavaScript 1.6 */
+    public static final int VERSION_1_6 = 160;
 
-    /**
-     * JavaScript 1.7
-     */
-    public static final int VERSION_1_7 =      170;
+    /** JavaScript 1.7 */
+    public static final int VERSION_1_7 = 170;
 
-    /**
-     * JavaScript 1.8
-     */
-    public static final int VERSION_1_8 =      180;
+    /** JavaScript 1.8 */
+    public static final int VERSION_1_8 = 180;
 
-    /**
-     * ECMAScript 6.
-     */
-    public static final int VERSION_ES6 =      200;
+    /** ECMAScript 6. */
+    public static final int VERSION_ES6 = 200;
 
     /**
-     * Controls behaviour of <tt>Date.prototype.getYear()</tt>.
-     * If <tt>hasFeature(FEATURE_NON_ECMA_GET_YEAR)</tt> returns true,
-     * Date.prototype.getYear subtructs 1900 only if 1900 <= date < 2000.
-     * The default behavior of {@link #hasFeature(int)} is always to subtruct
-     * 1900 as rquired by ECMAScript B.2.4.
+     * Controls behaviour of <code>Date.prototype.getYear()</code>. If <code>
+     * hasFeature(FEATURE_NON_ECMA_GET_YEAR)</code> returns true, Date.prototype.getYear subtructs
+     * 1900 only if 1900 <= date < 2000. The default behavior of {@link #hasFeature(int)} is
+     * always to subtruct 1900 as rquired by ECMAScript B.2.4.
      */
     public static final int FEATURE_NON_ECMA_GET_YEAR = 1;
 
     /**
-     * Control if member expression as function name extension is available.
-     * If <tt>hasFeature(FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME)</tt> returns
-     * true, allow <tt>function memberExpression(args) { body }</tt> to be
-     * syntax sugar for <tt>memberExpression = function(args) { body }</tt>,
-     * when memberExpression is not a simple identifier.
-     * See ECMAScript-262, section 11.2 for definition of memberExpression.
-     * By default {@link #hasFeature(int)} returns false.
+     * Control if member expression as function name extension is available. If <code>
+     * hasFeature(FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME)</code> returns true, allow <code>
+     * function memberExpression(args) { body }</code> to be syntax sugar for <code>
+     * memberExpression = function(args) { body }</code>, when memberExpression is not a simple
+     * identifier. See ECMAScript-262, section 11.2 for definition of memberExpression. By default
+     * {@link #hasFeature(int)} returns false.
      */
     public static final int FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME = 2;
 
     /**
-     * Control if reserved keywords are treated as identifiers.
-     * If <tt>hasFeature(RESERVED_KEYWORD_AS_IDENTIFIER)</tt> returns true,
-     * treat future reserved keyword (see  Ecma-262, section 7.5.3) as ordinary
-     * identifiers but warn about this usage.
+     * Control if reserved keywords are treated as identifiers. If <code>
+     * hasFeature(RESERVED_KEYWORD_AS_IDENTIFIER)</code> returns true, treat future reserved keyword
+     * (see Ecma-262, section 7.5.3) as ordinary identifiers but warn about this usage.
      *
-     * By default {@link #hasFeature(int)} returns false.
+     * <p>By default {@link #hasFeature(int)} returns false.
      */
     public static final int FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER = 3;
 
     /**
-     * Control if <tt>toString()</tt> should returns the same result
-     * as  <tt>toSource()</tt> when applied to objects and arrays.
-     * If <tt>hasFeature(FEATURE_TO_STRING_AS_SOURCE)</tt> returns true,
-     * calling <tt>toString()</tt> on JS objects gives the same result as
-     * calling <tt>toSource()</tt>. That is it returns JS source with code
-     * to create an object with all enumeratable fields of the original object
-     * instead of printing <tt>[object <i>result of
-     * {@link Scriptable#getClassName()}</i>]</tt>.
-     * <p>
-     * By default {@link #hasFeature(int)} returns true only if
-     * the current JS version is set to {@link #VERSION_1_2}.
+     * Control if <code>toString()</code> should returns the same result as <code>toSource()</code>
+     * when applied to objects and arrays. If <code>hasFeature(FEATURE_TO_STRING_AS_SOURCE)</code>
+     * returns true, calling <code>toString()</code> on JS objects gives the same result as calling
+     * <code>toSource()</code>. That is it returns JS source with code to create an object with all
+     * enumeratable fields of the original object instead of printing <code>[object <i>result of
+     * {@link Scriptable#getClassName()}</i>]</code>.
+     *
+     * <p>By default {@link #hasFeature(int)} returns true only if the current JS version is set to
+     * {@link #VERSION_1_2}.
      */
     public static final int FEATURE_TO_STRING_AS_SOURCE = 4;
 
     /**
-     * Control if properties <tt>__proto__</tt> and <tt>__parent__</tt>
-     * are treated specially.
-     * If <tt>hasFeature(FEATURE_PARENT_PROTO_PROPERTIES)</tt> returns true,
-     * treat <tt>__parent__</tt> and <tt>__proto__</tt> as special properties.
-     * <p>
-     * The properties allow to query and set scope and prototype chains for the
-     * objects. The special meaning of the properties is available
-     * only when they are used as the right hand side of the dot operator.
-     * For example, while <tt>x.__proto__ = y</tt> changes the prototype
-     * chain of the object <tt>x</tt> to point to <tt>y</tt>,
-     * <tt>x["__proto__"] = y</tt> simply assigns a new value to the property
-     * <tt>__proto__</tt> in <tt>x</tt> even when the feature is on.
+     * Control if properties <code>__proto__</code> and <code>__parent__</code> are treated
+     * specially. If <code>hasFeature(FEATURE_PARENT_PROTO_PROPERTIES)</code> returns true, treat
+     * <code>__parent__</code> and <code>__proto__</code> as special properties.
+     *
+     * <p>The properties allow to query and set scope and prototype chains for the objects. The
+     * special meaning of the properties is available only when they are used as the right hand side
+     * of the dot operator. For example, while <code>x.__proto__ = y</code> changes the prototype
+     * chain of the object <code>x</code> to point to <code>y</code>, <code>x["__proto__"] = y
+     * </code> simply assigns a new value to the property <code>__proto__</code> in <code>x</code>
+     * even when the feature is on.
      *
-     * By default {@link #hasFeature(int)} returns true.
+     * <p>By default {@link #hasFeature(int)} returns true.
      */
     public static final int FEATURE_PARENT_PROTO_PROPERTIES = 5;
 
-    /**
-     * @deprecated In previous releases, this name was given to
-     * FEATURE_PARENT_PROTO_PROPERTIES.
-     */
-    @Deprecated
-    public static final int FEATURE_PARENT_PROTO_PROPRTIES = 5;
+    /** @deprecated In previous releases, this name was given to FEATURE_PARENT_PROTO_PROPERTIES. */
+    @Deprecated public static final int FEATURE_PARENT_PROTO_PROPRTIES = 5;
 
     /**
-     * Control if support for E4X(ECMAScript for XML) extension is available.
-     * If hasFeature(FEATURE_E4X) returns true, the XML syntax is available.
-     * <p>
-     * By default {@link #hasFeature(int)} returns true if
-     * the current JS version is set to {@link #VERSION_DEFAULT}
-     * or is at least {@link #VERSION_1_6}.
+     * Control if support for E4X(ECMAScript for XML) extension is available. If
+     * hasFeature(FEATURE_E4X) returns true, the XML syntax is available.
+     *
+     * <p>By default {@link #hasFeature(int)} returns true if the current JS version is set to
+     * {@link #VERSION_DEFAULT} or is at least {@link #VERSION_1_6}.
+     *
      * @since 1.6 Release 1
      */
     public static final int FEATURE_E4X = 6;
 
     /**
-     * Control if dynamic scope should be used for name access.
-     * If hasFeature(FEATURE_DYNAMIC_SCOPE) returns true, then the name lookup
-     * during name resolution will use the top scope of the script or function
-     * which is at the top of JS execution stack instead of the top scope of the
-     * script or function from the current stack frame if the top scope of
-     * the top stack frame contains the top scope of the current stack frame
-     * on its prototype chain.
-     * <p>
-     * This is useful to define shared scope containing functions that can
-     * be called from scripts and functions using private scopes.
-     * <p>
-     * By default {@link #hasFeature(int)} returns false.
+     * Control if dynamic scope should be used for name access. If hasFeature(FEATURE_DYNAMIC_SCOPE)
+     * returns true, then the name lookup during name resolution will use the top scope of the
+     * script or function which is at the top of JS execution stack instead of the top scope of the
+     * script or function from the current stack frame if the top scope of the top stack frame
+     * contains the top scope of the current stack frame on its prototype chain.
+     *
+     * <p>This is useful to define shared scope containing functions that can be called from scripts
+     * and functions using private scopes.
+     *
+     * <p>By default {@link #hasFeature(int)} returns false.
+     *
      * @since 1.6 Release 1
      */
     public static final int FEATURE_DYNAMIC_SCOPE = 7;
 
     /**
-     * Control if strict variable mode is enabled.
-     * When the feature is on Rhino reports runtime errors if assignment
-     * to a global variable that does not exist is executed. When the feature
-     * is off such assignments create a new variable in the global scope as
-     * required by ECMA 262.
-     * <p>
-     * By default {@link #hasFeature(int)} returns false.
+     * Control if strict variable mode is enabled. When the feature is on Rhino reports runtime
+     * errors if assignment to a global variable that does not exist is executed. When the feature
+     * is off such assignments create a new variable in the global scope as required by ECMA 262.
+     *
+     * <p>By default {@link #hasFeature(int)} returns false.
+     *
      * @since 1.6 Release 1
      */
     public static final int FEATURE_STRICT_VARS = 8;
 
     /**
-     * Control if strict eval mode is enabled.
-     * When the feature is on Rhino reports runtime errors if non-string
-     * argument is passed to the eval function. When the feature is off
-     * eval simply return non-string argument as is without performing any
-     * evaluation as required by ECMA 262.
-     * <p>
-     * By default {@link #hasFeature(int)} returns false.
+     * Control if strict eval mode is enabled. When the feature is on Rhino reports runtime errors
+     * if non-string argument is passed to the eval function. When the feature is off eval simply
+     * return non-string argument as is without performing any evaluation as required by ECMA 262.
+     *
+     * <p>By default {@link #hasFeature(int)} returns false.
+     *
      * @since 1.6 Release 1
      */
     public static final int FEATURE_STRICT_EVAL = 9;
 
     /**
-     * When the feature is on Rhino will add a "fileName" and "lineNumber"
-     * properties to Error objects automatically. When the feature is off, you
-     * have to explicitly pass them as the second and third argument to the
-     * Error constructor. Note that neither behavior is fully ECMA 262
-     * compliant (as 262 doesn't specify a three-arg constructor), but keeping
-     * the feature off results in Error objects that don't have
-     * additional non-ECMA properties when constructed using the ECMA-defined
-     * single-arg constructor and is thus desirable if a stricter ECMA
-     * compliance is desired, specifically adherence to the point 15.11.5. of
-     * the standard.
-     * <p>
-     * By default {@link #hasFeature(int)} returns false.
+     * When the feature is on Rhino will add a "fileName" and "lineNumber" properties to Error
+     * objects automatically. When the feature is off, you have to explicitly pass them as the
+     * second and third argument to the Error constructor. Note that neither behavior is fully ECMA
+     * 262 compliant (as 262 doesn't specify a three-arg constructor), but keeping the feature off
+     * results in Error objects that don't have additional non-ECMA properties when constructed
+     * using the ECMA-defined single-arg constructor and is thus desirable if a stricter ECMA
+     * compliance is desired, specifically adherence to the point 15.11.5. of the standard.
+     *
+     * <p>By default {@link #hasFeature(int)} returns false.
+     *
      * @since 1.6 Release 6
      */
     public static final int FEATURE_LOCATION_INFORMATION_IN_ERROR = 10;
 
     /**
-     * Controls whether JS 1.5 'strict mode' is enabled.
-     * When the feature is on, Rhino reports more than a dozen different
-     * warnings.  When the feature is off, these warnings are not generated.
+     * Controls whether JS 1.5 'strict mode' is enabled. When the feature is on, Rhino reports more
+     * than a dozen different warnings. When the feature is off, these warnings are not generated.
      * FEATURE_STRICT_MODE implies FEATURE_STRICT_VARS and FEATURE_STRICT_EVAL.
-     * <p>
-     * By default {@link #hasFeature(int)} returns false.
+     *
+     * <p>By default {@link #hasFeature(int)} returns false.
+     *
      * @since 1.6 Release 6
      */
     public static final int FEATURE_STRICT_MODE = 11;
 
     /**
      * Controls whether a warning should be treated as an error.
+     *
      * @since 1.6 Release 6
      */
     public static final int FEATURE_WARNING_AS_ERROR = 12;
 
     /**
-     * Enables enhanced access to Java.
-     * Specifically, controls whether private and protected members can be
-     * accessed, and whether scripts can catch all Java exceptions.
-     * <p>
-     * Note that this feature should only be enabled for trusted scripts.
-     * <p>
-     * By default {@link #hasFeature(int)} returns false.
+     * Enables enhanced access to Java. Specifically, controls whether private and protected members
+     * can be accessed, and whether scripts can catch all Java exceptions.
+     *
+     * <p>Note that this feature should only be enabled for trusted scripts.
+     *
+     * <p>By default {@link #hasFeature(int)} returns false.
+     *
      * @since 1.7 Release 1
      */
     public static final int FEATURE_ENHANCED_JAVA_ACCESS = 13;
 
     /**
-     * Enables access to JavaScript features from ECMAscript 6 that are present in
-     * JavaScript engines that do not yet support version 6, such as V8.
-     * This includes support for typed arrays. Default is true.
+     * Enables access to JavaScript features from ECMAscript 6 that are present in JavaScript
+     * engines that do not yet support version 6, such as V8. This includes support for typed
+     * arrays. Default is true.
+     *
      * @since 1.7 Release 3
      */
     public static final int FEATURE_V8_EXTENSIONS = 14;
 
     /**
-     * Defines how an undefined  "this" parameter is handled in certain calls. Previously Rhino
-     * would convert an undefined "this" to null, whereas recent specs call for it to be treated
-     * differently. Default is to be set if language version <= 1.7.
+     * Defines how an undefined "this" parameter is handled in certain calls. Previously Rhino would
+     * convert an undefined "this" to null, whereas recent specs call for it to be treated
+     * differently. Default is to be set if language version <= 1.7.
+     *
      * @since 1.7.7
      */
     public static final int FEATURE_OLD_UNDEF_NULL_THIS = 15;
 
     /**
-     * If set, then the order of property key enumeration will be first numeric keys in numeric order,
-     * followed by string keys in order of creation, and finally Symbol keys, as specified in ES6.
-     * Default is true for language version >= "ES6" and false otherwise.
-     * @since 1.7.8
+     * If set, then the order of property key enumeration will be first numeric keys in numeric
+     * order, followed by string keys in order of creation, and finally Symbol keys, as specified in
+     * ES6. Default is true for language version >= "ES6" and false otherwise.
+     *
+     * @since 1.7.7.1
      */
     public static final int FEATURE_ENUMERATE_IDS_FIRST = 16;
 
-    public static final String languageVersionProperty = "language version";
-    public static final String errorReporterProperty   = "error reporter";
+    /**
+     * If set, then all objects will have a thread-safe property map. (Note that this doesn't make
+     * everything else that they do thread-safe -- that depends on the specific implementation. If
+     * not set, users should not share Rhino objects between threads, unless the "sync" function is
+     * used to wrap them with an explicit synchronizer. The default is false, which means that by
+     * default, individual objects are not thread-safe.
+     *
+     * @since 1.7.8
+     */
+    public static final int FEATURE_THREAD_SAFE_OBJECTS = 17;
+
+    /**
+     * If set, then all integer numbers will be returned without decimal place. For instance assume
+     * there is a function like this: <code>function foo() {return 5;}</code> 5 will be returned if
+     * feature is set, 5.0 otherwise.
+     */
+    public static final int FEATURE_INTEGER_WITHOUT_DECIMAL_PLACE = 18;
+
+    /**
+     * TypedArray buffer uses little/big endian depending on the platform. The default is big endian
+     * for Rhino.
+     *
+     * @since 1.7 Release 11
+     */
+    public static final int FEATURE_LITTLE_ENDIAN = 19;
+
+    /**
+     * Configure the XMLProcessor to parse XML with security features or not. Security features
+     * include not fetching remote entity references and disabling XIncludes
+     *
+     * @since 1.7 Release 12
+     */
+    public static final int FEATURE_ENABLE_XML_SECURE_PARSING = 20;
 
     /**
-     * Convenient value to use as zero-length array of objects.
+     * Configure whether the entries in a Java Map can be accessed by properties.
+     *
+     * <p>Not enabled:
+     *
+     * <p>var map = new java.util.HashMap(); map.put('foo', 1); map.foo; // undefined
+     *
+     * <p>Enabled:
+     *
+     * <p>var map = new java.util.HashMap(); map.put('foo', 1); map.foo; // 1
+     *
+     * <p>WARNING: This feature is similar to the one in Nashorn, but incomplete.
+     *
+     * <p>1. A entry has priority over method.
+     *
+     * <p>map.put("put", "abc"); map.put; // abc map.put("put", "efg"); // ERROR
+     *
+     * <p>2. The distinction between numeric keys and string keys is ambiguous.
+     *
+     * <p>map.put('1', 123); map['1']; // Not found. This means `map[1]`.
+     *
+     * @since 1.7 Release 14
      */
+    public static final int FEATURE_ENABLE_JAVA_MAP_ACCESS = 21;
+
+    public static final String languageVersionProperty = "language version";
+    public static final String errorReporterProperty = "error reporter";
+
+    /** Convenient value to use as zero-length array of objects. */
     public static final Object[] emptyArgs = ScriptRuntime.emptyArgs;
 
     /**
      * Creates a new Context. The context will be associated with the {@link
      * ContextFactory#getGlobal() global context factory}.
      *
-     * Note that the Context must be associated with a thread before
-     * it can be used to execute a script.
-     * @deprecated this constructor is deprecated because it creates a
-     * dependency on a static singleton context factory. Use
-     * {@link ContextFactory#enter()} or
-     * {@link ContextFactory#call(ContextAction)} instead. If you subclass
-     * this class, consider using {@link #Context(ContextFactory)} constructor
-     * instead in the subclasses' constructors.
+     * <p>Note that the Context must be associated with a thread before it can be used to execute a
+     * script.
+     *
+     * @deprecated this constructor is deprecated because it creates a dependency on a static
+     *     singleton context factory. Use {@link ContextFactory#enter()} or {@link
+     *     ContextFactory#call(ContextAction)} instead. If you subclass this class, consider using
+     *     {@link #Context(ContextFactory)} constructor instead in the subclasses' constructors.
      */
     @Deprecated
-    public Context()
-    {
+    public Context() {
         this(ContextFactory.getGlobal());
     }
 
     /**
-     * Creates a new context. Provided as a preferred super constructor for
-     * subclasses in place of the deprecated default public constructor.
-     * @param factory the context factory associated with this context (most
-     * likely, the one that created the context). Can not be null. The context
-     * features are inherited from the factory, and the context will also
-     * otherwise use its factory's services.
+     * Creates a new context. Provided as a preferred super constructor for subclasses in place of
+     * the deprecated default public constructor.
+     *
+     * @param factory the context factory associated with this context (most likely, the one that
+     *     created the context). Can not be null. The context features are inherited from the
+     *     factory, and the context will also otherwise use its factory's services.
      * @throws IllegalArgumentException if factory parameter is null.
      */
-    protected Context(ContextFactory factory)
-    {
-        if(factory == null) {
+    protected Context(ContextFactory factory) {
+        if (factory == null) {
             throw new IllegalArgumentException("factory == null");
         }
         this.factory = factory;
@@ -367,57 +385,53 @@
     /**
      * Get the current Context.
      *
-     * The current Context is per-thread; this method looks up
-     * the Context associated with the current thread. <p>
+     * <p>The current Context is per-thread; this method looks up the Context associated with the
+     * current thread.
+     *
+     * <p>
      *
-     * @return the Context associated with the current thread, or
-     *         null if no context is associated with the current
-     *         thread.
+     * @return the Context associated with the current thread, or null if no context is associated
+     *     with the current thread.
      * @see ContextFactory#enterContext()
      * @see ContextFactory#call(ContextAction)
      */
-    public static Context getCurrentContext()
-    {
+    public static Context getCurrentContext() {
         Object helper = VMBridge.instance.getThreadContextHelper();
         return VMBridge.instance.getContext(helper);
     }
 
     /**
-     * Same as calling {@link ContextFactory#enterContext()} on the global
-     * ContextFactory instance.
+     * Same as calling {@link ContextFactory#enterContext()} on the global ContextFactory instance.
+     *
      * @return a Context associated with the current thread
      * @see #getCurrentContext()
      * @see #exit()
      * @see #call(ContextAction)
      */
-    public static Context enter()
-    {
-        return enter(null);
+    public static Context enter() {
+        return enter(null, ContextFactory.getGlobal());
     }
 
     /**
-     * Get a Context associated with the current thread, using
-     * the given Context if need be.
-     * <p>
-     * The same as <code>enter()</code> except that <code>cx</code>
-     * is associated with the current thread and returned if
-     * the current thread has no associated context and <code>cx</code>
-     * is not associated with any other thread.
+     * Get a Context associated with the current thread, using the given Context if need be.
+     *
+     * <p>The same as <code>enter()</code> except that <code>cx</code> is associated with the
+     * current thread and returned if the current thread has no associated context and <code>cx
+     * </code> is not associated with any other thread.
+     *
      * @param cx a Context to associate with the thread if possible
      * @return a Context associated with the current thread
-     * @deprecated use {@link ContextFactory#enterContext(Context)} instead as
-     * this method relies on usage of a static singleton "global" ContextFactory.
+     * @deprecated use {@link ContextFactory#enterContext(Context)} instead as this method relies on
+     *     usage of a static singleton "global" ContextFactory.
      * @see ContextFactory#enterContext(Context)
      * @see ContextFactory#call(ContextAction)
      */
     @Deprecated
-    public static Context enter(Context cx)
-    {
+    public static Context enter(Context cx) {
         return enter(cx, ContextFactory.getGlobal());
     }
 
-    static final Context enter(Context cx, ContextFactory factory)
-    {
+    static final Context enter(Context cx, ContextFactory factory) {
         Object helper = VMBridge.instance.getThreadContextHelper();
         Context old = VMBridge.instance.getContext(helper);
         if (old != null) {
@@ -426,7 +440,8 @@
             if (cx == null) {
                 cx = factory.makeContext();
                 if (cx.enterCount != 0) {
-                    throw new IllegalStateException("factory.makeContext() returned Context instance already associated with some thread");
+                    throw new IllegalStateException(
+                            "factory.makeContext() returned Context instance already associated with some thread");
                 }
                 factory.onContextCreated(cx);
                 if (factory.isSealed() && !cx.isSealed()) {
@@ -434,33 +449,31 @@
                 }
             } else {
                 if (cx.enterCount != 0) {
-                    throw new IllegalStateException("can not use Context instance already associated with some thread");
+                    throw new IllegalStateException(
+                            "can not use Context instance already associated with some thread");
                 }
             }
             VMBridge.instance.setContext(helper, cx);
         }
         ++cx.enterCount;
         return cx;
-     }
+    }
 
     /**
      * Exit a block of code requiring a Context.
      *
-     * Calling <code>exit()</code> will remove the association between
-     * the current thread and a Context if the prior call to
-     * {@link ContextFactory#enterContext()} on this thread newly associated a
-     * Context with this thread. Once the current thread no longer has an
-     * associated Context, it cannot be used to execute JavaScript until it is
-     * again associated with a Context.
+     * <p>Calling <code>exit()</code> will remove the association between the current thread and a
+     * Context if the prior call to {@link ContextFactory#enterContext()} on this thread newly
+     * associated a Context with this thread. Once the current thread no longer has an associated
+     * Context, it cannot be used to execute JavaScript until it is again associated with a Context.
+     *
      * @see ContextFactory#enterContext()
      */
-    public static void exit()
-    {
+    public static void exit() {
         Object helper = VMBridge.instance.getThreadContextHelper();
         Context cx = VMBridge.instance.getContext(helper);
         if (cx == null) {
-            throw new IllegalStateException(
-                "Calling Context.exit without previous Context.enter");
+            throw new IllegalStateException("Calling Context.exit without previous Context.enter");
         }
         if (cx.enterCount < 1) Kit.codeBug();
         if (--cx.enterCount == 0) {
@@ -469,63 +482,58 @@
         }
     }
 
+    @Override
+    public void close() {
+        exit();
+    }
+
     /**
-     * Call {@link ContextAction#run(Context cx)}
-     * using the Context instance associated with the current thread.
-     * If no Context is associated with the thread, then
-     * <tt>ContextFactory.getGlobal().makeContext()</tt> will be called to
-     * construct new Context instance. The instance will be temporary
-     * associated with the thread during call to
-     * {@link ContextAction#run(Context)}.
-     * @deprecated use {@link ContextFactory#call(ContextAction)} instead as
-     * this method relies on usage of a static singleton "global"
-     * ContextFactory.
+     * Call {@link ContextAction#run(Context cx)} using the Context instance associated with the
+     * current thread. If no Context is associated with the thread, then <code>
+     * ContextFactory.getGlobal().makeContext()</code> will be called to construct new Context
+     * instance. The instance will be temporary associated with the thread during call to {@link
+     * ContextAction#run(Context)}.
+     *
+     * @deprecated use {@link ContextFactory#call(ContextAction)} instead as this method relies on
+     *     usage of a static singleton "global" ContextFactory.
      * @return The result of {@link ContextAction#run(Context)}.
      */
     @Deprecated
-    public static Object call(ContextAction action)
-    {
+    public static <T> T call(ContextAction<T> action) {
         return call(ContextFactory.getGlobal(), action);
     }
 
     /**
-     * Call {@link
-     * Callable#call(Context cx, Scriptable scope, Scriptable thisObj,
-     *               Object[] args)}
-     * using the Context instance associated with the current thread.
-     * If no Context is associated with the thread, then
-     * {@link ContextFactory#makeContext()} will be called to construct
-     * new Context instance. The instance will be temporary associated
-     * with the thread during call to {@link ContextAction#run(Context)}.
-     * <p>
-     * It is allowed but not advisable to use null for <tt>factory</tt>
-     * argument in which case the global static singleton ContextFactory
-     * instance will be used to create new context instances.
+     * Call {@link Callable#call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args)}
+     * using the Context instance associated with the current thread. If no Context is associated
+     * with the thread, then {@link ContextFactory#makeContext()} will be called to construct new
+     * Context instance. The instance will be temporary associated with the thread during call to
+     * {@link ContextAction#run(Context)}.
+     *
+     * <p>It is allowed but not advisable to use null for <code>factory</code> argument in which
+     * case the global static singleton ContextFactory instance will be used to create new context
+     * instances.
+     *
      * @see ContextFactory#call(ContextAction)
      */
-    public static Object call(ContextFactory factory, final Callable callable,
-                              final Scriptable scope, final Scriptable thisObj,
-                              final Object[] args)
-    {
-        if(factory == null) {
+    public static Object call(
+            ContextFactory factory,
+            final Callable callable,
+            final Scriptable scope,
+            final Scriptable thisObj,
+            final Object[] args) {
+        if (factory == null) {
             factory = ContextFactory.getGlobal();
         }
-        return call(factory, new ContextAction() {
-            public Object run(Context cx) {
-                return callable.call(cx, scope, thisObj, args);
-            }
-        });
+        return call(factory, cx -> callable.call(cx, scope, thisObj, args));
     }
 
-    /**
-     * The method implements {@link ContextFactory#call(ContextAction)} logic.
-     */
-    static Object call(ContextFactory factory, ContextAction action) {
+    /** The method implements {@link ContextFactory#call(ContextAction)} logic. */
+    static <T> T call(ContextFactory factory, ContextAction<T> action) {
         Context cx = enter(null, factory);
         try {
             return action.run(cx);
-        }
-        finally {
+        } finally {
             exit();
         }
     }
@@ -536,23 +544,19 @@
      * @see ContextFactory#getGlobal()
      */
     @Deprecated
-    public static void addContextListener(ContextListener listener)
-    {
+    public static void addContextListener(ContextListener listener) {
         // Special workaround for the debugger
         String DBG = "org.mozilla.javascript.tools.debugger.Main";
         if (DBG.equals(listener.getClass().getName())) {
             Class<?> cl = listener.getClass();
-            Class<?> factoryClass = Kit.classOrNull(
-                "org.mozilla.javascript.ContextFactory");
-            Class<?>[] sig = { factoryClass };
-            Object[] args = { ContextFactory.getGlobal() };
+            Class<?> factoryClass = Kit.classOrNull("org.mozilla.javascript.ContextFactory");
+            Class<?>[] sig = {factoryClass};
+            Object[] args = {ContextFactory.getGlobal()};
             try {
                 Method m = cl.getMethod("attachTo", sig);
                 m.invoke(listener, args);
             } catch (Exception ex) {
-                RuntimeException rex = new RuntimeException();
-                Kit.initCause(rex, ex);
-                throw rex;
+                throw new RuntimeException(ex);
             }
             return;
         }
@@ -566,60 +570,50 @@
      * @see ContextFactory#getGlobal()
      */
     @Deprecated
-    public static void removeContextListener(ContextListener listener)
-    {
+    public static void removeContextListener(ContextListener listener) {
         ContextFactory.getGlobal().addListener(listener);
     }
 
-    /**
-     * Return {@link ContextFactory} instance used to create this Context.
-     */
-    public final ContextFactory getFactory()
-    {
+    /** Return {@link ContextFactory} instance used to create this Context. */
+    public final ContextFactory getFactory() {
         return factory;
     }
 
     /**
-     * Checks if this is a sealed Context. A sealed Context instance does not
-     * allow to modify any of its properties and will throw an exception
-     * on any such attempt.
+     * Checks if this is a sealed Context. A sealed Context instance does not allow to modify any of
+     * its properties and will throw an exception on any such attempt.
+     *
      * @see #seal(Object sealKey)
      */
-    public final boolean isSealed()
-    {
+    public final boolean isSealed() {
         return sealed;
     }
 
     /**
-     * Seal this Context object so any attempt to modify any of its properties
-     * including calling {@link #enter()} and {@link #exit()} methods will
-     * throw an exception.
-     * <p>
-     * If <tt>sealKey</tt> is not null, calling
-     * {@link #unseal(Object sealKey)} with the same key unseals
-     * the object. If <tt>sealKey</tt> is null, unsealing is no longer possible.
+     * Seal this Context object so any attempt to modify any of its properties including calling
+     * {@link #enter()} and {@link #exit()} methods will throw an exception.
+     *
+     * <p>If <code>sealKey</code> is not null, calling {@link #unseal(Object sealKey)} with the same
+     * key unseals the object. If <code>sealKey</code> is null, unsealing is no longer possible.
      *
      * @see #isSealed()
      * @see #unseal(Object)
      */
-    public final void seal(Object sealKey)
-    {
+    public final void seal(Object sealKey) {
         if (sealed) onSealedMutation();
         sealed = true;
         this.sealKey = sealKey;
     }
 
     /**
-     * Unseal previously sealed Context object.
-     * The <tt>sealKey</tt> argument should not be null and should match
-     * <tt>sealKey</tt> suplied with the last call to
-     * {@link #seal(Object)} or an exception will be thrown.
+     * Unseal previously sealed Context object. The <code>sealKey</code> argument should not be null
+     * and should match <code>sealKey</code> suplied with the last call to {@link #seal(Object)} or
+     * an exception will be thrown.
      *
      * @see #isSealed()
      * @see #seal(Object sealKey)
      */
-    public final void unseal(Object sealKey)
-    {
+    public final void unseal(Object sealKey) {
         if (sealKey == null) throw new IllegalArgumentException();
         if (this.sealKey != sealKey) throw new IllegalArgumentException();
         if (!sealed) throw new IllegalStateException();
@@ -627,49 +621,45 @@
         this.sealKey = null;
     }
 
-    static void onSealedMutation()
-    {
+    static void onSealedMutation() {
         throw new IllegalStateException();
     }
 
     /**
      * Get the current language version.
-     * <p>
-     * The language version number affects JavaScript semantics as detailed
-     * in the overview documentation.
+     *
+     * <p>The language version number affects JavaScript semantics as detailed in the overview
+     * documentation.
      *
      * @return an integer that is one of VERSION_1_0, VERSION_1_1, etc.
      */
-    public final int getLanguageVersion()
-    {
-       return version;
+    public final int getLanguageVersion() {
+        return version;
     }
 
     /**
      * Set the language version.
      *
-     * <p>
-     * Setting the language version will affect functions and scripts compiled
-     * subsequently. See the overview documentation for version-specific
-     * behavior.
+     * <p>Setting the language version will affect functions and scripts compiled subsequently. See
+     * the overview documentation for version-specific behavior.
      *
      * @param version the version as specified by VERSION_1_0, VERSION_1_1, etc.
      */
-    public void setLanguageVersion(int version)
-    {
+    public void setLanguageVersion(int version) {
         if (sealed) onSealedMutation();
         checkLanguageVersion(version);
         Object listeners = propertyListeners;
         if (listeners != null && version != this.version) {
-            firePropertyChangeImpl(listeners, languageVersionProperty,
-                               Integer.valueOf(this.version),
-                               Integer.valueOf(version));
+            firePropertyChangeImpl(
+                    listeners,
+                    languageVersionProperty,
+                    Integer.valueOf(this.version),
+                    Integer.valueOf(version));
         }
         this.version = version;
     }
 
-    public static boolean isValidLanguageVersion(int version)
-    {
+    public static boolean isValidLanguageVersion(int version) {
         switch (version) {
             case VERSION_DEFAULT:
             case VERSION_1_0:
@@ -687,66 +677,30 @@
         return false;
     }
 
-    public static void checkLanguageVersion(int version)
-    {
+    public static void checkLanguageVersion(int version) {
         if (isValidLanguageVersion(version)) {
             return;
         }
-        throw new IllegalArgumentException("Bad language version: "+version);
+        throw new IllegalArgumentException("Bad language version: " + version);
     }
 
     /**
      * Get the implementation version.
      *
-     * <p>
-     * The implementation version is of the form
+     * <p>The implementation version is of the form
+     *
      * <pre>
      *    "<i>name langVer</i> <code>release</code> <i>relNum date</i>"
      * </pre>
-     * where <i>name</i> is the name of the product, <i>langVer</i> is
-     * the language version, <i>relNum</i> is the release number, and
-     * <i>date</i> is the release date for that specific
+     *
+     * where <i>name</i> is the name of the product, <i>langVer</i> is the language version,
+     * <i>relNum</i> is the release number, and <i>date</i> is the release date for that specific
      * release in the form "yyyy mm dd".
      *
-     * @return a string that encodes the product, language version, release
-     *         number, and date.
+     * @return a string that encodes the product, language version, release number, and date.
      */
-    public final String getImplementationVersion()
-    {
-        if (implementationVersion == null) {
-            Enumeration<URL> urls;
-            try {
-                urls = Context.class.getClassLoader().getResources("META-INF/MANIFEST.MF");
-            } catch (IOException ioe) {
-                return null;
-            }
-
-            // There will be many manifests in the world -- enumerate all of them until we find the right one.
-            while (urls.hasMoreElements()) {
-                URL metaUrl = urls.nextElement();
-                InputStream is = null;
-                try {
-                    is = metaUrl.openStream();
-                    Manifest mf = new Manifest(is);
-                    Attributes attrs = mf.getMainAttributes();
-                    if ("Mozilla Rhino".equals(attrs.getValue("Implementation-Title"))) {
-                        implementationVersion =
-                            "Rhino " + attrs.getValue("Implementation-Version") + " " + attrs.getValue("Built-Date").replaceAll("-", " ");
-                        return implementationVersion;
-                    }
-                } catch (IOException e) {
-                    // Ignore this unlikely event
-                } finally {
-                    try {
-                        if (is != null) is.close();
-                    } catch (IOException e) {
-                        // Ignore this even unlikelier event
-                    }
-                }
-            }
-        }
-
-        return implementationVersion;
+    public final String getImplementationVersion() {
+        return ImplementationVersion.get();
     }
 
     /**
@@ -754,8 +708,7 @@
      *
      * @see org.mozilla.javascript.ErrorReporter
      */
-    public final ErrorReporter getErrorReporter()
-    {
+    public final ErrorReporter getErrorReporter() {
         if (errorReporter == null) {
             return DefaultErrorReporter.instance;
         }
@@ -768,8 +721,7 @@
      * @return the previous error reporter
      * @see org.mozilla.javascript.ErrorReporter
      */
-    public final ErrorReporter setErrorReporter(ErrorReporter reporter)
-    {
+    public final ErrorReporter setErrorReporter(ErrorReporter reporter) {
         if (sealed) onSealedMutation();
         if (reporter == null) throw new IllegalArgumentException();
         ErrorReporter old = getErrorReporter();
@@ -778,24 +730,21 @@
         }
         Object listeners = propertyListeners;
         if (listeners != null) {
-            firePropertyChangeImpl(listeners, errorReporterProperty,
-                                   old, reporter);
+            firePropertyChangeImpl(
+                    listeners, errorReporterProperty,
+                    old, reporter);
         }
         this.errorReporter = reporter;
         return old;
     }
 
     /**
-     * Get the current locale.  Returns the default locale if none has
-     * been set.
+     * Get the current locale. Returns the default locale if none has been set.
      *
      * @see java.util.Locale
      */
-
-    public final Locale getLocale()
-    {
-        if (locale == null)
-            locale = Locale.getDefault();
+    public final Locale getLocale() {
+        if (locale == null) locale = Locale.getDefault();
         return locale;
     }
 
@@ -804,8 +753,7 @@
      *
      * @see java.util.Locale
      */
-    public final Locale setLocale(Locale loc)
-    {
+    public final Locale setLocale(Locale loc) {
         if (sealed) onSealedMutation();
         Locale result = locale;
         locale = loc;
@@ -813,61 +761,56 @@
     }
 
     /**
-     * Register an object to receive notifications when a bound property
-     * has changed
+     * Register an object to receive notifications when a bound property has changed
+     *
      * @see java.beans.PropertyChangeEvent
      * @see #removePropertyChangeListener(java.beans.PropertyChangeListener)
      * @param l the listener
      */
-    public final void addPropertyChangeListener(PropertyChangeListener l)
-    {
+    public final void addPropertyChangeListener(PropertyChangeListener l) {
         if (sealed) onSealedMutation();
         propertyListeners = Kit.addListener(propertyListeners, l);
     }
 
     /**
-     * Remove an object from the list of objects registered to receive
-     * notification of changes to a bounded property
+     * Remove an object from the list of objects registered to receive notification of changes to a
+     * bounded property
+     *
      * @see java.beans.PropertyChangeEvent
      * @see #addPropertyChangeListener(java.beans.PropertyChangeListener)
      * @param l the listener
      */
-    public final void removePropertyChangeListener(PropertyChangeListener l)
-    {
+    public final void removePropertyChangeListener(PropertyChangeListener l) {
         if (sealed) onSealedMutation();
         propertyListeners = Kit.removeListener(propertyListeners, l);
     }
 
     /**
      * Notify any registered listeners that a bounded property has changed
+     *
      * @see #addPropertyChangeListener(java.beans.PropertyChangeListener)
      * @see #removePropertyChangeListener(java.beans.PropertyChangeListener)
      * @see java.beans.PropertyChangeListener
      * @see java.beans.PropertyChangeEvent
-     * @param  property  the bound property
-     * @param  oldValue  the old value
-     * @param  newValue   the new value
-     */
-    final void firePropertyChange(String property, Object oldValue,
-                                  Object newValue)
-    {
+     * @param property the bound property
+     * @param oldValue the old value
+     * @param newValue the new value
+     */
+    final void firePropertyChange(String property, Object oldValue, Object newValue) {
         Object listeners = propertyListeners;
         if (listeners != null) {
             firePropertyChangeImpl(listeners, property, oldValue, newValue);
         }
     }
 
-    private void firePropertyChangeImpl(Object listeners, String property,
-                                        Object oldValue, Object newValue)
-    {
+    private void firePropertyChangeImpl(
+            Object listeners, String property, Object oldValue, Object newValue) {
         for (int i = 0; ; ++i) {
             Object l = Kit.getListener(listeners, i);
-            if (l == null)
-                break;
+            if (l == null) break;
             if (l instanceof PropertyChangeListener) {
-                PropertyChangeListener pcl = (PropertyChangeListener)l;
-                pcl.propertyChange(new PropertyChangeEvent(
-                    this, property, oldValue, newValue));
+                PropertyChangeListener pcl = (PropertyChangeListener) l;
+                pcl.propertyChange(new PropertyChangeEvent(this, property, oldValue, newValue));
             }
         }
     }
@@ -882,16 +825,12 @@
      * @param lineOffset the offset into lineSource where problem was detected
      * @see org.mozilla.javascript.ErrorReporter
      */
-    public static void reportWarning(String message, String sourceName,
-                                     int lineno, String lineSource,
-                                     int lineOffset)
-    {
+    public static void reportWarning(
+            String message, String sourceName, int lineno, String lineSource, int lineOffset) {
         Context cx = Context.getContext();
         if (cx.hasFeature(FEATURE_WARNING_AS_ERROR))
             reportError(message, sourceName, lineno, lineSource, lineOffset);
-        else
-            cx.getErrorReporter().warning(message, sourceName, lineno,
-                                          lineSource, lineOffset);
+        else cx.getErrorReporter().warning(message, sourceName, lineno, lineSource, lineOffset);
     }
 
     /**
@@ -900,16 +839,14 @@
      * @param message the warning message to report
      * @see org.mozilla.javascript.ErrorReporter
      */
-    public static void reportWarning(String message)
-    {
-        int[] linep = { 0 };
+    public static void reportWarning(String message) {
+        int[] linep = {0};
         String filename = getSourcePositionFromStack(linep);
         Context.reportWarning(message, filename, linep[0], null, 0);
     }
 
-    public static void reportWarning(String message, Throwable t)
-    {
-        int[] linep = { 0 };
+    public static void reportWarning(String message, Throwable t) {
+        int[] linep = {0};
         String filename = getSourcePositionFromStack(linep);
         Writer sw = new StringWriter();
         PrintWriter pw = new PrintWriter(sw);
@@ -929,17 +866,13 @@
      * @param lineOffset the offset into lineSource where problem was detected
      * @see org.mozilla.javascript.ErrorReporter
      */
-    public static void reportError(String message, String sourceName,
-                                   int lineno, String lineSource,
-                                   int lineOffset)
-    {
+    public static void reportError(
+            String message, String sourceName, int lineno, String lineSource, int lineOffset) {
         Context cx = getCurrentContext();
         if (cx != null) {
-            cx.getErrorReporter().error(message, sourceName, lineno,
-                                        lineSource, lineOffset);
+            cx.getErrorReporter().error(message, sourceName, lineno, lineSource, lineOffset);
         } else {
-            throw new EvaluatorException(message, sourceName, lineno,
-                                         lineSource, lineOffset);
+            throw new EvaluatorException(message, sourceName, lineno, lineSource, lineOffset);
         }
     }
 
@@ -949,9 +882,8 @@
      * @param message the error message to report
      * @see org.mozilla.javascript.ErrorReporter
      */
-    public static void reportError(String message)
-    {
-        int[] linep = { 0 };
+    public static void reportError(String message) {
+        int[] linep = {0};
         String filename = getSourcePositionFromStack(linep);
         Context.reportError(message, filename, linep[0], null, 0);
     }
@@ -964,61 +896,58 @@
      * @param lineno the starting line number
      * @param lineSource the text of the line (may be null)
      * @param lineOffset the offset into lineSource where problem was detected
-     * @return a runtime exception that will be thrown to terminate the
-     *         execution of the script
+     * @return a runtime exception that will be thrown to terminate the execution of the script
      * @see org.mozilla.javascript.ErrorReporter
      */
-    public static EvaluatorException reportRuntimeError(String message,
-                                                        String sourceName,
-                                                        int lineno,
-                                                        String lineSource,
-                                                        int lineOffset)
-    {
+    public static EvaluatorException reportRuntimeError(
+            String message, String sourceName, int lineno, String lineSource, int lineOffset) {
         Context cx = getCurrentContext();
         if (cx != null) {
-            return cx.getErrorReporter().
-                            runtimeError(message, sourceName, lineno,
-                                         lineSource, lineOffset);
-        } else {
-            throw new EvaluatorException(message, sourceName, lineno,
-                                         lineSource, lineOffset);
+            return cx.getErrorReporter()
+                    .runtimeError(message, sourceName, lineno, lineSource, lineOffset);
         }
+        throw new EvaluatorException(message, sourceName, lineno, lineSource, lineOffset);
     }
 
-    static EvaluatorException reportRuntimeError0(String messageId)
-    {
-        String msg = ScriptRuntime.getMessage0(messageId);
+    static EvaluatorException reportRuntimeErrorById(String messageId, Object... args) {
+        String msg = ScriptRuntime.getMessageById(messageId, args);
         return reportRuntimeError(msg);
     }
 
-    static EvaluatorException reportRuntimeError1(String messageId,
-                                                  Object arg1)
-    {
-        String msg = ScriptRuntime.getMessage1(messageId, arg1);
+    /** @deprecated Use {@link #reportRuntimeErrorById(String messageId, Object... args)} instead */
+    @Deprecated
+    static EvaluatorException reportRuntimeError0(String messageId) {
+        String msg = ScriptRuntime.getMessageById(messageId);
+        return reportRuntimeError(msg);
+    }
+
+    /** @deprecated Use {@link #reportRuntimeErrorById(String messageId, Object... args)} instead */
+    @Deprecated
+    static EvaluatorException reportRuntimeError1(String messageId, Object arg1) {
+        String msg = ScriptRuntime.getMessageById(messageId, arg1);
         return reportRuntimeError(msg);
     }
 
-    static EvaluatorException reportRuntimeError2(String messageId,
-                                                  Object arg1, Object arg2)
-    {
-        String msg = ScriptRuntime.getMessage2(messageId, arg1, arg2);
+    /** @deprecated Use {@link #reportRuntimeErrorById(String messageId, Object... args)} instead */
+    @Deprecated
+    static EvaluatorException reportRuntimeError2(String messageId, Object arg1, Object arg2) {
+        String msg = ScriptRuntime.getMessageById(messageId, arg1, arg2);
         return reportRuntimeError(msg);
     }
 
-    static EvaluatorException reportRuntimeError3(String messageId,
-                                                  Object arg1, Object arg2,
-                                                  Object arg3)
-    {
-        String msg = ScriptRuntime.getMessage3(messageId, arg1, arg2, arg3);
+    /** @deprecated Use {@link #reportRuntimeErrorById(String messageId, Object... args)} instead */
+    @Deprecated
+    static EvaluatorException reportRuntimeError3(
+            String messageId, Object arg1, Object arg2, Object arg3) {
+        String msg = ScriptRuntime.getMessageById(messageId, arg1, arg2, arg3);
         return reportRuntimeError(msg);
     }
 
-    static EvaluatorException reportRuntimeError4(String messageId,
-                                                  Object arg1, Object arg2,
-                                                  Object arg3, Object arg4)
-    {
-        String msg
-            = ScriptRuntime.getMessage4(messageId, arg1, arg2, arg3, arg4);
+    /** @deprecated Use {@link #reportRuntimeErrorById(String messageId, Object... args)} instead */
+    @Deprecated
+    static EvaluatorException reportRuntimeError4(
+            String messageId, Object arg1, Object arg2, Object arg3, Object arg4) {
+        String msg = ScriptRuntime.getMessageById(messageId, arg1, arg2, arg3, arg4);
         return reportRuntimeError(msg);
     }
 
@@ -1028,9 +957,8 @@
      * @param message the error message to report
      * @see org.mozilla.javascript.ErrorReporter
      */
-    public static EvaluatorException reportRuntimeError(String message)
-    {
-        int[] linep = { 0 };
+    public static EvaluatorException reportRuntimeError(String message) {
+        int[] linep = {0};
         String filename = getSourcePositionFromStack(linep);
         return Context.reportRuntimeError(message, filename, linep[0], null, 0);
     }
@@ -1038,298 +966,253 @@
     /**
      * Initialize the standard objects.
      *
-     * Creates instances of the standard objects and their constructors
-     * (Object, String, Number, Date, etc.), setting up 'scope' to act
-     * as a global object as in ECMA 15.1.<p>
+     * <p>Creates instances of the standard objects and their constructors (Object, String, Number,
+     * Date, etc.), setting up 'scope' to act as a global object as in ECMA 15.1.
      *
-     * This method must be called to initialize a scope before scripts
-     * can be evaluated in that scope.<p>
+     * <p>This method must be called to initialize a scope before scripts can be evaluated in that
+     * scope.
      *
-     * This method does not affect the Context it is called upon.
+     * <p>This method does not affect the Context it is called upon.
      *
      * @return the initialized scope
      */
-    public final ScriptableObject initStandardObjects()
-    {
+    public final ScriptableObject initStandardObjects() {
         return initStandardObjects(null, false);
     }
 
     /**
-     * Initialize the standard objects, leaving out those that offer access directly
-     * to Java classes. This sets up "scope" to have access to all the standard
-     * JavaScript classes, but does not create global objects for any top-level
-     * Java packages. In addition, the "Packages," "JavaAdapter," and
-     * "JavaImporter" classes, and the "getClass" function, are not
-     * initialized.
-     *
-     * The result of this function is a scope that may be safely used in a "sandbox"
-     * environment where it is not desirable to give access to Java code from JavaScript.
-     *
-     * Creates instances of the standard objects and their constructors
-     * (Object, String, Number, Date, etc.), setting up 'scope' to act
-     * as a global object as in ECMA 15.1.<p>
+     * Initialize the standard objects, leaving out those that offer access directly to Java
+     * classes. This sets up "scope" to have access to all the standard JavaScript classes, but does
+     * not create global objects for any top-level Java packages. In addition, the "Packages,"
+     * "JavaAdapter," and "JavaImporter" classes, and the "getClass" function, are not initialized.
+     *
+     * <p>The result of this function is a scope that may be safely used in a "sandbox" environment
+     * where it is not desirable to give access to Java code from JavaScript.
      *
-     * This method must be called to initialize a scope before scripts
-     * can be evaluated in that scope.<p>
+     * <p>Creates instances of the standard objects and their constructors (Object, String, Number,
+     * Date, etc.), setting up 'scope' to act as a global object as in ECMA 15.1.
      *
-     * This method does not affect the Context it is called upon.
+     * <p>This method must be called to initialize a scope before scripts can be evaluated in that
+     * scope.
+     *
+     * <p>This method does not affect the Context it is called upon.
      *
      * @return the initialized scope
      */
-    public final ScriptableObject initSafeStandardObjects()
-    {
+    public final ScriptableObject initSafeStandardObjects() {
         return initSafeStandardObjects(null, false);
     }
 
     /**
      * Initialize the standard objects.
      *
-     * Creates instances of the standard objects and their constructors
-     * (Object, String, Number, Date, etc.), setting up 'scope' to act
-     * as a global object as in ECMA 15.1.<p>
-     *
-     * This method must be called to initialize a scope before scripts
-     * can be evaluated in that scope.<p>
-     *
-     * This method does not affect the Context it is called upon.
-     *
-     * @param scope the scope to initialize, or null, in which case a new
-     *        object will be created to serve as the scope
-     * @return the initialized scope. The method returns the value of the scope
-     *         argument if it is not null or newly allocated scope object which
-     *         is an instance {@link ScriptableObject}.
+     * <p>Creates instances of the standard objects and their constructors (Object, String, Number,
+     * Date, etc.), setting up 'scope' to act as a global object as in ECMA 15.1.
+     *
+     * <p>This method must be called to initialize a scope before scripts can be evaluated in that
+     * scope.
+     *
+     * <p>This method does not affect the Context it is called upon.
+     *
+     * @param scope the scope to initialize, or null, in which case a new object will be created to
+     *     serve as the scope
+     * @return the initialized scope. The method returns the value of the scope argument if it is
+     *     not null or newly allocated scope object which is an instance {@link ScriptableObject}.
      */
-    public final Scriptable initStandardObjects(ScriptableObject scope)
-    {
+    public final Scriptable initStandardObjects(ScriptableObject scope) {
         return initStandardObjects(scope, false);
     }
 
     /**
-     * Initialize the standard objects, leaving out those that offer access directly
-     * to Java classes. This sets up "scope" to have access to all the standard
-     * JavaScript classes, but does not create global objects for any top-level
-     * Java packages. In addition, the "Packages," "JavaAdapter," and
-     * "JavaImporter" classes, and the "getClass" function, are not
-     * initialized.
-     *
-     * The result of this function is a scope that may be safely used in a "sandbox"
-     * environment where it is not desirable to give access to Java code from JavaScript.
-     *
-     * Creates instances of the standard objects and their constructors
-     * (Object, String, Number, Date, etc.), setting up 'scope' to act
-     * as a global object as in ECMA 15.1.<p>
-     *
-     * This method must be called to initialize a scope before scripts
-     * can be evaluated in that scope.<p>
-     *
-     * This method does not affect the Context it is called upon.
-     *
-     * @param scope the scope to initialize, or null, in which case a new
-     *        object will be created to serve as the scope
-     * @return the initialized scope. The method returns the value of the scope
-     *         argument if it is not null or newly allocated scope object which
-     *         is an instance {@link ScriptableObject}.
+     * Initialize the standard objects, leaving out those that offer access directly to Java
+     * classes. This sets up "scope" to have access to all the standard JavaScript classes, but does
+     * not create global objects for any top-level Java packages. In addition, the "Packages,"
+     * "JavaAdapter," and "JavaImporter" classes, and the "getClass" function, are not initialized.
+     *
+     * <p>The result of this function is a scope that may be safely used in a "sandbox" environment
+     * where it is not desirable to give access to Java code from JavaScript.
+     *
+     * <p>Creates instances of the standard objects and their constructors (Object, String, Number,
+     * Date, etc.), setting up 'scope' to act as a global object as in ECMA 15.1.
+     *
+     * <p>This method must be called to initialize a scope before scripts can be evaluated in that
+     * scope.
+     *
+     * <p>This method does not affect the Context it is called upon.
+     *
+     * @param scope the scope to initialize, or null, in which case a new object will be created to
+     *     serve as the scope
+     * @return the initialized scope. The method returns the value of the scope argument if it is
+     *     not null or newly allocated scope object which is an instance {@link ScriptableObject}.
      */
-    public final Scriptable initSafeStandardObjects(ScriptableObject scope)
-    {
+    public final Scriptable initSafeStandardObjects(ScriptableObject scope) {
         return initSafeStandardObjects(scope, false);
     }
 
     /**
      * Initialize the standard objects.
      *
-     * Creates instances of the standard objects and their constructors
-     * (Object, String, Number, Date, etc.), setting up 'scope' to act
-     * as a global object as in ECMA 15.1.<p>
-     *
-     * This method must be called to initialize a scope before scripts
-     * can be evaluated in that scope.<p>
-     *
-     * This method does not affect the Context it is called upon.<p>
-     *
-     * This form of the method also allows for creating "sealed" standard
-     * objects. An object that is sealed cannot have properties added, changed,
-     * or removed. This is useful to create a "superglobal" that can be shared
-     * among several top-level objects. Note that sealing is not allowed in
-     * the current ECMA/ISO language specification, but is likely for
-     * the next version.
-     *
-     * @param scope the scope to initialize, or null, in which case a new
-     *        object will be created to serve as the scope
-     * @param sealed whether or not to create sealed standard objects that
-     *        cannot be modified.
-     * @return the initialized scope. The method returns the value of the scope
-     *         argument if it is not null or newly allocated scope object.
+     * <p>Creates instances of the standard objects and their constructors (Object, String, Number,
+     * Date, etc.), setting up 'scope' to act as a global object as in ECMA 15.1.
+     *
+     * <p>This method must be called to initialize a scope before scripts can be evaluated in that
+     * scope.
+     *
+     * <p>This method does not affect the Context it is called upon.
+     *
+     * <p>This form of the method also allows for creating "sealed" standard objects. An object that
+     * is sealed cannot have properties added, changed, or removed. This is useful to create a
+     * "superglobal" that can be shared among several top-level objects. Note that sealing is not
+     * allowed in the current ECMA/ISO language specification, but is likely for the next version.
+     *
+     * @param scope the scope to initialize, or null, in which case a new object will be created to
+     *     serve as the scope
+     * @param sealed whether or not to create sealed standard objects that cannot be modified.
+     * @return the initialized scope. The method returns the value of the scope argument if it is
+     *     not null or newly allocated scope object.
      * @since 1.4R3
      */
-    public ScriptableObject initStandardObjects(ScriptableObject scope,
-                                                boolean sealed)
-    {
+    public ScriptableObject initStandardObjects(ScriptableObject scope, boolean sealed) {
         return ScriptRuntime.initStandardObjects(this, scope, sealed);
     }
 
     /**
-     * Initialize the standard objects, leaving out those that offer access directly
-     * to Java classes. This sets up "scope" to have access to all the standard
-     * JavaScript classes, but does not create global objects for any top-level
-     * Java packages. In addition, the "Packages," "JavaAdapter," and
-     * "JavaImporter" classes, and the "getClass" function, are not
-     * initialized.
-     *
-     * The result of this function is a scope that may be safely used in a "sandbox"
-     * environment where it is not desirable to give access to Java code from JavaScript.
-     *
-     * Creates instances of the standard objects and their constructors
-     * (Object, String, Number, Date, etc.), setting up 'scope' to act
-     * as a global object as in ECMA 15.1.<p>
-     *
-     * This method must be called to initialize a scope before scripts
-     * can be evaluated in that scope.<p>
-     *
-     * This method does not affect the Context it is called upon.<p>
-     *
-     * This form of the method also allows for creating "sealed" standard
-     * objects. An object that is sealed cannot have properties added, changed,
-     * or removed. This is useful to create a "superglobal" that can be shared
-     * among several top-level objects. Note that sealing is not allowed in
-     * the current ECMA/ISO language specification, but is likely for
-     * the next version.
-     *
-     * @param scope the scope to initialize, or null, in which case a new
-     *        object will be created to serve as the scope
-     * @param sealed whether or not to create sealed standard objects that
-     *        cannot be modified.
-     * @return the initialized scope. The method returns the value of the scope
-     *         argument if it is not null or newly allocated scope object.
+     * Initialize the standard objects, leaving out those that offer access directly to Java
+     * classes. This sets up "scope" to have access to all the standard JavaScript classes, but does
+     * not create global objects for any top-level Java packages. In addition, the "Packages,"
+     * "JavaAdapter," and "JavaImporter" classes, and the "getClass" function, are not initialized.
+     *
+     * <p>The result of this function is a scope that may be safely used in a "sandbox" environment
+     * where it is not desirable to give access to Java code from JavaScript.
+     *
+     * <p>Creates instances of the standard objects and their constructors (Object, String, Number,
+     * Date, etc.), setting up 'scope' to act as a global object as in ECMA 15.1.
+     *
+     * <p>This method must be called to initialize a scope before scripts can be evaluated in that
+     * scope.
+     *
+     * <p>This method does not affect the Context it is called upon.
+     *
+     * <p>This form of the method also allows for creating "sealed" standard objects. An object that
+     * is sealed cannot have properties added, changed, or removed. This is useful to create a
+     * "superglobal" that can be shared among several top-level objects. Note that sealing is not
+     * allowed in the current ECMA/ISO language specification, but is likely for the next version.
+     *
+     * @param scope the scope to initialize, or null, in which case a new object will be created to
+     *     serve as the scope
+     * @param sealed whether or not to create sealed standard objects that cannot be modified.
+     * @return the initialized scope. The method returns the value of the scope argument if it is
+     *     not null or newly allocated scope object.
      * @since 1.7.6
      */
-    public ScriptableObject initSafeStandardObjects(ScriptableObject scope,
-                                                    boolean sealed)
-    {
+    public ScriptableObject initSafeStandardObjects(ScriptableObject scope, boolean sealed) {
         return ScriptRuntime.initSafeStandardObjects(this, scope, sealed);
     }
 
-    /**
-     * Get the singleton object that represents the JavaScript Undefined value.
-     */
-    public static Object getUndefinedValue()
-    {
+    /** Get the singleton object that represents the JavaScript Undefined value. */
+    public static Object getUndefinedValue() {
         return Undefined.instance;
     }
 
     /**
      * Evaluate a JavaScript source string.
      *
-     * The provided source name and line number are used for error messages
-     * and for producing debug information.
+     * <p>The provided source name and line number are used for error messages and for producing
+     * debug information.
      *
      * @param scope the scope to execute in
      * @param source the JavaScript source
      * @param sourceName a string describing the source, such as a filename
      * @param lineno the starting line number
-     * @param securityDomain an arbitrary object that specifies security
-     *        information about the origin or owner of the script. For
-     *        implementations that don't care about security, this value
-     *        may be null.
+     * @param securityDomain an arbitrary object that specifies security information about the
+     *     origin or owner of the script. For implementations that don't care about security, this
+     *     value may be null.
      * @return the result of evaluating the string
      * @see org.mozilla.javascript.SecurityController
      */
-    public final Object evaluateString(Scriptable scope, String source,
-                                       String sourceName, int lineno,
-                                       Object securityDomain)
-    {
-        Script script = compileString(source, sourceName, lineno,
-                                      securityDomain);
+    public final Object evaluateString(
+            Scriptable scope, String source, String sourceName, int lineno, Object securityDomain) {
+        Script script = compileString(source, sourceName, lineno, securityDomain);
         if (script != null) {
             return script.exec(this, scope);
-        } else {
-            return null;
         }
+        return null;
     }
 
     /**
      * Evaluate a reader as JavaScript source.
      *
-     * All characters of the reader are consumed.
+     * <p>All characters of the reader are consumed.
      *
      * @param scope the scope to execute in
      * @param in the Reader to get JavaScript source from
      * @param sourceName a string describing the source, such as a filename
      * @param lineno the starting line number
-     * @param securityDomain an arbitrary object that specifies security
-     *        information about the origin or owner of the script. For
-     *        implementations that don't care about security, this value
-     *        may be null.
+     * @param securityDomain an arbitrary object that specifies security information about the
+     *     origin or owner of the script. For implementations that don't care about security, this
+     *     value may be null.
      * @return the result of evaluating the source
-     *
      * @exception IOException if an IOException was generated by the Reader
      */
-    public final Object evaluateReader(Scriptable scope, Reader in,
-                                       String sourceName, int lineno,
-                                       Object securityDomain)
-        throws IOException
-    {
-        Script script = compileReader(scope, in, sourceName, lineno,
-                                      securityDomain);
+    public final Object evaluateReader(
+            Scriptable scope, Reader in, String sourceName, int lineno, Object securityDomain)
+            throws IOException {
+        Script script = compileReader(scope, in, sourceName, lineno, securityDomain);
         if (script != null) {
             return script.exec(this, scope);
-        } else {
-            return null;
         }
+        return null;
     }
 
     /**
-     * Execute script that may pause execution by capturing a continuation.
-     * Caller must be prepared to catch a ContinuationPending exception
-     * and resume execution by calling
-     * {@link #resumeContinuation(Object, Scriptable, Object)}.
-     * @param script The script to execute. Script must have been compiled
-     *      with interpreted mode (optimization level -1)
+     * Execute script that may pause execution by capturing a continuation. Caller must be prepared
+     * to catch a ContinuationPending exception and resume execution by calling {@link
+     * #resumeContinuation(Object, Scriptable, Object)}.
+     *
+     * @param script The script to execute. Script must have been compiled with interpreted mode
+     *     (optimization level -1)
      * @param scope The scope to execute the script against
-     * @throws ContinuationPending if the script calls a function that results
-     *      in a call to {@link #captureContinuation()}
+     * @throws ContinuationPending if the script calls a function that results in a call to {@link
+     *     #captureContinuation()}
      * @since 1.7 Release 2
      */
-    public Object executeScriptWithContinuations(Script script,
-            Scriptable scope)
-        throws ContinuationPending
-    {
-        if (!(script instanceof InterpretedFunction) ||
-            !((InterpretedFunction)script).isScript())
-        {
+    public Object executeScriptWithContinuations(Script script, Scriptable scope)
+            throws ContinuationPending {
+        if (!(script instanceof InterpretedFunction)
+                || !((InterpretedFunction) script).isScript()) {
             // Can only be applied to scripts
-            throw new IllegalArgumentException("Script argument was not" +
-                    " a script or was not created by interpreted mode ");
+            throw new IllegalArgumentException(
+                    "Script argument was not"
+                            + " a script or was not created by interpreted mode ");
         }
-        return callFunctionWithContinuations((InterpretedFunction) script,
-                scope, ScriptRuntime.emptyArgs);
+        return callFunctionWithContinuations(
+                (InterpretedFunction) script, scope, ScriptRuntime.emptyArgs);
     }
 
     /**
-     * Call function that may pause execution by capturing a continuation.
-     * Caller must be prepared to catch a ContinuationPending exception
-     * and resume execution by calling
-     * {@link #resumeContinuation(Object, Scriptable, Object)}.
-     * @param function The function to call. The function must have been
-     *      compiled with interpreted mode (optimization level -1)
+     * Call function that may pause execution by capturing a continuation. Caller must be prepared
+     * to catch a ContinuationPending exception and resume execution by calling {@link
+     * #resumeContinuation(Object, Scriptable, Object)}.
+     *
+     * @param function The function to call. The function must have been compiled with interpreted
+     *     mode (optimization level -1)
      * @param scope The scope to execute the script against
      * @param args The arguments for the function
-     * @throws ContinuationPending if the script calls a function that results
-     *      in a call to {@link #captureContinuation()}
+     * @throws ContinuationPending if the script calls a function that results in a call to {@link
+     *     #captureContinuation()}
      * @since 1.7 Release 2
      */
-    public Object callFunctionWithContinuations(Callable function,
-            Scriptable scope, Object[] args)
-        throws ContinuationPending
-    {
+    public Object callFunctionWithContinuations(Callable function, Scriptable scope, Object[] args)
+            throws ContinuationPending {
         if (!(function instanceof InterpretedFunction)) {
             // Can only be applied to scripts
-            throw new IllegalArgumentException("Function argument was not" +
-                    " created by interpreted mode ");
+            throw new IllegalArgumentException(
+                    "Function argument was not" + " created by interpreted mode ");
         }
         if (ScriptRuntime.hasTopCall(this)) {
-            throw new IllegalStateException("Cannot have any pending top " +
-                    "calls when executing a script with continuations");
+            throw new IllegalStateException(
+                    "Cannot have any pending top "
+                            + "calls when executing a script with continuations");
         }
         // Annotate so we can check later to ensure no java code in
         // intervening frames
@@ -1338,60 +1221,49 @@
     }
 
     /**
-     * Capture a continuation from the current execution. The execution must
-     * have been started via a call to
-     * {@link #executeScriptWithContinuations(Script, Scriptable)} or
-     * {@link #callFunctionWithContinuations(Callable, Scriptable, Object[])}.
-     * This implies that the code calling
-     * this method must have been called as a function from the
-     * JavaScript script. Also, there cannot be any non-JavaScript code
-     * between the JavaScript frames (e.g., a call to eval()). The
-     * ContinuationPending exception returned must be thrown.
+     * Capture a continuation from the current execution. The execution must have been started via a
+     * call to {@link #executeScriptWithContinuations(Script, Scriptable)} or {@link
+     * #callFunctionWithContinuations(Callable, Scriptable, Object[])}. This implies that the code
+     * calling this method must have been called as a function from the JavaScript script. Also,
+     * there cannot be any non-JavaScript code between the JavaScript frames (e.g., a call to
+     * eval()). The ContinuationPending exception returned must be thrown.
+     *
      * @return A ContinuationPending exception that must be thrown
      * @since 1.7 Release 2
      */
     public ContinuationPending captureContinuation() {
-        return new ContinuationPending(
-                Interpreter.captureContinuation(this));
+        return new ContinuationPending(Interpreter.captureContinuation(this));
     }
 
     /**
-     * Restarts execution of the JavaScript suspended at the call
-     * to {@link #captureContinuation()}. Execution of the code will resume
-     * with the functionResult as the result of the call that captured the
-     * continuation.
-     * Execution of the script will either conclude normally and the
-     * result returned, another continuation will be captured and
-     * thrown, or the script will terminate abnormally and throw an exception.
-     * @param continuation The value returned by
-     * {@link ContinuationPending#getContinuation()}
-     * @param functionResult This value will appear to the code being resumed
-     *      as the result of the function that captured the continuation
-     * @throws ContinuationPending if another continuation is captured before
-     *      the code terminates
+     * Restarts execution of the JavaScript suspended at the call to {@link #captureContinuation()}.
+     * Execution of the code will resume with the functionResult as the result of the call that
+     * captured the continuation. Execution of the script will either conclude normally and the
+     * result returned, another continuation will be captured and thrown, or the script will
+     * terminate abnormally and throw an exception.
+     *
+     * @param continuation The value returned by {@link ContinuationPending#getContinuation()}
+     * @param functionResult This value will appear to the code being resumed as the result of the
+     *     function that captured the continuation
+     * @throws ContinuationPending if another continuation is captured before the code terminates
      * @since 1.7 Release 2
      */
-    public Object resumeContinuation(Object continuation,
-            Scriptable scope, Object functionResult)
-            throws ContinuationPending
-    {
-        Object[] args = { functionResult };
+    public Object resumeContinuation(Object continuation, Scriptable scope, Object functionResult)
+            throws ContinuationPending {
+        Object[] args = {functionResult};
         return Interpreter.restartContinuation(
-                (org.mozilla.javascript.NativeContinuation) continuation,
-                this, scope, args);
+                (org.mozilla.javascript.NativeContinuation) continuation, this, scope, args);
     }
 
     /**
      * Check whether a string is ready to be compiled.
-     * <p>
-     * stringIsCompilableUnit is intended to support interactive compilation of
-     * JavaScript.  If compiling the string would result in an error
-     * that might be fixed by appending more source, this method
-     * returns false.  In every other case, it returns true.
-     * <p>
-     * Interactive shells may accumulate source lines, using this
-     * method after each new line is appended to check whether the
-     * statement being entered is complete.
+     *
+     * <p>stringIsCompilableUnit is intended to support interactive compilation of JavaScript. If
+     * compiling the string would result in an error that might be fixed by appending more source,
+     * this method returns false. In every other case, it returns true.
+     *
+     * <p>Interactive shells may accumulate source lines, using this method after each new line is
+     * appended to check whether the statement being entered is complete.
      *
      * @param source the source buffer to check
      * @return whether the source is ready for compilation
@@ -1418,188 +1290,190 @@
 
     /**
      * @deprecated
-     * @see #compileReader(Reader in, String sourceName, int lineno,
-     *                     Object securityDomain)
+     * @see #compileReader(Reader in, String sourceName, int lineno, Object securityDomain)
      */
     @Deprecated
-    public final Script compileReader(Scriptable scope, Reader in,
-                                      String sourceName, int lineno,
-                                      Object securityDomain)
-        throws IOException
-    {
+    public final Script compileReader(
+            Scriptable scope, Reader in, String sourceName, int lineno, Object securityDomain)
+            throws IOException {
         return compileReader(in, sourceName, lineno, securityDomain);
     }
 
     /**
      * Compiles the source in the given reader.
-     * <p>
-     * Returns a script that may later be executed.
-     * Will consume all the source in the reader.
+     *
+     * <p>Returns a script that may later be executed. Will consume all the source in the reader.
      *
      * @param in the input reader
      * @param sourceName a string describing the source, such as a filename
      * @param lineno the starting line number for reporting errors
-     * @param securityDomain an arbitrary object that specifies security
-     *        information about the origin or owner of the script. For
-     *        implementations that don't care about security, this value
-     *        may be null.
+     * @param securityDomain an arbitrary object that specifies security information about the
+     *     origin or owner of the script. For implementations that don't care about security, this
+     *     value may be null.
      * @return a script that may later be executed
      * @exception IOException if an IOException was generated by the Reader
      * @see org.mozilla.javascript.Script
      */
-    public final Script compileReader(Reader in, String sourceName,
-                                      int lineno, Object securityDomain)
-        throws IOException
-    {
+    public final Script compileReader(
+            Reader in, String sourceName, int lineno, Object securityDomain) throws IOException {
         if (lineno < 0) {
             // For compatibility IllegalArgumentException can not be thrown here
             lineno = 0;
         }
-        return (Script) compileImpl(null, in, null, sourceName, lineno,
-                                    securityDomain, false, null, null);
+
+        return (Script)
+                compileImpl(
+                        null,
+                        Kit.readReader(in),
+                        sourceName,
+                        lineno,
+                        securityDomain,
+                        false,
+                        null,
+                        null);
     }
 
     /**
      * Compiles the source in the given string.
-     * <p>
-     * Returns a script that may later be executed.
+     *
+     * <p>Returns a script that may later be executed.
      *
      * @param source the source string
      * @param sourceName a string describing the source, such as a filename
-     * @param lineno the starting line number for reporting errors. Use
-     *        0 if the line number is unknown.
-     * @param securityDomain an arbitrary object that specifies security
-     *        information about the origin or owner of the script. For
-     *        implementations that don't care about security, this value
-     *        may be null.
+     * @param lineno the starting line number for reporting errors. Use 0 if the line number is
+     *     unknown.
+     * @param securityDomain an arbitrary object that specifies security information about the
+     *     origin or owner of the script. For implementations that don't care about security, this
+     *     value may be null.
      * @return a script that may later be executed
      * @see org.mozilla.javascript.Script
      */
-    public final Script compileString(String source,
-                                      String sourceName, int lineno,
-                                      Object securityDomain)
-    {
+    public final Script compileString(
+            String source, String sourceName, int lineno, Object securityDomain) {
         if (lineno < 0) {
             // For compatibility IllegalArgumentException can not be thrown here
             lineno = 0;
         }
-        return compileString(source, null, null, sourceName, lineno,
-                             securityDomain);
+        return compileString(source, null, null, sourceName, lineno, securityDomain);
     }
 
-    final Script compileString(String source,
-                               Evaluator compiler,
-                               ErrorReporter compilationErrorReporter,
-                               String sourceName, int lineno,
-                               Object securityDomain)
-    {
+    final Script compileString(
+            String source,
+            Evaluator compiler,
+            ErrorReporter compilationErrorReporter,
+            String sourceName,
+            int lineno,
+            Object securityDomain) {
         try {
-            return (Script) compileImpl(null, null, source, sourceName, lineno,
-                                        securityDomain, false,
-                                        compiler, compilationErrorReporter);
-        } catch (IOException ex) {
+            return (Script)
+                    compileImpl(
+                            null,
+                            source,
+                            sourceName,
+                            lineno,
+                            securityDomain,
+                            false,
+                            compiler,
+                            compilationErrorReporter);
+        } catch (IOException ioe) {
             // Should not happen when dealing with source as string
-            throw new RuntimeException();
+            throw new RuntimeException(ioe);
         }
     }
 
     /**
      * Compile a JavaScript function.
-     * <p>
-     * The function source must be a function definition as defined by
-     * ECMA (e.g., "function f(a) { return a; }").
+     *
+     * <p>The function source must be a function definition as defined by ECMA (e.g., "function f(a)
+     * { return a; }").
      *
      * @param scope the scope to compile relative to
      * @param source the function definition source
      * @param sourceName a string describing the source, such as a filename
      * @param lineno the starting line number
-     * @param securityDomain an arbitrary object that specifies security
-     *        information about the origin or owner of the script. For
-     *        implementations that don't care about security, this value
-     *        may be null.
+     * @param securityDomain an arbitrary object that specifies security information about the
+     *     origin or owner of the script. For implementations that don't care about security, this
+     *     value may be null.
      * @return a Function that may later be called
      * @see org.mozilla.javascript.Function
      */
-    public final Function compileFunction(Scriptable scope, String source,
-                                          String sourceName, int lineno,
-                                          Object securityDomain)
-    {
-        return compileFunction(scope, source, null, null, sourceName, lineno,
-                               securityDomain);
+    public final Function compileFunction(
+            Scriptable scope, String source, String sourceName, int lineno, Object securityDomain) {
+        return compileFunction(scope, source, null, null, sourceName, lineno, securityDomain);
     }
 
-    final Function compileFunction(Scriptable scope, String source,
-                                   Evaluator compiler,
-                                   ErrorReporter compilationErrorReporter,
-                                   String sourceName, int lineno,
-                                   Object securityDomain)
-    {
+    final Function compileFunction(
+            Scriptable scope,
+            String source,
+            Evaluator compiler,
+            ErrorReporter compilationErrorReporter,
+            String sourceName,
+            int lineno,
+            Object securityDomain) {
         try {
-            return (Function) compileImpl(scope, null, source, sourceName,
-                                          lineno, securityDomain, true,
-                                          compiler, compilationErrorReporter);
-        }
-        catch (IOException ioe) {
+            return (Function)
+                    compileImpl(
+                            scope,
+                            source,
+                            sourceName,
+                            lineno,
+                            securityDomain,
+                            true,
+                            compiler,
+                            compilationErrorReporter);
+        } catch (IOException ioe) {
             // Should never happen because we just made the reader
             // from a String
-            throw new RuntimeException();
+            throw new RuntimeException(ioe);
         }
     }
 
     /**
      * Decompile the script.
-     * <p>
-     * The canonical source of the script is returned.
+     *
+     * <p>The canonical source of the script is returned.
      *
      * @param script the script to decompile
      * @param indent the number of spaces to indent the result
      * @return a string representing the script source
      */
-    public final String decompileScript(Script script, int indent)
-    {
+    public final String decompileScript(Script script, int indent) {
         NativeFunction scriptImpl = (NativeFunction) script;
         return scriptImpl.decompile(indent, 0);
     }
 
     /**
      * Decompile a JavaScript Function.
-     * <p>
-     * Decompiles a previously compiled JavaScript function object to
-     * canonical source.
-     * <p>
-     * Returns function body of '[native code]' if no decompilation
-     * information is available.
+     *
+     * <p>Decompiles a previously compiled JavaScript function object to canonical source.
+     *
+     * <p>Returns function body of '[native code]' if no decompilation information is available.
      *
      * @param fun the JavaScript function to decompile
      * @param indent the number of spaces to indent the result
      * @return a string representing the function source
      */
-    public final String decompileFunction(Function fun, int indent)
-    {
-        if (fun instanceof BaseFunction)
-            return ((BaseFunction)fun).decompile(indent, 0);
-        else
-            return "function " + fun.getClassName() +
-                   "() {\n\t[native code]\n}\n";
+    public final String decompileFunction(Function fun, int indent) {
+        if (fun instanceof BaseFunction) return ((BaseFunction) fun).decompile(indent, 0);
+
+        return "function " + fun.getClassName() + "() {\n\t[native code]\n}\n";
     }
 
     /**
      * Decompile the body of a JavaScript Function.
-     * <p>
-     * Decompiles the body a previously compiled JavaScript Function
-     * object to canonical source, omitting the function header and
-     * trailing brace.
      *
-     * Returns '[native code]' if no decompilation information is available.
+     * <p>Decompiles the body a previously compiled JavaScript Function object to canonical source,
+     * omitting the function header and trailing brace.
+     *
+     * <p>Returns '[native code]' if no decompilation information is available.
      *
      * @param fun the JavaScript function to decompile
      * @param indent the number of spaces to indent the result
      * @return a string representing the function body source.
      */
-    public final String decompileFunctionBody(Function fun, int indent)
-    {
+    public final String decompileFunctionBody(Function fun, int indent) {
         if (fun instanceof BaseFunction) {
-            BaseFunction bf = (BaseFunction)fun;
+            BaseFunction bf = (BaseFunction) fun;
             return bf.decompile(indent, Decompiler.ONLY_BODY_FLAG);
         }
         // ALERT: not sure what the right response here is.
@@ -1609,72 +1483,67 @@
     /**
      * Create a new JavaScript object.
      *
-     * Equivalent to evaluating "new Object()".
-     * @param scope the scope to search for the constructor and to evaluate
-     *              against
+     * <p>Equivalent to evaluating "new Object()".
+     *
+     * @param scope the scope to search for the constructor and to evaluate against
      * @return the new object
      */
-    public Scriptable newObject(Scriptable scope)
-    {
+    public Scriptable newObject(Scriptable scope) {
         NativeObject result = new NativeObject();
-        ScriptRuntime.setBuiltinProtoAndParent(result, scope,
-                TopLevel.Builtins.Object);
+        ScriptRuntime.setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Object);
         return result;
     }
 
     /**
      * Create a new JavaScript object by executing the named constructor.
      *
-     * The call <code>newObject(scope, "Foo")</code> is equivalent to
-     * evaluating "new Foo()".
+     * <p>The call <code>newObject(scope, "Foo")</code> is equivalent to evaluating "new Foo()".
      *
      * @param scope the scope to search for the constructor and to evaluate against
      * @param constructorName the name of the constructor to call
      * @return the new object
      */
-    public Scriptable newObject(Scriptable scope, String constructorName)
-    {
+    public Scriptable newObject(Scriptable scope, String constructorName) {
         return newObject(scope, constructorName, ScriptRuntime.emptyArgs);
     }
 
     /**
      * Creates a new JavaScript object by executing the named constructor.
      *
-     * Searches <code>scope</code> for the named constructor, calls it with
-     * the given arguments, and returns the result.<p>
+     * <p>Searches <code>scope</code> for the named constructor, calls it with the given arguments,
+     * and returns the result.
+     *
+     * <p>The code
      *
-     * The code
      * <pre>
      * Object[] args = { "a", "b" };
      * newObject(scope, "Foo", args)</pre>
-     * is equivalent to evaluating "new Foo('a', 'b')", assuming that the Foo
-     * constructor has been defined in <code>scope</code>.
      *
-     * @param scope The scope to search for the constructor and to evaluate
-     *              against
+     * is equivalent to evaluating "new Foo('a', 'b')", assuming that the Foo constructor has been
+     * defined in <code>scope</code>.
+     *
+     * @param scope The scope to search for the constructor and to evaluate against
      * @param constructorName the name of the constructor to call
      * @param args the array of arguments for the constructor
      * @return the new object
      */
-    public Scriptable newObject(Scriptable scope, String constructorName,
-                                Object[] args)
-    {
+    public Scriptable newObject(Scriptable scope, String constructorName, Object[] args) {
         return ScriptRuntime.newObject(this, scope, constructorName, args);
     }
 
     /**
      * Create an array with a specified initial length.
+     *
      * <p>
+     *
      * @param scope the scope to create the object in
-     * @param length the initial length (JavaScript arrays may have
-     *               additional properties added dynamically).
+     * @param length the initial length (JavaScript arrays may have additional properties added
+     *     dynamically).
      * @return the new array object
      */
-    public Scriptable newArray(Scriptable scope, int length)
-    {
+    public Scriptable newArray(Scriptable scope, int length) {
         NativeArray result = new NativeArray(length);
-        ScriptRuntime.setBuiltinProtoAndParent(result, scope,
-                TopLevel.Builtins.Array);
+        ScriptRuntime.setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Array);
         return result;
     }
 
@@ -1682,107 +1551,92 @@
      * Create an array with a set of initial elements.
      *
      * @param scope the scope to create the object in.
-     * @param elements the initial elements. Each object in this array
-     *                 must be an acceptable JavaScript type and type
-     *                 of array should be exactly Object[], not
-     *                 SomeObjectSubclass[].
+     * @param elements the initial elements. Each object in this array must be an acceptable
+     *     JavaScript type and type of array should be exactly Object[], not SomeObjectSubclass[].
      * @return the new array object.
      */
-    public Scriptable newArray(Scriptable scope, Object[] elements)
-    {
+    public Scriptable newArray(Scriptable scope, Object[] elements) {
         if (elements.getClass().getComponentType() != ScriptRuntime.ObjectClass)
             throw new IllegalArgumentException();
         NativeArray result = new NativeArray(elements);
-        ScriptRuntime.setBuiltinProtoAndParent(result, scope,
-                TopLevel.Builtins.Array);
+        ScriptRuntime.setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Array);
         return result;
     }
 
     /**
      * Get the elements of a JavaScript array.
-     * <p>
-     * If the object defines a length property convertible to double number,
-     * then the number is converted Uint32 value as defined in Ecma 9.6
-     * and Java array of that size is allocated.
-     * The array is initialized with the values obtained by
-     * calling get() on object for each value of i in [0,length-1]. If
-     * there is not a defined value for a property the Undefined value
-     * is used to initialize the corresponding element in the array. The
-     * Java array is then returned.
-     * If the object doesn't define a length property or it is not a number,
-     * empty array is returned.
+     *
+     * <p>If the object defines a length property convertible to double number, then the number is
+     * converted Uint32 value as defined in Ecma 9.6 and Java array of that size is allocated. The
+     * array is initialized with the values obtained by calling get() on object for each value of i
+     * in [0,length-1]. If there is not a defined value for a property the Undefined value is used
+     * to initialize the corresponding element in the array. The Java array is then returned. If the
+     * object doesn't define a length property or it is not a number, empty array is returned.
+     *
      * @param object the JavaScript array or array-like object
      * @return a Java array of objects
      * @since 1.4 release 2
      */
-    public final Object[] getElements(Scriptable object)
-    {
+    public final Object[] getElements(Scriptable object) {
         return ScriptRuntime.getArrayElements(object);
     }
 
     /**
      * Convert the value to a JavaScript boolean value.
-     * <p>
-     * See ECMA 9.2.
+     *
+     * <p>See ECMA 9.2.
      *
      * @param value a JavaScript value
-     * @return the corresponding boolean value converted using
-     *         the ECMA rules
+     * @return the corresponding boolean value converted using the ECMA rules
      */
-    public static boolean toBoolean(Object value)
-    {
+    public static boolean toBoolean(Object value) {
         return ScriptRuntime.toBoolean(value);
     }
 
     /**
      * Convert the value to a JavaScript Number value.
-     * <p>
-     * Returns a Java double for the JavaScript Number.
-     * <p>
-     * See ECMA 9.3.
+     *
+     * <p>Returns a Java double for the JavaScript Number.
+     *
+     * <p>See ECMA 9.3.
      *
      * @param value a JavaScript value
-     * @return the corresponding double value converted using
-     *         the ECMA rules
+     * @return the corresponding double value converted using the ECMA rules
      */
-    public static double toNumber(Object value)
-    {
+    public static double toNumber(Object value) {
         return ScriptRuntime.toNumber(value);
     }
 
     /**
      * Convert the value to a JavaScript String value.
+     *
+     * <p>See ECMA 9.8.
+     *
      * <p>
-     * See ECMA 9.8.
-     * <p>
+     *
      * @param value a JavaScript value
-     * @return the corresponding String value converted using
-     *         the ECMA rules
+     * @return the corresponding String value converted using the ECMA rules
      */
-    public static String toString(Object value)
-    {
+    public static String toString(Object value) {
         return ScriptRuntime.toString(value);
     }
 
     /**
      * Convert the value to an JavaScript object value.
-     * <p>
-     * Note that a scope must be provided to look up the constructors
-     * for Number, Boolean, and String.
-     * <p>
-     * See ECMA 9.9.
-     * <p>
-     * Additionally, arbitrary Java objects and classes will be
-     * wrapped in a Scriptable object with its Java fields and methods
-     * reflected as JavaScript properties of the object.
+     *
+     * <p>Note that a scope must be provided to look up the constructors for Number, Boolean, and
+     * String.
+     *
+     * <p>See ECMA 9.9.
+     *
+     * <p>Additionally, arbitrary Java objects and classes will be wrapped in a Scriptable object
+     * with its Java fields and methods reflected as JavaScript properties of the object.
      *
      * @param value any Java object
-     * @param scope global scope containing constructors for Number,
-     *              Boolean, and String
+     * @param scope global scope containing constructors for Number, Boolean, and String
      * @return new JavaScript object
      */
-    public static Scriptable toObject(Object value, Scriptable scope)
-    {
+    public static Scriptable toObject(Object value, Scriptable scope) {
         return ScriptRuntime.toObject(scope, value);
     }
 
@@ -1791,31 +1645,27 @@
      * @see #toObject(Object, Scriptable)
      */
     @Deprecated
-    public static Scriptable toObject(Object value, Scriptable scope,
-                                      Class<?> staticType)
-    {
+    public static Scriptable toObject(Object value, Scriptable scope, Class<?> staticType) {
         return ScriptRuntime.toObject(scope, value);
     }
 
     /**
-     * Convenient method to convert java value to its closest representation
-     * in JavaScript.
-     * <p>
-     * If value is an instance of String, Number, Boolean, Function or
-     * Scriptable, it is returned as it and will be treated as the corresponding
-     * JavaScript type of string, number, boolean, function and object.
-     * <p>
-     * Note that for Number instances during any arithmetic operation in
-     * JavaScript the engine will always use the result of
-     * <tt>Number.doubleValue()</tt> resulting in a precision loss if
-     * the number can not fit into double.
-     * <p>
-     * If value is an instance of Character, it will be converted to string of
-     * length 1 and its JavaScript type will be string.
-     * <p>
-     * The rest of values will be wrapped as LiveConnect objects
-     * by calling {@link WrapFactory#wrap(Context cx, Scriptable scope,
-     * Object obj, Class staticType)} as in:
+     * Convenient method to convert java value to its closest representation in JavaScript.
+     *
+     * <p>If value is an instance of String, Number, Boolean, Function or Scriptable, it is returned
+     * as it and will be treated as the corresponding JavaScript type of string, number, boolean,
+     * function and object.
+     *
+     * <p>Note that for Number instances during any arithmetic operation in JavaScript the engine
+     * will always use the result of <code>Number.doubleValue()</code> resulting in a precision loss
+     * if the number can not fit into double.
+     *
+     * <p>If value is an instance of Character, it will be converted to string of length 1 and its
+     * JavaScript type will be string.
+     *
+     * <p>The rest of values will be wrapped as LiveConnect objects by calling {@link
+     * WrapFactory#wrap(Context cx, Scriptable scope, Object obj, Class staticType)} as in:
+     *
      * <pre>
      *    Context cx = Context.getCurrentContext();
      *    return cx.getWrapFactory().wrap(cx, scope, value, null);
@@ -1825,312 +1675,358 @@
      * @param scope top scope object
      * @return value suitable to pass to any API that takes JavaScript values.
      */
-    public static Object javaToJS(Object value, Scriptable scope)
-    {
-        if (value instanceof String || value instanceof Number
-            || value instanceof Boolean || value instanceof Scriptable)
-        {
+    public static Object javaToJS(Object value, Scriptable scope) {
+        return javaToJS(value, scope, null);
+    }
+
+    /**
+     * Convenient method to convert java value to its closest representation in JavaScript.
+     *
+     * <p>If value is an instance of String, Number, Boolean, Function or Scriptable, it is returned
+     * as it and will be treated as the corresponding JavaScript type of string, number, boolean,
+     * function and object.
+     *
+     * <p>Note that for Number instances during any arithmetic operation in JavaScript the engine
+     * will always use the result of <code>Number.doubleValue()</code> resulting in a precision loss
+     * if the number can not fit into double.
+     *
+     * <p>If value is an instance of Character, it will be converted to string of length 1 and its
+     * JavaScript type will be string.
+     *
+     * <p>The rest of values will be wrapped as LiveConnect objects by calling {@link
+     * WrapFactory#wrap(Context cx, Scriptable scope, Object obj, Class staticType)} as in:
+     *
+     * <pre>
+     *    return cx.getWrapFactory().wrap(cx, scope, value, null);
+     * </pre>
+     *
+     * @param value any Java object
+     * @param scope top scope object
+     * @param cx context to use for wrapping LiveConnect objects
+     * @return value suitable to pass to any API that takes JavaScript values.
+     */
+    public static Object javaToJS(Object value, Scriptable scope, Context cx) {
+        if (value instanceof String
+                || value instanceof Number
+                || value instanceof Boolean
+                || value instanceof Scriptable) {
             return value;
         } else if (value instanceof Character) {
-            return String.valueOf(((Character)value).charValue());
+            return String.valueOf(((Character) value).charValue());
         } else {
-            Context cx = Context.getContext();
+            if (cx == null) {
+                cx = Context.getContext();
+            }
             return cx.getWrapFactory().wrap(cx, scope, value, null);
         }
     }
 
     /**
-     * Convert a JavaScript value into the desired type.
-     * Uses the semantics defined with LiveConnect3 and throws an
-     * Illegal argument exception if the conversion cannot be performed.
+     * Convert a JavaScript value into the desired type. Uses the semantics defined with
+     * LiveConnect3 and throws an Illegal argument exception if the conversion cannot be performed.
+     *
      * @param value the JavaScript value to convert
-     * @param desiredType the Java type to convert to. Primitive Java
-     *        types are represented using the TYPE fields in the corresponding
-     *        wrapper class in java.lang.
+     * @param desiredType the Java type to convert to. Primitive Java types are represented using
+     *     the TYPE fields in the corresponding wrapper class in java.lang.
      * @return the converted value
      * @throws EvaluatorException if the conversion cannot be performed
      */
-    public static Object jsToJava(Object value, Class<?> desiredType)
-        throws EvaluatorException
-    {
+    public static Object jsToJava(Object value, Class<?> desiredType) throws EvaluatorException {
         return NativeJavaObject.coerceTypeImpl(desiredType, value);
     }
 
     /**
      * @deprecated
      * @see #jsToJava(Object, Class)
-     * @throws IllegalArgumentException if the conversion cannot be performed.
-     *         Note that {@link #jsToJava(Object, Class)} throws
-     *         {@link EvaluatorException} instead.
+     * @throws IllegalArgumentException if the conversion cannot be performed. Note that {@link
+     *     #jsToJava(Object, Class)} throws {@link EvaluatorException} instead.
      */
     @Deprecated
     public static Object toType(Object value, Class<?> desiredType)
-        throws IllegalArgumentException
-    {
+            throws IllegalArgumentException {
         try {
             return jsToJava(value, desiredType);
         } catch (EvaluatorException ex) {
-            IllegalArgumentException
-                ex2 = new IllegalArgumentException(ex.getMessage());
-            Kit.initCause(ex2, ex);
-            throw ex2;
+            throw new IllegalArgumentException(ex.getMessage(), ex);
         }
     }
 
     /**
-     * Rethrow the exception wrapping it as the script runtime exception.
-     * Unless the exception is instance of {@link EcmaError} or
-     * {@link EvaluatorException} it will be wrapped as
-     * {@link WrappedException}, a subclass of {@link EvaluatorException}.
-     * The resulting exception object always contains
-     * source name and line number of script that triggered exception.
-     * <p>
-     * This method always throws an exception, its return value is provided
-     * only for convenience to allow a usage like:
+     * Returns the javaToJSONConverter for this Context.
+     *
+     * <p>The converter is used by the JSON.stringify method for Java objects other than instances
+     * of {@link java.util.Map Map}, {@link java.util.Collection Collection}, or {@link
+     * java.lang.Object Object[]}.
+     *
+     * <p>The default converter if unset will convert Java Objects to their toString() value.
+     *
+     * @return javaToJSONConverter for this Context
+     */
+    public UnaryOperator<Object> getJavaToJSONConverter() {
+        if (javaToJSONConverter == null) {
+            return JavaToJSONConverters.STRING;
+        }
+        return javaToJSONConverter;
+    }
+
+    /**
+     * Sets the javaToJSONConverter for this Context.
+     *
+     * <p>The converter is used by the JSON.stringify method for Java objects other than instances
+     * of {@link java.util.Map Map}, {@link java.util.Collection Collection}, or {@link
+     * java.lang.Object Object[]}.
+     *
+     * <p>Objects returned by the converter will converted with {@link #javaToJS(Object,
+     * Scriptable)} and then stringified themselves.
+     *
+     * @param javaToJSONConverter
+     * @throws IllegalArgumentException if javaToJSONConverter is null
+     */
+    public void setJavaToJSONConverter(UnaryOperator<Object> javaToJSONConverter)
+            throws IllegalArgumentException {
+        if (javaToJSONConverter == null) {
+            throw new IllegalArgumentException("javaToJSONConverter == null");
+        }
+        this.javaToJSONConverter = javaToJSONConverter;
+    }
+
+    /**
+     * Rethrow the exception wrapping it as the script runtime exception. Unless the exception is
+     * instance of {@link EcmaError} or {@link EvaluatorException} it will be wrapped as {@link
+     * WrappedException}, a subclass of {@link EvaluatorException}. The resulting exception object
+     * always contains source name and line number of script that triggered exception.
+     *
+     * <p>This method always throws an exception, its return value is provided only for convenience
+     * to allow a usage like:
+     *
      * <pre>
      * throw Context.throwAsScriptRuntimeEx(ex);
      * </pre>
+     *
      * to indicate that code after the method is unreachable.
+     *
      * @throws EvaluatorException
      * @throws EcmaError
      */
-    public static RuntimeException throwAsScriptRuntimeEx(Throwable e)
-    {
+    public static RuntimeException throwAsScriptRuntimeEx(Throwable e) {
         while ((e instanceof InvocationTargetException)) {
             e = ((InvocationTargetException) e).getTargetException();
         }
         // special handling of Error so scripts would not catch them
         if (e instanceof Error) {
             Context cx = getContext();
-            if (cx == null ||
-                !cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS))
-            {
-                throw (Error)e;
+            if (cx == null || !cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)) {
+                throw (Error) e;
             }
         }
         if (e instanceof RhinoException) {
-            throw (RhinoException)e;
+            throw (RhinoException) e;
         }
         throw new WrappedException(e);
     }
 
     /**
      * Tell whether debug information is being generated.
+     *
      * @since 1.3
      */
-    public final boolean isGeneratingDebug()
-    {
+    public final boolean isGeneratingDebug() {
         return generatingDebug;
     }
 
     /**
      * Specify whether or not debug information should be generated.
-     * <p>
-     * Setting the generation of debug information on will set the
-     * optimization level to zero.
+     *
+     * <p>Setting the generation of debug information on will set the optimization level to zero.
+     *
      * @since 1.3
      */
-    public final void setGeneratingDebug(boolean generatingDebug)
-    {
+    public final void setGeneratingDebug(boolean generatingDebug) {
         if (sealed) onSealedMutation();
         generatingDebugChanged = true;
-        if (generatingDebug && getOptimizationLevel() > 0)
-            setOptimizationLevel(0);
+        if (generatingDebug && getOptimizationLevel() > 0) setOptimizationLevel(0);
         this.generatingDebug = generatingDebug;
     }
 
     /**
      * Tell whether source information is being generated.
+     *
      * @since 1.3
      */
-    public final boolean isGeneratingSource()
-    {
+    public final boolean isGeneratingSource() {
         return generatingSource;
     }
 
     /**
      * Specify whether or not source information should be generated.
-     * <p>
-     * Without source information, evaluating the "toString" method
-     * on JavaScript functions produces only "[native code]" for
-     * the body of the function.
-     * Note that code generated without source is not fully ECMA
-     * conformant.
+     *
+     * <p>Without source information, evaluating the "toString" method on JavaScript functions
+     * produces only "[native code]" for the body of the function. Note that code generated without
+     * source is not fully ECMA conformant.
+     *
      * @since 1.3
      */
-    public final void setGeneratingSource(boolean generatingSource)
-    {
+    public final void setGeneratingSource(boolean generatingSource) {
         if (sealed) onSealedMutation();
         this.generatingSource = generatingSource;
     }
 
     /**
      * Get the current optimization level.
-     * <p>
-     * The optimization level is expressed as an integer between -1 and
-     * 9.
-     * @since 1.3
      *
+     * <p>The optimization level is expressed as an integer between -1 and 9.
+     *
+     * @since 1.3
      */
-    public final int getOptimizationLevel()
-    {
+    public final int getOptimizationLevel() {
         return optimizationLevel;
     }
 
     /**
      * Set the current optimization level.
-     * <p>
-     * The optimization level is expected to be an integer between -1 and
-     * 9. Any negative values will be interpreted as -1, and any values
-     * greater than 9 will be interpreted as 9.
-     * An optimization level of -1 indicates that interpretive mode will
-     * always be used. Levels 0 through 9 indicate that class files may
-     * be generated. Higher optimization levels trade off compile time
-     * performance for runtime performance.
-     * The optimizer level can't be set greater than -1 if the optimizer
-     * package doesn't exist at run time.
-     * @param optimizationLevel an integer indicating the level of
-     *        optimization to perform
-     * @since 1.3
      *
+     * <p>The optimization level is expected to be an integer between -1 and 9. Any negative values
+     * will be interpreted as -1, and any values greater than 9 will be interpreted as 9. An
+     * optimization level of -1 indicates that interpretive mode will always be used. Levels 0
+     * through 9 indicate that class files may be generated. Higher optimization levels trade off
+     * compile time performance for runtime performance. The optimizer level can't be set greater
+     * than -1 if the optimizer package doesn't exist at run time.
+     *
+     * @param optimizationLevel an integer indicating the level of optimization to perform
+     * @since 1.3
      */
-    public final void setOptimizationLevel(int optimizationLevel)
-    {
+    public final void setOptimizationLevel(int optimizationLevel) {
         if (sealed) onSealedMutation();
         if (optimizationLevel == -2) {
             // To be compatible with Cocoon fork
             optimizationLevel = -1;
         }
         checkOptimizationLevel(optimizationLevel);
-        if (codegenClass == null)
-            optimizationLevel = -1;
+        if (codegenClass == null) optimizationLevel = -1;
         this.optimizationLevel = optimizationLevel;
     }
 
-    public static boolean isValidOptimizationLevel(int optimizationLevel)
-    {
+    public static boolean isValidOptimizationLevel(int optimizationLevel) {
         return -1 <= optimizationLevel && optimizationLevel <= 9;
     }
 
-    public static void checkOptimizationLevel(int optimizationLevel)
-    {
+    public static void checkOptimizationLevel(int optimizationLevel) {
         if (isValidOptimizationLevel(optimizationLevel)) {
             return;
         }
         throw new IllegalArgumentException(
-            "Optimization level outside [-1..9]: "+optimizationLevel);
+                "Optimization level outside [-1..9]: " + optimizationLevel);
     }
 
     /**
-     * Returns the maximum stack depth (in terms of number of call frames)
-     * allowed in a single invocation of interpreter. If the set depth would be
-     * exceeded, the interpreter will throw an EvaluatorException in the script.
-     * Defaults to Integer.MAX_VALUE. The setting only has effect for
-     * interpreted functions (those compiled with optimization level set to -1).
-     * As the interpreter doesn't use the Java stack but rather manages its own
-     * stack in the heap memory, a runaway recursion in interpreted code would
-     * eventually consume all available memory and cause OutOfMemoryError
-     * instead of a StackOverflowError limited to only a single thread. This
+     * Returns the maximum stack depth (in terms of number of call frames) allowed in a single
+     * invocation of interpreter. If the set depth would be exceeded, the interpreter will throw an
+     * EvaluatorException in the script. Defaults to Integer.MAX_VALUE. The setting only has effect
+     * for interpreted functions (those compiled with optimization level set to -1). As the
+     * interpreter doesn't use the Java stack but rather manages its own stack in the heap memory, a
+     * runaway recursion in interpreted code would eventually consume all available memory and cause
+     * OutOfMemoryError instead of a StackOverflowError limited to only a single thread. This
      * setting helps prevent such situations.
      *
      * @return The current maximum interpreter stack depth.
      */
-    public final int getMaximumInterpreterStackDepth()
-    {
+    public final int getMaximumInterpreterStackDepth() {
         return maximumInterpreterStackDepth;
     }
 
     /**
-     * Sets the maximum stack depth (in terms of number of call frames)
-     * allowed in a single invocation of interpreter. If the set depth would be
-     * exceeded, the interpreter will throw an EvaluatorException in the script.
-     * Defaults to Integer.MAX_VALUE. The setting only has effect for
-     * interpreted functions (those compiled with optimization level set to -1).
-     * As the interpreter doesn't use the Java stack but rather manages its own
-     * stack in the heap memory, a runaway recursion in interpreted code would
-     * eventually consume all available memory and cause OutOfMemoryError
-     * instead of a StackOverflowError limited to only a single thread. This
+     * Sets the maximum stack depth (in terms of number of call frames) allowed in a single
+     * invocation of interpreter. If the set depth would be exceeded, the interpreter will throw an
+     * EvaluatorException in the script. Defaults to Integer.MAX_VALUE. The setting only has effect
+     * for interpreted functions (those compiled with optimization level set to -1). As the
+     * interpreter doesn't use the Java stack but rather manages its own stack in the heap memory, a
+     * runaway recursion in interpreted code would eventually consume all available memory and cause
+     * OutOfMemoryError instead of a StackOverflowError limited to only a single thread. This
      * setting helps prevent such situations.
      *
      * @param max the new maximum interpreter stack depth
-     * @throws IllegalStateException if this context's optimization level is not
-     * -1
+     * @throws IllegalStateException if this context's optimization level is not -1
      * @throws IllegalArgumentException if the new depth is not at least 1
      */
-    public final void setMaximumInterpreterStackDepth(int max)
-    {
-        if(sealed) onSealedMutation();
-        if(optimizationLevel != -1) {
-            throw new IllegalStateException("Cannot set maximumInterpreterStackDepth when optimizationLevel != -1");
+    public final void setMaximumInterpreterStackDepth(int max) {
+        if (sealed) onSealedMutation();
+        if (optimizationLevel != -1) {
+            throw new IllegalStateException(
+                    "Cannot set maximumInterpreterStackDepth when optimizationLevel != -1");
         }
-        if(max < 1) {
-            throw new IllegalArgumentException("Cannot set maximumInterpreterStackDepth to less than 1");
+        if (max < 1) {
+            throw new IllegalArgumentException(
+                    "Cannot set maximumInterpreterStackDepth to less than 1");
         }
         maximumInterpreterStackDepth = max;
     }
 
     /**
      * Set the security controller for this context.
-     * <p> SecurityController may only be set if it is currently null
-     * and {@link SecurityController#hasGlobal()} is <tt>false</tt>.
-     * Otherwise a SecurityException is thrown.
+     *
+     * <p>SecurityController may only be set if it is currently null and {@link
+     * SecurityController#hasGlobal()} is <code>false</code>. Otherwise a SecurityException is
+     * thrown.
+     *
      * @param controller a SecurityController object
-     * @throws SecurityException if there is already a SecurityController
-     *         object for this Context or globally installed.
+     * @throws SecurityException if there is already a SecurityController object for this Context or
+     *     globally installed.
      * @see SecurityController#initGlobal(SecurityController controller)
      * @see SecurityController#hasGlobal()
      */
-    public final void setSecurityController(SecurityController controller)
-    {
+    public final void setSecurityController(SecurityController controller) {
         if (sealed) onSealedMutation();
         if (controller == null) throw new IllegalArgumentException();
         if (securityController != null) {
             throw new SecurityException("Can not overwrite existing SecurityController object");
         }
         if (SecurityController.hasGlobal()) {
-            throw new SecurityException("Can not overwrite existing global SecurityController object");
+            throw new SecurityException(
+                    "Can not overwrite existing global SecurityController object");
         }
         securityController = controller;
     }
 
     /**
      * Set the LiveConnect access filter for this context.
-     * <p> {@link ClassShutter} may only be set if it is currently null.
-     * Otherwise a SecurityException is thrown.
+     *
+     * <p>{@link ClassShutter} may only be set if it is currently null. Otherwise a
+     * SecurityException is thrown.
+     *
      * @param shutter a ClassShutter object
-     * @throws SecurityException if there is already a ClassShutter
-     *         object for this Context
+     * @throws SecurityException if there is already a ClassShutter object for this Context
      */
-    public synchronized final void setClassShutter(ClassShutter shutter)
-    {
+    public final synchronized void setClassShutter(ClassShutter shutter) {
         if (sealed) onSealedMutation();
         if (shutter == null) throw new IllegalArgumentException();
         if (hasClassShutter) {
-            throw new SecurityException("Cannot overwrite existing " +
-                                        "ClassShutter object");
+            throw new SecurityException("Cannot overwrite existing " + "ClassShutter object");
         }
         classShutter = shutter;
         hasClassShutter = true;
     }
 
-    final synchronized ClassShutter getClassShutter()
-    {
+    final synchronized ClassShutter getClassShutter() {
         return classShutter;
     }
 
     public interface ClassShutterSetter {
         public void setClassShutter(ClassShutter shutter);
+
         public ClassShutter getClassShutter();
     }
 
     public final synchronized ClassShutterSetter getClassShutterSetter() {
-        if (hasClassShutter)
-            return null;
+        if (hasClassShutter) return null;
         hasClassShutter = true;
         return new ClassShutterSetter() {
+
+            @Override
             public void setClassShutter(ClassShutter shutter) {
                 classShutter = shutter;
             }
+
+            @Override
             public ClassShutter getClassShutter() {
                 return classShutter;
             }
@@ -2139,50 +2035,46 @@
 
     /**
      * Get a value corresponding to a key.
-     * <p>
-     * Since the Context is associated with a thread it can be
-     * used to maintain values that can be later retrieved using
-     * the current thread.
-     * <p>
-     * Note that the values are maintained with the Context, so
-     * if the Context is disassociated from the thread the values
-     * cannot be retrieved. Also, if private data is to be maintained
-     * in this manner the key should be a java.lang.Object
-     * whose reference is not divulged to untrusted code.
+     *
+     * <p>Since the Context is associated with a thread it can be used to maintain values that can
+     * be later retrieved using the current thread.
+     *
+     * <p>Note that the values are maintained with the Context, so if the Context is disassociated
+     * from the thread the values cannot be retrieved. Also, if private data is to be maintained in
+     * this manner the key should be a java.lang.Object whose reference is not divulged to untrusted
+     * code.
+     *
      * @param key the key used to lookup the value
      * @return a value previously stored using putThreadLocal.
      */
-    public final Object getThreadLocal(Object key)
-    {
-        if (threadLocalMap == null)
-            return null;
+    public final Object getThreadLocal(Object key) {
+        if (threadLocalMap == null) return null;
         return threadLocalMap.get(key);
     }
 
     /**
      * Put a value that can later be retrieved using a given key.
+     *
      * <p>
+     *
      * @param key the key used to index the value
      * @param value the value to save
      */
-    public synchronized final void putThreadLocal(Object key, Object value)
-    {
+    public final synchronized void putThreadLocal(Object key, Object value) {
         if (sealed) onSealedMutation();
-        if (threadLocalMap == null)
-            threadLocalMap = new HashMap<Object,Object>();
+        if (threadLocalMap == null) threadLocalMap = new HashMap<Object, Object>();
         threadLocalMap.put(key, value);
     }
 
     /**
      * Remove values from thread-local storage.
+     *
      * @param key the key for the entry to remove.
      * @since 1.5 release 2
      */
-    public final void removeThreadLocal(Object key)
-    {
+    public final void removeThreadLocal(Object key) {
         if (sealed) onSealedMutation();
-        if (threadLocalMap == null)
-            return;
+        if (threadLocalMap == null) return;
         threadLocalMap.remove(key);
     }
 
@@ -2192,20 +2084,18 @@
      * @see ClassCache#setCachingEnabled(boolean)
      */
     @Deprecated
-    public static void setCachingEnabled(boolean cachingEnabled)
-    {
-    }
+    public static void setCachingEnabled(boolean cachingEnabled) {}
 
     /**
      * Set a WrapFactory for this Context.
-     * <p>
-     * The WrapFactory allows custom object wrapping behavior for
-     * Java object manipulated with JavaScript.
+     *
+     * <p>The WrapFactory allows custom object wrapping behavior for Java object manipulated with
+     * JavaScript.
+     *
      * @see WrapFactory
      * @since 1.5 Release 4
      */
-    public final void setWrapFactory(WrapFactory wrapFactory)
-    {
+    public final void setWrapFactory(WrapFactory wrapFactory) {
         if (sealed) onSealedMutation();
         if (wrapFactory == null) throw new IllegalArgumentException();
         this.wrapFactory = wrapFactory;
@@ -2213,11 +2103,11 @@
 
     /**
      * Return the current WrapFactory, or null if none is defined.
+     *
      * @see WrapFactory
      * @since 1.5 Release 4
      */
-    public final WrapFactory getWrapFactory()
-    {
+    public final WrapFactory getWrapFactory() {
         if (wrapFactory == null) {
             wrapFactory = new WrapFactory();
         }
@@ -2226,58 +2116,53 @@
 
     /**
      * Return the current debugger.
+     *
      * @return the debugger, or null if none is attached.
      */
-    public final Debugger getDebugger()
-    {
+    public final Debugger getDebugger() {
         return debugger;
     }
 
     /**
      * Return the debugger context data associated with current context.
+     *
      * @return the debugger data, or null if debugger is not attached
      */
-    public final Object getDebuggerContextData()
-    {
+    public final Object getDebuggerContextData() {
         return debuggerData;
     }
 
     /**
      * Set the associated debugger.
-     * @param debugger the debugger to be used on callbacks from
-     * the engine.
-     * @param contextData arbitrary object that debugger can use to store
-     *        per Context data.
+     *
+     * @param debugger the debugger to be used on callbacks from the engine.
+     * @param contextData arbitrary object that debugger can use to store per Context data.
      */
-    public final void setDebugger(Debugger debugger, Object contextData)
-    {
+    public final void setDebugger(Debugger debugger, Object contextData) {
         if (sealed) onSealedMutation();
         this.debugger = debugger;
         debuggerData = contextData;
     }
 
     /**
-     * Return DebuggableScript instance if any associated with the script.
-     * If callable supports DebuggableScript implementation, the method
-     * returns it. Otherwise null is returned.
+     * Return DebuggableScript instance if any associated with the script. If callable supports
+     * DebuggableScript implementation, the method returns it. Otherwise null is returned.
      */
-    public static DebuggableScript getDebuggableView(Script script)
-    {
+    public static DebuggableScript getDebuggableView(Script script) {
         if (script instanceof NativeFunction) {
-            return ((NativeFunction)script).getDebuggableView();
+            return ((NativeFunction) script).getDebuggableView();
         }
         return null;
     }
 
     /**
-     * Controls certain aspects of script semantics.
-     * Should be overwritten to alter default behavior.
-     * <p>
-     * The default implementation calls
-     * {@link ContextFactory#hasFeature(Context cx, int featureIndex)}
-     * that allows to customize Context behavior without introducing
-     * Context subclasses.  {@link ContextFactory} documentation gives
-     * an example of hasFeature implementation.
+     * Controls certain aspects of script semantics. Should be overwritten to alter default
+     * behavior.
+     *
+     * <p>The default implementation calls {@link ContextFactory#hasFeature(Context cx, int
+     * featureIndex)} that allows to customize Context behavior without introducing Context
+     * subclasses. {@link ContextFactory} documentation gives an example of hasFeature
+     * implementation.
      *
      * @param featureIndex feature index to check
      * @return true if the <code>featureIndex</code> feature is turned on
@@ -2295,57 +2180,49 @@
      * @see #FEATURE_WARNING_AS_ERROR
      * @see #FEATURE_ENHANCED_JAVA_ACCESS
      */
-    public boolean hasFeature(int featureIndex)
-    {
+    public boolean hasFeature(int featureIndex) {
         ContextFactory f = getFactory();
         return f.hasFeature(this, featureIndex);
     }
 
     /**
-     * Returns an object which specifies an E4X implementation to use within
-     * this <code>Context</code>. Note that the XMLLib.Factory interface should
-     * be considered experimental.
+     * Returns an object which specifies an E4X implementation to use within this <code>Context
+     * </code>. Note that the XMLLib.Factory interface should be considered experimental.
      *
-     * The default implementation uses the implementation provided by this
-     * <code>Context</code>'s {@link ContextFactory}.
+     * <p>The default implementation uses the implementation provided by this <code>Context</code>'s
+     * {@link ContextFactory}.
      *
-     * @return An XMLLib.Factory. Should not return <code>null</code> if
-     *         {@link #FEATURE_E4X} is enabled. See {@link #hasFeature}.
+     * @return An XMLLib.Factory. Should not return <code>null</code> if {@link #FEATURE_E4X} is
+     *     enabled. See {@link #hasFeature}.
      */
     public XMLLib.Factory getE4xImplementationFactory() {
         return getFactory().getE4xImplementationFactory();
     }
 
     /**
-     * Get threshold of executed instructions counter that triggers call to
-     * <code>observeInstructionCount()</code>.
-     * When the threshold is zero, instruction counting is disabled,
-     * otherwise each time the run-time executes at least the threshold value
-     * of script instructions, <code>observeInstructionCount()</code> will
-     * be called.
+     * Get threshold of executed instructions counter that triggers call to <code>
+     * observeInstructionCount()</code>. When the threshold is zero, instruction counting is
+     * disabled, otherwise each time the run-time executes at least the threshold value of script
+     * instructions, <code>observeInstructionCount()</code> will be called.
      */
-    public final int getInstructionObserverThreshold()
-    {
+    public final int getInstructionObserverThreshold() {
         return instructionThreshold;
     }
 
     /**
-     * Set threshold of executed instructions counter that triggers call to
-     * <code>observeInstructionCount()</code>.
-     * When the threshold is zero, instruction counting is disabled,
-     * otherwise each time the run-time executes at least the threshold value
-     * of script instructions, <code>observeInstructionCount()</code> will
-     * be called.<p/>
-     * Note that the meaning of "instruction" is not guaranteed to be
-     * consistent between compiled and interpretive modes: executing a given
-     * script or function in the different modes will result in different
-     * instruction counts against the threshold.
-     * {@link #setGenerateObserverCount} is called with true if
-     * <code>threshold</code> is greater than zero, false otherwise.
+     * Set threshold of executed instructions counter that triggers call to <code>
+     * observeInstructionCount()</code>. When the threshold is zero, instruction counting is
+     * disabled, otherwise each time the run-time executes at least the threshold value of script
+     * instructions, <code>observeInstructionCount()</code> will be called.<br>
+     * Note that the meaning of "instruction" is not guaranteed to be consistent between compiled
+     * and interpretive modes: executing a given script or function in the different modes will
+     * result in different instruction counts against the threshold. {@link
+     * #setGenerateObserverCount} is called with true if <code>threshold</code> is greater than
+     * zero, false otherwise.
+     *
      * @param threshold The instruction threshold
      */
-    public final void setInstructionObserverThreshold(int threshold)
-    {
+    public final void setInstructionObserverThreshold(int threshold) {
         if (sealed) onSealedMutation();
         if (threshold < 0) throw new IllegalArgumentException();
         instructionThreshold = threshold;
@@ -2353,67 +2230,55 @@
     }
 
     /**
-     * Turn on or off generation of code with callbacks to
-     * track the count of executed instructions.
-     * Currently only affects JVM byte code generation: this slows down the
-     * generated code, but code generated without the callbacks will not
-     * be counted toward instruction thresholds. Rhino's interpretive
-     * mode does instruction counting without inserting callbacks, so
-     * there is no requirement to compile code differently.
-     * @param generateObserverCount if true, generated code will contain
-     * calls to accumulate an estimate of the instructions executed.
+     * Turn on or off generation of code with callbacks to track the count of executed instructions.
+     * Currently only affects JVM byte code generation: this slows down the generated code, but code
+     * generated without the callbacks will not be counted toward instruction thresholds. Rhino's
+     * interpretive mode does instruction counting without inserting callbacks, so there is no
+     * requirement to compile code differently.
+     *
+     * @param generateObserverCount if true, generated code will contain calls to accumulate an
+     *     estimate of the instructions executed.
      */
     public void setGenerateObserverCount(boolean generateObserverCount) {
         this.generateObserverCount = generateObserverCount;
     }
 
     /**
-     * Allow application to monitor counter of executed script instructions
-     * in Context subclasses.
-     * Run-time calls this when instruction counting is enabled and the counter
-     * reaches limit set by <code>setInstructionObserverThreshold()</code>.
-     * The method is useful to observe long running scripts and if necessary
-     * to terminate them.
-     * <p>
-     * The default implementation calls
-     * {@link ContextFactory#observeInstructionCount(Context cx,
-     *                                               int instructionCount)}
-     * that allows to customize Context behavior without introducing
-     * Context subclasses.
+     * Allow application to monitor counter of executed script instructions in Context subclasses.
+     * Run-time calls this when instruction counting is enabled and the counter reaches limit set by
+     * <code>setInstructionObserverThreshold()</code>. The method is useful to observe long running
+     * scripts and if necessary to terminate them.
+     *
+     * <p>The default implementation calls {@link ContextFactory#observeInstructionCount(Context cx,
+     * int instructionCount)} that allows to customize Context behavior without introducing Context
+     * subclasses.
      *
-     * @param instructionCount amount of script instruction executed since
-     * last call to <code>observeInstructionCount</code>
+     * @param instructionCount amount of script instruction executed since last call to <code>
+     *     observeInstructionCount</code>
      * @throws Error to terminate the script
      * @see #setOptimizationLevel(int)
      */
-    protected void observeInstructionCount(int instructionCount)
-    {
+    protected void observeInstructionCount(int instructionCount) {
         ContextFactory f = getFactory();
         f.observeInstructionCount(this, instructionCount);
     }
 
     /**
-     * Create class loader for generated classes.
-     * The method calls {@link ContextFactory#createClassLoader(ClassLoader)}
-     * using the result of {@link #getFactory()}.
+     * Create class loader for generated classes. The method calls {@link
+     * ContextFactory#createClassLoader(ClassLoader)} using the result of {@link #getFactory()}.
      */
-    public GeneratedClassLoader createClassLoader(ClassLoader parent)
-    {
+    public GeneratedClassLoader createClassLoader(ClassLoader parent) {
         ContextFactory f = getFactory();
         return f.createClassLoader(parent);
     }
 
-    public final ClassLoader getApplicationClassLoader()
-    {
+    public final ClassLoader getApplicationClassLoader() {
         if (applicationClassLoader == null) {
             ContextFactory f = getFactory();
             ClassLoader loader = f.getApplicationClassLoader();
             if (loader == null) {
-                ClassLoader threadLoader
-                    = VMBridge.instance.getCurrentThreadClassLoader();
-                if (threadLoader != null
-                    && Kit.testIfCanLoadRhinoClasses(threadLoader))
-                {
+                ClassLoader threadLoader = Thread.currentThread().getContextClassLoader();
+                if (threadLoader != null && Kit.testIfCanLoadRhinoClasses(threadLoader)) {
                     // Thread.getContextClassLoader is not cached since
                     // its caching prevents it from GC which may lead to
                     // a memory leak and hides updates to
@@ -2435,8 +2300,7 @@
         return applicationClassLoader;
     }
 
-    public final void setApplicationClassLoader(ClassLoader loader)
-    {
+    public final void setApplicationClassLoader(ClassLoader loader) {
         if (sealed) onSealedMutation();
         if (loader == null) {
             // restore default behaviour
@@ -2444,46 +2308,86 @@
             return;
         }
         if (!Kit.testIfCanLoadRhinoClasses(loader)) {
-            throw new IllegalArgumentException(
-                "Loader can not resolve Rhino classes");
+            throw new IllegalArgumentException("Loader can not resolve Rhino classes");
         }
         applicationClassLoader = loader;
     }
 
-    /********** end of API **********/
+    /**
+     * Add a task that will be executed at the end of the current operation. The various "evaluate"
+     * functions will all call this before exiting to ensure that all microtasks run to completion.
+     * Otherwise, callers should call "processMicrotasks" to run them all. This feature is primarily
+     * used to implement Promises. The microtask queue is not thread-safe.
+     */
+    public void enqueueMicrotask(Runnable task) {
+        microtasks.add(task);
+    }
+
+    /**
+     * Run all the microtasks for the current context to completion. This is called by the various
+     * "evaluate" functions. Frameworks that call Function objects directly should call this
+     * function to ensure that everything completes if they want all Promises to eventually resolve.
+     * This function is idempotent, but the microtask queue is not thread-safe.
+     */
+    public void processMicrotasks() {
+        Runnable head;
+        do {
+            head = microtasks.poll();
+            if (head != null) {
+                head.run();
+            }
+        } while (head != null);
+    }
+
+    /**
+     * Control whether to track unhandled promise rejections. If "track" is set to true, then the
+     * tracker returned by "getUnhandledPromiseTracker" must be periodically used to process the
+     * queue of unhandled promise rejections, or a memory leak may result.
+     *
+     * @param track if true, then track unhandled promise rejections
+     */
+    public void setTrackUnhandledPromiseRejections(boolean track) {
+        unhandledPromises.enable(track);
+    }
 
     /**
-     * Internal method that reports an error for missing calls to
-     * enter().
+     * Return the object used to track unhandled promise rejections.
+     *
+     * @return the tracker object
      */
-    static Context getContext()
-    {
+    public UnhandledRejectionTracker getUnhandledPromiseTracker() {
+        return unhandledPromises;
+    }
+
+    /* ******** end of API ********* */
+
+    /** Internal method that reports an error for missing calls to enter(). */
+    static Context getContext() {
         Context cx = getCurrentContext();
         if (cx == null) {
-            throw new RuntimeException(
-                "No Context associated with current Thread");
+            throw new RuntimeException("No Context associated with current Thread");
         }
         return cx;
     }
 
-    private Object compileImpl(Scriptable scope,
-                               Reader sourceReader, String sourceString,
-                               String sourceName, int lineno,
-                               Object securityDomain, boolean returnFunction,
-                               Evaluator compiler,
-                               ErrorReporter compilationErrorReporter)
-        throws IOException
-    {
-        if(sourceName == null) {
+    protected Object compileImpl(
+            Scriptable scope,
+            String sourceString,
+            String sourceName,
+            int lineno,
+            Object securityDomain,
+            boolean returnFunction,
+            Evaluator compiler,
+            ErrorReporter compilationErrorReporter)
+            throws IOException {
+        if (sourceName == null) {
             sourceName = "unnamed script";
         }
         if (securityDomain != null && getSecurityController() == null) {
             throw new IllegalArgumentException(
-                "securityDomain should be null if setSecurityController() was never called");
+                    "securityDomain should be null if setSecurityController() was never called");
         }
 
-        // One of sourceReader or sourceString has to be null
-        if (!(sourceReader == null ^ sourceString == null)) Kit.codeBug();
         // scope should be given if and only if compiling function
         if (!(scope == null ^ returnFunction)) Kit.codeBug();
 
@@ -2493,58 +2397,44 @@
             compilationErrorReporter = compilerEnv.getErrorReporter();
         }
 
-        if (debugger != null) {
-            if (sourceReader != null) {
-                sourceString = Kit.readReader(sourceReader);
-                sourceReader = null;
-            }
-        }
+        ScriptNode tree =
+                parse(
+                        sourceString,
+                        sourceName,
+                        lineno,
+                        compilerEnv,
+                        compilationErrorReporter,
+                        returnFunction);
 
-        Parser p = new Parser(compilerEnv, compilationErrorReporter);
-        if (returnFunction) {
-            p.calledByCompileFunction = true;
-        }
-        if (isStrictMode()) {
-            p.setDefaultUseStrictDirective(true);
-        }
-        AstRoot ast;
-        if (sourceString != null) {
-            ast = p.parse(sourceString, sourceName, lineno);
-        } else {
-            ast = p.parse(sourceReader, sourceName, lineno);
-        }
-        if (returnFunction) {
-            // parser no longer adds function to script node
-            if (!(ast.getFirstChild() != null
-                  && ast.getFirstChild().getType() == Token.FUNCTION))
-            {
-                // XXX: the check just looks for the first child
-                // and allows for more nodes after it for compatibility
-                // with sources like function() {};;;
-                throw new IllegalArgumentException(
-                    "compileFunction only accepts source with single JS function: "+sourceString);
+        Object bytecode;
+        try {
+            if (compiler == null) {
+                compiler = createCompiler();
             }
-        }
 
-        IRFactory irf = new IRFactory(compilerEnv, compilationErrorReporter);
-        ScriptNode tree = irf.transformTree(ast);
+            bytecode = compiler.compile(compilerEnv, tree, tree.getEncodedSource(), returnFunction);
+        } catch (ClassFileFormatException e) {
+            // we hit some class file limit, fall back to interpreter or report
+
+            // we have to recreate the tree because the compile call might have changed the tree
+            // already
+            tree =
+                    parse(
+                            sourceString,
+                            sourceName,
+                            lineno,
+                            compilerEnv,
+                            compilationErrorReporter,
+                            returnFunction);
 
-        // discard everything but the IR tree
-        p = null;
-        ast = null;
-        irf = null;
-
-        if (compiler == null) {
-            compiler = createCompiler();
+            compiler = createInterpreter();
+            bytecode = compiler.compile(compilerEnv, tree, tree.getEncodedSource(), returnFunction);
         }
 
-        Object bytecode = compiler.compile(compilerEnv,
-                                           tree, tree.getEncodedSource(),
-                                           returnFunction);
         if (debugger != null) {
             if (sourceString == null) Kit.codeBug();
             if (bytecode instanceof DebuggableScript) {
-                DebuggableScript dscript = (DebuggableScript)bytecode;
+                DebuggableScript dscript = (DebuggableScript) bytecode;
                 notifyDebugger_r(this, dscript, sourceString);
             } else {
                 throw new RuntimeException("NOT SUPPORTED");
@@ -2561,25 +2451,56 @@
         return result;
     }
 
-    private static void notifyDebugger_r(Context cx, DebuggableScript dscript,
-                                         String debugSource)
-    {
+    private ScriptNode parse(
+            String sourceString,
+            String sourceName,
+            int lineno,
+            CompilerEnvirons compilerEnv,
+            ErrorReporter compilationErrorReporter,
+            boolean returnFunction)
+            throws IOException {
+        Parser p = new Parser(compilerEnv, compilationErrorReporter);
+        if (returnFunction) {
+            p.calledByCompileFunction = true;
+        }
+        if (isStrictMode()) {
+            p.setDefaultUseStrictDirective(true);
+        }
+
+        AstRoot ast = p.parse(sourceString, sourceName, lineno);
+        if (returnFunction) {
+            // parser no longer adds function to script node
+            if (!(ast.getFirstChild() != null && ast.getFirstChild().getType() == Token.FUNCTION)) {
+                // XXX: the check just looks for the first child
+                // and allows for more nodes after it for compatibility
+                // with sources like function() {};;;
+                throw new IllegalArgumentException(
+                        "compileFunction only accepts source with single JS function: "
+                                + sourceString);
+            }
+        }
+
+        IRFactory irf = new IRFactory(compilerEnv, compilationErrorReporter);
+        ScriptNode tree = irf.transformTree(ast);
+        return tree;
+    }
+
+    private static void notifyDebugger_r(Context cx, DebuggableScript dscript, String debugSource) {
         cx.debugger.handleCompilationDone(cx, dscript, debugSource);
         for (int i = 0; i != dscript.getFunctionCount(); ++i) {
             notifyDebugger_r(cx, dscript.getFunction(i), debugSource);
         }
     }
 
-    private static Class<?> codegenClass = Kit.classOrNull(
-                             "org.mozilla.javascript.optimizer.Codegen");
-    private static Class<?> interpreterClass = Kit.classOrNull(
-                             "org.mozilla.javascript.Interpreter");
+    private static Class<?> codegenClass =
+            Kit.classOrNull("org.mozilla.javascript.optimizer.Codegen");
+    private static Class<?> interpreterClass =
+            Kit.classOrNull("org.mozilla.javascript.Interpreter");
 
-    private Evaluator createCompiler()
-    {
+    private Evaluator createCompiler() {
         Evaluator result = null;
         if (optimizationLevel >= 0 && codegenClass != null) {
-            result = (Evaluator)Kit.newInstanceOrNull(codegenClass);
+            result = (Evaluator) Kit.newInstanceOrNull(codegenClass);
         }
         if (result == null) {
             result = createInterpreter();
@@ -2587,24 +2508,20 @@
         return result;
     }
 
-    static Evaluator createInterpreter()
-    {
-        return (Evaluator)Kit.newInstanceOrNull(interpreterClass);
+    static Evaluator createInterpreter() {
+        return (Evaluator) Kit.newInstanceOrNull(interpreterClass);
     }
 
-    static String getSourcePositionFromStack(int[] linep)
-    {
+    static String getSourcePositionFromStack(int[] linep) {
         Context cx = getCurrentContext();
-        if (cx == null)
-            return null;
+        if (cx == null) return null;
         if (cx.lastInterpreterFrame != null) {
             Evaluator evaluator = createInterpreter();
-            if (evaluator != null)
-                return evaluator.getSourcePositionFromStack(cx, linep);
+            if (evaluator != null) return evaluator.getSourcePositionFromStack(cx, linep);
         }
         /**
-         * A bit of a hack, but the only way to get filename and line
-         * number from an enclosing frame.
+         * A bit of a hack, but the only way to get filename and line number from an enclosing
+         * frame.
          */
         StackTraceElement[] stackTrace = new Throwable().getStackTrace();
         for (StackTraceElement st : stackTrace) {
@@ -2621,26 +2538,22 @@
         return null;
     }
 
-    RegExpProxy getRegExpProxy()
-    {
+    RegExpProxy getRegExpProxy() {
         if (regExpProxy == null) {
-            Class<?> cl = Kit.classOrNull(
-                          "org.mozilla.javascript.regexp.RegExpImpl");
+            Class<?> cl = Kit.classOrNull("org.mozilla.javascript.regexp.RegExpImpl");
             if (cl != null) {
-                regExpProxy = (RegExpProxy)Kit.newInstanceOrNull(cl);
+                regExpProxy = (RegExpProxy) Kit.newInstanceOrNull(cl);
             }
         }
         return regExpProxy;
     }
 
-    final boolean isVersionECMA1()
-    {
+    final boolean isVersionECMA1() {
         return version == VERSION_DEFAULT || version >= VERSION_1_3;
     }
 
-// The method must NOT be public or protected
-    SecurityController getSecurityController()
-    {
+    // The method must NOT be public or protected
+    SecurityController getSecurityController() {
         SecurityController global = SecurityController.global();
         if (global != null) {
             return global;
@@ -2648,56 +2561,56 @@
         return securityController;
     }
 
-    public final boolean isGeneratingDebugChanged()
-    {
+    public final boolean isGeneratingDebugChanged() {
         return generatingDebugChanged;
     }
 
     /**
-     * Add a name to the list of names forcing the creation of real
-     * activation objects for functions.
+     * Add a name to the list of names forcing the creation of real activation objects for
+     * functions.
      *
      * @param name the name of the object to add to the list
      */
-    public void addActivationName(String name)
-    {
+    public void addActivationName(String name) {
         if (sealed) onSealedMutation();
-        if (activationNames == null)
-            activationNames = new HashSet<String>();
+        if (activationNames == null) activationNames = new HashSet<String>();
         activationNames.add(name);
     }
 
     /**
-     * Check whether the name is in the list of names of objects
-     * forcing the creation of activation objects.
+     * Check whether the name is in the list of names of objects forcing the creation of activation
+     * objects.
      *
      * @param name the name of the object to test
-     *
      * @return true if an function activation object is needed.
      */
-    public final boolean isActivationNeeded(String name)
-    {
+    public final boolean isActivationNeeded(String name) {
         return activationNames != null && activationNames.contains(name);
     }
 
     /**
-     * Remove a name from the list of names forcing the creation of real
-     * activation objects for functions.
+     * Remove a name from the list of names forcing the creation of real activation objects for
+     * functions.
      *
      * @param name the name of the object to remove from the list
      */
-    public void removeActivationName(String name)
-    {
+    public void removeActivationName(String name) {
         if (sealed) onSealedMutation();
-        if (activationNames != null)
-            activationNames.remove(name);
+        if (activationNames != null) activationNames.remove(name);
     }
 
     public final boolean isStrictMode() {
-        return isTopLevelStrict || (currentActivationCall != null && currentActivationCall.isStrict);
+        return isTopLevelStrict
+                || (currentActivationCall != null && currentActivationCall.isStrict);
     }
 
-    private static String implementationVersion;
+    public static boolean isCurrentContextStrict() {
+        Context cx = getCurrentContext();
+        if (cx == null) {
+            return false;
+        }
+        return cx.isStrictMode();
+    }
 
     private final ContextFactory factory;
     private boolean sealed;
@@ -2726,7 +2639,7 @@
     private Locale locale;
     private boolean generatingDebug;
     private boolean generatingDebugChanged;
-    private boolean generatingSource=true;
+    private boolean generatingSource = true;
     boolean useDynamicScope;
     private int optimizationLevel;
     private int maximumInterpreterStackDepth;
@@ -2735,13 +2648,13 @@
     private Object debuggerData;
     private int enterCount;
     private Object propertyListeners;
-    private Map<Object,Object> threadLocalMap;
+    private Map<Object, Object> threadLocalMap;
     private ClassLoader applicationClassLoader;
+    private UnaryOperator<Object> javaToJSONConverter;
+    private final ArrayDeque<Runnable> microtasks = new ArrayDeque<>();
+    private final UnhandledRejectionTracker unhandledPromises = new UnhandledRejectionTracker();
 
-    /**
-     * This is the list of names of objects forcing the creation of
-     * function activation records.
-     */
+    /** This is the list of names of objects forcing the creation of function activation records. */
     Set<String> activationNames;
 
     // For the interpreter to store the last frame for error reports etc.
@@ -2755,9 +2668,6 @@
     int instructionCount;
     int instructionThreshold;
 
-    // It can be used to return the second index-like result from function
-    int scratchIndex;
-
     // It can be used to return the second uint32 result from function
     long scratchUint32;
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/debug/DebuggableScript.java rhino-1.7.14/src/org/mozilla/javascript/debug/DebuggableScript.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/debug/DebuggableScript.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/debug/DebuggableScript.java	2022-01-06 22:57:21.000000000 +0100
@@ -48,8 +48,8 @@
 
     /**
      * Get name of a declared parameter or local variable.
-     * <tt>index</tt> should be less then {@link #getParamAndVarCount()}.
-     * If <tt>index < {@link #getParamCount()}</tt>, return
+     * <code>index</code> should be less then {@link #getParamAndVarCount()}.
+     * If <code>index < {@link #getParamCount()}</code>, return
      * the name of the corresponding parameter, otherwise return the name
      * of variable.
      * If this script is not function, return the name of the declared
@@ -65,14 +65,14 @@
 
     /**
      * Returns true if this script or function were runtime-generated
-     * from JavaScript using <tt>eval</tt> function or <tt>Function</tt>
-     * or <tt>Script</tt> constructors.
+     * from JavaScript using <code>eval</code> function or <code>Function</code>
+     * or <code>Script</code> constructors.
      */
     public boolean isGeneratedScript();
 
     /**
      * Get array containing the line numbers that
-     * that can be passed to <code>DebugFrame.onLineChange()<code>.
+     * that can be passed to <code>DebugFrame.onLineChange()</code>.
      * Note that line order in the resulting array is arbitrary
      */
     public int[] getLineNumbers();
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/Decompiler.java rhino-1.7.14/src/org/mozilla/javascript/Decompiler.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/Decompiler.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/Decompiler.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,190 +6,168 @@
 
 package org.mozilla.javascript;
 
+import java.math.BigInteger;
 import org.mozilla.javascript.ast.FunctionNode;
 
 /**
- * The following class save decompilation information about the source.
- * Source information is returned from the parser as a String
- * associated with function nodes and with the toplevel script.  When
- * saved in the constant pool of a class, this string will be UTF-8
- * encoded, and token values will occupy a single byte.
-
- * Source is saved (mostly) as token numbers.  The tokens saved pretty
- * much correspond to the token stream of a 'canonical' representation
- * of the input program, as directed by the parser.  (There were a few
- * cases where tokens could have been left out where decompiler could
- * easily reconstruct them, but I left them in for clarity).  (I also
- * looked adding source collection to TokenStream instead, where I
- * could have limited the changes to a few lines in getToken... but
- * this wouldn't have saved any space in the resulting source
- * representation, and would have meant that I'd have to duplicate
- * parser logic in the decompiler to disambiguate situations where
- * newlines are important.)  The function decompile expands the
- * tokens back into their string representations, using simple
- * lookahead to correct spacing and indentation.
+ * The following class save decompilation information about the source. Source information is
+ * returned from the parser as a String associated with function nodes and with the toplevel script.
+ * When saved in the constant pool of a class, this string will be UTF-8 encoded, and token values
+ * will occupy a single byte.
  *
- * Assignments are saved as two-token pairs (Token.ASSIGN, op). Number tokens
- * are stored inline, as a NUMBER token, a character representing the type, and
- * either 1 or 4 characters representing the bit-encoding of the number.  String
- * types NAME, STRING and OBJECT are currently stored as a token type,
- * followed by a character giving the length of the string (assumed to
- * be less than 2^16), followed by the characters of the string
- * inlined into the source string.  Changing this to some reference to
- * to the string in the compiled class' constant pool would probably
- * save a lot of space... but would require some method of deriving
- * the final constant pool entry from information available at parse
- * time.
+ * <p>Source is saved (mostly) as token numbers. The tokens saved pretty much correspond to the
+ * token stream of a 'canonical' representation of the input program, as directed by the parser.
+ * (There were a few cases where tokens could have been left out where decompiler could easily
+ * reconstruct them, but I left them in for clarity). (I also looked adding source collection to
+ * TokenStream instead, where I could have limited the changes to a few lines in getToken... but
+ * this wouldn't have saved any space in the resulting source representation, and would have meant
+ * that I'd have to duplicate parser logic in the decompiler to disambiguate situations where
+ * newlines are important.) The function decompile expands the tokens back into their string
+ * representations, using simple lookahead to correct spacing and indentation.
+ *
+ * <p>Assignments are saved as two-token pairs (Token.ASSIGN, op). Number tokens are stored inline,
+ * as a NUMBER token, a character representing the type, and either 1 or 4 characters representing
+ * the bit-encoding of the number. String types NAME, STRING and OBJECT are currently stored as a
+ * token type, followed by a character giving the length of the string (assumed to be less than
+ * 2^16), followed by the characters of the string inlined into the source string. Changing this to
+ * some reference to to the string in the compiled class' constant pool would probably save a lot of
+ * space... but would require some method of deriving the final constant pool entry from information
+ * available at parse time.
  */
-public class Decompiler
-{
+public class Decompiler {
     /**
-     * Flag to indicate that the decompilation should omit the
-     * function header and trailing brace.
+     * Flag to indicate that the decompilation should omit the function header and trailing brace.
      */
     public static final int ONLY_BODY_FLAG = 1 << 0;
 
-    /**
-     * Flag to indicate that the decompilation generates toSource result.
-     */
+    /** Flag to indicate that the decompilation generates toSource result. */
     public static final int TO_SOURCE_FLAG = 1 << 1;
 
-    /**
-     * Decompilation property to specify initial ident value.
-     */
+    /** Decompilation property to specify initial ident value. */
     public static final int INITIAL_INDENT_PROP = 1;
 
-    /**
-     * Decompilation property to specify default identation offset.
-     */
+    /** Decompilation property to specify default identation offset. */
     public static final int INDENT_GAP_PROP = 2;
 
-    /**
-     * Decompilation property to specify identation offset for case labels.
-     */
+    /** Decompilation property to specify identation offset for case labels. */
     public static final int CASE_GAP_PROP = 3;
 
     // Marker to denote the last RC of function so it can be distinguished from
     // the last RC of object literals in case of function expressions
     private static final int FUNCTION_END = Token.LAST_TOKEN + 1;
 
-    String getEncodedSource()
-    {
+    String getEncodedSource() {
         return sourceToString(0);
     }
 
-    int getCurrentOffset()
-    {
+    int getCurrentOffset() {
         return sourceTop;
     }
 
-    int markFunctionStart(int functionType)
-    {
+    int markFunctionStart(int functionType) {
         int savedOffset = getCurrentOffset();
         if (functionType != FunctionNode.ARROW_FUNCTION) {
             addToken(Token.FUNCTION);
-            append((char)functionType);
+            append((char) functionType);
         }
         return savedOffset;
     }
 
-    int markFunctionEnd(int functionStart)
-    {
+    int markFunctionEnd(int functionStart) {
         int offset = getCurrentOffset();
-        append((char)FUNCTION_END);
+        append((char) FUNCTION_END);
         return offset;
     }
 
-    void addToken(int token)
-    {
-        if (!(0 <= token && token <= Token.LAST_TOKEN))
-            throw new IllegalArgumentException();
+    void addToken(int token) {
+        if (!(0 <= token && token <= Token.LAST_TOKEN)) throw new IllegalArgumentException();
 
-        append((char)token);
+        append((char) token);
     }
 
-    void addEOL(int token)
-    {
-        if (!(0 <= token && token <= Token.LAST_TOKEN))
-            throw new IllegalArgumentException();
+    void addEOL(int token) {
+        if (!(0 <= token && token <= Token.LAST_TOKEN)) throw new IllegalArgumentException();
 
-        append((char)token);
-        append((char)Token.EOL);
+        append((char) token);
+        append((char) Token.EOL);
     }
 
-    void addName(String str)
-    {
+    void addName(String str) {
         addToken(Token.NAME);
         appendString(str);
     }
 
-    void addString(String str)
-    {
+    void addString(String str) {
         addToken(Token.STRING);
         appendString(str);
     }
 
-    void addRegexp(String regexp, String flags)
-    {
+    void addTemplateLiteral(String str) {
+        addToken(Token.TEMPLATE_CHARS);
+        appendString(str);
+    }
+
+    void addRegexp(String regexp, String flags) {
         addToken(Token.REGEXP);
         appendString('/' + regexp + '/' + flags);
     }
 
-    void addNumber(double n)
-    {
+    void addNumber(double n) {
         addToken(Token.NUMBER);
 
         /* encode the number in the source stream.
-         * Save as NUMBER type (char | char char char char)
-         * where type is
-         * 'D' - double, 'S' - short, 'J' - long.
-
-         * We need to retain float vs. integer type info to keep the
-         * behavior of liveconnect type-guessing the same after
-         * decompilation.  (Liveconnect tries to present 1.0 to Java
-         * as a float/double)
-         * OPT: This is no longer true. We could compress the format.
-
-         * This may not be the most space-efficient encoding;
-         * the chars created below may take up to 3 bytes in
-         * constant pool UTF-8 encoding, so a Double could take
-         * up to 12 bytes.
-         */
+        * Save as NUMBER type (char | char char char char)
+        * where type is
+        * 'D' - double, 'S' - short, 'J' - long.
+
+        * We need to retain float vs. integer type info to keep the
+        * behavior of liveconnect type-guessing the same after
+        * decompilation.  (Liveconnect tries to present 1.0 to Java
+        * as a float/double)
+        * OPT: This is no longer true. We could compress the format.
+
+        * This may not be the most space-efficient encoding;
+        * the chars created below may take up to 3 bytes in
+        * constant pool UTF-8 encoding, so a Double could take
+        * up to 12 bytes.
+        */
 
-        long lbits = (long)n;
+        long lbits = (long) n;
         if (lbits != n) {
             // if it's floating point, save as a Double bit pattern.
             // (12/15/97 our scanner only returns Double for f.p.)
             lbits = Double.doubleToLongBits(n);
             append('D');
-            append((char)(lbits >> 48));
-            append((char)(lbits >> 32));
-            append((char)(lbits >> 16));
-            append((char)lbits);
-        }
-        else {
+            append((char) (lbits >> 48));
+            append((char) (lbits >> 32));
+            append((char) (lbits >> 16));
+            append((char) lbits);
+        } else {
             // we can ignore negative values, bc they're already prefixed
             // by NEG
-               if (lbits < 0) Kit.codeBug();
+            if (lbits < 0) Kit.codeBug();
 
             // will it fit in a char?
             // this gives a short encoding for integer values up to 2^16.
             if (lbits <= Character.MAX_VALUE) {
                 append('S');
-                append((char)lbits);
-            }
-            else { // Integral, but won't fit in a char. Store as a long.
+                append((char) lbits);
+            } else { // Integral, but won't fit in a char. Store as a long.
                 append('J');
-                append((char)(lbits >> 48));
-                append((char)(lbits >> 32));
-                append((char)(lbits >> 16));
-                append((char)lbits);
+                append((char) (lbits >> 48));
+                append((char) (lbits >> 32));
+                append((char) (lbits >> 16));
+                append((char) lbits);
             }
         }
     }
 
-    private void appendString(String str)
-    {
+    void addBigInt(BigInteger n) {
+        addToken(Token.BIGINT);
+        appendString(n.toString());
+    }
+
+    private void appendString(String str) {
         int L = str.length();
         int lengthEncodingSize = 1;
         if (L >= 0x8000) {
@@ -202,17 +180,16 @@
         if (L >= 0x8000) {
             // Use 2 chars to encode strings exceeding 32K, were the highest
             // bit in the first char indicates presence of the next byte
-            sourceBuffer[sourceTop] = (char)(0x8000 | (L >>> 16));
+            sourceBuffer[sourceTop] = (char) (0x8000 | (L >>> 16));
             ++sourceTop;
         }
-        sourceBuffer[sourceTop] = (char)L;
+        sourceBuffer[sourceTop] = (char) L;
         ++sourceTop;
         str.getChars(0, L, sourceBuffer, sourceTop);
         sourceTop = nextTop;
     }
 
-    private void append(char c)
-    {
+    private void append(char c) {
         if (sourceTop == sourceBuffer.length) {
             increaseSourceCapacity(sourceTop + 1);
         }
@@ -220,8 +197,7 @@
         ++sourceTop;
     }
 
-    private void increaseSourceCapacity(int minimalCapacity)
-    {
+    private void increaseSourceCapacity(int minimalCapacity) {
         // Call this only when capacity increase is must
         if (minimalCapacity <= sourceBuffer.length) Kit.codeBug();
         int newCapacity = sourceBuffer.length * 2;
@@ -233,33 +209,27 @@
         sourceBuffer = tmp;
     }
 
-    private String sourceToString(int offset)
-    {
+    private String sourceToString(int offset) {
         if (offset < 0 || sourceTop < offset) Kit.codeBug();
         return new String(sourceBuffer, offset, sourceTop - offset);
     }
 
     /**
-     * Decompile the source information associated with this js
-     * function/script back into a string.  For the most part, this
-     * just means translating tokens back to their string
-     * representations; there's a little bit of lookahead logic to
-     * decide the proper spacing/indentation.  Most of the work in
-     * mapping the original source to the prettyprinted decompiled
-     * version is done by the parser.
+     * Decompile the source information associated with this js function/script back into a string.
+     * For the most part, this just means translating tokens back to their string representations;
+     * there's a little bit of lookahead logic to decide the proper spacing/indentation. Most of the
+     * work in mapping the original source to the prettyprinted decompiled version is done by the
+     * parser.
      *
      * @param source encoded source tree presentation
-     *
      * @param flags flags to select output format
-     *
      * @param properties indentation properties
-     *
      */
-    public static String decompile(String source, int flags,
-                                   UintMap properties)
-    {
+    public static String decompile(String source, int flags, UintMap properties) {
         int length = source.length();
-        if (length == 0) { return ""; }
+        if (length == 0) {
+            return "";
+        }
 
         int indent = properties.getInt(INITIAL_INDENT_PROP, 0);
         if (indent < 0) throw new IllegalArgumentException();
@@ -286,15 +256,14 @@
                 if (tokenname == null) {
                     tokenname = "---";
                 }
-                String pad = tokenname.length() > 7
-                    ? "\t"
-                    : "\t\t";
-                System.err.println
-                    (tokenname
-                     + pad + (int)source.charAt(i)
-                     + "\t'" + ScriptRuntime.escapeString
-                     (source.substring(i, i+1))
-                     + "'");
+                String pad = tokenname.length() > 7 ? "\t" : "\t\t";
+                System.err.println(
+                        tokenname
+                                + pad
+                                + (int) source.charAt(i)
+                                + "\t'"
+                                + ScriptRuntime.escapeString(source.substring(i, i + 1))
+                                + "'");
             }
             System.err.println();
         }
@@ -313,8 +282,7 @@
         if (!toSource) {
             // add an initial newline to exactly match js.
             result.append('\n');
-            for (int j = 0; j < indent; j++)
-                result.append(' ');
+            for (int j = 0; j < indent; j++) result.append(' ');
         } else {
             if (topFunctionType == FunctionNode.FUNCTION_EXPRESSION) {
                 result.append('(');
@@ -322,494 +290,508 @@
         }
 
         while (i < length) {
-            switch(source.charAt(i)) {
-            case Token.GET:
-            case Token.SET:
-            case Token.METHOD:
-                if (source.charAt(i) == Token.GET) {
-                    result.append("get ");
-                } else if (source.charAt(i) == Token.SET) {
-                    result.append("set ");
-                }
-                ++i;
-                i = printSourceString(source, i + 1, false, result);
-                // Now increment one more to get past the FUNCTION token
-                ++i;
-                break;
-
-            case Token.NAME:
-            case Token.REGEXP:  // re-wrapped in '/'s in parser...
-                i = printSourceString(source, i + 1, false, result);
-                continue;
-
-            case Token.STRING:
-                i = printSourceString(source, i + 1, true, result);
-                continue;
-
-            case Token.NUMBER:
-                i = printSourceNumber(source, i + 1, result);
-                continue;
-
-            case Token.TRUE:
-                result.append("true");
-                break;
-
-            case Token.FALSE:
-                result.append("false");
-                break;
-
-            case Token.NULL:
-                result.append("null");
-                break;
-
-            case Token.THIS:
-                result.append("this");
-                break;
-
-            case Token.FUNCTION:
-                ++i; // skip function type
-                result.append("function ");
-                break;
-
-            case FUNCTION_END:
-                // Do nothing
-                break;
-
-            case Token.COMMA:
-                result.append(", ");
-                break;
-
-            case Token.LC:
-                ++braceNesting;
-                if (Token.EOL == getNext(source, length, i))
-                    indent += indentGap;
-                result.append('{');
-                break;
-
-            case Token.RC: {
-                --braceNesting;
-                /* don't print the closing RC if it closes the
-                 * toplevel function and we're called from
-                 * decompileFunctionBody.
-                 */
-                if (justFunctionBody && braceNesting == 0)
-                    break;
-
-                result.append('}');
-                switch (getNext(source, length, i)) {
-                    case Token.EOL:
-                    case FUNCTION_END:
-                        indent -= indentGap;
-                        break;
-                    case Token.WHILE:
-                    case Token.ELSE:
-                        indent -= indentGap;
-                        result.append(' ');
-                        break;
-                }
-                break;
-            }
-            case Token.LP:
-                result.append('(');
-                break;
+            switch (source.charAt(i)) {
+                case Token.GET:
+                case Token.SET:
+                case Token.METHOD:
+                    if (source.charAt(i) == Token.GET) {
+                        result.append("get ");
+                    } else if (source.charAt(i) == Token.SET) {
+                        result.append("set ");
+                    }
+                    ++i;
+                    i = printSourceString(source, i + 1, false, result);
+                    // Now increment one more to get past the FUNCTION token
+                    ++i;
+                    break;
 
-            case Token.RP:
-                result.append(')');
-                if (Token.LC == getNext(source, length, i))
-                    result.append(' ');
-                break;
-
-            case Token.LB:
-                result.append('[');
-                break;
-
-            case Token.RB:
-                result.append(']');
-                break;
-
-            case Token.EOL: {
-                if (toSource) break;
-                boolean newLine = true;
-                if (!afterFirstEOL) {
-                    afterFirstEOL = true;
-                    if (justFunctionBody) {
-                        /* throw away just added 'function name(...) {'
-                         * and restore the original indent
+                case Token.NAME:
+                case Token.REGEXP: // re-wrapped in '/'s in parser...
+                    i = printSourceString(source, i + 1, false, result);
+                    continue;
+
+                case Token.STRING:
+                    i = printSourceString(source, i + 1, true, result);
+                    continue;
+
+                case Token.NUMBER:
+                    i = printSourceNumber(source, i + 1, result);
+                    continue;
+
+                case Token.BIGINT:
+                    i = printSourceBigInt(source, i + 1, result);
+                    continue;
+
+                case Token.TRUE:
+                    result.append("true");
+                    break;
+
+                case Token.FALSE:
+                    result.append("false");
+                    break;
+
+                case Token.NULL:
+                    result.append("null");
+                    break;
+
+                case Token.THIS:
+                    result.append("this");
+                    break;
+
+                case Token.FUNCTION:
+                    ++i; // skip function type
+                    result.append("function ");
+                    break;
+
+                case FUNCTION_END:
+                    // Do nothing
+                    break;
+
+                case Token.COMMA:
+                    result.append(", ");
+                    break;
+
+                case Token.LC:
+                    ++braceNesting;
+                    if (Token.EOL == getNext(source, length, i)) indent += indentGap;
+                    result.append('{');
+                    break;
+
+                case Token.RC:
+                    {
+                        --braceNesting;
+                        /* don't print the closing RC if it closes the
+                         * toplevel function and we're called from
+                         * decompileFunctionBody.
                          */
-                        result.setLength(0);
-                        indent -= indentGap;
-                        newLine = false;
+                        if (justFunctionBody && braceNesting == 0) break;
+
+                        result.append('}');
+                        switch (getNext(source, length, i)) {
+                            case Token.EOL:
+                            case FUNCTION_END:
+                                indent -= indentGap;
+                                break;
+                            case Token.WHILE:
+                            case Token.ELSE:
+                                indent -= indentGap;
+                                result.append(' ');
+                                break;
+                        }
+                        break;
                     }
-                }
-                if (newLine) {
-                    result.append('\n');
-                }
+                case Token.LP:
+                    result.append('(');
+                    break;
+
+                case Token.RP:
+                    result.append(')');
+                    if (Token.LC == getNext(source, length, i)) result.append(' ');
+                    break;
+
+                case Token.LB:
+                    result.append('[');
+                    break;
+
+                case Token.RB:
+                    result.append(']');
+                    break;
 
-                /* add indent if any tokens remain,
-                 * less setback if next token is
-                 * a label, case or default.
-                 */
-                if (i + 1 < length) {
-                    int less = 0;
-                    int nextToken = source.charAt(i + 1);
-                    if (nextToken == Token.CASE
-                        || nextToken == Token.DEFAULT)
+                case Token.EOL:
                     {
-                        less = indentGap - caseGap;
-                    } else if (nextToken == Token.RC) {
-                        less = indentGap;
-                    }
+                        if (toSource) break;
+                        boolean newLine = true;
+                        if (!afterFirstEOL) {
+                            afterFirstEOL = true;
+                            if (justFunctionBody) {
+                                /* throw away just added 'function name(...) {'
+                                 * and restore the original indent
+                                 */
+                                result.setLength(0);
+                                indent -= indentGap;
+                                newLine = false;
+                            }
+                        }
+                        if (newLine) {
+                            result.append('\n');
+                        }
+
+                        /* add indent if any tokens remain,
+                         * less setback if next token is
+                         * a label, case or default.
+                         */
+                        if (i + 1 < length) {
+                            int less = 0;
+                            int nextToken = source.charAt(i + 1);
+                            if (nextToken == Token.CASE || nextToken == Token.DEFAULT) {
+                                less = indentGap - caseGap;
+                            } else if (nextToken == Token.RC) {
+                                less = indentGap;
+                            }
+
+                            /* elaborate check against label... skip past a
+                             * following inlined NAME and look for a COLON.
+                             */
+                            else if (nextToken == Token.NAME) {
+                                int afterName = getSourceStringEnd(source, i + 2);
+                                if (source.charAt(afterName) == Token.COLON) less = indentGap;
+                            }
 
-                    /* elaborate check against label... skip past a
-                     * following inlined NAME and look for a COLON.
-                     */
-                    else if (nextToken == Token.NAME) {
-                        int afterName = getSourceStringEnd(source, i + 2);
-                        if (source.charAt(afterName) == Token.COLON)
-                            less = indentGap;
+                            for (; less < indent; less++) result.append(' ');
+                        }
+                        break;
                     }
+                case Token.DOT:
+                    result.append('.');
+                    break;
+
+                case Token.NEW:
+                    result.append("new ");
+                    break;
+
+                case Token.DELPROP:
+                    result.append("delete ");
+                    break;
+
+                case Token.IF:
+                    result.append("if ");
+                    break;
+
+                case Token.ELSE:
+                    result.append("else ");
+                    break;
+
+                case Token.FOR:
+                    result.append("for ");
+                    break;
+
+                case Token.IN:
+                    result.append(" in ");
+                    break;
+
+                case Token.WITH:
+                    result.append("with ");
+                    break;
+
+                case Token.WHILE:
+                    result.append("while ");
+                    break;
+
+                case Token.DO:
+                    result.append("do ");
+                    break;
+
+                case Token.TRY:
+                    result.append("try ");
+                    break;
+
+                case Token.CATCH:
+                    result.append("catch ");
+                    break;
+
+                case Token.FINALLY:
+                    result.append("finally ");
+                    break;
+
+                case Token.THROW:
+                    result.append("throw ");
+                    break;
 
-                    for (; less < indent; less++)
+                case Token.SWITCH:
+                    result.append("switch ");
+                    break;
+
+                case Token.BREAK:
+                    result.append("break");
+                    if (Token.NAME == getNext(source, length, i)) result.append(' ');
+                    break;
+
+                case Token.CONTINUE:
+                    result.append("continue");
+                    if (Token.NAME == getNext(source, length, i)) result.append(' ');
+                    break;
+
+                case Token.CASE:
+                    result.append("case ");
+                    break;
+
+                case Token.DEFAULT:
+                    result.append("default");
+                    break;
+
+                case Token.RETURN:
+                    result.append("return");
+                    if (Token.SEMI != getNext(source, length, i)) result.append(' ');
+                    break;
+
+                case Token.VAR:
+                    result.append("var ");
+                    break;
+
+                case Token.LET:
+                    result.append("let ");
+                    break;
+
+                case Token.SEMI:
+                    result.append(';');
+                    if (Token.EOL != getNext(source, length, i)) {
+                        // separators in FOR
                         result.append(' ');
-                }
-                break;
-            }
-            case Token.DOT:
-                result.append('.');
-                break;
-
-            case Token.NEW:
-                result.append("new ");
-                break;
-
-            case Token.DELPROP:
-                result.append("delete ");
-                break;
-
-            case Token.IF:
-                result.append("if ");
-                break;
-
-            case Token.ELSE:
-                result.append("else ");
-                break;
-
-            case Token.FOR:
-                result.append("for ");
-                break;
-
-            case Token.IN:
-                result.append(" in ");
-                break;
-
-            case Token.WITH:
-                result.append("with ");
-                break;
-
-            case Token.WHILE:
-                result.append("while ");
-                break;
-
-            case Token.DO:
-                result.append("do ");
-                break;
-
-            case Token.TRY:
-                result.append("try ");
-                break;
-
-            case Token.CATCH:
-                result.append("catch ");
-                break;
-
-            case Token.FINALLY:
-                result.append("finally ");
-                break;
-
-            case Token.THROW:
-                result.append("throw ");
-                break;
-
-            case Token.SWITCH:
-                result.append("switch ");
-                break;
-
-            case Token.BREAK:
-                result.append("break");
-                if (Token.NAME == getNext(source, length, i))
-                    result.append(' ');
-                break;
-
-            case Token.CONTINUE:
-                result.append("continue");
-                if (Token.NAME == getNext(source, length, i))
-                    result.append(' ');
-                break;
-
-            case Token.CASE:
-                result.append("case ");
-                break;
-
-            case Token.DEFAULT:
-                result.append("default");
-                break;
-
-            case Token.RETURN:
-                result.append("return");
-                if (Token.SEMI != getNext(source, length, i))
-                    result.append(' ');
-                break;
-
-            case Token.VAR:
-                result.append("var ");
-                break;
-
-            case Token.LET:
-              result.append("let ");
-              break;
-
-            case Token.SEMI:
-                result.append(';');
-                if (Token.EOL != getNext(source, length, i)) {
-                    // separators in FOR
-                    result.append(' ');
-                }
-                break;
+                    }
+                    break;
+
+                case Token.ASSIGN:
+                    result.append(" = ");
+                    break;
+
+                case Token.ASSIGN_ADD:
+                    result.append(" += ");
+                    break;
+
+                case Token.ASSIGN_SUB:
+                    result.append(" -= ");
+                    break;
+
+                case Token.ASSIGN_MUL:
+                    result.append(" *= ");
+                    break;
+
+                case Token.ASSIGN_DIV:
+                    result.append(" /= ");
+                    break;
+
+                case Token.ASSIGN_MOD:
+                    result.append(" %= ");
+                    break;
+
+                case Token.ASSIGN_BITOR:
+                    result.append(" |= ");
+                    break;
+
+                case Token.ASSIGN_BITXOR:
+                    result.append(" ^= ");
+                    break;
+
+                case Token.ASSIGN_BITAND:
+                    result.append(" &= ");
+                    break;
+
+                case Token.ASSIGN_LSH:
+                    result.append(" <<= ");
+                    break;
+
+                case Token.ASSIGN_RSH:
+                    result.append(" >>= ");
+                    break;
+
+                case Token.ASSIGN_URSH:
+                    result.append(" >>>= ");
+                    break;
+
+                case Token.HOOK:
+                    result.append(" ? ");
+                    break;
+
+                case Token.OBJECTLIT:
+                    // pun OBJECTLIT to mean colon in objlit property
+                    // initialization.
+                    // This needs to be distinct from COLON in the general case
+                    // to distinguish from the colon in a ternary... which needs
+                    // different spacing.
+                    result.append(": ");
+                    break;
+
+                case Token.COLON:
+                    if (Token.EOL == getNext(source, length, i))
+                        // it's the end of a label
+                        result.append(':');
+                    else
+                        // it's the middle part of a ternary
+                        result.append(" : ");
+                    break;
+
+                case Token.OR:
+                    result.append(" || ");
+                    break;
+
+                case Token.AND:
+                    result.append(" && ");
+                    break;
+
+                case Token.BITOR:
+                    result.append(" | ");
+                    break;
+
+                case Token.BITXOR:
+                    result.append(" ^ ");
+                    break;
+
+                case Token.BITAND:
+                    result.append(" & ");
+                    break;
+
+                case Token.SHEQ:
+                    result.append(" === ");
+                    break;
+
+                case Token.SHNE:
+                    result.append(" !== ");
+                    break;
+
+                case Token.EQ:
+                    result.append(" == ");
+                    break;
+
+                case Token.NE:
+                    result.append(" != ");
+                    break;
+
+                case Token.LE:
+                    result.append(" <= ");
+                    break;
+
+                case Token.LT:
+                    result.append(" < ");
+                    break;
+
+                case Token.GE:
+                    result.append(" >= ");
+                    break;
+
+                case Token.GT:
+                    result.append(" > ");
+                    break;
+
+                case Token.INSTANCEOF:
+                    result.append(" instanceof ");
+                    break;
+
+                case Token.LSH:
+                    result.append(" << ");
+                    break;
+
+                case Token.RSH:
+                    result.append(" >> ");
+                    break;
+
+                case Token.URSH:
+                    result.append(" >>> ");
+                    break;
+
+                case Token.TYPEOF:
+                    result.append("typeof ");
+                    break;
+
+                case Token.VOID:
+                    result.append("void ");
+                    break;
+
+                case Token.CONST:
+                    result.append("const ");
+                    break;
+
+                case Token.YIELD:
+                    result.append("yield ");
+                    break;
+
+                case Token.YIELD_STAR:
+                    result.append("yield *");
+                    break;
+
+                case Token.NOT:
+                    result.append('!');
+                    break;
+
+                case Token.BITNOT:
+                    result.append('~');
+                    break;
+
+                case Token.POS:
+                    result.append('+');
+                    break;
+
+                case Token.NEG:
+                    result.append('-');
+                    break;
+
+                case Token.INC:
+                    result.append("++");
+                    break;
+
+                case Token.DEC:
+                    result.append("--");
+                    break;
+
+                case Token.ADD:
+                    result.append(" + ");
+                    break;
+
+                case Token.SUB:
+                    result.append(" - ");
+                    break;
+
+                case Token.MUL:
+                    result.append(" * ");
+                    break;
+
+                case Token.DIV:
+                    result.append(" / ");
+                    break;
+
+                case Token.MOD:
+                    result.append(" % ");
+                    break;
+
+                case Token.EXP:
+                    result.append(" ** ");
+                    break;
+
+                case Token.COLONCOLON:
+                    result.append("::");
+                    break;
+
+                case Token.DOTDOT:
+                    result.append("..");
+                    break;
+
+                case Token.DOTQUERY:
+                    result.append(".(");
+                    break;
+
+                case Token.XMLATTR:
+                    result.append('@');
+                    break;
+
+                case Token.DEBUGGER:
+                    result.append("debugger;\n");
+                    break;
+
+                case Token.ARROW:
+                    result.append(" => ");
+                    break;
+
+                case Token.TEMPLATE_LITERAL:
+                    result.append("`");
+                    break;
+
+                case Token.TEMPLATE_LITERAL_SUBST:
+                    result.append("${");
+                    break;
 
-            case Token.ASSIGN:
-                result.append(" = ");
-                break;
-
-            case Token.ASSIGN_ADD:
-                result.append(" += ");
-                break;
-
-            case Token.ASSIGN_SUB:
-                result.append(" -= ");
-                break;
-
-            case Token.ASSIGN_MUL:
-                result.append(" *= ");
-                break;
-
-            case Token.ASSIGN_DIV:
-                result.append(" /= ");
-                break;
-
-            case Token.ASSIGN_MOD:
-                result.append(" %= ");
-                break;
-
-            case Token.ASSIGN_BITOR:
-                result.append(" |= ");
-                break;
-
-            case Token.ASSIGN_BITXOR:
-                result.append(" ^= ");
-                break;
-
-            case Token.ASSIGN_BITAND:
-                result.append(" &= ");
-                break;
-
-            case Token.ASSIGN_LSH:
-                result.append(" <<= ");
-                break;
-
-            case Token.ASSIGN_RSH:
-                result.append(" >>= ");
-                break;
-
-            case Token.ASSIGN_URSH:
-                result.append(" >>>= ");
-                break;
-
-            case Token.HOOK:
-                result.append(" ? ");
-                break;
-
-            case Token.OBJECTLIT:
-                // pun OBJECTLIT to mean colon in objlit property
-                // initialization.
-                // This needs to be distinct from COLON in the general case
-                // to distinguish from the colon in a ternary... which needs
-                // different spacing.
-                result.append(": ");
-                break;
-
-            case Token.COLON:
-                if (Token.EOL == getNext(source, length, i))
-                    // it's the end of a label
-                    result.append(':');
-                else
-                    // it's the middle part of a ternary
-                    result.append(" : ");
-                break;
-
-            case Token.OR:
-                result.append(" || ");
-                break;
-
-            case Token.AND:
-                result.append(" && ");
-                break;
-
-            case Token.BITOR:
-                result.append(" | ");
-                break;
-
-            case Token.BITXOR:
-                result.append(" ^ ");
-                break;
-
-            case Token.BITAND:
-                result.append(" & ");
-                break;
-
-            case Token.SHEQ:
-                result.append(" === ");
-                break;
-
-            case Token.SHNE:
-                result.append(" !== ");
-                break;
-
-            case Token.EQ:
-                result.append(" == ");
-                break;
-
-            case Token.NE:
-                result.append(" != ");
-                break;
-
-            case Token.LE:
-                result.append(" <= ");
-                break;
-
-            case Token.LT:
-                result.append(" < ");
-                break;
-
-            case Token.GE:
-                result.append(" >= ");
-                break;
-
-            case Token.GT:
-                result.append(" > ");
-                break;
-
-            case Token.INSTANCEOF:
-                result.append(" instanceof ");
-                break;
-
-            case Token.LSH:
-                result.append(" << ");
-                break;
-
-            case Token.RSH:
-                result.append(" >> ");
-                break;
-
-            case Token.URSH:
-                result.append(" >>> ");
-                break;
-
-            case Token.TYPEOF:
-                result.append("typeof ");
-                break;
-
-            case Token.VOID:
-                result.append("void ");
-                break;
-
-            case Token.CONST:
-                result.append("const ");
-                break;
-
-            case Token.YIELD:
-                result.append("yield ");
-                break;
-
-            case Token.NOT:
-                result.append('!');
-                break;
-
-            case Token.BITNOT:
-                result.append('~');
-                break;
-
-            case Token.POS:
-                result.append('+');
-                break;
-
-            case Token.NEG:
-                result.append('-');
-                break;
-
-            case Token.INC:
-                result.append("++");
-                break;
-
-            case Token.DEC:
-                result.append("--");
-                break;
-
-            case Token.ADD:
-                result.append(" + ");
-                break;
-
-            case Token.SUB:
-                result.append(" - ");
-                break;
-
-            case Token.MUL:
-                result.append(" * ");
-                break;
-
-            case Token.DIV:
-                result.append(" / ");
-                break;
-
-            case Token.MOD:
-                result.append(" % ");
-                break;
-
-            case Token.COLONCOLON:
-                result.append("::");
-                break;
-
-            case Token.DOTDOT:
-                result.append("..");
-                break;
-
-            case Token.DOTQUERY:
-                result.append(".(");
-                break;
-
-            case Token.XMLATTR:
-                result.append('@');
-                break;
-
-            case Token.DEBUGGER:
-                result.append("debugger;\n");
-                break;
-
-            case Token.ARROW:
-                result.append(" => ");
-                break;
-
-            default:
-                // If we don't know how to decompile it, raise an exception.
-                throw new RuntimeException("Token: " +
-                                               Token.name(source.charAt(i)));
+                case Token.TEMPLATE_CHARS:
+                    i = printSourceString(source, i + 1, false, result);
+                    continue;
+
+                default:
+                    // If we don't know how to decompile it, raise an exception.
+                    throw new RuntimeException("Token: " + Token.name(source.charAt(i)));
             }
             ++i;
         }
 
         if (!toSource) {
             // add that trailing newline if it's an outermost function.
-            if (!justFunctionBody)
-                result.append('\n');
+            if (!justFunctionBody) result.append('\n');
         } else {
             if (topFunctionType == FunctionNode.FUNCTION_EXPRESSION) {
                 result.append(')');
@@ -819,20 +801,16 @@
         return result.toString();
     }
 
-    private static int getNext(String source, int length, int i)
-    {
+    private static int getNext(String source, int length, int i) {
         return (i + 1 < length) ? source.charAt(i + 1) : Token.EOF;
     }
 
-    private static int getSourceStringEnd(String source, int offset)
-    {
+    private static int getSourceStringEnd(String source, int offset) {
         return printSourceString(source, offset, false, null);
     }
 
-    private static int printSourceString(String source, int offset,
-                                         boolean asQuotedString,
-                                         StringBuilder sb)
-    {
+    private static int printSourceString(
+            String source, int offset, boolean asQuotedString, StringBuilder sb) {
         int length = source.charAt(offset);
         ++offset;
         if ((0x8000 & length) != 0) {
@@ -852,9 +830,7 @@
         return offset + length;
     }
 
-    private static int printSourceNumber(String source, int offset,
-                                         StringBuilder sb)
-    {
+    private static int printSourceNumber(String source, int offset, StringBuilder sb) {
         double number = 0.0;
         char type = source.charAt(offset);
         ++offset;
@@ -867,9 +843,9 @@
         } else if (type == 'J' || type == 'D') {
             if (sb != null) {
                 long lbits;
-                lbits = (long)source.charAt(offset) << 48;
-                lbits |= (long)source.charAt(offset + 1) << 32;
-                lbits |= (long)source.charAt(offset + 2) << 16;
+                lbits = (long) source.charAt(offset) << 48;
+                lbits |= (long) source.charAt(offset + 1) << 32;
+                lbits |= (long) source.charAt(offset + 2) << 16;
                 lbits |= source.charAt(offset + 3);
                 if (type == 'J') {
                     number = lbits;
@@ -888,13 +864,30 @@
         return offset;
     }
 
+    /**
+     * @see #printSourceString(String source, int offset, boolean asQuotedString, StringBuilder sb)
+     */
+    private static int printSourceBigInt(String source, int offset, StringBuilder sb) {
+        int length = source.charAt(offset);
+        ++offset;
+        if ((0x8000 & length) != 0) {
+            length = ((0x7FFF & length) << 16) | source.charAt(offset);
+            ++offset;
+        }
+        if (sb != null) {
+            String str = source.substring(offset, offset + length);
+            sb.append(str);
+            sb.append('n');
+        }
+        return offset + length;
+    }
+
     private char[] sourceBuffer = new char[128];
 
-// Per script/function source buffer top: parent source does not include a
-// nested functions source and uses function index as a reference instead.
+    // Per script/function source buffer top: parent source does not include a
+    // nested functions source and uses function index as a reference instead.
     private int sourceTop;
 
-// whether to do a debug print of the source information, when decompiling.
+    // whether to do a debug print of the source information, when decompiling.
     private static final boolean printSource = false;
-
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/DefaultErrorReporter.java rhino-1.7.14/src/org/mozilla/javascript/DefaultErrorReporter.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/DefaultErrorReporter.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/DefaultErrorReporter.java	2022-01-06 22:57:21.000000000 +0100
@@ -28,6 +28,7 @@
         return r;
     }
 
+    @Override
     public void warning(String message, String sourceURI, int line,
                         String lineText, int lineOffset)
     {
@@ -39,6 +40,7 @@
         }
     }
 
+    @Override
     public void error(String message, String sourceURI, int line,
                       String lineText, int lineOffset)
     {
@@ -66,16 +68,14 @@
         }
     }
 
+    @Override
     public EvaluatorException runtimeError(String message, String sourceURI,
                                            int line, String lineText,
                                            int lineOffset)
     {
         if (chainedReporter != null) {
-            return chainedReporter.runtimeError(
-                message, sourceURI, line, lineText, lineOffset);
-        } else {
-            return new EvaluatorException(
-                message, sourceURI, line, lineText, lineOffset);
+            return chainedReporter.runtimeError(message, sourceURI, line, lineText, lineOffset);
         }
+        return new EvaluatorException(message, sourceURI, line, lineText, lineOffset);
     }
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/DefiningClassLoader.java rhino-1.7.14/src/org/mozilla/javascript/DefiningClassLoader.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/DefiningClassLoader.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/DefiningClassLoader.java	2022-01-06 22:57:21.000000000 +0100
@@ -20,6 +20,7 @@
         this.parentLoader = parentLoader;
     }
 
+    @Override
     public Class<?> defineClass(String name, byte[] data) {
         // Use our own protection domain for the generated classes.
         // TODO: we might want to use a separate protection domain for classes
@@ -28,6 +29,7 @@
                 SecurityUtilities.getProtectionDomain(getClass()));
     }
 
+    @Override
     public void linkClass(Class<?> cl) {
         resolveClass(cl);
     }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/Delegator.java rhino-1.7.14/src/org/mozilla/javascript/Delegator.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/Delegator.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/Delegator.java	2022-01-06 22:57:21.000000000 +0100
@@ -23,7 +23,8 @@
  * @author Matthias Radestock
  */
 
-public class Delegator implements Function {
+public class Delegator
+    implements Function, SymbolScriptable {
 
     protected Scriptable obj = null;
 
@@ -72,6 +73,7 @@
     public Scriptable getDelegee() {
         return obj;
     }
+
     /**
      * Set the delegee.
      *
@@ -81,90 +83,156 @@
     public void setDelegee(Scriptable obj) {
         this.obj = obj;
     }
+
     /**
      * @see org.mozilla.javascript.Scriptable#getClassName
      */
+    @Override
     public String getClassName() {
-        return obj.getClassName();
+        return getDelegee().getClassName();
     }
+
     /**
      * @see org.mozilla.javascript.Scriptable#get(String, Scriptable)
      */
+    @Override
     public Object get(String name, Scriptable start) {
-        return obj.get(name,start);
+        return getDelegee().get(name,start);
+    }
+
+    @Override
+    public Object get(Symbol key, Scriptable start) {
+        final Scriptable delegee = getDelegee();
+        if (delegee instanceof SymbolScriptable) {
+            return ((SymbolScriptable) delegee).get(key, start);
+        }
+        return Scriptable.NOT_FOUND;
     }
+
     /**
      * @see org.mozilla.javascript.Scriptable#get(int, Scriptable)
      */
+    @Override
     public Object get(int index, Scriptable start) {
-        return obj.get(index,start);
-        }
+        return getDelegee().get(index,start);
+    }
+
     /**
      * @see org.mozilla.javascript.Scriptable#has(String, Scriptable)
      */
+    @Override
     public boolean has(String name, Scriptable start) {
-        return obj.has(name,start);
+        return getDelegee().has(name,start);
+    }
+
+    @Override
+    public boolean has(Symbol key, Scriptable start) {
+        final Scriptable delegee = getDelegee();
+        if (delegee instanceof SymbolScriptable) {
+            return ((SymbolScriptable) delegee).has(key, start);
         }
+        return false;
+    }
+
     /**
      * @see org.mozilla.javascript.Scriptable#has(int, Scriptable)
      */
+    @Override
     public boolean has(int index, Scriptable start) {
-        return obj.has(index,start);
-        }
+        return getDelegee().has(index,start);
+    }
+
     /**
      * @see org.mozilla.javascript.Scriptable#put(String, Scriptable, Object)
      */
+    @Override
     public void put(String name, Scriptable start, Object value) {
-        obj.put(name,start,value);
+        getDelegee().put(name,start,value);
     }
+
+    /**
+     * @see org.mozilla.javascript.SymbolScriptable#put(Symbol, Scriptable, Object)
+     */
+    @Override
+    public void put(Symbol symbol, Scriptable start, Object value) {
+        final Scriptable delegee = getDelegee();
+        if (delegee instanceof SymbolScriptable) {
+            ((SymbolScriptable) delegee).put(symbol, start, value);
+        }
+    }
+
     /**
      * @see org.mozilla.javascript.Scriptable#put(int, Scriptable, Object)
      */
+    @Override
     public void put(int index, Scriptable start, Object value) {
-        obj.put(index,start,value);
+        getDelegee().put(index,start,value);
     }
+
     /**
      * @see org.mozilla.javascript.Scriptable#delete(String)
      */
+    @Override
     public void delete(String name) {
-        obj.delete(name);
+        getDelegee().delete(name);
     }
+
+    @Override
+    public void delete(Symbol key) {
+        final Scriptable delegee = getDelegee();
+        if (delegee instanceof SymbolScriptable) {
+            ((SymbolScriptable) delegee).delete(key);
+        }
+    }
+
     /**
      * @see org.mozilla.javascript.Scriptable#delete(int)
      */
+    @Override
     public void delete(int index) {
-        obj.delete(index);
+        getDelegee().delete(index);
     }
+
     /**
      * @see org.mozilla.javascript.Scriptable#getPrototype
      */
+    @Override
     public Scriptable getPrototype() {
-        return obj.getPrototype();
+        return getDelegee().getPrototype();
     }
+
     /**
      * @see org.mozilla.javascript.Scriptable#setPrototype
      */
+    @Override
     public void setPrototype(Scriptable prototype) {
-        obj.setPrototype(prototype);
+        getDelegee().setPrototype(prototype);
     }
+
     /**
      * @see org.mozilla.javascript.Scriptable#getParentScope
      */
+    @Override
     public Scriptable getParentScope() {
-        return obj.getParentScope();
+        return getDelegee().getParentScope();
     }
+
     /**
      * @see org.mozilla.javascript.Scriptable#setParentScope
      */
+    @Override
     public void setParentScope(Scriptable parent) {
-        obj.setParentScope(parent);
+        getDelegee().setParentScope(parent);
     }
+
     /**
      * @see org.mozilla.javascript.Scriptable#getIds
      */
+    @Override
     public Object[] getIds() {
-        return obj.getIds();
+        return getDelegee().getIds();
     }
+
     /**
      * Note that this method does not get forwarded to the delegee if
      * the <code>hint</code> parameter is null,
@@ -177,25 +245,30 @@
      *
      * @see org.mozilla.javascript.Scriptable#getDefaultValue
      */
+    @Override
     public Object getDefaultValue(Class<?> hint) {
         return (hint == null ||
                 hint == ScriptRuntime.ScriptableClass ||
                 hint == ScriptRuntime.FunctionClass) ?
-            this : obj.getDefaultValue(hint);
+            this : getDelegee().getDefaultValue(hint);
     }
+
     /**
      * @see org.mozilla.javascript.Scriptable#hasInstance
      */
+    @Override
     public boolean hasInstance(Scriptable instance) {
-        return obj.hasInstance(instance);
+        return getDelegee().hasInstance(instance);
     }
+
     /**
      * @see org.mozilla.javascript.Function#call
      */
+    @Override
     public Object call(Context cx, Scriptable scope, Scriptable thisObj,
                        Object[] args)
     {
-        return ((Function)obj).call(cx,scope,thisObj,args);
+        return ((Function)getDelegee()).call(cx,scope,thisObj,args);
     }
 
     /**
@@ -213,11 +286,12 @@
      *
      * @see Function#construct(Context, Scriptable, Object[])
      */
+    @Override
     public Scriptable construct(Context cx, Scriptable scope, Object[] args)
     {
-        if (obj == null) {
-            //this little trick allows us to declare prototype objects for
-            //Delegators
+        Scriptable myDelegee = getDelegee();
+        if (myDelegee == null) {
+            //this little trick allows us to declare prototype objects for Delegators
             Delegator n = newInstance();
             Scriptable delegee;
             if (args.length == 0) {
@@ -228,8 +302,6 @@
             n.setDelegee(delegee);
             return n;
         }
-        else {
-            return ((Function)obj).construct(cx,scope,args);
-        }
+        return ((Function)myDelegee).construct(cx, scope, args);
     }
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/DToA.java rhino-1.7.14/src/org/mozilla/javascript/DToA.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/DToA.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/DToA.java	2022-01-06 22:57:21.000000000 +0100
@@ -260,101 +260,99 @@
         if (d == dfloor) {
             // No fraction part
             return intDigits;
-        } else {
-            /* We have a fraction. */
+        }
+        /* We have a fraction. */
+
+        StringBuilder buffer;       /* The output string */
+        int digit;
+        double df;           /* The fractional part of d */
+        BigInteger b;
+
+        buffer = new StringBuilder();
+        buffer.append(intDigits).append('.');
+        df = d - dfloor;
 
-            StringBuilder buffer;       /* The output string */
-            int digit;
-            double df;           /* The fractional part of d */
-            BigInteger b;
-
-            buffer = new StringBuilder();
-            buffer.append(intDigits).append('.');
-            df = d - dfloor;
-
-            long dBits = Double.doubleToLongBits(d);
-            int word0 = (int)(dBits >> 32);
-            int word1 = (int)(dBits);
+        long dBits = Double.doubleToLongBits(d);
+        int word0 = (int)(dBits >> 32);
+        int word1 = (int)(dBits);
 
-            int[] e = new int[1];
-            int[] bbits = new int[1];
+        int[] e = new int[1];
+        int[] bbits = new int[1];
 
-            b = d2b(df, e, bbits);
+        b = d2b(df, e, bbits);
 //            JS_ASSERT(e < 0);
-            /* At this point df = b * 2^e.  e must be less than zero because 0 < df < 1. */
+        /* At this point df = b * 2^e.  e must be less than zero because 0 < df < 1. */
 
-            int s2 = -(word0 >>> Exp_shift1 & Exp_mask >> Exp_shift1);
-            if (s2 == 0)
-                s2 = -1;
-            s2 += Bias + P;
-            /* 1/2^s2 = (nextDouble(d) - d)/2 */
+        int s2 = -(word0 >>> Exp_shift1 & Exp_mask >> Exp_shift1);
+        if (s2 == 0)
+            s2 = -1;
+        s2 += Bias + P;
+        /* 1/2^s2 = (nextDouble(d) - d)/2 */
 //            JS_ASSERT(-s2 < e);
-            BigInteger mlo = BigInteger.valueOf(1);
-            BigInteger mhi = mlo;
-            if ((word1 == 0) && ((word0 & Bndry_mask) == 0)
-                && ((word0 & (Exp_mask & Exp_mask << 1)) != 0)) {
-                /* The special case.  Here we want to be within a quarter of the last input
-                   significant digit instead of one half of it when the output string's value is less than d.  */
-                s2 += Log2P;
-                mhi = BigInteger.valueOf(1<<Log2P);
+        BigInteger mlo = BigInteger.valueOf(1);
+        BigInteger mhi = mlo;
+        if ((word1 == 0) && ((word0 & Bndry_mask) == 0)
+            && ((word0 & (Exp_mask & Exp_mask << 1)) != 0)) {
+            /* The special case.  Here we want to be within a quarter of the last input
+               significant digit instead of one half of it when the output string's value is less than d.  */
+            s2 += Log2P;
+            mhi = BigInteger.valueOf(1<<Log2P);
+        }
+
+        b = b.shiftLeft(e[0] + s2);
+        BigInteger s = BigInteger.valueOf(1);
+        s = s.shiftLeft(s2);
+        /* At this point we have the following:
+         *   s = 2^s2;
+         *   1 > df = b/2^s2 > 0;
+         *   (d - prevDouble(d))/2 = mlo/2^s2;
+         *   (nextDouble(d) - d)/2 = mhi/2^s2. */
+        BigInteger bigBase = BigInteger.valueOf(base);
+
+        boolean done = false;
+        do {
+            b = b.multiply(bigBase);
+            BigInteger[] divResult = b.divideAndRemainder(s);
+            b = divResult[1];
+            digit = (char)(divResult[0].intValue());
+            if (mlo == mhi)
+                mlo = mhi = mlo.multiply(bigBase);
+            else {
+                mlo = mlo.multiply(bigBase);
+                mhi = mhi.multiply(bigBase);
             }
 
-            b = b.shiftLeft(e[0] + s2);
-            BigInteger s = BigInteger.valueOf(1);
-            s = s.shiftLeft(s2);
-            /* At this point we have the following:
-             *   s = 2^s2;
-             *   1 > df = b/2^s2 > 0;
-             *   (d - prevDouble(d))/2 = mlo/2^s2;
-             *   (nextDouble(d) - d)/2 = mhi/2^s2. */
-            BigInteger bigBase = BigInteger.valueOf(base);
-
-            boolean done = false;
-            do {
-                b = b.multiply(bigBase);
-                BigInteger[] divResult = b.divideAndRemainder(s);
-                b = divResult[1];
-                digit = (char)(divResult[0].intValue());
-                if (mlo == mhi)
-                    mlo = mhi = mlo.multiply(bigBase);
-                else {
-                    mlo = mlo.multiply(bigBase);
-                    mhi = mhi.multiply(bigBase);
-                }
-
-                /* Do we yet have the shortest string that will round to d? */
-                int j = b.compareTo(mlo);
-                /* j is b/2^s2 compared with mlo/2^s2. */
-                BigInteger delta = s.subtract(mhi);
-                int j1 = (delta.signum() <= 0) ? 1 : b.compareTo(delta);
-                /* j1 is b/2^s2 compared with 1 - mhi/2^s2. */
-                if (j1 == 0 && ((word1 & 1) == 0)) {
-                    if (j > 0)
-                        digit++;
-                    done = true;
-                } else
-                if (j < 0 || (j == 0 && ((word1 & 1) == 0))) {
-                    if (j1 > 0) {
-                        /* Either dig or dig+1 would work here as the least significant digit.
-                           Use whichever would produce an output value closer to d. */
-                        b = b.shiftLeft(1);
-                        j1 = b.compareTo(s);
-                        if (j1 > 0) /* The even test (|| (j1 == 0 && (digit & 1))) is not here because it messes up odd base output
-                                     * such as 3.5 in base 3.  */
-                            digit++;
-                    }
-                    done = true;
-                } else if (j1 > 0) {
+            /* Do we yet have the shortest string that will round to d? */
+            int j = b.compareTo(mlo);
+            /* j is b/2^s2 compared with mlo/2^s2. */
+            BigInteger delta = s.subtract(mhi);
+            int j1 = (delta.signum() <= 0) ? 1 : b.compareTo(delta);
+            /* j1 is b/2^s2 compared with 1 - mhi/2^s2. */
+            if (j1 == 0 && ((word1 & 1) == 0)) {
+                if (j > 0)
                     digit++;
-                    done = true;
+                done = true;
+            } else
+            if (j < 0 || (j == 0 && ((word1 & 1) == 0))) {
+                if (j1 > 0) {
+                    /* Either dig or dig+1 would work here as the least significant digit.
+                       Use whichever would produce an output value closer to d. */
+                    b = b.shiftLeft(1);
+                    j1 = b.compareTo(s);
+                    if (j1 > 0) /* The even test (|| (j1 == 0 && (digit & 1))) is not here because it messes up odd base output
+                                 * such as 3.5 in base 3.  */
+                        digit++;
                 }
+                done = true;
+            } else if (j1 > 0) {
+                digit++;
+                done = true;
+            }
 //                JS_ASSERT(digit < (uint32)base);
-                buffer.append(BASEDIGIT(digit));
-            } while (!done);
-
-            return buffer.toString();
-        }
+            buffer.append(BASEDIGIT(digit));
+        } while (!done);
 
+        return buffer.toString();
     }
 
     /* dtoa for IEEE arithmetic (dmg): convert double to ASCII string.
@@ -602,7 +600,7 @@
                 break;
             case 2:
                 leftright = false;
-                /* fallthru */
+                /* fall through */
             case 4:
                 if (ndigits <= 0)
                     ndigits = 1;
@@ -610,7 +608,7 @@
                 break;
             case 3:
                 leftright = false;
-                /* fallthru */
+                /* fall through */
             case 5:
                 i = ndigits + k + 1;
                 ilim = i;
@@ -1168,7 +1166,7 @@
                 case DTOSTR_EXPONENTIAL:
 //                    JS_ASSERT(precision > 0);
                     minNDigits = precision;
-                    /* fallthru */
+                    /* fall through */
                 case DTOSTR_STANDARD_EXPONENTIAL:
                     exponentialNotation = true;
                     break;
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/EcmaError.java rhino-1.7.14/src/org/mozilla/javascript/EcmaError.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/EcmaError.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/EcmaError.java	2022-01-06 22:57:21.000000000 +0100
@@ -14,7 +14,7 @@
  */
 public class EcmaError extends RhinoException
 {
-    static final long serialVersionUID = -6261226256957286699L;
+    private static final long serialVersionUID = -6261226256957286699L;
 
     private String errorName;
     private String errorMessage;
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/EmbeddedSlotMap.java rhino-1.7.14/src/org/mozilla/javascript/EmbeddedSlotMap.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/EmbeddedSlotMap.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/EmbeddedSlotMap.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,284 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+/*
+ * This class implements the SlotMap interface using an embedded hash table. This hash table
+ * has the minimum overhead needed to get the job done. In particular, it embeds the Slot
+ * directly into the hash table rather than creating an intermediate object, which seems
+ * to have a measurable performance benefit.
+ */
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+
+public class EmbeddedSlotMap implements SlotMap {
+
+    private Slot[] slots;
+
+    // gateways into the definition-order linked list of slots
+    private Slot firstAdded;
+    private Slot lastAdded;
+
+    private int count;
+
+    // initial slot array size, must be a power of 2
+    private static final int INITIAL_SLOT_SIZE = 4;
+
+    private static final class Iter implements Iterator<Slot> {
+        private Slot next;
+
+        Iter(Slot slot) {
+            next = slot;
+        }
+
+        @Override
+        public boolean hasNext() {
+            return next != null;
+        }
+
+        @Override
+        public Slot next() {
+            Slot ret = next;
+            if (ret == null) {
+                throw new NoSuchElementException();
+            }
+            next = next.orderedNext;
+            return ret;
+        }
+    }
+
+    public EmbeddedSlotMap() {}
+
+    @Override
+    public int size() {
+        return count;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return count == 0;
+    }
+
+    @Override
+    public Iterator<Slot> iterator() {
+        return new Iter(firstAdded);
+    }
+
+    /** Locate the slot with the given name or index. */
+    @Override
+    public Slot query(Object key, int index) {
+        if (slots == null) {
+            return null;
+        }
+
+        int indexOrHash = (key != null ? key.hashCode() : index);
+        int slotIndex = getSlotIndex(slots.length, indexOrHash);
+        for (Slot slot = slots[slotIndex]; slot != null; slot = slot.next) {
+            if (indexOrHash == slot.indexOrHash && Objects.equals(slot.name, key)) {
+                return slot;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Locate the slot with given name or index, and create a new one if necessary.
+     *
+     * @param key either a String or a Symbol object that identifies the property
+     * @param index index or 0 if slot holds property name.
+     */
+    @Override
+    public Slot modify(Object key, int index, int attributes) {
+        final int indexOrHash = (key != null ? key.hashCode() : index);
+        Slot slot;
+
+        if (slots != null) {
+            final int slotIndex = getSlotIndex(slots.length, indexOrHash);
+            for (slot = slots[slotIndex]; slot != null; slot = slot.next) {
+                if (indexOrHash == slot.indexOrHash && Objects.equals(slot.name, key)) {
+                    break;
+                }
+            }
+            if (slot != null) {
+                return slot;
+            }
+        }
+
+        // A new slot has to be inserted.
+        return createSlot(key, indexOrHash, attributes);
+    }
+
+    private Slot createSlot(Object key, int indexOrHash, int attributes) {
+        if (count == 0) {
+            // Always throw away old slots if any on empty insert.
+            slots = new Slot[INITIAL_SLOT_SIZE];
+        }
+
+        // Check if the table is not too full before inserting.
+        if (4 * (count + 1) > 3 * slots.length) {
+            // table size must be a power of 2 -- always grow by x2!
+            Slot[] newSlots = new Slot[slots.length * 2];
+            copyTable(slots, newSlots);
+            slots = newSlots;
+        }
+
+        Slot newSlot = new Slot(key, indexOrHash, attributes);
+        insertNewSlot(newSlot);
+        return newSlot;
+    }
+
+    @Override
+    public void replace(Slot oldSlot, Slot newSlot) {
+        final int insertPos = getSlotIndex(slots.length, oldSlot.indexOrHash);
+        Slot prev = slots[insertPos];
+        Slot tmpSlot = prev;
+        // Find original slot and previous one in hash table
+        while (tmpSlot != null) {
+            if (tmpSlot == oldSlot) {
+                break;
+            }
+            prev = tmpSlot;
+            tmpSlot = tmpSlot.next;
+        }
+
+        // It's an error to call this when the slot isn't already there
+        assert (tmpSlot == oldSlot);
+
+        // add new slot to hash table
+        if (prev == oldSlot) {
+            slots[insertPos] = newSlot;
+        } else {
+            prev.next = newSlot;
+        }
+        newSlot.next = oldSlot.next;
+
+        // Replace new slot in linked list, keeping same order
+        if (oldSlot == firstAdded) {
+            firstAdded = newSlot;
+        } else {
+            Slot ps = firstAdded;
+            while ((ps != null) && (ps.orderedNext != oldSlot)) {
+                ps = ps.orderedNext;
+            }
+            if (ps != null) {
+                ps.orderedNext = newSlot;
+            }
+        }
+        newSlot.orderedNext = oldSlot.orderedNext;
+        if (oldSlot == lastAdded) {
+            lastAdded = newSlot;
+        }
+    }
+
+    @Override
+    public void add(Slot newSlot) {
+        if (slots == null) {
+            slots = new Slot[INITIAL_SLOT_SIZE];
+        }
+        insertNewSlot(newSlot);
+    }
+
+    private void insertNewSlot(Slot newSlot) {
+        ++count;
+        // add new slot to linked list
+        if (lastAdded != null) {
+            lastAdded.orderedNext = newSlot;
+        }
+        if (firstAdded == null) {
+            firstAdded = newSlot;
+        }
+        lastAdded = newSlot;
+        // add new slot to hash table, return it
+        addKnownAbsentSlot(slots, newSlot);
+    }
+
+    @Override
+    public void remove(Object key, int index) {
+        int indexOrHash = (key != null ? key.hashCode() : index);
+
+        if (count != 0) {
+            final int slotIndex = getSlotIndex(slots.length, indexOrHash);
+            Slot prev = slots[slotIndex];
+            Slot slot = prev;
+            while (slot != null) {
+                if (slot.indexOrHash == indexOrHash && Objects.equals(slot.name, key)) {
+                    break;
+                }
+                prev = slot;
+                slot = slot.next;
+            }
+            if (slot != null) {
+                // non-configurable
+                if ((slot.getAttributes() & ScriptableObject.PERMANENT) != 0) {
+                    Context cx = Context.getContext();
+                    if (cx.isStrictMode()) {
+                        throw ScriptRuntime.typeErrorById(
+                                "msg.delete.property.with.configurable.false", key);
+                    }
+                    return;
+                }
+                count--;
+                // remove slot from hash table
+                if (prev == slot) {
+                    slots[slotIndex] = slot.next;
+                } else {
+                    prev.next = slot.next;
+                }
+
+                // remove from ordered list. Previously this was done lazily in
+                // getIds() but delete is an infrequent operation so O(n)
+                // should be ok
+
+                // ordered list always uses the actual slot
+                if (slot == firstAdded) {
+                    prev = null;
+                    firstAdded = slot.orderedNext;
+                } else {
+                    prev = firstAdded;
+                    while (prev.orderedNext != slot) {
+                        prev = prev.orderedNext;
+                    }
+                    prev.orderedNext = slot.orderedNext;
+                }
+                if (slot == lastAdded) {
+                    lastAdded = prev;
+                }
+            }
+        }
+    }
+
+    private static void copyTable(Slot[] oldSlots, Slot[] newSlots) {
+        for (Slot slot : oldSlots) {
+            while (slot != null) {
+                Slot nextSlot = slot.next;
+                slot.next = null;
+                addKnownAbsentSlot(newSlots, slot);
+                slot = nextSlot;
+            }
+        }
+    }
+
+    /**
+     * Add slot with keys that are known to absent from the table. This is an optimization to use
+     * when inserting into empty table, after table growth or during deserialization.
+     */
+    private static void addKnownAbsentSlot(Slot[] addSlots, Slot slot) {
+        final int insertPos = getSlotIndex(addSlots.length, slot.indexOrHash);
+        Slot old = addSlots[insertPos];
+        addSlots[insertPos] = slot;
+        slot.next = old;
+    }
+
+    private static int getSlotIndex(int tableSize, int indexOrHash) {
+        // This is a Java trick to efficiently "mod" the hash code by the table size.
+        // It only works if the table size is a power of 2.
+        // The performance improvement is measurable.
+        return indexOrHash & (tableSize - 1);
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/engine/BindingsObject.java rhino-1.7.14/src/org/mozilla/javascript/engine/BindingsObject.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/engine/BindingsObject.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/engine/BindingsObject.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,59 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript.engine;
+
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+import javax.script.Bindings;
+
+/**
+ * This class makes the Bindings object into a Scriptable. That way, we can query and modify
+ * the contents of the Bindings on demand.
+ */
+public class BindingsObject
+  extends ScriptableObject {
+  private final Bindings bindings;
+
+  BindingsObject(Bindings bindings) {
+    if (bindings == null) {
+      throw new IllegalArgumentException("Bindings must not be null");
+    }
+    this.bindings = bindings;
+  }
+
+  @Override
+  public String getClassName() {
+    return "BindingsObject";
+  }
+
+  @Override
+  public Object get(String name, Scriptable start) {
+    if (!bindings.containsKey(name)) {
+      return Scriptable.NOT_FOUND;
+    }
+    return Context.jsToJava(bindings.get(name), Object.class);
+  }
+
+  @Override
+  public void put(String name, Scriptable start, Object value) {
+    bindings.put(name, Context.javaToJS(value, start));
+  }
+
+  @Override
+  public void delete(String name) {
+    bindings.remove(name);
+  }
+
+  @Override
+  public boolean has(String name, Scriptable start) {
+    return bindings.containsKey(name);
+  }
+
+  @Override
+  public Object[] getIds() {
+    return bindings.keySet().toArray();
+  }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/engine/Builtins.java rhino-1.7.14/src/org/mozilla/javascript/engine/Builtins.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/engine/Builtins.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/engine/Builtins.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,59 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript.engine;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import javax.script.ScriptContext;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Function;
+import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+
+/**
+ * <p>
+ * This class defines the following built-in functions for the RhinoScriptEngine.
+ * </p>
+ * <ul>
+ * <li>print(arg, arg, ...): Write each argument, concatenated to the ScriptEngine's
+ * "standard output" as a string.</li>
+ * </ul>
+ */
+public class Builtins {
+
+  static final Object BUILTIN_KEY = new Object();
+
+  private Writer stdout;
+
+  void register(Context cx, ScriptableObject scope, ScriptContext sc) {
+    if (sc.getWriter() == null) {
+      stdout = new OutputStreamWriter(System.out);
+    } else {
+      stdout = sc.getWriter();
+    }
+
+    scope.defineFunctionProperties(new String[]{"print"},
+        Builtins.class,
+        ScriptableObject.PERMANENT | ScriptableObject.DONTENUM);
+  }
+
+  public static void print(Context cx, Scriptable thisObj, Object[] args, Function f)
+      throws IOException {
+    Builtins self = getSelf(thisObj);
+    for (Object arg : args) {
+      self.stdout.write(ScriptRuntime.toString(arg));
+    }
+    self.stdout.write('\n');
+  }
+
+  private static Builtins getSelf(Scriptable scope) {
+    // Since this class is invoked as a set of anonymous functions, "this"
+    // in JavaScript does not point to "this" in Java. We set a key on the
+    // top-level scope to address this.
+    return (Builtins) ScriptableObject.getTopScopeValue(scope, BUILTIN_KEY);
+  }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/engine/RhinoCompiledScript.java rhino-1.7.14/src/org/mozilla/javascript/engine/RhinoCompiledScript.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/engine/RhinoCompiledScript.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/engine/RhinoCompiledScript.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,33 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript.engine;
+
+import javax.script.CompiledScript;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptException;
+import org.mozilla.javascript.Script;
+
+public class RhinoCompiledScript
+  extends CompiledScript {
+
+  private final RhinoScriptEngine engine;
+  private final Script script;
+
+  RhinoCompiledScript(RhinoScriptEngine engine, Script script) {
+    this.engine = engine;
+    this.script = script;
+  }
+
+  @Override
+  public Object eval(ScriptContext context) throws ScriptException {
+    return engine.eval(script, context);
+  }
+
+  @Override
+  public ScriptEngine getEngine() {
+    return engine;
+  }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/engine/RhinoInvocationHandler.java rhino-1.7.14/src/org/mozilla/javascript/engine/RhinoInvocationHandler.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/engine/RhinoInvocationHandler.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/engine/RhinoInvocationHandler.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,25 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript.engine;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+public class RhinoInvocationHandler
+    implements InvocationHandler {
+
+  private final Object thiz;
+  private final RhinoScriptEngine engine;
+
+  RhinoInvocationHandler(RhinoScriptEngine engine, Object thiz) {
+    this.engine = engine;
+    this.thiz = thiz;
+  }
+
+  @Override
+  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+    return engine.invokeMethodRaw(thiz, method.getName(), method.getReturnType(), args);
+  }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/engine/RhinoScriptEngineFactory.java rhino-1.7.14/src/org/mozilla/javascript/engine/RhinoScriptEngineFactory.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/engine/RhinoScriptEngineFactory.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/engine/RhinoScriptEngineFactory.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,137 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript.engine;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineFactory;
+import org.mozilla.javascript.Context;
+
+/**
+ * <p>
+ * This is an implementation of the standard Java "ScriptEngine" for Rhino. If the Rhino engine
+ * (typically in the form of the "rhino-engine" JAR) is in the classpath, then this script
+ * engine will be activated.
+ * </p>
+ * <p>
+ * See the list of constants in this class for the list of language names, file extensions, and
+ * MIME types that this engine supports. This list is essentially the same as the list supported
+ * in the Nashorn script engine that was included in Java 8.
+ * </p>
+ * <p>
+ * Since this engine and Nashorn support the same language and file extensions, then unless
+ * you are sure you are running in an environment that has Nashorn, the best way to get this
+ * engine is to call ScriptEngine.getEngineByName("rhino") to ask for Rhino directly.
+ * </p>
+ */
+public class RhinoScriptEngineFactory
+  implements ScriptEngineFactory {
+
+  public static final String NAME = "rhino";
+  private static final String LANGUAGE = "javascript";
+  private static final List<String> NAMES =
+      Arrays.asList("rhino", "Rhino", "javascript", "JavaScript");
+  private static final List<String> EXTENSIONS =
+      Collections.singletonList("js");
+  private static final List<String> MIME_TYPES =
+      Arrays.asList("application/javascript", "application/ecmascript",
+          "text/javascript", "text/ecmascript");
+  private static final String LANGUAGE_VERSION =
+      String.valueOf(RhinoScriptEngine.DEFAULT_LANGUAGE_VERSION);
+
+  @Override
+  public String getEngineName() {
+    return NAME;
+  }
+
+  @Override
+  public String getEngineVersion() {
+    try (Context cx = Context.enter()) {
+      String v = cx.getImplementationVersion();
+      return (v == null ? "unknown" : v);
+    }
+  }
+
+  @Override
+  public List<String> getExtensions() {
+    return EXTENSIONS;
+  }
+
+  @Override
+  public List<String> getMimeTypes() {
+    return MIME_TYPES;
+  }
+
+  @Override
+  public List<String> getNames() {
+    return NAMES;
+  }
+
+  @Override
+  public String getLanguageName() {
+    return LANGUAGE;
+  }
+
+  @Override
+  public String getLanguageVersion() {
+    return LANGUAGE_VERSION;
+  }
+
+  @Override
+  public Object getParameter(String key) {
+    switch (key) {
+      case ScriptEngine.ENGINE:
+        return getEngineName();
+      case ScriptEngine.ENGINE_VERSION:
+        return getEngineVersion();
+      case ScriptEngine.LANGUAGE:
+        return getLanguageName();
+      case ScriptEngine.LANGUAGE_VERSION:
+        return getLanguageVersion();
+      case ScriptEngine.NAME:
+        return NAME;
+      case "THREADING":
+        // Engines are explicitly not thread-safe
+        return null;
+      default:
+        return null;
+    }
+  }
+
+  @Override
+  public String getMethodCallSyntax(String obj, String m, String... args) {
+    StringBuilder sb = new StringBuilder();
+    sb.append(obj).append('.').append(m).append('(');
+    for (int i = 0; i < args.length; i++) {
+      if (i > 0) {
+        sb.append(',');
+      }
+      sb.append(args[i]);
+    }
+    sb.append(");");
+    return sb.toString();
+  }
+
+  @Override
+  public String getOutputStatement(String toDisplay) {
+    return "print('" + toDisplay + "');";
+  }
+
+  @Override
+  public String getProgram(String... statements) {
+    StringBuilder sb = new StringBuilder();
+    for (String stmt : statements) {
+      sb.append(stmt).append(";\n");
+    }
+    return sb.toString();
+  }
+
+  @Override
+  public ScriptEngine getScriptEngine() {
+    return new RhinoScriptEngine(this);
+  }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/engine/RhinoScriptEngine.java rhino-1.7.14/src/org/mozilla/javascript/engine/RhinoScriptEngine.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/engine/RhinoScriptEngine.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/engine/RhinoScriptEngine.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,341 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript.engine;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import javax.script.AbstractScriptEngine;
+import javax.script.Bindings;
+import javax.script.Compilable;
+import javax.script.CompiledScript;
+import javax.script.Invocable;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineFactory;
+import javax.script.ScriptException;
+import javax.script.SimpleBindings;
+import org.mozilla.javascript.Callable;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.ContextFactory;
+import org.mozilla.javascript.RhinoException;
+import org.mozilla.javascript.Script;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+
+/**
+ * <p>
+ * This is the implementation of the standard ScriptEngine interface for Rhino.
+ * </p>
+ * <p>
+ * An instance of the Rhino ScriptEngine is fully self-contained. Bindings at the GLOBAL_SCOPE may
+ * be set, but there is nothing special about them -- if both global and ENGINE_SCOPE bindings are
+ * set then the "engine" bindings override the global ones.
+ * </p>
+ * <p>
+ * The Rhino engine is not thread safe. Rhino does no synchronization of ScriptEngine instances and
+ * no synchronization of Bindings instances. It is up to the caller to ensure that the ScriptEngine
+ * and all its Bindings are used by a single thread at a time.
+ * </p>
+ * <p>
+ * The Rhino script engine includes some top-level built-in functions. See the Builtins class for
+ * more documentation.
+ * </p>
+ * <p>
+ * The engine supports a few configuration parameters that may be set at the "engine scope". Both
+ * are numbers that may be set to a String or Number object.
+ * </p>
+ * <ul>
+ * <li>javax.script.language_version: The version of the JavaScript language supported,
+ * which is an integer defined in the Context class. The default is the latest "ES6"
+ * version, defined as 200.</li>
+ * <li>org.mozilla.javascript.optimization_level: The level of optimization Rhino performs
+ * on the generated bytecode. Default is 9, which is the most. Set to -1 to use interpreted
+ * mode.</li>
+ * </ul>
+ */
+public class RhinoScriptEngine
+    extends AbstractScriptEngine
+    implements Compilable, Invocable {
+
+  /**
+   * Reserved key for the Rhino optimization level. Default is "9," for optimized and compiled code.
+   * Set this to "-1" to run Rhino in interpreted mode -- this is much much slower but the only
+   * option on platforms like Android that don't support class files.
+   */
+  public static final String OPTIMIZATION_LEVEL = "org.mozilla.javascript.optimization_level";
+
+  static final int DEFAULT_LANGUAGE_VERSION = Context.VERSION_ES6;
+  private static final int DEFAULT_OPT = 9;
+  private static final boolean DEFAULT_DEBUG = true;
+  private static final String DEFAULT_FILENAME = "eval";
+
+  private static final CtxFactory ctxFactory = new CtxFactory();
+
+  private final RhinoScriptEngineFactory factory;
+  private final Builtins builtins;
+  private ScriptableObject topLevelScope = null;
+
+  RhinoScriptEngine(RhinoScriptEngineFactory factory) {
+    this.factory = factory;
+    this.builtins = new Builtins();
+  }
+
+  private Scriptable initScope(Context cx, ScriptContext sc) throws ScriptException {
+    configureContext(cx);
+
+    if (topLevelScope == null) {
+      topLevelScope = cx.initStandardObjects();
+      // We need to stash this away so that the built in functions can find
+      // this engine's specific stuff that they need to work.
+      topLevelScope.associateValue(Builtins.BUILTIN_KEY, builtins);
+      builtins.register(cx, topLevelScope, sc);
+    }
+
+    Scriptable engineScope = new BindingsObject(
+        sc.getBindings(ScriptContext.ENGINE_SCOPE));
+    engineScope.setParentScope(null);
+    engineScope.setPrototype(topLevelScope);
+
+    if (sc.getBindings(ScriptContext.GLOBAL_SCOPE) != null) {
+      Scriptable globalScope = new BindingsObject(
+          sc.getBindings(ScriptContext.GLOBAL_SCOPE));
+      globalScope.setParentScope(null);
+      globalScope.setPrototype(topLevelScope);
+      engineScope.setPrototype(globalScope);
+    }
+
+    return engineScope;
+  }
+
+  @Override
+  public Object eval(String script, ScriptContext context) throws ScriptException {
+    try (Context cx = ctxFactory.enterContext()) {
+      Scriptable scope = initScope(cx, context);
+      Object ret = cx.evaluateString(scope, script, getFilename(), 0, null);
+      return Context.jsToJava(ret, Object.class);
+    } catch (RhinoException re) {
+      throw new ScriptException(re.getMessage(), re.sourceName(), re.lineNumber(),
+          re.columnNumber());
+    }
+  }
+
+  @Override
+  public Object eval(Reader reader, ScriptContext context) throws ScriptException {
+    try (Context cx = ctxFactory.enterContext()) {
+      Scriptable scope = initScope(cx, context);
+      Object ret = cx.evaluateReader(scope, reader, getFilename(), 0, null);
+      return Context.jsToJava(ret, Object.class);
+    } catch (RhinoException re) {
+      throw new ScriptException(re.getMessage(), re.sourceName(), re.lineNumber(),
+          re.columnNumber());
+    } catch (IOException ioe) {
+      throw new ScriptException(ioe);
+    }
+  }
+
+  @Override
+  public CompiledScript compile(String script) throws ScriptException {
+    try (Context cx = ctxFactory.enterContext()) {
+      configureContext(cx);
+      Script s =
+          cx.compileString(script, getFilename(), 1, null);
+      return new RhinoCompiledScript(this, s);
+    } catch (RhinoException re) {
+      throw new ScriptException(re.getMessage(), re.sourceName(), re.lineNumber(),
+          re.columnNumber());
+    }
+  }
+
+  @Override
+  public CompiledScript compile(Reader script) throws ScriptException {
+    try (Context cx = ctxFactory.enterContext()) {
+      configureContext(cx);
+      Script s =
+          cx.compileReader(script, getFilename(), 1, null);
+      return new RhinoCompiledScript(this, s);
+    } catch (RhinoException re) {
+      throw new ScriptException(re.getMessage(), re.sourceName(), re.lineNumber(),
+          re.columnNumber());
+    } catch (IOException ioe) {
+      throw new ScriptException(ioe);
+    }
+  }
+
+  Object eval(Script script, ScriptContext sc) throws ScriptException {
+    try (Context cx = ctxFactory.enterContext()) {
+      Scriptable scope = initScope(cx, sc);
+      Object ret = script.exec(cx, scope);
+      return Context.jsToJava(ret, Object.class);
+    } catch (RhinoException re) {
+      throw new ScriptException(re.getMessage(), re.sourceName(), re.lineNumber(),
+          re.columnNumber());
+    }
+  }
+
+  @Override
+  public Object invokeFunction(String name, Object... args)
+      throws ScriptException, NoSuchMethodException {
+    return invokeMethod(null, name, args);
+  }
+
+  @Override
+  public Object invokeMethod(Object thiz, String name, Object... args)
+      throws ScriptException, NoSuchMethodException {
+    return invokeMethodRaw(thiz, name, Object.class, args);
+  }
+
+  Object invokeMethodRaw(Object thiz, String name, Class<?> returnType, Object... args)
+      throws ScriptException, NoSuchMethodException {
+    try (Context cx = ctxFactory.enterContext()) {
+      Scriptable scope = initScope(cx, context);
+
+      Scriptable localThis;
+      if (thiz == null) {
+        localThis = scope;
+      } else {
+        localThis = Context.toObject(thiz, scope);
+      }
+
+      Object f = ScriptableObject.getProperty(localThis, name);
+      if (f == Scriptable.NOT_FOUND) {
+        throw new NoSuchMethodException(name);
+      }
+      if (!(f instanceof Callable)) {
+        throw new ScriptException("\"" + name + "\" is not a function");
+      }
+      Callable func = (Callable) f;
+
+      if (args != null) {
+        for (int i = 0; i < args.length; i++) {
+          args[i] = Context.javaToJS(args[i], scope);
+        }
+      }
+
+      Object ret = func.call(cx, scope, localThis, args);
+      if (returnType == Void.TYPE) {
+        return null;
+      }
+      return Context.jsToJava(ret, returnType);
+
+    } catch (RhinoException re) {
+      throw new ScriptException(re.getMessage(), re.sourceName(), re.lineNumber(),
+          re.columnNumber());
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public <T> T getInterface(Class<T> clasz) {
+    if ((clasz == null) || !clasz.isInterface()) {
+      throw new IllegalArgumentException("Not an interface");
+    }
+    try (Context cx = ctxFactory.enterContext()) {
+      Scriptable scope = initScope(cx, context);
+      if (methodsMissing(scope, clasz)) {
+        return null;
+      }
+    } catch (ScriptException se) {
+      return null;
+    }
+    return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
+        new Class<?>[]{clasz}, new RhinoInvocationHandler(this, null));
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public <T> T getInterface(Object thiz, Class<T> clasz) {
+    if ((clasz == null) || !clasz.isInterface()) {
+      throw new IllegalArgumentException("Not an interface");
+    }
+    try (Context cx = ctxFactory.enterContext()) {
+      Scriptable scope = initScope(cx, context);
+      Scriptable thisObj = Context.toObject(thiz, scope);
+      if (methodsMissing(thisObj, clasz)) {
+        return null;
+      }
+    } catch (ScriptException se) {
+      return null;
+    }
+    return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
+        new Class<?>[]{clasz}, new RhinoInvocationHandler(this, thiz));
+  }
+
+  @Override
+  public Bindings createBindings() {
+    return new SimpleBindings();
+  }
+
+  @Override
+  public ScriptEngineFactory getFactory() {
+    return factory;
+  }
+
+  private void configureContext(Context cx) throws ScriptException {
+    Object lv = get(ScriptEngine.LANGUAGE_VERSION);
+    if (lv != null) {
+      cx.setLanguageVersion(parseInteger(lv));
+    }
+    Object ol = get(OPTIMIZATION_LEVEL);
+    if (ol != null) {
+      cx.setOptimizationLevel(parseInteger(ol));
+    }
+  }
+
+  private static int parseInteger(Object v) throws ScriptException {
+    if (v instanceof String) {
+      try {
+        return Integer.parseInt((String) v);
+      } catch (NumberFormatException nfe) {
+        throw new ScriptException("Invalid number " + v);
+      }
+    } else if (v instanceof Integer) {
+      return ((Integer) v).intValue();
+    } else {
+      throw new ScriptException("Value must be a string or number");
+    }
+  }
+
+  private String getFilename() {
+    Object fn = get(ScriptEngine.FILENAME);
+    if (fn instanceof String) {
+      return (String) fn;
+    }
+    return DEFAULT_FILENAME;
+  }
+
+  private static boolean methodsMissing(Scriptable scope, Class<?> clasz) {
+    for (Method m : clasz.getMethods()) {
+      if (m.getDeclaringClass() == Object.class) {
+        continue;
+      }
+      Object methodObj = ScriptableObject.getProperty(scope, m.getName());
+      if (!(methodObj instanceof Callable)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private static final class CtxFactory
+      extends ContextFactory {
+
+    @Override
+    protected boolean hasFeature(Context cx, int featureIndex) {
+      if (featureIndex == Context.FEATURE_INTEGER_WITHOUT_DECIMAL_PLACE) {
+        return true;
+      }
+      return super.hasFeature(cx, featureIndex);
+    }
+
+    @Override
+    protected void onContextCreated(Context cx) {
+      cx.setLanguageVersion(Context.VERSION_ES6);
+      cx.setOptimizationLevel(DEFAULT_OPT);
+      cx.setGeneratingDebug(DEFAULT_DEBUG);
+    }
+  }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/EqualObjectGraphs.java rhino-1.7.14/src/org/mozilla/javascript/EqualObjectGraphs.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/EqualObjectGraphs.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/EqualObjectGraphs.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,321 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+import java.util.Arrays;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.mozilla.javascript.debug.DebuggableObject;
+
+/**
+ * An object that implements deep equality test of objects, including their
+ * reference graph topology, that is in addition to establishing by-value
+ * equality of objects, it also establishes that their reachable object graphs
+ * have identical shape. It is capable of custom-comparing a wide range of
+ * various objects, including various Rhino Scriptables, Java arrays, Java
+ * Lists, and to some degree Java Maps and Sets (sorted Maps are okay, as well
+ * as Sets with elements that can be sorted using their Comparable
+ * implementation, and Maps whose keysets work the same). The requirement for
+ * sortable maps and sets is to ensure deterministic order of traversal, which
+ * is necessary for establishing structural equality of object graphs.
+ *
+ * An instance of this object is stateful in that it memoizes pairs of objects
+ * that already compared equal, so reusing an instance for repeated equality
+ * tests of potentially overlapping object graph is beneficial for performance
+ * as long as all equality test invocations returns true. Reuse is not advised
+ * after an equality test returned false since there is a heuristic in comparing
+ * cyclic data structures that can memoize false equalities if two cyclic data
+ * structures end up being unequal.
+ */
+final class EqualObjectGraphs  {
+    private static final ThreadLocal<EqualObjectGraphs> instance = new ThreadLocal<>();
+
+    // Object pairs already known to be equal. Used to short-circuit repeated traversals of objects reachable through
+    // different paths as well as to detect structural inequality.
+    private final Map<Object, Object> knownEquals = new IdentityHashMap<>();
+    // Currently compared objects; used to avoid infinite recursion over cyclic object graphs.
+    private final Map<Object, Object> currentlyCompared = new IdentityHashMap<>();
+
+    static <T> T withThreadLocal(java.util.function.Function<EqualObjectGraphs, T> action) {
+        final EqualObjectGraphs currEq = instance.get();
+        if (currEq == null) {
+            final EqualObjectGraphs eq = new EqualObjectGraphs();
+            instance.set(eq);
+            try {
+                return action.apply(eq);
+            } finally {
+                instance.set(null);
+            }
+        }
+        return action.apply(currEq);
+    }
+
+    boolean equalGraphs(Object o1, Object o2) {
+        if (o1 == o2) {
+            return true;
+        } else if (o1 == null || o2 == null) {
+            return false;
+        }
+
+        final Object curr2 = currentlyCompared.get(o1);
+        if (curr2 == o2) {
+            // Provisionally afford that if we're already recursively comparing
+            // (o1, o2) that they'll be equal. NOTE: this is the heuristic
+            // mentioned in the class JavaDoc that can drive memoizing false
+            // equalities if cyclic data structures end up being unequal.
+            // While it would be possible to fix that with additional code, the
+            // usual usage of equality comparisons is short-circuit-on-false anyway,
+            // so this edge case should not arise in normal usage and the additional
+            // code complexity to guard against it is not worth it.
+            return true;
+        } else if (curr2 != null) {
+            // If we're already recursively comparing o1 to some other object,
+            // this comparison is structurally false.
+            return false;
+        }
+
+        final Object prev2 = knownEquals.get(o1);
+        if (prev2 == o2) {
+            // o1 known to be equal to o2.
+            return true;
+        } else if (prev2 != null) {
+            // o1 known to be equal to something other than o2.
+            return false;
+        }
+
+        final Object prev1 = knownEquals.get(o2);
+        assert prev1 != o1; // otherwise we would've already returned at prev2 == o2
+        if (prev1 != null) {
+            // o2 known to be equal to something other than o1.
+            return false;
+        }
+
+        currentlyCompared.put(o1, o2);
+        final boolean eq = equalGraphsNoMemo(o1, o2);
+        if (eq) {
+            knownEquals.put(o1, o2);
+            knownEquals.put(o2, o1);
+        }
+        currentlyCompared.remove(o1);
+        return eq;
+    }
+
+    private boolean equalGraphsNoMemo(Object o1, Object o2) {
+        if (o1 instanceof Wrapper) {
+            return o2 instanceof Wrapper && equalGraphs(((Wrapper)o1).unwrap(), ((Wrapper)o2).unwrap());
+        } else if (o1 instanceof Scriptable) {
+            return o2 instanceof Scriptable && equalScriptables((Scriptable)o1, (Scriptable)o2);
+        } else if (o1 instanceof ConsString) {
+            return ((ConsString)o1).toString().equals(o2);
+        } else if (o2 instanceof ConsString) {
+            return o1.equals(((ConsString)o2).toString());
+        } else if (o1 instanceof SymbolKey) {
+            return o2 instanceof SymbolKey && equalGraphs(((SymbolKey)o1).getName(), ((SymbolKey)o2).getName());
+        } else if (o1 instanceof Object[]) {
+            return o2 instanceof Object[] && equalObjectArrays((Object[])o1, (Object[])o2);
+        } else if (o1.getClass().isArray()) {
+            return Objects.deepEquals(o1,  o2);
+        } else if (o1 instanceof List<?>) {
+            return o2 instanceof List<?> && equalLists((List<?>)o1, (List<?>)o2);
+        } else if (o1 instanceof Map<?, ?>) {
+            return o2 instanceof Map<?, ?> && equalMaps((Map<?, ?>)o1, (Map<?, ?>)o2);
+        } else if (o1 instanceof Set<?>) {
+            return o2 instanceof Set<?> && equalSets((Set<?>)o1, (Set<?>)o2);
+        } else if (o1 instanceof NativeGlobal) {
+            return o2 instanceof NativeGlobal; // stateless objects
+        } else if (o1 instanceof JavaAdapter) {
+            return o2 instanceof JavaAdapter; // stateless objects
+        } else if (o1 instanceof NativeJavaTopPackage) {
+            return o2 instanceof NativeJavaTopPackage; // stateless objects
+        }
+
+        // Fallback case for everything else.
+        return o1.equals(o2);
+    }
+
+    private boolean equalScriptables(final Scriptable s1, final Scriptable s2) {
+        final Object[] ids1 = getSortedIds(s1);
+        final Object[] ids2 = getSortedIds(s2);
+        if (!equalObjectArrays(ids1, ids2)) {
+            return false;
+        }
+        final int l = ids1.length;
+        for(int i = 0; i < l; ++i) {
+            if (!equalGraphs(getValue(s1, ids1[i]), getValue(s2, ids2[i]))) {
+                return false;
+            }
+        }
+        if (!equalGraphs(s1.getPrototype(), s2.getPrototype())) {
+            return false;
+        } else if (!equalGraphs(s1.getParentScope(), s2.getParentScope())) {
+            return false;
+        }
+
+        // Handle special Scriptable implementations
+        if (s1 instanceof NativeContinuation) {
+            return s2 instanceof NativeContinuation && NativeContinuation.equalImplementations((NativeContinuation)s1, (NativeContinuation)s2);
+        } else if (s1 instanceof NativeJavaPackage) {
+            return s1.equals(s2); // Overridden appropriately
+        } else if (s1 instanceof IdFunctionObject) {
+            return s2 instanceof IdFunctionObject && IdFunctionObject.equalObjectGraphs((IdFunctionObject)s1, (IdFunctionObject)s2, this);
+        } else if (s1 instanceof InterpretedFunction) {
+            return s2 instanceof InterpretedFunction && equalInterpretedFunctions((InterpretedFunction)s1, (InterpretedFunction)s2);
+        } else if (s1 instanceof ArrowFunction) {
+            return s2 instanceof ArrowFunction && ArrowFunction.equalObjectGraphs((ArrowFunction)s1, (ArrowFunction)s2, this);
+        } else if (s1 instanceof BoundFunction) {
+            return s2 instanceof BoundFunction && BoundFunction.equalObjectGraphs((BoundFunction)s1, (BoundFunction)s2, this);
+        } else if (s1 instanceof NativeSymbol) {
+            return s2 instanceof NativeSymbol && equalGraphs(((NativeSymbol)s1).getKey(), ((NativeSymbol)s2).getKey());
+        }
+        return true;
+    }
+
+    private boolean equalObjectArrays(final Object[] a1, final Object[] a2) {
+        if (a1.length != a2.length) {
+            return false;
+        }
+        for(int i = 0; i < a1.length; ++i) {
+            if (!equalGraphs(a1[i], a2[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private boolean equalLists(final List<?> l1, final List<?> l2) {
+        if (l1.size() != l2.size()) {
+            return false;
+        }
+        final Iterator<?> i1 = l1.iterator();
+        final Iterator<?> i2 = l2.iterator();
+        while(i1.hasNext() && i2.hasNext()) {
+            if (!equalGraphs(i1.next(), i2.next())) {
+                return false;
+            }
+        }
+        assert !(i1.hasNext() || i2.hasNext());
+        return true;
+    }
+
+    @SuppressWarnings("rawtypes")
+    private boolean equalMaps(final Map<?, ?> m1, final Map<?, ?> m2) {
+        if (m1.size() != m2.size()) {
+            return false;
+        }
+        final Iterator<Map.Entry> i1 = sortedEntries(m1);
+        final Iterator<Map.Entry> i2 = sortedEntries(m2);
+
+        while(i1.hasNext() && i2.hasNext()) {
+            final Map.Entry kv1 = i1.next();
+            final Map.Entry kv2 = i2.next();
+            if (!(equalGraphs(kv1.getKey(), kv2.getKey()) && equalGraphs(kv1.getValue(), kv2.getValue()))) {
+                return false;
+            }
+        }
+        assert !(i1.hasNext() || i2.hasNext());
+        // TODO: assert linked maps traversal order?
+        return true;
+
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    private static Iterator<Map.Entry> sortedEntries(final Map m) {
+        // Yes, this throws ClassCastException if the keys aren't comparable. That's okay. We only support maps with
+        // deterministic traversal order.
+        final Map sortedMap = (m instanceof SortedMap<?, ?> ? m : new TreeMap(m));
+        return sortedMap.entrySet().iterator();
+    }
+
+    private boolean equalSets(final Set<?> s1, final Set<?> s2) {
+        return equalObjectArrays(sortedSet(s1), sortedSet(s2));
+    }
+
+    private static Object[] sortedSet(final Set<?> s) {
+        final Object[] a = s.toArray();
+        Arrays.sort(a); // ClassCastException possible
+        return a;
+    }
+
+    private static boolean equalInterpretedFunctions(final InterpretedFunction f1, final InterpretedFunction f2) {
+        return Objects.equals(f1.getEncodedSource(), f2.getEncodedSource());
+    }
+
+    // Sort IDs deterministically
+    private static Object[] getSortedIds(final Scriptable s) {
+        final Object[] ids = getIds(s);
+        Arrays.sort(ids, (a, b) -> {
+            if (a instanceof Integer) {
+                if (b instanceof Integer) {
+                    return ((Integer)a).compareTo((Integer)b);
+                } else if (b instanceof String || b instanceof Symbol) {
+                    return -1; // ints before strings or symbols
+                }
+            } else if (a instanceof String) {
+                if (b instanceof String) {
+                    return ((String)a).compareTo((String)b);
+                } else if (b instanceof Integer) {
+                    return 1; // strings after ints
+                } else if (b instanceof Symbol) {
+                    return -1; // strings before symbols
+                }
+            } else if (a instanceof Symbol) {
+                if (b instanceof Symbol) {
+                    // As long as people bother to reasonably name their symbols,
+                    // this will work. If there's clashes in symbol names (e.g.
+                    // lots of unnamed symbols) it can lead to false inequalities.
+                    return getSymbolName((Symbol)a).compareTo(getSymbolName((Symbol)b));
+                } else if (b instanceof Integer || b instanceof String) {
+                    return 1; // symbols after ints and strings
+                }
+            }
+            // We can only compare Rhino key types: Integer, String, Symbol
+            throw new ClassCastException();
+        });
+        return ids;
+    }
+
+    private static String getSymbolName(final Symbol s) {
+        if (s instanceof SymbolKey) {
+            return ((SymbolKey)s).getName();
+        } else if (s instanceof NativeSymbol) {
+            return ((NativeSymbol)s).getKey().getName();
+        } else {
+            // We can only handle native Rhino Symbol types
+            throw new ClassCastException();
+        }
+    }
+
+    private static Object[] getIds(final Scriptable s) {
+        if (s instanceof ScriptableObject) {
+            // Grabs symbols too
+            return ((ScriptableObject)s).getIds(true, true);
+        } else if (s instanceof DebuggableObject) {
+            return ((DebuggableObject)s).getAllIds();
+        } else {
+            return s.getIds();
+        }
+    }
+
+    private static Object getValue(final Scriptable s, final Object id) {
+        if (id instanceof Symbol) {
+            return ScriptableObject.getProperty(s, (Symbol)id);
+        } else if (id instanceof Integer) {
+            return ScriptableObject.getProperty(s, ((Integer)id).intValue());
+        } else if (id instanceof String) {
+            return ScriptableObject.getProperty(s, (String)id);
+        } else {
+            throw new ClassCastException();
+        }
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ES6Generator.java rhino-1.7.14/src/org/mozilla/javascript/ES6Generator.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ES6Generator.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/ES6Generator.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,460 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+public final class ES6Generator extends IdScriptableObject {
+    private static final long serialVersionUID = 1645892441041347273L;
+
+    private static final Object GENERATOR_TAG = "Generator";
+
+    static ES6Generator init(ScriptableObject scope, boolean sealed) {
+
+        ES6Generator prototype = new ES6Generator();
+        if (scope != null) {
+            prototype.setParentScope(scope);
+            prototype.setPrototype(getObjectPrototype(scope));
+        }
+        prototype.activatePrototypeMap(MAX_PROTOTYPE_ID);
+        if (sealed) {
+            prototype.sealObject();
+        }
+
+        // Need to access Generator prototype when constructing
+        // Generator instances, but don't have a generator constructor
+        // to use to find the prototype. Use the "associateValue"
+        // approach instead.
+        if (scope != null) {
+            scope.associateValue(GENERATOR_TAG, prototype);
+        }
+
+        return prototype;
+    }
+
+    /** Only for constructing the prototype object. */
+    private ES6Generator() {}
+
+    public ES6Generator(Scriptable scope, NativeFunction function, Object savedState) {
+        this.function = function;
+        this.savedState = savedState;
+        // Set parent and prototype properties. Since we don't have a
+        // "Generator" constructor in the top scope, we stash the
+        // prototype in the top scope's associated value.
+        Scriptable top = ScriptableObject.getTopLevelScope(scope);
+        this.setParentScope(top);
+        ES6Generator prototype =
+                (ES6Generator) ScriptableObject.getTopScopeValue(top, GENERATOR_TAG);
+        this.setPrototype(prototype);
+    }
+
+    @Override
+    public String getClassName() {
+        return "Generator";
+    }
+
+    @Override
+    protected void initPrototypeId(int id) {
+        if (id == SymbolId_iterator) {
+            initPrototypeMethod(GENERATOR_TAG, id, SymbolKey.ITERATOR, "[Symbol.iterator]", 0);
+            return;
+        }
+
+        String s;
+        int arity;
+        switch (id) {
+            case Id_next:
+                arity = 1;
+                s = "next";
+                break;
+            case Id_return:
+                arity = 1;
+                s = "return";
+                break;
+            case Id_throw:
+                arity = 1;
+                s = "throw";
+                break;
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
+        }
+        initPrototypeMethod(GENERATOR_TAG, id, s, arity);
+    }
+
+    @Override
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        if (!f.hasTag(GENERATOR_TAG)) {
+            return super.execIdCall(f, cx, scope, thisObj, args);
+        }
+        int id = f.methodId();
+
+        ES6Generator generator = ensureType(thisObj, ES6Generator.class, f);
+        Object value = args.length >= 1 ? args[0] : Undefined.instance;
+
+        switch (id) {
+            case Id_return:
+                if (generator.delegee == null) {
+                    return generator.resumeAbruptLocal(
+                            cx, scope, NativeGenerator.GENERATOR_CLOSE, value);
+                }
+                return generator.resumeDelegeeReturn(cx, scope, value);
+            case Id_next:
+                if (generator.delegee == null) {
+                    return generator.resumeLocal(cx, scope, value);
+                }
+                return generator.resumeDelegee(cx, scope, value);
+            case Id_throw:
+                if (generator.delegee == null) {
+                    return generator.resumeAbruptLocal(
+                            cx, scope, NativeGenerator.GENERATOR_THROW, value);
+                }
+                return generator.resumeDelegeeThrow(cx, scope, value);
+            case SymbolId_iterator:
+                return thisObj;
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
+        }
+    }
+
+    private Scriptable resumeDelegee(Context cx, Scriptable scope, Object value) {
+        try {
+            // Be super-careful and only pass an arg to next if it expects one
+            Object[] nextArgs =
+                    Undefined.instance.equals(value)
+                            ? ScriptRuntime.emptyArgs
+                            : new Object[] {value};
+
+            Callable nextFn =
+                    ScriptRuntime.getPropFunctionAndThis(
+                            delegee, ES6Iterator.NEXT_METHOD, cx, scope);
+            Scriptable nextThis = ScriptRuntime.lastStoredScriptable(cx);
+            Object nr = nextFn.call(cx, scope, nextThis, nextArgs);
+
+            Scriptable nextResult = ScriptableObject.ensureScriptable(nr);
+            if (ScriptRuntime.isIteratorDone(cx, nextResult)) {
+                // Iterator is "done".
+                delegee = null;
+                // Return a result to the original generator
+                return resumeLocal(
+                        cx,
+                        scope,
+                        ScriptableObject.getProperty(nextResult, ES6Iterator.VALUE_PROPERTY));
+            }
+            // Otherwise, we have a normal result and should continue
+            return nextResult;
+
+        } catch (RhinoException re) {
+            // Exceptions from the delegee should be handled by the enclosing
+            // generator, including if they're because functions can't be found.
+            delegee = null;
+            return resumeAbruptLocal(cx, scope, NativeGenerator.GENERATOR_THROW, re);
+        }
+    }
+
+    private Scriptable resumeDelegeeThrow(Context cx, Scriptable scope, Object value) {
+        boolean returnCalled = false;
+        try {
+            // Delegate to "throw" method. If it's not defined we'll get an error here.
+            Callable throwFn = ScriptRuntime.getPropFunctionAndThis(delegee, "throw", cx, scope);
+            Scriptable nextThis = ScriptRuntime.lastStoredScriptable(cx);
+            Object throwResult = throwFn.call(cx, scope, nextThis, new Object[] {value});
+
+            if (ScriptRuntime.isIteratorDone(cx, throwResult)) {
+                // Iterator is "done".
+                try {
+                    // Return a result to the original generator, but first optionally call "return"
+                    returnCalled = true;
+                    callReturnOptionally(cx, scope, Undefined.instance);
+                } finally {
+                    delegee = null;
+                }
+                return resumeLocal(
+                        cx,
+                        scope,
+                        ScriptRuntime.getObjectProp(
+                                throwResult, ES6Iterator.VALUE_PROPERTY, cx, scope));
+            }
+            // Otherwise, we have a normal result and should continue
+            return ensureScriptable(throwResult);
+
+        } catch (RhinoException re) {
+            // Handle all exceptions, including missing methods, by delegating to original.
+            try {
+                if (!returnCalled) {
+                    try {
+                        callReturnOptionally(cx, scope, Undefined.instance);
+                    } catch (RhinoException re2) {
+                        return resumeAbruptLocal(cx, scope, NativeGenerator.GENERATOR_THROW, re2);
+                    }
+                }
+            } finally {
+                delegee = null;
+            }
+            return resumeAbruptLocal(cx, scope, NativeGenerator.GENERATOR_THROW, re);
+        }
+    }
+
+    private Scriptable resumeDelegeeReturn(Context cx, Scriptable scope, Object value) {
+        try {
+            // Call "return" but don't throw if it can't be found
+            Object retResult = callReturnOptionally(cx, scope, value);
+            if (retResult != null) {
+                if (ScriptRuntime.isIteratorDone(cx, retResult)) {
+                    // Iterator is "done".
+                    delegee = null;
+                    // Return a result to the original generator
+                    return resumeAbruptLocal(
+                            cx,
+                            scope,
+                            NativeGenerator.GENERATOR_CLOSE,
+                            ScriptRuntime.getObjectPropNoWarn(
+                                    retResult, ES6Iterator.VALUE_PROPERTY, cx, scope));
+                } else {
+                    // Not actually done yet!
+                    return ensureScriptable(retResult);
+                }
+            }
+
+            // No "return" -- let the original iterator return the value.
+            delegee = null;
+            return resumeAbruptLocal(cx, scope, NativeGenerator.GENERATOR_CLOSE, value);
+
+        } catch (RhinoException re) {
+            // Exceptions from the delegee should be handled by the enclosing
+            // generator, including if they're because functions can't be found.
+            delegee = null;
+            return resumeAbruptLocal(cx, scope, NativeGenerator.GENERATOR_THROW, re);
+        }
+    }
+
+    private Scriptable resumeLocal(Context cx, Scriptable scope, Object value) {
+        if (state == State.COMPLETED) {
+            return ES6Iterator.makeIteratorResult(cx, scope, Boolean.TRUE);
+        }
+        if (state == State.EXECUTING) {
+            throw ScriptRuntime.typeErrorById("msg.generator.executing");
+        }
+
+        Scriptable result = ES6Iterator.makeIteratorResult(cx, scope, Boolean.FALSE);
+        state = State.EXECUTING;
+
+        try {
+            Object r =
+                    function.resumeGenerator(
+                            cx, scope, NativeGenerator.GENERATOR_SEND, savedState, value);
+
+            if (r instanceof YieldStarResult) {
+                // This special result tells us that we are executing a "yield *"
+                state = State.SUSPENDED_YIELD;
+                YieldStarResult ysResult = (YieldStarResult) r;
+                try {
+                    delegee = ScriptRuntime.callIterator(ysResult.getResult(), cx, scope);
+                } catch (RhinoException re) {
+                    // Need to handle exceptions if the iterator cannot be called.
+                    return resumeAbruptLocal(cx, scope, NativeGenerator.GENERATOR_THROW, re);
+                }
+
+                Scriptable delResult;
+                try {
+                    // Re-execute but update state in case we end up back here
+                    // Value shall be Undefined based on the very complex spec!
+                    delResult = resumeDelegee(cx, scope, Undefined.instance);
+                } finally {
+                    state = State.EXECUTING;
+                }
+                if (ScriptRuntime.isIteratorDone(cx, delResult)) {
+                    state = State.COMPLETED;
+                }
+                return delResult;
+            }
+
+            ScriptableObject.putProperty(result, ES6Iterator.VALUE_PROPERTY, r);
+
+        } catch (NativeGenerator.GeneratorClosedException gce) {
+            state = State.COMPLETED;
+        } catch (JavaScriptException jse) {
+            state = State.COMPLETED;
+            if (jse.getValue() instanceof NativeIterator.StopIteration) {
+                ScriptableObject.putProperty(
+                        result,
+                        ES6Iterator.VALUE_PROPERTY,
+                        ((NativeIterator.StopIteration) jse.getValue()).getValue());
+            } else {
+                lineNumber = jse.lineNumber();
+                lineSource = jse.lineSource();
+                if (jse.getValue() instanceof RhinoException) {
+                    throw (RhinoException) jse.getValue();
+                }
+                throw jse;
+            }
+        } catch (RhinoException re) {
+            lineNumber = re.lineNumber();
+            lineSource = re.lineSource();
+            throw re;
+        } finally {
+            if (state == State.COMPLETED) {
+                ScriptableObject.putProperty(result, ES6Iterator.DONE_PROPERTY, Boolean.TRUE);
+            } else {
+                state = State.SUSPENDED_YIELD;
+            }
+        }
+        return result;
+    }
+
+    private Scriptable resumeAbruptLocal(Context cx, Scriptable scope, int op, Object value) {
+        if (state == State.EXECUTING) {
+            throw ScriptRuntime.typeErrorById("msg.generator.executing");
+        }
+        if (state == State.SUSPENDED_START) {
+            // Throw right away if we never started
+            state = State.COMPLETED;
+        }
+
+        Scriptable result = ES6Iterator.makeIteratorResult(cx, scope, Boolean.FALSE);
+        if (state == State.COMPLETED) {
+            if (op == NativeGenerator.GENERATOR_THROW) {
+                throw new JavaScriptException(value, lineSource, lineNumber);
+            }
+            ScriptableObject.putProperty(result, ES6Iterator.DONE_PROPERTY, Boolean.TRUE);
+            return result;
+        }
+
+        state = State.EXECUTING;
+
+        Object throwValue = value;
+        if (op == NativeGenerator.GENERATOR_CLOSE) {
+            if (!(value instanceof NativeGenerator.GeneratorClosedException)) {
+                throwValue = new NativeGenerator.GeneratorClosedException();
+            }
+        } else {
+            if (value instanceof JavaScriptException) {
+                throwValue = ((JavaScriptException) value).getValue();
+            } else if (value instanceof RhinoException) {
+                throwValue = ScriptRuntime.wrapException((Throwable) value, scope, cx);
+            }
+        }
+
+        try {
+            Object r = function.resumeGenerator(cx, scope, op, savedState, throwValue);
+            ScriptableObject.putProperty(result, ES6Iterator.VALUE_PROPERTY, r);
+            // If we get here without an exception we can still run.
+            state = State.SUSPENDED_YIELD;
+
+        } catch (NativeGenerator.GeneratorClosedException gce) {
+            state = State.COMPLETED;
+        } catch (JavaScriptException jse) {
+            state = State.COMPLETED;
+            if (jse.getValue() instanceof NativeIterator.StopIteration) {
+                ScriptableObject.putProperty(
+                        result,
+                        ES6Iterator.VALUE_PROPERTY,
+                        ((NativeIterator.StopIteration) jse.getValue()).getValue());
+            } else {
+                lineNumber = jse.lineNumber();
+                lineSource = jse.lineSource();
+                if (jse.getValue() instanceof RhinoException) {
+                    throw (RhinoException) jse.getValue();
+                }
+                throw jse;
+            }
+        } catch (RhinoException re) {
+            state = State.COMPLETED;
+            lineNumber = re.lineNumber();
+            lineSource = re.lineSource();
+            throw re;
+        } finally {
+            // After an abrupt completion we are always, umm, complete,
+            // and we will never delegate to the delegee again
+            if (state == State.COMPLETED) {
+                delegee = null;
+                ScriptableObject.putProperty(result, ES6Iterator.DONE_PROPERTY, Boolean.TRUE);
+            }
+        }
+        return result;
+    }
+
+    private Object callReturnOptionally(Context cx, Scriptable scope, Object value) {
+        Object[] retArgs =
+                Undefined.instance.equals(value) ? ScriptRuntime.emptyArgs : new Object[] {value};
+        // Delegate to "return" method. If it's not defined we ignore it
+        Object retFnObj =
+                ScriptRuntime.getObjectPropNoWarn(delegee, ES6Iterator.RETURN_METHOD, cx, scope);
+        if (!Undefined.instance.equals(retFnObj)) {
+            if (!(retFnObj instanceof Callable)) {
+                throw ScriptRuntime.typeErrorById(
+                        "msg.isnt.function",
+                        ES6Iterator.RETURN_METHOD,
+                        ScriptRuntime.typeof(retFnObj));
+            }
+            return ((Callable) retFnObj).call(cx, scope, ensureScriptable(delegee), retArgs);
+        }
+        return null;
+    }
+
+    @Override
+    protected int findPrototypeId(Symbol k) {
+        if (SymbolKey.ITERATOR.equals(k)) {
+            return SymbolId_iterator;
+        }
+        return 0;
+    }
+
+    @Override
+    protected int findPrototypeId(String s) {
+        int id;
+        L0:
+        {
+            id = 0;
+            String X = null;
+            int s_length = s.length();
+            if (s_length == 4) {
+                X = "next";
+                id = Id_next;
+            } else if (s_length == 5) {
+                X = "throw";
+                id = Id_throw;
+            } else if (s_length == 6) {
+                X = "return";
+                id = Id_return;
+            }
+            if (X != null && X != s && !X.equals(s)) id = 0;
+            break L0;
+        }
+        return id;
+    }
+
+    private static final int Id_next = 1,
+            Id_return = 2,
+            Id_throw = 3,
+            SymbolId_iterator = 4,
+            MAX_PROTOTYPE_ID = SymbolId_iterator;
+
+    private NativeFunction function;
+    private Object savedState;
+    private String lineSource;
+    private int lineNumber;
+    private State state = State.SUSPENDED_START;
+    private Object delegee;
+
+    enum State {
+        SUSPENDED_START,
+        SUSPENDED_YIELD,
+        EXECUTING,
+        COMPLETED
+    }
+
+    public static final class YieldStarResult {
+        private Object result;
+
+        public YieldStarResult(Object result) {
+            this.result = result;
+        }
+
+        Object getResult() {
+            return result;
+        }
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ES6Iterator.java rhino-1.7.14/src/org/mozilla/javascript/ES6Iterator.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ES6Iterator.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ES6Iterator.java	2022-01-06 22:57:21.000000000 +0100
@@ -8,7 +8,9 @@
 
 public abstract class ES6Iterator extends IdScriptableObject {
 
-    static void init(ScriptableObject scope, boolean sealed, IdScriptableObject prototype, String tag) {
+    private static final long serialVersionUID = 2438373029140003950L;
+
+    protected static void init(ScriptableObject scope, boolean sealed, IdScriptableObject prototype, String tag) {
         if (scope != null) {
             prototype.setParentScope(scope);
             prototype.setPrototype(getObjectPrototype(scope));
@@ -28,17 +30,19 @@
     }
 
     protected boolean exhausted = false;
+    private String tag;
 
-    ES6Iterator() {}
+    protected ES6Iterator() {}
 
-    ES6Iterator(Scriptable scope) {
+    protected ES6Iterator(Scriptable scope, String tag) {
         // Set parent and prototype properties. Since we don't have a
         // "Iterator" constructor in the top scope, we stash the
         // prototype in the top scope's associated value.
+        this.tag = tag;
         Scriptable top = ScriptableObject.getTopLevelScope(scope);
         this.setParentScope(top);
         IdScriptableObject prototype = (IdScriptableObject)
-            ScriptableObject.getTopScopeValue(top, getTag());
+            ScriptableObject.getTopScopeValue(top, tag);
         setPrototype(prototype);
     }
 
@@ -49,11 +53,11 @@
             case Id_next:
                 initPrototypeMethod(getTag(), id, NEXT_METHOD, 0);
                 return;
-            case Id_iterator:
-                initPrototypeMethod(getTag(), id, SymbolKey.ITERATOR, "[Symbol.iterator]", 0);
+            case SymbolId_iterator:
+                initPrototypeMethod(getTag(), id, SymbolKey.ITERATOR, "[Symbol.iterator]", DONTENUM | READONLY);
                 return;
-            case Id_toStringTag:
-                initPrototypeValue(Id_toStringTag, SymbolKey.TO_STRING_TAG, getClassName(), DONTENUM | READONLY);
+            case SymbolId_toStringTag:
+                initPrototypeValue(SymbolId_toStringTag, SymbolKey.TO_STRING_TAG, getClassName(), DONTENUM | READONLY);
                 return;
             default: throw new IllegalArgumentException(String.valueOf(id));
         }
@@ -68,15 +72,12 @@
         }
         int id = f.methodId();
 
-        if (!(thisObj instanceof ES6Iterator))
-            throw incompatibleCallError(f);
-
-        ES6Iterator iterator = (ES6Iterator) thisObj;
+        ES6Iterator iterator = ensureType(thisObj, ES6Iterator.class, f);
 
         switch (id) {
         case Id_next:
             return iterator.next(cx, scope);
-        case Id_iterator:
+        case SymbolId_iterator:
             return iterator;
         default:
             throw new IllegalArgumentException(String.valueOf(id));
@@ -86,16 +87,16 @@
     @Override
     protected int findPrototypeId(Symbol k) {
         if (SymbolKey.ITERATOR.equals(k)) {
-            return Id_iterator;
+            return SymbolId_iterator;
         } else if (SymbolKey.TO_STRING_TAG.equals(k)) {
-            return Id_toStringTag;
+            return SymbolId_toStringTag;
         }
         return 0;
     }
 
     @Override
     protected int findPrototypeId(String s) {
-        if ("next".equals(s)) {
+        if (NEXT_METHOD.equals(s)) {
             return Id_next;
         }
         return 0;
@@ -113,26 +114,34 @@
         } else {
             this.exhausted = true;
         }
-        return makeIteratorResult(cx, scope, done, value);
+        return makeIteratorResult(cx, scope, Boolean.valueOf(done), value);
     }
 
-    abstract protected String getTag();
+    protected String getTag() {
+        return tag;
+    }
 
     // 25.1.1.3 The IteratorResult Interface
-    private Scriptable makeIteratorResult(Context cx, Scriptable scope, boolean done, Object value) {
-        Scriptable iteratorResult = cx.newObject(scope);
+    static Scriptable makeIteratorResult(Context cx, Scriptable scope, Boolean done) {
+        return makeIteratorResult(cx, scope, done, Undefined.instance);
+    }
+
+    static Scriptable makeIteratorResult(Context cx, Scriptable scope, Boolean done, Object value) {
+        final Scriptable iteratorResult = cx.newObject(scope);
         ScriptableObject.putProperty(iteratorResult, VALUE_PROPERTY, value);
         ScriptableObject.putProperty(iteratorResult, DONE_PROPERTY, done);
         return iteratorResult;
     }
 
     private static final int
-        Id_next             = 1,
-        Id_iterator         = 2,
-        Id_toStringTag      = 3,
-        MAX_PROTOTYPE_ID    = Id_toStringTag;
+        Id_next              = 1,
+        SymbolId_iterator    = 2,
+        SymbolId_toStringTag = 3,
+        MAX_PROTOTYPE_ID     = SymbolId_toStringTag;
 
     public static final String NEXT_METHOD = "next";
     public static final String DONE_PROPERTY = "done";
+    public static final String RETURN_PROPERTY = "return";
     public static final String VALUE_PROPERTY = "value";
+    public static final String RETURN_METHOD = "return";
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/EvaluatorException.java rhino-1.7.14/src/org/mozilla/javascript/EvaluatorException.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/EvaluatorException.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/EvaluatorException.java	2022-01-06 22:57:21.000000000 +0100
@@ -12,7 +12,7 @@
  */
 public class EvaluatorException extends RhinoException
 {
-    static final long serialVersionUID = -8743165779676009808L;
+    private static final long serialVersionUID = -8743165779676009808L;
 
     public EvaluatorException(String detail)
     {
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/Evaluator.java rhino-1.7.14/src/org/mozilla/javascript/Evaluator.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/Evaluator.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/Evaluator.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,10 +6,10 @@
 
 package org.mozilla.javascript;
 
-import org.mozilla.javascript.ast.ScriptNode;
-
 import java.util.List;
 
+import org.mozilla.javascript.ast.ScriptNode;
+
 /**
  * Abstraction of evaluation, which can be implemented either by an
  * interpreter or compiler.
@@ -64,7 +64,7 @@
     /**
      * Get the source position information by examining the stack.
      * @param cx Context
-     * @param linep Array object of length >= 1; getSourcePositionFromStack
+     * @param linep Array object of length >= 1; getSourcePositionFromStack
      *              will assign the line number to linep[0].
      * @return the name of the file or other source container
      */
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/Function.java rhino-1.7.14/src/org/mozilla/javascript/Function.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/Function.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/Function.java	2022-01-06 22:57:21.000000000 +0100
@@ -9,44 +9,40 @@
 package org.mozilla.javascript;
 
 /**
- * This is interface that all functions in JavaScript must implement.
- * The interface provides for calling functions and constructors.
+ * This is interface that all functions in JavaScript must implement. The interface provides for
+ * calling functions and constructors.
  *
  * @see org.mozilla.javascript.Scriptable
  * @author Norris Boyd
  */
-
-public interface Function extends Scriptable, Callable
-{
+public interface Function extends Scriptable, Callable, Constructable {
     /**
      * Call the function.
      *
-     * Note that the array of arguments is not guaranteed to have
-     * length greater than 0.
+     * <p>Note that the array of arguments is not guaranteed to have length greater than 0.
      *
      * @param cx the current Context for this thread
-     * @param scope the scope to execute the function relative to. This is
-     *              set to the value returned by getParentScope() except
-     *              when the function is called from a closure.
+     * @param scope the scope to execute the function relative to. This is set to the value returned
+     *     by getParentScope() except when the function is called from a closure.
      * @param thisObj the JavaScript <code>this</code> object
      * @param args the array of arguments
      * @return the result of the call
      */
-    public Object call(Context cx, Scriptable scope, Scriptable thisObj,
-                       Object[] args);
+    @Override
+    Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args);
 
     /**
      * Call the function as a constructor.
      *
-     * This method is invoked by the runtime in order to satisfy a use
-     * of the JavaScript <code>new</code> operator.  This method is
-     * expected to create a new object and return it.
+     * <p>This method is invoked by the runtime in order to satisfy a use of the JavaScript <code>
+     * new</code> operator. This method is expected to create a new object and return it.
      *
      * @param cx the current Context for this thread
-     * @param scope an enclosing scope of the caller except
-     *              when the function is called from a closure.
+     * @param scope an enclosing scope of the caller except when the function is called from a
+     *     closure.
      * @param args the array of arguments
      * @return the allocated object
      */
-    public Scriptable construct(Context cx, Scriptable scope, Object[] args);
+    @Override
+    Scriptable construct(Context cx, Scriptable scope, Object[] args);
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/FunctionObject.java rhino-1.7.14/src/org/mozilla/javascript/FunctionObject.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/FunctionObject.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/FunctionObject.java	2022-01-06 22:57:21.000000000 +0100
@@ -8,80 +8,78 @@
 
 package org.mozilla.javascript;
 
-import java.lang.reflect.*;
-import java.io.*;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import org.mozilla.javascript.commonjs.module.ModuleScope;
 
-public class FunctionObject extends BaseFunction
-{
-    static final long serialVersionUID = -5332312783643935019L;
+public class FunctionObject extends BaseFunction {
+    private static final long serialVersionUID = -5332312783643935019L;
 
     /**
      * Create a JavaScript function object from a Java method.
      *
-     * <p>The <code>member</code> argument must be either a java.lang.reflect.Method
-     * or a java.lang.reflect.Constructor and must match one of two forms.<p>
+     * <p>The <code>member</code> argument must be either a java.lang.reflect.Method or a
+     * java.lang.reflect.Constructor and must match one of two forms.
+     *
+     * <p>The first form is a member with zero or more parameters of the following types: Object,
+     * String, boolean, Scriptable, int, or double. The Long type is not supported because the
+     * double representation of a long (which is the EMCA-mandated storage type for Numbers) may
+     * lose precision. If the member is a Method, the return value must be void or one of the types
+     * allowed for parameters.
+     *
+     * <p>The runtime will perform appropriate conversions based upon the type of the parameter. A
+     * parameter type of Object specifies that no conversions are to be done. A parameter of type
+     * String will use Context.toString to convert arguments. Similarly, parameters of type double,
+     * boolean, and Scriptable will cause Context.toNumber, Context.toBoolean, and Context.toObject,
+     * respectively, to be called.
+     *
+     * <p>If the method is not static, the Java 'this' value will correspond to the JavaScript
+     * 'this' value. Any attempt to call the function with a 'this' value that is not of the right
+     * Java type will result in an error.
+     *
+     * <p>The second form is the variable arguments (or "varargs") form. If the FunctionObject will
+     * be used as a constructor, the member must have the following parameters
      *
-     * The first form is a member with zero or more parameters
-     * of the following types: Object, String, boolean, Scriptable,
-     * int, or double. The Long type is not supported
-     * because the double representation of a long (which is the
-     * EMCA-mandated storage type for Numbers) may lose precision.
-     * If the member is a Method, the return value must be void or one
-     * of the types allowed for parameters.<p>
-     *
-     * The runtime will perform appropriate conversions based
-     * upon the type of the parameter. A parameter type of
-     * Object specifies that no conversions are to be done. A parameter
-     * of type String will use Context.toString to convert arguments.
-     * Similarly, parameters of type double, boolean, and Scriptable
-     * will cause Context.toNumber, Context.toBoolean, and
-     * Context.toObject, respectively, to be called.<p>
-     *
-     * If the method is not static, the Java 'this' value will
-     * correspond to the JavaScript 'this' value. Any attempt
-     * to call the function with a 'this' value that is not
-     * of the right Java type will result in an error.<p>
-     *
-     * The second form is the variable arguments (or "varargs")
-     * form. If the FunctionObject will be used as a constructor,
-     * the member must have the following parameters
      * <pre>
      *      (Context cx, Object[] args, Function ctorObj,
      *       boolean inNewExpr)</pre>
-     * and if it is a Method, be static and return an Object result.<p>
      *
-     * Otherwise, if the FunctionObject will <i>not</i> be used to define a
-     * constructor, the member must be a static Method with parameters
+     * and if it is a Method, be static and return an Object result.
+     *
+     * <p>Otherwise, if the FunctionObject will <i>not</i> be used to define a constructor, the
+     * member must be a static Method with parameters
+     *
      * <pre>
      *      (Context cx, Scriptable thisObj, Object[] args,
      *       Function funObj) </pre>
-     * and an Object result.<p>
      *
-     * When the function varargs form is called as part of a function call,
-     * the <code>args</code> parameter contains the
-     * arguments, with <code>thisObj</code>
-     * set to the JavaScript 'this' value. <code>funObj</code>
-     * is the function object for the invoked function.<p>
-     *
-     * When the constructor varargs form is called or invoked while evaluating
-     * a <code>new</code> expression, <code>args</code> contains the
-     * arguments, <code>ctorObj</code> refers to this FunctionObject, and
-     * <code>inNewExpr</code> is true if and only if  a <code>new</code>
-     * expression caused the call. This supports defining a function that
-     * has different behavior when called as a constructor than when
-     * invoked as a normal function call. (For example, the Boolean
-     * constructor, when called as a function,
-     * will convert to boolean rather than creating a new object.)<p>
+     * and an Object result.
+     *
+     * <p>When the function varargs form is called as part of a function call, the <code>args</code>
+     * parameter contains the arguments, with <code>thisObj</code> set to the JavaScript 'this'
+     * value. <code>funObj</code> is the function object for the invoked function.
+     *
+     * <p>When the constructor varargs form is called or invoked while evaluating a <code>new</code>
+     * expression, <code>args</code> contains the arguments, <code>ctorObj</code> refers to this
+     * FunctionObject, and <code>inNewExpr</code> is true if and only if a <code>new</code>
+     * expression caused the call. This supports defining a function that has different behavior
+     * when called as a constructor than when invoked as a normal function call. (For example, the
+     * Boolean constructor, when called as a function, will convert to boolean rather than creating
+     * a new object.)
+     *
+     * <p>
      *
      * @param name the name of the function
-     * @param methodOrConstructor a java.lang.reflect.Method or a java.lang.reflect.Constructor
-     *                            that defines the object
+     * @param methodOrConstructor a java.lang.reflect.Method or a java.lang.reflect.Constructor that
+     *     defines the object
      * @param scope enclosing scope of function
      * @see org.mozilla.javascript.Scriptable
      */
-    public FunctionObject(String name, Member methodOrConstructor,
-                          Scriptable scope)
-    {
+    public FunctionObject(String name, Member methodOrConstructor, Scriptable scope) {
         if (methodOrConstructor instanceof Constructor) {
             member = new MemberBox((Constructor<?>) methodOrConstructor);
             isStatic = true; // well, doesn't take a 'this'
@@ -96,25 +94,21 @@
         if (arity == 4 && (types[1].isArray() || types[2].isArray())) {
             // Either variable args or an error.
             if (types[1].isArray()) {
-                if (!isStatic ||
-                    types[0] != ScriptRuntime.ContextClass ||
-                    types[1].getComponentType() != ScriptRuntime.ObjectClass ||
-                    types[2] != ScriptRuntime.FunctionClass ||
-                    types[3] != Boolean.TYPE)
-                {
-                    throw Context.reportRuntimeError1(
-                        "msg.varargs.ctor", methodName);
+                if (!isStatic
+                        || types[0] != ScriptRuntime.ContextClass
+                        || types[1].getComponentType() != ScriptRuntime.ObjectClass
+                        || types[2] != ScriptRuntime.FunctionClass
+                        || types[3] != Boolean.TYPE) {
+                    throw Context.reportRuntimeErrorById("msg.varargs.ctor", methodName);
                 }
                 parmsLength = VARARGS_CTOR;
             } else {
-                if (!isStatic ||
-                    types[0] != ScriptRuntime.ContextClass ||
-                    types[1] != ScriptRuntime.ScriptableClass ||
-                    types[2].getComponentType() != ScriptRuntime.ObjectClass ||
-                    types[3] != ScriptRuntime.FunctionClass)
-                {
-                    throw Context.reportRuntimeError1(
-                        "msg.varargs.fun", methodName);
+                if (!isStatic
+                        || types[0] != ScriptRuntime.ContextClass
+                        || types[1] != ScriptRuntime.ScriptableClass
+                        || types[2].getComponentType() != ScriptRuntime.ObjectClass
+                        || types[3] != ScriptRuntime.FunctionClass) {
+                    throw Context.reportRuntimeErrorById("msg.varargs.fun", methodName);
                 }
                 parmsLength = VARARGS_METHOD;
             }
@@ -125,10 +119,10 @@
                 for (int i = 0; i != arity; ++i) {
                     int tag = getTypeTag(types[i]);
                     if (tag == JAVA_UNSUPPORTED_TYPE) {
-                        throw Context.reportRuntimeError2(
-                            "msg.bad.parms", types[i].getName(), methodName);
+                        throw Context.reportRuntimeErrorById(
+                                "msg.bad.parms", types[i].getName(), methodName);
                     }
-                    typeTags[i] = (byte)tag;
+                    typeTags[i] = (byte) tag;
                 }
             }
         }
@@ -144,8 +138,7 @@
         } else {
             Class<?> ctorType = member.getDeclaringClass();
             if (!ScriptRuntime.ScriptableClass.isAssignableFrom(ctorType)) {
-                throw Context.reportRuntimeError1(
-                    "msg.bad.ctor.return", ctorType.getName());
+                throw Context.reportRuntimeErrorById("msg.bad.ctor.return", ctorType.getName());
             }
         }
 
@@ -153,24 +146,16 @@
     }
 
     /**
-     * @return One of <tt>JAVA_*_TYPE</tt> constants to indicate desired type
-     *         or {@link #JAVA_UNSUPPORTED_TYPE} if the convertion is not
-     *         possible
+     * @return One of <code>JAVA_*_TYPE</code> constants to indicate desired type or {@link
+     *     #JAVA_UNSUPPORTED_TYPE} if the convertion is not possible
      */
-    public static int getTypeTag(Class<?> type)
-    {
-        if (type == ScriptRuntime.StringClass)
-            return JAVA_STRING_TYPE;
-        if (type == ScriptRuntime.IntegerClass || type == Integer.TYPE)
-            return JAVA_INT_TYPE;
-        if (type == ScriptRuntime.BooleanClass || type == Boolean.TYPE)
-            return JAVA_BOOLEAN_TYPE;
-        if (type == ScriptRuntime.DoubleClass || type == Double.TYPE)
-            return JAVA_DOUBLE_TYPE;
-        if (ScriptRuntime.ScriptableClass.isAssignableFrom(type))
-            return JAVA_SCRIPTABLE_TYPE;
-        if (type == ScriptRuntime.ObjectClass)
-            return JAVA_OBJECT_TYPE;
+    public static int getTypeTag(Class<?> type) {
+        if (type == ScriptRuntime.StringClass) return JAVA_STRING_TYPE;
+        if (type == ScriptRuntime.IntegerClass || type == Integer.TYPE) return JAVA_INT_TYPE;
+        if (type == ScriptRuntime.BooleanClass || type == Boolean.TYPE) return JAVA_BOOLEAN_TYPE;
+        if (type == ScriptRuntime.DoubleClass || type == Double.TYPE) return JAVA_DOUBLE_TYPE;
+        if (ScriptRuntime.ScriptableClass.isAssignableFrom(type)) return JAVA_SCRIPTABLE_TYPE;
+        if (type == ScriptRuntime.ObjectClass) return JAVA_OBJECT_TYPE;
 
         // Note that the long type is not supported; see the javadoc for
         // the constructor for this class
@@ -178,65 +163,52 @@
         return JAVA_UNSUPPORTED_TYPE;
     }
 
-    public static Object convertArg(Context cx, Scriptable scope,
-                                    Object arg, int typeTag)
-    {
+    public static Object convertArg(Context cx, Scriptable scope, Object arg, int typeTag) {
         switch (typeTag) {
-          case JAVA_STRING_TYPE:
-              if (arg instanceof String)
-                return arg;
-            return ScriptRuntime.toString(arg);
-          case JAVA_INT_TYPE:
-              if (arg instanceof Integer)
-                return arg;
-            return Integer.valueOf(ScriptRuntime.toInt32(arg));
-          case JAVA_BOOLEAN_TYPE:
-              if (arg instanceof Boolean)
+            case JAVA_STRING_TYPE:
+                if (arg instanceof String) return arg;
+                return ScriptRuntime.toString(arg);
+            case JAVA_INT_TYPE:
+                if (arg instanceof Integer) return arg;
+                return Integer.valueOf(ScriptRuntime.toInt32(arg));
+            case JAVA_BOOLEAN_TYPE:
+                if (arg instanceof Boolean) return arg;
+                return ScriptRuntime.toBoolean(arg) ? Boolean.TRUE : Boolean.FALSE;
+            case JAVA_DOUBLE_TYPE:
+                if (arg instanceof Double) return arg;
+                return Double.valueOf(ScriptRuntime.toNumber(arg));
+            case JAVA_SCRIPTABLE_TYPE:
+                return ScriptRuntime.toObjectOrNull(cx, arg, scope);
+            case JAVA_OBJECT_TYPE:
+                if (arg instanceof ConsString) return arg.toString();
                 return arg;
-            return ScriptRuntime.toBoolean(arg) ? Boolean.TRUE
-                                                : Boolean.FALSE;
-          case JAVA_DOUBLE_TYPE:
-            if (arg instanceof Double)
-                return arg;
-            return new Double(ScriptRuntime.toNumber(arg));
-          case JAVA_SCRIPTABLE_TYPE:
-              return ScriptRuntime.toObjectOrNull(cx, arg, scope);
-          case JAVA_OBJECT_TYPE:
-            return arg;
-          default:
-            throw new IllegalArgumentException();
+            default:
+                throw new IllegalArgumentException();
         }
     }
 
     /**
-     * Return the value defined by  the method used to construct the object
-     * (number of parameters of the method, or 1 if the method is a "varargs"
-     * form).
+     * Return the value defined by the method used to construct the object (number of parameters of
+     * the method, or 1 if the method is a "varargs" form).
      */
     @Override
     public int getArity() {
         return parmsLength < 0 ? 1 : parmsLength;
     }
 
-    /**
-     * Return the same value as {@link #getArity()}.
-     */
+    /** Return the same value as {@link #getArity()}. */
     @Override
     public int getLength() {
         return getArity();
     }
 
     @Override
-    public String getFunctionName()
-    {
+    public String getFunctionName() {
         return (functionName == null) ? "" : functionName;
     }
 
-    /**
-     * Get Java method or constructor this function represent.
-     */
-    public Member getMethodOrConstructor()
-    {
+    /** Get Java method or constructor this function represent. */
+    public Member getMethodOrConstructor() {
         if (member.isMethod()) {
             return member.method();
         } else {
@@ -244,16 +216,14 @@
         }
     }
 
-    static Method findSingleMethod(Method[] methods, String name)
-    {
+    static Method findSingleMethod(Method[] methods, String name) {
         Method found = null;
         for (int i = 0, N = methods.length; i != N; ++i) {
             Method method = methods[i];
             if (method != null && name.equals(method.getName())) {
                 if (found != null) {
-                    throw Context.reportRuntimeError2(
-                        "msg.no.overload", name,
-                        method.getDeclaringClass().getName());
+                    throw Context.reportRuntimeErrorById(
+                            "msg.no.overload", name, method.getDeclaringClass().getName());
                 }
                 found = method;
             }
@@ -262,8 +232,7 @@
     }
 
     /**
-     * Returns all public methods declared by the specified class. This excludes
-     * inherited methods.
+     * Returns all public methods declared by the specified class. This excludes inherited methods.
      *
      * @param clazz the class from which to pull public declared methods
      * @return the public methods declared in the specified class
@@ -274,8 +243,7 @@
         try {
             // getDeclaredMethods may be rejected by the security manager
             // but getMethods is more expensive
-            if (!sawSecurityException)
-                methods = clazz.getDeclaredMethods();
+            if (!sawSecurityException) methods = clazz.getDeclaredMethods();
         } catch (SecurityException e) {
             // If we get an exception once, give up on getDeclaredMethods
             sawSecurityException = true;
@@ -284,123 +252,113 @@
             methods = clazz.getMethods();
         }
         int count = 0;
-        for (int i=0; i < methods.length; i++) {
+        for (int i = 0; i < methods.length; i++) {
             if (sawSecurityException
-                ? methods[i].getDeclaringClass() != clazz
-                : !Modifier.isPublic(methods[i].getModifiers()))
-            {
+                    ? methods[i].getDeclaringClass() != clazz
+                    : !Modifier.isPublic(methods[i].getModifiers())) {
                 methods[i] = null;
             } else {
                 count++;
             }
         }
         Method[] result = new Method[count];
-        int j=0;
-        for (int i=0; i < methods.length; i++) {
-            if (methods[i] != null)
-                result[j++] = methods[i];
+        int j = 0;
+        for (int i = 0; i < methods.length; i++) {
+            if (methods[i] != null) result[j++] = methods[i];
         }
         return result;
     }
 
     /**
      * Define this function as a JavaScript constructor.
-     * <p>
-     * Sets up the "prototype" and "constructor" properties. Also
-     * calls setParent and setPrototype with appropriate values.
-     * Then adds the function object as a property of the given scope, using
-     *      <code>prototype.getClassName()</code>
-     * as the name of the property.
      *
-     * @param scope the scope in which to define the constructor (typically
-     *              the global object)
+     * <p>Sets up the "prototype" and "constructor" properties. Also calls setParent and
+     * setPrototype with appropriate values. Then adds the function object as a property of the
+     * given scope, using <code>prototype.getClassName()</code> as the name of the property.
+     *
+     * @param scope the scope in which to define the constructor (typically the global object)
      * @param prototype the prototype object
      * @see org.mozilla.javascript.Scriptable#setParentScope
      * @see org.mozilla.javascript.Scriptable#setPrototype
      * @see org.mozilla.javascript.Scriptable#getClassName
      */
-    public void addAsConstructor(Scriptable scope, Scriptable prototype)
-    {
+    public void addAsConstructor(Scriptable scope, Scriptable prototype) {
         initAsConstructor(scope, prototype);
-        defineProperty(scope, prototype.getClassName(),
-                       this, ScriptableObject.DONTENUM);
+        defineProperty(scope, prototype.getClassName(), this, ScriptableObject.DONTENUM);
     }
 
-    void initAsConstructor(Scriptable scope, Scriptable prototype)
-    {
+    void initAsConstructor(Scriptable scope, Scriptable prototype) {
         ScriptRuntime.setFunctionProtoAndParent(this, scope);
         setImmunePrototypeProperty(prototype);
 
         prototype.setParentScope(this);
 
-        defineProperty(prototype, "constructor", this,
-                       ScriptableObject.DONTENUM  |
-                       ScriptableObject.PERMANENT |
-                       ScriptableObject.READONLY);
+        defineProperty(
+                prototype,
+                "constructor",
+                this,
+                ScriptableObject.DONTENUM | ScriptableObject.PERMANENT | ScriptableObject.READONLY);
         setParentScope(scope);
     }
 
     /**
-     * @deprecated Use {@link #getTypeTag(Class)}
-     * and {@link #convertArg(Context, Scriptable, Object, int)}
-     * for type conversion.
+     * @deprecated Use {@link #getTypeTag(Class)} and {@link #convertArg(Context, Scriptable,
+     *     Object, int)} for type conversion.
      */
     @Deprecated
-    public static Object convertArg(Context cx, Scriptable scope,
-                                    Object arg, Class<?> desired)
-    {
+    public static Object convertArg(Context cx, Scriptable scope, Object arg, Class<?> desired) {
         int tag = getTypeTag(desired);
         if (tag == JAVA_UNSUPPORTED_TYPE) {
-            throw Context.reportRuntimeError1
-                ("msg.cant.convert", desired.getName());
+            throw Context.reportRuntimeErrorById("msg.cant.convert", desired.getName());
         }
         return convertArg(cx, scope, arg, tag);
     }
 
     /**
-     * Performs conversions on argument types if needed and
-     * invokes the underlying Java method or constructor.
-     * <p>
-     * Implements Function.call.
+     * Performs conversions on argument types if needed and invokes the underlying Java method or
+     * constructor.
+     *
+     * <p>Implements Function.call.
      *
-     * @see org.mozilla.javascript.Function#call(
-     *          Context, Scriptable, Scriptable, Object[])
+     * @see org.mozilla.javascript.Function#call( Context, Scriptable, Scriptable, Object[])
      */
     @Override
-    public Object call(Context cx, Scriptable scope, Scriptable thisObj,
-                       Object[] args)
-    {
+    public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         Object result;
         boolean checkMethodResult = false;
         int argsLength = args.length;
 
-        for (int i = 0; i < argsLength; i++) {
-            // flatten cons-strings before passing them as arguments
-            if (args[i] instanceof ConsString) {
-                args[i] = args[i].toString();
+        if (parmsLength < 0) {
+            for (int i = 0; i < argsLength; i++) {
+                // flatten cons-strings before passing them as arguments
+                if (args[i] instanceof ConsString) {
+                    args[i] = args[i].toString();
+                }
             }
-        }
 
-        if (parmsLength < 0) {
             if (parmsLength == VARARGS_METHOD) {
-                Object[] invokeArgs = { cx, thisObj, args, this };
+                Object[] invokeArgs = {cx, thisObj, args, this};
                 result = member.invoke(null, invokeArgs);
                 checkMethodResult = true;
             } else {
                 boolean inNewExpr = (thisObj == null);
                 Boolean b = inNewExpr ? Boolean.TRUE : Boolean.FALSE;
-                Object[] invokeArgs = { cx, args, this, b };
-                result = (member.isCtor())
-                         ? member.newInstance(invokeArgs)
-                         : member.invoke(null, invokeArgs);
+                Object[] invokeArgs = {cx, args, this, b};
+                result =
+                        (member.isCtor())
+                                ? member.newInstance(invokeArgs)
+                                : member.invoke(null, invokeArgs);
             }
 
         } else {
             if (!isStatic) {
                 Class<?> clazz = member.getDeclaringClass();
+                if (thisObj instanceof Delegator) {
+                    thisObj = ((Delegator) thisObj).getDelegee();
+                }
                 if (!clazz.isInstance(thisObj)) {
                     boolean compatible = false;
-                    if (thisObj == scope) {
+                    if (thisObj == scope || thisObj instanceof ModuleScope) {
                         Scriptable parentScope = getParentScope();
                         if (scope != parentScope) {
                             // Call with dynamic scope for standalone function,
@@ -413,8 +371,7 @@
                     }
                     if (!compatible) {
                         // Couldn't find an object to call this on.
-                        throw ScriptRuntime.typeError1("msg.incompat.call",
-                                                       functionName);
+                        throw ScriptRuntime.typeErrorById("msg.incompat.call", functionName);
                     }
                 }
             }
@@ -439,9 +396,7 @@
             } else {
                 invokeArgs = new Object[parmsLength];
                 for (int i = 0; i != parmsLength; ++i) {
-                    Object arg = (i < argsLength)
-                                 ? args[i]
-                                 : Undefined.instance;
+                    Object arg = (i < argsLength) ? args[i] : Undefined.instance;
                     invokeArgs[i] = convertArg(cx, scope, arg, typeTags[i]);
                 }
             }
@@ -452,7 +407,6 @@
             } else {
                 result = member.newInstance(invokeArgs);
             }
-
         }
 
         if (checkMethodResult) {
@@ -471,9 +425,8 @@
     }
 
     /**
-     * Return new {@link Scriptable} instance using the default
-     * constructor for the class of the underlying Java method.
-     * Return null to indicate that the call method should be used to create
+     * Return new {@link Scriptable} instance using the default constructor for the class of the
+     * underlying Java method. Return null to indicate that the call method should be used to create
      * new objects.
      */
     @Override
@@ -501,15 +454,13 @@
         return parmsLength == VARARGS_CTOR;
     }
 
-    private void readObject(ObjectInputStream in)
-        throws IOException, ClassNotFoundException
-    {
+    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
         in.defaultReadObject();
         if (parmsLength > 0) {
             Class<?>[] types = member.argTypes;
             typeTags = new byte[parmsLength];
             for (int i = 0; i != parmsLength; ++i) {
-                typeTags[i] = (byte)getTypeTag(types[i]);
+                typeTags[i] = (byte) getTypeTag(types[i]);
             }
         }
         if (member.isMethod()) {
@@ -524,17 +475,17 @@
     }
 
     private static final short VARARGS_METHOD = -1;
-    private static final short VARARGS_CTOR =   -2;
+    private static final short VARARGS_CTOR = -2;
 
     private static boolean sawSecurityException;
 
     public static final int JAVA_UNSUPPORTED_TYPE = 0;
-    public static final int JAVA_STRING_TYPE      = 1;
-    public static final int JAVA_INT_TYPE         = 2;
-    public static final int JAVA_BOOLEAN_TYPE     = 3;
-    public static final int JAVA_DOUBLE_TYPE      = 4;
-    public static final int JAVA_SCRIPTABLE_TYPE  = 5;
-    public static final int JAVA_OBJECT_TYPE      = 6;
+    public static final int JAVA_STRING_TYPE = 1;
+    public static final int JAVA_INT_TYPE = 2;
+    public static final int JAVA_BOOLEAN_TYPE = 3;
+    public static final int JAVA_DOUBLE_TYPE = 4;
+    public static final int JAVA_SCRIPTABLE_TYPE = 5;
+    public static final int JAVA_OBJECT_TYPE = 6;
 
     MemberBox member;
     private String functionName;
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/HashSlotMap.java rhino-1.7.14/src/org/mozilla/javascript/HashSlotMap.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/HashSlotMap.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/HashSlotMap.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,97 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+
+/**
+ * This class implements the SlotMap interface using a java.util.HashMap. This class has more
+ * overhead than EmbeddedSlotMap, especially because it puts each "Slot" inside an intermediate
+ * object. However it is much more resistant to large number of hash collisions than EmbeddedSlotMap
+ * and therefore we use this implementation when an object gains a large number of properties.
+ */
+public class HashSlotMap implements SlotMap {
+
+    private final LinkedHashMap<Object, Slot> map = new LinkedHashMap<>();
+
+    @Override
+    public int size() {
+        return map.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return map.isEmpty();
+    }
+
+    @Override
+    public Slot query(Object key, int index) {
+        Object name = makeKey(key, index);
+        return map.get(name);
+    }
+
+    @Override
+    public Slot modify(Object key, int index, int attributes) {
+        Object name = makeKey(key, index);
+        Slot slot = map.get(name);
+        if (slot != null) {
+            return slot;
+        }
+
+        return createSlot(key, index, attributes);
+    }
+
+    @Override
+    public void replace(Slot oldSlot, Slot newSlot) {
+        Object name = makeKey(oldSlot);
+        map.put(name, newSlot);
+    }
+
+    private Slot createSlot(Object key, int index, int attributes) {
+        Slot newSlot = new Slot(key, index, attributes);
+        add(newSlot);
+        return newSlot;
+    }
+
+    @Override
+    public void add(Slot newSlot) {
+        Object name = makeKey(newSlot);
+        map.put(name, newSlot);
+    }
+
+    @Override
+    public void remove(Object key, int index) {
+        Object name = makeKey(key, index);
+        Slot slot = map.get(name);
+        if (slot != null) {
+            // non-configurable
+            if ((slot.getAttributes() & ScriptableObject.PERMANENT) != 0) {
+                Context cx = Context.getContext();
+                if (cx.isStrictMode()) {
+                    throw ScriptRuntime.typeErrorById(
+                            "msg.delete.property.with.configurable.false", key);
+                }
+                return;
+            }
+            map.remove(name);
+        }
+    }
+
+    @Override
+    public Iterator<Slot> iterator() {
+        return map.values().iterator();
+    }
+
+    private Object makeKey(Object name, int index) {
+        return name == null ? String.valueOf(index) : name;
+    }
+
+    private Object makeKey(Slot slot) {
+        return slot.name == null ? String.valueOf(slot.indexOrHash) : slot.name;
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/Hashtable.java rhino-1.7.14/src/org/mozilla/javascript/Hashtable.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/Hashtable.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/Hashtable.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,317 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+import java.io.Serializable;
+import java.math.BigInteger;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * This generic hash table class is used by Set and Map. It uses a standard HashMap for storing keys
+ * and values so that we can handle lots of hash collisions if necessary, and a doubly-linked list
+ * to support the iterator capability.
+ *
+ * <p>This second one is important because JavaScript handling of the iterator is completely
+ * different from the way that Java does it. In Java an attempt to modify a collection on a HashMap
+ * or LinkedHashMap while iterating through it (except by using the "remove" method on the Iterator
+ * object itself) results in a ConcurrentModificationException. JavaScript Maps and Sets explicitly
+ * allow the collection to be modified, or even cleared completely, while iterators exist, and even
+ * lets an iterator keep on iterating on a collection that was empty when it was created..
+ */
+public class Hashtable implements Serializable, Iterable<Hashtable.Entry> {
+
+    private static final long serialVersionUID = -7151554912419543747L;
+    private final HashMap<Object, Entry> map = new HashMap<>();
+    private Entry first = null;
+    private Entry last = null;
+
+    /**
+     * One entry in the hash table. Override equals and hashcode because this is another area in
+     * which JavaScript and Java differ. This entry also becomes a node in the linked list.
+     */
+    public static final class Entry implements Serializable {
+        private static final long serialVersionUID = 4086572107122965503L;
+        protected Object key;
+        protected Object value;
+        protected boolean deleted;
+        protected Entry next;
+        protected Entry prev;
+        private final int hashCode;
+
+        Entry() {
+            hashCode = 0;
+        }
+
+        Entry(Object k, Object value) {
+            if (k instanceof Number) {
+                if (k instanceof Double || k instanceof BigInteger) {
+                    // BigInteger needs to retain its own type, due to
+                    // "If Type(x) is different from Type(y), return false." in
+                    // ecma262/multipage/abstract-operations.html#sec-samevaluezero
+                    key = k;
+                } else {
+                    // Hash comparison won't work if we don't do this
+                    key = Double.valueOf(((Number) k).doubleValue());
+                }
+            } else if (k instanceof ConsString) {
+                key = k.toString();
+            } else {
+                key = k;
+            }
+
+            if (key == null) {
+                hashCode = 0;
+            } else if (k.equals(ScriptRuntime.negativeZeroObj)) {
+                hashCode = 0;
+            } else {
+                hashCode = key.hashCode();
+            }
+
+            this.value = value;
+        }
+
+        public Object key() {
+            return key;
+        }
+
+        public Object value() {
+            return value;
+        }
+
+        /** Zero out key and value and return old value. */
+        void clear() {
+            key = Undefined.instance;
+            value = Undefined.instance;
+            deleted = true;
+        }
+
+        @Override
+        public int hashCode() {
+            return hashCode;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o == null) {
+                return false;
+            }
+            try {
+                return ScriptRuntime.sameZero(key, ((Entry) o).key);
+            } catch (ClassCastException cce) {
+                return false;
+            }
+        }
+    }
+
+    private static Entry makeDummy() {
+        final Entry d = new Entry();
+        d.clear();
+        return d;
+    }
+
+    public int size() {
+        return map.size();
+    }
+
+    public void put(Object key, Object value) {
+        final Entry nv = new Entry(key, value);
+        final Entry ev = map.putIfAbsent(nv, nv);
+        if (ev == null) {
+            // New value -- insert to end of doubly-linked list
+            if (first == null) {
+                first = last = nv;
+            } else {
+                last.next = nv;
+                nv.prev = last;
+                last = nv;
+            }
+        } else {
+            // Update the existing value and keep it in the same place in the list
+            ev.value = value;
+        }
+    }
+
+    /**
+     * @deprecated use getEntry(Object key) instead because this returns null if the entry was not
+     *     found or the value of the entry is null
+     */
+    public Object get(Object key) {
+        final Entry e = new Entry(key, null);
+        final Entry v = map.get(e);
+        if (v == null) {
+            return null;
+        }
+        return v.value;
+    }
+
+    public Entry getEntry(Object key) {
+        final Entry e = new Entry(key, null);
+        return map.get(e);
+    }
+
+    public boolean has(Object key) {
+        final Entry e = new Entry(key, null);
+        return map.containsKey(e);
+    }
+
+    /**
+     * @deprecated use deleteEntry(Object key) instead because this returns null if the entry was
+     *     not found or the value of the entry is null
+     */
+    public Object delete(Object key) {
+        final Entry e = new Entry(key, null);
+        final Entry v = map.remove(e);
+        if (v == null) {
+            return null;
+        }
+
+        // To keep existing iterators moving forward as specified in EC262,
+        // we will remove the "prev" pointers from the list but leave the "next"
+        // pointers intact. Once we do that, then the only things pointing to
+        // the deleted nodes are existing iterators. Once those are gone, then
+        // these objects will be GCed.
+        // This way, new iterators will not "see" the deleted elements, and
+        // existing iterators will continue from wherever they left off to
+        // continue iterating in insertion order.
+        if (v == first) {
+            if (v == last) {
+                // Removing the only element. Leave it as a dummy or existing iterators
+                // will never stop.
+                v.clear();
+                v.prev = null;
+            } else {
+                first = v.next;
+                first.prev = null;
+                if (first.next != null) {
+                    first.next.prev = first;
+                }
+            }
+        } else {
+            final Entry prev = v.prev;
+            prev.next = v.next;
+            v.prev = null;
+            if (v.next != null) {
+                v.next.prev = prev;
+            } else {
+                assert (v == last);
+                last = prev;
+            }
+        }
+        // Still clear the node in case it is in the chain of some iterator
+        final Object ret = v.value;
+        v.clear();
+        return ret;
+    }
+
+    public boolean deleteEntry(Object key) {
+        final Entry e = new Entry(key, null);
+        final Entry v = map.remove(e);
+        if (v == null) {
+            return false;
+        }
+
+        // To keep existing iterators moving forward as specified in EC262,
+        // we will remove the "prev" pointers from the list but leave the "next"
+        // pointers intact. Once we do that, then the only things pointing to
+        // the deleted nodes are existing iterators. Once those are gone, then
+        // these objects will be GCed.
+        // This way, new iterators will not "see" the deleted elements, and
+        // existing iterators will continue from wherever they left off to
+        // continue iterating in insertion order.
+        if (v == first) {
+            if (v == last) {
+                // Removing the only element. Leave it as a dummy or existing iterators
+                // will never stop.
+                v.clear();
+                v.prev = null;
+            } else {
+                first = v.next;
+                first.prev = null;
+                if (first.next != null) {
+                    first.next.prev = first;
+                }
+            }
+        } else {
+            final Entry prev = v.prev;
+            prev.next = v.next;
+            v.prev = null;
+            if (v.next != null) {
+                v.next.prev = prev;
+            } else {
+                assert (v == last);
+                last = prev;
+            }
+        }
+        // Still clear the node in case it is in the chain of some iterator
+        v.clear();
+        return true;
+    }
+
+    public void clear() {
+        // Zero out all the entries so that existing iterators will skip them all
+        Iterator<Entry> it = iterator();
+        it.forEachRemaining(Entry::clear);
+
+        // Replace the existing list with a dummy, and make it the last node
+        // of the current list. If new nodes are added now, existing iterators
+        // will drive forward right into the new list. If they are not, then
+        // nothing is referencing the old list and it'll get GCed.
+        if (first != null) {
+            Entry dummy = makeDummy();
+            last.next = dummy;
+            first = last = dummy;
+        }
+
+        // Now we can clear the actual hashtable!
+        map.clear();
+    }
+
+    public Iterator<Entry> iterator() {
+        return new Iter(first);
+    }
+
+    // The iterator for this class works directly on the linked list so that it implements
+    // the specified iteration behavior, which is very different from Java.
+    private static final class Iter implements Iterator<Entry> {
+        private Entry pos;
+
+        Iter(Entry start) {
+            // Keep the logic simpler by having a dummy at the start
+            Entry dummy = makeDummy();
+            dummy.next = start;
+            this.pos = dummy;
+        }
+
+        private void skipDeleted() {
+            // Skip forward past deleted elements, which could appear due to
+            // "delete" or a "clear" operation after this iterator was created.
+            // End up just before the next non-deleted node.
+            while ((pos.next != null) && pos.next.deleted) {
+                pos = pos.next;
+            }
+        }
+
+        @Override
+        public boolean hasNext() {
+            skipDeleted();
+            return ((pos != null) && (pos.next != null));
+        }
+
+        @Override
+        public Entry next() {
+            skipDeleted();
+            if ((pos == null) || (pos.next == null)) {
+                throw new NoSuchElementException();
+            }
+            final Entry e = pos.next;
+            pos = pos.next;
+            return e;
+        }
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/Icode.java rhino-1.7.14/src/org/mozilla/javascript/Icode.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/Icode.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/Icode.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,140 +6,148 @@
 
 package org.mozilla.javascript;
 
-/**
- * Additional interpreter-specific codes
- */
+/** Additional interpreter-specific codes */
 abstract class Icode {
 
     static final int
 
-    // delete operator used on a name
-        Icode_DELNAME                    = 0,
+            // delete operator used on a name
+            Icode_DELNAME = 0,
 
-    // Stack: ... value1 -> ... value1 value1
-        Icode_DUP                       = -1,
+            // Stack: ... value1 -> ... value1 value1
+            Icode_DUP = -1,
 
-    // Stack: ... value2 value1 -> ... value2 value1 value2 value1
-        Icode_DUP2                      = -2,
+            // Stack: ... value2 value1 -> ... value2 value1 value2 value1
+            Icode_DUP2 = -2,
 
-    // Stack: ... value2 value1 -> ... value1 value2
-        Icode_SWAP                      = -3,
-
-    // Stack: ... value1 -> ...
-        Icode_POP                       = -4,
-
-    // Store stack top into return register and then pop it
-        Icode_POP_RESULT                = -5,
-
-    // To jump conditionally and pop additional stack value
-        Icode_IFEQ_POP                  = -6,
-
-    // various types of ++/--
-        Icode_VAR_INC_DEC               = -7,
-        Icode_NAME_INC_DEC              = -8,
-        Icode_PROP_INC_DEC              = -9,
-        Icode_ELEM_INC_DEC              = -10,
-        Icode_REF_INC_DEC               = -11,
-
-    // load/save scope from/to local
-        Icode_SCOPE_LOAD                = -12,
-        Icode_SCOPE_SAVE                = -13,
-
-        Icode_TYPEOFNAME                = -14,
-
-    // helper for function calls
-        Icode_NAME_AND_THIS             = -15,
-        Icode_PROP_AND_THIS             = -16,
-        Icode_ELEM_AND_THIS             = -17,
-        Icode_VALUE_AND_THIS            = -18,
-
-    // Create closure object for nested functions
-        Icode_CLOSURE_EXPR              = -19,
-        Icode_CLOSURE_STMT              = -20,
-
-    // Special calls
-        Icode_CALLSPECIAL               = -21,
-
-    // To return undefined value
-        Icode_RETUNDEF                  = -22,
-
-    // Exception handling implementation
-        Icode_GOSUB                     = -23,
-        Icode_STARTSUB                  = -24,
-        Icode_RETSUB                    = -25,
-
-    // To indicating a line number change in icodes.
-        Icode_LINE                      = -26,
-
-    // To store shorts and ints inline
-        Icode_SHORTNUMBER               = -27,
-        Icode_INTNUMBER                 = -28,
-
-    // To create and populate array to hold values for [] and {} literals
-        Icode_LITERAL_NEW               = -29,
-        Icode_LITERAL_SET               = -30,
-
-    // Array literal with skipped index like [1,,2]
-        Icode_SPARE_ARRAYLIT            = -31,
-
-    // Load index register to prepare for the following index operation
-        Icode_REG_IND_C0                = -32,
-        Icode_REG_IND_C1                = -33,
-        Icode_REG_IND_C2                = -34,
-        Icode_REG_IND_C3                = -35,
-        Icode_REG_IND_C4                = -36,
-        Icode_REG_IND_C5                = -37,
-        Icode_REG_IND1                  = -38,
-        Icode_REG_IND2                  = -39,
-        Icode_REG_IND4                  = -40,
-
-    // Load string register to prepare for the following string operation
-        Icode_REG_STR_C0                = -41,
-        Icode_REG_STR_C1                = -42,
-        Icode_REG_STR_C2                = -43,
-        Icode_REG_STR_C3                = -44,
-        Icode_REG_STR1                  = -45,
-        Icode_REG_STR2                  = -46,
-        Icode_REG_STR4                  = -47,
-
-    // Version of getvar/setvar that read var index directly from bytecode
-        Icode_GETVAR1                   = -48,
-        Icode_SETVAR1                   = -49,
-
-    // Load undefined
-        Icode_UNDEF                     = -50,
-        Icode_ZERO                      = -51,
-        Icode_ONE                       = -52,
-
-    // entrance and exit from .()
-       Icode_ENTERDQ                    = -53,
-       Icode_LEAVEDQ                    = -54,
-
-       Icode_TAIL_CALL                  = -55,
-
-    // Clear local to allow GC its context
-       Icode_LOCAL_CLEAR                = -56,
-
-    // Literal get/set
-       Icode_LITERAL_GETTER             = -57,
-       Icode_LITERAL_SETTER             = -58,
-
-    // const
-       Icode_SETCONST                   = -59,
-       Icode_SETCONSTVAR                = -60,
-       Icode_SETCONSTVAR1               = -61,
-
-    // Generator opcodes (along with Token.YIELD)
-       Icode_GENERATOR                  = -62,
-       Icode_GENERATOR_END              = -63,
+            // Stack: ... value2 value1 -> ... value1 value2
+            Icode_SWAP = -3,
+
+            // Stack: ... value1 -> ...
+            Icode_POP = -4,
+
+            // Store stack top into return register and then pop it
+            Icode_POP_RESULT = -5,
+
+            // To jump conditionally and pop additional stack value
+            Icode_IFEQ_POP = -6,
+
+            // various types of ++/--
+            Icode_VAR_INC_DEC = -7,
+            Icode_NAME_INC_DEC = -8,
+            Icode_PROP_INC_DEC = -9,
+            Icode_ELEM_INC_DEC = -10,
+            Icode_REF_INC_DEC = -11,
+
+            // load/save scope from/to local
+            Icode_SCOPE_LOAD = -12,
+            Icode_SCOPE_SAVE = -13,
+            Icode_TYPEOFNAME = -14,
+
+            // helper for function calls
+            Icode_NAME_AND_THIS = -15,
+            Icode_PROP_AND_THIS = -16,
+            Icode_ELEM_AND_THIS = -17,
+            Icode_VALUE_AND_THIS = -18,
+
+            // Create closure object for nested functions
+            Icode_CLOSURE_EXPR = -19,
+            Icode_CLOSURE_STMT = -20,
+
+            // Special calls
+            Icode_CALLSPECIAL = -21,
+
+            // To return undefined value
+            Icode_RETUNDEF = -22,
+
+            // Exception handling implementation
+            Icode_GOSUB = -23,
+            Icode_STARTSUB = -24,
+            Icode_RETSUB = -25,
+
+            // To indicating a line number change in icodes.
+            Icode_LINE = -26,
+
+            // To store shorts and ints inline
+            Icode_SHORTNUMBER = -27,
+            Icode_INTNUMBER = -28,
+
+            // To create and populate array to hold values for [] and {} literals
+            Icode_LITERAL_NEW = -29,
+            Icode_LITERAL_SET = -30,
+
+            // Array literal with skipped index like [1,,2]
+            Icode_SPARE_ARRAYLIT = -31,
+
+            // Load index register to prepare for the following index operation
+            Icode_REG_IND_C0 = -32,
+            Icode_REG_IND_C1 = -33,
+            Icode_REG_IND_C2 = -34,
+            Icode_REG_IND_C3 = -35,
+            Icode_REG_IND_C4 = -36,
+            Icode_REG_IND_C5 = -37,
+            Icode_REG_IND1 = -38,
+            Icode_REG_IND2 = -39,
+            Icode_REG_IND4 = -40,
+
+            // Load string register to prepare for the following string operation
+            Icode_REG_STR_C0 = -41,
+            Icode_REG_STR_C1 = -42,
+            Icode_REG_STR_C2 = -43,
+            Icode_REG_STR_C3 = -44,
+            Icode_REG_STR1 = -45,
+            Icode_REG_STR2 = -46,
+            Icode_REG_STR4 = -47,
+
+            // Version of getvar/setvar that read var index directly from bytecode
+            Icode_GETVAR1 = -48,
+            Icode_SETVAR1 = -49,
+
+            // Load undefined
+            Icode_UNDEF = -50,
+            Icode_ZERO = -51,
+            Icode_ONE = -52,
+
+            // entrance and exit from .()
+            Icode_ENTERDQ = -53,
+            Icode_LEAVEDQ = -54,
+            Icode_TAIL_CALL = -55,
+
+            // Clear local to allow GC its context
+            Icode_LOCAL_CLEAR = -56,
+
+            // Literal get/set
+            Icode_LITERAL_GETTER = -57,
+            Icode_LITERAL_SETTER = -58,
+
+            // const
+            Icode_SETCONST = -59,
+            Icode_SETCONSTVAR = -60,
+            Icode_SETCONSTVAR1 = -61,
+
+            // Generator opcodes (along with Token.YIELD)
+            Icode_GENERATOR = -62,
+            Icode_GENERATOR_END = -63,
+            Icode_DEBUGGER = -64,
+            Icode_GENERATOR_RETURN = -65,
+            Icode_YIELD_STAR = -66,
+
+            // Load BigInt register to prepare for the following BigInt operation
+            Icode_REG_BIGINT_C0 = -67,
+            Icode_REG_BIGINT_C1 = -68,
+            Icode_REG_BIGINT_C2 = -69,
+            Icode_REG_BIGINT_C3 = -70,
+            Icode_REG_BIGINT1 = -71,
+            Icode_REG_BIGINT2 = -72,
+            Icode_REG_BIGINT4 = -73,
 
-       Icode_DEBUGGER                   = -64,
+            // Call to GetTemplateLiteralCallSite
+            Icode_TEMPLATE_LITERAL_CALLSITE = -74,
 
-       // Last icode
-        MIN_ICODE                       = -64;
+            // Last icode
+            MIN_ICODE = -74;
 
-    static String bytecodeName(int bytecode)
-    {
+    static String bytecodeName(int bytecode) {
         if (!validBytecode(bytecode)) {
             throw new IllegalArgumentException(String.valueOf(bytecode));
         }
@@ -153,89 +161,169 @@
         }
 
         switch (bytecode) {
-          case Icode_DUP:              return "DUP";
-          case Icode_DUP2:             return "DUP2";
-          case Icode_SWAP:             return "SWAP";
-          case Icode_POP:              return "POP";
-          case Icode_POP_RESULT:       return "POP_RESULT";
-          case Icode_IFEQ_POP:         return "IFEQ_POP";
-          case Icode_VAR_INC_DEC:      return "VAR_INC_DEC";
-          case Icode_NAME_INC_DEC:     return "NAME_INC_DEC";
-          case Icode_PROP_INC_DEC:     return "PROP_INC_DEC";
-          case Icode_ELEM_INC_DEC:     return "ELEM_INC_DEC";
-          case Icode_REF_INC_DEC:      return "REF_INC_DEC";
-          case Icode_SCOPE_LOAD:       return "SCOPE_LOAD";
-          case Icode_SCOPE_SAVE:       return "SCOPE_SAVE";
-          case Icode_TYPEOFNAME:       return "TYPEOFNAME";
-          case Icode_NAME_AND_THIS:    return "NAME_AND_THIS";
-          case Icode_PROP_AND_THIS:    return "PROP_AND_THIS";
-          case Icode_ELEM_AND_THIS:    return "ELEM_AND_THIS";
-          case Icode_VALUE_AND_THIS:   return "VALUE_AND_THIS";
-          case Icode_CLOSURE_EXPR:     return "CLOSURE_EXPR";
-          case Icode_CLOSURE_STMT:     return "CLOSURE_STMT";
-          case Icode_CALLSPECIAL:      return "CALLSPECIAL";
-          case Icode_RETUNDEF:         return "RETUNDEF";
-          case Icode_GOSUB:            return "GOSUB";
-          case Icode_STARTSUB:         return "STARTSUB";
-          case Icode_RETSUB:           return "RETSUB";
-          case Icode_LINE:             return "LINE";
-          case Icode_SHORTNUMBER:      return "SHORTNUMBER";
-          case Icode_INTNUMBER:        return "INTNUMBER";
-          case Icode_LITERAL_NEW:      return "LITERAL_NEW";
-          case Icode_LITERAL_SET:      return "LITERAL_SET";
-          case Icode_SPARE_ARRAYLIT:   return "SPARE_ARRAYLIT";
-          case Icode_REG_IND_C0:       return "REG_IND_C0";
-          case Icode_REG_IND_C1:       return "REG_IND_C1";
-          case Icode_REG_IND_C2:       return "REG_IND_C2";
-          case Icode_REG_IND_C3:       return "REG_IND_C3";
-          case Icode_REG_IND_C4:       return "REG_IND_C4";
-          case Icode_REG_IND_C5:       return "REG_IND_C5";
-          case Icode_REG_IND1:         return "LOAD_IND1";
-          case Icode_REG_IND2:         return "LOAD_IND2";
-          case Icode_REG_IND4:         return "LOAD_IND4";
-          case Icode_REG_STR_C0:       return "REG_STR_C0";
-          case Icode_REG_STR_C1:       return "REG_STR_C1";
-          case Icode_REG_STR_C2:       return "REG_STR_C2";
-          case Icode_REG_STR_C3:       return "REG_STR_C3";
-          case Icode_REG_STR1:         return "LOAD_STR1";
-          case Icode_REG_STR2:         return "LOAD_STR2";
-          case Icode_REG_STR4:         return "LOAD_STR4";
-          case Icode_GETVAR1:          return "GETVAR1";
-          case Icode_SETVAR1:          return "SETVAR1";
-          case Icode_UNDEF:            return "UNDEF";
-          case Icode_ZERO:             return "ZERO";
-          case Icode_ONE:              return "ONE";
-          case Icode_ENTERDQ:          return "ENTERDQ";
-          case Icode_LEAVEDQ:          return "LEAVEDQ";
-          case Icode_TAIL_CALL:        return "TAIL_CALL";
-          case Icode_LOCAL_CLEAR:      return "LOCAL_CLEAR";
-          case Icode_LITERAL_GETTER:   return "LITERAL_GETTER";
-          case Icode_LITERAL_SETTER:   return "LITERAL_SETTER";
-          case Icode_SETCONST:         return "SETCONST";
-          case Icode_SETCONSTVAR:      return "SETCONSTVAR";
-          case Icode_SETCONSTVAR1:     return "SETCONSTVAR1";
-          case Icode_GENERATOR:        return "GENERATOR";
-          case Icode_GENERATOR_END:    return "GENERATOR_END";
-          case Icode_DEBUGGER:         return "DEBUGGER";
+            case Icode_DUP:
+                return "DUP";
+            case Icode_DUP2:
+                return "DUP2";
+            case Icode_SWAP:
+                return "SWAP";
+            case Icode_POP:
+                return "POP";
+            case Icode_POP_RESULT:
+                return "POP_RESULT";
+            case Icode_IFEQ_POP:
+                return "IFEQ_POP";
+            case Icode_VAR_INC_DEC:
+                return "VAR_INC_DEC";
+            case Icode_NAME_INC_DEC:
+                return "NAME_INC_DEC";
+            case Icode_PROP_INC_DEC:
+                return "PROP_INC_DEC";
+            case Icode_ELEM_INC_DEC:
+                return "ELEM_INC_DEC";
+            case Icode_REF_INC_DEC:
+                return "REF_INC_DEC";
+            case Icode_SCOPE_LOAD:
+                return "SCOPE_LOAD";
+            case Icode_SCOPE_SAVE:
+                return "SCOPE_SAVE";
+            case Icode_TYPEOFNAME:
+                return "TYPEOFNAME";
+            case Icode_NAME_AND_THIS:
+                return "NAME_AND_THIS";
+            case Icode_PROP_AND_THIS:
+                return "PROP_AND_THIS";
+            case Icode_ELEM_AND_THIS:
+                return "ELEM_AND_THIS";
+            case Icode_VALUE_AND_THIS:
+                return "VALUE_AND_THIS";
+            case Icode_CLOSURE_EXPR:
+                return "CLOSURE_EXPR";
+            case Icode_CLOSURE_STMT:
+                return "CLOSURE_STMT";
+            case Icode_CALLSPECIAL:
+                return "CALLSPECIAL";
+            case Icode_RETUNDEF:
+                return "RETUNDEF";
+            case Icode_GOSUB:
+                return "GOSUB";
+            case Icode_STARTSUB:
+                return "STARTSUB";
+            case Icode_RETSUB:
+                return "RETSUB";
+            case Icode_LINE:
+                return "LINE";
+            case Icode_SHORTNUMBER:
+                return "SHORTNUMBER";
+            case Icode_INTNUMBER:
+                return "INTNUMBER";
+            case Icode_LITERAL_NEW:
+                return "LITERAL_NEW";
+            case Icode_LITERAL_SET:
+                return "LITERAL_SET";
+            case Icode_SPARE_ARRAYLIT:
+                return "SPARE_ARRAYLIT";
+            case Icode_REG_IND_C0:
+                return "REG_IND_C0";
+            case Icode_REG_IND_C1:
+                return "REG_IND_C1";
+            case Icode_REG_IND_C2:
+                return "REG_IND_C2";
+            case Icode_REG_IND_C3:
+                return "REG_IND_C3";
+            case Icode_REG_IND_C4:
+                return "REG_IND_C4";
+            case Icode_REG_IND_C5:
+                return "REG_IND_C5";
+            case Icode_REG_IND1:
+                return "LOAD_IND1";
+            case Icode_REG_IND2:
+                return "LOAD_IND2";
+            case Icode_REG_IND4:
+                return "LOAD_IND4";
+            case Icode_REG_STR_C0:
+                return "REG_STR_C0";
+            case Icode_REG_STR_C1:
+                return "REG_STR_C1";
+            case Icode_REG_STR_C2:
+                return "REG_STR_C2";
+            case Icode_REG_STR_C3:
+                return "REG_STR_C3";
+            case Icode_REG_STR1:
+                return "LOAD_STR1";
+            case Icode_REG_STR2:
+                return "LOAD_STR2";
+            case Icode_REG_STR4:
+                return "LOAD_STR4";
+            case Icode_GETVAR1:
+                return "GETVAR1";
+            case Icode_SETVAR1:
+                return "SETVAR1";
+            case Icode_UNDEF:
+                return "UNDEF";
+            case Icode_ZERO:
+                return "ZERO";
+            case Icode_ONE:
+                return "ONE";
+            case Icode_ENTERDQ:
+                return "ENTERDQ";
+            case Icode_LEAVEDQ:
+                return "LEAVEDQ";
+            case Icode_TAIL_CALL:
+                return "TAIL_CALL";
+            case Icode_LOCAL_CLEAR:
+                return "LOCAL_CLEAR";
+            case Icode_LITERAL_GETTER:
+                return "LITERAL_GETTER";
+            case Icode_LITERAL_SETTER:
+                return "LITERAL_SETTER";
+            case Icode_SETCONST:
+                return "SETCONST";
+            case Icode_SETCONSTVAR:
+                return "SETCONSTVAR";
+            case Icode_SETCONSTVAR1:
+                return "SETCONSTVAR1";
+            case Icode_GENERATOR:
+                return "GENERATOR";
+            case Icode_GENERATOR_END:
+                return "GENERATOR_END";
+            case Icode_DEBUGGER:
+                return "DEBUGGER";
+            case Icode_GENERATOR_RETURN:
+                return "GENERATOR_RETURN";
+            case Icode_YIELD_STAR:
+                return "YIELD_STAR";
+            case Icode_REG_BIGINT_C0:
+                return "REG_BIGINT_C0";
+            case Icode_REG_BIGINT_C1:
+                return "REG_BIGINT_C1";
+            case Icode_REG_BIGINT_C2:
+                return "REG_BIGINT_C2";
+            case Icode_REG_BIGINT_C3:
+                return "REG_BIGINT_C3";
+            case Icode_REG_BIGINT1:
+                return "LOAD_BIGINT1";
+            case Icode_REG_BIGINT2:
+                return "LOAD_BIGINT2";
+            case Icode_REG_BIGINT4:
+                return "LOAD_BIGINT4";
+            case Icode_TEMPLATE_LITERAL_CALLSITE:
+                return "TEMPLATE_LITERAL_CALLSITE";
         }
 
         // icode without name
         throw new IllegalStateException(String.valueOf(bytecode));
     }
 
-    static boolean validIcode(int icode)
-    {
+    static boolean validIcode(int icode) {
         return MIN_ICODE <= icode && icode <= 0;
     }
 
-    static boolean validTokenCode(int token)
-    {
-        return Token.FIRST_BYTECODE_TOKEN <= token
-               && token <= Token.LAST_BYTECODE_TOKEN;
+    static boolean validTokenCode(int token) {
+        return Token.FIRST_BYTECODE_TOKEN <= token && token <= Token.LAST_BYTECODE_TOKEN;
     }
 
-    static boolean validBytecode(int bytecode)
-    {
+    static boolean validBytecode(int bytecode) {
         return validIcode(bytecode) || validTokenCode(bytecode);
     }
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/IdFunctionObjectES6.java rhino-1.7.14/src/org/mozilla/javascript/IdFunctionObjectES6.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/IdFunctionObjectES6.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/IdFunctionObjectES6.java	2022-01-06 22:57:21.000000000 +0100
@@ -2,14 +2,13 @@
 
 public class IdFunctionObjectES6 extends IdFunctionObject{
 
+    private static final long serialVersionUID = -8023088662589035261L;
+
     public IdFunctionObjectES6(IdFunctionCall idcall, Object tag, int id, String name, int arity, Scriptable scope) {
         super(idcall, tag, id, name, arity, scope);
     }
 
-    private static final int
-        Id_length       = 1,
-        Id_name         = 3;
-
+    private static final int Id_length = 1, Id_name = 3;
     private boolean myLength = true;
     private boolean myName = true;
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/IdFunctionObject.java rhino-1.7.14/src/org/mozilla/javascript/IdFunctionObject.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/IdFunctionObject.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/IdFunctionObject.java	2022-01-06 22:57:21.000000000 +0100
@@ -11,7 +11,7 @@
 public class IdFunctionObject extends BaseFunction
 {
 
-    static final long serialVersionUID = -5332312783643935019L;
+    private static final long serialVersionUID = -5332312783643935019L;
 
     public IdFunctionObject(IdFunctionCall idcall, Object tag, int id, int arity)
     {
@@ -22,7 +22,6 @@
         this.tag = tag;
         this.methodId = id;
         this.arity = arity;
-        if (arity < 0) throw new IllegalArgumentException();
     }
 
     public IdFunctionObject(IdFunctionCall idcall, Object tag, int id,
@@ -111,7 +110,7 @@
         // to satisfy ECMAScript standard (see bugzilla 202019).
         // To follow current (2003-05-01) SpiderMonkey behavior, change it to:
         // return super.createObject(cx, scope);
-        throw ScriptRuntime.typeError1("msg.not.ctor", functionName);
+        throw ScriptRuntime.typeErrorById("msg.not.ctor", functionName);
     }
 
     @Override
@@ -159,6 +158,10 @@
             "BAD FUNCTION ID="+methodId+" MASTER="+idcall);
     }
 
+    static boolean equalObjectGraphs(IdFunctionObject f1, IdFunctionObject f2, EqualObjectGraphs eq) {
+        return f1.methodId == f2.methodId && f1.hasTag(f2.tag) && eq.equalGraphs(f1.idcall, f2.idcall);
+    }
+
     private final IdFunctionCall idcall;
     private final Object tag;
     private final int methodId;
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/IdScriptableObject.java rhino-1.7.14/src/org/mozilla/javascript/IdScriptableObject.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/IdScriptableObject.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/IdScriptableObject.java	2022-01-06 22:57:21.000000000 +0100
@@ -12,32 +12,24 @@
 import java.io.Serializable;
 
 /**
-Base class for native object implementation that uses IdFunctionObject to export its methods to script via <class-name>.prototype object.
-
-Any descendant should implement at least the following methods:
-    findInstanceIdInfo
-    getInstanceIdName
-    execIdCall
-    methodArity
-
-To define non-function properties, the descendant should override
-    getInstanceIdValue
-    setInstanceIdValue
-to get/set property value and provide its default attributes.
-
-
-To customize initialization of constructor and prototype objects, descendant
-may override scopeInit or fillConstructorProperties methods.
-
-*/
-public abstract class IdScriptableObject extends ScriptableObject
-    implements IdFunctionCall
-{
+ * Base class for native object implementation that uses IdFunctionObject to export its methods to
+ * script via <class-name>.prototype object.
+ *
+ * <p>Any descendant should implement at least the following methods: findInstanceIdInfo
+ * getInstanceIdName execIdCall methodArity
+ *
+ * <p>To define non-function properties, the descendant should override getInstanceIdValue
+ * setInstanceIdValue to get/set property value and provide its default attributes.
+ *
+ * <p>To customize initialization of constructor and prototype objects, descendant may override
+ * scopeInit or fillConstructorProperties methods.
+ */
+public abstract class IdScriptableObject extends ScriptableObject implements IdFunctionCall {
+    private static final long serialVersionUID = -3744239272168621609L;
     private transient PrototypeValues prototypeValues;
 
-    private static final class PrototypeValues implements Serializable
-    {
-        static final long serialVersionUID = 3038645279153854371L;
+    private static final class PrototypeValues implements Serializable {
+        private static final long serialVersionUID = 3038645279153854371L;
 
         private static final int NAME_SLOT = 1;
         private static final int SLOT_SPAN = 2;
@@ -53,73 +45,60 @@
         private IdFunctionObject constructor;
         private short constructorAttrs;
 
-        PrototypeValues(IdScriptableObject obj, int maxId)
-        {
+        PrototypeValues(IdScriptableObject obj, int maxId) {
             if (obj == null) throw new IllegalArgumentException();
             if (maxId < 1) throw new IllegalArgumentException();
             this.obj = obj;
             this.maxId = maxId;
         }
 
-        final int getMaxId()
-        {
+        final int getMaxId() {
             return maxId;
         }
 
-        final void initValue(int id, String name, Object value, int attributes)
-        {
-            if (!(1 <= id && id <= maxId))
-                throw new IllegalArgumentException();
-            if (name == null)
-                throw new IllegalArgumentException();
-            if (value == NOT_FOUND)
-                throw new IllegalArgumentException();
+        final void initValue(int id, String name, Object value, int attributes) {
+            if (!(1 <= id && id <= maxId)) throw new IllegalArgumentException();
+            if (name == null) throw new IllegalArgumentException();
+            if (value == NOT_FOUND) throw new IllegalArgumentException();
             ScriptableObject.checkValidAttributes(attributes);
-            if (obj.findPrototypeId(name) != id)
-                throw new IllegalArgumentException(name);
+            if (obj.findPrototypeId(name) != id) throw new IllegalArgumentException(name);
 
             if (id == constructorId) {
                 if (!(value instanceof IdFunctionObject)) {
-                    throw new IllegalArgumentException("consructor should be initialized with IdFunctionObject");
+                    throw new IllegalArgumentException(
+                            "consructor should be initialized with IdFunctionObject");
                 }
-                constructor = (IdFunctionObject)value;
-                constructorAttrs = (short)attributes;
+                constructor = (IdFunctionObject) value;
+                constructorAttrs = (short) attributes;
                 return;
             }
 
             initSlot(id, name, value, attributes);
         }
 
-        final void initValue(int id, Symbol key, Object value, int attributes)
-        {
-            if (!(1 <= id && id <= maxId))
-                throw new IllegalArgumentException();
-            if (key == null)
-                throw new IllegalArgumentException();
-            if (value == NOT_FOUND)
-                throw new IllegalArgumentException();
+        final void initValue(int id, Symbol key, Object value, int attributes) {
+            if (!(1 <= id && id <= maxId)) throw new IllegalArgumentException();
+            if (key == null) throw new IllegalArgumentException();
+            if (value == NOT_FOUND) throw new IllegalArgumentException();
             ScriptableObject.checkValidAttributes(attributes);
-            if (obj.findPrototypeId(key) != id)
-                throw new IllegalArgumentException(key.toString());
+            if (obj.findPrototypeId(key) != id) throw new IllegalArgumentException(key.toString());
 
             if (id == constructorId) {
                 if (!(value instanceof IdFunctionObject)) {
-                    throw new IllegalArgumentException("consructor should be initialized with IdFunctionObject");
+                    throw new IllegalArgumentException(
+                            "consructor should be initialized with IdFunctionObject");
                 }
-                constructor = (IdFunctionObject)value;
-                constructorAttrs = (short)attributes;
+                constructor = (IdFunctionObject) value;
+                constructorAttrs = (short) attributes;
                 return;
             }
 
-            initSlot(id, "", value, attributes);
+            initSlot(id, key, value, attributes);
         }
 
-        private void initSlot(int id, String name, Object value,
-                              int attributes)
-        {
+        private void initSlot(int id, Object name, Object value, int attributes) {
             Object[] array = valueArray;
-            if (array == null)
-                throw new IllegalStateException();
+            if (array == null) throw new IllegalStateException();
 
             if (value == null) {
                 value = UniqueTag.NULL_VALUE;
@@ -130,52 +109,47 @@
                 if (value2 == null) {
                     array[index] = value;
                     array[index + NAME_SLOT] = name;
-                    attributeArray[id - 1] = (short)attributes;
+                    attributeArray[id - 1] = (short) attributes;
                 } else {
-                    if (!name.equals(array[index + NAME_SLOT]))
-                         throw new IllegalStateException();
+                    if (!name.equals(array[index + NAME_SLOT])) throw new IllegalStateException();
                 }
             }
         }
 
-        final IdFunctionObject createPrecachedConstructor()
-        {
+        final IdFunctionObject createPrecachedConstructor() {
             if (constructorId != 0) throw new IllegalStateException();
             constructorId = obj.findPrototypeId("constructor");
             if (constructorId == 0) {
-                throw new IllegalStateException(
-                    "No id for constructor property");
+                throw new IllegalStateException("No id for constructor property");
             }
             obj.initPrototypeId(constructorId);
             if (constructor == null) {
                 throw new IllegalStateException(
-                    obj.getClass().getName()+".initPrototypeId() did not "
-                    +"initialize id="+constructorId);
+                        obj.getClass().getName()
+                                + ".initPrototypeId() did not "
+                                + "initialize id="
+                                + constructorId);
             }
-            constructor.initFunction(obj.getClassName(),
-                                     ScriptableObject.getTopLevelScope(obj));
+            constructor.initFunction(obj.getClassName(), ScriptableObject.getTopLevelScope(obj));
             constructor.markAsConstructor(obj);
             return constructor;
         }
 
-        final int findId(String name)
-        {
+        final int findId(String name) {
             return obj.findPrototypeId(name);
         }
 
-        final int findId(Symbol key)
-        {
+        final int findId(Symbol key) {
             return obj.findPrototypeId(key);
         }
 
-        final boolean has(int id)
-        {
+        final boolean has(int id) {
             Object[] array = valueArray;
             if (array == null) {
                 // Not yet initialized, assume all exists
                 return true;
             }
-            int valueSlot = (id  - 1) * SLOT_SPAN;
+            int valueSlot = (id - 1) * SLOT_SPAN;
             Object value = array[valueSlot];
             if (value == null) {
                 // The particular entry has not been yet initialized
@@ -184,8 +158,7 @@
             return value != NOT_FOUND;
         }
 
-        final Object get(int id)
-        {
+        final Object get(int id) {
             Object value = ensureId(id);
             if (value == UniqueTag.NULL_VALUE) {
                 value = null;
@@ -193,8 +166,7 @@
             return value;
         }
 
-        final void set(int id, Scriptable start, Object value)
-        {
+        final void set(int id, Scriptable start, Object value) {
             if (value == NOT_FOUND) throw new IllegalArgumentException();
             ensureId(id);
             int attr = attributeArray[id - 1];
@@ -203,33 +175,38 @@
                     if (value == null) {
                         value = UniqueTag.NULL_VALUE;
                     }
-                    int valueSlot = (id  - 1) * SLOT_SPAN;
+                    int valueSlot = (id - 1) * SLOT_SPAN;
                     synchronized (this) {
                         valueArray[valueSlot] = value;
                     }
-                }
-                else {
-                    int nameSlot = (id  - 1) * SLOT_SPAN + NAME_SLOT;
-                    String name = (String)valueArray[nameSlot];
-                    start.put(name, start, value);
+                } else {
+                    int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT;
+                    Object name = valueArray[nameSlot];
+                    if (name instanceof Symbol) {
+                        if (start instanceof SymbolScriptable) {
+                            ((SymbolScriptable) start).put((Symbol) name, start, value);
+                        }
+                    } else {
+                        start.put((String) name, start, value);
+                    }
                 }
             }
         }
 
-        final void delete(int id)
-        {
+        final void delete(int id) {
             ensureId(id);
             int attr = attributeArray[id - 1];
             // non-configurable
             if ((attr & PERMANENT) != 0) {
                 Context cx = Context.getContext();
                 if (cx.isStrictMode()) {
-                    int nameSlot = (id  - 1) * SLOT_SPAN + NAME_SLOT;
-                    String name = (String)valueArray[nameSlot];
-                    throw ScriptRuntime.typeError1("msg.delete.property.with.configurable.false", name);
+                    int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT;
+                    String name = (String) valueArray[nameSlot];
+                    throw ScriptRuntime.typeErrorById(
+                            "msg.delete.property.with.configurable.false", name);
                 }
             } else {
-                int valueSlot = (id  - 1) * SLOT_SPAN;
+                int valueSlot = (id - 1) * SLOT_SPAN;
                 synchronized (this) {
                     valueArray[valueSlot] = NOT_FOUND;
                     attributeArray[id - 1] = EMPTY;
@@ -237,35 +214,39 @@
             }
         }
 
-        final int getAttributes(int id)
-        {
+        final int getAttributes(int id) {
             ensureId(id);
             return attributeArray[id - 1];
         }
 
-        final void setAttributes(int id, int attributes)
-        {
+        final void setAttributes(int id, int attributes) {
             ScriptableObject.checkValidAttributes(attributes);
             ensureId(id);
             synchronized (this) {
-                attributeArray[id - 1] = (short)attributes;
+                attributeArray[id - 1] = (short) attributes;
             }
         }
 
-        final Object[] getNames(boolean getAll, Object[] extraEntries)
-        {
+        final Object[] getNames(boolean getAll, boolean getSymbols, Object[] extraEntries) {
             Object[] names = null;
             int count = 0;
             for (int id = 1; id <= maxId; ++id) {
                 Object value = ensureId(id);
                 if (getAll || (attributeArray[id - 1] & DONTENUM) == 0) {
                     if (value != NOT_FOUND) {
-                        int nameSlot = (id  - 1) * SLOT_SPAN + NAME_SLOT;
-                        String name = (String)valueArray[nameSlot];
-                        if (names == null) {
-                            names = new Object[maxId];
+                        int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT;
+                        Object name = valueArray[nameSlot];
+                        if (name instanceof String) {
+                            if (names == null) {
+                                names = new Object[maxId];
+                            }
+                            names[count++] = name;
+                        } else if (getSymbols && (name instanceof Symbol)) {
+                            if (names == null) {
+                                names = new Object[maxId];
+                            }
+                            names[count++] = name.toString();
                         }
-                        names[count++] = name;
                     }
                 }
             }
@@ -287,8 +268,7 @@
             }
         }
 
-        private Object ensureId(int id)
-        {
+        private Object ensureId(int id) {
             Object[] array = valueArray;
             if (array == null) {
                 synchronized (this) {
@@ -300,12 +280,11 @@
                     }
                 }
             }
-            int valueSlot = (id  - 1) * SLOT_SPAN;
+            int valueSlot = (id - 1) * SLOT_SPAN;
             Object value = array[valueSlot];
             if (value == null) {
                 if (id == constructorId) {
-                    initSlot(constructorId, "constructor",
-                             constructor, constructorAttrs);
+                    initSlot(constructorId, "constructor", constructor, constructorAttrs);
                     constructor = null; // no need to refer it any longer
                 } else {
                     obj.initPrototypeId(id);
@@ -313,41 +292,36 @@
                 value = array[valueSlot];
                 if (value == null) {
                     throw new IllegalStateException(
-                        obj.getClass().getName()+".initPrototypeId(int id) "
-                        +"did not initialize id="+id);
+                            obj.getClass().getName()
+                                    + ".initPrototypeId(int id) "
+                                    + "did not initialize id="
+                                    + id);
                 }
             }
             return value;
         }
     }
 
-    public IdScriptableObject()
-    {
-    }
+    public IdScriptableObject() {}
 
-    public IdScriptableObject(Scriptable scope, Scriptable prototype)
-    {
+    public IdScriptableObject(Scriptable scope, Scriptable prototype) {
         super(scope, prototype);
     }
 
-    protected final boolean defaultHas(String name)
-    {
+    protected final boolean defaultHas(String name) {
         return super.has(name, this);
     }
 
-    protected final Object defaultGet(String name)
-    {
+    protected final Object defaultGet(String name) {
         return super.get(name, this);
     }
 
-    protected final void defaultPut(String name, Object value)
-    {
+    protected final void defaultPut(String name, Object value) {
         super.put(name, this, value);
     }
 
     @Override
-    public boolean has(String name, Scriptable start)
-    {
+    public boolean has(String name, Scriptable start) {
         int info = findInstanceIdInfo(name);
         if (info != 0) {
             int attr = (info >>> 16);
@@ -366,10 +340,8 @@
         return super.has(name, start);
     }
 
-
     @Override
-    public boolean has(Symbol key, Scriptable start)
-    {
+    public boolean has(Symbol key, Scriptable start) {
         int info = findInstanceIdInfo(key);
         if (info != 0) {
             int attr = (info >>> 16);
@@ -389,8 +361,7 @@
     }
 
     @Override
-    public Object get(String name, Scriptable start)
-    {
+    public Object get(String name, Scriptable start) {
         // Check for slot first for performance. This is a very hot code
         // path that should be further optimized.
         Object value = super.get(name, start);
@@ -414,8 +385,7 @@
     }
 
     @Override
-    public Object get(Symbol key, Scriptable start)
-    {
+    public Object get(Symbol key, Scriptable start) {
         Object value = super.get(key, start);
         if (value != NOT_FOUND) {
             return value;
@@ -437,21 +407,18 @@
     }
 
     @Override
-    public void put(String name, Scriptable start, Object value)
-    {
+    public void put(String name, Scriptable start, Object value) {
         int info = findInstanceIdInfo(name);
         if (info != 0) {
             if (start == this && isSealed()) {
-                throw Context.reportRuntimeError1("msg.modify.sealed",
-                                                  name);
+                throw Context.reportRuntimeErrorById("msg.modify.sealed", name);
             }
             int attr = (info >>> 16);
             if ((attr & READONLY) == 0) {
                 if (start == this) {
                     int id = (info & 0xFFFF);
                     setInstanceIdValue(id, value);
-                }
-                else {
+                } else {
                     start.put(name, start, value);
                 }
             }
@@ -461,8 +428,7 @@
             int id = prototypeValues.findId(name);
             if (id != 0) {
                 if (start == this && isSealed()) {
-                    throw Context.reportRuntimeError1("msg.modify.sealed",
-                                                      name);
+                    throw Context.reportRuntimeErrorById("msg.modify.sealed", name);
                 }
                 prototypeValues.set(id, start, value);
                 return;
@@ -472,20 +438,18 @@
     }
 
     @Override
-    public void put(Symbol key, Scriptable start, Object value)
-    {
+    public void put(Symbol key, Scriptable start, Object value) {
         int info = findInstanceIdInfo(key);
         if (info != 0) {
             if (start == this && isSealed()) {
-                throw Context.reportRuntimeError0("msg.modify.sealed");
+                throw Context.reportRuntimeErrorById("msg.modify.sealed");
             }
             int attr = (info >>> 16);
             if ((attr & READONLY) == 0) {
                 if (start == this) {
                     int id = (info & 0xFFFF);
                     setInstanceIdValue(id, value);
-                }
-                else {
+                } else {
                     ensureSymbolScriptable(start).put(key, start, value);
                 }
             }
@@ -495,7 +459,7 @@
             int id = prototypeValues.findId(key);
             if (id != 0) {
                 if (start == this && isSealed()) {
-                    throw Context.reportRuntimeError0("msg.modify.sealed");
+                    throw Context.reportRuntimeErrorById("msg.modify.sealed");
                 }
                 prototypeValues.set(id, start, value);
                 return;
@@ -505,8 +469,7 @@
     }
 
     @Override
-    public void delete(String name)
-    {
+    public void delete(String name) {
         int info = findInstanceIdInfo(name);
         if (info != 0) {
             // Let the super class to throw exceptions for sealed objects
@@ -516,7 +479,8 @@
                 if ((attr & PERMANENT) != 0) {
                     Context cx = Context.getContext();
                     if (cx.isStrictMode()) {
-                        throw ScriptRuntime.typeError1("msg.delete.property.with.configurable.false", name);
+                        throw ScriptRuntime.typeErrorById(
+                                "msg.delete.property.with.configurable.false", name);
                     }
                 } else {
                     int id = (info & 0xFFFF);
@@ -538,8 +502,7 @@
     }
 
     @Override
-    public void delete(Symbol key)
-    {
+    public void delete(Symbol key) {
         int info = findInstanceIdInfo(key);
         if (info != 0) {
             // Let the super class to throw exceptions for sealed objects
@@ -549,7 +512,8 @@
                 if ((attr & PERMANENT) != 0) {
                     Context cx = Context.getContext();
                     if (cx.isStrictMode()) {
-                        throw ScriptRuntime.typeError0("msg.delete.property.with.configurable.false");
+                        throw ScriptRuntime.typeErrorById(
+                                "msg.delete.property.with.configurable.false");
                     }
                 } else {
                     int id = (info & 0xFFFF);
@@ -571,8 +535,7 @@
     }
 
     @Override
-    public int getAttributes(String name)
-    {
+    public int getAttributes(String name) {
         int info = findInstanceIdInfo(name);
         if (info != 0) {
             int attr = (info >>> 16);
@@ -588,8 +551,23 @@
     }
 
     @Override
-    public void setAttributes(String name, int attributes)
-    {
+    public int getAttributes(Symbol key) {
+        int info = findInstanceIdInfo(key);
+        if (info != 0) {
+            int attr = (info >>> 16);
+            return attr;
+        }
+        if (prototypeValues != null) {
+            int id = prototypeValues.findId(key);
+            if (id != 0) {
+                return prototypeValues.getAttributes(id);
+            }
+        }
+        return super.getAttributes(key);
+    }
+
+    @Override
+    public void setAttributes(String name, int attributes) {
         ScriptableObject.checkValidAttributes(attributes);
         int info = findInstanceIdInfo(name);
         if (info != 0) {
@@ -611,12 +589,11 @@
     }
 
     @Override
-    Object[] getIds(boolean getNonEnumerable, boolean getSymbols)
-    {
+    Object[] getIds(boolean getNonEnumerable, boolean getSymbols) {
         Object[] result = super.getIds(getNonEnumerable, getSymbols);
 
         if (prototypeValues != null) {
-            result = prototypeValues.getNames(getNonEnumerable, result);
+            result = prototypeValues.getNames(getNonEnumerable, getSymbols, result);
         }
 
         int maxInstanceId = getMaxInstanceId();
@@ -646,8 +623,7 @@
             if (count != 0) {
                 if (result.length == 0 && ids.length == count) {
                     result = ids;
-                }
-                else {
+                } else {
                     Object[] tmp = new Object[result.length + count];
                     System.arraycopy(result, 0, tmp, 0, result.length);
                     System.arraycopy(ids, 0, tmp, result.length, count);
@@ -658,92 +634,83 @@
         return result;
     }
 
-    /**
-     * Get maximum id findInstanceIdInfo can generate.
-     */
-    protected int getMaxInstanceId()
-    {
+    /** Get maximum id findInstanceIdInfo can generate. */
+    protected int getMaxInstanceId() {
         return 0;
     }
 
-    protected static int instanceIdInfo(int attributes, int id)
-    {
+    protected static int instanceIdInfo(int attributes, int id) {
         return (attributes << 16) | id;
     }
 
     /**
-     * Map name to id of instance property.
-     * Should return 0 if not found or the result of
-     * {@link #instanceIdInfo(int, int)}.
+     * Map name to id of instance property. Should return 0 if not found or the result of {@link
+     * #instanceIdInfo(int, int)}.
      */
-    protected int findInstanceIdInfo(String name)
-    {
+    protected int findInstanceIdInfo(String name) {
         return 0;
     }
 
     /**
-     * Map name to id of instance property.
-     * Should return 0 if not found or the result of
-     * {@link #instanceIdInfo(int, int)}.
+     * Map name to id of instance property. Should return 0 if not found or the result of {@link
+     * #instanceIdInfo(int, int)}.
      */
-    protected int findInstanceIdInfo(Symbol key)
-    {
+    protected int findInstanceIdInfo(Symbol key) {
         return 0;
     }
 
-    /** Map id back to property name it defines.
-     */
-    protected String getInstanceIdName(int id)
-    {
+    /** Map id back to property name it defines. */
+    protected String getInstanceIdName(int id) {
         throw new IllegalArgumentException(String.valueOf(id));
     }
 
-    /** Get id value.
-     ** If id value is constant, descendant can call cacheIdValue to store
-     ** value in the permanent cache.
-     ** Default implementation creates IdFunctionObject instance for given id
-     ** and cache its value
+    /**
+     * Get id value. * If id value is constant, descendant can call cacheIdValue to store * value in
+     * the permanent cache. * Default implementation creates IdFunctionObject instance for given id
+     * * and cache its value
      */
-    protected Object getInstanceIdValue(int id)
-    {
+    protected Object getInstanceIdValue(int id) {
         throw new IllegalStateException(String.valueOf(id));
     }
 
     /**
-     * Set or delete id value. If value == NOT_FOUND , the implementation
-     * should make sure that the following getInstanceIdValue return NOT_FOUND.
+     * Set or delete id value. If value == NOT_FOUND , the implementation should make sure that the
+     * following getInstanceIdValue return NOT_FOUND.
      */
-    protected void setInstanceIdValue(int id, Object value)
-    {
+    protected void setInstanceIdValue(int id, Object value) {
         throw new IllegalStateException(String.valueOf(id));
     }
 
     /**
-     * Update the attributes of the given instance property. Classes which
-     * want to support changing property attributes via Object.defineProperty
-     * must override this method. The default implementation throws
-     * InternalError.
+     * Update the attributes of the given instance property. Classes which want to support changing
+     * property attributes via Object.defineProperty must override this method. The default
+     * implementation throws InternalError.
+     *
      * @param id the instance property id
      * @param attr the new attribute bitset
      */
     protected void setInstanceIdAttributes(int id, int attr) {
-        throw ScriptRuntime.constructError("InternalError",
-                "Changing attributes not supported for " + getClassName()
-                + " " + getInstanceIdName(id) + " property");
+        throw ScriptRuntime.constructError(
+                "InternalError",
+                "Changing attributes not supported for "
+                        + getClassName()
+                        + " "
+                        + getInstanceIdName(id)
+                        + " property");
     }
 
-    /** 'thisObj' will be null if invoked as constructor, in which case
-     ** instance of Scriptable should be returned. */
-    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
-                             Scriptable thisObj, Object[] args)
-    {
+    /**
+     * 'thisObj' will be null if invoked as constructor, in which case * instance of Scriptable
+     * should be returned.
+     */
+    @Override
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         throw f.unknown();
     }
 
-    public final IdFunctionObject exportAsJSClass(int maxPrototypeId,
-                                                  Scriptable scope,
-                                                  boolean sealed)
-    {
+    public final IdFunctionObject exportAsJSClass(
+            int maxPrototypeId, Scriptable scope, boolean sealed) {
         // Set scope and prototype unless this is top level scope itself
         if (scope != this && scope != null) {
             setParentScope(scope);
@@ -763,127 +730,114 @@
         return ctor;
     }
 
-    public final boolean hasPrototypeMap()
-    {
+    public final boolean hasPrototypeMap() {
         return prototypeValues != null;
     }
 
-    public final void activatePrototypeMap(int maxPrototypeId)
-    {
+    public final void activatePrototypeMap(int maxPrototypeId) {
         PrototypeValues values = new PrototypeValues(this, maxPrototypeId);
         synchronized (this) {
-            if (prototypeValues != null)
-                throw new IllegalStateException();
+            if (prototypeValues != null) throw new IllegalStateException();
             prototypeValues = values;
         }
     }
 
-    public final IdFunctionObject initPrototypeMethod(Object tag, int id, String name,
-                                          int arity)
-    {
+    public final IdFunctionObject initPrototypeMethod(Object tag, int id, String name, int arity) {
         return initPrototypeMethod(tag, id, name, name, arity);
     }
 
-    public final IdFunctionObject initPrototypeMethod(Object tag, int id, String propertyName, String functionName,
-                                          int arity)
-    {
+    public final IdFunctionObject initPrototypeMethod(
+            Object tag, int id, String propertyName, String functionName, int arity) {
         Scriptable scope = ScriptableObject.getTopLevelScope(this);
-        IdFunctionObject function = newIdFunction(tag, id,
-            functionName != null ? functionName : propertyName,
-            arity, scope);
+        IdFunctionObject function =
+                newIdFunction(
+                        tag, id, functionName != null ? functionName : propertyName, arity, scope);
         prototypeValues.initValue(id, propertyName, function, DONTENUM);
         return function;
     }
 
-    public final IdFunctionObject initPrototypeMethod(Object tag, int id, Symbol key, String functionName,
-                                          int arity)
-    {
+    public final IdFunctionObject initPrototypeMethod(
+            Object tag, int id, Symbol key, String functionName, int arity) {
         Scriptable scope = ScriptableObject.getTopLevelScope(this);
         IdFunctionObject function = newIdFunction(tag, id, functionName, arity, scope);
         prototypeValues.initValue(id, key, function, DONTENUM);
         return function;
     }
 
-    public final void initPrototypeConstructor(IdFunctionObject f)
-    {
+    public final void initPrototypeConstructor(IdFunctionObject f) {
         int id = prototypeValues.constructorId;
-        if (id == 0)
-            throw new IllegalStateException();
-        if (f.methodId() != id)
-            throw new IllegalArgumentException();
-        if (isSealed()) { f.sealObject(); }
+        if (id == 0) throw new IllegalStateException();
+        if (f.methodId() != id) throw new IllegalArgumentException();
+        if (isSealed()) {
+            f.sealObject();
+        }
         prototypeValues.initValue(id, "constructor", f, DONTENUM);
     }
 
-    public final void initPrototypeValue(int id, String name, Object value,
-                                         int attributes)
-    {
+    public final void initPrototypeValue(int id, String name, Object value, int attributes) {
         prototypeValues.initValue(id, name, value, attributes);
     }
 
-    public final void initPrototypeValue(int id, Symbol key, Object value,
-                                         int attributes)
-    {
+    public final void initPrototypeValue(int id, Symbol key, Object value, int attributes) {
         prototypeValues.initValue(id, key, value, attributes);
     }
 
-    protected void initPrototypeId(int id)
-    {
+    protected void initPrototypeId(int id) {
         throw new IllegalStateException(String.valueOf(id));
     }
 
-    protected int findPrototypeId(String name)
-    {
+    protected int findPrototypeId(String name) {
         throw new IllegalStateException(name);
     }
 
-    protected int findPrototypeId(Symbol key)
-    {
+    protected int findPrototypeId(Symbol key) {
         return 0;
     }
 
-    protected void fillConstructorProperties(IdFunctionObject ctor)
-    {
-    }
+    protected void fillConstructorProperties(IdFunctionObject ctor) {}
 
-    protected void addIdFunctionProperty(Scriptable obj, Object tag, int id,
-                                         String name, int arity)
-    {
+    protected void addIdFunctionProperty(
+            Scriptable obj, Object tag, int id, String name, int arity) {
         Scriptable scope = ScriptableObject.getTopLevelScope(obj);
         IdFunctionObject f = newIdFunction(tag, id, name, arity, scope);
         f.addAsProperty(obj);
     }
 
     /**
-     * Utility method to construct type error to indicate incompatible call
-     * when converting script thisObj to a particular type is not possible.
+     * Utility method to check the type and do the cast or throw an incompatible call error.
      * Possible usage would be to have a private function like realThis:
+     *
      * <pre>
-     *  private static NativeSomething realThis(Scriptable thisObj,
-     *                                          IdFunctionObject f)
+     *  private static NativeSomething realThis(Scriptable thisObj, IdFunctionObject f)
      *  {
-     *      if (!(thisObj instanceof NativeSomething))
-     *          throw incompatibleCallError(f);
-     *      return (NativeSomething)thisObj;
+     *      return ensureType(thisObj, NativeSomething.class, f);
      * }
      * </pre>
-     * Note that although such function can be implemented universally via
-     * java.lang.Class.isInstance(), it would be much more slower.
-     * @param f function that is attempting to convert 'this'
-     * object.
-     * @return Scriptable object suitable for a check by the instanceof
-     * operator.
-     * @throws RuntimeException if no more instanceof target can be found
+     *
+     * @param obj the object to check/cast
+     * @param clazz the target type
+     * @param f function that is attempting to convert 'this' object.
+     * @return obj casted to the target type
+     * @throws EcmaError if the cast failed.
      */
-    protected static EcmaError incompatibleCallError(IdFunctionObject f)
-    {
-        throw ScriptRuntime.typeError1("msg.incompat.call",
-                                       f.getFunctionName());
+    @SuppressWarnings("unchecked")
+    protected static <T> T ensureType(Object obj, Class<T> clazz, IdFunctionObject f) {
+        if (clazz.isInstance(obj)) {
+            return (T) obj;
+        }
+        if (obj == null) {
+            throw ScriptRuntime.typeErrorById(
+                    "msg.incompat.call.details", f.getFunctionName(), "null", clazz.getName());
+        }
+        throw ScriptRuntime.typeErrorById(
+                "msg.incompat.call.details",
+                f.getFunctionName(),
+                obj.getClass().getName(),
+                clazz.getName());
     }
 
-    private IdFunctionObject newIdFunction(Object tag, int id, String name,
-                                           int arity, Scriptable scope)
-    {
+    private IdFunctionObject newIdFunction(
+            Object tag, int id, String name, int arity, Scriptable scope) {
         IdFunctionObject function = null;
         if (Context.getContext().getLanguageVersion() < Context.VERSION_ES6) {
             function = new IdFunctionObject(this, tag, id, name, arity, scope);
@@ -891,125 +845,134 @@
             function = new IdFunctionObjectES6(this, tag, id, name, arity, scope);
         }
 
-        if (isSealed()) { function.sealObject(); }
+        if (isSealed()) {
+            function.sealObject();
+        }
         return function;
     }
 
     @Override
-    public void defineOwnProperty(Context cx, Object key, ScriptableObject desc) {
-      if (key instanceof String) {
-        String name = (String) key;
-        int info = findInstanceIdInfo(name);
-        if (info != 0) {
-            int id = (info & 0xFFFF);
-            if (isAccessorDescriptor(desc)) {
-              delete(id); // it will be replaced with a slot
-            } else {
-              checkPropertyDefinition(desc);
-              ScriptableObject current = getOwnPropertyDescriptor(cx, key);
-              checkPropertyChange(name, current, desc);
-              int attr = (info >>> 16);
-              Object value = getProperty(desc, "value");
-              if (value != NOT_FOUND && (attr & READONLY) == 0) {
-                Object currentValue = getInstanceIdValue(id);
-                if (!sameValue(value, currentValue)) {
-                  setInstanceIdValue(id, value);
-                }
-              }
-              setAttributes(name, applyDescriptorToAttributeBitset(attr, desc));
-              return;
+    protected void defineOwnProperty(
+            Context cx, Object key, ScriptableObject desc, boolean checkValid) {
+        if (key instanceof String) {
+            String name = (String) key;
+            int info = findInstanceIdInfo(name);
+            if (info != 0) {
+                int id = (info & 0xFFFF);
+                if (isAccessorDescriptor(desc)) {
+                    delete(id); // it will be replaced with a slot
+                } else {
+                    checkPropertyDefinition(desc);
+                    ScriptableObject current = getOwnPropertyDescriptor(cx, key);
+                    checkPropertyChange(name, current, desc);
+                    int attr = (info >>> 16);
+                    Object value = getProperty(desc, "value");
+                    if (value != NOT_FOUND && (attr & READONLY) == 0) {
+                        Object currentValue = getInstanceIdValue(id);
+                        if (!sameValue(value, currentValue)) {
+                            setInstanceIdValue(id, value);
+                        }
+                    }
+                    setAttributes(name, applyDescriptorToAttributeBitset(attr, desc));
+                    return;
+                }
             }
-        }
-        if (prototypeValues != null) {
-            int id = prototypeValues.findId(name);
-            if (id != 0) {
-              if (isAccessorDescriptor(desc)) {
-                prototypeValues.delete(id); // it will be replaced with a slot
-              } else {
-                checkPropertyDefinition(desc);
-                ScriptableObject current = getOwnPropertyDescriptor(cx, key);
-                checkPropertyChange(name, current, desc);
-                int attr = prototypeValues.getAttributes(id);
-                Object value = getProperty(desc, "value");
-                if (value != NOT_FOUND && (attr & READONLY) == 0) {
-                  Object currentValue = prototypeValues.get(id);
-                  if (!sameValue(value, currentValue)) {
-                    prototypeValues.set(id, this, value);
-                  }
+            if (prototypeValues != null) {
+                int id = prototypeValues.findId(name);
+                if (id != 0) {
+                    if (isAccessorDescriptor(desc)) {
+                        prototypeValues.delete(id); // it will be replaced with a slot
+                    } else {
+                        checkPropertyDefinition(desc);
+                        ScriptableObject current = getOwnPropertyDescriptor(cx, key);
+                        checkPropertyChange(name, current, desc);
+                        int attr = prototypeValues.getAttributes(id);
+                        Object value = getProperty(desc, "value");
+                        if (value != NOT_FOUND && (attr & READONLY) == 0) {
+                            Object currentValue = prototypeValues.get(id);
+                            if (!sameValue(value, currentValue)) {
+                                prototypeValues.set(id, this, value);
+                            }
+                        }
+                        prototypeValues.setAttributes(
+                                id, applyDescriptorToAttributeBitset(attr, desc));
+
+                        // Handle the regular slot that was created if this property was previously
+                        // replaced
+                        // with an accessor descriptor.
+                        if (super.has(name, this)) {
+                            super.delete(name);
+                        }
+
+                        return;
+                    }
                 }
-                prototypeValues.setAttributes(id, applyDescriptorToAttributeBitset(attr, desc));
-                return;
-              }
             }
         }
-      }
-      super.defineOwnProperty(cx, key, desc);
+        super.defineOwnProperty(cx, key, desc, checkValid);
     }
 
-
     @Override
     protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) {
-      ScriptableObject desc = super.getOwnPropertyDescriptor(cx, id);
-      if (desc == null) {
-          if (id instanceof String) {
-              desc = getBuiltInDescriptor((String) id);
-          } else if (ScriptRuntime.isSymbol(id)) {
-              desc = getBuiltInDescriptor(((NativeSymbol)id).getKey());
-          }
-      }
-      return desc;
+        ScriptableObject desc = super.getOwnPropertyDescriptor(cx, id);
+        if (desc == null) {
+            if (id instanceof String) {
+                desc = getBuiltInDescriptor((String) id);
+            } else if (ScriptRuntime.isSymbol(id)) {
+                desc = getBuiltInDescriptor(((NativeSymbol) id).getKey());
+            }
+        }
+        return desc;
     }
 
     private ScriptableObject getBuiltInDescriptor(String name) {
-      Object value = null;
-      int attr = EMPTY;
+        Object value = null;
+        int attr = EMPTY;
 
-      Scriptable scope = getParentScope();
-      if (scope == null) {
-        scope = this;
-      }
-
-      int info = findInstanceIdInfo(name);
-      if (info != 0) {
-        int id = (info & 0xFFFF);
-        value = getInstanceIdValue(id);
-        attr = (info >>> 16);
-        return buildDataDescriptor(scope, value, attr);
-      }
-      if (prototypeValues != null) {
-        int id = prototypeValues.findId(name);
-        if (id != 0) {
-          value = prototypeValues.get(id);
-          attr = prototypeValues.getAttributes(id);
-          return buildDataDescriptor(scope, value, attr);
+        Scriptable scope = getParentScope();
+        if (scope == null) {
+            scope = this;
         }
-      }
-      return null;
+
+        int info = findInstanceIdInfo(name);
+        if (info != 0) {
+            int id = (info & 0xFFFF);
+            value = getInstanceIdValue(id);
+            attr = (info >>> 16);
+            return buildDataDescriptor(scope, value, attr);
+        }
+        if (prototypeValues != null) {
+            int id = prototypeValues.findId(name);
+            if (id != 0) {
+                value = prototypeValues.get(id);
+                attr = prototypeValues.getAttributes(id);
+                return buildDataDescriptor(scope, value, attr);
+            }
+        }
+        return null;
     }
 
     private ScriptableObject getBuiltInDescriptor(Symbol key) {
-      Object value = null;
-      int attr = EMPTY;
+        Object value = null;
+        int attr = EMPTY;
+
+        Scriptable scope = getParentScope();
+        if (scope == null) {
+            scope = this;
+        }
 
-      Scriptable scope = getParentScope();
-      if (scope == null) {
-        scope = this;
-      }
-
-      if (prototypeValues != null) {
-        int id = prototypeValues.findId(key);
-        if (id != 0) {
-          value = prototypeValues.get(id);
-          attr = prototypeValues.getAttributes(id);
-          return buildDataDescriptor(scope, value, attr);
-        }
-      }
-      return null;
-    }
-
-    private void readObject(ObjectInputStream stream)
-        throws IOException, ClassNotFoundException
-    {
+        if (prototypeValues != null) {
+            int id = prototypeValues.findId(key);
+            if (id != 0) {
+                value = prototypeValues.get(id);
+                attr = prototypeValues.getAttributes(id);
+                return buildDataDescriptor(scope, value, attr);
+            }
+        }
+        return null;
+    }
+
+    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
         stream.defaultReadObject();
         int maxPrototypeId = stream.readInt();
         if (maxPrototypeId != 0) {
@@ -1017,9 +980,7 @@
         }
     }
 
-    private void writeObject(ObjectOutputStream stream)
-        throws IOException
-    {
+    private void writeObject(ObjectOutputStream stream) throws IOException {
         stream.defaultWriteObject();
         int maxPrototypeId = 0;
         if (prototypeValues != null) {
@@ -1027,6 +988,4 @@
         }
         stream.writeInt(maxPrototypeId);
     }
-
 }
-
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ImplementationVersion.java rhino-1.7.14/src/org/mozilla/javascript/ImplementationVersion.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ImplementationVersion.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/ImplementationVersion.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,60 @@
+package org.mozilla.javascript;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+
+/**
+ * This class is a singleton that just exists to serve up the implementation version. This should
+ * encourage that it's safely but lazily loaded just once per VM.
+ */
+public class ImplementationVersion {
+
+    private String versionString;
+
+    private static final ImplementationVersion version = new ImplementationVersion();
+
+    public static String get() {
+        return version.versionString;
+    }
+
+    private ImplementationVersion() {
+        Enumeration<URL> urls;
+        try {
+            urls =
+                    ImplementationVersion.class
+                            .getClassLoader()
+                            .getResources("META-INF/MANIFEST.MF");
+        } catch (IOException ioe) {
+            return;
+        }
+
+        // There will be many manifests in the world -- enumerate all of them until we find the
+        // right one.
+        while (urls.hasMoreElements()) {
+            URL metaUrl = urls.nextElement();
+            try (InputStream is = metaUrl.openStream()) {
+                Manifest mf = new Manifest(is);
+                Attributes attrs = mf.getMainAttributes();
+                if ("Mozilla Rhino".equals(attrs.getValue("Implementation-Title"))) {
+                    StringBuilder buf = new StringBuilder(23);
+                    buf.append("Rhino ").append(attrs.getValue("Implementation-Version"));
+                    String builtDate = attrs.getValue("Built-Date");
+                    if (builtDate != null) {
+                        builtDate = builtDate.replaceAll("-", " ");
+                        buf.append(' ').append(builtDate);
+                    }
+                    versionString = buf.toString();
+                    return;
+                }
+            } catch (IOException e) {
+                // Ignore this unlikely event
+            }
+        }
+        // We are probably in a IDE
+        versionString = "Rhino Snapshot";
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ImporterTopLevel.java rhino-1.7.14/src/org/mozilla/javascript/ImporterTopLevel.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ImporterTopLevel.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ImporterTopLevel.java	2022-01-06 22:57:21.000000000 +0100
@@ -11,63 +11,63 @@
 /**
  * Class ImporterTopLevel
  *
- * This class defines a ScriptableObject that can be instantiated
- * as a top-level ("global") object to provide functionality similar
- * to Java's "import" statement.
- * <p>
- * This class can be used to create a top-level scope using the following code:
+ * <p>This class defines a ScriptableObject that can be instantiated as a top-level ("global")
+ * object to provide functionality similar to Java's "import" statement.
+ *
+ * <p>This class can be used to create a top-level scope using the following code:
+ *
  * <pre>
  *  Scriptable scope = new ImporterTopLevel(cx);
  * </pre>
+ *
  * Then JavaScript code will have access to the following methods:
+ *
  * <ul>
- * <li>importClass - will "import" a class by making its unqualified name
- *                   available as a property of the top-level scope
- * <li>importPackage - will "import" all the classes of the package by
- *                     searching for unqualified names as classes qualified
- *                     by the given package.
+ *   <li>importClass - will "import" a class by making its unqualified name available as a property
+ *       of the top-level scope
+ *   <li>importPackage - will "import" all the classes of the package by searching for unqualified
+ *       names as classes qualified by the given package.
  * </ul>
+ *
  * The following code from the shell illustrates this use:
+ *
  * <pre>
- * js> importClass(java.io.File)
- * js> f = new File('help.txt')
+ * js> importClass(java.io.File)
+ * js> f = new File('help.txt')
  * help.txt
- * js> importPackage(java.util)
- * js> v = new Vector()
+ * js> importPackage(java.util)
+ * js> v = new Vector()
  * []
+ * </pre>
  *
  * @author Norris Boyd
  */
 public class ImporterTopLevel extends TopLevel {
-    static final long serialVersionUID = -9095380847465315412L;
+    private static final long serialVersionUID = -9095380847465315412L;
 
     private static final Object IMPORTER_TAG = "Importer";
 
-    public ImporterTopLevel() { }
+    public ImporterTopLevel() {}
 
     public ImporterTopLevel(Context cx) {
         this(cx, false);
     }
 
-    public ImporterTopLevel(Context cx, boolean sealed)
-    {
+    public ImporterTopLevel(Context cx, boolean sealed) {
         initStandardObjects(cx, sealed);
     }
 
     @Override
-    public String getClassName()
-    {
+    public String getClassName() {
         return (topScopeFlag) ? "global" : "JavaImporter";
     }
 
-    public static void init(Context cx, Scriptable scope, boolean sealed)
-    {
+    public static void init(Context cx, Scriptable scope, boolean sealed) {
         ImporterTopLevel obj = new ImporterTopLevel();
         obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
     }
 
-    public void initStandardObjects(Context cx, boolean sealed)
-    {
+    public void initStandardObjects(Context cx, boolean sealed) {
         // Assume that Context.initStandardObjects initialize JavaImporter
         // property lazily so the above init call is not yet called
         cx.initStandardObjects(this, sealed);
@@ -87,62 +87,74 @@
 
     @Override
     public boolean has(String name, Scriptable start) {
-        return super.has(name, start)
-               || getPackageProperty(name, start) != NOT_FOUND;
+        return super.has(name, start) || getPackageProperty(name, start) != NOT_FOUND;
     }
 
     @Override
     public Object get(String name, Scriptable start) {
         Object result = super.get(name, start);
-        if (result != NOT_FOUND)
-            return result;
+        if (result != NOT_FOUND) return result;
         result = getPackageProperty(name, start);
         return result;
     }
 
     private Object getPackageProperty(String name, Scriptable start) {
         Object result = NOT_FOUND;
-        Object[] elements;
-        synchronized (importedPackages) {
-            elements = importedPackages.toArray();
+        Scriptable scope = start;
+        if (topScopeFlag) {
+            scope = ScriptableObject.getTopLevelScope(scope);
+        }
+        Object[] elements = getNativeJavaPackages(scope);
+        if (elements == null) {
+            return result;
         }
-        for (int i=0; i < elements.length; i++) {
+        for (int i = 0; i < elements.length; i++) {
             NativeJavaPackage p = (NativeJavaPackage) elements[i];
             Object v = p.getPkgProperty(name, start, false);
             if (v != null && !(v instanceof NativeJavaPackage)) {
                 if (result == NOT_FOUND) {
                     result = v;
                 } else {
-                    throw Context.reportRuntimeError2(
-                        "msg.ambig.import", result.toString(), v.toString());
+                    throw Context.reportRuntimeErrorById(
+                            "msg.ambig.import", result.toString(), v.toString());
                 }
             }
         }
+
         return result;
     }
 
-    /**
-     * @deprecated Kept only for compatibility.
-     */
+    private static Object[] getNativeJavaPackages(Scriptable scope) {
+        // retrive the native java packages stored in top scope.
+        synchronized (scope) {
+            if (scope instanceof ScriptableObject) {
+                ScriptableObject so = (ScriptableObject) scope;
+                ObjArray importedPackages = (ObjArray) so.getAssociatedValue(AKEY);
+                if (importedPackages != null) {
+                    return importedPackages.toArray();
+                }
+            }
+        }
+        return null;
+    }
+
+    /** @deprecated Kept only for compatibility. */
     @Deprecated
-    public void importPackage(Context cx, Scriptable thisObj, Object[] args,
-                              Function funObj)
-    {
-        js_importPackage(args);
+    public void importPackage(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
+        js_importPackage(this, args);
     }
 
-    private Object js_construct(Scriptable scope, Object[] args)
-    {
+    private Object js_construct(Scriptable scope, Object[] args) {
         ImporterTopLevel result = new ImporterTopLevel();
         for (int i = 0; i != args.length; ++i) {
             Object arg = args[i];
             if (arg instanceof NativeJavaClass) {
-                result.importClass((NativeJavaClass)arg);
+                ImporterTopLevel.importClass(result, (NativeJavaClass) arg);
             } else if (arg instanceof NativeJavaPackage) {
-                result.importPackage((NativeJavaPackage)arg);
+                ImporterTopLevel.importPackage(result, (NativeJavaPackage) arg);
             } else {
-                throw Context.reportRuntimeError1(
-                    "msg.not.class.not.pkg", Context.toString(arg));
+                throw Context.reportRuntimeErrorById(
+                        "msg.not.class.not.pkg", Context.toString(arg));
             }
         }
         // set explicitly prototype and scope
@@ -155,38 +167,38 @@
         return result;
     }
 
-    private Object js_importClass(Object[] args)
-    {
+    private static Object js_importClass(Scriptable scope, Object[] args) {
         for (int i = 0; i != args.length; i++) {
             Object arg = args[i];
             if (!(arg instanceof NativeJavaClass)) {
-                throw Context.reportRuntimeError1(
-                    "msg.not.class", Context.toString(arg));
+                throw Context.reportRuntimeErrorById("msg.not.class", Context.toString(arg));
             }
-            importClass((NativeJavaClass)arg);
+            importClass(scope, (NativeJavaClass) arg);
         }
         return Undefined.instance;
     }
 
-    private Object js_importPackage(Object[] args)
-    {
+    private static Object js_importPackage(ScriptableObject scope, Object[] args) {
         for (int i = 0; i != args.length; i++) {
             Object arg = args[i];
             if (!(arg instanceof NativeJavaPackage)) {
-                throw Context.reportRuntimeError1(
-                    "msg.not.pkg", Context.toString(arg));
+                throw Context.reportRuntimeErrorById("msg.not.pkg", Context.toString(arg));
             }
-            importPackage((NativeJavaPackage)arg);
+            importPackage(scope, (NativeJavaPackage) arg);
         }
         return Undefined.instance;
     }
 
-    private void importPackage(NativeJavaPackage pkg)
-    {
-        if(pkg == null) {
+    private static void importPackage(ScriptableObject scope, NativeJavaPackage pkg) {
+        if (pkg == null) {
             return;
         }
-        synchronized (importedPackages) {
+        synchronized (scope) {
+            ObjArray importedPackages = (ObjArray) scope.getAssociatedValue(AKEY);
+            if (importedPackages == null) {
+                importedPackages = new ObjArray();
+                scope.associateValue(AKEY, importedPackages);
+            }
             for (int j = 0; j != importedPackages.size(); j++) {
                 if (pkg.equals(importedPackages.get(j))) {
                     return;
@@ -196,95 +208,95 @@
         }
     }
 
-    private void importClass(NativeJavaClass cl)
-    {
+    private static void importClass(Scriptable scope, NativeJavaClass cl) {
         String s = cl.getClassObject().getName();
-        String n = s.substring(s.lastIndexOf('.')+1);
-        Object val = get(n, this);
+        String n = s.substring(s.lastIndexOf('.') + 1);
+        Object val = scope.get(n, scope);
         if (val != NOT_FOUND && val != cl) {
-            throw Context.reportRuntimeError1("msg.prop.defined", n);
+            throw Context.reportRuntimeErrorById("msg.prop.defined", n);
         }
-        //defineProperty(n, cl, DONTENUM);
-        put(n, this, cl);
+        // defineProperty(n, cl, DONTENUM);
+        scope.put(n, scope, cl);
     }
 
     @Override
-    protected void initPrototypeId(int id)
-    {
+    protected void initPrototypeId(int id) {
         String s;
         int arity;
         switch (id) {
-          case Id_constructor:   arity=0; s="constructor";   break;
-          case Id_importClass:   arity=1; s="importClass";   break;
-          case Id_importPackage: arity=1; s="importPackage"; break;
-          default: throw new IllegalArgumentException(String.valueOf(id));
+            case Id_constructor:
+                arity = 0;
+                s = "constructor";
+                break;
+            case Id_importClass:
+                arity = 1;
+                s = "importClass";
+                break;
+            case Id_importPackage:
+                arity = 1;
+                s = "importPackage";
+                break;
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
         }
         initPrototypeMethod(IMPORTER_TAG, id, s, arity);
     }
 
     @Override
-    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
-                             Scriptable thisObj, Object[] args)
-    {
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         if (!f.hasTag(IMPORTER_TAG)) {
             return super.execIdCall(f, cx, scope, thisObj, args);
         }
         int id = f.methodId();
         switch (id) {
-          case Id_constructor:
-            return js_construct(scope, args);
+            case Id_constructor:
+                return js_construct(scope, args);
 
-          case Id_importClass:
-            return realThis(thisObj, f).js_importClass(args);
+            case Id_importClass:
+                return js_importClass(realScope(scope, thisObj, f), args);
 
-          case Id_importPackage:
-            return realThis(thisObj, f).js_importPackage(args);
+            case Id_importPackage:
+                return js_importPackage(realScope(scope, thisObj, f), args);
         }
         throw new IllegalArgumentException(String.valueOf(id));
     }
 
-    private ImporterTopLevel realThis(Scriptable thisObj, IdFunctionObject f)
-    {
+    private ScriptableObject realScope(Scriptable scope, Scriptable thisObj, IdFunctionObject f) {
         if (topScopeFlag) {
             // when used as top scope importPackage and importClass are global
-            // function that ignore thisObj
-            return this;
+            // function that ignore thisObj. We use the the top level scope
+            // which might not be the same as 'this' when used shared scopes
+            thisObj = ScriptableObject.getTopLevelScope(scope);
         }
-        if (!(thisObj instanceof ImporterTopLevel))
-            throw incompatibleCallError(f);
-        return (ImporterTopLevel)thisObj;
+        return ensureType(thisObj, ScriptableObject.class, f);
     }
 
-// #string_id_map#
-
     @Override
-    protected int findPrototypeId(String s)
-    {
+    protected int findPrototypeId(String s) {
         int id;
-// #generated# Last update: 2007-05-09 08:15:24 EDT
-        L0: { id = 0; String X = null; int c;
-            int s_length = s.length();
-            if (s_length==11) {
-                c=s.charAt(0);
-                if (c=='c') { X="constructor";id=Id_constructor; }
-                else if (c=='i') { X="importClass";id=Id_importClass; }
-            }
-            else if (s_length==13) { X="importPackage";id=Id_importPackage; }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-            break L0;
+        switch (s) {
+            case "constructor":
+                id = Id_constructor;
+                break;
+            case "importClass":
+                id = Id_importClass;
+                break;
+            case "importPackage":
+                id = Id_importPackage;
+                break;
+            default:
+                id = 0;
+                break;
         }
-// #/generated#
         return id;
     }
 
-    private static final int
-        Id_constructor          = 1,
-        Id_importClass          = 2,
-        Id_importPackage        = 3,
-        MAX_PROTOTYPE_ID        = 3;
-
-// #/string_id_map#
+    private static final int Id_constructor = 1,
+            Id_importClass = 2,
+            Id_importPackage = 3,
+            MAX_PROTOTYPE_ID = 3;
 
-    private ObjArray importedPackages = new ObjArray();
+    private static final String AKEY = "importedPackages";
     private boolean topScopeFlag;
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/InterfaceAdapter.java rhino-1.7.14/src/org/mozilla/javascript/InterfaceAdapter.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/InterfaceAdapter.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/InterfaceAdapter.java	2022-01-06 22:57:21.000000000 +0100
@@ -7,6 +7,9 @@
 package org.mozilla.javascript;
 
 import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import java.util.HashSet;
 
 /**
  * Adapter to use JS function as implementation of Java interfaces with
@@ -21,7 +24,7 @@
      * call the supplied JS function when called.
      * Only interfaces were all methods have the same signature is supported.
      *
-     * @return The glue object or null if <tt>cl</tt> is not interface or
+     * @return The glue object or null if <code>cl</code> is not interface or
      *         has methods with different signatures.
      */
     static Object create(Context cx, Class<?> cl, ScriptableObject object)
@@ -34,25 +37,40 @@
         adapter = (InterfaceAdapter)cache.getInterfaceAdapter(cl);
         ContextFactory cf = cx.getFactory();
         if (adapter == null) {
-            Method[] methods = cl.getMethods();
-            if ( object instanceof Callable) {
+            if (object instanceof Callable) {
+                Method[] methods = cl.getMethods();
                 // Check if interface can be implemented by a single function.
-                // We allow this if the interface has only one method or multiple 
-                // methods with the same name (in which case they'd result in 
+                // We allow this if the interface has only one method or multiple
+                // methods with the same name (in which case they'd result in
                 // the same function to be invoked anyway).
-                int length = methods.length;
-                if (length == 0) {
-                    throw Context.reportRuntimeError1(
-                        "msg.no.empty.interface.conversion", cl.getName());
-                }
-                if (length > 1) {
-                    String methodName = methods[0].getName();
-                    for (int i = 1; i < length; i++) {
-                        if (!methodName.equals(methods[i].getName())) {
-                            throw Context.reportRuntimeError1(
-                                    "msg.no.function.interface.conversion",
-                                    cl.getName());
+                HashSet<String> functionalMethodNames = new HashSet<>();
+                HashSet<String> defaultMethodNames = new HashSet<>();
+                for (Method method : methods) {
+                    // there are multiple methods in the interface we inspect
+                    // only abstract ones, they must all have the same name.
+                    if (isFunctionalMethodCandidate(method)) {
+                        functionalMethodNames.add(method.getName());
+                        if (functionalMethodNames.size() > 1) {
+                            break;
                         }
+                    } else {
+                        defaultMethodNames.add(method.getName());
+                    }
+                }
+
+                boolean canConvert =
+                    (functionalMethodNames.size() == 1) ||
+                    (functionalMethodNames.isEmpty() && defaultMethodNames.size() == 1);
+                // There is no abstract method or there are multiple methods.
+                if (!canConvert) {
+                    if (functionalMethodNames.isEmpty() && defaultMethodNames.isEmpty()) {
+                        throw Context.reportRuntimeErrorById(
+                            "msg.no.empty.interface.conversion",
+                            cl.getName());
+                    } else {
+                        throw Context.reportRuntimeErrorById(
+                            "msg.no.function.interface.conversion",
+                            cl.getName());
                     }
                 }
             }
@@ -63,6 +81,24 @@
             adapter.proxyHelper, cf, adapter, object, topScope);
     }
 
+    /**
+     * We have to ignore java8 default methods and methods like 'equals', 'hashCode'
+     * and 'toString' as it occurs for example in the Comparator interface.
+     *
+     * @return true, if the function
+     */
+    private static boolean isFunctionalMethodCandidate(Method method) {
+        if (method.getName().equals("equals")
+            || method.getName().equals("hashCode")
+            || method.getName().equals("toString")) {
+            // it should be safe to ignore them as there is also a special
+            // case for these methods in VMBridge_jdk18.newInterfaceProxy
+            return false;
+        } else {
+            return Modifier.isAbstract(method.getModifiers());
+        }
+    }
+
     private InterfaceAdapter(ContextFactory cf, Class<?> cl)
     {
         this.proxyHelper
@@ -77,13 +113,7 @@
                          final Method method,
                          final Object[] args)
     {
-        ContextAction action = new ContextAction() {
-                public Object run(Context cx)
-                {
-                    return invokeImpl(cx, target, topScope, thisObject, method, args);
-                }
-            };
-        return cf.call(action);
+        return cf.call(cx -> invokeImpl(cx, target, topScope, thisObject, method, args));
     }
 
     Object invokeImpl(Context cx,
@@ -100,21 +130,20 @@
             Scriptable s = (Scriptable)target;
             String methodName = method.getName();
             Object value = ScriptableObject.getProperty(s, methodName);
-            if (value == ScriptableObject.NOT_FOUND) {
+            if (value == Scriptable.NOT_FOUND) {
                 // We really should throw an error here, but for the sake of
                 // compatibility with JavaAdapter we silently ignore undefined
                 // methods.
-                Context.reportWarning(ScriptRuntime.getMessage1(
+                Context.reportWarning(ScriptRuntime.getMessageById(
                         "msg.undefined.function.interface", methodName));
                 Class<?> resultType = method.getReturnType();
                 if (resultType == Void.TYPE) {
                     return null;
-                } else {
-                    return Context.jsToJava(null, resultType);
                 }
+                return Context.jsToJava(null, resultType);
             }
             if (!(value instanceof Callable)) {
-                throw Context.reportRuntimeError1(
+                throw Context.reportRuntimeErrorById(
                         "msg.not.function.interface",methodName);
             }
             function = (Callable)value;
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/InterpretedFunction.java rhino-1.7.14/src/org/mozilla/javascript/InterpretedFunction.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/InterpretedFunction.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/InterpretedFunction.java	2022-01-06 22:57:21.000000000 +0100
@@ -8,17 +8,14 @@
 
 import org.mozilla.javascript.debug.DebuggableScript;
 
-final class InterpretedFunction extends NativeFunction implements Script
-{
-    static final long serialVersionUID = 541475680333911468L;
+final class InterpretedFunction extends NativeFunction implements Script {
+    private static final long serialVersionUID = 541475680333911468L;
 
     InterpreterData idata;
     SecurityController securityController;
     Object securityDomain;
 
-    private InterpretedFunction(InterpreterData idata,
-                                Object staticSecurityDomain)
-    {
+    private InterpretedFunction(InterpreterData idata, Object staticSecurityDomain) {
         this.idata = idata;
 
         // Always get Context from the current thread to
@@ -40,88 +37,76 @@
         this.securityDomain = dynamicDomain;
     }
 
-    private InterpretedFunction(InterpretedFunction parent, int index)
-    {
+    private InterpretedFunction(InterpretedFunction parent, int index) {
         this.idata = parent.idata.itsNestedFunctions[index];
         this.securityController = parent.securityController;
         this.securityDomain = parent.securityDomain;
     }
 
-    /**
-     * Create script from compiled bytecode.
-     */
-    static InterpretedFunction createScript(InterpreterData idata,
-                                            Object staticSecurityDomain)
-    {
+    /** Create script from compiled bytecode. */
+    static InterpretedFunction createScript(InterpreterData idata, Object staticSecurityDomain) {
         InterpretedFunction f;
         f = new InterpretedFunction(idata, staticSecurityDomain);
         return f;
     }
 
-    /**
-     * Create function compiled from Function(...) constructor.
-     */
-    static InterpretedFunction createFunction(Context cx,Scriptable scope,
-                                              InterpreterData idata,
-                                              Object staticSecurityDomain)
-    {
+    /** Create function compiled from Function(...) constructor. */
+    static InterpretedFunction createFunction(
+            Context cx, Scriptable scope, InterpreterData idata, Object staticSecurityDomain) {
         InterpretedFunction f;
         f = new InterpretedFunction(idata, staticSecurityDomain);
-        f.initScriptFunction(cx, scope);
+        f.initScriptFunction(cx, scope, f.idata.isES6Generator);
         return f;
     }
 
-    /**
-     * Create function embedded in script or another function.
-     */
-    static InterpretedFunction createFunction(Context cx, Scriptable scope,
-                                              InterpretedFunction parent,
-                                              int index)
-    {
+    /** Create function embedded in script or another function. */
+    static InterpretedFunction createFunction(
+            Context cx, Scriptable scope, InterpretedFunction parent, int index) {
         InterpretedFunction f = new InterpretedFunction(parent, index);
-        f.initScriptFunction(cx, scope);
+        f.initScriptFunction(cx, scope, f.idata.isES6Generator);
         return f;
     }
 
-
     @Override
-    public String getFunctionName()
-    {
+    public String getFunctionName() {
         return (idata.itsName == null) ? "" : idata.itsName;
     }
 
     /**
      * Calls the function.
+     *
      * @param cx the current context
      * @param scope the scope used for the call
      * @param thisObj the value of "this"
-     * @param args function arguments. Must not be null. You can use
-     * {@link ScriptRuntime#emptyArgs} to pass empty arguments.
+     * @param args function arguments. Must not be null. You can use {@link ScriptRuntime#emptyArgs}
+     *     to pass empty arguments.
      * @return the result of the function call.
      */
     @Override
-    public Object call(Context cx, Scriptable scope, Scriptable thisObj,
-                       Object[] args)
-    {
+    public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         if (!ScriptRuntime.hasTopCall(cx)) {
             return ScriptRuntime.doTopCall(this, cx, scope, thisObj, args, idata.isStrict);
         }
         return Interpreter.interpret(this, cx, scope, thisObj, args);
     }
 
-    public Object exec(Context cx, Scriptable scope)
-    {
+    @Override
+    public Object exec(Context cx, Scriptable scope) {
         if (!isScript()) {
             // Can only be applied to scripts
             throw new IllegalStateException();
         }
+        Object ret;
         if (!ScriptRuntime.hasTopCall(cx)) {
             // It will go through "call" path. but they are equivalent
-            return ScriptRuntime.doTopCall(
-                this, cx, scope, scope, ScriptRuntime.emptyArgs, idata.isStrict);
+            ret =
+                    ScriptRuntime.doTopCall(
+                            this, cx, scope, scope, ScriptRuntime.emptyArgs, idata.isStrict);
+        } else {
+            ret = Interpreter.interpret(this, cx, scope, scope, ScriptRuntime.emptyArgs);
         }
-        return Interpreter.interpret(
-            this, cx, scope, scope, ScriptRuntime.emptyArgs);
+        cx.processMicrotasks();
+        return ret;
     }
 
     public boolean isScript() {
@@ -129,52 +114,54 @@
     }
 
     @Override
-    public String getEncodedSource()
-    {
+    public String getEncodedSource() {
         return Interpreter.getEncodedSource(idata);
     }
 
     @Override
-    public DebuggableScript getDebuggableView()
-    {
+    public DebuggableScript getDebuggableView() {
         return idata;
     }
 
     @Override
-    public Object resumeGenerator(Context cx, Scriptable scope, int operation,
-                                  Object state, Object value)
-    {
+    public Object resumeGenerator(
+            Context cx, Scriptable scope, int operation, Object state, Object value) {
         return Interpreter.resumeGenerator(cx, scope, operation, state, value);
     }
 
     @Override
-    protected int getLanguageVersion()
-    {
+    protected int getLanguageVersion() {
         return idata.languageVersion;
     }
 
     @Override
-    protected int getParamCount()
-    {
+    protected int getParamCount() {
         return idata.argCount;
     }
 
     @Override
-    protected int getParamAndVarCount()
-    {
+    protected int getParamAndVarCount() {
         return idata.argNames.length;
     }
 
     @Override
-    protected String getParamOrVarName(int index)
-    {
+    protected String getParamOrVarName(int index) {
         return idata.argNames[index];
     }
 
     @Override
-    protected boolean getParamOrVarConst(int index)
-    {
+    protected boolean getParamOrVarConst(int index) {
         return idata.argIsConst[index];
     }
-}
 
+    boolean hasFunctionNamed(String name) {
+        for (int f = 0; f < idata.getFunctionCount(); f++) {
+            InterpreterData functionData = (InterpreterData) idata.getFunction(f);
+            if (!functionData.declaredAsFunctionExpression
+                    && name.equals(functionData.getFunctionName())) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/InterpreterData.java rhino-1.7.14/src/org/mozilla/javascript/InterpreterData.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/InterpreterData.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/InterpreterData.java	2022-01-06 22:57:21.000000000 +0100
@@ -7,20 +7,20 @@
 package org.mozilla.javascript;
 
 import java.io.Serializable;
-
+import java.math.BigInteger;
+import java.util.Arrays;
 import org.mozilla.javascript.debug.DebuggableScript;
 
-final class InterpreterData implements Serializable, DebuggableScript
-{
-    static final long serialVersionUID = 5067677351589230234L;
+final class InterpreterData implements Serializable, DebuggableScript {
+    private static final long serialVersionUID = 5067677351589230234L;
 
     static final int INITIAL_MAX_ICODE_LENGTH = 1024;
     static final int INITIAL_STRINGTABLE_SIZE = 64;
     static final int INITIAL_NUMBERTABLE_SIZE = 64;
+    static final int INITIAL_BIGINTTABLE_SIZE = 64;
 
-    InterpreterData(int languageVersion, String sourceFile,
-                    String encodedSource, boolean isStrict)
-    {
+    InterpreterData(
+            int languageVersion, String sourceFile, String encodedSource, boolean isStrict) {
         this.languageVersion = languageVersion;
         this.itsSourceFile = sourceFile;
         this.encodedSource = encodedSource;
@@ -28,8 +28,7 @@
         init();
     }
 
-    InterpreterData(InterpreterData parent)
-    {
+    InterpreterData(InterpreterData parent) {
         this.parentData = parent;
         this.languageVersion = parent.languageVersion;
         this.itsSourceFile = parent.itsSourceFile;
@@ -38,10 +37,10 @@
         init();
     }
 
-    private void init()
-    {
+    private void init() {
         itsICode = new byte[INITIAL_MAX_ICODE_LENGTH];
         itsStringTable = new String[INITIAL_STRINGTABLE_SIZE];
+        itsBigIntTable = new BigInteger[INITIAL_BIGINTTABLE_SIZE];
     }
 
     String itsName;
@@ -51,8 +50,10 @@
 
     String[] itsStringTable;
     double[] itsDoubleTable;
+    BigInteger[] itsBigIntTable;
     InterpreterData[] itsNestedFunctions;
     Object[] itsRegExpLiterals;
+    Object[] itsTemplateLiterals;
 
     byte[] itsICode;
 
@@ -78,6 +79,7 @@
 
     boolean isStrict;
     boolean topLevel;
+    boolean isES6Generator;
 
     Object[] literalIds;
 
@@ -89,68 +91,83 @@
 
     boolean evalScriptFlag; // true if script corresponds to eval() code
 
-    public boolean isTopLevel()
-    {
+    private int icodeHashCode = 0;
+
+    /** true if the function has been declared like "var foo = function() {...}" */
+    boolean declaredAsVar;
+
+    /** true if the function has been declared like "!function() {}". */
+    boolean declaredAsFunctionExpression;
+
+    @Override
+    public boolean isTopLevel() {
         return topLevel;
     }
 
-    public boolean isFunction()
-    {
+    @Override
+    public boolean isFunction() {
         return itsFunctionType != 0;
     }
 
-    public String getFunctionName()
-    {
+    @Override
+    public String getFunctionName() {
         return itsName;
     }
 
-    public int getParamCount()
-    {
+    @Override
+    public int getParamCount() {
         return argCount;
     }
 
-    public int getParamAndVarCount()
-    {
+    @Override
+    public int getParamAndVarCount() {
         return argNames.length;
     }
 
-    public String getParamOrVarName(int index)
-    {
+    @Override
+    public String getParamOrVarName(int index) {
         return argNames[index];
     }
 
-    public boolean getParamOrVarConst(int index)
-    {
+    public boolean getParamOrVarConst(int index) {
         return argIsConst[index];
     }
 
-    public String getSourceName()
-    {
+    @Override
+    public String getSourceName() {
         return itsSourceFile;
     }
 
-    public boolean isGeneratedScript()
-    {
+    @Override
+    public boolean isGeneratedScript() {
         return ScriptRuntime.isGeneratedScript(itsSourceFile);
     }
 
-    public int[] getLineNumbers()
-    {
+    @Override
+    public int[] getLineNumbers() {
         return Interpreter.getLineNumbers(this);
     }
 
-    public int getFunctionCount()
-    {
+    @Override
+    public int getFunctionCount() {
         return (itsNestedFunctions == null) ? 0 : itsNestedFunctions.length;
     }
 
-    public DebuggableScript getFunction(int index)
-    {
+    @Override
+    public DebuggableScript getFunction(int index) {
         return itsNestedFunctions[index];
     }
 
-    public DebuggableScript getParent()
-    {
-         return parentData;
+    @Override
+    public DebuggableScript getParent() {
+        return parentData;
+    }
+
+    public int icodeHashCode() {
+        int h = icodeHashCode;
+        if (h == 0) {
+            icodeHashCode = h = Arrays.hashCode(itsICode);
+        }
+        return h;
     }
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/Interpreter.java rhino-1.7.14/src/org/mozilla/javascript/Interpreter.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/Interpreter.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/Interpreter.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,69 +6,71 @@
 
 package org.mozilla.javascript;
 
+import static org.mozilla.javascript.UniqueTag.DOUBLE_MARK;
+
 import java.io.PrintStream;
 import java.io.Serializable;
-import java.util.List;
+import java.math.BigInteger;
 import java.util.ArrayList;
-
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import org.mozilla.javascript.ScriptRuntime.NoSuchMethodShim;
 import org.mozilla.javascript.ast.FunctionNode;
 import org.mozilla.javascript.ast.ScriptNode;
-import org.mozilla.javascript.ScriptRuntime.NoSuchMethodShim;
 import org.mozilla.javascript.debug.DebugFrame;
 
-import static org.mozilla.javascript.UniqueTag.DOUBLE_MARK;
-
-public final class Interpreter extends Icode implements Evaluator
-{
+public final class Interpreter extends Icode implements Evaluator {
     // data for parsing
     InterpreterData itsData;
 
-    static final int EXCEPTION_TRY_START_SLOT  = 0;
-    static final int EXCEPTION_TRY_END_SLOT    = 1;
-    static final int EXCEPTION_HANDLER_SLOT    = 2;
-    static final int EXCEPTION_TYPE_SLOT       = 3;
-    static final int EXCEPTION_LOCAL_SLOT      = 4;
-    static final int EXCEPTION_SCOPE_SLOT      = 5;
+    static final int EXCEPTION_TRY_START_SLOT = 0;
+    static final int EXCEPTION_TRY_END_SLOT = 1;
+    static final int EXCEPTION_HANDLER_SLOT = 2;
+    static final int EXCEPTION_TYPE_SLOT = 3;
+    static final int EXCEPTION_LOCAL_SLOT = 4;
+    static final int EXCEPTION_SCOPE_SLOT = 5;
     // SLOT_SIZE: space for try start/end, handler, start, handler type,
     //            exception local and scope local
-    static final int EXCEPTION_SLOT_SIZE       = 6;
+    static final int EXCEPTION_SLOT_SIZE = 6;
 
-    /**
-     * Class to hold data corresponding to one interpreted call stack frame.
-     */
-    private static class CallFrame implements Cloneable, Serializable
-    {
-        static final long serialVersionUID = -2843792508994958978L;
+    /** Class to hold data corresponding to one interpreted call stack frame. */
+    private static class CallFrame implements Cloneable, Serializable {
+        private static final long serialVersionUID = -2843792508994958978L;
 
-        CallFrame parentFrame;
+        // fields marked "final" in a comment are effectively final except when they're modified
+        // immediately after cloning.
+
+        /*final*/ CallFrame parentFrame;
         // amount of stack frames before this one on the interpretation stack
-        int frameIndex;
+        /*final*/ int frameIndex;
         // If true indicates read-only frame that is a part of continuation
         boolean frozen;
 
-        InterpretedFunction fnOrScript;
-        InterpreterData idata;
+        final InterpretedFunction fnOrScript;
+        final InterpreterData idata;
 
-// Stack structure
-// stack[0 <= i < localShift]: arguments and local variables
-// stack[localShift <= i <= emptyStackTop]: used for local temporaries
-// stack[emptyStackTop < i < stack.length]: stack data
-// sDbl[i]: if stack[i] is UniqueTag.DOUBLE_MARK, sDbl[i] holds the number value
-
-        Object[] stack;
-        int[] stackAttributes;
-        double[] sDbl;
-        CallFrame varSource; // defaults to this unless continuation frame
-        int localShift;
-        int emptyStackTop;
+        // Stack structure
+        // stack[0 <= i < localShift]: arguments and local variables
+        // stack[localShift <= i <= emptyStackTop]: used for local temporaries
+        // stack[emptyStackTop < i < stack.length]: stack data
+        // sDbl[i]: if stack[i] is UniqueTag.DOUBLE_MARK, sDbl[i] holds the number value
+
+        /*final*/ Object[] stack;
+        /*final*/ int[] stackAttributes;
+        /*final*/ double[] sDbl;
+
+        final CallFrame varSource; // defaults to this unless continuation frame
+        final int localShift;
+        final int emptyStackTop;
 
-        DebugFrame debuggerFrame;
-        boolean useActivation;
+        final DebugFrame debuggerFrame;
+        final boolean useActivation;
         boolean isContinuationsTopFrame;
 
-        Scriptable thisObj;
+        final Scriptable thisObj;
 
-// The values that change during interpretation
+        // The values that change during interpretation
 
         Object result;
         double resultDbl;
@@ -81,13 +83,119 @@
         int savedCallOp;
         Object throwable;
 
-        CallFrame cloneFrozen()
-        {
+        CallFrame(
+                Context cx,
+                Scriptable thisObj,
+                InterpretedFunction fnOrScript,
+                CallFrame parentFrame) {
+            idata = fnOrScript.idata;
+
+            debuggerFrame = cx.debugger != null ? cx.debugger.getFrame(cx, idata) : null;
+            useActivation = debuggerFrame != null || idata.itsNeedsActivation;
+
+            emptyStackTop = idata.itsMaxVars + idata.itsMaxLocals - 1;
+            this.fnOrScript = fnOrScript;
+            varSource = this;
+            localShift = idata.itsMaxVars;
+            this.thisObj = thisObj;
+
+            this.parentFrame = parentFrame;
+            frameIndex = (parentFrame == null) ? 0 : parentFrame.frameIndex + 1;
+            if (frameIndex > cx.getMaximumInterpreterStackDepth()) {
+                throw Context.reportRuntimeError("Exceeded maximum stack depth");
+            }
+
+            // Initialize initial values of variables that change during
+            // interpretation.
+            result = Undefined.instance;
+            pcSourceLineStart = idata.firstLinePC;
+
+            savedStackTop = emptyStackTop;
+        }
+
+        void initializeArgs(
+                Context cx,
+                Scriptable callerScope,
+                Object[] args,
+                double[] argsDbl,
+                int argShift,
+                int argCount) {
+            if (useActivation) {
+                // Copy args to new array to pass to enterActivationFunction
+                // or debuggerFrame.onEnter
+                if (argsDbl != null) {
+                    args = getArgsArray(args, argsDbl, argShift, argCount);
+                }
+                argShift = 0;
+                argsDbl = null;
+            }
+
+            if (idata.itsFunctionType != 0) {
+                scope = fnOrScript.getParentScope();
+
+                if (useActivation) {
+                    if (idata.itsFunctionType == FunctionNode.ARROW_FUNCTION) {
+                        scope =
+                                ScriptRuntime.createArrowFunctionActivation(
+                                        fnOrScript, scope, args, idata.isStrict);
+                    } else {
+                        scope =
+                                ScriptRuntime.createFunctionActivation(
+                                        fnOrScript, scope, args, idata.isStrict);
+                    }
+                }
+            } else {
+                scope = callerScope;
+                ScriptRuntime.initScript(
+                        fnOrScript, thisObj, cx, scope, fnOrScript.idata.evalScriptFlag);
+            }
+
+            if (idata.itsNestedFunctions != null) {
+                if (idata.itsFunctionType != 0 && !idata.itsNeedsActivation) Kit.codeBug();
+                for (int i = 0; i < idata.itsNestedFunctions.length; i++) {
+                    InterpreterData fdata = idata.itsNestedFunctions[i];
+                    if (fdata.itsFunctionType == FunctionNode.FUNCTION_STATEMENT) {
+                        initFunction(cx, scope, fnOrScript, i);
+                    }
+                }
+            }
+
+            final int maxFrameArray = idata.itsMaxFrameArray;
+            // TODO: move this check into InterpreterData construction
+            if (maxFrameArray != emptyStackTop + idata.itsMaxStack + 1) Kit.codeBug();
+
+            // Initialize args, vars, locals and stack
+
+            stack = new Object[maxFrameArray];
+            stackAttributes = new int[maxFrameArray];
+            sDbl = new double[maxFrameArray];
+
+            int varCount = idata.getParamAndVarCount();
+            for (int i = 0; i < varCount; i++) {
+                if (idata.getParamOrVarConst(i)) stackAttributes[i] = ScriptableObject.CONST;
+            }
+            int definedArgs = idata.argCount;
+            if (definedArgs > argCount) {
+                definedArgs = argCount;
+            }
+
+            // Fill the frame structure
+
+            System.arraycopy(args, argShift, stack, 0, definedArgs);
+            if (argsDbl != null) {
+                System.arraycopy(argsDbl, argShift, sDbl, 0, definedArgs);
+            }
+            for (int i = definedArgs; i != idata.itsMaxVars; ++i) {
+                stack[i] = Undefined.instance;
+            }
+        }
+
+        CallFrame cloneFrozen() {
             if (!frozen) Kit.codeBug();
 
             CallFrame copy;
             try {
-                copy = (CallFrame)clone();
+                copy = (CallFrame) clone();
             } catch (CloneNotSupportedException ex) {
                 throw new IllegalStateException();
             }
@@ -102,20 +210,116 @@
             copy.frozen = false;
             return copy;
         }
+
+        @Override
+        public boolean equals(Object other) {
+            // Overridden for semantic equality comparison. These objects
+            // are typically exposed as NativeContinuation.implementation,
+            // comparing them allows establishing whether the continuations
+            // are semantically equal.
+            if (other instanceof CallFrame) {
+                // If the call is not within a Context with a top call, we force
+                // one. It is required as some objects within fully initialized
+                // global scopes (notably, XMLLibImpl) need to have a top scope
+                // in order to evaluate their attributes.
+                final Context cx = Context.enter();
+                try {
+                    if (ScriptRuntime.hasTopCall(cx)) {
+                        return equalsInTopScope(other).booleanValue();
+                    }
+                    final Scriptable top = ScriptableObject.getTopLevelScope(scope);
+                    return ((Boolean)
+                                    ScriptRuntime.doTopCall(
+                                            (Callable)
+                                                    (c, scope, thisObj, args) ->
+                                                            equalsInTopScope(other),
+                                            cx,
+                                            top,
+                                            top,
+                                            ScriptRuntime.emptyArgs,
+                                            isStrictTopFrame()))
+                            .booleanValue();
+                } finally {
+                    Context.exit();
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            // Overridden for consistency with equals.
+            // Trying to strike a balance between speed of calculation and
+            // distribution. Not hashing stack variables as those could have
+            // unbounded computational cost and limit it to topmost 8 frames.
+            int depth = 0;
+            CallFrame f = this;
+            int h = 0;
+            do {
+                h = 31 * (31 * h + f.pc) + f.idata.icodeHashCode();
+                f = f.parentFrame;
+            } while (f != null && depth++ < 8);
+            return h;
+        }
+
+        private Boolean equalsInTopScope(Object other) {
+            return EqualObjectGraphs.withThreadLocal(eq -> equals(this, (CallFrame) other, eq));
+        }
+
+        private boolean isStrictTopFrame() {
+            CallFrame f = this;
+            for (; ; ) {
+                final CallFrame p = f.parentFrame;
+                if (p == null) {
+                    return f.idata.isStrict;
+                }
+                f = p;
+            }
+        }
+
+        private static Boolean equals(CallFrame f1, CallFrame f2, EqualObjectGraphs equal) {
+            // Iterative instead of recursive, as interpreter stack depth can
+            // be larger than JVM stack depth.
+            for (; ; ) {
+                if (f1 == f2) {
+                    return Boolean.TRUE;
+                } else if (f1 == null || f2 == null) {
+                    return Boolean.FALSE;
+                } else if (!f1.fieldsEqual(f2, equal)) {
+                    return Boolean.FALSE;
+                } else {
+                    f1 = f1.parentFrame;
+                    f2 = f2.parentFrame;
+                }
+            }
+        }
+
+        private boolean fieldsEqual(CallFrame other, EqualObjectGraphs equal) {
+            return frameIndex == other.frameIndex
+                    && pc == other.pc
+                    && compareIdata(idata, other.idata)
+                    && equal.equalGraphs(varSource.stack, other.varSource.stack)
+                    && Arrays.equals(varSource.sDbl, other.varSource.sDbl)
+                    && equal.equalGraphs(thisObj, other.thisObj)
+                    && equal.equalGraphs(fnOrScript, other.fnOrScript)
+                    && equal.equalGraphs(scope, other.scope);
+        }
+    }
+
+    private static boolean compareIdata(InterpreterData i1, InterpreterData i2) {
+        return i1 == i2 || Objects.equals(getEncodedSource(i1), getEncodedSource(i2));
     }
 
-    private static final class ContinuationJump implements Serializable
-    {
-        static final long serialVersionUID = 7687739156004308247L;
+    private static final class ContinuationJump implements Serializable {
+        private static final long serialVersionUID = 7687739156004308247L;
 
         CallFrame capturedFrame;
         CallFrame branchFrame;
         Object result;
         double resultDbl;
 
-        ContinuationJump(NativeContinuation c, CallFrame current)
-        {
-            this.capturedFrame = (CallFrame)c.getImplementation();
+        ContinuationJump(NativeContinuation c, CallFrame current) {
+            this.capturedFrame = (CallFrame) c.getImplementation();
             if (this.capturedFrame == null || current == null) {
                 // Continuation and current execution does not share
                 // any frames if there is nothing to capture or
@@ -152,22 +356,21 @@
                 }
 
                 this.branchFrame = chain1;
-                if (this.branchFrame != null && !this.branchFrame.frozen)
-                    Kit.codeBug();
+                if (this.branchFrame != null && !this.branchFrame.frozen) Kit.codeBug();
             }
         }
     }
 
     private static CallFrame captureFrameForGenerator(CallFrame frame) {
-      frame.frozen = true;
-      CallFrame result = frame.cloneFrozen();
-      frame.frozen = false;
-
-      // now isolate this frame from its previous context
-      result.parentFrame = null;
-      result.frameIndex = 0;
+        frame.frozen = true;
+        CallFrame result = frame.cloneFrozen();
+        frame.frozen = false;
+
+        // now isolate this frame from its previous context
+        result.parentFrame = null;
+        result.frameIndex = 0;
 
-      return result;
+        return result;
     }
 
     static {
@@ -185,40 +388,37 @@
         }
     }
 
-    public Object compile(CompilerEnvirons compilerEnv,
-                          ScriptNode tree,
-                          String encodedSource,
-                          boolean returnFunction)
-    {
+    @Override
+    public Object compile(
+            CompilerEnvirons compilerEnv,
+            ScriptNode tree,
+            String encodedSource,
+            boolean returnFunction) {
         CodeGenerator cgen = new CodeGenerator();
         itsData = cgen.compile(compilerEnv, tree, encodedSource, returnFunction);
         return itsData;
     }
 
-    public Script createScriptObject(Object bytecode, Object staticSecurityDomain)
-    {
-        if(bytecode != itsData)
-        {
+    @Override
+    public Script createScriptObject(Object bytecode, Object staticSecurityDomain) {
+        if (bytecode != itsData) {
             Kit.codeBug();
         }
-        return InterpretedFunction.createScript(itsData,
-                                                staticSecurityDomain);
+        return InterpretedFunction.createScript(itsData, staticSecurityDomain);
     }
 
+    @Override
     public void setEvalScriptFlag(Script script) {
-        ((InterpretedFunction)script).idata.evalScriptFlag = true;
+        ((InterpretedFunction) script).idata.evalScriptFlag = true;
     }
 
-
-    public Function createFunctionObject(Context cx, Scriptable scope,
-            Object bytecode, Object staticSecurityDomain)
-    {
-        if(bytecode != itsData)
-        {
+    @Override
+    public Function createFunctionObject(
+            Context cx, Scriptable scope, Object bytecode, Object staticSecurityDomain) {
+        if (bytecode != itsData) {
             Kit.codeBug();
         }
-        return InterpretedFunction.createFunction(cx, scope, itsData,
-                                                  staticSecurityDomain);
+        return InterpretedFunction.createFunction(cx, scope, itsData, staticSecurityDomain);
     }
 
     private static int getShort(byte[] iCode, int pc) {
@@ -230,13 +430,13 @@
     }
 
     private static int getInt(byte[] iCode, int pc) {
-        return (iCode[pc] << 24) | ((iCode[pc + 1] & 0xFF) << 16)
-               | ((iCode[pc + 2] & 0xFF) << 8) | (iCode[pc + 3] & 0xFF);
+        return (iCode[pc] << 24)
+                | ((iCode[pc + 1] & 0xFF) << 16)
+                | ((iCode[pc + 2] & 0xFF) << 8)
+                | (iCode[pc + 3] & 0xFF);
     }
 
-    private static int getExceptionHandler(CallFrame frame,
-                                           boolean onlyFinally)
-    {
+    private static int getExceptionHandler(CallFrame frame, boolean onlyFinally) {
         int[] exceptionTable = frame.idata.itsExceptionTable;
         if (exceptionTable == null) {
             // No exception handlers
@@ -268,7 +468,7 @@
                 }
                 // Check the above assumption
                 if (bestStart > start) Kit.codeBug(); // should be nested
-                if (bestEnd == end) Kit.codeBug();  // no ens sharing
+                if (bestEnd == end) Kit.codeBug(); // no ens sharing
             }
             best = i;
             bestStart = start;
@@ -277,8 +477,7 @@
         return best;
     }
 
-    static void dumpICode(InterpreterData idata)
-    {
+    static void dumpICode(InterpreterData idata) {
         if (!Token.printICode) {
             return;
         }
@@ -286,9 +485,9 @@
         byte iCode[] = idata.itsICode;
         int iCodeLength = iCode.length;
         String[] strings = idata.itsStringTable;
+        BigInteger[] bigInts = idata.itsBigIntTable;
         PrintStream out = System.out;
-        out.println("ICode dump, for " + idata.itsName
-                    + ", length = " + iCodeLength);
+        out.println("ICode dump, for " + idata.itsName + ", length = " + iCodeLength);
         out.println("MaxStack = " + idata.itsMaxStack);
 
         int indexReg = 0;
@@ -301,213 +500,261 @@
             int old_pc = pc;
             ++pc;
             switch (token) {
-              default:
-                if (icodeLength != 1) Kit.codeBug();
-                out.println(tname);
-                break;
+                default:
+                    if (icodeLength != 1) Kit.codeBug();
+                    out.println(tname);
+                    break;
 
-              case Icode_GOSUB :
-              case Token.GOTO :
-              case Token.IFEQ :
-              case Token.IFNE :
-              case Icode_IFEQ_POP :
-              case Icode_LEAVEDQ : {
-                int newPC = pc + getShort(iCode, pc) - 1;
-                out.println(tname + " " + newPC);
-                pc += 2;
-                break;
-              }
-              case Icode_VAR_INC_DEC :
-              case Icode_NAME_INC_DEC :
-              case Icode_PROP_INC_DEC :
-              case Icode_ELEM_INC_DEC :
-              case Icode_REF_INC_DEC: {
-                int incrDecrType = iCode[pc];
-                out.println(tname + " " + incrDecrType);
-                ++pc;
-                break;
-              }
+                case Icode_GOSUB:
+                case Token.GOTO:
+                case Token.IFEQ:
+                case Token.IFNE:
+                case Icode_IFEQ_POP:
+                case Icode_LEAVEDQ:
+                    {
+                        int newPC = pc + getShort(iCode, pc) - 1;
+                        out.println(tname + " " + newPC);
+                        pc += 2;
+                        break;
+                    }
+                case Icode_VAR_INC_DEC:
+                case Icode_NAME_INC_DEC:
+                case Icode_PROP_INC_DEC:
+                case Icode_ELEM_INC_DEC:
+                case Icode_REF_INC_DEC:
+                    {
+                        int incrDecrType = iCode[pc];
+                        out.println(tname + " " + incrDecrType);
+                        ++pc;
+                        break;
+                    }
 
-              case Icode_CALLSPECIAL : {
-                int callType = iCode[pc] & 0xFF;
-                boolean isNew =  (iCode[pc + 1] != 0);
-                int line = getIndex(iCode, pc+2);
-                out.println(tname+" "+callType+" "+isNew+" "+indexReg+" "+line);
-                pc += 4;
-                break;
-              }
+                case Icode_CALLSPECIAL:
+                    {
+                        int callType = iCode[pc] & 0xFF;
+                        boolean isNew = (iCode[pc + 1] != 0);
+                        int line = getIndex(iCode, pc + 2);
+                        out.println(
+                                tname + " " + callType + " " + isNew + " " + indexReg + " " + line);
+                        pc += 4;
+                        break;
+                    }
 
-              case Token.CATCH_SCOPE:
-                {
-                    boolean afterFisrtFlag =  (iCode[pc] != 0);
-                    out.println(tname+" "+afterFisrtFlag);
+                case Token.CATCH_SCOPE:
+                    {
+                        boolean afterFisrtFlag = (iCode[pc] != 0);
+                        out.println(tname + " " + afterFisrtFlag);
+                        ++pc;
+                    }
+                    break;
+                case Token.REGEXP:
+                    out.println(tname + " " + idata.itsRegExpLiterals[indexReg]);
+                    break;
+                case Token.OBJECTLIT:
+                case Icode_SPARE_ARRAYLIT:
+                    out.println(tname + " " + idata.literalIds[indexReg]);
+                    break;
+                case Icode_CLOSURE_EXPR:
+                case Icode_CLOSURE_STMT:
+                    out.println(tname + " " + idata.itsNestedFunctions[indexReg]);
+                    break;
+                case Token.CALL:
+                case Icode_TAIL_CALL:
+                case Token.REF_CALL:
+                case Token.NEW:
+                    out.println(tname + ' ' + indexReg);
+                    break;
+                case Token.THROW:
+                case Token.YIELD:
+                case Icode_YIELD_STAR:
+                case Icode_GENERATOR:
+                case Icode_GENERATOR_END:
+                case Icode_GENERATOR_RETURN:
+                    {
+                        int line = getIndex(iCode, pc);
+                        out.println(tname + " : " + line);
+                        pc += 2;
+                        break;
+                    }
+                case Icode_SHORTNUMBER:
+                    {
+                        int value = getShort(iCode, pc);
+                        out.println(tname + " " + value);
+                        pc += 2;
+                        break;
+                    }
+                case Icode_INTNUMBER:
+                    {
+                        int value = getInt(iCode, pc);
+                        out.println(tname + " " + value);
+                        pc += 4;
+                        break;
+                    }
+                case Token.NUMBER:
+                    {
+                        double value = idata.itsDoubleTable[indexReg];
+                        out.println(tname + " " + value);
+                        break;
+                    }
+                case Icode_LINE:
+                    {
+                        int line = getIndex(iCode, pc);
+                        out.println(tname + " : " + line);
+                        pc += 2;
+                        break;
+                    }
+                case Icode_REG_STR1:
+                    {
+                        String str = strings[0xFF & iCode[pc]];
+                        out.println(tname + " \"" + str + '"');
+                        ++pc;
+                        break;
+                    }
+                case Icode_REG_STR2:
+                    {
+                        String str = strings[getIndex(iCode, pc)];
+                        out.println(tname + " \"" + str + '"');
+                        pc += 2;
+                        break;
+                    }
+                case Icode_REG_STR4:
+                    {
+                        String str = strings[getInt(iCode, pc)];
+                        out.println(tname + " \"" + str + '"');
+                        pc += 4;
+                        break;
+                    }
+                case Icode_REG_IND_C0:
+                    indexReg = 0;
+                    out.println(tname);
+                    break;
+                case Icode_REG_IND_C1:
+                    indexReg = 1;
+                    out.println(tname);
+                    break;
+                case Icode_REG_IND_C2:
+                    indexReg = 2;
+                    out.println(tname);
+                    break;
+                case Icode_REG_IND_C3:
+                    indexReg = 3;
+                    out.println(tname);
+                    break;
+                case Icode_REG_IND_C4:
+                    indexReg = 4;
+                    out.println(tname);
+                    break;
+                case Icode_REG_IND_C5:
+                    indexReg = 5;
+                    out.println(tname);
+                    break;
+                case Icode_REG_IND1:
+                    {
+                        indexReg = 0xFF & iCode[pc];
+                        out.println(tname + " " + indexReg);
+                        ++pc;
+                        break;
+                    }
+                case Icode_REG_IND2:
+                    {
+                        indexReg = getIndex(iCode, pc);
+                        out.println(tname + " " + indexReg);
+                        pc += 2;
+                        break;
+                    }
+                case Icode_REG_IND4:
+                    {
+                        indexReg = getInt(iCode, pc);
+                        out.println(tname + " " + indexReg);
+                        pc += 4;
+                        break;
+                    }
+                case Icode_GETVAR1:
+                case Icode_SETVAR1:
+                case Icode_SETCONSTVAR1:
+                    indexReg = iCode[pc];
+                    out.println(tname + " " + indexReg);
                     ++pc;
-                }
-                break;
-              case Token.REGEXP :
-                out.println(tname+" "+idata.itsRegExpLiterals[indexReg]);
-                break;
-              case Token.OBJECTLIT :
-              case Icode_SPARE_ARRAYLIT :
-                out.println(tname+" "+idata.literalIds[indexReg]);
-                break;
-              case Icode_CLOSURE_EXPR :
-              case Icode_CLOSURE_STMT :
-                out.println(tname+" "+idata.itsNestedFunctions[indexReg]);
-                break;
-              case Token.CALL :
-              case Icode_TAIL_CALL :
-              case Token.REF_CALL :
-              case Token.NEW :
-                out.println(tname+' '+indexReg);
-                break;
-              case Token.THROW :
-              case Token.YIELD :
-              case Icode_GENERATOR :
-              case Icode_GENERATOR_END :
-              {
-                int line = getIndex(iCode, pc);
-                out.println(tname + " : " + line);
-                pc += 2;
-                break;
-              }
-              case Icode_SHORTNUMBER : {
-                int value = getShort(iCode, pc);
-                out.println(tname + " " + value);
-                pc += 2;
-                break;
-              }
-              case Icode_INTNUMBER : {
-                int value = getInt(iCode, pc);
-                out.println(tname + " " + value);
-                pc += 4;
-                break;
-              }
-              case Token.NUMBER : {
-                double value = idata.itsDoubleTable[indexReg];
-                out.println(tname + " " + value);
-                break;
-              }
-              case Icode_LINE : {
-                int line = getIndex(iCode, pc);
-                out.println(tname + " : " + line);
-                pc += 2;
-                break;
-              }
-              case Icode_REG_STR1: {
-                String str = strings[0xFF & iCode[pc]];
-                out.println(tname + " \"" + str + '"');
-                ++pc;
-                break;
-              }
-              case Icode_REG_STR2: {
-                String str = strings[getIndex(iCode, pc)];
-                out.println(tname + " \"" + str + '"');
-                pc += 2;
-                break;
-              }
-              case Icode_REG_STR4: {
-                String str = strings[getInt(iCode, pc)];
-                out.println(tname + " \"" + str + '"');
-                pc += 4;
-                break;
-              }
-              case Icode_REG_IND_C0:
-                  indexReg = 0;
-                  out.println(tname);
-                  break;
-              case Icode_REG_IND_C1:
-                  indexReg = 1;
-                  out.println(tname);
-                  break;
-              case Icode_REG_IND_C2:
-                  indexReg = 2;
-                  out.println(tname);
-                  break;
-              case Icode_REG_IND_C3:
-                  indexReg = 3;
-                  out.println(tname);
-                  break;
-              case Icode_REG_IND_C4:
-                  indexReg = 4;
-                  out.println(tname);
-                  break;
-              case Icode_REG_IND_C5:
-                  indexReg = 5;
-                  out.println(tname);
-                  break;
-              case Icode_REG_IND1: {
-                indexReg = 0xFF & iCode[pc];
-                out.println(tname+" "+indexReg);
-                ++pc;
-                break;
-              }
-              case Icode_REG_IND2: {
-                indexReg = getIndex(iCode, pc);
-                out.println(tname+" "+indexReg);
-                pc += 2;
-                break;
-              }
-              case Icode_REG_IND4: {
-                indexReg = getInt(iCode, pc);
-                out.println(tname+" "+indexReg);
-                pc += 4;
-                break;
-              }
-              case Icode_GETVAR1:
-              case Icode_SETVAR1:
-              case Icode_SETCONSTVAR1:
-                indexReg = iCode[pc];
-                out.println(tname+" "+indexReg);
-                ++pc;
-                break;
+                    break;
+                    // TODO: Icode_REG_STR_C0-3 is not dump. I made this the same it.
+                case Icode_REG_BIGINT_C0:
+                case Icode_REG_BIGINT_C1:
+                case Icode_REG_BIGINT_C2:
+                case Icode_REG_BIGINT_C3:
+                    Kit.codeBug();
+                    break;
+                case Icode_REG_BIGINT1:
+                    {
+                        BigInteger bigInt = bigInts[0xFF & iCode[pc]];
+                        out.println(tname + " " + bigInt.toString() + 'n');
+                        ++pc;
+                        break;
+                    }
+                case Icode_REG_BIGINT2:
+                    {
+                        BigInteger bigInt = bigInts[getIndex(iCode, pc)];
+                        out.println(tname + " " + bigInt.toString() + 'n');
+                        pc += 2;
+                        break;
+                    }
+                case Icode_REG_BIGINT4:
+                    {
+                        BigInteger bigInt = bigInts[getInt(iCode, pc)];
+                        out.println(tname + " " + bigInt.toString() + 'n');
+                        pc += 4;
+                        break;
+                    }
             }
             if (old_pc + icodeLength != pc) Kit.codeBug();
         }
 
         int[] table = idata.itsExceptionTable;
         if (table != null) {
-            out.println("Exception handlers: "
-                         +table.length / EXCEPTION_SLOT_SIZE);
-            for (int i = 0; i != table.length;
-                 i += EXCEPTION_SLOT_SIZE)
-            {
-                int tryStart       = table[i + EXCEPTION_TRY_START_SLOT];
-                int tryEnd         = table[i + EXCEPTION_TRY_END_SLOT];
-                int handlerStart   = table[i + EXCEPTION_HANDLER_SLOT];
-                int type           = table[i + EXCEPTION_TYPE_SLOT];
+            out.println("Exception handlers: " + table.length / EXCEPTION_SLOT_SIZE);
+            for (int i = 0; i != table.length; i += EXCEPTION_SLOT_SIZE) {
+                int tryStart = table[i + EXCEPTION_TRY_START_SLOT];
+                int tryEnd = table[i + EXCEPTION_TRY_END_SLOT];
+                int handlerStart = table[i + EXCEPTION_HANDLER_SLOT];
+                int type = table[i + EXCEPTION_TYPE_SLOT];
                 int exceptionLocal = table[i + EXCEPTION_LOCAL_SLOT];
-                int scopeLocal     = table[i + EXCEPTION_SCOPE_SLOT];
 
-                out.println(" tryStart="+tryStart+" tryEnd="+tryEnd
-                            +" handlerStart="+handlerStart
-                            +" type="+(type == 0 ? "catch" : "finally")
-                            +" exceptionLocal="+exceptionLocal);
+                out.println(
+                        " tryStart="
+                                + tryStart
+                                + " tryEnd="
+                                + tryEnd
+                                + " handlerStart="
+                                + handlerStart
+                                + " type="
+                                + (type == 0 ? "catch" : "finally")
+                                + " exceptionLocal="
+                                + exceptionLocal);
             }
         }
         out.flush();
     }
 
-    private static int bytecodeSpan(int bytecode)
-    {
+    private static int bytecodeSpan(int bytecode) {
         switch (bytecode) {
-            case Token.THROW :
+            case Token.THROW:
             case Token.YIELD:
+            case Icode_YIELD_STAR:
             case Icode_GENERATOR:
             case Icode_GENERATOR_END:
+            case Icode_GENERATOR_RETURN:
                 // source line
                 return 1 + 2;
 
-            case Icode_GOSUB :
-            case Token.GOTO :
-            case Token.IFEQ :
-            case Token.IFNE :
-            case Icode_IFEQ_POP :
-            case Icode_LEAVEDQ :
+            case Icode_GOSUB:
+            case Token.GOTO:
+            case Token.IFEQ:
+            case Token.IFNE:
+            case Icode_IFEQ_POP:
+            case Icode_LEAVEDQ:
                 // target pc offset
                 return 1 + 2;
 
-            case Icode_CALLSPECIAL :
+            case Icode_CALLSPECIAL:
                 // call type
                 // is new
                 // line number
@@ -525,11 +772,11 @@
                 // type of ++/--
                 return 1 + 1;
 
-            case Icode_SHORTNUMBER :
+            case Icode_SHORTNUMBER:
                 // short number
                 return 1 + 2;
 
-            case Icode_INTNUMBER :
+            case Icode_INTNUMBER:
                 // int number
                 return 1 + 4;
 
@@ -563,7 +810,7 @@
                 // byte var index
                 return 1 + 1;
 
-            case Icode_LINE :
+            case Icode_LINE:
                 // line number
                 return 1 + 2;
         }
@@ -571,13 +818,12 @@
         return 1;
     }
 
-    static int[] getLineNumbers(InterpreterData data)
-    {
+    static int[] getLineNumbers(InterpreterData data) {
         UintMap presentLines = new UintMap();
 
         byte[] iCode = data.itsICode;
         int iCodeLength = iCode.length;
-        for (int pc = 0; pc != iCodeLength;) {
+        for (int pc = 0; pc != iCodeLength; ) {
             int bytecode = iCode[pc];
             int span = bytecodeSpan(bytecode);
             if (bytecode == Icode_LINE) {
@@ -591,8 +837,8 @@
         return presentLines.getKeys();
     }
 
-    public void captureStackInfo(RhinoException ex)
-    {
+    @Override
+    public void captureStackInfo(RhinoException ex) {
         Context cx = Context.getCurrentContext();
         if (cx == null || cx.lastInterpreterFrame == null) {
             // No interpreter invocations
@@ -603,14 +849,11 @@
         // has interpreter frame on the stack
         CallFrame[] array;
         if (cx.previousInterpreterInvocations == null
-            || cx.previousInterpreterInvocations.size() == 0)
-        {
+                || cx.previousInterpreterInvocations.size() == 0) {
             array = new CallFrame[1];
         } else {
             int previousCount = cx.previousInterpreterInvocations.size();
-            if (cx.previousInterpreterInvocations.peek()
-                == cx.lastInterpreterFrame)
-            {
+            if (cx.previousInterpreterInvocations.peek() == cx.lastInterpreterFrame) {
                 // It can happen if exception was generated after
                 // frame was pushed to cx.previousInterpreterInvocations
                 // but before assignment to cx.lastInterpreterFrame.
@@ -620,7 +863,7 @@
             array = new CallFrame[previousCount + 1];
             cx.previousInterpreterInvocations.toArray(array);
         }
-        array[array.length - 1]  = (CallFrame)cx.lastInterpreterFrame;
+        array[array.length - 1] = (CallFrame) cx.lastInterpreterFrame;
 
         int interpreterFrameCount = 0;
         for (int i = 0; i != array.length; ++i) {
@@ -631,7 +874,7 @@
         // Fill linePC with pc positions from all interpreter frames.
         // Start from the most nested frame
         int linePCIndex = interpreterFrameCount;
-        for (int i = array.length; i != 0;) {
+        for (int i = array.length; i != 0; ) {
             --i;
             CallFrame frame = array[i];
             while (frame != null) {
@@ -646,9 +889,9 @@
         ex.interpreterLineData = linePC;
     }
 
-    public String getSourcePositionFromStack(Context cx, int[] linep)
-    {
-        CallFrame frame = (CallFrame)cx.lastInterpreterFrame;
+    @Override
+    public String getSourcePositionFromStack(Context cx, int[] linep) {
+        CallFrame frame = (CallFrame) cx.lastInterpreterFrame;
         InterpreterData idata = frame.idata;
         if (frame.pcSourceLineStart >= 0) {
             linep[0] = getIndex(idata.itsICode, frame.pcSourceLineStart);
@@ -658,14 +901,13 @@
         return idata.itsSourceFile;
     }
 
-    public String getPatchedStack(RhinoException ex,
-                                  String nativeStackTrace)
-    {
+    @Override
+    public String getPatchedStack(RhinoException ex, String nativeStackTrace) {
         String tag = "org.mozilla.javascript.Interpreter.interpretLoop";
         StringBuilder sb = new StringBuilder(nativeStackTrace.length() + 1000);
         String lineSeparator = SecurityUtilities.getSystemProperty("line.separator");
 
-        CallFrame[] array = (CallFrame[])ex.interpreterStackInfo;
+        CallFrame[] array = (CallFrame[]) ex.interpreterStackInfo;
         int[] linePC = ex.interpreterLineData;
         int arrayIndex = array.length;
         int linePCIndex = linePC.length;
@@ -717,11 +959,11 @@
         return sb.toString();
     }
 
+    @Override
     public List<String> getScriptStack(RhinoException ex) {
         ScriptStackElement[][] stack = getScriptStackElements(ex);
         List<String> list = new ArrayList<String>(stack.length);
-        String lineSeparator =
-                SecurityUtilities.getSystemProperty("line.separator");
+        String lineSeparator = SecurityUtilities.getSystemProperty("line.separator");
         for (ScriptStackElement[] group : stack) {
             StringBuilder sb = new StringBuilder();
             for (ScriptStackElement elem : group) {
@@ -733,15 +975,14 @@
         return list;
     }
 
-    public ScriptStackElement[][] getScriptStackElements(RhinoException ex)
-    {
+    public ScriptStackElement[][] getScriptStackElements(RhinoException ex) {
         if (ex.interpreterStackInfo == null) {
             return null;
         }
 
         List<ScriptStackElement[]> list = new ArrayList<ScriptStackElement[]>();
 
-        CallFrame[] array = (CallFrame[])ex.interpreterStackInfo;
+        CallFrame[] array = (CallFrame[]) ex.interpreterStackInfo;
         int[] linePC = ex.interpreterLineData;
         int arrayIndex = array.length;
         int linePCIndex = linePC.length;
@@ -771,28 +1012,27 @@
         return list.toArray(new ScriptStackElement[list.size()][]);
     }
 
-    static String getEncodedSource(InterpreterData idata)
-    {
+    static String getEncodedSource(InterpreterData idata) {
         if (idata.encodedSource == null) {
             return null;
         }
-        return idata.encodedSource.substring(idata.encodedSourceStart,
-                                             idata.encodedSourceEnd);
+        return idata.encodedSource.substring(idata.encodedSourceStart, idata.encodedSourceEnd);
     }
 
-    private static void initFunction(Context cx, Scriptable scope,
-                                     InterpretedFunction parent, int index)
-    {
+    private static void initFunction(
+            Context cx, Scriptable scope, InterpretedFunction parent, int index) {
         InterpretedFunction fn;
         fn = InterpretedFunction.createFunction(cx, scope, parent, index);
-        ScriptRuntime.initFunction(cx, scope, fn, fn.idata.itsFunctionType,
-                                   parent.idata.evalScriptFlag);
+        ScriptRuntime.initFunction(
+                cx, scope, fn, fn.idata.itsFunctionType, parent.idata.evalScriptFlag);
     }
 
-    static Object interpret(InterpretedFunction ifun,
-                            Context cx, Scriptable scope,
-                            Scriptable thisObj, Object[] args)
-    {
+    static Object interpret(
+            InterpretedFunction ifun,
+            Context cx,
+            Scriptable scope,
+            Scriptable thisObj,
+            Object[] args) {
         if (!ScriptRuntime.hasTopCall(cx)) Kit.codeBug();
 
         if (cx.interpreterSecurityDomain != ifun.securityDomain) {
@@ -800,15 +1040,13 @@
             cx.interpreterSecurityDomain = ifun.securityDomain;
             try {
                 return ifun.securityController.callWithDomain(
-                    ifun.securityDomain, cx, ifun, scope, thisObj, args);
+                        ifun.securityDomain, cx, ifun, scope, thisObj, args);
             } finally {
                 cx.interpreterSecurityDomain = savedDomain;
             }
         }
 
-        CallFrame frame = new CallFrame();
-        initFrame(cx, scope, thisObj, args, null, 0, args.length,
-                  ifun, null, frame);
+        CallFrame frame = initFrame(cx, scope, thisObj, args, null, 0, args.length, ifun, null);
         frame.isContinuationsTopFrame = cx.isContinuationsTopCall;
         cx.isContinuationsTopCall = false;
 
@@ -820,38 +1058,32 @@
             this.operation = operation;
             this.value = value;
         }
+
         int operation;
         Object value;
         RuntimeException returnedException;
     }
 
-    public static Object resumeGenerator(Context cx,
-                                         Scriptable scope,
-                                         int operation,
-                                         Object savedState,
-                                         Object value)
-    {
-      CallFrame frame = (CallFrame) savedState;
-      GeneratorState generatorState = new GeneratorState(operation, value);
-      if (operation == NativeGenerator.GENERATOR_CLOSE) {
-          try {
-              return interpretLoop(cx, frame, generatorState);
-          } catch (RuntimeException e) {
-              // Only propagate exceptions other than closingException
-              if (e != value)
-                  throw e;
-          }
-          return Undefined.instance;
-      }
-      Object result = interpretLoop(cx, frame, generatorState);
-      if (generatorState.returnedException != null)
-          throw generatorState.returnedException;
-      return result;
-    }
-
-    public static Object restartContinuation(NativeContinuation c, Context cx,
-                                             Scriptable scope, Object[] args)
-    {
+    public static Object resumeGenerator(
+            Context cx, Scriptable scope, int operation, Object savedState, Object value) {
+        CallFrame frame = (CallFrame) savedState;
+        GeneratorState generatorState = new GeneratorState(operation, value);
+        if (operation == NativeGenerator.GENERATOR_CLOSE) {
+            try {
+                return interpretLoop(cx, frame, generatorState);
+            } catch (RuntimeException e) {
+                // Only propagate exceptions other than closingException
+                if (e != value) throw e;
+            }
+            return Undefined.instance;
+        }
+        Object result = interpretLoop(cx, frame, generatorState);
+        if (generatorState.returnedException != null) throw generatorState.returnedException;
+        return result;
+    }
+
+    public static Object restartContinuation(
+            NativeContinuation c, Context cx, Scriptable scope, Object[] args) {
         if (!ScriptRuntime.hasTopCall(cx)) {
             return ScriptRuntime.doTopCall(c, cx, scope, null, args, cx.isTopLevelStrict);
         }
@@ -863,7 +1095,7 @@
             arg = args[0];
         }
 
-        CallFrame capturedFrame = (CallFrame)c.getImplementation();
+        CallFrame capturedFrame = (CallFrame) c.getImplementation();
         if (capturedFrame == null) {
             // No frames to restart
             return arg;
@@ -875,9 +1107,7 @@
         return interpretLoop(cx, null, cjump);
     }
 
-    private static Object interpretLoop(Context cx, CallFrame frame,
-                                        Object throwable)
-    {
+    private static Object interpretLoop(Context cx, CallFrame frame, Object throwable) {
         // throwable holds exception object to rethrow or catch
         // It is also used for continuation restart in which case
         // it holds ContinuationJump
@@ -893,6 +1123,7 @@
         final int EXCEPTION_COST = 100;
 
         String stringReg = null;
+        BigInteger bigIntReg = null;
         int indexReg = -1;
 
         if (cx.lastInterpreterFrame != null) {
@@ -914,11 +1145,11 @@
         GeneratorState generatorState = null;
         if (throwable != null) {
             if (throwable instanceof GeneratorState) {
-              generatorState = (GeneratorState) throwable;
+                generatorState = (GeneratorState) throwable;
 
-              // reestablish this call frame
-              enterFrame(cx, frame, ScriptRuntime.emptyArgs, true);
-              throwable = null;
+                // reestablish this call frame
+                enterFrame(cx, frame, ScriptRuntime.emptyArgs, true);
+                throwable = null;
             } else if (!(throwable instanceof ContinuationJump)) {
                 // It should be continuation
                 Kit.codeBug();
@@ -928,15 +1159,16 @@
         Object interpreterResult = null;
         double interpreterResultDbl = 0.0;
 
-        StateLoop: for (;;) {
-            withoutExceptions: try {
+        StateLoop:
+        for (; ; ) {
+            withoutExceptions:
+            try {
 
                 if (throwable != null) {
                     // Need to return both 'frame' and 'throwable' from
                     // 'processThrowable', so just added a 'throwable'
                     // member in 'frame'.
-                    frame = processThrowable(cx, throwable, frame, indexReg,
-                                             instructionCounting);
+                    frame = processThrowable(cx, throwable, frame, indexReg, instructionCounting);
                     throwable = frame.throwable;
                     frame.throwable = null;
                 } else {
@@ -952,6 +1184,7 @@
                 int[] varAttributes = frame.varSource.stackAttributes;
                 byte[] iCode = frame.idata.itsICode;
                 String[] strings = frame.idata.itsStringTable;
+                BigInteger[] bigInts = frame.idata.itsBigIntTable;
 
                 // Use local for stackTop as well. Since execption handlers
                 // can only exist at statement level where stack is empty,
@@ -962,944 +1195,1223 @@
                 // Store new frame in cx which is used for error reporting etc.
                 cx.lastInterpreterFrame = frame;
 
-                Loop: for (;;) {
+                Loop:
+                for (; ; ) {
 
                     // Exception handler assumes that PC is already incremented
                     // pass the instruction start when it searches the
                     // exception handler
                     int op = iCode[frame.pc++];
-                    jumplessRun: {
-
-    // Back indent to ease implementation reading
-switch (op) {
-    case Icode_GENERATOR: {
-        if (!frame.frozen) {
-          // First time encountering this opcode: create new generator
-          // object and return
-          frame.pc--; // we want to come back here when we resume
-          CallFrame generatorFrame = captureFrameForGenerator(frame);
-          generatorFrame.frozen = true;
-          NativeGenerator generator = new NativeGenerator(frame.scope,
-              generatorFrame.fnOrScript, generatorFrame);
-          frame.result = generator;
-          break Loop;
-        } else {
-          // We are now resuming execution. Fall through to YIELD case.
-        }
-    }
-    // fall through...
-    case Token.YIELD: {
-        if (!frame.frozen) {
-            return freezeGenerator(cx, frame, stackTop, generatorState);
-        } else {
-            Object obj = thawGenerator(frame, stackTop, generatorState, op);
-            if (obj != Scriptable.NOT_FOUND) {
-                throwable = obj;
-                break withoutExceptions;
-            }
-            continue Loop;
-        }
-    }
-    case Icode_GENERATOR_END: {
-      // throw StopIteration
-      frame.frozen = true;
-      int sourceLine = getIndex(iCode, frame.pc);
-      generatorState.returnedException = new JavaScriptException(
-          NativeIterator.getStopIterationObject(frame.scope),
-          frame.idata.itsSourceFile, sourceLine);
-      break Loop;
-    }
-    case Token.THROW: {
-        Object value = stack[stackTop];
-        if (value == DBL_MRK) value = ScriptRuntime.wrapNumber(sDbl[stackTop]);
-        --stackTop;
+                    jumplessRun:
+                    {
 
-        int sourceLine = getIndex(iCode, frame.pc);
-        throwable = new JavaScriptException(value,
-                                            frame.idata.itsSourceFile,
-                                            sourceLine);
-        break withoutExceptions;
-    }
-    case Token.RETHROW: {
-        indexReg += frame.localShift;
-        throwable = stack[indexReg];
-        break withoutExceptions;
-    }
-    case Token.GE :
-    case Token.LE :
-    case Token.GT :
-    case Token.LT : {
-        stackTop = doCompare(frame, op, stack, sDbl, stackTop);
-        continue Loop;
-    }
-    case Token.IN :
-    case Token.INSTANCEOF : {
-        stackTop = doInOrInstanceof(cx, op, stack, sDbl, stackTop);
-        continue Loop;
-    }
-    case Token.EQ :
-    case Token.NE : {
-        --stackTop;
-        boolean valBln = doEquals(stack, sDbl, stackTop);
-        valBln ^= (op == Token.NE);
-        stack[stackTop] = ScriptRuntime.wrapBoolean(valBln);
-        continue Loop;
-    }
-    case Token.SHEQ :
-    case Token.SHNE : {
-        --stackTop;
-        boolean valBln = doShallowEquals(stack, sDbl, stackTop);
-        valBln ^= (op == Token.SHNE);
-        stack[stackTop] = ScriptRuntime.wrapBoolean(valBln);
-        continue Loop;
-    }
-    case Token.IFNE :
-        if (stack_boolean(frame, stackTop--)) {
-            frame.pc += 2;
-            continue Loop;
-        }
-        break jumplessRun;
-    case Token.IFEQ :
-        if (!stack_boolean(frame, stackTop--)) {
-            frame.pc += 2;
-            continue Loop;
-        }
-        break jumplessRun;
-    case Icode_IFEQ_POP :
-        if (!stack_boolean(frame, stackTop--)) {
-            frame.pc += 2;
-            continue Loop;
-        }
-        stack[stackTop--] = null;
-        break jumplessRun;
-    case Token.GOTO :
-        break jumplessRun;
-    case Icode_GOSUB :
-        ++stackTop;
-        stack[stackTop] = DBL_MRK;
-        sDbl[stackTop] = frame.pc + 2;
-        break jumplessRun;
-    case Icode_STARTSUB :
-        if (stackTop == frame.emptyStackTop + 1) {
-            // Call from Icode_GOSUB: store return PC address in the local
-            indexReg += frame.localShift;
-            stack[indexReg] = stack[stackTop];
-            sDbl[indexReg] = sDbl[stackTop];
-            --stackTop;
-        } else {
-            // Call from exception handler: exception object is already stored
-            // in the local
-            if (stackTop != frame.emptyStackTop) Kit.codeBug();
-        }
-        continue Loop;
-    case Icode_RETSUB : {
-        // indexReg: local to store return address
-        if (instructionCounting) {
-            addInstructionCount(cx, frame, 0);
-        }
-        indexReg += frame.localShift;
-        Object value = stack[indexReg];
-        if (value != DBL_MRK) {
-            // Invocation from exception handler, restore object to rethrow
-            throwable = value;
-            break withoutExceptions;
-        }
-        // Normal return from GOSUB
-        frame.pc = (int)sDbl[indexReg];
-        if (instructionCounting) {
-            frame.pcPrevBranch = frame.pc;
-        }
-        continue Loop;
-    }
-    case Icode_POP :
-        stack[stackTop] = null;
-        stackTop--;
-        continue Loop;
-    case Icode_POP_RESULT :
-        frame.result = stack[stackTop];
-        frame.resultDbl = sDbl[stackTop];
-        stack[stackTop] = null;
-        --stackTop;
-        continue Loop;
-    case Icode_DUP :
-        stack[stackTop + 1] = stack[stackTop];
-        sDbl[stackTop + 1] = sDbl[stackTop];
-        stackTop++;
-        continue Loop;
-    case Icode_DUP2 :
-        stack[stackTop + 1] = stack[stackTop - 1];
-        sDbl[stackTop + 1] = sDbl[stackTop - 1];
-        stack[stackTop + 2] = stack[stackTop];
-        sDbl[stackTop + 2] = sDbl[stackTop];
-        stackTop += 2;
-        continue Loop;
-    case Icode_SWAP : {
-        Object o = stack[stackTop];
-        stack[stackTop] = stack[stackTop - 1];
-        stack[stackTop - 1] = o;
-        double d = sDbl[stackTop];
-        sDbl[stackTop] = sDbl[stackTop - 1];
-        sDbl[stackTop - 1] = d;
-        continue Loop;
-    }
-    case Token.RETURN :
-        frame.result = stack[stackTop];
-        frame.resultDbl = sDbl[stackTop];
-        --stackTop;
-        break Loop;
-    case Token.RETURN_RESULT :
-        break Loop;
-    case Icode_RETUNDEF :
-        frame.result = undefined;
-        break Loop;
-    case Token.BITNOT : {
-        int rIntValue = stack_int32(frame, stackTop);
-        stack[stackTop] = DBL_MRK;
-        sDbl[stackTop] = ~rIntValue;
-        continue Loop;
-    }
-    case Token.BITAND :
-    case Token.BITOR :
-    case Token.BITXOR :
-    case Token.LSH :
-    case Token.RSH : {
-        stackTop = doBitOp(frame, op, stack, sDbl, stackTop);
-        continue Loop;
-    }
-    case Token.URSH : {
-        double lDbl = stack_double(frame, stackTop - 1);
-        int rIntValue = stack_int32(frame, stackTop) & 0x1F;
-        stack[--stackTop] = DBL_MRK;
-        sDbl[stackTop] = ScriptRuntime.toUint32(lDbl) >>> rIntValue;
-        continue Loop;
-    }
-    case Token.NEG :
-    case Token.POS : {
-        double rDbl = stack_double(frame, stackTop);
-        stack[stackTop] = DBL_MRK;
-        if (op == Token.NEG) {
-            rDbl = -rDbl;
-        }
-        sDbl[stackTop] = rDbl;
-        continue Loop;
-    }
-    case Token.ADD :
-        --stackTop;
-        doAdd(stack, sDbl, stackTop, cx);
-        continue Loop;
-    case Token.SUB :
-    case Token.MUL :
-    case Token.DIV :
-    case Token.MOD : {
-        stackTop = doArithmetic(frame, op, stack, sDbl, stackTop);
-        continue Loop;
-    }
-    case Token.NOT :
-        stack[stackTop] = ScriptRuntime.wrapBoolean(
-                              !stack_boolean(frame, stackTop));
-        continue Loop;
-    case Token.BINDNAME :
-        stack[++stackTop] = ScriptRuntime.bind(cx, frame.scope, stringReg);
-        continue Loop;
-    case Token.STRICT_SETNAME:
-    case Token.SETNAME : {
-        Object rhs = stack[stackTop];
-        if (rhs == DBL_MRK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
-        --stackTop;
-        Scriptable lhs = (Scriptable)stack[stackTop];
-        stack[stackTop] = op == Token.SETNAME ?
-                ScriptRuntime.setName(lhs, rhs, cx,
-                                      frame.scope, stringReg) :
-                ScriptRuntime.strictSetName(lhs, rhs, cx,
-                                      frame.scope, stringReg);
-        continue Loop;
-    }
-    case Icode_SETCONST: {
-        Object rhs = stack[stackTop];
-        if (rhs == DBL_MRK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
-        --stackTop;
-        Scriptable lhs = (Scriptable)stack[stackTop];
-        stack[stackTop] = ScriptRuntime.setConst(lhs, rhs, cx, stringReg);
-        continue Loop;
-    }
-    case Token.DELPROP :
-    case Icode_DELNAME : {
-        stackTop = doDelName(cx, frame, op, stack, sDbl, stackTop);
-        continue Loop;
-    }
-    case Token.GETPROPNOWARN : {
-        Object lhs = stack[stackTop];
-        if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
-        stack[stackTop] = ScriptRuntime.getObjectPropNoWarn(lhs, stringReg,
-                                                            cx, frame.scope);
-        continue Loop;
-    }
-    case Token.GETPROP : {
-        Object lhs = stack[stackTop];
-        if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
-        stack[stackTop] = ScriptRuntime.getObjectProp(lhs, stringReg,
-                                                      cx, frame.scope);
-        continue Loop;
-    }
-    case Token.SETPROP : {
-        Object rhs = stack[stackTop];
-        if (rhs == DBL_MRK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
-        --stackTop;
-        Object lhs = stack[stackTop];
-        if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
-        stack[stackTop] = ScriptRuntime.setObjectProp(lhs, stringReg, rhs,
-                                                      cx, frame.scope);
-        continue Loop;
-    }
-    case Icode_PROP_INC_DEC : {
-        Object lhs = stack[stackTop];
-        if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
-        stack[stackTop] = ScriptRuntime.propIncrDecr(lhs, stringReg,
-                                                     cx, frame.scope,
-                                                     iCode[frame.pc]);
-        ++frame.pc;
-        continue Loop;
-    }
-    case Token.GETELEM : {
-        stackTop = doGetElem(cx, frame, stack, sDbl, stackTop);
-        continue Loop;
-    }
-    case Token.SETELEM : {
-        stackTop = doSetElem(cx, frame, stack, sDbl, stackTop);
-        continue Loop;
-    }
-    case Icode_ELEM_INC_DEC: {
-        stackTop = doElemIncDec(cx, frame, iCode, stack, sDbl, stackTop);
-        continue Loop;
-    }
-    case Token.GET_REF : {
-        Ref ref = (Ref)stack[stackTop];
-        stack[stackTop] = ScriptRuntime.refGet(ref, cx);
-        continue Loop;
-    }
-    case Token.SET_REF : {
-        Object value = stack[stackTop];
-        if (value == DBL_MRK) value = ScriptRuntime.wrapNumber(sDbl[stackTop]);
-        --stackTop;
-        Ref ref = (Ref)stack[stackTop];
-        stack[stackTop] = ScriptRuntime.refSet(ref, value, cx, frame.scope);
-        continue Loop;
-    }
-    case Token.DEL_REF : {
-        Ref ref = (Ref)stack[stackTop];
-        stack[stackTop] = ScriptRuntime.refDel(ref, cx);
-        continue Loop;
-    }
-    case Icode_REF_INC_DEC : {
-        Ref ref = (Ref)stack[stackTop];
-        stack[stackTop] = ScriptRuntime.refIncrDecr(ref, cx, frame.scope,
+                        // Back indent to ease implementation reading
+                        switch (op) {
+                            case Icode_GENERATOR:
+                                {
+                                    if (!frame.frozen) {
+                                        // First time encountering this opcode: create new generator
+                                        // object and return
+                                        frame.pc--; // we want to come back here when we resume
+                                        CallFrame generatorFrame = captureFrameForGenerator(frame);
+                                        generatorFrame.frozen = true;
+                                        if (cx.getLanguageVersion() >= Context.VERSION_ES6) {
+                                            frame.result =
+                                                    new ES6Generator(
+                                                            frame.scope,
+                                                            generatorFrame.fnOrScript,
+                                                            generatorFrame);
+                                        } else {
+                                            frame.result =
+                                                    new NativeGenerator(
+                                                            frame.scope,
+                                                            generatorFrame.fnOrScript,
+                                                            generatorFrame);
+                                        }
+                                        break Loop;
+                                    }
+                                    // We are now resuming execution. Fall through to YIELD case.
+                                }
+                                // fall through...
+                            case Token.YIELD:
+                            case Icode_YIELD_STAR:
+                                {
+                                    if (!frame.frozen) {
+                                        return freezeGenerator(
+                                                cx,
+                                                frame,
+                                                stackTop,
+                                                generatorState,
+                                                op == Icode_YIELD_STAR);
+                                    }
+                                    Object obj = thawGenerator(frame, stackTop, generatorState, op);
+                                    if (obj != Scriptable.NOT_FOUND) {
+                                        throwable = obj;
+                                        break withoutExceptions;
+                                    }
+                                    continue Loop;
+                                }
+                            case Icode_GENERATOR_END:
+                                {
+                                    // throw StopIteration
+                                    frame.frozen = true;
+                                    int sourceLine = getIndex(iCode, frame.pc);
+                                    generatorState.returnedException =
+                                            new JavaScriptException(
+                                                    NativeIterator.getStopIterationObject(
+                                                            frame.scope),
+                                                    frame.idata.itsSourceFile,
+                                                    sourceLine);
+                                    break Loop;
+                                }
+                            case Icode_GENERATOR_RETURN:
+                                {
+                                    // throw StopIteration with the value of "return"
+                                    frame.frozen = true;
+                                    frame.result = stack[stackTop];
+                                    frame.resultDbl = sDbl[stackTop];
+                                    --stackTop;
+
+                                    NativeIterator.StopIteration si =
+                                            new NativeIterator.StopIteration(
+                                                    (frame.result == DOUBLE_MARK)
+                                                            ? Double.valueOf(frame.resultDbl)
+                                                            : frame.result);
+
+                                    int sourceLine = getIndex(iCode, frame.pc);
+                                    generatorState.returnedException =
+                                            new JavaScriptException(
+                                                    si, frame.idata.itsSourceFile, sourceLine);
+                                    break Loop;
+                                }
+                            case Token.THROW:
+                                {
+                                    Object value = stack[stackTop];
+                                    if (value == DBL_MRK)
+                                        value = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+                                    --stackTop;
+
+                                    int sourceLine = getIndex(iCode, frame.pc);
+                                    throwable =
+                                            new JavaScriptException(
+                                                    value, frame.idata.itsSourceFile, sourceLine);
+                                    break withoutExceptions;
+                                }
+                            case Token.RETHROW:
+                                {
+                                    indexReg += frame.localShift;
+                                    throwable = stack[indexReg];
+                                    break withoutExceptions;
+                                }
+                            case Token.GE:
+                            case Token.LE:
+                            case Token.GT:
+                            case Token.LT:
+                                {
+                                    stackTop = doCompare(frame, op, stack, sDbl, stackTop);
+                                    continue Loop;
+                                }
+                            case Token.IN:
+                            case Token.INSTANCEOF:
+                                {
+                                    stackTop = doInOrInstanceof(cx, op, stack, sDbl, stackTop);
+                                    continue Loop;
+                                }
+                            case Token.EQ:
+                            case Token.NE:
+                                {
+                                    --stackTop;
+                                    boolean valBln = doEquals(stack, sDbl, stackTop);
+                                    valBln ^= (op == Token.NE);
+                                    stack[stackTop] = ScriptRuntime.wrapBoolean(valBln);
+                                    continue Loop;
+                                }
+                            case Token.SHEQ:
+                            case Token.SHNE:
+                                {
+                                    --stackTop;
+                                    boolean valBln = doShallowEquals(stack, sDbl, stackTop);
+                                    valBln ^= (op == Token.SHNE);
+                                    stack[stackTop] = ScriptRuntime.wrapBoolean(valBln);
+                                    continue Loop;
+                                }
+                            case Token.IFNE:
+                                if (stack_boolean(frame, stackTop--)) {
+                                    frame.pc += 2;
+                                    continue Loop;
+                                }
+                                break jumplessRun;
+                            case Token.IFEQ:
+                                if (!stack_boolean(frame, stackTop--)) {
+                                    frame.pc += 2;
+                                    continue Loop;
+                                }
+                                break jumplessRun;
+                            case Icode_IFEQ_POP:
+                                if (!stack_boolean(frame, stackTop--)) {
+                                    frame.pc += 2;
+                                    continue Loop;
+                                }
+                                stack[stackTop--] = null;
+                                break jumplessRun;
+                            case Token.GOTO:
+                                break jumplessRun;
+                            case Icode_GOSUB:
+                                ++stackTop;
+                                stack[stackTop] = DBL_MRK;
+                                sDbl[stackTop] = frame.pc + 2;
+                                break jumplessRun;
+                            case Icode_STARTSUB:
+                                if (stackTop == frame.emptyStackTop + 1) {
+                                    // Call from Icode_GOSUB: store return PC address in the local
+                                    indexReg += frame.localShift;
+                                    stack[indexReg] = stack[stackTop];
+                                    sDbl[indexReg] = sDbl[stackTop];
+                                    --stackTop;
+                                } else {
+                                    // Call from exception handler: exception object is already
+                                    // stored
+                                    // in the local
+                                    if (stackTop != frame.emptyStackTop) Kit.codeBug();
+                                }
+                                continue Loop;
+                            case Icode_RETSUB:
+                                {
+                                    // indexReg: local to store return address
+                                    if (instructionCounting) {
+                                        addInstructionCount(cx, frame, 0);
+                                    }
+                                    indexReg += frame.localShift;
+                                    Object value = stack[indexReg];
+                                    if (value != DBL_MRK) {
+                                        // Invocation from exception handler, restore object to
+                                        // rethrow
+                                        throwable = value;
+                                        break withoutExceptions;
+                                    }
+                                    // Normal return from GOSUB
+                                    frame.pc = (int) sDbl[indexReg];
+                                    if (instructionCounting) {
+                                        frame.pcPrevBranch = frame.pc;
+                                    }
+                                    continue Loop;
+                                }
+                            case Icode_POP:
+                                stack[stackTop] = null;
+                                stackTop--;
+                                continue Loop;
+                            case Icode_POP_RESULT:
+                                frame.result = stack[stackTop];
+                                frame.resultDbl = sDbl[stackTop];
+                                stack[stackTop] = null;
+                                --stackTop;
+                                continue Loop;
+                            case Icode_DUP:
+                                stack[stackTop + 1] = stack[stackTop];
+                                sDbl[stackTop + 1] = sDbl[stackTop];
+                                stackTop++;
+                                continue Loop;
+                            case Icode_DUP2:
+                                stack[stackTop + 1] = stack[stackTop - 1];
+                                sDbl[stackTop + 1] = sDbl[stackTop - 1];
+                                stack[stackTop + 2] = stack[stackTop];
+                                sDbl[stackTop + 2] = sDbl[stackTop];
+                                stackTop += 2;
+                                continue Loop;
+                            case Icode_SWAP:
+                                {
+                                    Object o = stack[stackTop];
+                                    stack[stackTop] = stack[stackTop - 1];
+                                    stack[stackTop - 1] = o;
+                                    double d = sDbl[stackTop];
+                                    sDbl[stackTop] = sDbl[stackTop - 1];
+                                    sDbl[stackTop - 1] = d;
+                                    continue Loop;
+                                }
+                            case Token.RETURN:
+                                frame.result = stack[stackTop];
+                                frame.resultDbl = sDbl[stackTop];
+                                --stackTop;
+                                break Loop;
+                            case Token.RETURN_RESULT:
+                                break Loop;
+                            case Icode_RETUNDEF:
+                                frame.result = undefined;
+                                break Loop;
+                            case Token.BITNOT:
+                                {
+                                    stackTop = doBitNOT(frame, stack, sDbl, stackTop);
+                                    continue Loop;
+                                }
+                            case Token.BITAND:
+                            case Token.BITOR:
+                            case Token.BITXOR:
+                            case Token.LSH:
+                            case Token.RSH:
+                                {
+                                    stackTop = doBitOp(frame, op, stack, sDbl, stackTop);
+                                    continue Loop;
+                                }
+                            case Token.URSH:
+                                {
+                                    double lDbl = stack_double(frame, stackTop - 1);
+                                    int rIntValue = stack_int32(frame, stackTop) & 0x1F;
+                                    stack[--stackTop] = DBL_MRK;
+                                    sDbl[stackTop] = ScriptRuntime.toUint32(lDbl) >>> rIntValue;
+                                    continue Loop;
+                                }
+                            case Token.POS:
+                                {
+                                    double rDbl = stack_double(frame, stackTop);
+                                    stack[stackTop] = DBL_MRK;
+                                    sDbl[stackTop] = rDbl;
+                                    continue Loop;
+                                }
+                            case Token.NEG:
+                                {
+                                    Number rNum = stack_numeric(frame, stackTop);
+                                    Number rNegNum = ScriptRuntime.negate(rNum);
+                                    if (rNegNum instanceof BigInteger) {
+                                        stack[stackTop] = rNegNum;
+                                    } else {
+                                        stack[stackTop] = DBL_MRK;
+                                        sDbl[stackTop] = rNegNum.doubleValue();
+                                    }
+                                    continue Loop;
+                                }
+                            case Token.ADD:
+                                --stackTop;
+                                doAdd(stack, sDbl, stackTop, cx);
+                                continue Loop;
+                            case Token.SUB:
+                            case Token.MUL:
+                            case Token.DIV:
+                            case Token.MOD:
+                            case Token.EXP:
+                                {
+                                    stackTop = doArithmetic(frame, op, stack, sDbl, stackTop);
+                                    continue Loop;
+                                }
+                            case Token.NOT:
+                                stack[stackTop] =
+                                        ScriptRuntime.wrapBoolean(!stack_boolean(frame, stackTop));
+                                continue Loop;
+                            case Token.BINDNAME:
+                                stack[++stackTop] = ScriptRuntime.bind(cx, frame.scope, stringReg);
+                                continue Loop;
+                            case Token.STRICT_SETNAME:
+                            case Token.SETNAME:
+                                {
+                                    Object rhs = stack[stackTop];
+                                    if (rhs == DBL_MRK)
+                                        rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+                                    --stackTop;
+                                    Scriptable lhs = (Scriptable) stack[stackTop];
+                                    stack[stackTop] =
+                                            op == Token.SETNAME
+                                                    ? ScriptRuntime.setName(
+                                                            lhs, rhs, cx, frame.scope, stringReg)
+                                                    : ScriptRuntime.strictSetName(
+                                                            lhs, rhs, cx, frame.scope, stringReg);
+                                    continue Loop;
+                                }
+                            case Icode_SETCONST:
+                                {
+                                    Object rhs = stack[stackTop];
+                                    if (rhs == DBL_MRK)
+                                        rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+                                    --stackTop;
+                                    Scriptable lhs = (Scriptable) stack[stackTop];
+                                    stack[stackTop] =
+                                            ScriptRuntime.setConst(lhs, rhs, cx, stringReg);
+                                    continue Loop;
+                                }
+                            case Token.DELPROP:
+                            case Icode_DELNAME:
+                                {
+                                    stackTop = doDelName(cx, frame, op, stack, sDbl, stackTop);
+                                    continue Loop;
+                                }
+                            case Token.GETPROPNOWARN:
+                                {
+                                    Object lhs = stack[stackTop];
+                                    if (lhs == DBL_MRK)
+                                        lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+                                    stack[stackTop] =
+                                            ScriptRuntime.getObjectPropNoWarn(
+                                                    lhs, stringReg, cx, frame.scope);
+                                    continue Loop;
+                                }
+                            case Token.GETPROP:
+                                {
+                                    Object lhs = stack[stackTop];
+                                    if (lhs == DBL_MRK)
+                                        lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+                                    stack[stackTop] =
+                                            ScriptRuntime.getObjectProp(
+                                                    lhs, stringReg, cx, frame.scope);
+                                    continue Loop;
+                                }
+                            case Token.SETPROP:
+                                {
+                                    Object rhs = stack[stackTop];
+                                    if (rhs == DBL_MRK)
+                                        rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+                                    --stackTop;
+                                    Object lhs = stack[stackTop];
+                                    if (lhs == DBL_MRK)
+                                        lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+                                    stack[stackTop] =
+                                            ScriptRuntime.setObjectProp(
+                                                    lhs, stringReg, rhs, cx, frame.scope);
+                                    continue Loop;
+                                }
+                            case Icode_PROP_INC_DEC:
+                                {
+                                    Object lhs = stack[stackTop];
+                                    if (lhs == DBL_MRK)
+                                        lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+                                    stack[stackTop] =
+                                            ScriptRuntime.propIncrDecr(
+                                                    lhs,
+                                                    stringReg,
+                                                    cx,
+                                                    frame.scope,
                                                     iCode[frame.pc]);
-        ++frame.pc;
-        continue Loop;
-    }
-    case Token.LOCAL_LOAD :
-        ++stackTop;
-        indexReg += frame.localShift;
-        stack[stackTop] = stack[indexReg];
-        sDbl[stackTop] = sDbl[indexReg];
-        continue Loop;
-    case Icode_LOCAL_CLEAR :
-        indexReg += frame.localShift;
-        stack[indexReg] = null;
-        continue Loop;
-    case Icode_NAME_AND_THIS :
-        // stringReg: name
-        ++stackTop;
-        stack[stackTop] = ScriptRuntime.getNameFunctionAndThis(stringReg,
-                                                               cx, frame.scope);
-        ++stackTop;
-        stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx);
-        continue Loop;
-    case Icode_PROP_AND_THIS: {
-        Object obj = stack[stackTop];
-        if (obj == DBL_MRK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop]);
-        // stringReg: property
-        stack[stackTop] = ScriptRuntime.getPropFunctionAndThis(obj, stringReg,
-                                                               cx, frame.scope);
-        ++stackTop;
-        stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx);
-        continue Loop;
-    }
-    case Icode_ELEM_AND_THIS: {
-        Object obj = stack[stackTop - 1];
-        if (obj == DBL_MRK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop - 1]);
-        Object id = stack[stackTop];
-        if (id == DBL_MRK) id = ScriptRuntime.wrapNumber(sDbl[stackTop]);
-        stack[stackTop - 1] = ScriptRuntime.getElemFunctionAndThis(obj, id, cx,
-                                                                   frame.scope);
-        stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx);
-        continue Loop;
-    }
-    case Icode_VALUE_AND_THIS : {
-        Object value = stack[stackTop];
-        if (value == DBL_MRK) value = ScriptRuntime.wrapNumber(sDbl[stackTop]);
-        stack[stackTop] = ScriptRuntime.getValueFunctionAndThis(value, cx);
-        ++stackTop;
-        stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx);
-        continue Loop;
-    }
-    case Icode_CALLSPECIAL : {
-        if (instructionCounting) {
-            cx.instructionCount += INVOCATION_COST;
-        }
-        stackTop = doCallSpecial(cx, frame, stack, sDbl, stackTop, iCode, indexReg);
-        continue Loop;
-    }
-    case Token.CALL :
-    case Icode_TAIL_CALL :
-    case Token.REF_CALL : {
-        if (instructionCounting) {
-            cx.instructionCount += INVOCATION_COST;
-        }
-        // stack change: function thisObj arg0 .. argN -> result
-        // indexReg: number of arguments
-        stackTop -= 1 + indexReg;
-
-        // CALL generation ensures that fun and funThisObj
-        // are already Scriptable and Callable objects respectively
-        Callable fun = (Callable)stack[stackTop];
-        Scriptable funThisObj = (Scriptable)stack[stackTop + 1];
-        if (op == Token.REF_CALL) {
-            Object[] outArgs = getArgsArray(stack, sDbl, stackTop + 2,
-                                            indexReg);
-            stack[stackTop] = ScriptRuntime.callRef(fun, funThisObj,
-                                                    outArgs, cx);
-            continue Loop;
-        }
-        Scriptable calleeScope = frame.scope;
-        if (frame.useActivation) {
-            calleeScope = ScriptableObject.getTopLevelScope(frame.scope);
-        }
-        if (fun instanceof InterpretedFunction) {
-            InterpretedFunction ifun = (InterpretedFunction)fun;
-            if (frame.fnOrScript.securityDomain == ifun.securityDomain) {
-                CallFrame callParentFrame = frame;
-                CallFrame calleeFrame = new CallFrame();
-                if (op == Icode_TAIL_CALL) {
-                    // In principle tail call can re-use the current
-                    // frame and its stack arrays but it is hard to
-                    // do properly. Any exceptions that can legally
-                    // happen during frame re-initialization including
-                    // StackOverflowException during innocent looking
-                    // System.arraycopy may leave the current frame
-                    // data corrupted leading to undefined behaviour
-                    // in the catch code bellow that unwinds JS stack
-                    // on exceptions. Then there is issue about frame release
-                    // end exceptions there.
-                    // To avoid frame allocation a released frame
-                    // can be cached for re-use which would also benefit
-                    // non-tail calls but it is not clear that this caching
-                    // would gain in performance due to potentially
-                    // bad interaction with GC.
-                    callParentFrame = frame.parentFrame;
-                    // Release the current frame. See Bug #344501 to see why
-                    // it is being done here.
-                    exitFrame(cx, frame, null);
-                }
-                initFrame(cx, calleeScope, funThisObj, stack, sDbl,
-                          stackTop + 2, indexReg, ifun, callParentFrame,
-                          calleeFrame);
-                if (op != Icode_TAIL_CALL) {
-                    frame.savedStackTop = stackTop;
-                    frame.savedCallOp = op;
-                }
-                frame = calleeFrame;
-                continue StateLoop;
-            }
-        }
-
-        if (fun instanceof NativeContinuation) {
-            // Jump to the captured continuation
-            ContinuationJump cjump;
-            cjump = new ContinuationJump((NativeContinuation)fun, frame);
-
-            // continuation result is the first argument if any
-            // of continuation call
-            if (indexReg == 0) {
-                cjump.result = undefined;
-            } else {
-                cjump.result = stack[stackTop + 2];
-                cjump.resultDbl = sDbl[stackTop + 2];
-            }
-
-            // Start the real unwind job
-            throwable = cjump;
-            break withoutExceptions;
-        }
-
-        if (fun instanceof IdFunctionObject) {
-            IdFunctionObject ifun = (IdFunctionObject)fun;
-            if (NativeContinuation.isContinuationConstructor(ifun)) {
-                frame.stack[stackTop] = captureContinuation(cx,
-                        frame.parentFrame, false);
-                continue Loop;
-            }
-            // Bug 405654 -- make best effort to keep Function.apply and
-            // Function.call within this interpreter loop invocation
-            if (BaseFunction.isApplyOrCall(ifun)) {
-                Callable applyCallable = ScriptRuntime.getCallable(funThisObj);
-                if (applyCallable instanceof InterpretedFunction) {
-                    InterpretedFunction iApplyCallable = (InterpretedFunction)applyCallable;
-                    if (frame.fnOrScript.securityDomain == iApplyCallable.securityDomain) {
-                        frame = initFrameForApplyOrCall(cx, frame, indexReg,
-                                stack, sDbl, stackTop, op, calleeScope, ifun,
-                                iApplyCallable);
-                        continue StateLoop;
-                    }
-                }
-            }
-        }
-
-        // Bug 447697 -- make best effort to keep __noSuchMethod__ within this
-        // interpreter loop invocation
-        if (fun instanceof NoSuchMethodShim) {
-            // get the shim and the actual method
-            NoSuchMethodShim noSuchMethodShim = (NoSuchMethodShim) fun;
-            Callable noSuchMethodMethod = noSuchMethodShim.noSuchMethodMethod;
-            // if the method is in fact an InterpretedFunction
-            if (noSuchMethodMethod instanceof InterpretedFunction) {
-                InterpretedFunction ifun = (InterpretedFunction) noSuchMethodMethod;
-                if (frame.fnOrScript.securityDomain == ifun.securityDomain) {
-                    frame = initFrameForNoSuchMethod(cx, frame, indexReg, stack, sDbl,
-                                             stackTop, op, funThisObj, calleeScope,
-                                             noSuchMethodShim, ifun);
-                    continue StateLoop;
-                }
-            }
-        }
-
-        cx.lastInterpreterFrame = frame;
-        frame.savedCallOp = op;
-        frame.savedStackTop = stackTop;
-        stack[stackTop] = fun.call(cx, calleeScope, funThisObj,
-                getArgsArray(stack, sDbl, stackTop + 2, indexReg));
-
-        continue Loop;
-    }
-    case Token.NEW : {
-        if (instructionCounting) {
-            cx.instructionCount += INVOCATION_COST;
-        }
-        // stack change: function arg0 .. argN -> newResult
-        // indexReg: number of arguments
-        stackTop -= indexReg;
-
-        Object lhs = stack[stackTop];
-        if (lhs instanceof InterpretedFunction) {
-            InterpretedFunction f = (InterpretedFunction)lhs;
-            if (frame.fnOrScript.securityDomain == f.securityDomain) {
-                Scriptable newInstance = f.createObject(cx, frame.scope);
-                CallFrame calleeFrame = new CallFrame();
-                initFrame(cx, frame.scope, newInstance, stack, sDbl,
-                          stackTop + 1, indexReg, f, frame,
-                          calleeFrame);
-
-                stack[stackTop] = newInstance;
-                frame.savedStackTop = stackTop;
-                frame.savedCallOp = op;
-                frame = calleeFrame;
-                continue StateLoop;
-            }
-        }
-        if (!(lhs instanceof Function)) {
-            if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
-            throw ScriptRuntime.notFunctionError(lhs);
-        }
-        Function fun = (Function)lhs;
-
-        if (fun instanceof IdFunctionObject) {
-            IdFunctionObject ifun = (IdFunctionObject)fun;
-            if (NativeContinuation.isContinuationConstructor(ifun)) {
-                frame.stack[stackTop] =
-                    captureContinuation(cx, frame.parentFrame, false);
-                continue Loop;
-            }
-        }
-
-        Object[] outArgs = getArgsArray(stack, sDbl, stackTop + 1, indexReg);
-        stack[stackTop] = fun.construct(cx, frame.scope, outArgs);
-        continue Loop;
-    }
-    case Token.TYPEOF : {
-        Object lhs = stack[stackTop];
-        if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
-        stack[stackTop] = ScriptRuntime.typeof(lhs);
-        continue Loop;
-    }
-    case Icode_TYPEOFNAME :
-        stack[++stackTop] = ScriptRuntime.typeofName(frame.scope, stringReg);
-        continue Loop;
-    case Token.STRING :
-        stack[++stackTop] = stringReg;
-        continue Loop;
-    case Icode_SHORTNUMBER :
-        ++stackTop;
-        stack[stackTop] = DBL_MRK;
-        sDbl[stackTop] = getShort(iCode, frame.pc);
-        frame.pc += 2;
-        continue Loop;
-    case Icode_INTNUMBER :
-        ++stackTop;
-        stack[stackTop] = DBL_MRK;
-        sDbl[stackTop] = getInt(iCode, frame.pc);
-        frame.pc += 4;
-        continue Loop;
-    case Token.NUMBER :
-        ++stackTop;
-        stack[stackTop] = DBL_MRK;
-        sDbl[stackTop] = frame.idata.itsDoubleTable[indexReg];
-        continue Loop;
-    case Token.NAME :
-        stack[++stackTop] = ScriptRuntime.name(cx, frame.scope, stringReg);
-        continue Loop;
-    case Icode_NAME_INC_DEC :
-        stack[++stackTop] = ScriptRuntime.nameIncrDecr(frame.scope, stringReg,
-                                                       cx, iCode[frame.pc]);
-        ++frame.pc;
-        continue Loop;
-    case Icode_SETCONSTVAR1:
-        indexReg = iCode[frame.pc++];
-        // fallthrough
-    case Token.SETCONSTVAR :
-        stackTop = doSetConstVar(frame, stack, sDbl, stackTop, vars, varDbls,
-                                 varAttributes, indexReg);
-        continue Loop;
-    case Icode_SETVAR1:
-        indexReg = iCode[frame.pc++];
-        // fallthrough
-    case Token.SETVAR :
-        stackTop = doSetVar(frame, stack, sDbl, stackTop, vars, varDbls,
-                            varAttributes, indexReg);
-        continue Loop;
-    case Icode_GETVAR1:
-        indexReg = iCode[frame.pc++];
-        // fallthrough
-    case Token.GETVAR :
-        stackTop = doGetVar(frame, stack, sDbl, stackTop, vars, varDbls, indexReg);
-        continue Loop;
-    case Icode_VAR_INC_DEC : {
-        stackTop = doVarIncDec(cx, frame, stack, sDbl, stackTop,
-                               vars, varDbls, varAttributes, indexReg);
-        continue Loop;
-    }
-    case Icode_ZERO :
-        ++stackTop;
-        stack[stackTop] = DBL_MRK;
-        sDbl[stackTop] = 0;
-        continue Loop;
-    case Icode_ONE :
-        ++stackTop;
-        stack[stackTop] = DBL_MRK;
-        sDbl[stackTop] = 1;
-        continue Loop;
-    case Token.NULL :
-        stack[++stackTop] = null;
-        continue Loop;
-    case Token.THIS :
-        stack[++stackTop] = frame.thisObj;
-        continue Loop;
-    case Token.THISFN :
-        stack[++stackTop] = frame.fnOrScript;
-        continue Loop;
-    case Token.FALSE :
-        stack[++stackTop] = Boolean.FALSE;
-        continue Loop;
-    case Token.TRUE :
-        stack[++stackTop] = Boolean.TRUE;
-        continue Loop;
-    case Icode_UNDEF :
-        stack[++stackTop] = undefined;
-        continue Loop;
-    case Token.ENTERWITH : {
-        Object lhs = stack[stackTop];
-        if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
-        --stackTop;
-        frame.scope = ScriptRuntime.enterWith(lhs, cx, frame.scope);
-        continue Loop;
-    }
-    case Token.LEAVEWITH :
-        frame.scope = ScriptRuntime.leaveWith(frame.scope);
-        continue Loop;
-    case Token.CATCH_SCOPE : {
-        // stack top: exception object
-        // stringReg: name of exception variable
-        // indexReg: local for exception scope
-        --stackTop;
-        indexReg += frame.localShift;
-
-        boolean afterFirstScope =  (frame.idata.itsICode[frame.pc] != 0);
-        Throwable caughtException = (Throwable)stack[stackTop + 1];
-        Scriptable lastCatchScope;
-        if (!afterFirstScope) {
-            lastCatchScope = null;
-        } else {
-            lastCatchScope = (Scriptable)stack[indexReg];
-        }
-        stack[indexReg] = ScriptRuntime.newCatchScope(caughtException,
-                                                      lastCatchScope, stringReg,
-                                                      cx, frame.scope);
-        ++frame.pc;
-        continue Loop;
-    }
-    case Token.ENUM_INIT_KEYS :
-    case Token.ENUM_INIT_VALUES :
-    case Token.ENUM_INIT_ARRAY :
-    case Token.ENUM_INIT_VALUES_IN_ORDER : {
-        Object lhs = stack[stackTop];
-        if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
-        --stackTop;
-        indexReg += frame.localShift;
-        int enumType = op == Token.ENUM_INIT_KEYS
-                         ? ScriptRuntime.ENUMERATE_KEYS :
-                       op == Token.ENUM_INIT_VALUES
-                         ? ScriptRuntime.ENUMERATE_VALUES :
-                       op == Token.ENUM_INIT_VALUES_IN_ORDER
-                         ? ScriptRuntime.ENUMERATE_VALUES_IN_ORDER :
-                       ScriptRuntime.ENUMERATE_ARRAY;
-        stack[indexReg] = ScriptRuntime.enumInit(lhs, cx, frame.scope, enumType);
-        continue Loop;
-    }
-    case Token.ENUM_NEXT :
-    case Token.ENUM_ID : {
-        indexReg += frame.localShift;
-        Object val = stack[indexReg];
-        ++stackTop;
-        stack[stackTop] = (op == Token.ENUM_NEXT)
-                          ? (Object)ScriptRuntime.enumNext(val)
-                          : (Object)ScriptRuntime.enumId(val, cx);
-        continue Loop;
-    }
-    case Token.REF_SPECIAL : {
-        //stringReg: name of special property
-        Object obj = stack[stackTop];
-        if (obj == DBL_MRK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop]);
-        stack[stackTop] = ScriptRuntime.specialRef(obj, stringReg,
-                                                   cx, frame.scope);
-        continue Loop;
-    }
-    case Token.REF_MEMBER: {
-        //indexReg: flags
-        stackTop = doRefMember(cx, stack, sDbl, stackTop, indexReg);
-        continue Loop;
-    }
-    case Token.REF_NS_MEMBER: {
-        //indexReg: flags
-        stackTop = doRefNsMember(cx, stack, sDbl, stackTop, indexReg);
-        continue Loop;
-    }
-    case Token.REF_NAME: {
-        //indexReg: flags
-        Object name = stack[stackTop];
-        if (name == DBL_MRK) name = ScriptRuntime.wrapNumber(sDbl[stackTop]);
-        stack[stackTop] = ScriptRuntime.nameRef(name, cx, frame.scope,
+                                    ++frame.pc;
+                                    continue Loop;
+                                }
+                            case Token.GETELEM:
+                                {
+                                    stackTop = doGetElem(cx, frame, stack, sDbl, stackTop);
+                                    continue Loop;
+                                }
+                            case Token.SETELEM:
+                                {
+                                    stackTop = doSetElem(cx, frame, stack, sDbl, stackTop);
+                                    continue Loop;
+                                }
+                            case Icode_ELEM_INC_DEC:
+                                {
+                                    stackTop =
+                                            doElemIncDec(cx, frame, iCode, stack, sDbl, stackTop);
+                                    continue Loop;
+                                }
+                            case Token.GET_REF:
+                                {
+                                    Ref ref = (Ref) stack[stackTop];
+                                    stack[stackTop] = ScriptRuntime.refGet(ref, cx);
+                                    continue Loop;
+                                }
+                            case Token.SET_REF:
+                                {
+                                    Object value = stack[stackTop];
+                                    if (value == DBL_MRK)
+                                        value = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+                                    --stackTop;
+                                    Ref ref = (Ref) stack[stackTop];
+                                    stack[stackTop] =
+                                            ScriptRuntime.refSet(ref, value, cx, frame.scope);
+                                    continue Loop;
+                                }
+                            case Token.DEL_REF:
+                                {
+                                    Ref ref = (Ref) stack[stackTop];
+                                    stack[stackTop] = ScriptRuntime.refDel(ref, cx);
+                                    continue Loop;
+                                }
+                            case Icode_REF_INC_DEC:
+                                {
+                                    Ref ref = (Ref) stack[stackTop];
+                                    stack[stackTop] =
+                                            ScriptRuntime.refIncrDecr(
+                                                    ref, cx, frame.scope, iCode[frame.pc]);
+                                    ++frame.pc;
+                                    continue Loop;
+                                }
+                            case Token.LOCAL_LOAD:
+                                ++stackTop;
+                                indexReg += frame.localShift;
+                                stack[stackTop] = stack[indexReg];
+                                sDbl[stackTop] = sDbl[indexReg];
+                                continue Loop;
+                            case Icode_LOCAL_CLEAR:
+                                indexReg += frame.localShift;
+                                stack[indexReg] = null;
+                                continue Loop;
+                            case Icode_NAME_AND_THIS:
+                                // stringReg: name
+                                ++stackTop;
+                                stack[stackTop] =
+                                        ScriptRuntime.getNameFunctionAndThis(
+                                                stringReg, cx, frame.scope);
+                                ++stackTop;
+                                stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx);
+                                continue Loop;
+                            case Icode_PROP_AND_THIS:
+                                {
+                                    Object obj = stack[stackTop];
+                                    if (obj == DBL_MRK)
+                                        obj = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+                                    // stringReg: property
+                                    stack[stackTop] =
+                                            ScriptRuntime.getPropFunctionAndThis(
+                                                    obj, stringReg, cx, frame.scope);
+                                    ++stackTop;
+                                    stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx);
+                                    continue Loop;
+                                }
+                            case Icode_ELEM_AND_THIS:
+                                {
+                                    Object obj = stack[stackTop - 1];
+                                    if (obj == DBL_MRK)
+                                        obj = ScriptRuntime.wrapNumber(sDbl[stackTop - 1]);
+                                    Object id = stack[stackTop];
+                                    if (id == DBL_MRK)
+                                        id = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+                                    stack[stackTop - 1] =
+                                            ScriptRuntime.getElemFunctionAndThis(
+                                                    obj, id, cx, frame.scope);
+                                    stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx);
+                                    continue Loop;
+                                }
+                            case Icode_VALUE_AND_THIS:
+                                {
+                                    Object value = stack[stackTop];
+                                    if (value == DBL_MRK)
+                                        value = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+                                    stack[stackTop] =
+                                            ScriptRuntime.getValueFunctionAndThis(value, cx);
+                                    ++stackTop;
+                                    stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx);
+                                    continue Loop;
+                                }
+                            case Icode_CALLSPECIAL:
+                                {
+                                    if (instructionCounting) {
+                                        cx.instructionCount += INVOCATION_COST;
+                                    }
+                                    stackTop =
+                                            doCallSpecial(
+                                                    cx, frame, stack, sDbl, stackTop, iCode,
+                                                    indexReg);
+                                    continue Loop;
+                                }
+                            case Token.CALL:
+                            case Icode_TAIL_CALL:
+                            case Token.REF_CALL:
+                                {
+                                    if (instructionCounting) {
+                                        cx.instructionCount += INVOCATION_COST;
+                                    }
+                                    // stack change: function thisObj arg0 .. argN -> result
+                                    // indexReg: number of arguments
+                                    stackTop -= 1 + indexReg;
+
+                                    // CALL generation ensures that fun and funThisObj
+                                    // are already Scriptable and Callable objects respectively
+                                    Callable fun = (Callable) stack[stackTop];
+                                    Scriptable funThisObj = (Scriptable) stack[stackTop + 1];
+                                    if (op == Token.REF_CALL) {
+                                        Object[] outArgs =
+                                                getArgsArray(stack, sDbl, stackTop + 2, indexReg);
+                                        stack[stackTop] =
+                                                ScriptRuntime.callRef(
+                                                        fun, funThisObj,
+                                                        outArgs, cx);
+                                        continue Loop;
+                                    }
+                                    Scriptable calleeScope = frame.scope;
+                                    if (frame.useActivation) {
+                                        calleeScope =
+                                                ScriptableObject.getTopLevelScope(frame.scope);
+                                    }
+                                    if (fun instanceof InterpretedFunction) {
+                                        InterpretedFunction ifun = (InterpretedFunction) fun;
+                                        if (frame.fnOrScript.securityDomain
+                                                == ifun.securityDomain) {
+                                            CallFrame callParentFrame = frame;
+                                            if (op == Icode_TAIL_CALL) {
+                                                // In principle tail call can re-use the current
+                                                // frame and its stack arrays but it is hard to
+                                                // do properly. Any exceptions that can legally
+                                                // happen during frame re-initialization including
+                                                // StackOverflowException during innocent looking
+                                                // System.arraycopy may leave the current frame
+                                                // data corrupted leading to undefined behaviour
+                                                // in the catch code bellow that unwinds JS stack
+                                                // on exceptions. Then there is issue about frame
+                                                // release
+                                                // end exceptions there.
+                                                // To avoid frame allocation a released frame
+                                                // can be cached for re-use which would also benefit
+                                                // non-tail calls but it is not clear that this
+                                                // caching
+                                                // would gain in performance due to potentially
+                                                // bad interaction with GC.
+                                                callParentFrame = frame.parentFrame;
+                                                // Release the current frame. See Bug #344501 to see
+                                                // why
+                                                // it is being done here.
+                                                exitFrame(cx, frame, null);
+                                            }
+                                            CallFrame calleeFrame =
+                                                    initFrame(
+                                                            cx,
+                                                            calleeScope,
+                                                            funThisObj,
+                                                            stack,
+                                                            sDbl,
+                                                            stackTop + 2,
+                                                            indexReg,
+                                                            ifun,
+                                                            callParentFrame);
+                                            if (op != Icode_TAIL_CALL) {
+                                                frame.savedStackTop = stackTop;
+                                                frame.savedCallOp = op;
+                                            }
+                                            frame = calleeFrame;
+                                            continue StateLoop;
+                                        }
+                                    }
+
+                                    if (fun instanceof NativeContinuation) {
+                                        // Jump to the captured continuation
+                                        ContinuationJump cjump;
+                                        cjump =
+                                                new ContinuationJump(
+                                                        (NativeContinuation) fun, frame);
+
+                                        // continuation result is the first argument if any
+                                        // of continuation call
+                                        if (indexReg == 0) {
+                                            cjump.result = undefined;
+                                        } else {
+                                            cjump.result = stack[stackTop + 2];
+                                            cjump.resultDbl = sDbl[stackTop + 2];
+                                        }
+
+                                        // Start the real unwind job
+                                        throwable = cjump;
+                                        break withoutExceptions;
+                                    }
+
+                                    if (fun instanceof IdFunctionObject) {
+                                        IdFunctionObject ifun = (IdFunctionObject) fun;
+                                        if (NativeContinuation.isContinuationConstructor(ifun)) {
+                                            frame.stack[stackTop] =
+                                                    captureContinuation(
+                                                            cx, frame.parentFrame, false);
+                                            continue Loop;
+                                        }
+                                        // Bug 405654 -- make best effort to keep Function.apply and
+                                        // Function.call within this interpreter loop invocation
+                                        if (BaseFunction.isApplyOrCall(ifun)) {
+                                            Callable applyCallable =
+                                                    ScriptRuntime.getCallable(funThisObj);
+                                            if (applyCallable instanceof InterpretedFunction) {
+                                                InterpretedFunction iApplyCallable =
+                                                        (InterpretedFunction) applyCallable;
+                                                if (frame.fnOrScript.securityDomain
+                                                        == iApplyCallable.securityDomain) {
+                                                    frame =
+                                                            initFrameForApplyOrCall(
+                                                                    cx,
+                                                                    frame,
+                                                                    indexReg,
+                                                                    stack,
+                                                                    sDbl,
+                                                                    stackTop,
+                                                                    op,
+                                                                    calleeScope,
+                                                                    ifun,
+                                                                    iApplyCallable);
+                                                    continue StateLoop;
+                                                }
+                                            }
+                                        }
+                                    }
+
+                                    // Bug 447697 -- make best effort to keep __noSuchMethod__
+                                    // within this
+                                    // interpreter loop invocation
+                                    if (fun instanceof NoSuchMethodShim) {
+                                        // get the shim and the actual method
+                                        NoSuchMethodShim noSuchMethodShim = (NoSuchMethodShim) fun;
+                                        Callable noSuchMethodMethod =
+                                                noSuchMethodShim.noSuchMethodMethod;
+                                        // if the method is in fact an InterpretedFunction
+                                        if (noSuchMethodMethod instanceof InterpretedFunction) {
+                                            InterpretedFunction ifun =
+                                                    (InterpretedFunction) noSuchMethodMethod;
+                                            if (frame.fnOrScript.securityDomain
+                                                    == ifun.securityDomain) {
+                                                frame =
+                                                        initFrameForNoSuchMethod(
+                                                                cx,
+                                                                frame,
+                                                                indexReg,
+                                                                stack,
+                                                                sDbl,
+                                                                stackTop,
+                                                                op,
+                                                                funThisObj,
+                                                                calleeScope,
+                                                                noSuchMethodShim,
+                                                                ifun);
+                                                continue StateLoop;
+                                            }
+                                        }
+                                    }
+
+                                    cx.lastInterpreterFrame = frame;
+                                    frame.savedCallOp = op;
+                                    frame.savedStackTop = stackTop;
+                                    stack[stackTop] =
+                                            fun.call(
+                                                    cx,
+                                                    calleeScope,
+                                                    funThisObj,
+                                                    getArgsArray(
+                                                            stack, sDbl, stackTop + 2, indexReg));
+
+                                    continue Loop;
+                                }
+                            case Token.NEW:
+                                {
+                                    if (instructionCounting) {
+                                        cx.instructionCount += INVOCATION_COST;
+                                    }
+                                    // stack change: function arg0 .. argN -> newResult
+                                    // indexReg: number of arguments
+                                    stackTop -= indexReg;
+
+                                    Object lhs = stack[stackTop];
+                                    if (lhs instanceof InterpretedFunction) {
+                                        InterpretedFunction f = (InterpretedFunction) lhs;
+                                        if (frame.fnOrScript.securityDomain == f.securityDomain) {
+                                            Scriptable newInstance =
+                                                    f.createObject(cx, frame.scope);
+                                            CallFrame calleeFrame =
+                                                    initFrame(
+                                                            cx,
+                                                            frame.scope,
+                                                            newInstance,
+                                                            stack,
+                                                            sDbl,
+                                                            stackTop + 1,
+                                                            indexReg,
+                                                            f,
+                                                            frame);
+
+                                            stack[stackTop] = newInstance;
+                                            frame.savedStackTop = stackTop;
+                                            frame.savedCallOp = op;
+                                            frame = calleeFrame;
+                                            continue StateLoop;
+                                        }
+                                    }
+                                    if (!(lhs instanceof Function)) {
+                                        if (lhs == DBL_MRK)
+                                            lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+                                        throw ScriptRuntime.notFunctionError(lhs);
+                                    }
+                                    Function fun = (Function) lhs;
+
+                                    if (fun instanceof IdFunctionObject) {
+                                        IdFunctionObject ifun = (IdFunctionObject) fun;
+                                        if (NativeContinuation.isContinuationConstructor(ifun)) {
+                                            frame.stack[stackTop] =
+                                                    captureContinuation(
+                                                            cx, frame.parentFrame, false);
+                                            continue Loop;
+                                        }
+                                    }
+
+                                    Object[] outArgs =
+                                            getArgsArray(stack, sDbl, stackTop + 1, indexReg);
+                                    stack[stackTop] = fun.construct(cx, frame.scope, outArgs);
+                                    continue Loop;
+                                }
+                            case Token.TYPEOF:
+                                {
+                                    Object lhs = stack[stackTop];
+                                    if (lhs == DBL_MRK)
+                                        lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+                                    stack[stackTop] = ScriptRuntime.typeof(lhs);
+                                    continue Loop;
+                                }
+                            case Icode_TYPEOFNAME:
+                                stack[++stackTop] =
+                                        ScriptRuntime.typeofName(frame.scope, stringReg);
+                                continue Loop;
+                            case Token.STRING:
+                                stack[++stackTop] = stringReg;
+                                continue Loop;
+                            case Icode_SHORTNUMBER:
+                                ++stackTop;
+                                stack[stackTop] = DBL_MRK;
+                                sDbl[stackTop] = getShort(iCode, frame.pc);
+                                frame.pc += 2;
+                                continue Loop;
+                            case Icode_INTNUMBER:
+                                ++stackTop;
+                                stack[stackTop] = DBL_MRK;
+                                sDbl[stackTop] = getInt(iCode, frame.pc);
+                                frame.pc += 4;
+                                continue Loop;
+                            case Token.NUMBER:
+                                ++stackTop;
+                                stack[stackTop] = DBL_MRK;
+                                sDbl[stackTop] = frame.idata.itsDoubleTable[indexReg];
+                                continue Loop;
+                            case Token.BIGINT:
+                                stack[++stackTop] = bigIntReg;
+                                continue Loop;
+                            case Token.NAME:
+                                stack[++stackTop] = ScriptRuntime.name(cx, frame.scope, stringReg);
+                                continue Loop;
+                            case Icode_NAME_INC_DEC:
+                                stack[++stackTop] =
+                                        ScriptRuntime.nameIncrDecr(
+                                                frame.scope, stringReg, cx, iCode[frame.pc]);
+                                ++frame.pc;
+                                continue Loop;
+                            case Icode_SETCONSTVAR1:
+                                indexReg = iCode[frame.pc++];
+                                // fallthrough
+                            case Token.SETCONSTVAR:
+                                stackTop =
+                                        doSetConstVar(
+                                                frame,
+                                                stack,
+                                                sDbl,
+                                                stackTop,
+                                                vars,
+                                                varDbls,
+                                                varAttributes,
                                                 indexReg);
-        continue Loop;
-    }
-    case Token.REF_NS_NAME: {
-        //indexReg: flags
-        stackTop = doRefNsName(cx, frame, stack, sDbl, stackTop, indexReg);
-        continue Loop;
-    }
-    case Icode_SCOPE_LOAD :
-        indexReg += frame.localShift;
-        frame.scope = (Scriptable)stack[indexReg];
-        continue Loop;
-    case Icode_SCOPE_SAVE :
-        indexReg += frame.localShift;
-        stack[indexReg] = frame.scope;
-        continue Loop;
-    case Icode_CLOSURE_EXPR :
-        InterpretedFunction fn = InterpretedFunction.createFunction(cx, frame.scope,
-                                                                    frame.fnOrScript,
-                                                                    indexReg);
-        if (fn.idata.itsFunctionType == FunctionNode.ARROW_FUNCTION) {
-            stack[++stackTop] = new ArrowFunction(cx, frame.scope, fn, frame.thisObj);
-        } else {
-            stack[++stackTop] = fn;
-        }
-        continue Loop;
-    case Icode_CLOSURE_STMT :
-        initFunction(cx, frame.scope, frame.fnOrScript, indexReg);
-        continue Loop;
-    case Token.REGEXP :
-        Object re = frame.idata.itsRegExpLiterals[indexReg];
-        stack[++stackTop] = ScriptRuntime.wrapRegExp(cx, frame.scope, re);
-        continue Loop;
-    case Icode_LITERAL_NEW :
-        // indexReg: number of values in the literal
-        ++stackTop;
-        stack[stackTop] = new int[indexReg];
-        ++stackTop;
-        stack[stackTop] = new Object[indexReg];
-        sDbl[stackTop] = 0;
-        continue Loop;
-    case Icode_LITERAL_SET : {
-        Object value = stack[stackTop];
-        if (value == DBL_MRK) value = ScriptRuntime.wrapNumber(sDbl[stackTop]);
-        --stackTop;
-        int i = (int)sDbl[stackTop];
-        ((Object[])stack[stackTop])[i] = value;
-        sDbl[stackTop] = i + 1;
-        continue Loop;
-    }
-    case Icode_LITERAL_GETTER : {
-        Object value = stack[stackTop];
-        --stackTop;
-        int i = (int)sDbl[stackTop];
-        ((Object[])stack[stackTop])[i] = value;
-        ((int[])stack[stackTop - 1])[i] = -1;
-        sDbl[stackTop] = i + 1;
-        continue Loop;
-    }
-    case Icode_LITERAL_SETTER : {
-        Object value = stack[stackTop];
-        --stackTop;
-        int i = (int)sDbl[stackTop];
-        ((Object[])stack[stackTop])[i] = value;
-        ((int[])stack[stackTop - 1])[i] = +1;
-        sDbl[stackTop] = i + 1;
-        continue Loop;
-    }
-    case Token.ARRAYLIT :
-    case Icode_SPARE_ARRAYLIT :
-    case Token.OBJECTLIT : {
-        Object[] data = (Object[])stack[stackTop];
-        --stackTop;
-        int[] getterSetters = (int[])stack[stackTop];
-        Object val;
-        if (op == Token.OBJECTLIT) {
-            Object[] ids = (Object[])frame.idata.literalIds[indexReg];
-            val = ScriptRuntime.newObjectLiteral(ids, data, getterSetters, cx,
-                    frame.scope);
-        } else {
-            int[] skipIndexces = null;
-            if (op == Icode_SPARE_ARRAYLIT) {
-                skipIndexces = (int[])frame.idata.literalIds[indexReg];
-            }
-            val = ScriptRuntime.newArrayLiteral(data, skipIndexces, cx,
-                                                frame.scope);
-        }
-        stack[stackTop] = val;
-        continue Loop;
-    }
-    case Icode_ENTERDQ : {
-        Object lhs = stack[stackTop];
-        if (lhs == DBL_MRK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
-        --stackTop;
-        frame.scope = ScriptRuntime.enterDotQuery(lhs, frame.scope);
-        continue Loop;
-    }
-    case Icode_LEAVEDQ : {
-        boolean valBln = stack_boolean(frame, stackTop);
-        Object x = ScriptRuntime.updateDotQuery(valBln, frame.scope);
-        if (x != null) {
-            stack[stackTop] = x;
-            frame.scope = ScriptRuntime.leaveDotQuery(frame.scope);
-            frame.pc += 2;
-            continue Loop;
-        }
-        // reset stack and PC to code after ENTERDQ
-        --stackTop;
-        break jumplessRun;
-    }
-    case Token.DEFAULTNAMESPACE : {
-        Object value = stack[stackTop];
-        if (value == DBL_MRK) value = ScriptRuntime.wrapNumber(sDbl[stackTop]);
-        stack[stackTop] = ScriptRuntime.setDefaultNamespace(value, cx);
-        continue Loop;
-    }
-    case Token.ESCXMLATTR : {
-        Object value = stack[stackTop];
-        if (value != DBL_MRK) {
-            stack[stackTop] = ScriptRuntime.escapeAttributeValue(value, cx);
-        }
-        continue Loop;
-    }
-    case Token.ESCXMLTEXT : {
-        Object value = stack[stackTop];
-        if (value != DBL_MRK) {
-            stack[stackTop] = ScriptRuntime.escapeTextValue(value, cx);
-        }
-        continue Loop;
-    }
-    case Icode_DEBUGGER:
-        if (frame.debuggerFrame != null) {
-            frame.debuggerFrame.onDebuggerStatement(cx);
-        }
-        continue Loop;
-    case Icode_LINE :
-        frame.pcSourceLineStart = frame.pc;
-        if (frame.debuggerFrame != null) {
-            int line = getIndex(iCode, frame.pc);
-            frame.debuggerFrame.onLineChange(cx, line);
-        }
-        frame.pc += 2;
-        continue Loop;
-    case Icode_REG_IND_C0:
-        indexReg = 0;
-        continue Loop;
-    case Icode_REG_IND_C1:
-        indexReg = 1;
-        continue Loop;
-    case Icode_REG_IND_C2:
-        indexReg = 2;
-        continue Loop;
-    case Icode_REG_IND_C3:
-        indexReg = 3;
-        continue Loop;
-    case Icode_REG_IND_C4:
-        indexReg = 4;
-        continue Loop;
-    case Icode_REG_IND_C5:
-        indexReg = 5;
-        continue Loop;
-    case Icode_REG_IND1:
-        indexReg = 0xFF & iCode[frame.pc];
-        ++frame.pc;
-        continue Loop;
-    case Icode_REG_IND2:
-        indexReg = getIndex(iCode, frame.pc);
-        frame.pc += 2;
-        continue Loop;
-    case Icode_REG_IND4:
-        indexReg = getInt(iCode, frame.pc);
-        frame.pc += 4;
-        continue Loop;
-    case Icode_REG_STR_C0:
-        stringReg = strings[0];
-        continue Loop;
-    case Icode_REG_STR_C1:
-        stringReg = strings[1];
-        continue Loop;
-    case Icode_REG_STR_C2:
-        stringReg = strings[2];
-        continue Loop;
-    case Icode_REG_STR_C3:
-        stringReg = strings[3];
-        continue Loop;
-    case Icode_REG_STR1:
-        stringReg = strings[0xFF & iCode[frame.pc]];
-        ++frame.pc;
-        continue Loop;
-    case Icode_REG_STR2:
-        stringReg = strings[getIndex(iCode, frame.pc)];
-        frame.pc += 2;
-        continue Loop;
-    case Icode_REG_STR4:
-        stringReg = strings[getInt(iCode, frame.pc)];
-        frame.pc += 4;
-        continue Loop;
-    default :
-        dumpICode(frame.idata);
-        throw new RuntimeException("Unknown icode : " + op
-                                 + " @ pc : " + (frame.pc-1));
-}  // end of interpreter switch
-
+                                continue Loop;
+                            case Icode_SETVAR1:
+                                indexReg = iCode[frame.pc++];
+                                // fallthrough
+                            case Token.SETVAR:
+                                stackTop =
+                                        doSetVar(
+                                                frame,
+                                                stack,
+                                                sDbl,
+                                                stackTop,
+                                                vars,
+                                                varDbls,
+                                                varAttributes,
+                                                indexReg);
+                                continue Loop;
+                            case Icode_GETVAR1:
+                                indexReg = iCode[frame.pc++];
+                                // fallthrough
+                            case Token.GETVAR:
+                                stackTop =
+                                        doGetVar(
+                                                frame, stack, sDbl, stackTop, vars, varDbls,
+                                                indexReg);
+                                continue Loop;
+                            case Icode_VAR_INC_DEC:
+                                {
+                                    stackTop =
+                                            doVarIncDec(
+                                                    cx,
+                                                    frame,
+                                                    stack,
+                                                    sDbl,
+                                                    stackTop,
+                                                    vars,
+                                                    varDbls,
+                                                    varAttributes,
+                                                    indexReg);
+                                    continue Loop;
+                                }
+                            case Icode_ZERO:
+                                ++stackTop;
+                                stack[stackTop] = DBL_MRK;
+                                sDbl[stackTop] = 0;
+                                continue Loop;
+                            case Icode_ONE:
+                                ++stackTop;
+                                stack[stackTop] = DBL_MRK;
+                                sDbl[stackTop] = 1;
+                                continue Loop;
+                            case Token.NULL:
+                                stack[++stackTop] = null;
+                                continue Loop;
+                            case Token.THIS:
+                                stack[++stackTop] = frame.thisObj;
+                                continue Loop;
+                            case Token.THISFN:
+                                stack[++stackTop] = frame.fnOrScript;
+                                continue Loop;
+                            case Token.FALSE:
+                                stack[++stackTop] = Boolean.FALSE;
+                                continue Loop;
+                            case Token.TRUE:
+                                stack[++stackTop] = Boolean.TRUE;
+                                continue Loop;
+                            case Icode_UNDEF:
+                                stack[++stackTop] = undefined;
+                                continue Loop;
+                            case Token.ENTERWITH:
+                                {
+                                    Object lhs = stack[stackTop];
+                                    if (lhs == DBL_MRK)
+                                        lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+                                    --stackTop;
+                                    frame.scope = ScriptRuntime.enterWith(lhs, cx, frame.scope);
+                                    continue Loop;
+                                }
+                            case Token.LEAVEWITH:
+                                frame.scope = ScriptRuntime.leaveWith(frame.scope);
+                                continue Loop;
+                            case Token.CATCH_SCOPE:
+                                {
+                                    // stack top: exception object
+                                    // stringReg: name of exception variable
+                                    // indexReg: local for exception scope
+                                    --stackTop;
+                                    indexReg += frame.localShift;
+
+                                    boolean afterFirstScope = (frame.idata.itsICode[frame.pc] != 0);
+                                    Throwable caughtException = (Throwable) stack[stackTop + 1];
+                                    Scriptable lastCatchScope;
+                                    if (!afterFirstScope) {
+                                        lastCatchScope = null;
+                                    } else {
+                                        lastCatchScope = (Scriptable) stack[indexReg];
+                                    }
+                                    stack[indexReg] =
+                                            ScriptRuntime.newCatchScope(
+                                                    caughtException,
+                                                    lastCatchScope,
+                                                    stringReg,
+                                                    cx,
+                                                    frame.scope);
+                                    ++frame.pc;
+                                    continue Loop;
+                                }
+                            case Token.ENUM_INIT_KEYS:
+                            case Token.ENUM_INIT_VALUES:
+                            case Token.ENUM_INIT_ARRAY:
+                            case Token.ENUM_INIT_VALUES_IN_ORDER:
+                                {
+                                    Object lhs = stack[stackTop];
+                                    if (lhs == DBL_MRK)
+                                        lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+                                    --stackTop;
+                                    indexReg += frame.localShift;
+                                    int enumType =
+                                            op == Token.ENUM_INIT_KEYS
+                                                    ? ScriptRuntime.ENUMERATE_KEYS
+                                                    : op == Token.ENUM_INIT_VALUES
+                                                            ? ScriptRuntime.ENUMERATE_VALUES
+                                                            : op == Token.ENUM_INIT_VALUES_IN_ORDER
+                                                                    ? ScriptRuntime
+                                                                            .ENUMERATE_VALUES_IN_ORDER
+                                                                    : ScriptRuntime.ENUMERATE_ARRAY;
+                                    stack[indexReg] =
+                                            ScriptRuntime.enumInit(lhs, cx, frame.scope, enumType);
+                                    continue Loop;
+                                }
+                            case Token.ENUM_NEXT:
+                            case Token.ENUM_ID:
+                                {
+                                    indexReg += frame.localShift;
+                                    Object val = stack[indexReg];
+                                    ++stackTop;
+                                    stack[stackTop] =
+                                            (op == Token.ENUM_NEXT)
+                                                    ? (Object) ScriptRuntime.enumNext(val)
+                                                    : (Object) ScriptRuntime.enumId(val, cx);
+                                    continue Loop;
+                                }
+                            case Token.REF_SPECIAL:
+                                {
+                                    // stringReg: name of special property
+                                    Object obj = stack[stackTop];
+                                    if (obj == DBL_MRK)
+                                        obj = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+                                    stack[stackTop] =
+                                            ScriptRuntime.specialRef(
+                                                    obj, stringReg, cx, frame.scope);
+                                    continue Loop;
+                                }
+                            case Token.REF_MEMBER:
+                                {
+                                    // indexReg: flags
+                                    stackTop = doRefMember(cx, stack, sDbl, stackTop, indexReg);
+                                    continue Loop;
+                                }
+                            case Token.REF_NS_MEMBER:
+                                {
+                                    // indexReg: flags
+                                    stackTop = doRefNsMember(cx, stack, sDbl, stackTop, indexReg);
+                                    continue Loop;
+                                }
+                            case Token.REF_NAME:
+                                {
+                                    // indexReg: flags
+                                    Object name = stack[stackTop];
+                                    if (name == DBL_MRK)
+                                        name = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+                                    stack[stackTop] =
+                                            ScriptRuntime.nameRef(name, cx, frame.scope, indexReg);
+                                    continue Loop;
+                                }
+                            case Token.REF_NS_NAME:
+                                {
+                                    // indexReg: flags
+                                    stackTop =
+                                            doRefNsName(cx, frame, stack, sDbl, stackTop, indexReg);
+                                    continue Loop;
+                                }
+                            case Icode_SCOPE_LOAD:
+                                indexReg += frame.localShift;
+                                frame.scope = (Scriptable) stack[indexReg];
+                                continue Loop;
+                            case Icode_SCOPE_SAVE:
+                                indexReg += frame.localShift;
+                                stack[indexReg] = frame.scope;
+                                continue Loop;
+                            case Icode_CLOSURE_EXPR:
+                                InterpretedFunction fn =
+                                        InterpretedFunction.createFunction(
+                                                cx, frame.scope, frame.fnOrScript, indexReg);
+                                if (fn.idata.itsFunctionType == FunctionNode.ARROW_FUNCTION) {
+                                    stack[++stackTop] =
+                                            new ArrowFunction(cx, frame.scope, fn, frame.thisObj);
+                                } else {
+                                    stack[++stackTop] = fn;
+                                }
+                                continue Loop;
+                            case Icode_CLOSURE_STMT:
+                                initFunction(cx, frame.scope, frame.fnOrScript, indexReg);
+                                continue Loop;
+                            case Token.REGEXP:
+                                Object re = frame.idata.itsRegExpLiterals[indexReg];
+                                stack[++stackTop] = ScriptRuntime.wrapRegExp(cx, frame.scope, re);
+                                continue Loop;
+                            case Icode_TEMPLATE_LITERAL_CALLSITE:
+                                Object[] templateLiterals = frame.idata.itsTemplateLiterals;
+                                stack[++stackTop] =
+                                        ScriptRuntime.getTemplateLiteralCallSite(
+                                                cx, frame.scope, templateLiterals, indexReg);
+                                continue Loop;
+                            case Icode_LITERAL_NEW:
+                                // indexReg: number of values in the literal
+                                ++stackTop;
+                                stack[stackTop] = new int[indexReg];
+                                ++stackTop;
+                                stack[stackTop] = new Object[indexReg];
+                                sDbl[stackTop] = 0;
+                                continue Loop;
+                            case Icode_LITERAL_SET:
+                                {
+                                    Object value = stack[stackTop];
+                                    if (value == DBL_MRK)
+                                        value = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+                                    --stackTop;
+                                    int i = (int) sDbl[stackTop];
+                                    ((Object[]) stack[stackTop])[i] = value;
+                                    sDbl[stackTop] = i + 1;
+                                    continue Loop;
+                                }
+                            case Icode_LITERAL_GETTER:
+                                {
+                                    Object value = stack[stackTop];
+                                    --stackTop;
+                                    int i = (int) sDbl[stackTop];
+                                    ((Object[]) stack[stackTop])[i] = value;
+                                    ((int[]) stack[stackTop - 1])[i] = -1;
+                                    sDbl[stackTop] = i + 1;
+                                    continue Loop;
+                                }
+                            case Icode_LITERAL_SETTER:
+                                {
+                                    Object value = stack[stackTop];
+                                    --stackTop;
+                                    int i = (int) sDbl[stackTop];
+                                    ((Object[]) stack[stackTop])[i] = value;
+                                    ((int[]) stack[stackTop - 1])[i] = +1;
+                                    sDbl[stackTop] = i + 1;
+                                    continue Loop;
+                                }
+                            case Token.ARRAYLIT:
+                            case Icode_SPARE_ARRAYLIT:
+                            case Token.OBJECTLIT:
+                                {
+                                    Object[] data = (Object[]) stack[stackTop];
+                                    --stackTop;
+                                    int[] getterSetters = (int[]) stack[stackTop];
+                                    Object val;
+                                    if (op == Token.OBJECTLIT) {
+                                        Object[] ids = (Object[]) frame.idata.literalIds[indexReg];
+                                        val =
+                                                ScriptRuntime.newObjectLiteral(
+                                                        ids, data, getterSetters, cx, frame.scope);
+                                    } else {
+                                        int[] skipIndexces = null;
+                                        if (op == Icode_SPARE_ARRAYLIT) {
+                                            skipIndexces = (int[]) frame.idata.literalIds[indexReg];
+                                        }
+                                        val =
+                                                ScriptRuntime.newArrayLiteral(
+                                                        data, skipIndexces, cx, frame.scope);
+                                    }
+                                    stack[stackTop] = val;
+                                    continue Loop;
+                                }
+                            case Icode_ENTERDQ:
+                                {
+                                    Object lhs = stack[stackTop];
+                                    if (lhs == DBL_MRK)
+                                        lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+                                    --stackTop;
+                                    frame.scope = ScriptRuntime.enterDotQuery(lhs, frame.scope);
+                                    continue Loop;
+                                }
+                            case Icode_LEAVEDQ:
+                                {
+                                    boolean valBln = stack_boolean(frame, stackTop);
+                                    Object x = ScriptRuntime.updateDotQuery(valBln, frame.scope);
+                                    if (x != null) {
+                                        stack[stackTop] = x;
+                                        frame.scope = ScriptRuntime.leaveDotQuery(frame.scope);
+                                        frame.pc += 2;
+                                        continue Loop;
+                                    }
+                                    // reset stack and PC to code after ENTERDQ
+                                    --stackTop;
+                                    break jumplessRun;
+                                }
+                            case Token.DEFAULTNAMESPACE:
+                                {
+                                    Object value = stack[stackTop];
+                                    if (value == DBL_MRK)
+                                        value = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+                                    stack[stackTop] = ScriptRuntime.setDefaultNamespace(value, cx);
+                                    continue Loop;
+                                }
+                            case Token.ESCXMLATTR:
+                                {
+                                    Object value = stack[stackTop];
+                                    if (value != DBL_MRK) {
+                                        stack[stackTop] =
+                                                ScriptRuntime.escapeAttributeValue(value, cx);
+                                    }
+                                    continue Loop;
+                                }
+                            case Token.ESCXMLTEXT:
+                                {
+                                    Object value = stack[stackTop];
+                                    if (value != DBL_MRK) {
+                                        stack[stackTop] = ScriptRuntime.escapeTextValue(value, cx);
+                                    }
+                                    continue Loop;
+                                }
+                            case Icode_DEBUGGER:
+                                if (frame.debuggerFrame != null) {
+                                    frame.debuggerFrame.onDebuggerStatement(cx);
+                                }
+                                continue Loop;
+                            case Icode_LINE:
+                                frame.pcSourceLineStart = frame.pc;
+                                if (frame.debuggerFrame != null) {
+                                    int line = getIndex(iCode, frame.pc);
+                                    frame.debuggerFrame.onLineChange(cx, line);
+                                }
+                                frame.pc += 2;
+                                continue Loop;
+                            case Icode_REG_IND_C0:
+                                indexReg = 0;
+                                continue Loop;
+                            case Icode_REG_IND_C1:
+                                indexReg = 1;
+                                continue Loop;
+                            case Icode_REG_IND_C2:
+                                indexReg = 2;
+                                continue Loop;
+                            case Icode_REG_IND_C3:
+                                indexReg = 3;
+                                continue Loop;
+                            case Icode_REG_IND_C4:
+                                indexReg = 4;
+                                continue Loop;
+                            case Icode_REG_IND_C5:
+                                indexReg = 5;
+                                continue Loop;
+                            case Icode_REG_IND1:
+                                indexReg = 0xFF & iCode[frame.pc];
+                                ++frame.pc;
+                                continue Loop;
+                            case Icode_REG_IND2:
+                                indexReg = getIndex(iCode, frame.pc);
+                                frame.pc += 2;
+                                continue Loop;
+                            case Icode_REG_IND4:
+                                indexReg = getInt(iCode, frame.pc);
+                                frame.pc += 4;
+                                continue Loop;
+                            case Icode_REG_STR_C0:
+                                stringReg = strings[0];
+                                continue Loop;
+                            case Icode_REG_STR_C1:
+                                stringReg = strings[1];
+                                continue Loop;
+                            case Icode_REG_STR_C2:
+                                stringReg = strings[2];
+                                continue Loop;
+                            case Icode_REG_STR_C3:
+                                stringReg = strings[3];
+                                continue Loop;
+                            case Icode_REG_STR1:
+                                stringReg = strings[0xFF & iCode[frame.pc]];
+                                ++frame.pc;
+                                continue Loop;
+                            case Icode_REG_STR2:
+                                stringReg = strings[getIndex(iCode, frame.pc)];
+                                frame.pc += 2;
+                                continue Loop;
+                            case Icode_REG_STR4:
+                                stringReg = strings[getInt(iCode, frame.pc)];
+                                frame.pc += 4;
+                                continue Loop;
+                            case Icode_REG_BIGINT_C0:
+                                bigIntReg = bigInts[0];
+                                continue Loop;
+                            case Icode_REG_BIGINT_C1:
+                                bigIntReg = bigInts[1];
+                                continue Loop;
+                            case Icode_REG_BIGINT_C2:
+                                bigIntReg = bigInts[2];
+                                continue Loop;
+                            case Icode_REG_BIGINT_C3:
+                                bigIntReg = bigInts[3];
+                                continue Loop;
+                            case Icode_REG_BIGINT1:
+                                bigIntReg = bigInts[0xFF & iCode[frame.pc]];
+                                ++frame.pc;
+                                continue Loop;
+                            case Icode_REG_BIGINT2:
+                                bigIntReg = bigInts[getIndex(iCode, frame.pc)];
+                                frame.pc += 2;
+                                continue Loop;
+                            case Icode_REG_BIGINT4:
+                                bigIntReg = bigInts[getInt(iCode, frame.pc)];
+                                frame.pc += 4;
+                                continue Loop;
+                            default:
+                                dumpICode(frame.idata);
+                                throw new RuntimeException(
+                                        "Unknown icode : " + op + " @ pc : " + (frame.pc - 1));
+                        } // end of interpreter switch
                     } // end of jumplessRun label block
 
                     // This should be reachable only for jump implementation
@@ -1912,14 +2424,12 @@
                         // -1 accounts for pc pointing to jump opcode + 1
                         frame.pc += offset - 1;
                     } else {
-                        frame.pc = frame.idata.longJumps.
-                                       getExistingInt(frame.pc);
+                        frame.pc = frame.idata.longJumps.getExistingInt(frame.pc);
                     }
                     if (instructionCounting) {
                         frame.pcPrevBranch = frame.pc;
                     }
                     continue Loop;
-
                 } // end of Loop: for
 
                 exitFrame(cx, frame, null);
@@ -1930,14 +2440,13 @@
                     if (frame.frozen) {
                         frame = frame.cloneFrozen();
                     }
-                    setCallResult(
-                        frame, interpreterResult, interpreterResultDbl);
+                    setCallResult(frame, interpreterResult, interpreterResultDbl);
                     interpreterResult = null; // Help GC
                     continue StateLoop;
                 }
                 break StateLoop;
 
-            }  // end of interpreter withoutExceptions: try
+            } // end of interpreter withoutExceptions: try
             catch (Throwable ex) {
                 if (throwable != null) {
                     // This is serious bug and it is better to track it ASAP
@@ -1960,10 +2469,9 @@
             int exState;
             ContinuationJump cjump = null;
 
-            if (generatorState != null &&
-                generatorState.operation == NativeGenerator.GENERATOR_CLOSE &&
-                throwable == generatorState.value)
-            {
+            if (generatorState != null
+                    && generatorState.operation == NativeGenerator.GENERATOR_CLOSE
+                    && throwable == generatorState.value) {
                 exState = EX_FINALLY_STATE;
             } else if (throwable instanceof JavaScriptException) {
                 exState = EX_CATCH_STATE;
@@ -1975,21 +2483,24 @@
             } else if (throwable instanceof ContinuationPending) {
                 exState = EX_NO_JS_STATE;
             } else if (throwable instanceof RuntimeException) {
-                exState = cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)
-                          ? EX_CATCH_STATE
-                          : EX_FINALLY_STATE;
+                exState =
+                        cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)
+                                ? EX_CATCH_STATE
+                                : EX_FINALLY_STATE;
             } else if (throwable instanceof Error) {
-                exState = cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)
-                          ? EX_CATCH_STATE
-                          : EX_NO_JS_STATE;
+                exState =
+                        cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)
+                                ? EX_CATCH_STATE
+                                : EX_NO_JS_STATE;
             } else if (throwable instanceof ContinuationJump) {
                 // It must be ContinuationJump
                 exState = EX_FINALLY_STATE;
-                cjump = (ContinuationJump)throwable;
+                cjump = (ContinuationJump) throwable;
             } else {
-                exState = cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)
-                          ? EX_CATCH_STATE
-                          : EX_FINALLY_STATE;
+                exState =
+                        cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)
+                                ? EX_CATCH_STATE
+                                : EX_FINALLY_STATE;
             }
 
             if (instructionCounting) {
@@ -2006,11 +2517,9 @@
                     exState = EX_NO_JS_STATE;
                 }
             }
-            if (frame.debuggerFrame != null
-                && throwable instanceof RuntimeException)
-            {
+            if (frame.debuggerFrame != null && throwable instanceof RuntimeException) {
                 // Call debugger only for RuntimeException
-                RuntimeException rex = (RuntimeException)throwable;
+                RuntimeException rex = (RuntimeException) throwable;
                 try {
                     frame.debuggerFrame.onExceptionThrown(cx, rex);
                 } catch (Throwable ex) {
@@ -2022,7 +2531,7 @@
                 }
             }
 
-            for (;;) {
+            for (; ; ) {
                 if (exState != EX_NO_JS_STATE) {
                     boolean onlyFinally = (exState != EX_CATCH_STATE);
                     indexReg = getExceptionHandler(frame, onlyFinally);
@@ -2039,7 +2548,9 @@
                 exitFrame(cx, frame, throwable);
 
                 frame = frame.parentFrame;
-                if (frame == null) { break; }
+                if (frame == null) {
+                    break;
+                }
                 if (cjump != null && cjump.branchFrame == frame) {
                     // Continuation branch point was hit,
                     // restart the state loop to reenter continuation
@@ -2065,16 +2576,13 @@
                 throwable = null;
             }
             break StateLoop;
-
         } // end of StateLoop: for(;;)
 
         // Do cleanups/restorations before the final return or throw
 
         if (cx.previousInterpreterInvocations != null
-            && cx.previousInterpreterInvocations.size() != 0)
-        {
-            cx.lastInterpreterFrame
-                = cx.previousInterpreterInvocations.pop();
+                && cx.previousInterpreterInvocations.size() != 0) {
+            cx.lastInterpreterFrame = cx.previousInterpreterInvocations.pop();
         } else {
             // It was the last interpreter frame on the stack
             cx.lastInterpreterFrame = null;
@@ -2084,20 +2592,19 @@
 
         if (throwable != null) {
             if (throwable instanceof RuntimeException) {
-                throw (RuntimeException)throwable;
-            } else {
-                // Must be instance of Error or code bug
-                throw (Error)throwable;
+                throw (RuntimeException) throwable;
             }
+            // Must be instance of Error or code bug
+            throw (Error) throwable;
         }
 
         return (interpreterResult != DBL_MRK)
-               ? interpreterResult
-               : ScriptRuntime.wrapNumber(interpreterResultDbl);
+                ? interpreterResult
+                : ScriptRuntime.wrapNumber(interpreterResultDbl);
     }
 
-    private static int doInOrInstanceof(Context cx, int op, Object[] stack,
-                                        double[] sDbl, int stackTop) {
+    private static int doInOrInstanceof(
+            Context cx, int op, Object[] stack, double[] sDbl, int stackTop) {
         Object rhs = stack[stackTop];
         if (rhs == DOUBLE_MARK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
         --stackTop;
@@ -2113,8 +2620,8 @@
         return stackTop;
     }
 
-    private static int doCompare(CallFrame frame, int op, Object[] stack,
-                                 double[] sDbl, int stackTop) {
+    private static int doCompare(
+            CallFrame frame, int op, Object[] stack, double[] sDbl, int stackTop) {
         --stackTop;
         Object rhs = stack[stackTop + 1];
         Object lhs = stack[stackTop];
@@ -2123,94 +2630,84 @@
         {
             number_compare:
             {
-                double rDbl, lDbl;
+                Number rNum, lNum;
                 if (rhs == DOUBLE_MARK) {
-                    rDbl = sDbl[stackTop + 1];
-                    lDbl = stack_double(frame, stackTop);
+                    rNum = sDbl[stackTop + 1];
+                    lNum = stack_numeric(frame, stackTop);
                 } else if (lhs == DOUBLE_MARK) {
-                    rDbl = ScriptRuntime.toNumber(rhs);
-                    lDbl = sDbl[stackTop];
+                    rNum = ScriptRuntime.toNumeric(rhs);
+                    lNum = sDbl[stackTop];
                 } else {
                     break number_compare;
                 }
-                switch (op) {
-                    case Token.GE:
-                        valBln = (lDbl >= rDbl);
-                        break object_compare;
-                    case Token.LE:
-                        valBln = (lDbl <= rDbl);
-                        break object_compare;
-                    case Token.GT:
-                        valBln = (lDbl > rDbl);
-                        break object_compare;
-                    case Token.LT:
-                        valBln = (lDbl < rDbl);
-                        break object_compare;
-                    default:
-                        throw Kit.codeBug();
-                }
-            }
-            switch (op) {
-                case Token.GE:
-                    valBln = ScriptRuntime.cmp_LE(rhs, lhs);
-                    break;
-                case Token.LE:
-                    valBln = ScriptRuntime.cmp_LE(lhs, rhs);
-                    break;
-                case Token.GT:
-                    valBln = ScriptRuntime.cmp_LT(rhs, lhs);
-                    break;
-                case Token.LT:
-                    valBln = ScriptRuntime.cmp_LT(lhs, rhs);
-                    break;
-                default:
-                    throw Kit.codeBug();
+                valBln = ScriptRuntime.compare(lNum, rNum, op);
+                break object_compare;
             }
+            valBln = ScriptRuntime.compare(lhs, rhs, op);
         }
         stack[stackTop] = ScriptRuntime.wrapBoolean(valBln);
         return stackTop;
     }
 
-    private static int doBitOp(CallFrame frame, int op, Object[] stack,
-                               double[] sDbl, int stackTop) {
-        int lIntValue = stack_int32(frame, stackTop - 1);
-        int rIntValue = stack_int32(frame, stackTop);
-        stack[--stackTop] = DOUBLE_MARK;
+    private static int doBitOp(
+            CallFrame frame, int op, Object[] stack, double[] sDbl, int stackTop) {
+        Number lValue = stack_numeric(frame, stackTop - 1);
+        Number rValue = stack_numeric(frame, stackTop);
+        stackTop--;
+
+        Number result = null;
         switch (op) {
-          case Token.BITAND:
-            lIntValue &= rIntValue;
-            break;
-          case Token.BITOR:
-            lIntValue |= rIntValue;
-            break;
-          case Token.BITXOR:
-            lIntValue ^= rIntValue;
-            break;
-          case Token.LSH:
-            lIntValue <<= rIntValue;
-            break;
-          case Token.RSH:
-            lIntValue >>= rIntValue;
-            break;
+            case Token.BITAND:
+                result = ScriptRuntime.bitwiseAND(lValue, rValue);
+                break;
+            case Token.BITOR:
+                result = ScriptRuntime.bitwiseOR(lValue, rValue);
+                break;
+            case Token.BITXOR:
+                result = ScriptRuntime.bitwiseXOR(lValue, rValue);
+                break;
+            case Token.LSH:
+                result = ScriptRuntime.leftShift(lValue, rValue);
+                break;
+            case Token.RSH:
+                result = ScriptRuntime.signedRightShift(lValue, rValue);
+                break;
+        }
+
+        if (result instanceof BigInteger) {
+            stack[stackTop] = result;
+        } else {
+            stack[stackTop] = DOUBLE_MARK;
+            sDbl[stackTop] = result.doubleValue();
+        }
+        return stackTop;
+    }
+
+    private static int doBitNOT(CallFrame frame, Object[] stack, double[] sDbl, int stackTop) {
+        Number value = stack_numeric(frame, stackTop);
+        Number result = ScriptRuntime.bitwiseNOT(value);
+        if (result instanceof BigInteger) {
+            stack[stackTop] = result;
+        } else {
+            stack[stackTop] = DOUBLE_MARK;
+            sDbl[stackTop] = result.doubleValue();
         }
-        sDbl[stackTop] = lIntValue;
         return stackTop;
     }
 
-    private static int doDelName(Context cx, CallFrame frame, int op,
-                                 Object[] stack, double[] sDbl, int stackTop) {
+    private static int doDelName(
+            Context cx, CallFrame frame, int op, Object[] stack, double[] sDbl, int stackTop) {
         Object rhs = stack[stackTop];
         if (rhs == DOUBLE_MARK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
         --stackTop;
         Object lhs = stack[stackTop];
         if (lhs == DOUBLE_MARK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
-        stack[stackTop] = ScriptRuntime.delete(lhs, rhs, cx, frame.scope,
-                                               op == Icode_DELNAME);
+        stack[stackTop] = ScriptRuntime.delete(lhs, rhs, cx, frame.scope, op == Icode_DELNAME);
         return stackTop;
     }
 
-    private static int doGetElem(Context cx, CallFrame frame, Object[] stack,
-                                 double[] sDbl, int stackTop) {
+    private static int doGetElem(
+            Context cx, CallFrame frame, Object[] stack, double[] sDbl, int stackTop) {
         --stackTop;
         Object lhs = stack[stackTop];
         if (lhs == DOUBLE_MARK) {
@@ -2228,8 +2725,8 @@
         return stackTop;
     }
 
-    private static int doSetElem(Context cx, CallFrame frame, Object[] stack,
-                                 double[] sDbl, int stackTop) {
+    private static int doSetElem(
+            Context cx, CallFrame frame, Object[] stack, double[] sDbl, int stackTop) {
         stackTop -= 2;
         Object rhs = stack[stackTop + 2];
         if (rhs == DOUBLE_MARK) {
@@ -2251,25 +2748,33 @@
         return stackTop;
     }
 
-    private static int doElemIncDec(Context cx, CallFrame frame, byte[] iCode,
-                                    Object[] stack, double[] sDbl, int stackTop) {
+    private static int doElemIncDec(
+            Context cx,
+            CallFrame frame,
+            byte[] iCode,
+            Object[] stack,
+            double[] sDbl,
+            int stackTop) {
         Object rhs = stack[stackTop];
         if (rhs == DOUBLE_MARK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
         --stackTop;
         Object lhs = stack[stackTop];
         if (lhs == DOUBLE_MARK) lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
-        stack[stackTop] = ScriptRuntime.elemIncrDecr(lhs, rhs, cx, frame.scope,
-                                                     iCode[frame.pc]);
+        stack[stackTop] = ScriptRuntime.elemIncrDecr(lhs, rhs, cx, frame.scope, iCode[frame.pc]);
         ++frame.pc;
         return stackTop;
     }
 
-    private static int doCallSpecial(Context cx, CallFrame frame,
-                                     Object[] stack, double[] sDbl,
-                                     int stackTop, byte[] iCode,
-                                     int indexReg) {
+    private static int doCallSpecial(
+            Context cx,
+            CallFrame frame,
+            Object[] stack,
+            double[] sDbl,
+            int stackTop,
+            byte[] iCode,
+            int indexReg) {
         int callType = iCode[frame.pc] & 0xFF;
-        boolean isNew =  (iCode[frame.pc + 1] != 0);
+        boolean isNew = (iCode[frame.pc + 1] != 0);
         int sourceLine = getIndex(iCode, frame.pc + 2);
 
         // indexReg: number of arguments
@@ -2278,43 +2783,50 @@
             stackTop -= indexReg;
 
             Object function = stack[stackTop];
-            if (function == DOUBLE_MARK)
-                function = ScriptRuntime.wrapNumber(sDbl[stackTop]);
-            Object[] outArgs = getArgsArray(
-                                   stack, sDbl, stackTop + 1, indexReg);
-            stack[stackTop] = ScriptRuntime.newSpecial(
-                                  cx, function, outArgs, frame.scope, callType);
+            if (function == DOUBLE_MARK) function = ScriptRuntime.wrapNumber(sDbl[stackTop]);
+            Object[] outArgs = getArgsArray(stack, sDbl, stackTop + 1, indexReg);
+            stack[stackTop] =
+                    ScriptRuntime.newSpecial(cx, function, outArgs, frame.scope, callType);
         } else {
             // stack change: function thisObj arg0 .. argN -> result
             stackTop -= 1 + indexReg;
 
             // Call code generation ensure that stack here
             // is ... Callable Scriptable
-            Scriptable functionThis = (Scriptable)stack[stackTop + 1];
-            Callable function = (Callable)stack[stackTop];
-            Object[] outArgs = getArgsArray(
-                                   stack, sDbl, stackTop + 2, indexReg);
-            stack[stackTop] = ScriptRuntime.callSpecial(
-                                  cx, function, functionThis, outArgs,
-                                  frame.scope, frame.thisObj, callType,
-                                  frame.idata.itsSourceFile, sourceLine);
+            Scriptable functionThis = (Scriptable) stack[stackTop + 1];
+            Callable function = (Callable) stack[stackTop];
+            Object[] outArgs = getArgsArray(stack, sDbl, stackTop + 2, indexReg);
+            stack[stackTop] =
+                    ScriptRuntime.callSpecial(
+                            cx,
+                            function,
+                            functionThis,
+                            outArgs,
+                            frame.scope,
+                            frame.thisObj,
+                            callType,
+                            frame.idata.itsSourceFile,
+                            sourceLine);
         }
         frame.pc += 4;
         return stackTop;
     }
 
-    private static int doSetConstVar(CallFrame frame, Object[] stack,
-                                     double[] sDbl, int stackTop,
-                                     Object[] vars, double[] varDbls,
-                                     int[] varAttributes, int indexReg) {
+    private static int doSetConstVar(
+            CallFrame frame,
+            Object[] stack,
+            double[] sDbl,
+            int stackTop,
+            Object[] vars,
+            double[] varDbls,
+            int[] varAttributes,
+            int indexReg) {
         if (!frame.useActivation) {
             if ((varAttributes[indexReg] & ScriptableObject.READONLY) == 0) {
-                throw Context.reportRuntimeError1("msg.var.redecl",
-                                                  frame.idata.argNames[indexReg]);
+                throw Context.reportRuntimeErrorById(
+                        "msg.var.redecl", frame.idata.argNames[indexReg]);
             }
-            if ((varAttributes[indexReg] & ScriptableObject.UNINITIALIZED_CONST)
-                != 0)
-            {
+            if ((varAttributes[indexReg] & ScriptableObject.UNINITIALIZED_CONST) != 0) {
                 vars[indexReg] = stack[stackTop];
                 varAttributes[indexReg] &= ~ScriptableObject.UNINITIALIZED_CONST;
                 varDbls[indexReg] = sDbl[stackTop];
@@ -2324,18 +2836,22 @@
             if (val == DOUBLE_MARK) val = ScriptRuntime.wrapNumber(sDbl[stackTop]);
             String stringReg = frame.idata.argNames[indexReg];
             if (frame.scope instanceof ConstProperties) {
-                ConstProperties cp = (ConstProperties)frame.scope;
+                ConstProperties cp = (ConstProperties) frame.scope;
                 cp.putConst(stringReg, frame.scope, val);
-            } else
-                throw Kit.codeBug();
+            } else throw Kit.codeBug();
         }
         return stackTop;
     }
 
-    private static int doSetVar(CallFrame frame, Object[] stack,
-                                double[] sDbl, int stackTop,
-                                Object[] vars, double[] varDbls,
-                                int[] varAttributes, int indexReg) {
+    private static int doSetVar(
+            CallFrame frame,
+            Object[] stack,
+            double[] sDbl,
+            int stackTop,
+            Object[] vars,
+            double[] varDbls,
+            int[] varAttributes,
+            int indexReg) {
         if (!frame.useActivation) {
             if ((varAttributes[indexReg] & ScriptableObject.READONLY) == 0) {
                 vars[indexReg] = stack[stackTop];
@@ -2350,10 +2866,14 @@
         return stackTop;
     }
 
-    private static int doGetVar(CallFrame frame, Object[] stack,
-                                double[] sDbl, int stackTop,
-                                Object[] vars, double[] varDbls,
-                                int indexReg) {
+    private static int doGetVar(
+            CallFrame frame,
+            Object[] stack,
+            double[] sDbl,
+            int stackTop,
+            Object[] vars,
+            double[] varDbls,
+            int indexReg) {
         ++stackTop;
         if (!frame.useActivation) {
             stack[stackTop] = vars[indexReg];
@@ -2365,51 +2885,83 @@
         return stackTop;
     }
 
-    private static int doVarIncDec(Context cx, CallFrame frame,
-                                   Object[] stack, double[] sDbl,
-                                   int stackTop, Object[] vars,
-                                   double[] varDbls, int[] varAttributes,
-                                   int indexReg) {
+    private static int doVarIncDec(
+            Context cx,
+            CallFrame frame,
+            Object[] stack,
+            double[] sDbl,
+            int stackTop,
+            Object[] vars,
+            double[] varDbls,
+            int[] varAttributes,
+            int indexReg) {
         // indexReg : varindex
         ++stackTop;
         int incrDecrMask = frame.idata.itsICode[frame.pc];
         if (!frame.useActivation) {
             Object varValue = vars[indexReg];
-            double d;
+            double d = 0.0;
+            BigInteger bi = null;
             if (varValue == DOUBLE_MARK) {
                 d = varDbls[indexReg];
             } else {
-                d = ScriptRuntime.toNumber(varValue);
+                Number num = ScriptRuntime.toNumeric(varValue);
+                if (num instanceof BigInteger) {
+                    bi = (BigInteger) num;
+                } else {
+                    d = num.doubleValue();
+                }
             }
-            double d2 = ((incrDecrMask & Node.DECR_FLAG) == 0)
-                        ? d + 1.0 : d - 1.0;
-            boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
-            if ((varAttributes[indexReg] & ScriptableObject.READONLY) == 0) {
-                if (varValue != DOUBLE_MARK) {
-                    vars[indexReg] = DOUBLE_MARK;
+            if (bi == null) {
+                // double
+                double d2 = ((incrDecrMask & Node.DECR_FLAG) == 0) ? d + 1.0 : d - 1.0;
+                boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
+                if ((varAttributes[indexReg] & ScriptableObject.READONLY) == 0) {
+                    if (varValue != DOUBLE_MARK) {
+                        vars[indexReg] = DOUBLE_MARK;
+                    }
+                    varDbls[indexReg] = d2;
+                    stack[stackTop] = DOUBLE_MARK;
+                    sDbl[stackTop] = post ? d : d2;
+                } else {
+                    if (post && varValue != DOUBLE_MARK) {
+                        stack[stackTop] = varValue;
+                    } else {
+                        stack[stackTop] = DOUBLE_MARK;
+                        sDbl[stackTop] = post ? d : d2;
+                    }
                 }
-                varDbls[indexReg] = d2;
-                stack[stackTop] = DOUBLE_MARK;
-                sDbl[stackTop] = post ? d : d2;
             } else {
-                if (post && varValue != DOUBLE_MARK) {
-                    stack[stackTop] = varValue;
+                // BigInt
+                BigInteger result;
+                if ((incrDecrMask & Node.DECR_FLAG) == 0) {
+                    result = bi.add(BigInteger.ONE);
                 } else {
-                    stack[stackTop] = DOUBLE_MARK;
-                    sDbl[stackTop] = post ? d : d2;
+                    result = bi.subtract(BigInteger.ONE);
+                }
+
+                boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
+                if ((varAttributes[indexReg] & ScriptableObject.READONLY) == 0) {
+                    vars[indexReg] = result;
+                    stack[stackTop] = post ? bi : result;
+                } else {
+                    if (post && varValue != DOUBLE_MARK) {
+                        stack[stackTop] = varValue;
+                    } else {
+                        stack[stackTop] = post ? bi : result;
+                    }
                 }
             }
         } else {
             String varName = frame.idata.argNames[indexReg];
-            stack[stackTop] = ScriptRuntime.nameIncrDecr(frame.scope, varName,
-                                                         cx, incrDecrMask);
+            stack[stackTop] = ScriptRuntime.nameIncrDecr(frame.scope, varName, cx, incrDecrMask);
         }
         ++frame.pc;
         return stackTop;
     }
 
-    private static int doRefMember(Context cx, Object[] stack, double[] sDbl,
-                                   int stackTop, int flags) {
+    private static int doRefMember(
+            Context cx, Object[] stack, double[] sDbl, int stackTop, int flags) {
         Object elem = stack[stackTop];
         if (elem == DOUBLE_MARK) elem = ScriptRuntime.wrapNumber(sDbl[stackTop]);
         --stackTop;
@@ -2419,8 +2971,8 @@
         return stackTop;
     }
 
-    private static int doRefNsMember(Context cx, Object[] stack, double[] sDbl,
-                                     int stackTop, int flags) {
+    private static int doRefNsMember(
+            Context cx, Object[] stack, double[] sDbl, int stackTop, int flags) {
         Object elem = stack[stackTop];
         if (elem == DOUBLE_MARK) elem = ScriptRuntime.wrapNumber(sDbl[stackTop]);
         --stackTop;
@@ -2433,9 +2985,8 @@
         return stackTop;
     }
 
-    private static int doRefNsName(Context cx, CallFrame frame,
-                                   Object[] stack, double[] sDbl,
-                                   int stackTop, int flags) {
+    private static int doRefNsName(
+            Context cx, CallFrame frame, Object[] stack, double[] sDbl, int stackTop, int flags) {
         Object name = stack[stackTop];
         if (name == DOUBLE_MARK) name = ScriptRuntime.wrapNumber(sDbl[stackTop]);
         --stackTop;
@@ -2445,21 +2996,26 @@
         return stackTop;
     }
 
-    /**
-     * Call __noSuchMethod__.
-     */
-    private static CallFrame initFrameForNoSuchMethod(Context cx,
-            CallFrame frame, int indexReg, Object[] stack, double[] sDbl,
-            int stackTop, int op, Scriptable funThisObj, Scriptable calleeScope,
-            NoSuchMethodShim noSuchMethodShim, InterpretedFunction ifun)
-    {
+    /** Call __noSuchMethod__. */
+    private static CallFrame initFrameForNoSuchMethod(
+            Context cx,
+            CallFrame frame,
+            int indexReg,
+            Object[] stack,
+            double[] sDbl,
+            int stackTop,
+            int op,
+            Scriptable funThisObj,
+            Scriptable calleeScope,
+            NoSuchMethodShim noSuchMethodShim,
+            InterpretedFunction ifun) {
         // create an args array from the stack
         Object[] argsArray = null;
         // exactly like getArgsArray except that the first argument
         // is the method name from the shim
         int shift = stackTop + 2;
         Object[] elements = new Object[indexReg];
-        for (int i=0; i < indexReg; ++i, ++shift) {
+        for (int i = 0; i < indexReg; ++i, ++shift) {
             Object val = stack[shift];
             if (val == DOUBLE_MARK) {
                 val = ScriptRuntime.wrapNumber(sDbl[shift]);
@@ -2472,15 +3028,15 @@
 
         // exactly the same as if it's a regular InterpretedFunction
         CallFrame callParentFrame = frame;
-        CallFrame calleeFrame = new CallFrame();
         if (op == Icode_TAIL_CALL) {
             callParentFrame = frame.parentFrame;
             exitFrame(cx, frame, null);
         }
         // init the frame with the underlying method with the
         // adjusted args array and shim's function
-        initFrame(cx, calleeScope, funThisObj, argsArray, null,
-          0, 2, ifun, callParentFrame, calleeFrame);
+        CallFrame calleeFrame =
+                initFrame(
+                        cx, calleeScope, funThisObj, argsArray, null, 0, 2, ifun, callParentFrame);
         if (op != Icode_TAIL_CALL) {
             frame.savedStackTop = stackTop;
             frame.savedCallOp = op;
@@ -2488,28 +3044,22 @@
         return calleeFrame;
     }
 
-    private static boolean doEquals(Object[] stack, double[] sDbl,
-                                    int stackTop) {
+    private static boolean doEquals(Object[] stack, double[] sDbl, int stackTop) {
         Object rhs = stack[stackTop + 1];
         Object lhs = stack[stackTop];
         if (rhs == DOUBLE_MARK) {
             if (lhs == DOUBLE_MARK) {
                 return (sDbl[stackTop] == sDbl[stackTop + 1]);
-            } else {
-                return ScriptRuntime.eqNumber(sDbl[stackTop + 1], lhs);
-            }
-        } else {
-            if (lhs == DOUBLE_MARK) {
-                return ScriptRuntime.eqNumber(sDbl[stackTop], rhs);
-            } else {
-                return ScriptRuntime.eq(lhs, rhs);
             }
+            return ScriptRuntime.eqNumber(sDbl[stackTop + 1], lhs);
         }
+        if (lhs == DOUBLE_MARK) {
+            return ScriptRuntime.eqNumber(sDbl[stackTop], rhs);
+        }
+        return ScriptRuntime.eq(lhs, rhs);
     }
 
-    private static boolean doShallowEquals(Object[] stack, double[] sDbl,
-                                           int stackTop)
-    {
+    private static boolean doShallowEquals(Object[] stack, double[] sDbl, int stackTop) {
         Object rhs = stack[stackTop + 1];
         Object lhs = stack[stackTop];
         final Object DBL_MRK = DOUBLE_MARK;
@@ -2518,15 +3068,15 @@
             rdbl = sDbl[stackTop + 1];
             if (lhs == DBL_MRK) {
                 ldbl = sDbl[stackTop];
-            } else if (lhs instanceof Number) {
-                ldbl = ((Number)lhs).doubleValue();
+            } else if (lhs instanceof Number && !(lhs instanceof BigInteger)) {
+                ldbl = ((Number) lhs).doubleValue();
             } else {
                 return false;
             }
         } else if (lhs == DBL_MRK) {
             ldbl = sDbl[stackTop];
-            if (rhs instanceof Number) {
-                rdbl = ((Number)rhs).doubleValue();
+            if (rhs instanceof Number && !(rhs instanceof BigInteger)) {
+                rdbl = ((Number) rhs).doubleValue();
             } else {
                 return false;
             }
@@ -2536,10 +3086,12 @@
         return (ldbl == rdbl);
     }
 
-    private static CallFrame processThrowable(Context cx, Object throwable,
-                                              CallFrame frame, int indexReg,
-                                              boolean instructionCounting)
-    {
+    private static CallFrame processThrowable(
+            Context cx,
+            Object throwable,
+            CallFrame frame,
+            int indexReg,
+            boolean instructionCounting) {
         // Recovering from exception, indexReg contains
         // the index of handler
 
@@ -2560,19 +3112,15 @@
             }
 
             frame.savedStackTop = frame.emptyStackTop;
-            int scopeLocal = frame.localShift
-                             + table[indexReg
-                                     + EXCEPTION_SCOPE_SLOT];
-            int exLocal = frame.localShift
-                             + table[indexReg
-                                     + EXCEPTION_LOCAL_SLOT];
-            frame.scope = (Scriptable)frame.stack[scopeLocal];
+            int scopeLocal = frame.localShift + table[indexReg + EXCEPTION_SCOPE_SLOT];
+            int exLocal = frame.localShift + table[indexReg + EXCEPTION_LOCAL_SLOT];
+            frame.scope = (Scriptable) frame.stack[scopeLocal];
             frame.stack[exLocal] = throwable;
 
             throwable = null;
         } else {
             // Continuation restoration
-            ContinuationJump cjump = (ContinuationJump)throwable;
+            ContinuationJump cjump = (ContinuationJump) throwable;
 
             // Clear throwable to indicate that exceptions are OK
             throwable = null;
@@ -2597,13 +3145,12 @@
             CallFrame x = cjump.capturedFrame;
             for (int i = 0; i != rewindCount; ++i) {
                 if (!x.frozen) Kit.codeBug();
-                if (isFrameEnterExitRequired(x)) {
+                if (x.useActivation) {
                     if (enterFrames == null) {
                         // Allocate enough space to store the rest
                         // of rewind frames in case all of them
                         // would require to enter
-                        enterFrames = new CallFrame[rewindCount
-                                                    - i];
+                        enterFrames = new CallFrame[rewindCount - i];
                     }
                     enterFrames[enterCount] = x;
                     ++enterCount;
@@ -2632,255 +3179,148 @@
         return frame;
     }
 
-    private static Object freezeGenerator(Context cx, CallFrame frame,
-                                          int stackTop,
-                                          GeneratorState generatorState)
-    {
-          if (generatorState.operation == NativeGenerator.GENERATOR_CLOSE) {
-              // Error: no yields when generator is closing
-              throw ScriptRuntime.typeError0("msg.yield.closing");
-          }
-          // return to our caller (which should be a method of NativeGenerator)
-          frame.frozen = true;
-          frame.result = frame.stack[stackTop];
-          frame.resultDbl = frame.sDbl[stackTop];
-          frame.savedStackTop = stackTop;
-          frame.pc--; // we want to come back here when we resume
-          ScriptRuntime.exitActivationFunction(cx);
-          return (frame.result != DOUBLE_MARK)
-              ? frame.result
-              : ScriptRuntime.wrapNumber(frame.resultDbl);
-    }
-
-    private static Object thawGenerator(CallFrame frame, int stackTop,
-                                        GeneratorState generatorState, int op)
-    {
-          // we are resuming execution
-          frame.frozen = false;
-          int sourceLine = getIndex(frame.idata.itsICode, frame.pc);
-          frame.pc += 2; // skip line number data
-          if (generatorState.operation == NativeGenerator.GENERATOR_THROW) {
-              // processing a call to <generator>.throw(exception): must
-              // act as if exception was thrown from resumption point
-              return new JavaScriptException(generatorState.value,
-                                                  frame.idata.itsSourceFile,
-                                                  sourceLine);
-          }
-          if (generatorState.operation == NativeGenerator.GENERATOR_CLOSE) {
-              return generatorState.value;
-          }
-          if (generatorState.operation != NativeGenerator.GENERATOR_SEND)
-              throw Kit.codeBug();
-          if (op == Token.YIELD)
-              frame.stack[stackTop] = generatorState.value;
-          return Scriptable.NOT_FOUND;
-    }
-
-    private static CallFrame initFrameForApplyOrCall(Context cx, CallFrame frame,
-            int indexReg, Object[] stack, double[] sDbl, int stackTop, int op,
-            Scriptable calleeScope, IdFunctionObject ifun,
-            InterpretedFunction iApplyCallable)
-    {
+    private static Object freezeGenerator(
+            Context cx,
+            CallFrame frame,
+            int stackTop,
+            GeneratorState generatorState,
+            boolean yieldStar) {
+        if (generatorState.operation == NativeGenerator.GENERATOR_CLOSE) {
+            // Error: no yields when generator is closing
+            throw ScriptRuntime.typeErrorById("msg.yield.closing");
+        }
+        // return to our caller (which should be a method of NativeGenerator)
+        frame.frozen = true;
+        frame.result = frame.stack[stackTop];
+        frame.resultDbl = frame.sDbl[stackTop];
+        frame.savedStackTop = stackTop;
+        frame.pc--; // we want to come back here when we resume
+        ScriptRuntime.exitActivationFunction(cx);
+        final Object result =
+                (frame.result != DOUBLE_MARK)
+                        ? frame.result
+                        : ScriptRuntime.wrapNumber(frame.resultDbl);
+        if (yieldStar) {
+            return new ES6Generator.YieldStarResult(result);
+        }
+        return result;
+    }
+
+    private static Object thawGenerator(
+            CallFrame frame, int stackTop, GeneratorState generatorState, int op) {
+        // we are resuming execution
+        frame.frozen = false;
+        int sourceLine = getIndex(frame.idata.itsICode, frame.pc);
+        frame.pc += 2; // skip line number data
+        if (generatorState.operation == NativeGenerator.GENERATOR_THROW) {
+            // processing a call to <generator>.throw(exception): must
+            // act as if exception was thrown from resumption point.
+            return new JavaScriptException(
+                    generatorState.value, frame.idata.itsSourceFile, sourceLine);
+        }
+        if (generatorState.operation == NativeGenerator.GENERATOR_CLOSE) {
+            return generatorState.value;
+        }
+        if (generatorState.operation != NativeGenerator.GENERATOR_SEND) throw Kit.codeBug();
+        if ((op == Token.YIELD) || (op == Icode_YIELD_STAR)) {
+            frame.stack[stackTop] = generatorState.value;
+        }
+        return Scriptable.NOT_FOUND;
+    }
+
+    private static CallFrame initFrameForApplyOrCall(
+            Context cx,
+            CallFrame frame,
+            int indexReg,
+            Object[] stack,
+            double[] sDbl,
+            int stackTop,
+            int op,
+            Scriptable calleeScope,
+            IdFunctionObject ifun,
+            InterpretedFunction iApplyCallable) {
         Scriptable applyThis;
         if (indexReg != 0) {
             Object obj = stack[stackTop + 2];
-            if (obj == DOUBLE_MARK)
-                obj = ScriptRuntime.wrapNumber(sDbl[stackTop + 2]);
+            if (obj == DOUBLE_MARK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop + 2]);
             applyThis = ScriptRuntime.toObjectOrNull(cx, obj, frame.scope);
-        }
-        else {
+        } else {
             applyThis = null;
         }
         if (applyThis == null) {
             // This covers the case of args[0] == (null|undefined) as well.
             applyThis = ScriptRuntime.getTopCallScope(cx);
         }
-        if(op == Icode_TAIL_CALL) {
+        if (op == Icode_TAIL_CALL) {
             exitFrame(cx, frame, null);
             frame = frame.parentFrame;
-        }
-        else {
+        } else {
             frame.savedStackTop = stackTop;
             frame.savedCallOp = op;
         }
-        CallFrame calleeFrame = new CallFrame();
-        if(BaseFunction.isApply(ifun)) {
-            Object[] callArgs = indexReg < 2 ? ScriptRuntime.emptyArgs :
-                ScriptRuntime.getApplyArguments(cx, stack[stackTop + 3]);
-            initFrame(cx, calleeScope, applyThis, callArgs, null, 0,
-                    callArgs.length, iApplyCallable, frame, calleeFrame);
-        }
-        else {
+        final CallFrame calleeFrame;
+        if (BaseFunction.isApply(ifun)) {
+            Object[] callArgs =
+                    indexReg < 2
+                            ? ScriptRuntime.emptyArgs
+                            : ScriptRuntime.getApplyArguments(cx, stack[stackTop + 3]);
+            calleeFrame =
+                    initFrame(
+                            cx,
+                            calleeScope,
+                            applyThis,
+                            callArgs,
+                            null,
+                            0,
+                            callArgs.length,
+                            iApplyCallable,
+                            frame);
+        } else {
             // Shift args left
-            for(int i = 1; i < indexReg; ++i) {
+            for (int i = 1; i < indexReg; ++i) {
                 stack[stackTop + 1 + i] = stack[stackTop + 2 + i];
                 sDbl[stackTop + 1 + i] = sDbl[stackTop + 2 + i];
             }
             int argCount = indexReg < 2 ? 0 : indexReg - 1;
-            initFrame(cx, calleeScope, applyThis, stack, sDbl, stackTop + 2,
-                    argCount, iApplyCallable, frame, calleeFrame);
+            calleeFrame =
+                    initFrame(
+                            cx,
+                            calleeScope,
+                            applyThis,
+                            stack,
+                            sDbl,
+                            stackTop + 2,
+                            argCount,
+                            iApplyCallable,
+                            frame);
         }
 
-        frame = calleeFrame;
-        return frame;
+        return calleeFrame;
     }
 
-    private static void initFrame(Context cx, Scriptable callerScope,
-                                  Scriptable thisObj,
-                                  Object[] args, double[] argsDbl,
-                                  int argShift, int argCount,
-                                  InterpretedFunction fnOrScript,
-                                  CallFrame parentFrame, CallFrame frame)
-    {
-        InterpreterData idata = fnOrScript.idata;
-
-        boolean useActivation = idata.itsNeedsActivation;
-        DebugFrame debuggerFrame = null;
-        if (cx.debugger != null) {
-            debuggerFrame = cx.debugger.getFrame(cx, idata);
-            if (debuggerFrame != null) {
-                useActivation = true;
-            }
-        }
-
-        if (useActivation) {
-            // Copy args to new array to pass to enterActivationFunction
-            // or debuggerFrame.onEnter
-            if (argsDbl != null) {
-                args = getArgsArray(args, argsDbl, argShift, argCount);
-            }
-            argShift = 0;
-            argsDbl = null;
-        }
-
-        Scriptable scope;
-        if (idata.itsFunctionType != 0) {
-            scope = fnOrScript.getParentScope();
-
-            if (useActivation) {
-                if (idata.itsFunctionType == FunctionNode.ARROW_FUNCTION) {
-                    scope = ScriptRuntime.createArrowFunctionActivation(fnOrScript, scope, args, idata.isStrict);
-                } else {
-                    scope = ScriptRuntime.createFunctionActivation(fnOrScript, scope, args, idata.isStrict);
-                }
-            }
-        } else {
-            scope = callerScope;
-            ScriptRuntime.initScript(fnOrScript, thisObj, cx, scope,
-                                     fnOrScript.idata.evalScriptFlag);
-        }
-
-        if (idata.itsNestedFunctions != null) {
-            if (idata.itsFunctionType != 0 && !idata.itsNeedsActivation)
-                Kit.codeBug();
-            for (int i = 0; i < idata.itsNestedFunctions.length; i++) {
-                InterpreterData fdata = idata.itsNestedFunctions[i];
-                if (fdata.itsFunctionType == FunctionNode.FUNCTION_STATEMENT) {
-                    initFunction(cx, scope, fnOrScript, i);
-                }
-            }
-        }
-
-        // Initialize args, vars, locals and stack
-
-        int emptyStackTop = idata.itsMaxVars + idata.itsMaxLocals - 1;
-        int maxFrameArray = idata.itsMaxFrameArray;
-        if (maxFrameArray != emptyStackTop + idata.itsMaxStack + 1)
-            Kit.codeBug();
-
-        Object[] stack;
-        int[] stackAttributes;
-        double[] sDbl;
-        boolean stackReuse;
-        if (frame.stack != null && maxFrameArray <= frame.stack.length) {
-            // Reuse stacks from old frame
-            stackReuse = true;
-            stack = frame.stack;
-            stackAttributes = frame.stackAttributes;
-            sDbl = frame.sDbl;
-        } else {
-            stackReuse = false;
-            stack = new Object[maxFrameArray];
-            stackAttributes = new int[maxFrameArray];
-            sDbl = new double[maxFrameArray];
-        }
-
-        int varCount = idata.getParamAndVarCount();
-        for (int i = 0; i < varCount; i++) {
-            if (idata.getParamOrVarConst(i))
-                stackAttributes[i] = ScriptableObject.CONST;
-        }
-        int definedArgs = idata.argCount;
-        if (definedArgs > argCount) { definedArgs = argCount; }
-
-        // Fill the frame structure
-
-        frame.parentFrame = parentFrame;
-        frame.frameIndex = (parentFrame == null)
-                           ? 0 : parentFrame.frameIndex + 1;
-        if(frame.frameIndex > cx.getMaximumInterpreterStackDepth())
-        {
-            throw Context.reportRuntimeError("Exceeded maximum stack depth");
-        }
-        frame.frozen = false;
-
-        frame.fnOrScript = fnOrScript;
-        frame.idata = idata;
-
-        frame.stack = stack;
-        frame.stackAttributes = stackAttributes;
-        frame.sDbl = sDbl;
-        frame.varSource = frame;
-        frame.localShift = idata.itsMaxVars;
-        frame.emptyStackTop = emptyStackTop;
-
-        frame.debuggerFrame = debuggerFrame;
-        frame.useActivation = useActivation;
-
-        frame.thisObj = thisObj;
-
-        // Initialize initial values of variables that change during
-        // interpretation.
-        frame.result = Undefined.instance;
-        frame.pc = 0;
-        frame.pcPrevBranch = 0;
-        frame.pcSourceLineStart = idata.firstLinePC;
-        frame.scope = scope;
-
-        frame.savedStackTop = emptyStackTop;
-        frame.savedCallOp = 0;
-
-        System.arraycopy(args, argShift, stack, 0, definedArgs);
-        if (argsDbl != null) {
-            System.arraycopy(argsDbl, argShift, sDbl, 0, definedArgs);
-        }
-        for (int i = definedArgs; i != idata.itsMaxVars; ++i) {
-            stack[i] = Undefined.instance;
-        }
-        if (stackReuse) {
-            // Clean the stack part and space beyond stack if any
-            // of the old array to allow to GC objects there
-            for (int i = emptyStackTop + 1; i != stack.length; ++i) {
-                stack[i] = null;
-            }
-        }
-
+    private static CallFrame initFrame(
+            Context cx,
+            Scriptable callerScope,
+            Scriptable thisObj,
+            Object[] args,
+            double[] argsDbl,
+            int argShift,
+            int argCount,
+            InterpretedFunction fnOrScript,
+            CallFrame parentFrame) {
+        CallFrame frame = new CallFrame(cx, thisObj, fnOrScript, parentFrame);
+        frame.initializeArgs(cx, callerScope, args, argsDbl, argShift, argCount);
         enterFrame(cx, frame, args, false);
+        return frame;
     }
 
-    private static boolean isFrameEnterExitRequired(CallFrame frame)
-    {
-        return frame.debuggerFrame != null || frame.idata.itsNeedsActivation;
-    }
-
-    private static void enterFrame(Context cx, CallFrame frame, Object[] args,
-                                   boolean continuationRestart)
-    {
+    private static void enterFrame(
+            Context cx, CallFrame frame, Object[] args, boolean continuationRestart) {
         boolean usesActivation = frame.idata.itsNeedsActivation;
         boolean isDebugged = frame.debuggerFrame != null;
-        if(usesActivation || isDebugged) {
+        if (usesActivation || isDebugged) {
             Scriptable scope = frame.scope;
-            if(scope == null) {
+            if (scope == null) {
                 Kit.codeBug();
             } else if (continuationRestart) {
                 // Walk the parent chain of frame.scope until a NativeCall is
@@ -2891,12 +3331,12 @@
                 // the continuation was captured within a "with" or "catch"
                 // block ("catch" implicitly uses NativeWith to create a scope
                 // to expose the exception variable).
-                for(;;) {
-                    if(scope instanceof NativeWith) {
+                for (; ; ) {
+                    if (scope instanceof NativeWith) {
                         scope = scope.getParentScope();
-                        if (scope == null || (frame.parentFrame != null &&
-                                              frame.parentFrame.scope == scope))
-                        {
+                        if (scope == null
+                                || (frame.parentFrame != null
+                                        && frame.parentFrame.scope == scope)) {
                             // If we get here, we didn't find a NativeCall in
                             // the call chain before reaching parent frame's
                             // scope. This should not be possible.
@@ -2904,8 +3344,7 @@
                             break; // Never reached, but keeps the static analyzer
                             // happy about "scope" not being null 5 lines above.
                         }
-                    }
-                    else {
+                    } else {
                         break;
                     }
                 }
@@ -2922,9 +3361,7 @@
         }
     }
 
-    private static void exitFrame(Context cx, CallFrame frame,
-                                  Object throwable)
-    {
+    private static void exitFrame(Context cx, CallFrame frame, Object throwable) {
         if (frame.idata.itsNeedsActivation) {
             ScriptRuntime.exitActivationFunction(cx);
         }
@@ -2935,7 +3372,7 @@
                     frame.debuggerFrame.onExit(cx, true, throwable);
                 } else {
                     Object result;
-                    ContinuationJump cjump = (ContinuationJump)throwable;
+                    ContinuationJump cjump = (ContinuationJump) throwable;
                     if (cjump == null) {
                         result = frame.result;
                     } else {
@@ -2953,17 +3390,13 @@
                     frame.debuggerFrame.onExit(cx, false, result);
                 }
             } catch (Throwable ex) {
-                System.err.println(
-"RHINO USAGE WARNING: onExit terminated with exception");
+                System.err.println("RHINO USAGE WARNING: onExit terminated with exception");
                 ex.printStackTrace(System.err);
             }
         }
     }
 
-    private static void setCallResult(CallFrame frame,
-                                      Object callResult,
-                                      double callResultDbl)
-    {
+    private static void setCallResult(CallFrame frame, Object callResult, double callResultDbl) {
         if (frame.savedCallOp == Token.CALL) {
             frame.stack[frame.savedStackTop] = callResult;
             frame.sDbl[frame.savedStackTop] = callResultDbl;
@@ -2981,20 +3414,16 @@
     }
 
     public static NativeContinuation captureContinuation(Context cx) {
-        if (cx.lastInterpreterFrame == null ||
-            !(cx.lastInterpreterFrame instanceof CallFrame))
-        {
+        if (cx.lastInterpreterFrame == null || !(cx.lastInterpreterFrame instanceof CallFrame)) {
             throw new IllegalStateException("Interpreter frames not found");
         }
-        return captureContinuation(cx, (CallFrame)cx.lastInterpreterFrame, true);
+        return captureContinuation(cx, (CallFrame) cx.lastInterpreterFrame, true);
     }
 
-    private static NativeContinuation captureContinuation(Context cx, CallFrame frame,
-        boolean requireContinuationsTopFrame)
-    {
+    private static NativeContinuation captureContinuation(
+            Context cx, CallFrame frame, boolean requireContinuationsTopFrame) {
         NativeContinuation c = new NativeContinuation();
-        ScriptRuntime.setObjectProtoAndParent(
-            c, ScriptRuntime.getTopCallScope(cx));
+        ScriptRuntime.setObjectProtoAndParent(c, ScriptRuntime.getTopCallScope(cx));
 
         // Make sure that all frames are frozen
         CallFrame x = frame;
@@ -3021,14 +3450,14 @@
         }
 
         if (requireContinuationsTopFrame) {
-            while (outermost.parentFrame != null)
-                outermost = outermost.parentFrame;
+            while (outermost.parentFrame != null) outermost = outermost.parentFrame;
 
             if (!outermost.isContinuationsTopFrame) {
-                throw new IllegalStateException("Cannot capture continuation " +
-                        "from JavaScript code not called directly by " +
-                        "executeScriptWithContinuations or " +
-                        "callFunctionWithContinuations");
+                throw new IllegalStateException(
+                        "Cannot capture continuation "
+                                + "from JavaScript code not called directly by "
+                                + "executeScriptWithContinuations or "
+                                + "callFunctionWithContinuations");
             }
         }
 
@@ -3036,51 +3465,52 @@
         return c;
     }
 
-    private static int stack_int32(CallFrame frame, int i)
-    {
+    private static int stack_int32(CallFrame frame, int i) {
         Object x = frame.stack[i];
         if (x == UniqueTag.DOUBLE_MARK) {
             return ScriptRuntime.toInt32(frame.sDbl[i]);
-        } else {
-            return ScriptRuntime.toInt32(x);
         }
+        return ScriptRuntime.toInt32(x);
     }
 
-    private static double stack_double(CallFrame frame, int i)
-    {
+    private static double stack_double(CallFrame frame, int i) {
         Object x = frame.stack[i];
         if (x != UniqueTag.DOUBLE_MARK) {
             return ScriptRuntime.toNumber(x);
-        } else {
-            return frame.sDbl[i];
         }
+        return frame.sDbl[i];
     }
 
-    private static boolean stack_boolean(CallFrame frame, int i)
-    {
+    private static Number stack_numeric(CallFrame frame, int i) {
         Object x = frame.stack[i];
-        if (x == Boolean.TRUE) {
+        if (x != UniqueTag.DOUBLE_MARK) {
+            return ScriptRuntime.toNumeric(x);
+        }
+        return frame.sDbl[i];
+    }
+
+    private static boolean stack_boolean(CallFrame frame, int i) {
+        Object x = frame.stack[i];
+        if (Boolean.TRUE.equals(x)) {
             return true;
-        } else if (x == Boolean.FALSE) {
+        } else if (Boolean.FALSE.equals(x)) {
             return false;
         } else if (x == UniqueTag.DOUBLE_MARK) {
             double d = frame.sDbl[i];
-            return d == d && d != 0.0;
+            return !Double.isNaN(d) && d != 0.0;
         } else if (x == null || x == Undefined.instance) {
             return false;
+        } else if (x instanceof BigInteger) {
+            return !((BigInteger) x).equals(BigInteger.ZERO);
         } else if (x instanceof Number) {
-            double d = ((Number)x).doubleValue();
-            return (d == d && d != 0.0);
-        } else if (x instanceof Boolean) {
-            return ((Boolean)x).booleanValue();
+            double d = ((Number) x).doubleValue();
+            return (!Double.isNaN(d) && d != 0.0);
         } else {
             return ScriptRuntime.toBoolean(x);
         }
     }
 
-    private static void doAdd(Object[] stack, double[] sDbl, int stackTop,
-                              Context cx)
-    {
+    private static void doAdd(Object[] stack, double[] sDbl, int stackTop, Context cx) {
         Object rhs = stack[stackTop + 1];
         Object lhs = stack[stackTop];
         double d;
@@ -3101,17 +3531,32 @@
         } else {
             if (lhs instanceof Scriptable || rhs instanceof Scriptable) {
                 stack[stackTop] = ScriptRuntime.add(lhs, rhs, cx);
-            } else if (lhs instanceof CharSequence || rhs instanceof CharSequence) {
-                CharSequence lstr = ScriptRuntime.toCharSequence(lhs);
-                CharSequence rstr = ScriptRuntime.toCharSequence(rhs);
-                stack[stackTop] = new ConsString(lstr, rstr);
+
+                // the next two else if branches are a bit more tricky
+                // to reduce method calls
+            } else if (lhs instanceof CharSequence) {
+                if (rhs instanceof CharSequence) {
+                    stack[stackTop] = new ConsString((CharSequence) lhs, (CharSequence) rhs);
+                } else {
+                    stack[stackTop] =
+                            new ConsString((CharSequence) lhs, ScriptRuntime.toCharSequence(rhs));
+                }
+            } else if (rhs instanceof CharSequence) {
+                stack[stackTop] =
+                        new ConsString(ScriptRuntime.toCharSequence(lhs), (CharSequence) rhs);
+
             } else {
-                double lDbl = (lhs instanceof Number)
-                    ? ((Number)lhs).doubleValue() : ScriptRuntime.toNumber(lhs);
-                double rDbl = (rhs instanceof Number)
-                    ? ((Number)rhs).doubleValue() : ScriptRuntime.toNumber(rhs);
-                stack[stackTop] = DOUBLE_MARK;
-                sDbl[stackTop] = lDbl + rDbl;
+                Number lNum = (lhs instanceof Number) ? (Number) lhs : ScriptRuntime.toNumeric(lhs);
+                Number rNum = (rhs instanceof Number) ? (Number) rhs : ScriptRuntime.toNumeric(rhs);
+
+                if (lNum instanceof BigInteger && rNum instanceof BigInteger) {
+                    stack[stackTop] = ((BigInteger) lNum).add((BigInteger) rNum);
+                } else if (lNum instanceof BigInteger || rNum instanceof BigInteger) {
+                    throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
+                } else {
+                    stack[stackTop] = DOUBLE_MARK;
+                    sDbl[stackTop] = lNum.doubleValue() + rNum.doubleValue();
+                }
             }
             return;
         }
@@ -3126,48 +3571,58 @@
             }
             stack[stackTop] = ScriptRuntime.add(lhs, rhs, cx);
         } else if (lhs instanceof CharSequence) {
-            CharSequence lstr = (CharSequence)lhs;
-            CharSequence rstr = ScriptRuntime.toCharSequence(d);
+            CharSequence rstr = ScriptRuntime.numberToString(d, 10);
             if (leftRightOrder) {
-                stack[stackTop] = new ConsString(lstr, rstr);
+                stack[stackTop] = new ConsString((CharSequence) lhs, rstr);
             } else {
-                stack[stackTop] = new ConsString(rstr, lstr);
+                stack[stackTop] = new ConsString(rstr, (CharSequence) lhs);
             }
         } else {
-            double lDbl = (lhs instanceof Number)
-                ? ((Number)lhs).doubleValue() : ScriptRuntime.toNumber(lhs);
-            stack[stackTop] = DOUBLE_MARK;
-            sDbl[stackTop] = lDbl + d;
+            Number lNum = (lhs instanceof Number) ? (Number) lhs : ScriptRuntime.toNumeric(lhs);
+            if (lNum instanceof BigInteger) {
+                throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
+            } else {
+                stack[stackTop] = DOUBLE_MARK;
+                sDbl[stackTop] = lNum.doubleValue() + d;
+            }
         }
     }
 
-    private static int doArithmetic(CallFrame frame, int op, Object[] stack,
-                                    double[] sDbl, int stackTop) {
-        double rDbl = stack_double(frame, stackTop);
+    private static int doArithmetic(
+            CallFrame frame, int op, Object[] stack, double[] sDbl, int stackTop) {
+        Number lNum = stack_numeric(frame, stackTop - 1);
+        Number rNum = stack_numeric(frame, stackTop);
         --stackTop;
-        double lDbl = stack_double(frame, stackTop);
-        stack[stackTop] = DOUBLE_MARK;
+
+        Number result = null;
         switch (op) {
-          case Token.SUB:
-            lDbl -= rDbl;
-            break;
-          case Token.MUL:
-            lDbl *= rDbl;
-            break;
-          case Token.DIV:
-            lDbl /= rDbl;
-            break;
-          case Token.MOD:
-            lDbl %= rDbl;
-            break;
+            case Token.SUB:
+                result = ScriptRuntime.subtract(lNum, rNum);
+                break;
+            case Token.MUL:
+                result = ScriptRuntime.multiply(lNum, rNum);
+                break;
+            case Token.DIV:
+                result = ScriptRuntime.divide(lNum, rNum);
+                break;
+            case Token.MOD:
+                result = ScriptRuntime.remainder(lNum, rNum);
+                break;
+            case Token.EXP:
+                result = ScriptRuntime.exponentiate(lNum, rNum);
+                break;
+        }
+
+        if (result instanceof BigInteger) {
+            stack[stackTop] = result;
+        } else {
+            stack[stackTop] = DOUBLE_MARK;
+            sDbl[stackTop] = result.doubleValue();
         }
-        sDbl[stackTop] = lDbl;
         return stackTop;
     }
 
-    private static Object[] getArgsArray(Object[] stack, double[] sDbl,
-                                         int shift, int count)
-    {
+    private static Object[] getArgsArray(Object[] stack, double[] sDbl, int shift, int count) {
         if (count == 0) {
             return ScriptRuntime.emptyArgs;
         }
@@ -3182,9 +3637,7 @@
         return args;
     }
 
-    private static void addInstructionCount(Context cx, CallFrame frame,
-                                            int extra)
-    {
+    private static void addInstructionCount(Context cx, CallFrame frame, int extra) {
         cx.instructionCount += frame.pc - frame.pcPrevBranch + extra;
         if (cx.instructionCount > cx.instructionThreshold) {
             cx.observeInstructionCount(cx.instructionCount);
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/IRFactory.java rhino-1.7.14/src/org/mozilla/javascript/IRFactory.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/IRFactory.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/IRFactory.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,12 +6,15 @@
 
 package org.mozilla.javascript;
 
+import java.util.ArrayList;
+import java.util.List;
 import org.mozilla.javascript.ast.ArrayComprehension;
 import org.mozilla.javascript.ast.ArrayComprehensionLoop;
 import org.mozilla.javascript.ast.ArrayLiteral;
 import org.mozilla.javascript.ast.Assignment;
 import org.mozilla.javascript.ast.AstNode;
 import org.mozilla.javascript.ast.AstRoot;
+import org.mozilla.javascript.ast.BigIntLiteral;
 import org.mozilla.javascript.ast.Block;
 import org.mozilla.javascript.ast.BreakStatement;
 import org.mozilla.javascript.ast.CatchClause;
@@ -31,6 +34,7 @@
 import org.mozilla.javascript.ast.IfStatement;
 import org.mozilla.javascript.ast.InfixExpression;
 import org.mozilla.javascript.ast.Jump;
+import org.mozilla.javascript.ast.KeywordLiteral;
 import org.mozilla.javascript.ast.Label;
 import org.mozilla.javascript.ast.LabeledStatement;
 import org.mozilla.javascript.ast.LetNode;
@@ -50,9 +54,13 @@
 import org.mozilla.javascript.ast.SwitchCase;
 import org.mozilla.javascript.ast.SwitchStatement;
 import org.mozilla.javascript.ast.Symbol;
+import org.mozilla.javascript.ast.TaggedTemplateLiteral;
+import org.mozilla.javascript.ast.TemplateCharacters;
+import org.mozilla.javascript.ast.TemplateLiteral;
 import org.mozilla.javascript.ast.ThrowStatement;
 import org.mozilla.javascript.ast.TryStatement;
 import org.mozilla.javascript.ast.UnaryExpression;
+import org.mozilla.javascript.ast.UpdateExpression;
 import org.mozilla.javascript.ast.VariableDeclaration;
 import org.mozilla.javascript.ast.VariableInitializer;
 import org.mozilla.javascript.ast.WhileLoop;
@@ -68,9 +76,6 @@
 import org.mozilla.javascript.ast.XmlString;
 import org.mozilla.javascript.ast.Yield;
 
-import java.util.List;
-import java.util.ArrayList;
-
 /**
  * This class rewrites the parse tree into an IR suitable for codegen.
  *
@@ -78,11 +83,10 @@
  * @author Mike McCabe
  * @author Norris Boyd
  */
-public final class IRFactory extends Parser
-{
+public final class IRFactory extends Parser {
     private static final int LOOP_DO_WHILE = 0;
-    private static final int LOOP_WHILE    = 1;
-    private static final int LOOP_FOR      = 2;
+    private static final int LOOP_WHILE = 1;
+    private static final int LOOP_FOR = 2;
 
     private static final int ALWAYS_TRUE_BOOLEAN = 1;
     private static final int ALWAYS_FALSE_BOOLEAN = -1;
@@ -102,8 +106,8 @@
     }
 
     /**
-     * Transforms the tree into a lower-level IR suitable for codegen.
-     * Optionally generates the encoded source.
+     * Transforms the tree into a lower-level IR suitable for codegen. Optionally generates the
+     * encoded source.
      */
     public ScriptNode transformTree(AstRoot root) {
         currentScriptOrFn = root;
@@ -114,11 +118,10 @@
             System.out.println("IRFactory.transformTree");
             System.out.println(root.debugPrint());
         }
-        ScriptNode script = (ScriptNode)transform(root);
+        ScriptNode script = (ScriptNode) transform(root);
 
         int sourceEndOffset = decompiler.getCurrentOffset();
-        script.setEncodedSourceBounds(sourceStartOffset,
-                                      sourceEndOffset);
+        script.setEncodedSourceBounds(sourceStartOffset, sourceEndOffset);
 
         if (compilerEnv.isGeneratingSource()) {
             script.setEncodedSource(decompiler.getEncodedSource());
@@ -134,111 +137,121 @@
     // Another possibility:  create AstTransformer interface and adapter.
     public Node transform(AstNode node) {
         switch (node.getType()) {
-          case Token.ARRAYCOMP:
-              return transformArrayComp((ArrayComprehension)node);
-          case Token.ARRAYLIT:
-              return transformArrayLiteral((ArrayLiteral)node);
-          case Token.BLOCK:
-              return transformBlock(node);
-          case Token.BREAK:
-              return transformBreak((BreakStatement)node);
-          case Token.CALL:
-              return transformFunctionCall((FunctionCall)node);
-          case Token.CONTINUE:
-              return transformContinue((ContinueStatement)node);
-          case Token.DO:
-              return transformDoLoop((DoLoop)node);
-          case Token.EMPTY:
-              return node;
-          case Token.FOR:
-              if (node instanceof ForInLoop) {
-                  return transformForInLoop((ForInLoop)node);
-              } else {
-                  return transformForLoop((ForLoop)node);
-              }
-          case Token.FUNCTION:
-              return transformFunction((FunctionNode)node);
-          case Token.GENEXPR:
-              return transformGenExpr((GeneratorExpression)node);
-          case Token.GETELEM:
-              return transformElementGet((ElementGet)node);
-          case Token.GETPROP:
-              return transformPropertyGet((PropertyGet)node);
-          case Token.HOOK:
-              return transformCondExpr((ConditionalExpression)node);
-          case Token.IF:
-              return transformIf((IfStatement)node);
-
-          case Token.TRUE:
-          case Token.FALSE:
-          case Token.THIS:
-          case Token.NULL:
-          case Token.DEBUGGER:
-              return transformLiteral(node);
-
-          case Token.NAME:
-              return transformName((Name)node);
-          case Token.NUMBER:
-              return transformNumber((NumberLiteral)node);
-          case Token.NEW:
-              return transformNewExpr((NewExpression)node);
-          case Token.OBJECTLIT:
-              return transformObjectLiteral((ObjectLiteral)node);
-          case Token.REGEXP:
-              return transformRegExp((RegExpLiteral)node);
-          case Token.RETURN:
-              return transformReturn((ReturnStatement)node);
-          case Token.SCRIPT:
-              return transformScript((ScriptNode)node);
-          case Token.STRING:
-              return transformString((StringLiteral)node);
-          case Token.SWITCH:
-              return transformSwitch((SwitchStatement)node);
-          case Token.THROW:
-              return transformThrow((ThrowStatement)node);
-          case Token.TRY:
-              return transformTry((TryStatement)node);
-          case Token.WHILE:
-              return transformWhileLoop((WhileLoop)node);
-          case Token.WITH:
-              return transformWith((WithStatement)node);
-          case Token.YIELD:
-              return transformYield((Yield)node);
-          default:
-              if (node instanceof ExpressionStatement) {
-                  return transformExprStmt((ExpressionStatement)node);
-              }
-              if (node instanceof Assignment) {
-                  return transformAssignment((Assignment)node);
-              }
-              if (node instanceof UnaryExpression) {
-                  return transformUnary((UnaryExpression)node);
-              }
-              if (node instanceof XmlMemberGet) {
-                  return transformXmlMemberGet((XmlMemberGet)node);
-              }
-              if (node instanceof InfixExpression) {
-                  return transformInfix((InfixExpression)node);
-              }
-              if (node instanceof VariableDeclaration) {
-                  return transformVariables((VariableDeclaration)node);
-              }
-              if (node instanceof ParenthesizedExpression) {
-                  return transformParenExpr((ParenthesizedExpression)node);
-              }
-              if (node instanceof LabeledStatement) {
-                  return transformLabeledStatement((LabeledStatement)node);
-              }
-              if (node instanceof LetNode) {
-                  return transformLetNode((LetNode)node);
-              }
-              if (node instanceof XmlRef) {
-                  return transformXmlRef((XmlRef)node);
-              }
-              if (node instanceof XmlLiteral) {
-                  return transformXmlLiteral((XmlLiteral)node);
-              }
-              throw new IllegalArgumentException("Can't transform: " + node);
+            case Token.ARRAYCOMP:
+                return transformArrayComp((ArrayComprehension) node);
+            case Token.ARRAYLIT:
+                return transformArrayLiteral((ArrayLiteral) node);
+            case Token.BIGINT:
+                return transformBigInt((BigIntLiteral) node);
+            case Token.BLOCK:
+                return transformBlock(node);
+            case Token.BREAK:
+                return transformBreak((BreakStatement) node);
+            case Token.CALL:
+                return transformFunctionCall((FunctionCall) node);
+            case Token.CONTINUE:
+                return transformContinue((ContinueStatement) node);
+            case Token.DO:
+                return transformDoLoop((DoLoop) node);
+            case Token.EMPTY:
+            case Token.COMMENT:
+                return node;
+            case Token.FOR:
+                if (node instanceof ForInLoop) {
+                    return transformForInLoop((ForInLoop) node);
+                }
+                return transformForLoop((ForLoop) node);
+            case Token.FUNCTION:
+                return transformFunction((FunctionNode) node);
+            case Token.GENEXPR:
+                return transformGenExpr((GeneratorExpression) node);
+            case Token.GETELEM:
+                return transformElementGet((ElementGet) node);
+            case Token.GETPROP:
+                return transformPropertyGet((PropertyGet) node);
+            case Token.HOOK:
+                return transformCondExpr((ConditionalExpression) node);
+            case Token.IF:
+                return transformIf((IfStatement) node);
+
+            case Token.TRUE:
+            case Token.FALSE:
+            case Token.THIS:
+            case Token.NULL:
+            case Token.DEBUGGER:
+                return transformLiteral(node);
+
+            case Token.NAME:
+                return transformName((Name) node);
+            case Token.NUMBER:
+                return transformNumber((NumberLiteral) node);
+            case Token.NEW:
+                return transformNewExpr((NewExpression) node);
+            case Token.OBJECTLIT:
+                return transformObjectLiteral((ObjectLiteral) node);
+            case Token.TEMPLATE_LITERAL:
+                return transformTemplateLiteral((TemplateLiteral) node);
+            case Token.TAGGED_TEMPLATE_LITERAL:
+                return transformTemplateLiteralCall((TaggedTemplateLiteral) node);
+            case Token.REGEXP:
+                return transformRegExp((RegExpLiteral) node);
+            case Token.RETURN:
+                return transformReturn((ReturnStatement) node);
+            case Token.SCRIPT:
+                return transformScript((ScriptNode) node);
+            case Token.STRING:
+                return transformString((StringLiteral) node);
+            case Token.SWITCH:
+                return transformSwitch((SwitchStatement) node);
+            case Token.THROW:
+                return transformThrow((ThrowStatement) node);
+            case Token.TRY:
+                return transformTry((TryStatement) node);
+            case Token.WHILE:
+                return transformWhileLoop((WhileLoop) node);
+            case Token.WITH:
+                return transformWith((WithStatement) node);
+            case Token.YIELD:
+            case Token.YIELD_STAR:
+                return transformYield((Yield) node);
+            default:
+                if (node instanceof ExpressionStatement) {
+                    return transformExprStmt((ExpressionStatement) node);
+                }
+                if (node instanceof Assignment) {
+                    return transformAssignment((Assignment) node);
+                }
+                if (node instanceof UnaryExpression) {
+                    return transformUnary((UnaryExpression) node);
+                }
+                if (node instanceof UpdateExpression) {
+                    return transformUpdate((UpdateExpression) node);
+                }
+                if (node instanceof XmlMemberGet) {
+                    return transformXmlMemberGet((XmlMemberGet) node);
+                }
+                if (node instanceof InfixExpression) {
+                    return transformInfix((InfixExpression) node);
+                }
+                if (node instanceof VariableDeclaration) {
+                    return transformVariables((VariableDeclaration) node);
+                }
+                if (node instanceof ParenthesizedExpression) {
+                    return transformParenExpr((ParenthesizedExpression) node);
+                }
+                if (node instanceof LabeledStatement) {
+                    return transformLabeledStatement((LabeledStatement) node);
+                }
+                if (node instanceof LetNode) {
+                    return transformLetNode((LetNode) node);
+                }
+                if (node instanceof XmlRef) {
+                    return transformXmlRef((XmlRef) node);
+                }
+                if (node instanceof XmlLiteral) {
+                    return transformXmlLiteral((XmlLiteral) node);
+                }
+                throw new IllegalArgumentException("Can't transform: " + node);
         }
     }
 
@@ -271,11 +284,11 @@
             defineSymbol(Token.LET, arrayName, false);
             Node block = new Node(Token.BLOCK, lineno);
             Node newArray = createCallOrNew(Token.NEW, createName("Array"));
-            Node init = new Node(Token.EXPR_VOID,
-                                 createAssignment(Token.ASSIGN,
-                                                  createName(arrayName),
-                                                  newArray),
-                                 lineno);
+            Node init =
+                    new Node(
+                            Token.EXPR_VOID,
+                            createAssignment(Token.ASSIGN, createName(arrayName), newArray),
+                            lineno);
             block.addChildToBack(init);
             block.addChildToBack(arrayCompTransformHelper(node, arrayName));
             scopeNode.addChildToBack(block);
@@ -286,8 +299,7 @@
         }
     }
 
-    private Node arrayCompTransformHelper(ArrayComprehension node,
-                                          String arrayName) {
+    private Node arrayCompTransformHelper(ArrayComprehension node, String arrayName) {
         decompiler.addToken(Token.LB);
         int lineno = node.getLineno();
         Node expr = transform(node.getResult());
@@ -318,11 +330,11 @@
                 decompile(iter);
                 name = currentScriptOrFn.getNextTempName();
                 defineSymbol(Token.LP, name, false);
-                expr = createBinary(Token.COMMA,
-                                    createAssignment(Token.ASSIGN,
-                                                     iter,
-                                                     createName(name)),
-                                    expr);
+                expr =
+                        createBinary(
+                                Token.COMMA,
+                                createAssignment(Token.ASSIGN, iter, createName(name)),
+                                expr);
             }
             Node init = createName(name);
             // Define as a let since we want the scope of the variable to
@@ -340,10 +352,9 @@
         }
 
         // generate code for tmpArray.push(body)
-        Node call = createCallOrNew(Token.CALL,
-                                    createPropertyGet(createName(arrayName),
-                                                      null,
-                                                      "push", 0));
+        Node call =
+                createCallOrNew(
+                        Token.CALL, createPropertyGet(createName(arrayName), null, "push", 0));
 
         Node body = new Node(Token.EXPR_VOID, call, lineno);
 
@@ -358,19 +369,23 @@
         // Now walk loops in reverse to build up the body statement.
         int pushed = 0;
         try {
-            for (int i = numLoops-1; i >= 0; i--) {
+            for (int i = numLoops - 1; i >= 0; i--) {
                 ArrayComprehensionLoop acl = loops.get(i);
-                Scope loop = createLoopNode(null,  // no label
-                                            acl.getLineno());
+                Scope loop =
+                        createLoopNode(
+                                null, // no label
+                                acl.getLineno());
                 pushScope(loop);
                 pushed++;
-                body = createForIn(Token.LET,
-                                   loop,
-                                   iterators[i],
-                                   iteratedObjs[i],
-                                   body,
-                                   acl.isForEach(),
-                                   acl.isForOf());
+                body =
+                        createForIn(
+                                Token.LET,
+                                loop,
+                                iterators[i],
+                                iteratedObjs[i],
+                                body,
+                                acl.isForEach(),
+                                acl.isForOf());
             }
         } finally {
             for (int i = 0; i < pushed; i++) {
@@ -402,25 +417,25 @@
                 if (skipIndexes == null) {
                     skipIndexes = new ArrayList<Integer>();
                 }
-                skipIndexes.add(i);
+                skipIndexes.add(Integer.valueOf(i));
             }
-            if (i < elems.size() - 1)
-                decompiler.addToken(Token.COMMA);
+            if (i < elems.size() - 1) decompiler.addToken(Token.COMMA);
         }
         decompiler.addToken(Token.RB);
-        array.putIntProp(Node.DESTRUCTURING_ARRAY_LENGTH,
-                         node.getDestructuringLength());
+        array.putIntProp(Node.DESTRUCTURING_ARRAY_LENGTH, node.getDestructuringLength());
         if (skipIndexes != null) {
             int[] skips = new int[skipIndexes.size()];
-            for (int i = 0; i < skipIndexes.size(); i++)
-                skips[i] = skipIndexes.get(i);
+            for (int i = 0; i < skipIndexes.size(); i++) skips[i] = skipIndexes.get(i).intValue();
             array.putProp(Node.SKIP_INDEXES_PROP, skips);
         }
         return array;
     }
 
     private Node transformAssignment(Assignment node) {
+        AstNode right = node.getRight();
         AstNode left = removeParens(node.getLeft());
+        left = transformAssignmentLeft(node, left, right);
+
         Node target = null;
         if (isDestructuring(left)) {
             decompile(left);
@@ -428,20 +443,49 @@
         } else {
             target = transform(left);
         }
+
         decompiler.addToken(node.getType());
-        return createAssignment(node.getType(),
-                                target,
-                                transform(node.getRight()));
+        return createAssignment(node.getType(), target, transform(right));
+    }
+
+    private AstNode transformAssignmentLeft(Assignment node, AstNode left, AstNode right) {
+        if (right.getType() == Token.NULL
+                && node.getType() == Token.ASSIGN
+                && left instanceof Name
+                && right instanceof KeywordLiteral) {
+
+            String identifier = ((Name) left).getIdentifier();
+            for (AstNode p = node.getParent(); p != null; p = p.getParent()) {
+                if (p instanceof FunctionNode) {
+                    Name functionName = ((FunctionNode) p).getFunctionName();
+                    if (functionName != null && functionName.getIdentifier().equals(identifier)) {
+                        PropertyGet propertyGet = new PropertyGet();
+                        KeywordLiteral thisKeyword = new KeywordLiteral();
+                        thisKeyword.setType(Token.THIS);
+                        propertyGet.setLeft(thisKeyword);
+                        propertyGet.setRight(left);
+                        node.setLeft(propertyGet);
+                        return propertyGet;
+                    }
+                }
+            }
+        }
+        return left;
+    }
+
+    private Node transformBigInt(BigIntLiteral node) {
+        decompiler.addBigInt(node.getBigInt());
+        return node;
     }
 
     private Node transformBlock(AstNode node) {
         if (node instanceof Scope) {
-            pushScope((Scope)node);
+            pushScope((Scope) node);
         }
         try {
             List<Node> kids = new ArrayList<Node>();
             for (Node kid : node) {
-                kids.add(transform((AstNode)kid));
+                kids.add(transform((AstNode) kid));
             }
             node.removeChildren();
             for (Node kid : kids) {
@@ -495,8 +539,7 @@
             Node cond = transform(loop.getCondition());
             decompiler.addToken(Token.RP);
             decompiler.addEOL(Token.SEMI);
-            return createLoop(loop, LOOP_DO_WHILE,
-                              body, cond, null, null);
+            return createLoop(loop, LOOP_DO_WHILE, body, cond, null, null);
         } finally {
             popScope();
         }
@@ -520,8 +563,7 @@
 
     private Node transformForInLoop(ForInLoop loop) {
         decompiler.addToken(Token.FOR);
-        if (loop.isForEach())
-            decompiler.addName("each ");
+        if (loop.isForEach()) decompiler.addName("each ");
         decompiler.addToken(Token.LP);
 
         loop.setType(Token.LOOP);
@@ -530,7 +572,7 @@
             int declType = -1;
             AstNode iter = loop.getIterator();
             if (iter instanceof VariableDeclaration) {
-                declType = ((VariableDeclaration)iter).getType();
+                declType = ((VariableDeclaration) iter).getType();
             }
             Node lhs = transform(iter);
             if (loop.isForOf()) {
@@ -543,8 +585,7 @@
             decompiler.addEOL(Token.LC);
             Node body = transform(loop.getBody());
             decompiler.addEOL(Token.RC);
-            return createForIn(declType, loop, lhs, obj, body,
-                               loop.isForEach(), loop.isForOf());
+            return createForIn(declType, loop, lhs, obj, body, loop.isForEach(), loop.isForOf());
         } finally {
             popScope();
         }
@@ -584,11 +625,11 @@
         try {
             // If we start needing to record much more codegen metadata during
             // function parsing, we should lump it all into a helper class.
-            Node destructuring = (Node)fn.getProp(Node.DESTRUCTURING_PARAMS);
+            Node destructuring = (Node) fn.getProp(Node.DESTRUCTURING_PARAMS);
             fn.removeProp(Node.DESTRUCTURING_PARAMS);
 
             int lineno = fn.getBody().getLineno();
-            ++nestingOfFunction;  // only for body, not params
+            ++nestingOfFunction; // only for body, not params
             Node body = transform(fn.getBody());
 
             if (!fn.isExpressionClosure()) {
@@ -603,8 +644,7 @@
             }
 
             if (destructuring != null) {
-                body.addChildToFront(new Node(Token.EXPR_VOID,
-                                              destructuring, lineno));
+                body.addChildToFront(new Node(Token.EXPR_VOID, destructuring, lineno));
             }
 
             int syntheticType = fn.getFunctionType();
@@ -638,16 +678,16 @@
         decompiler.addToken(Token.RP);
         return call;
     }
-    
+
     private Node transformGenExpr(GeneratorExpression node) {
         Node pn;
-        
+
         FunctionNode fn = new FunctionNode();
         fn.setSourceName(currentScriptOrFn.getNextTempName());
         fn.setIsGenerator();
         fn.setFunctionType(FunctionNode.FUNCTION_EXPRESSION);
         fn.setRequiresActivation();
-      
+
         int functionType = fn.getFunctionType();
         int start = decompiler.markFunctionStart(functionType);
         Node mexpr = decompileFunctionHeader(fn);
@@ -657,11 +697,11 @@
         try {
             // If we start needing to record much more codegen metadata during
             // function parsing, we should lump it all into a helper class.
-            Node destructuring = (Node)fn.getProp(Node.DESTRUCTURING_PARAMS);
+            Node destructuring = (Node) fn.getProp(Node.DESTRUCTURING_PARAMS);
             fn.removeProp(Node.DESTRUCTURING_PARAMS);
 
             int lineno = node.lineno;
-            ++nestingOfFunction;  // only for body, not params
+            ++nestingOfFunction; // only for body, not params
             Node body = genExprTransformHelper(node);
 
             if (!fn.isExpressionClosure()) {
@@ -676,8 +716,7 @@
             }
 
             if (destructuring != null) {
-                body.addChildToFront(new Node(Token.EXPR_VOID,
-                                              destructuring, lineno));
+                body.addChildToFront(new Node(Token.EXPR_VOID, destructuring, lineno));
             }
 
             int syntheticType = fn.getFunctionType();
@@ -692,14 +731,14 @@
             --nestingOfFunction;
             savedVars.restore();
         }
-       
+
         Node call = createCallOrNew(Token.CALL, pn);
         call.setLineno(node.getLineno());
         decompiler.addToken(Token.LP);
         decompiler.addToken(Token.RP);
         return call;
     }
-    
+
     private Node genExprTransformHelper(GeneratorExpression node) {
         decompiler.addToken(Token.LP);
         int lineno = node.getLineno();
@@ -728,11 +767,11 @@
                 decompile(iter);
                 name = currentScriptOrFn.getNextTempName();
                 defineSymbol(Token.LP, name, false);
-                expr = createBinary(Token.COMMA,
-                                    createAssignment(Token.ASSIGN,
-                                                     iter,
-                                                     createName(name)),
-                                    expr);
+                expr =
+                        createBinary(
+                                Token.COMMA,
+                                createAssignment(Token.ASSIGN, iter, createName(name)),
+                                expr);
             }
             Node init = createName(name);
             // Define as a let since we want the scope of the variable to
@@ -765,19 +804,23 @@
         // Now walk loops in reverse to build up the body statement.
         int pushed = 0;
         try {
-            for (int i = numLoops-1; i >= 0; i--) {
+            for (int i = numLoops - 1; i >= 0; i--) {
                 GeneratorExpressionLoop acl = loops.get(i);
-                Scope loop = createLoopNode(null,  // no label
-                                            acl.getLineno());
+                Scope loop =
+                        createLoopNode(
+                                null, // no label
+                                acl.getLineno());
                 pushScope(loop);
                 pushed++;
-                body = createForIn(Token.LET,
-                                   loop,
-                                   iterators[i],
-                                   iteratedObjs[i],
-                                   body,
-                                   acl.isForEach(),
-                                   acl.isForOf());
+                body =
+                        createForIn(
+                                Token.LET,
+                                loop,
+                                iterators[i],
+                                iteratedObjs[i],
+                                body,
+                                acl.isForEach(),
+                                acl.isForOf());
             }
         } finally {
             for (int i = 0; i < pushed; i++) {
@@ -967,15 +1010,15 @@
     private Object getPropKey(Node id) {
         Object key;
         if (id instanceof Name) {
-            String s = ((Name)id).getIdentifier();
+            String s = ((Name) id).getIdentifier();
             decompiler.addName(s);
             key = ScriptRuntime.getIndexObject(s);
         } else if (id instanceof StringLiteral) {
-            String s = ((StringLiteral)id).getValue();
+            String s = ((StringLiteral) id).getValue();
             decompiler.addString(s);
             key = ScriptRuntime.getIndexObject(s);
         } else if (id instanceof NumberLiteral) {
-            double n = ((NumberLiteral)id).getNumber();
+            double n = ((NumberLiteral) id).getNumber();
             decompiler.addNumber(n);
             key = ScriptRuntime.getIndexObject(n);
         } else {
@@ -991,7 +1034,7 @@
         while (expr instanceof ParenthesizedExpression) {
             decompiler.addToken(Token.LP);
             count++;
-            expr = ((ParenthesizedExpression)expr).getExpression();
+            expr = ((ParenthesizedExpression) expr).getExpression();
         }
         Node result = transform(expr);
         for (int i = 0; i < count; i++) {
@@ -1009,6 +1052,54 @@
         return createPropertyGet(target, null, name, 0);
     }
 
+    private Node transformTemplateLiteral(TemplateLiteral node) {
+        decompiler.addToken(Token.TEMPLATE_LITERAL);
+        List<AstNode> elems = node.getElements();
+        // start with an empty string to ensure ToString() for each substitution
+        Node pn = Node.newString("");
+        for (int i = 0; i < elems.size(); ++i) {
+            AstNode elem = elems.get(i);
+            if (elem.getType() != Token.TEMPLATE_CHARS) {
+                decompiler.addToken(Token.TEMPLATE_LITERAL_SUBST);
+                pn = createBinary(Token.ADD, pn, transform(elem));
+                decompiler.addToken(Token.RC);
+            } else {
+                TemplateCharacters chars = (TemplateCharacters) elem;
+                decompiler.addTemplateLiteral(chars.getRawValue());
+                // skip empty parts, e.g. `xx${expr}xx` where xx denotes the empty string
+                String value = chars.getValue();
+                if (value.length() > 0) {
+                    pn = createBinary(Token.ADD, pn, Node.newString(value));
+                }
+            }
+        }
+        decompiler.addToken(Token.TEMPLATE_LITERAL);
+        return pn;
+    }
+
+    private Node transformTemplateLiteralCall(TaggedTemplateLiteral node) {
+        Node call = createCallOrNew(Token.CALL, transform(node.getTarget()));
+        call.setLineno(node.getLineno());
+        decompiler.addToken(Token.TEMPLATE_LITERAL);
+        TemplateLiteral templateLiteral = (TemplateLiteral) node.getTemplateLiteral();
+        List<AstNode> elems = templateLiteral.getElements();
+        call.addChildToBack(templateLiteral);
+        for (int i = 0; i < elems.size(); ++i) {
+            AstNode elem = elems.get(i);
+            if (elem.getType() != Token.TEMPLATE_CHARS) {
+                decompiler.addToken(Token.TEMPLATE_LITERAL_SUBST);
+                call.addChildToBack(transform(elem));
+                decompiler.addToken(Token.RC);
+            } else {
+                TemplateCharacters chars = (TemplateCharacters) elem;
+                decompiler.addTemplateLiteral(chars.getRawValue());
+            }
+        }
+        currentScriptOrFn.addTemplateLiteral(templateLiteral);
+        decompiler.addToken(Token.TEMPLATE_LITERAL);
+        return call;
+    }
+
     private Node transformRegExp(RegExpLiteral node) {
         decompiler.addRegexp(node.getValue(), node.getFlags());
         currentScriptOrFn.addRegExp(node);
@@ -1029,8 +1120,8 @@
         Node value = rv == null ? null : transform(rv);
         if (!expClosure) decompiler.addEOL(Token.SEMI);
         return rv == null
-            ? new Node(Token.RETURN, node.getLineno())
-            : new Node(Token.RETURN, value, node.getLineno());
+                ? new Node(Token.RETURN, node.getLineno())
+                : new Node(Token.RETURN, value, node.getLineno());
     }
 
     private Node transformScript(ScriptNode node) {
@@ -1039,7 +1130,7 @@
         currentScope = node;
         Node body = new Node(Token.BLOCK);
         for (Node kid : node) {
-            body.addChildToBack(transform((AstNode)kid));
+            body.addChildToBack(transform((AstNode) kid));
         }
         node.removeChildren();
         Node children = body.getFirstChild();
@@ -1165,8 +1256,7 @@
             Node body = transform(cc.getBody());
             decompiler.addEOL(Token.RC);
 
-            catchBlocks.addChildToBack(createCatch(varName, catchCond,
-                                                   body, cc.getLineno()));
+            catchBlocks.addChildToBack(createCatch(varName, catchCond, body, cc.getLineno()));
         }
         Node finallyBlock = null;
         if (node.getFinallyBlock() != null) {
@@ -1175,8 +1265,7 @@
             finallyBlock = transform(node.getFinallyBlock());
             decompiler.addEOL(Token.RC);
         }
-        return createTryCatchFinally(tryBlock, catchBlocks,
-                                     finallyBlock, node.getLineno());
+        return createTryCatchFinally(tryBlock, catchBlocks, finallyBlock, node.getLineno());
     }
 
     private Node transformUnary(UnaryExpression node) {
@@ -1184,6 +1273,14 @@
         if (type == Token.DEFAULTNAMESPACE) {
             return transformDefaultXmlNamepace(node);
         }
+        decompiler.addToken(type);
+
+        Node child = transform(node.getOperand());
+        return createUnary(type, child);
+    }
+
+    private Node transformUpdate(UpdateExpression node) {
+        int type = node.getType();
         if (node.isPrefix()) {
             decompiler.addToken(type);
         }
@@ -1191,10 +1288,7 @@
         if (node.isPostfix()) {
             decompiler.addToken(type);
         }
-        if (type == Token.INC || type == Token.DEC) {
-            return createIncDec(type, node.isPostfix(), child);
-        }
-        return createUnary(type, child);
+        return createIncDec(type, node.isPostfix(), child);
     }
 
     private Node transformVariables(VariableDeclaration node) {
@@ -1204,8 +1298,7 @@
         // Might be most robust to have parser record whether it was
         // a variable declaration statement, possibly as a node property.
         AstNode parent = node.getParent();
-        if (!(parent instanceof Loop)
-            && !(parent instanceof LetNode)) {
+        if (!(parent instanceof Loop) && !(parent instanceof LetNode)) {
             decompiler.addEOL(Token.SEMI);
         }
         return node;
@@ -1220,7 +1313,7 @@
 
             Node left = null;
             if (var.isDestructuring()) {
-                decompile(target);  // decompile but don't transform
+                decompile(target); // decompile but don't transform
                 left = target;
             } else {
                 left = transform(target);
@@ -1233,11 +1326,10 @@
             }
 
             if (var.isDestructuring()) {
-                if (right == null) {  // TODO:  should this ever happen?
+                if (right == null) { // TODO:  should this ever happen?
                     node.addChildToBack(left);
                 } else {
-                    Node d = createDestructuringAssignment(node.getType(),
-                                                           left, right);
+                    Node d = createDestructuringAssignment(node.getType(), left, right);
                     node.addChildToBack(d);
                 }
             } else {
@@ -1246,7 +1338,7 @@
                 }
                 node.addChildToBack(left);
             }
-            if (i++ < size-1) {
+            if (i++ < size - 1) {
                 decompiler.addToken(Token.COMMA);
             }
         }
@@ -1282,12 +1374,10 @@
     }
 
     private Node transformYield(Yield node) {
-        decompiler.addToken(Token.YIELD);
+        decompiler.addToken(node.getType());
         Node kid = node.getValue() == null ? null : transform(node.getValue());
-        if (kid != null)
-            return new Node(Token.YIELD, kid, node.getLineno());
-        else
-            return new Node(Token.YIELD, node.getLineno());
+        if (kid != null) return new Node(node.getType(), kid, node.getLineno());
+        return new Node(node.getType(), node.getLineno());
     }
 
     private Node transformXmlLiteral(XmlLiteral node) {
@@ -1297,14 +1387,14 @@
         Node pnXML = new Node(Token.NEW, node.getLineno());
         List<XmlFragment> frags = node.getFragments();
 
-        XmlString first = (XmlString)frags.get(0);
+        XmlString first = (XmlString) frags.get(0);
         boolean anon = first.getXml().trim().startsWith("<>");
         pnXML.addChildToBack(createName(anon ? "XMLList" : "XML"));
 
         Node pn = null;
         for (XmlFragment frag : frags) {
             if (frag instanceof XmlString) {
-                String xml = ((XmlString)frag).getXml();
+                String xml = ((XmlString) frag).getXml();
                 decompiler.addName(xml);
                 if (pn == null) {
                     pn = createString(xml);
@@ -1312,7 +1402,7 @@
                     pn = createBinary(Token.ADD, pn, createString(xml));
                 }
             } else {
-                XmlExpression xexpr = (XmlExpression)frag;
+                XmlExpression xexpr = (XmlExpression) frag;
                 boolean isXmlAttr = xexpr.isXmlAttribute();
                 Node expr;
                 decompiler.addToken(Token.LC);
@@ -1325,12 +1415,8 @@
                 if (isXmlAttr) {
                     // Need to put the result in double quotes
                     expr = createUnary(Token.ESCXMLATTR, expr);
-                    Node prepend = createBinary(Token.ADD,
-                                                createString("\""),
-                                                expr);
-                    expr = createBinary(Token.ADD,
-                                        prepend,
-                                        createString("\""));
+                    Node prepend = createBinary(Token.ADD, createString("\""), expr);
+                    expr = createBinary(Token.ADD, prepend, createString("\""));
                 } else {
                     expr = createUnary(Token.ESCXMLTEXT, expr);
                 }
@@ -1357,14 +1443,12 @@
 
     // We get here if we weren't a child of a . or .. infix node
     private Node transformXmlRef(XmlRef node) {
-        int memberTypeFlags = node.isAttributeAccess()
-            ? Node.ATTRIBUTE_FLAG : 0;
+        int memberTypeFlags = node.isAttributeAccess() ? Node.ATTRIBUTE_FLAG : 0;
         return transformXmlRef(null, node, memberTypeFlags);
     }
 
     private Node transformXmlRef(Node pn, XmlRef node, int memberTypeFlags) {
-        if ((memberTypeFlags & Node.ATTRIBUTE_FLAG) != 0)
-            decompiler.addToken(Token.XMLATTR);
+        if ((memberTypeFlags & Node.ATTRIBUTE_FLAG) != 0) decompiler.addToken(Token.XMLATTR);
         Name namespace = node.getNamespace();
         String ns = namespace != null ? namespace.getIdentifier() : null;
         if (ns != null) {
@@ -1372,15 +1456,14 @@
             decompiler.addToken(Token.COLONCOLON);
         }
         if (node instanceof XmlPropRef) {
-            String name = ((XmlPropRef)node).getPropName().getIdentifier();
+            String name = ((XmlPropRef) node).getPropName().getIdentifier();
             decompiler.addName(name);
             return createPropertyGet(pn, ns, name, memberTypeFlags);
-        } else {
-            decompiler.addToken(Token.LB);
-            Node expr = transform(((XmlElemRef)node).getExpression());
-            decompiler.addToken(Token.RB);
-            return createElementGet(pn, ns, expr, memberTypeFlags);
         }
+        decompiler.addToken(Token.LB);
+        Node expr = transform(((XmlElemRef) node).getExpression());
+        decompiler.addToken(Token.RB);
+        return createElementGet(pn, ns, expr, memberTypeFlags);
     }
 
     private Node transformDefaultXmlNamepace(UnaryExpression node) {
@@ -1392,14 +1475,10 @@
         return createUnary(Token.DEFAULTNAMESPACE, child);
     }
 
-    /**
-     * If caseExpression argument is null it indicates a default label.
-     */
-    private void addSwitchCase(Node switchBlock, Node caseExpression,
-                               Node statements)
-    {
+    /** If caseExpression argument is null it indicates a default label. */
+    private static void addSwitchCase(Node switchBlock, Node caseExpression, Node statements) {
         if (switchBlock.getType() != Token.BLOCK) throw Kit.codeBug();
-        Jump switchNode = (Jump)switchBlock.getFirstChild();
+        Jump switchNode = (Jump) switchBlock.getFirstChild();
         if (switchNode.getType() != Token.SWITCH) throw Kit.codeBug();
 
         Node gotoTarget = Node.newTarget();
@@ -1414,10 +1493,9 @@
         switchBlock.addChildToBack(statements);
     }
 
-    private void closeSwitch(Node switchBlock)
-    {
+    private static void closeSwitch(Node switchBlock) {
         if (switchBlock.getType() != Token.BLOCK) throw Kit.codeBug();
-        Jump switchNode = (Jump)switchBlock.getFirstChild();
+        Jump switchNode = (Jump) switchBlock.getFirstChild();
         if (switchNode.getType() != Token.SWITCH) throw Kit.codeBug();
 
         Node switchBreakTarget = Node.newTarget();
@@ -1430,38 +1508,36 @@
             defaultTarget = switchBreakTarget;
         }
 
-        switchBlock.addChildAfter(makeJump(Token.GOTO, defaultTarget),
-                                  switchNode);
+        switchBlock.addChildAfter(makeJump(Token.GOTO, defaultTarget), switchNode);
         switchBlock.addChildToBack(switchBreakTarget);
     }
 
-    private Node createExprStatementNoReturn(Node expr, int lineno) {
+    private static Node createExprStatementNoReturn(Node expr, int lineno) {
         return new Node(Token.EXPR_VOID, expr, lineno);
     }
 
-    private Node createString(String string) {
+    private static Node createString(String string) {
         return Node.newString(string);
     }
 
     /**
      * Catch clause of try/catch/finally
+     *
      * @param varName the name of the variable to bind to the exception
-     * @param catchCond the condition under which to catch the exception.
-     *                  May be null if no condition is given.
+     * @param catchCond the condition under which to catch the exception. May be null if no
+     *     condition is given.
      * @param stmts the statements in the catch clause
      * @param lineno the starting line number of the catch clause
      */
-    private Node createCatch(String varName, Node catchCond, Node stmts,
-                             int lineno) {
+    private Node createCatch(String varName, Node catchCond, Node stmts, int lineno) {
         if (catchCond == null) {
             catchCond = new Node(Token.EMPTY);
         }
-        return new Node(Token.CATCH, createName(varName),
-                        catchCond, stmts, lineno);
+        return new Node(Token.CATCH, createName(varName), catchCond, stmts, lineno);
     }
 
-    private Node initFunction(FunctionNode fnNode, int functionIndex,
-                              Node statements, int functionType) {
+    private static Node initFunction(
+            FunctionNode fnNode, int functionIndex, Node statements, int functionType) {
         fnNode.setFunctionType(functionType);
         fnNode.addChildToBack(statements);
 
@@ -1473,7 +1549,8 @@
 
         if (functionType == FunctionNode.FUNCTION_EXPRESSION) {
             Name name = fnNode.getFunctionName();
-            if (name != null && name.length() != 0
+            if (name != null
+                    && name.length() != 0
                     && fnNode.getSymbol(name.getIdentifier()) == null) {
                 // A function expression needs to have its name as a
                 // variable (if it isn't already allocated as a variable).
@@ -1483,11 +1560,13 @@
                 // function doesn't already define a formal parameter, var,
                 // or nested function with the same name.
                 fnNode.putSymbol(new Symbol(Token.FUNCTION, name.getIdentifier()));
-                Node setFn = new Node(Token.EXPR_VOID,
-                                 new Node(Token.SETNAME,
-                                          Node.newString(Token.BINDNAME,
-                                                         name.getIdentifier()),
-                                     new Node(Token.THISFN)));
+                Node setFn =
+                        new Node(
+                                Token.EXPR_VOID,
+                                new Node(
+                                        Token.SETNAME,
+                                        Node.newString(Token.BINDNAME, name.getIdentifier()),
+                                        new Node(Token.THISFN)));
                 statements.addChildrenToFront(setFn);
             }
         }
@@ -1505,19 +1584,17 @@
 
     /**
      * Create loop node. The code generator will later call
-     * createWhile|createDoWhile|createFor|createForIn
-     * to finish loop generation.
+     * createWhile|createDoWhile|createFor|createForIn to finish loop generation.
      */
     private Scope createLoopNode(Node loopLabel, int lineno) {
         Scope result = createScopeNode(Token.LOOP, lineno);
         if (loopLabel != null) {
-            ((Jump)loopLabel).setLoop(result);
+            ((Jump) loopLabel).setLoop(result);
         }
         return result;
     }
 
-    private Node createFor(Scope loop, Node init,
-                           Node test, Node incr, Node body) {
+    private static Node createFor(Scope loop, Node init, Node test, Node incr, Node body) {
         if (init.getType() == Token.LET) {
             // rewrite "for (let i=s; i < N; i++)..." as
             // "let (i=s) { for (; i < N; i++)..." so that "s" is evaluated
@@ -1525,16 +1602,14 @@
             Scope let = Scope.splitScope(loop);
             let.setType(Token.LET);
             let.addChildrenToBack(init);
-            let.addChildToBack(createLoop(loop, LOOP_FOR, body, test,
-                new Node(Token.EMPTY), incr));
+            let.addChildToBack(createLoop(loop, LOOP_FOR, body, test, new Node(Token.EMPTY), incr));
             return let;
         }
         return createLoop(loop, LOOP_FOR, body, test, init, incr);
     }
 
-    private Node createLoop(Jump loop, int loopType, Node body,
-                            Node cond, Node init, Node incr)
-    {
+    private static Node createLoop(
+            Jump loop, int loopType, Node body, Node cond, Node init, Node incr) {
         Node bodyTarget = Node.newTarget();
         Node condTarget = Node.newTarget();
         if (loopType == LOOP_FOR && cond.getType() == Token.EMPTY) {
@@ -1583,12 +1658,15 @@
         return loop;
     }
 
-    /**
-     * Generate IR for a for..in loop.
-     */
-    private Node createForIn(int declType, Node loop, Node lhs,
-                             Node obj, Node body, boolean isForEach, boolean isForOf)
-    {
+    /** Generate IR for a for..in loop. */
+    private Node createForIn(
+            int declType,
+            Node loop,
+            Node lhs,
+            Node obj,
+            Node body,
+            boolean isForEach,
+            boolean isForOf) {
         int destructuring = -1;
         int destructuringLen = 0;
         Node lvalue;
@@ -1596,8 +1674,7 @@
         if (type == Token.VAR || type == Token.LET) {
             Node kid = lhs.getLastChild();
             int kidType = kid.getType();
-            if (kidType == Token.ARRAYLIT || kidType == Token.OBJECTLIT)
-            {
+            if (kidType == Token.ARRAYLIT || kidType == Token.OBJECTLIT) {
                 type = destructuring = kidType;
                 lvalue = kid;
                 destructuringLen = 0;
@@ -1624,11 +1701,14 @@
         }
 
         Node localBlock = new Node(Token.LOCAL_BLOCK);
-        int initType = isForEach ? Token.ENUM_INIT_VALUES
-                       : isForOf ? Token.ENUM_INIT_VALUES_IN_ORDER
-                                 : (destructuring != -1
-                                    ? Token.ENUM_INIT_ARRAY
-                                    : Token.ENUM_INIT_KEYS);
+        int initType =
+                isForEach
+                        ? Token.ENUM_INIT_VALUES
+                        : isForOf
+                                ? Token.ENUM_INIT_VALUES_IN_ORDER
+                                : (destructuring != -1
+                                        ? Token.ENUM_INIT_ARRAY
+                                        : Token.ENUM_INIT_KEYS);
         Node init = new Node(initType, obj);
         init.putProp(Node.LOCAL_BLOCK_PROP, localBlock);
         Node cond = new Node(Token.ENUM_NEXT);
@@ -1640,10 +1720,9 @@
         Node assign;
         if (destructuring != -1) {
             assign = createDestructuringAssignment(declType, lvalue, id);
-            if (!isForEach && !isForOf &&
-                (destructuring == Token.OBJECTLIT ||
-                 destructuringLen != 2))
-            {
+            if (!isForEach
+                    && !isForOf
+                    && (destructuring == Token.OBJECTLIT || destructuringLen != 2)) {
                 // destructuring assignment is only allowed in for..each or
                 // with an array type of length 2 (to hold key and value)
                 reportError("msg.bad.for.in.destruct");
@@ -1654,10 +1733,9 @@
         newBody.addChildToBack(new Node(Token.EXPR_VOID, assign));
         newBody.addChildToBack(body);
 
-        loop = createLoop((Jump)loop, LOOP_WHILE, newBody, cond, null, null);
+        loop = createLoop((Jump) loop, LOOP_WHILE, newBody, cond, null, null);
         loop.addChildToFront(init);
-        if (type == Token.VAR || type == Token.LET)
-            loop.addChildToFront(lhs);
+        if (type == Token.VAR || type == Token.LET) loop.addChildToFront(lhs);
         localBlock.addChildToBack(loop);
 
         return localBlock;
@@ -1666,41 +1744,37 @@
     /**
      * Try/Catch/Finally
      *
-     * The IRFactory tries to express as much as possible in the tree;
-     * the responsibilities remaining for Codegen are to add the Java
-     * handlers: (Either (but not both) of TARGET and FINALLY might not
-     * be defined)
+     * <p>The IRFactory tries to express as much as possible in the tree; the responsibilities
+     * remaining for Codegen are to add the Java handlers: (Either (but not both) of TARGET and
+     * FINALLY might not be defined)
      *
-     * - a catch handler for javascript exceptions that unwraps the
-     * exception onto the stack and GOTOes to the catch target
+     * <p>- a catch handler for javascript exceptions that unwraps the exception onto the stack and
+     * GOTOes to the catch target
      *
-     * - a finally handler
+     * <p>- a finally handler
      *
-     * ... and a goto to GOTO around these handlers.
+     * <p>... and a goto to GOTO around these handlers.
      */
-    private Node createTryCatchFinally(Node tryBlock, Node catchBlocks,
-                                       Node finallyBlock, int lineno)
-    {
-        boolean hasFinally = (finallyBlock != null)
-                             && (finallyBlock.getType() != Token.BLOCK
-                                 || finallyBlock.hasChildren());
+    private Node createTryCatchFinally(
+            Node tryBlock, Node catchBlocks, Node finallyBlock, int lineno) {
+        boolean hasFinally =
+                (finallyBlock != null)
+                        && (finallyBlock.getType() != Token.BLOCK || finallyBlock.hasChildren());
 
         // short circuit
-        if (tryBlock.getType() == Token.BLOCK && !tryBlock.hasChildren()
-            && !hasFinally)
-        {
+        if (tryBlock.getType() == Token.BLOCK && !tryBlock.hasChildren() && !hasFinally) {
             return tryBlock;
         }
 
         boolean hasCatch = catchBlocks.hasChildren();
 
         // short circuit
-        if (!hasFinally && !hasCatch)  {
+        if (!hasFinally && !hasCatch) {
             // bc finally might be an empty block...
             return tryBlock;
         }
 
-        Node handlerBlock  = new Node(Token.LOCAL_BLOCK);
+        Node handlerBlock = new Node(Token.LOCAL_BLOCK);
         Jump pn = new Jump(Token.TRY, tryBlock, lineno);
         pn.putProp(Node.LOCAL_BLOCK_PROP, handlerBlock);
 
@@ -1792,22 +1866,19 @@
                     condStmt = catchStatement;
                     hasDefault = true;
                 } else {
-                    condStmt = createIf(cond, catchStatement, null,
-                                        catchLineNo);
+                    condStmt = createIf(cond, catchStatement, null, catchLineNo);
                 }
 
                 // Generate code to create the scope object and store
                 // it in catchScopeBlock register
-                Node catchScope = new Node(Token.CATCH_SCOPE, name,
-                                           createUseLocal(handlerBlock));
+                Node catchScope = new Node(Token.CATCH_SCOPE, name, createUseLocal(handlerBlock));
                 catchScope.putProp(Node.LOCAL_BLOCK_PROP, catchScopeBlock);
                 catchScope.putIntProp(Node.CATCH_SCOPE_PROP, scopeIndex);
                 catchScopeBlock.addChildToBack(catchScope);
 
                 // Add with statement based on catch scope object
                 catchScopeBlock.addChildToBack(
-                    createWith(createUseLocal(catchScopeBlock), condStmt,
-                               catchLineNo));
+                        createWith(createUseLocal(catchScopeBlock), condStmt, catchLineNo));
 
                 // move to next cb
                 cb = cb.getNext();
@@ -1856,8 +1927,7 @@
         return result;
     }
 
-    private Node createIf(Node cond, Node ifTrue, Node ifFalse, int lineno)
-    {
+    private static Node createIf(Node cond, Node ifTrue, Node ifFalse, int lineno) {
         int condStatus = isAlwaysDefinedBoolean(cond);
         if (condStatus == ALWAYS_TRUE_BOOLEAN) {
             return ifTrue;
@@ -1890,7 +1960,7 @@
         return result;
     }
 
-    private Node createCondExpr(Node cond, Node ifTrue, Node ifFalse) {
+    private static Node createCondExpr(Node cond, Node ifTrue, Node ifFalse) {
         int condStatus = isAlwaysDefinedBoolean(cond);
         if (condStatus == ALWAYS_TRUE_BOOLEAN) {
             return ifTrue;
@@ -1900,73 +1970,72 @@
         return new Node(Token.HOOK, cond, ifTrue, ifFalse);
     }
 
-    private Node createUnary(int nodeType, Node child)
-    {
+    private static Node createUnary(int nodeType, Node child) {
         int childType = child.getType();
         switch (nodeType) {
-          case Token.DELPROP: {
-            Node n;
-            if (childType == Token.NAME) {
-                // Transform Delete(Name "a")
-                //  to Delete(Bind("a"), String("a"))
-                child.setType(Token.BINDNAME);
-                Node left = child;
-                Node right = Node.newString(child.getString());
-                n = new Node(nodeType, left, right);
-            } else if (childType == Token.GETPROP ||
-                       childType == Token.GETELEM)
-            {
-                Node left = child.getFirstChild();
-                Node right = child.getLastChild();
-                child.removeChild(left);
-                child.removeChild(right);
-                n = new Node(nodeType, left, right);
-            } else if (childType == Token.GET_REF) {
-                Node ref = child.getFirstChild();
-                child.removeChild(ref);
-                n = new Node(Token.DEL_REF, ref);
-            } else {
-                // Always evaluate delete operand, see ES5 11.4.1 & bug #726121
-                n = new Node(nodeType, new Node(Token.TRUE), child);
-            }
-            return n;
-          }
-          case Token.TYPEOF:
-            if (childType == Token.NAME) {
-                child.setType(Token.TYPEOFNAME);
-                return child;
-            }
-            break;
-          case Token.BITNOT:
-            if (childType == Token.NUMBER) {
-                int value = ScriptRuntime.toInt32(child.getDouble());
-                child.setDouble(~value);
-                return child;
-            }
-            break;
-          case Token.NEG:
-            if (childType == Token.NUMBER) {
-                child.setDouble(-child.getDouble());
-                return child;
-            }
-            break;
-          case Token.NOT: {
-            int status = isAlwaysDefinedBoolean(child);
-            if (status != 0) {
-                int type;
-                if (status == ALWAYS_TRUE_BOOLEAN) {
-                    type = Token.FALSE;
-                } else {
-                    type = Token.TRUE;
+            case Token.DELPROP:
+                {
+                    Node n;
+                    if (childType == Token.NAME) {
+                        // Transform Delete(Name "a")
+                        //  to Delete(Bind("a"), String("a"))
+                        child.setType(Token.BINDNAME);
+                        Node left = child;
+                        Node right = Node.newString(child.getString());
+                        n = new Node(nodeType, left, right);
+                    } else if (childType == Token.GETPROP || childType == Token.GETELEM) {
+                        Node left = child.getFirstChild();
+                        Node right = child.getLastChild();
+                        child.removeChild(left);
+                        child.removeChild(right);
+                        n = new Node(nodeType, left, right);
+                    } else if (childType == Token.GET_REF) {
+                        Node ref = child.getFirstChild();
+                        child.removeChild(ref);
+                        n = new Node(Token.DEL_REF, ref);
+                    } else {
+                        // Always evaluate delete operand, see ES5 11.4.1 & bug #726121
+                        n = new Node(nodeType, new Node(Token.TRUE), child);
+                    }
+                    return n;
                 }
-                if (childType == Token.TRUE || childType == Token.FALSE) {
-                    child.setType(type);
+            case Token.TYPEOF:
+                if (childType == Token.NAME) {
+                    child.setType(Token.TYPEOFNAME);
                     return child;
                 }
-                return new Node(type);
-            }
-            break;
-          }
+                break;
+            case Token.BITNOT:
+                if (childType == Token.NUMBER) {
+                    int value = ScriptRuntime.toInt32(child.getDouble());
+                    child.setDouble(~value);
+                    return child;
+                }
+                break;
+            case Token.NEG:
+                if (childType == Token.NUMBER) {
+                    child.setDouble(-child.getDouble());
+                    return child;
+                }
+                break;
+            case Token.NOT:
+                {
+                    int status = isAlwaysDefinedBoolean(child);
+                    if (status != 0) {
+                        int type;
+                        if (status == ALWAYS_TRUE_BOOLEAN) {
+                            type = Token.FALSE;
+                        } else {
+                            type = Token.TRUE;
+                        }
+                        if (childType == Token.TRUE || childType == Token.FALSE) {
+                            child.setType(type);
+                            return child;
+                        }
+                        return new Node(type);
+                    }
+                    break;
+                }
         }
         return new Node(nodeType, child);
     }
@@ -1995,34 +2064,33 @@
         return node;
     }
 
-    private Node createIncDec(int nodeType, boolean post, Node child)
-    {
+    private static Node createIncDec(int nodeType, boolean post, Node child) {
         child = makeReference(child);
         int childType = child.getType();
 
         switch (childType) {
-          case Token.NAME:
-          case Token.GETPROP:
-          case Token.GETELEM:
-          case Token.GET_REF: {
-            Node n = new Node(nodeType, child);
-            int incrDecrMask = 0;
-            if (nodeType == Token.DEC) {
-                incrDecrMask |= Node.DECR_FLAG;
-            }
-            if (post) {
-                incrDecrMask |= Node.POST_FLAG;
-            }
-            n.putIntProp(Node.INCRDECR_PROP, incrDecrMask);
-            return n;
-          }
+            case Token.NAME:
+            case Token.GETPROP:
+            case Token.GETELEM:
+            case Token.GET_REF:
+                {
+                    Node n = new Node(nodeType, child);
+                    int incrDecrMask = 0;
+                    if (nodeType == Token.DEC) {
+                        incrDecrMask |= Node.DECR_FLAG;
+                    }
+                    if (post) {
+                        incrDecrMask |= Node.POST_FLAG;
+                    }
+                    n.putIntProp(Node.INCRDECR_PROP, incrDecrMask);
+                    return n;
+                }
         }
         throw Kit.codeBug();
     }
 
-    private Node createPropertyGet(Node target, String namespace, String name,
-                                   int memberTypeFlags)
-    {
+    private Node createPropertyGet(
+            Node target, String namespace, String name, int memberTypeFlags) {
         if (namespace == null && memberTypeFlags == 0) {
             if (target == null) {
                 return createName(name);
@@ -2046,9 +2114,7 @@
      * @param elem the node in the brackets
      * @param memberTypeFlags E4X flags
      */
-    private Node createElementGet(Node target, String namespace, Node elem,
-                                  int memberTypeFlags)
-    {
+    private Node createElementGet(Node target, String namespace, Node elem, int memberTypeFlags) {
         // OPT: could optimize to createPropertyGet
         // iff elem is string that can not be number
         if (namespace == null && memberTypeFlags == 0) {
@@ -2060,9 +2126,7 @@
         return createMemberRefGet(target, namespace, elem, memberTypeFlags);
     }
 
-    private Node createMemberRefGet(Node target, String namespace, Node elem,
-                                    int memberTypeFlags)
-    {
+    private Node createMemberRefGet(Node target, String namespace, Node elem, int memberTypeFlags) {
         Node nsNode = null;
         if (namespace != null) {
             // See 11.1.2 in ECMA 357
@@ -2092,142 +2156,140 @@
         return new Node(Token.GET_REF, ref);
     }
 
-    private Node createBinary(int nodeType, Node left, Node right) {
+    private static Node createBinary(int nodeType, Node left, Node right) {
         switch (nodeType) {
+            case Token.ADD:
+                // numerical addition and string concatenation
+                if (left.type == Token.STRING) {
+                    String s2;
+                    if (right.type == Token.STRING) {
+                        s2 = right.getString();
+                    } else if (right.type == Token.NUMBER) {
+                        s2 = ScriptRuntime.numberToString(right.getDouble(), 10);
+                    } else {
+                        break;
+                    }
+                    String s1 = left.getString();
+                    left.setString(s1.concat(s2));
+                    return left;
+                } else if (left.type == Token.NUMBER) {
+                    if (right.type == Token.NUMBER) {
+                        left.setDouble(left.getDouble() + right.getDouble());
+                        return left;
+                    } else if (right.type == Token.STRING) {
+                        String s1, s2;
+                        s1 = ScriptRuntime.numberToString(left.getDouble(), 10);
+                        s2 = right.getString();
+                        right.setString(s1.concat(s2));
+                        return right;
+                    }
+                }
+                // can't do anything if we don't know  both types - since
+                // 0 + object is supposed to call toString on the object and do
+                // string concantenation rather than addition
+                break;
 
-          case Token.ADD:
-            // numerical addition and string concatenation
-            if (left.type == Token.STRING) {
-                String s2;
-                if (right.type == Token.STRING) {
-                    s2 = right.getString();
+            case Token.SUB:
+                // numerical subtraction
+                if (left.type == Token.NUMBER) {
+                    double ld = left.getDouble();
+                    if (right.type == Token.NUMBER) {
+                        // both numbers
+                        left.setDouble(ld - right.getDouble());
+                        return left;
+                    } else if (ld == 0.0) {
+                        // first 0: 0-x -> -x
+                        return new Node(Token.NEG, right);
+                    }
                 } else if (right.type == Token.NUMBER) {
-                    s2 = ScriptRuntime.numberToString(right.getDouble(), 10);
-                } else {
-                    break;
+                    if (right.getDouble() == 0.0) {
+                        // second 0: x - 0 -> +x
+                        // can not make simply x because x - 0 must be number
+                        return new Node(Token.POS, left);
+                    }
                 }
-                String s1 = left.getString();
-                left.setString(s1.concat(s2));
-                return left;
-            } else if (left.type == Token.NUMBER) {
-                if (right.type == Token.NUMBER) {
-                    left.setDouble(left.getDouble() + right.getDouble());
-                    return left;
-                } else if (right.type == Token.STRING) {
-                    String s1, s2;
-                    s1 = ScriptRuntime.numberToString(left.getDouble(), 10);
-                    s2 = right.getString();
-                    right.setString(s1.concat(s2));
-                    return right;
+                break;
+
+            case Token.MUL:
+                // numerical multiplication
+                if (left.type == Token.NUMBER) {
+                    double ld = left.getDouble();
+                    if (right.type == Token.NUMBER) {
+                        // both numbers
+                        left.setDouble(ld * right.getDouble());
+                        return left;
+                    } else if (ld == 1.0) {
+                        // first 1: 1 *  x -> +x
+                        return new Node(Token.POS, right);
+                    }
+                } else if (right.type == Token.NUMBER) {
+                    if (right.getDouble() == 1.0) {
+                        // second 1: x * 1 -> +x
+                        // can not make simply x because x - 0 must be number
+                        return new Node(Token.POS, left);
+                    }
                 }
-            }
-            // can't do anything if we don't know  both types - since
-            // 0 + object is supposed to call toString on the object and do
-            // string concantenation rather than addition
-            break;
-
-          case Token.SUB:
-            // numerical subtraction
-            if (left.type == Token.NUMBER) {
-                double ld = left.getDouble();
-                if (right.type == Token.NUMBER) {
-                    //both numbers
-                    left.setDouble(ld - right.getDouble());
-                    return left;
-                } else if (ld == 0.0) {
-                    // first 0: 0-x -> -x
-                    return new Node(Token.NEG, right);
-                }
-            } else if (right.type == Token.NUMBER) {
-                if (right.getDouble() == 0.0) {
-                    //second 0: x - 0 -> +x
-                    // can not make simply x because x - 0 must be number
-                    return new Node(Token.POS, left);
-                }
-            }
-            break;
-
-          case Token.MUL:
-            // numerical multiplication
-            if (left.type == Token.NUMBER) {
-                double ld = left.getDouble();
+                // can't do x*0: Infinity * 0 gives NaN, not 0
+                break;
+
+            case Token.DIV:
+                // number division
                 if (right.type == Token.NUMBER) {
-                    //both numbers
-                    left.setDouble(ld * right.getDouble());
-                    return left;
-                } else if (ld == 1.0) {
-                    // first 1: 1 *  x -> +x
-                    return new Node(Token.POS, right);
-                }
-            } else if (right.type == Token.NUMBER) {
-                if (right.getDouble() == 1.0) {
-                    //second 1: x * 1 -> +x
-                    // can not make simply x because x - 0 must be number
-                    return new Node(Token.POS, left);
-                }
-            }
-            // can't do x*0: Infinity * 0 gives NaN, not 0
-            break;
-
-          case Token.DIV:
-            // number division
-            if (right.type == Token.NUMBER) {
-                double rd = right.getDouble();
-                if (left.type == Token.NUMBER) {
-                    // both constants -- just divide, trust Java to handle x/0
-                    left.setDouble(left.getDouble() / rd);
-                    return left;
-               } else if (rd == 1.0) {
-                    // second 1: x/1 -> +x
-                    // not simply x to force number convertion
-                    return new Node(Token.POS, left);
-                }
-            }
-            break;
-
-          case Token.AND: {
-            // Since x && y gives x, not false, when Boolean(x) is false,
-            // and y, not Boolean(y), when Boolean(x) is true, x && y
-            // can only be simplified if x is defined. See bug 309957.
-
-            int leftStatus = isAlwaysDefinedBoolean(left);
-            if (leftStatus == ALWAYS_FALSE_BOOLEAN) {
-                // if the first one is false, just return it
-                return left;
-            } else if (leftStatus == ALWAYS_TRUE_BOOLEAN) {
-                // if first is true, set to second
-                return right;
-            }
-            break;
-          }
-
-          case Token.OR: {
-            // Since x || y gives x, not true, when Boolean(x) is true,
-            // and y, not Boolean(y), when Boolean(x) is false, x || y
-            // can only be simplified if x is defined. See bug 309957.
-
-            int leftStatus = isAlwaysDefinedBoolean(left);
-            if (leftStatus == ALWAYS_TRUE_BOOLEAN) {
-                // if the first one is true, just return it
-                return left;
-            } else if (leftStatus == ALWAYS_FALSE_BOOLEAN) {
-                // if first is false, set to second
-                return right;
-            }
-            break;
-          }
+                    double rd = right.getDouble();
+                    if (left.type == Token.NUMBER) {
+                        // both constants -- just divide, trust Java to handle x/0
+                        left.setDouble(left.getDouble() / rd);
+                        return left;
+                    } else if (rd == 1.0) {
+                        // second 1: x/1 -> +x
+                        // not simply x to force number convertion
+                        return new Node(Token.POS, left);
+                    }
+                }
+                break;
+
+            case Token.AND:
+                {
+                    // Since x && y gives x, not false, when Boolean(x) is false,
+                    // and y, not Boolean(y), when Boolean(x) is true, x && y
+                    // can only be simplified if x is defined. See bug 309957.
+
+                    int leftStatus = isAlwaysDefinedBoolean(left);
+                    if (leftStatus == ALWAYS_FALSE_BOOLEAN) {
+                        // if the first one is false, just return it
+                        return left;
+                    } else if (leftStatus == ALWAYS_TRUE_BOOLEAN) {
+                        // if first is true, set to second
+                        return right;
+                    }
+                    break;
+                }
+
+            case Token.OR:
+                {
+                    // Since x || y gives x, not true, when Boolean(x) is true,
+                    // and y, not Boolean(y), when Boolean(x) is false, x || y
+                    // can only be simplified if x is defined. See bug 309957.
+
+                    int leftStatus = isAlwaysDefinedBoolean(left);
+                    if (leftStatus == ALWAYS_TRUE_BOOLEAN) {
+                        // if the first one is true, just return it
+                        return left;
+                    } else if (leftStatus == ALWAYS_FALSE_BOOLEAN) {
+                        // if first is false, set to second
+                        return right;
+                    }
+                    break;
+                }
         }
 
         return new Node(nodeType, left, right);
     }
 
-    private Node createAssignment(int assignType, Node left, Node right)
-    {
+    private Node createAssignment(int assignType, Node left, Node right) {
         Node ref = makeReference(left);
         if (ref == null) {
-            if (left.getType() == Token.ARRAYLIT ||
-                left.getType() == Token.OBJECTLIT)
-            {
+            if (left.getType() == Token.ARRAYLIT || left.getType() == Token.OBJECTLIT) {
                 if (assignType != Token.ASSIGN) {
                     reportError("msg.bad.destruct.op");
                     return right;
@@ -2241,78 +2303,105 @@
 
         int assignOp;
         switch (assignType) {
-          case Token.ASSIGN:
-            return simpleAssignment(left, right);
-          case Token.ASSIGN_BITOR:  assignOp = Token.BITOR;  break;
-          case Token.ASSIGN_BITXOR: assignOp = Token.BITXOR; break;
-          case Token.ASSIGN_BITAND: assignOp = Token.BITAND; break;
-          case Token.ASSIGN_LSH:    assignOp = Token.LSH;    break;
-          case Token.ASSIGN_RSH:    assignOp = Token.RSH;    break;
-          case Token.ASSIGN_URSH:   assignOp = Token.URSH;   break;
-          case Token.ASSIGN_ADD:    assignOp = Token.ADD;    break;
-          case Token.ASSIGN_SUB:    assignOp = Token.SUB;    break;
-          case Token.ASSIGN_MUL:    assignOp = Token.MUL;    break;
-          case Token.ASSIGN_DIV:    assignOp = Token.DIV;    break;
-          case Token.ASSIGN_MOD:    assignOp = Token.MOD;    break;
-          default: throw Kit.codeBug();
+            case Token.ASSIGN:
+                return simpleAssignment(left, right);
+            case Token.ASSIGN_BITOR:
+                assignOp = Token.BITOR;
+                break;
+            case Token.ASSIGN_BITXOR:
+                assignOp = Token.BITXOR;
+                break;
+            case Token.ASSIGN_BITAND:
+                assignOp = Token.BITAND;
+                break;
+            case Token.ASSIGN_LSH:
+                assignOp = Token.LSH;
+                break;
+            case Token.ASSIGN_RSH:
+                assignOp = Token.RSH;
+                break;
+            case Token.ASSIGN_URSH:
+                assignOp = Token.URSH;
+                break;
+            case Token.ASSIGN_ADD:
+                assignOp = Token.ADD;
+                break;
+            case Token.ASSIGN_SUB:
+                assignOp = Token.SUB;
+                break;
+            case Token.ASSIGN_MUL:
+                assignOp = Token.MUL;
+                break;
+            case Token.ASSIGN_DIV:
+                assignOp = Token.DIV;
+                break;
+            case Token.ASSIGN_MOD:
+                assignOp = Token.MOD;
+                break;
+            case Token.ASSIGN_EXP:
+                assignOp = Token.EXP;
+                break;
+            default:
+                throw Kit.codeBug();
         }
 
         int nodeType = left.getType();
         switch (nodeType) {
-          case Token.NAME: {
-            Node op = new Node(assignOp, left, right);
-            Node lvalueLeft = Node.newString(Token.BINDNAME, left.getString());
-            return new Node(Token.SETNAME, lvalueLeft, op);
-          }
-          case Token.GETPROP:
-          case Token.GETELEM: {
-            Node obj = left.getFirstChild();
-            Node id = left.getLastChild();
-
-            int type = nodeType == Token.GETPROP
-                       ? Token.SETPROP_OP
-                       : Token.SETELEM_OP;
-
-            Node opLeft = new Node(Token.USE_STACK);
-            Node op = new Node(assignOp, opLeft, right);
-            return new Node(type, obj, id, op);
-          }
-          case Token.GET_REF: {
-            ref = left.getFirstChild();
-            checkMutableReference(ref);
-            Node opLeft = new Node(Token.USE_STACK);
-            Node op = new Node(assignOp, opLeft, right);
-            return new Node(Token.SET_REF_OP, ref, op);
-          }
+            case Token.NAME:
+                {
+                    Node op = new Node(assignOp, left, right);
+                    Node lvalueLeft = Node.newString(Token.BINDNAME, left.getString());
+                    return new Node(Token.SETNAME, lvalueLeft, op);
+                }
+            case Token.GETPROP:
+            case Token.GETELEM:
+                {
+                    Node obj = left.getFirstChild();
+                    Node id = left.getLastChild();
+
+                    int type = nodeType == Token.GETPROP ? Token.SETPROP_OP : Token.SETELEM_OP;
+
+                    Node opLeft = new Node(Token.USE_STACK);
+                    Node op = new Node(assignOp, opLeft, right);
+                    return new Node(type, obj, id, op);
+                }
+            case Token.GET_REF:
+                {
+                    ref = left.getFirstChild();
+                    checkMutableReference(ref);
+                    Node opLeft = new Node(Token.USE_STACK);
+                    Node op = new Node(assignOp, opLeft, right);
+                    return new Node(Token.SET_REF_OP, ref, op);
+                }
         }
 
         throw Kit.codeBug();
     }
 
-    private Node createUseLocal(Node localBlock) {
+    private static Node createUseLocal(Node localBlock) {
         if (Token.LOCAL_BLOCK != localBlock.getType()) throw Kit.codeBug();
         Node result = new Node(Token.LOCAL_LOAD);
         result.putProp(Node.LOCAL_BLOCK_PROP, localBlock);
         return result;
     }
 
-    private Jump makeJump(int type, Node target) {
+    private static Jump makeJump(int type, Node target) {
         Jump n = new Jump(type);
         n.target = target;
         return n;
     }
 
-    private Node makeReference(Node node) {
+    private static Node makeReference(Node node) {
         int type = node.getType();
         switch (type) {
-          case Token.NAME:
-          case Token.GETPROP:
-          case Token.GETELEM:
-          case Token.GET_REF:
-            return node;
-          case Token.CALL:
-            node.setType(Token.REF_CALL);
-            return new Node(Token.GET_REF, node);
+            case Token.NAME:
+            case Token.GETPROP:
+            case Token.GETELEM:
+            case Token.GET_REF:
+                return node;
+            case Token.CALL:
+                node.setType(Token.REF_CALL);
+                return new Node(Token.GET_REF, node);
         }
         // Signal caller to report error
         return null;
@@ -2321,27 +2410,26 @@
     // Check if Node always mean true or false in boolean context
     private static int isAlwaysDefinedBoolean(Node node) {
         switch (node.getType()) {
-          case Token.FALSE:
-          case Token.NULL:
-            return ALWAYS_FALSE_BOOLEAN;
-          case Token.TRUE:
-            return ALWAYS_TRUE_BOOLEAN;
-          case Token.NUMBER: {
-            double num = node.getDouble();
-            if (num == num && num != 0.0) {
-                return ALWAYS_TRUE_BOOLEAN;
-            } else {
+            case Token.FALSE:
+            case Token.NULL:
                 return ALWAYS_FALSE_BOOLEAN;
-            }
-          }
+            case Token.TRUE:
+                return ALWAYS_TRUE_BOOLEAN;
+            case Token.NUMBER:
+                {
+                    double num = node.getDouble();
+                    if (!Double.isNaN(num) && num != 0.0) {
+                        return ALWAYS_TRUE_BOOLEAN;
+                    }
+                    return ALWAYS_FALSE_BOOLEAN;
+                }
         }
         return 0;
     }
 
     // Check if node is the target of a destructuring bind.
     boolean isDestructuring(Node n) {
-        return n instanceof DestructuringForm
-            && ((DestructuringForm)n).isDestructuring();
+        return n instanceof DestructuringForm && ((DestructuringForm) n).isDestructuring();
     }
 
     Node decompileFunctionHeader(FunctionNode fn) {
@@ -2377,35 +2465,37 @@
 
     void decompile(AstNode node) {
         switch (node.getType()) {
-          case Token.ARRAYLIT:
-              decompileArrayLiteral((ArrayLiteral)node);
-              break;
-          case Token.OBJECTLIT:
-              decompileObjectLiteral((ObjectLiteral)node);
-              break;
-          case Token.STRING:
-              decompiler.addString(((StringLiteral)node).getValue());
-              break;
-          case Token.NAME:
-              decompiler.addName(((Name)node).getIdentifier());
-              break;
-          case Token.NUMBER:
-              decompiler.addNumber(((NumberLiteral)node).getNumber());
-              break;
-          case Token.GETPROP:
-              decompilePropertyGet((PropertyGet)node);
-              break;
-          case Token.EMPTY:
-              break;
-          case Token.GETELEM:
-              decompileElementGet((ElementGet) node);
-              break;
-          case Token.THIS:
-              decompiler.addToken(node.getType());
-              break;
-          default:
-              Kit.codeBug("unexpected token: "
-                          + Token.typeToName(node.getType()));
+            case Token.ARRAYLIT:
+                decompileArrayLiteral((ArrayLiteral) node);
+                break;
+            case Token.OBJECTLIT:
+                decompileObjectLiteral((ObjectLiteral) node);
+                break;
+            case Token.STRING:
+                decompiler.addString(((StringLiteral) node).getValue());
+                break;
+            case Token.NAME:
+                decompiler.addName(((Name) node).getIdentifier());
+                break;
+            case Token.NUMBER:
+                decompiler.addNumber(((NumberLiteral) node).getNumber());
+                break;
+            case Token.BIGINT:
+                decompiler.addBigInt(((BigIntLiteral) node).getBigInt());
+                break;
+            case Token.GETPROP:
+                decompilePropertyGet((PropertyGet) node);
+                break;
+            case Token.EMPTY:
+                break;
+            case Token.GETELEM:
+                decompileElementGet((ElementGet) node);
+                break;
+            case Token.THIS:
+                decompiler.addToken(node.getType());
+                break;
+            default:
+                Kit.codeBug("unexpected token: " + Token.typeToName(node.getType()));
         }
     }
 
@@ -2431,10 +2521,10 @@
         int size = props.size();
         for (int i = 0; i < size; i++) {
             ObjectProperty prop = props.get(i);
-            boolean destructuringShorthand =
-                    Boolean.TRUE.equals(prop.getProp(Node.DESTRUCTURING_SHORTHAND));
+            boolean shorthandPropertyName =
+                    Boolean.TRUE.equals(prop.getProp(Node.SHORTHAND_PROPERTY_NAME));
             decompile(prop.getLeft());
-            if (!destructuringShorthand) {
+            if (!shorthandPropertyName) {
                 decompiler.addToken(Token.COLON);
                 decompile(prop.getRight());
             }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/IteratorLikeIterable.java rhino-1.7.14/src/org/mozilla/javascript/IteratorLikeIterable.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/IteratorLikeIterable.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/IteratorLikeIterable.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,109 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+import java.io.Closeable;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * This is a class that makes it easier to iterate over "iterator-like" objects as defined in the
+ * ECMAScript spec. The caller is responsible for retrieving an object that implements the
+ * "iterator" pattern. This class will follow that pattern and throw appropriate JavaScript
+ * exceptions.
+ *
+ * <p>The pattern that the target class should follow is: * It must have a function property called
+ * "next" * The function must return an object with a boolean value called "done". * If "done" is
+ * true, then the returned object should also contain a "value" property. * If it has a function
+ * property called "return" then it will be called when the caller is done iterating.
+ */
+public class IteratorLikeIterable implements Iterable<Object>, Closeable {
+    private final Context cx;
+    private final Scriptable scope;
+    private final Callable next;
+    private final Callable returnFunc;
+    private final Scriptable iterator;
+    private boolean closed;
+
+    public IteratorLikeIterable(Context cx, Scriptable scope, Object target) {
+        this.cx = cx;
+        this.scope = scope;
+        // This will throw if "next" is not a function or undefined
+        next = ScriptRuntime.getPropFunctionAndThis(target, ES6Iterator.NEXT_METHOD, cx, scope);
+        iterator = ScriptRuntime.lastStoredScriptable(cx);
+        Object retObj =
+                ScriptRuntime.getObjectPropNoWarn(target, ES6Iterator.RETURN_PROPERTY, cx, scope);
+        // We only care about "return" if it is not null or undefined
+        if ((retObj != null) && !Undefined.isUndefined(retObj)) {
+            if (!(retObj instanceof Callable)) {
+                throw ScriptRuntime.notFunctionError(target, retObj, ES6Iterator.RETURN_PROPERTY);
+            }
+            returnFunc = (Callable) retObj;
+        } else {
+            returnFunc = null;
+        }
+    }
+
+    @Override
+    public void close() {
+        if (!closed) {
+            closed = true;
+            if (returnFunc != null) {
+                returnFunc.call(cx, scope, iterator, ScriptRuntime.emptyArgs);
+            }
+        }
+    }
+
+    @Override
+    public Itr iterator() {
+        return new Itr();
+    }
+
+    public final class Itr implements Iterator<Object> {
+        private Object nextVal;
+        private boolean isDone;
+
+        @Override
+        public boolean hasNext() {
+            if (isDone) {
+                return false;
+            }
+            Object val = next.call(cx, scope, iterator, ScriptRuntime.emptyArgs);
+            // This will throw if "val" is not an object.
+            // "getObjectPropNoWarn" won't, so do this as follows.
+            Object doneval =
+                    ScriptableObject.getProperty(
+                            ScriptableObject.ensureScriptable(val), ES6Iterator.DONE_PROPERTY);
+            if (doneval == Scriptable.NOT_FOUND) {
+                doneval = Undefined.instance;
+            }
+            // It's OK if done is undefined.
+            if (ScriptRuntime.toBoolean(doneval)) {
+                isDone = true;
+                return false;
+            }
+            nextVal = ScriptRuntime.getObjectPropNoWarn(val, ES6Iterator.VALUE_PROPERTY, cx, scope);
+            return true;
+        }
+
+        @Override
+        public Object next() {
+            if (isDone) {
+                throw new NoSuchElementException();
+            }
+            return nextVal;
+        }
+
+        /** Find out if "hasNext" returned done without invoking the function again. */
+        public boolean isDone() {
+            return isDone;
+        }
+
+        /** Manually set "done." Used for exception handling in promises. */
+        public void setDone(boolean done) {
+            this.isDone = done;
+        }
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/JavaAdapter.java rhino-1.7.14/src/org/mozilla/javascript/JavaAdapter.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/JavaAdapter.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/JavaAdapter.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,11 +6,23 @@
 
 package org.mozilla.javascript;
 
-import org.mozilla.classfile.*;
-import java.lang.reflect.*;
-import java.io.*;
-import java.security.*;
-import java.util.*;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.security.CodeSource;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Map;
+
+import org.mozilla.classfile.ByteCode;
+import org.mozilla.classfile.ClassFileWriter;
 
 public final class JavaAdapter implements IdFunctionCall
 {
@@ -78,6 +90,7 @@
         ctor.exportAsScopeProperty();
     }
 
+    @Override
     public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
                              Scriptable thisObj, Object[] args)
     {
@@ -120,7 +133,7 @@
     {
         int N = args.length;
         if (N == 0) {
-            throw ScriptRuntime.typeError0("msg.adapter.zero.args");
+            throw ScriptRuntime.typeErrorById("msg.adapter.zero.args");
         }
 
         // Expected arguments:
@@ -140,7 +153,7 @@
                 break;
             }
             if (!(arg instanceof NativeJavaClass)) {
-                throw ScriptRuntime.typeError2("msg.not.java.class.arg",
+                throw ScriptRuntime.typeErrorById("msg.not.java.class.arg",
                                                String.valueOf(classCount),
                                                ScriptRuntime.toString(arg));
             }
@@ -152,7 +165,7 @@
             Class<?> c = ((NativeJavaClass) args[i]).getClassObject();
             if (!c.isInterface()) {
                 if (superClass != null) {
-                    throw ScriptRuntime.typeError2("msg.only.one.super",
+                    throw ScriptRuntime.typeErrorById("msg.only.one.super",
                               superClass.getName(), c.getName());
                 }
                 superClass = c;
@@ -190,7 +203,7 @@
                 int index = ctors.findCachedFunction(cx, ctorArgs);
                 if (index < 0) {
                     String sig = NativeJavaMethod.scriptSignature(args);
-                    throw Context.reportRuntimeError2(
+                    throw Context.reportRuntimeErrorById(
                             "msg.no.java.ctor", adapterClass.getName(), sig);
                 }
 
@@ -384,7 +397,7 @@
             for (int j = 0; j < methods.length; j++) {
                 Method method = methods[j];
                 int mods = method.getModifiers();
-                if (Modifier.isStatic(mods) || Modifier.isFinal(mods)) {
+                if (Modifier.isStatic(mods) || Modifier.isFinal(mods) || method.isDefault()) {
                     continue;
                 }
                 String methodName = method.getName();
@@ -487,7 +500,8 @@
     private static void appendOverridableMethods(Class<?> c,
             ArrayList<Method> list, HashSet<String> skip)
     {
-        Method[] methods = c.getDeclaredMethods();
+        Method[] methods=c.isInterface() ? c.getMethods() : c.getDeclaredMethods();
+        
         for (int i = 0; i < methods.length; i++) {
             String methodKey = methods[i].getName() +
                 getMethodSignature(methods[i],
@@ -579,14 +593,8 @@
         Context cx = Context.getCurrentContext();
         if (cx != null) {
             return doCall(cx, scope, thisObj, f, args, argsToWrap);
-        } else {
-            return factory.call(new ContextAction() {
-                public Object run(Context cx)
-                {
-                    return doCall(cx, scope, thisObj, f, args, argsToWrap);
-                }
-            });
         }
+        return factory.call(cx2 -> doCall(cx2, scope, thisObj, f, args, argsToWrap));
     }
 
     private static Object doCall(Context cx, Scriptable scope,
@@ -608,15 +616,11 @@
 
     public static Scriptable runScript(final Script script)
     {
-        return (Scriptable)ContextFactory.getGlobal().call(
-            new ContextAction() {
-                public Object run(Context cx)
-                {
-                    ScriptableObject global = ScriptRuntime.getGlobal(cx);
-                    script.exec(cx, global);
-                    return global;
-                }
-            });
+        return ContextFactory.getGlobal().call(cx -> {
+            ScriptableObject global = ScriptRuntime.getGlobal(cx);
+            script.exec(cx, global);
+            return global;
+        });
     }
 
     private static void generateCtor(ClassFileWriter cfw, String adapterName,
@@ -915,8 +919,7 @@
                 cfw.add(ByteCode.DRETURN);
                 break;
             default:
-                throw new RuntimeException("Unexpected return type " +
-                                           retType.toString());
+                throw new RuntimeException("Unexpected return type " + retType);
             }
 
         } else {
@@ -982,7 +985,7 @@
         if (parms.length > 64) {
             // If it will be an issue, then passing a static boolean array
             // can be an option, but for now using simple bitmask
-            throw Context.reportRuntimeError0(
+            throw Context.reportRuntimeErrorById(
                 "JavaAdapter can not subclass methods with more then"
                 +" 64 arguments.");
         }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/JavaMembers.java rhino-1.7.14/src/org/mozilla/javascript/JavaMembers.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/JavaMembers.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/JavaMembers.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,49 +6,61 @@
 
 package org.mozilla.javascript;
 
-import java.lang.reflect.*;
-import java.util.*;
-
 import static java.lang.reflect.Modifier.isProtected;
 import static java.lang.reflect.Modifier.isPublic;
 
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.security.AccessControlContext;
+import java.security.AllPermission;
+import java.security.Permission;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.lang.model.SourceVersion;
+
 /**
- *
  * @author Mike Shaver
  * @author Norris Boyd
  * @see NativeJavaObject
  * @see NativeJavaClass
  */
-class JavaMembers
-{
-    JavaMembers(Scriptable scope, Class<?> cl)
-    {
+class JavaMembers {
+
+    private static final boolean STRICT_REFLECTIVE_ACCESS =
+            SourceVersion.latestSupported().ordinal() > 8;
+
+    private static final Permission allPermission = new AllPermission();
+
+    JavaMembers(Scriptable scope, Class<?> cl) {
         this(scope, cl, false);
     }
 
-    JavaMembers(Scriptable scope, Class<?> cl, boolean includeProtected)
-    {
+    JavaMembers(Scriptable scope, Class<?> cl, boolean includeProtected) {
         try {
             Context cx = ContextFactory.getGlobal().enterContext();
             ClassShutter shutter = cx.getClassShutter();
             if (shutter != null && !shutter.visibleToScripts(cl.getName())) {
-                throw Context.reportRuntimeError1("msg.access.prohibited",
-                                                  cl.getName());
+                throw Context.reportRuntimeErrorById("msg.access.prohibited", cl.getName());
             }
-            this.members = new HashMap<String,Object>();
-            this.staticMembers = new HashMap<String,Object>();
+            this.members = new HashMap<String, Object>();
+            this.staticMembers = new HashMap<String, Object>();
             this.cl = cl;
-            boolean includePrivate = cx.hasFeature(
-                    Context.FEATURE_ENHANCED_JAVA_ACCESS);
+            boolean includePrivate = cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS);
             reflect(scope, includeProtected, includePrivate);
         } finally {
             Context.exit();
         }
     }
 
-    boolean has(String name, boolean isStatic)
-    {
-        Map<String,Object> ht = isStatic ? staticMembers : members;
+    boolean has(String name, boolean isStatic) {
+        Map<String, Object> ht = isStatic ? staticMembers : members;
         Object obj = ht.get(name);
         if (obj != null) {
             return true;
@@ -56,20 +68,19 @@
         return findExplicitFunction(name, isStatic) != null;
     }
 
-    Object get(Scriptable scope, String name, Object javaObject,
-               boolean isStatic)
-    {
-        Map<String,Object> ht = isStatic ? staticMembers : members;
+    Object get(Scriptable scope, String name, Object javaObject, boolean isStatic) {
+        Map<String, Object> ht = isStatic ? staticMembers : members;
         Object member = ht.get(name);
         if (!isStatic && member == null) {
             // Try to get static member from instance (LC3)
             member = staticMembers.get(name);
         }
         if (member == null) {
-            member = this.getExplicitFunction(scope, name,
-                                              javaObject, isStatic);
-            if (member == null)
-                return Scriptable.NOT_FOUND;
+            member =
+                    this.getExplicitFunction(
+                            scope, name,
+                            javaObject, isStatic);
+            if (member == null) return Scriptable.NOT_FOUND;
         }
         if (member instanceof Scriptable) {
             return member;
@@ -80,8 +91,7 @@
         try {
             if (member instanceof BeanProperty) {
                 BeanProperty bp = (BeanProperty) member;
-                if (bp.getter == null)
-                    return Scriptable.NOT_FOUND;
+                if (bp.getter == null) return Scriptable.NOT_FOUND;
                 rval = bp.getter.invoke(javaObject, Context.emptyArgs);
                 type = bp.getter.method().getReturnType();
             } else {
@@ -97,17 +107,14 @@
         return cx.getWrapFactory().wrap(cx, scope, rval, type);
     }
 
-    void put(Scriptable scope, String name, Object javaObject,
-             Object value, boolean isStatic)
-    {
-        Map<String,Object> ht = isStatic ? staticMembers : members;
+    void put(Scriptable scope, String name, Object javaObject, Object value, boolean isStatic) {
+        Map<String, Object> ht = isStatic ? staticMembers : members;
         Object member = ht.get(name);
         if (!isStatic && member == null) {
             // Try to get static member from instance (LC3)
             member = staticMembers.get(name);
         }
-        if (member == null)
-            throw reportMemberNotFound(name);
+        if (member == null) throw reportMemberNotFound(name);
         if (member instanceof FieldAndMethods) {
             FieldAndMethods fam = (FieldAndMethods) ht.get(name);
             member = fam.field;
@@ -115,7 +122,7 @@
 
         // Is this a bean property "set"?
         if (member instanceof BeanProperty) {
-            BeanProperty bp = (BeanProperty)member;
+            BeanProperty bp = (BeanProperty) member;
             if (bp.setter == null) {
                 throw reportMemberNotFound(name);
             }
@@ -124,26 +131,27 @@
             // setter to use:
             if (bp.setters == null || value == null) {
                 Class<?> setType = bp.setter.argTypes[0];
-                Object[] args = { Context.jsToJava(value, setType) };
+                Object[] args = {Context.jsToJava(value, setType)};
                 try {
                     bp.setter.invoke(javaObject, args);
                 } catch (Exception ex) {
-                  throw Context.throwAsScriptRuntimeEx(ex);
+                    throw Context.throwAsScriptRuntimeEx(ex);
                 }
             } else {
-                Object[] args = { value };
-                bp.setters.call(Context.getContext(),
-                                ScriptableObject.getTopLevelScope(scope),
-                                scope, args);
+                Object[] args = {value};
+                bp.setters.call(
+                        Context.getContext(),
+                        ScriptableObject.getTopLevelScope(scope),
+                        scope,
+                        args);
             }
-        }
-        else {
+        } else {
             if (!(member instanceof Field)) {
-                String str = (member == null) ? "msg.java.internal.private"
-                                              : "msg.java.method.assign";
-                throw Context.reportRuntimeError1(str, name);
+                String str =
+                        (member == null) ? "msg.java.internal.private" : "msg.java.method.assign";
+                throw Context.reportRuntimeErrorById(str, name);
             }
-            Field field = (Field)member;
+            Field field = (Field) member;
             Object javaValue = Context.jsToJava(value, field.getType());
             try {
                 field.set(javaObject, javaValue);
@@ -154,51 +162,49 @@
                 }
                 throw Context.throwAsScriptRuntimeEx(accessEx);
             } catch (IllegalArgumentException argEx) {
-                throw Context.reportRuntimeError3(
-                    "msg.java.internal.field.type",
-                    value.getClass().getName(), field,
-                    javaObject.getClass().getName());
+                throw Context.reportRuntimeErrorById(
+                        "msg.java.internal.field.type",
+                        value.getClass().getName(),
+                        field,
+                        javaObject.getClass().getName());
             }
         }
     }
 
-    Object[] getIds(boolean isStatic)
-    {
-        Map<String,Object> map = isStatic ? staticMembers : members;
+    Object[] getIds(boolean isStatic) {
+        Map<String, Object> map = isStatic ? staticMembers : members;
         return map.keySet().toArray(new Object[map.size()]);
     }
 
-    static String javaSignature(Class<?> type)
-    {
+    static String javaSignature(Class<?> type) {
         if (!type.isArray()) {
             return type.getName();
-        } else {
-            int arrayDimension = 0;
-            do {
-                ++arrayDimension;
-                type = type.getComponentType();
-            } while (type.isArray());
-            String name = type.getName();
-            String suffix = "[]";
-            if (arrayDimension == 1) {
-                return name.concat(suffix);
-            } else {
-                int length = name.length() + arrayDimension * suffix.length();
-                StringBuilder sb = new StringBuilder(length);
-                sb.append(name);
-                while (arrayDimension != 0) {
-                    --arrayDimension;
-                    sb.append(suffix);
-                }
-                return sb.toString();
-            }
         }
+        int arrayDimension = 0;
+        do {
+            ++arrayDimension;
+            type = type.getComponentType();
+        } while (type.isArray());
+        String name = type.getName();
+        String suffix = "[]";
+        if (arrayDimension == 1) {
+            return name.concat(suffix);
+        }
+        int length = name.length() + arrayDimension * suffix.length();
+        StringBuilder sb = new StringBuilder(length);
+        sb.append(name);
+        while (arrayDimension != 0) {
+            --arrayDimension;
+            sb.append(suffix);
+        }
+        return sb.toString();
     }
 
-    static String liveConnectSignature(Class<?>[] argTypes)
-    {
+    static String liveConnectSignature(Class<?>[] argTypes) {
         int N = argTypes.length;
-        if (N == 0) { return "()"; }
+        if (N == 0) {
+            return "()";
+        }
         StringBuilder sb = new StringBuilder();
         sb.append('(');
         for (int i = 0; i != N; ++i) {
@@ -211,12 +217,13 @@
         return sb.toString();
     }
 
-    private MemberBox findExplicitFunction(String name, boolean isStatic)
-    {
+    private MemberBox findExplicitFunction(String name, boolean isStatic) {
         int sigStart = name.indexOf('(');
-        if (sigStart < 0) { return null; }
+        if (sigStart < 0) {
+            return null;
+        }
 
-        Map<String,Object> ht = isStatic ? staticMembers : members;
+        Map<String, Object> ht = isStatic ? staticMembers : members;
         MemberBox[] methodsOrCtors = null;
         boolean isCtor = (isStatic && sigStart == 0);
 
@@ -225,14 +232,14 @@
             methodsOrCtors = ctors.methods;
         } else {
             // Explicit request for an overloaded method
-            String trueName = name.substring(0,sigStart);
+            String trueName = name.substring(0, sigStart);
             Object obj = ht.get(trueName);
             if (!isStatic && obj == null) {
                 // Try to get static member from instance (LC3)
                 obj = staticMembers.get(trueName);
             }
             if (obj instanceof NativeJavaMethod) {
-                NativeJavaMethod njm = (NativeJavaMethod)obj;
+                NativeJavaMethod njm = (NativeJavaMethod) obj;
                 methodsOrCtors = njm.methods;
             }
         }
@@ -242,8 +249,7 @@
                 Class<?>[] type = methodsOrCtor.argTypes;
                 String sig = liveConnectSignature(type);
                 if (sigStart + sig.length() == name.length()
-                        && name.regionMatches(sigStart, sig, 0, sig.length()))
-                {
+                        && name.regionMatches(sigStart, sig, 0, sig.length())) {
                     return methodsOrCtor;
                 }
             }
@@ -252,20 +258,17 @@
         return null;
     }
 
-    private Object getExplicitFunction(Scriptable scope, String name,
-                                       Object javaObject, boolean isStatic)
-    {
-        Map<String,Object> ht = isStatic ? staticMembers : members;
+    private Object getExplicitFunction(
+            Scriptable scope, String name, Object javaObject, boolean isStatic) {
+        Map<String, Object> ht = isStatic ? staticMembers : members;
         Object member = null;
         MemberBox methodOrCtor = findExplicitFunction(name, isStatic);
 
         if (methodOrCtor != null) {
-            Scriptable prototype =
-                ScriptableObject.getFunctionPrototype(scope);
+            Scriptable prototype = ScriptableObject.getFunctionPrototype(scope);
 
             if (methodOrCtor.isCtor()) {
-                NativeJavaConstructor fun =
-                    new NativeJavaConstructor(methodOrCtor);
+                NativeJavaConstructor fun = new NativeJavaConstructor(methodOrCtor);
                 fun.setPrototype(prototype);
                 member = fun;
                 ht.put(name, fun);
@@ -273,10 +276,9 @@
                 String trueName = methodOrCtor.getName();
                 member = ht.get(trueName);
 
-                if (member instanceof NativeJavaMethod &&
-                    ((NativeJavaMethod)member).methods.length > 1 ) {
-                    NativeJavaMethod fun =
-                        new NativeJavaMethod(methodOrCtor, name);
+                if (member instanceof NativeJavaMethod
+                        && ((NativeJavaMethod) member).methods.length > 1) {
+                    NativeJavaMethod fun = new NativeJavaMethod(methodOrCtor, name);
                     fun.setPrototype(prototype);
                     ht.put(name, fun);
                     member = fun;
@@ -288,25 +290,23 @@
     }
 
     /**
-     * Retrieves mapping of methods to accessible methods for a class.
-     * In case the class is not public, retrieves methods with same
-     * signature as its public methods from public superclasses and
-     * interfaces (if they exist). Basically upcasts every method to the
-     * nearest accessible method.
+     * Retrieves mapping of methods to accessible methods for a class. In case the class is not
+     * public, retrieves methods with same signature as its public methods from public superclasses
+     * and interfaces (if they exist). Basically upcasts every method to the nearest accessible
+     * method.
      */
-    private static Method[] discoverAccessibleMethods(Class<?> clazz,
-                                                      boolean includeProtected,
-                                                      boolean includePrivate)
-    {
-        Map<MethodSignature,Method> map = new HashMap<MethodSignature,Method>();
+    private Method[] discoverAccessibleMethods(
+            Class<?> clazz, boolean includeProtected, boolean includePrivate) {
+        Map<MethodSignature, Method> map = new HashMap<MethodSignature, Method>();
         discoverAccessibleMethods(clazz, map, includeProtected, includePrivate);
         return map.values().toArray(new Method[map.size()]);
     }
 
-    private static void discoverAccessibleMethods(Class<?> clazz,
-            Map<MethodSignature,Method> map, boolean includeProtected,
-            boolean includePrivate)
-    {
+    private void discoverAccessibleMethods(
+            Class<?> clazz,
+            Map<MethodSignature, Method> map,
+            boolean includeProtected,
+            boolean includePrivate) {
         if (isPublic(clazz.getModifiers()) || includePrivate) {
             try {
                 if (includeProtected || includePrivate) {
@@ -316,9 +316,7 @@
                             for (Method method : methods) {
                                 int mods = method.getModifiers();
 
-                                if (isPublic(mods)
-                                        || isProtected(mods)
-                                        || includePrivate) {
+                                if (isPublic(mods) || isProtected(mods) || includePrivate) {
                                     MethodSignature sig = new MethodSignature(method);
                                     if (!map.containsKey(sig)) {
                                         if (includePrivate && !method.isAccessible())
@@ -327,6 +325,11 @@
                                     }
                                 }
                             }
+                            Class<?>[] interfaces = clazz.getInterfaces();
+                            for (Class<?> intface : interfaces) {
+                                discoverAccessibleMethods(
+                                        intface, map, includeProtected, includePrivate);
+                            }
                             clazz = clazz.getSuperclass();
                         } catch (SecurityException e) {
                             // Some security settings (i.e., applets) disallow
@@ -335,28 +338,22 @@
                             Method[] methods = clazz.getMethods();
                             for (Method method : methods) {
                                 MethodSignature sig = new MethodSignature(method);
-                                if (!map.containsKey(sig))
-                                    map.put(sig, method);
+                                if (!map.containsKey(sig)) map.put(sig, method);
                             }
                             break; // getMethods gets superclass methods, no
-                                   // need to loop any more
+                            // need to loop any more
                         }
                     }
                 } else {
-                    Method[] methods = clazz.getMethods();
-                    for (Method method : methods) {
-                        MethodSignature sig = new MethodSignature(method);
-                        // Array may contain methods with same signature but different return value!
-                        if (!map.containsKey(sig))
-                            map.put(sig, method);
-                    }
+                    discoverPublicMethods(clazz, map);
                 }
                 return;
             } catch (SecurityException e) {
                 Context.reportWarning(
-                        "Could not discover accessible methods of class " +
-                            clazz.getName() + " due to lack of privileges, " +
-                            "attemping superclasses/interfaces.");
+                        "Could not discover accessible methods of class "
+                                + clazz.getName()
+                                + " due to lack of privileges, "
+                                + "attemping superclasses/interfaces.");
                 // Fall through and attempt to discover superclass/interface
                 // methods
             }
@@ -364,64 +361,65 @@
 
         Class<?>[] interfaces = clazz.getInterfaces();
         for (Class<?> intface : interfaces) {
-            discoverAccessibleMethods(intface, map, includeProtected,
-                    includePrivate);
+            discoverAccessibleMethods(intface, map, includeProtected, includePrivate);
         }
         Class<?> superclass = clazz.getSuperclass();
         if (superclass != null) {
-            discoverAccessibleMethods(superclass, map, includeProtected,
-                    includePrivate);
+            discoverAccessibleMethods(superclass, map, includeProtected, includePrivate);
+        }
+    }
+
+    void discoverPublicMethods(Class<?> clazz, Map<MethodSignature, Method> map) {
+        Method[] methods = clazz.getMethods();
+        for (Method method : methods) {
+            registerMethod(map, method);
         }
     }
 
-    private static final class MethodSignature
-    {
+    static void registerMethod(Map<MethodSignature, Method> map, Method method) {
+        MethodSignature sig = new MethodSignature(method);
+        // Array may contain methods with same signature but different return value!
+        map.putIfAbsent(sig, method);
+    }
+
+    static final class MethodSignature {
         private final String name;
         private final Class<?>[] args;
 
-        private MethodSignature(String name, Class<?>[] args)
-        {
+        private MethodSignature(String name, Class<?>[] args) {
             this.name = name;
             this.args = args;
         }
 
-        MethodSignature(Method method)
-        {
+        MethodSignature(Method method) {
             this(method.getName(), method.getParameterTypes());
         }
 
         @Override
-        public boolean equals(Object o)
-        {
-            if(o instanceof MethodSignature)
-            {
-                MethodSignature ms = (MethodSignature)o;
+        public boolean equals(Object o) {
+            if (o instanceof MethodSignature) {
+                MethodSignature ms = (MethodSignature) o;
                 return ms.name.equals(name) && Arrays.equals(args, ms.args);
             }
             return false;
         }
 
         @Override
-        public int hashCode()
-        {
+        public int hashCode() {
             return name.hashCode() ^ args.length;
         }
     }
 
-    private void reflect(Scriptable scope,
-                         boolean includeProtected,
-                         boolean includePrivate)
-    {
+    private void reflect(Scriptable scope, boolean includeProtected, boolean includePrivate) {
         // We reflect methods first, because we want overloaded field/method
         // names to be allocated to the NativeJavaMethod before the field
         // gets in the way.
 
-        Method[] methods = discoverAccessibleMethods(cl, includeProtected,
-                                                     includePrivate);
+        Method[] methods = discoverAccessibleMethods(cl, includeProtected, includePrivate);
         for (Method method : methods) {
             int mods = method.getModifiers();
             boolean isStatic = Modifier.isStatic(mods);
-            Map<String,Object> ht = isStatic ? staticMembers : members;
+            Map<String, Object> ht = isStatic ? staticMembers : members;
             String name = method.getName();
             Object value = ht.get(name);
             if (value == null) {
@@ -429,7 +427,7 @@
             } else {
                 ObjArray overloadedMethods;
                 if (value instanceof ObjArray) {
-                    overloadedMethods = (ObjArray)value;
+                    overloadedMethods = (ObjArray) value;
                 } else {
                     if (!(value instanceof Method)) Kit.codeBug();
                     // value should be instance of Method as at this stage
@@ -446,20 +444,20 @@
         // first in staticMembers and then in members
         for (int tableCursor = 0; tableCursor != 2; ++tableCursor) {
             boolean isStatic = (tableCursor == 0);
-            Map<String,Object> ht = isStatic ? staticMembers : members;
-            for (Map.Entry<String, Object> entry: ht.entrySet()) {
+            Map<String, Object> ht = isStatic ? staticMembers : members;
+            for (Map.Entry<String, Object> entry : ht.entrySet()) {
                 MemberBox[] methodBoxes;
                 Object value = entry.getValue();
                 if (value instanceof Method) {
                     methodBoxes = new MemberBox[1];
-                    methodBoxes[0] = new MemberBox((Method)value);
+                    methodBoxes[0] = new MemberBox((Method) value);
                 } else {
-                    ObjArray overloadedMethods = (ObjArray)value;
+                    ObjArray overloadedMethods = (ObjArray) value;
                     int N = overloadedMethods.size();
                     if (N < 2) Kit.codeBug();
                     methodBoxes = new MemberBox[N];
                     for (int i = 0; i != N; ++i) {
-                        Method method = (Method)overloadedMethods.get(i);
+                        Method method = (Method) overloadedMethods.get(i);
                         methodBoxes[i] = new MemberBox(method);
                     }
                 }
@@ -478,18 +476,17 @@
             int mods = field.getModifiers();
             try {
                 boolean isStatic = Modifier.isStatic(mods);
-                Map<String,Object> ht = isStatic ? staticMembers : members;
+                Map<String, Object> ht = isStatic ? staticMembers : members;
                 Object member = ht.get(name);
                 if (member == null) {
                     ht.put(name, field);
                 } else if (member instanceof NativeJavaMethod) {
                     NativeJavaMethod method = (NativeJavaMethod) member;
-                    FieldAndMethods fam
-                        = new FieldAndMethods(scope, method.methods, field);
-                    Map<String,FieldAndMethods> fmht = isStatic ? staticFieldAndMethods
-                                              : fieldAndMethods;
+                    FieldAndMethods fam = new FieldAndMethods(scope, method.methods, field);
+                    Map<String, FieldAndMethods> fmht =
+                            isStatic ? staticFieldAndMethods : fieldAndMethods;
                     if (fmht == null) {
-                        fmht = new HashMap<String,FieldAndMethods>();
+                        fmht = new HashMap<String, FieldAndMethods>();
                         if (isStatic) {
                             staticFieldAndMethods = fmht;
                         } else {
@@ -506,9 +503,7 @@
                     // reflected.
                     // For now, the first field found wins, unless another field
                     // explicitly shadows it.
-                    if (oldField.getDeclaringClass().
-                            isAssignableFrom(field.getDeclaringClass()))
-                    {
+                    if (oldField.getDeclaringClass().isAssignableFrom(field.getDeclaringClass())) {
                         ht.put(name, field);
                     }
                 } else {
@@ -517,9 +512,12 @@
                 }
             } catch (SecurityException e) {
                 // skip this field
-                Context.reportWarning("Could not access field "
-                        + name + " of class " + cl.getName() +
-                        " due to lack of privileges.");
+                Context.reportWarning(
+                        "Could not access field "
+                                + name
+                                + " of class "
+                                + cl.getName()
+                                + " due to lack of privileges.");
             }
         }
 
@@ -527,23 +525,20 @@
         // static members and then for instance members
         for (int tableCursor = 0; tableCursor != 2; ++tableCursor) {
             boolean isStatic = (tableCursor == 0);
-            Map<String,Object> ht = isStatic ? staticMembers : members;
+            Map<String, Object> ht = isStatic ? staticMembers : members;
 
-            Map<String,BeanProperty> toAdd = new HashMap<String,BeanProperty>();
+            Map<String, BeanProperty> toAdd = new HashMap<String, BeanProperty>();
 
             // Now, For each member, make "bean" properties.
-            for (String name: ht.keySet()) {
+            for (String name : ht.keySet()) {
                 // Is this a getter?
                 boolean memberIsGetMethod = name.startsWith("get");
                 boolean memberIsSetMethod = name.startsWith("set");
                 boolean memberIsIsMethod = name.startsWith("is");
-                if (memberIsGetMethod || memberIsIsMethod
-                        || memberIsSetMethod) {
+                if (memberIsGetMethod || memberIsIsMethod || memberIsSetMethod) {
                     // Double check name component.
-                    String nameComponent
-                        = name.substring(memberIsIsMethod ? 2 : 3);
-                    if (nameComponent.length() == 0)
-                        continue;
+                    String nameComponent = name.substring(memberIsIsMethod ? 2 : 3);
+                    if (nameComponent.length() == 0) continue;
 
                     // Make the bean property name.
                     String beanPropertyName = nameComponent;
@@ -554,23 +549,22 @@
                         } else {
                             char ch1 = nameComponent.charAt(1);
                             if (!Character.isUpperCase(ch1)) {
-                                beanPropertyName = Character.toLowerCase(ch0)
-                                                   +nameComponent.substring(1);
+                                beanPropertyName =
+                                        Character.toLowerCase(ch0) + nameComponent.substring(1);
                             }
                         }
                     }
 
                     // If we already have a member by this name, don't do this
                     // property.
-                    if (toAdd.containsKey(beanPropertyName))
-                        continue;
+                    if (toAdd.containsKey(beanPropertyName)) continue;
                     Object v = ht.get(beanPropertyName);
                     if (v != null) {
                         // A private field shouldn't mask a public getter/setter
-                        if (!includePrivate || !(v instanceof Member) ||
-                            !Modifier.isPrivate(((Member)v).getModifiers()))
+                        if (!includePrivate
+                                || !(v instanceof Member)
+                                || !Modifier.isPrivate(((Member) v).getModifiers())) {
 
-                        {
                             continue;
                         }
                     }
@@ -593,17 +587,15 @@
                         // Is this value a method?
                         Object member = ht.get(setterName);
                         if (member instanceof NativeJavaMethod) {
-                            NativeJavaMethod njmSet = (NativeJavaMethod)member;
+                            NativeJavaMethod njmSet = (NativeJavaMethod) member;
                             if (getter != null) {
                                 // We have a getter. Now, do we have a matching
                                 // setter?
                                 Class<?> type = getter.method().getReturnType();
-                                setter = extractSetMethod(type, njmSet.methods,
-                                                            isStatic);
+                                setter = extractSetMethod(type, njmSet.methods, isStatic);
                             } else {
                                 // No getter, find any set method
-                                setter = extractSetMethod(njmSet.methods,
-                                                            isStatic);
+                                setter = extractSetMethod(njmSet.methods, isStatic);
                             }
                             if (njmSet.methods.length > 1) {
                                 setters = njmSet;
@@ -611,17 +603,13 @@
                         }
                     }
                     // Make the property.
-                    BeanProperty bp = new BeanProperty(getter, setter,
-                                                       setters);
+                    BeanProperty bp = new BeanProperty(getter, setter, setters);
                     toAdd.put(beanPropertyName, bp);
                 }
             }
 
             // Add the new bean properties.
-            for (String key: toAdd.keySet()) {
-                Object value = toAdd.get(key);
-                ht.put(key, value);
-            }
+            ht.putAll(toAdd);
         }
 
         // Reflect constructors
@@ -633,28 +621,28 @@
         ctors = new NativeJavaMethod(ctorMembers, cl.getSimpleName());
     }
 
-    private Constructor<?>[] getAccessibleConstructors(boolean includePrivate)
-    {
-      // The JVM currently doesn't allow changing access on java.lang.Class
-      // constructors, so don't try
-      if (includePrivate && cl != ScriptRuntime.ClassClass) {
-          try {
-              Constructor<?>[] cons = cl.getDeclaredConstructors();
-              AccessibleObject.setAccessible(cons, true);
-
-              return cons;
-          } catch (SecurityException e) {
-              // Fall through to !includePrivate case
-              Context.reportWarning("Could not access constructor " +
-                    " of class " + cl.getName() +
-                    " due to lack of privileges.");
-          }
-      }
-      return cl.getConstructors();
+    private Constructor<?>[] getAccessibleConstructors(boolean includePrivate) {
+        // The JVM currently doesn't allow changing access on java.lang.Class
+        // constructors, so don't try
+        if (includePrivate && cl != ScriptRuntime.ClassClass) {
+            try {
+                Constructor<?>[] cons = cl.getDeclaredConstructors();
+                AccessibleObject.setAccessible(cons, true);
+
+                return cons;
+            } catch (SecurityException e) {
+                // Fall through to !includePrivate case
+                Context.reportWarning(
+                        "Could not access constructor "
+                                + " of class "
+                                + cl.getName()
+                                + " due to lack of privileges.");
+            }
+        }
+        return cl.getConstructors();
     }
 
-    private Field[] getAccessibleFields(boolean includeProtected,
-                                        boolean includePrivate) {
+    private Field[] getAccessibleFields(boolean includeProtected, boolean includePrivate) {
         if (includePrivate || includeProtected) {
             try {
                 List<Field> fieldsList = new ArrayList<Field>();
@@ -667,8 +655,7 @@
                     for (Field field : declared) {
                         int mod = field.getModifiers();
                         if (includePrivate || isPublic(mod) || isProtected(mod)) {
-                            if (!field.isAccessible())
-                                field.setAccessible(true);
+                            if (!field.isAccessible()) field.setAccessible(true);
                             fieldsList.add(field);
                         }
                     }
@@ -685,9 +672,8 @@
         return cl.getFields();
     }
 
-    private MemberBox findGetter(boolean isStatic, Map<String,Object> ht, String prefix,
-                                 String propertyName)
-    {
+    private static MemberBox findGetter(
+            boolean isStatic, Map<String, Object> ht, String prefix, String propertyName) {
         String getterName = prefix.concat(propertyName);
         if (ht.containsKey(getterName)) {
             // Check that the getter is a method.
@@ -700,9 +686,7 @@
         return null;
     }
 
-    private static MemberBox extractGetMethod(MemberBox[] methods,
-                                              boolean isStatic)
-    {
+    private static MemberBox extractGetMethod(MemberBox[] methods, boolean isStatic) {
         // Inspect the list of all MemberBox for the only one having no
         // parameters
         for (MemberBox method : methods) {
@@ -719,9 +703,8 @@
         return null;
     }
 
-    private static MemberBox extractSetMethod(Class<?> type, MemberBox[] methods,
-                                              boolean isStatic)
-    {
+    private static MemberBox extractSetMethod(
+            Class<?> type, MemberBox[] methods, boolean isStatic) {
         //
         // Note: it may be preferable to allow NativeJavaMethod.findFunction()
         //       to find the appropriate setter; unfortunately, it requires an
@@ -752,9 +735,7 @@
         return null;
     }
 
-    private static MemberBox extractSetMethod(MemberBox[] methods,
-                                              boolean isStatic)
-    {
+    private static MemberBox extractSetMethod(MemberBox[] methods, boolean isStatic) {
 
         for (MemberBox method : methods) {
             if (!isStatic || method.isStatic()) {
@@ -768,44 +749,40 @@
         return null;
     }
 
-    Map<String,FieldAndMethods> getFieldAndMethodsObjects(Scriptable scope,
-            Object javaObject, boolean isStatic)
-    {
-        Map<String,FieldAndMethods> ht = isStatic ? staticFieldAndMethods : fieldAndMethods;
-        if (ht == null)
-            return null;
+    Map<String, FieldAndMethods> getFieldAndMethodsObjects(
+            Scriptable scope, Object javaObject, boolean isStatic) {
+        Map<String, FieldAndMethods> ht = isStatic ? staticFieldAndMethods : fieldAndMethods;
+        if (ht == null) return null;
         int len = ht.size();
-        Map<String,FieldAndMethods> result = new HashMap<String,FieldAndMethods>(len);
-        for (FieldAndMethods fam: ht.values()) {
-            FieldAndMethods famNew = new FieldAndMethods(scope, fam.methods,
-                                                         fam.field);
+        Map<String, FieldAndMethods> result = new HashMap<String, FieldAndMethods>(len);
+        for (FieldAndMethods fam : ht.values()) {
+            FieldAndMethods famNew = new FieldAndMethods(scope, fam.methods, fam.field);
             famNew.javaObject = javaObject;
             result.put(fam.field.getName(), famNew);
         }
         return result;
     }
 
-    static JavaMembers lookupClass(Scriptable scope, Class<?> dynamicType,
-                                   Class<?> staticType, boolean includeProtected)
-    {
+    static JavaMembers lookupClass(
+            Scriptable scope, Class<?> dynamicType, Class<?> staticType, boolean includeProtected) {
         JavaMembers members;
         ClassCache cache = ClassCache.get(scope);
-        Map<Class<?>,JavaMembers> ct = cache.getClassCacheMap();
+        Map<ClassCache.CacheKey, JavaMembers> ct = cache.getClassCacheMap();
 
         Class<?> cl = dynamicType;
-        for (;;) {
-            members = ct.get(cl);
+        Object secCtx = getSecurityContext();
+        for (; ; ) {
+            members = ct.get(new ClassCache.CacheKey(cl, secCtx));
             if (members != null) {
                 if (cl != dynamicType) {
                     // member lookup for the original class failed because of
                     // missing privileges, cache the result so we don't try again
-                    ct.put(dynamicType, members);
+                    ct.put(new ClassCache.CacheKey(dynamicType, secCtx), members);
                 }
                 return members;
             }
             try {
-                members = new JavaMembers(cache.getAssociatedScope(), cl,
-                        includeProtected);
+                members = createJavaMembers(cache.getAssociatedScope(), cl, includeProtected);
                 break;
             } catch (SecurityException e) {
                 // Reflection may fail for objects that are in a restricted
@@ -831,34 +808,58 @@
         }
 
         if (cache.isCachingEnabled()) {
-            ct.put(cl, members);
+            ct.put(new ClassCache.CacheKey(cl, secCtx), members);
             if (cl != dynamicType) {
                 // member lookup for the original class failed because of
                 // missing privileges, cache the result so we don't try again
-                ct.put(dynamicType, members);
+                ct.put(new ClassCache.CacheKey(dynamicType, secCtx), members);
             }
         }
         return members;
     }
 
-    RuntimeException reportMemberNotFound(String memberName)
-    {
-        return Context.reportRuntimeError2(
-            "msg.java.member.not.found", cl.getName(), memberName);
+    private static JavaMembers createJavaMembers(
+            Scriptable associatedScope, Class<?> cl, boolean includeProtected) {
+        if (STRICT_REFLECTIVE_ACCESS) {
+            return new JavaMembers_jdk11(associatedScope, cl, includeProtected);
+        } else {
+            return new JavaMembers(associatedScope, cl, includeProtected);
+        }
+    }
+
+    private static Object getSecurityContext() {
+        Object sec = null;
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sec = sm.getSecurityContext();
+            if (sec instanceof AccessControlContext) {
+                try {
+                    ((AccessControlContext) sec).checkPermission(allPermission);
+                    // if we have allPermission, we do not need to store the
+                    // security object in the cache key
+                    return null;
+                } catch (SecurityException e) {
+                }
+            }
+        }
+        return sec;
+    }
+
+    RuntimeException reportMemberNotFound(String memberName) {
+        return Context.reportRuntimeErrorById(
+                "msg.java.member.not.found", cl.getName(), memberName);
     }
 
     private Class<?> cl;
-    private Map<String,Object> members;
-    private Map<String,FieldAndMethods> fieldAndMethods;
-    private Map<String,Object> staticMembers;
-    private Map<String,FieldAndMethods> staticFieldAndMethods;
+    private Map<String, Object> members;
+    private Map<String, FieldAndMethods> fieldAndMethods;
+    private Map<String, Object> staticMembers;
+    private Map<String, FieldAndMethods> staticFieldAndMethods;
     NativeJavaMethod ctors; // we use NativeJavaMethod for ctor overload resolution
 }
 
-class BeanProperty
-{
-    BeanProperty(MemberBox getter, MemberBox setter, NativeJavaMethod setters)
-    {
+class BeanProperty {
+    BeanProperty(MemberBox getter, MemberBox setter, NativeJavaMethod setters) {
         this.getter = getter;
         this.setter = setter;
         this.setters = setters;
@@ -869,12 +870,10 @@
     NativeJavaMethod setters;
 }
 
-class FieldAndMethods extends NativeJavaMethod
-{
-    static final long serialVersionUID = -9222428244284796755L;
+class FieldAndMethods extends NativeJavaMethod {
+    private static final long serialVersionUID = -9222428244284796755L;
 
-    FieldAndMethods(Scriptable scope, MemberBox[] methods, Field field)
-    {
+    FieldAndMethods(Scriptable scope, MemberBox[] methods, Field field) {
         super(methods);
         this.field = field;
         setParentScope(scope);
@@ -882,20 +881,17 @@
     }
 
     @Override
-    public Object getDefaultValue(Class<?> hint)
-    {
-        if (hint == ScriptRuntime.FunctionClass)
-            return this;
+    public Object getDefaultValue(Class<?> hint) {
+        if (hint == ScriptRuntime.FunctionClass) return this;
         Object rval;
         Class<?> type;
         try {
             rval = field.get(javaObject);
             type = field.getType();
         } catch (IllegalAccessException accEx) {
-            throw Context.reportRuntimeError1(
-                "msg.java.internal.private", field.getName());
+            throw Context.reportRuntimeErrorById("msg.java.internal.private", field.getName());
         }
-        Context cx  = Context.getContext();
+        Context cx = Context.getContext();
         rval = cx.getWrapFactory().wrap(cx, this, rval, type);
         if (rval instanceof Scriptable) {
             rval = ((Scriptable) rval).getDefaultValue(hint);
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/JavaMembers_jdk11.java rhino-1.7.14/src/org/mozilla/javascript/JavaMembers_jdk11.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/JavaMembers_jdk11.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/JavaMembers_jdk11.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,118 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Map;
+
+/** Version of {@link JavaMembers} for modular JDKs. */
+class JavaMembers_jdk11 extends JavaMembers {
+
+    JavaMembers_jdk11(Scriptable scope, Class<?> cl, boolean includeProtected) {
+        super(scope, cl, includeProtected);
+    }
+
+    @Override
+    void discoverPublicMethods(Class<?> clazz, Map<MethodSignature, Method> map) {
+        if (isExportedClass(clazz)) {
+            super.discoverPublicMethods(clazz, map);
+        } else {
+            Method[] methods = clazz.getMethods();
+            for (Method method : methods) {
+                method = findAccessibleMethod(method);
+                registerMethod(map, method);
+            }
+        }
+    }
+
+    private static boolean isExportedClass(Class<?> clazz) {
+        /*
+         * We are going to invoke, using reflection, the approximate equivalent
+         * to the following Java 9 code:
+         *
+         * return clazz.getModule().isExported(clazz.getPackageName());
+         */
+
+        // First, determine the package name
+        String pname;
+        Package pkg = clazz.getPackage();
+        if (pkg == null) {
+            // Primitive types, arrays, proxy classes
+            if (!Proxy.isProxyClass(clazz)) {
+                // Should be a primitive type or array
+                return true;
+            }
+            String clName = clazz.getName();
+            pname = clName.substring(0, clName.lastIndexOf('.'));
+        } else {
+            pname = pkg.getName();
+        }
+
+        // Obtain the module
+        Method getmodule;
+        Class<?> cl = clazz.getClass();
+        try {
+            getmodule = cl.getMethod("getModule");
+        } catch (NoSuchMethodException e) {
+            // We are on non-modular Java
+            return true;
+        }
+
+        Object module;
+        try {
+            module = getmodule.invoke(clazz);
+        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            return false;
+        }
+
+        Class<?> moduleClass = module.getClass();
+
+        // Execute isExported()
+        boolean exported;
+        try {
+            Method isexported = moduleClass.getMethod("isExported", String.class);
+            exported = (boolean) isexported.invoke(module, pname);
+        } catch (NoSuchMethodException
+                | IllegalAccessException
+                | IllegalArgumentException
+                | InvocationTargetException e) {
+            // If we reached this part of the code, this cannot happen
+            exported = false;
+        }
+        return exported;
+    }
+
+    private static Method findAccessibleMethod(Method method) {
+        Class<?> cl = method.getDeclaringClass();
+        final String methodName = method.getName();
+        final Class<?>[] methodTypes = method.getParameterTypes();
+        topLoop:
+        do {
+            for (Class<?> intface : cl.getInterfaces()) {
+                try {
+                    method = intface.getMethod(methodName, methodTypes);
+                    break topLoop;
+                } catch (NoSuchMethodException e) {
+                }
+            }
+            cl = cl.getSuperclass();
+            if (cl == null) {
+                break;
+            }
+            if (isExportedClass(cl)) {
+                try {
+                    method = cl.getMethod(methodName, methodTypes);
+                    break;
+                } catch (NoSuchMethodException e) {
+                }
+            }
+        } while (true);
+        return method;
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/JavaScriptException.java rhino-1.7.14/src/org/mozilla/javascript/JavaScriptException.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/JavaScriptException.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/JavaScriptException.java	2022-01-06 22:57:21.000000000 +0100
@@ -9,23 +9,20 @@
 package org.mozilla.javascript;
 
 /**
- * Java reflection of JavaScript exceptions.
- * Instances of this class are thrown by the JavaScript 'throw' keyword.
+ * Java reflection of JavaScript exceptions. Instances of this class are thrown by the JavaScript
+ * 'throw' keyword.
  *
  * @author Mike McCabe
  */
-public class JavaScriptException extends RhinoException
-{
-    static final long serialVersionUID = -7666130513694669293L;
+public class JavaScriptException extends RhinoException {
+    private static final long serialVersionUID = -7666130513694669293L;
 
     /**
-     * @deprecated
-     * Use {@link WrappedException#WrappedException(Throwable)} to report
-     * exceptions in Java code.
+     * @deprecated Use {@link WrappedException#WrappedException(Throwable)} to report exceptions in
+     *     Java code.
      */
     @Deprecated
-    public JavaScriptException(Object value)
-    {
+    public JavaScriptException(Object value) {
         this(value, "", 0);
     }
 
@@ -34,14 +31,13 @@
      *
      * @param value the JavaScript value thrown.
      */
-    public JavaScriptException(Object value, String sourceName, int lineNumber)
-    {
+    public JavaScriptException(Object value, String sourceName, int lineNumber) {
         recordErrorOrigin(sourceName, lineNumber, null, 0);
         this.value = value;
         // Fill in fileName and lineNumber automatically when not specified
         // explicitly, see Bugzilla issue #342807
-        if (value instanceof NativeError && Context.getContext()
-                .hasFeature(Context.FEATURE_LOCATION_INFORMATION_IN_ERROR)) {
+        if (value instanceof NativeError
+                && Context.getContext().hasFeature(Context.FEATURE_LOCATION_INFORMATION_IN_ERROR)) {
             NativeError error = (NativeError) value;
             if (!error.has("fileName", error)) {
                 error.put("fileName", error, sourceName);
@@ -52,53 +48,54 @@
             // set stack property, see bug #549604
             error.setStackProvider(this);
         }
+
+        // generate details string when exception is first created,
+        // since details() may be called later from a different thread
+        // (e.g. when printing failed test results), which
+        // would cause ScriptRuntime.toString to fail.
+        this.details = getDetails();
     }
 
     @Override
-    public String details()
-    {
+    public String details() {
+        return this.details;
+    }
+
+    public String getDetails() {
         if (value == null) {
             return "null";
         } else if (value instanceof NativeError) {
             return value.toString();
         }
+
         try {
             return ScriptRuntime.toString(value);
         } catch (RuntimeException rte) {
             // ScriptRuntime.toString may throw a RuntimeException
             if (value instanceof Scriptable) {
-                return ScriptRuntime.defaultObjectToString((Scriptable)value);
-            } else {
-                return value.toString();
+                return ScriptRuntime.defaultObjectToString((Scriptable) value);
             }
+            return value.toString();
         }
     }
 
-    /**
-     * @return the value wrapped by this exception
-     */
-    public Object getValue()
-    {
+    /** @return the value wrapped by this exception */
+    public Object getValue() {
         return value;
     }
 
-    /**
-     * @deprecated Use {@link RhinoException#sourceName()} from the super class.
-     */
+    /** @deprecated Use {@link RhinoException#sourceName()} from the super class. */
     @Deprecated
-    public String getSourceName()
-    {
+    public String getSourceName() {
         return sourceName();
     }
 
-    /**
-     * @deprecated Use {@link RhinoException#lineNumber()} from the super class.
-     */
+    /** @deprecated Use {@link RhinoException#lineNumber()} from the super class. */
     @Deprecated
-    public int getLineNumber()
-    {
+    public int getLineNumber() {
         return lineNumber();
     }
 
     private Object value;
+    private String details;
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/JavaToJSONConverters.java rhino-1.7.14/src/org/mozilla/javascript/JavaToJSONConverters.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/JavaToJSONConverters.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/JavaToJSONConverters.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,86 @@
+/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+import java.beans.BeanDescriptor;
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.function.UnaryOperator;
+
+/**
+ * This class provides implementations of converters for Java objects to be used by the
+ * JSON.stringify method.
+ *
+ * <p>JSON.stringify will automatically convert instances of java.util.Map to javascript objects.
+ * Instances of java.util.Collection and java Arrays will be converted to javascript arrays.
+ *
+ * <p>This is a final effort at conversion for other java objects that appear as values, and may be
+ * preempted by objects which define a toJSON() method or by a replacer function passed to
+ * JSON.stringify. The return value will, in turn, be converted according to {@link
+ * Context#javaToJS} and stringified.
+ *
+ * @author Tony Germano
+ */
+public class JavaToJSONConverters {
+
+    private JavaToJSONConverters() {}
+
+    /** Convert Object to its toString() value. */
+    public static final UnaryOperator<Object> STRING = o -> o.toString();
+
+    /** Always return undefined */
+    public static final UnaryOperator<Object> UNDEFINED = o -> Undefined.instance;
+
+    /** Always return an empty object */
+    public static final UnaryOperator<Object> EMPTY_OBJECT = o -> Collections.EMPTY_MAP;
+
+    /** Throw a TypeError naming the class that could not be converted */
+    public static final UnaryOperator<Object> THROW_TYPE_ERROR =
+            o -> {
+                throw ScriptRuntime.typeErrorById(
+                        "msg.json.cant.serialize", o.getClass().getName());
+            };
+
+    /**
+     * Convert JavaBean to an object as long as it has at least one readable property
+     *
+     * <p>If unable to determine properties or if none exist, null is returned. This method can be
+     * called from other converters to provide an alternate value on a returned null.
+     */
+    public static final UnaryOperator<Object> BEAN =
+            value -> {
+                BeanInfo beanInfo;
+                try {
+                    beanInfo = Introspector.getBeanInfo(value.getClass(), Object.class);
+                } catch (IntrospectionException e) {
+                    return null;
+                }
+                LinkedHashMap<String, Object> properties = new LinkedHashMap<String, Object>();
+                for (PropertyDescriptor descriptor : beanInfo.getPropertyDescriptors()) {
+                    if (descriptor.getReadMethod() == null) continue;
+                    Object propValue;
+                    try {
+                        propValue = descriptor.getReadMethod().invoke(value);
+                    } catch (Exception e) {
+                        continue;
+                    }
+                    properties.put(descriptor.getName(), propValue);
+                }
+
+                if (properties.size() == 0) return null;
+
+                LinkedHashMap<String, Object> obj = new LinkedHashMap<String, Object>();
+                BeanDescriptor beanDescriptor = beanInfo.getBeanDescriptor();
+                obj.put("beanClass", beanDescriptor.getBeanClass().getName());
+                obj.put("properties", properties);
+                return obj;
+            };
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/jdk13/VMBridge_jdk13.java rhino-1.7.14/src/org/mozilla/javascript/jdk13/VMBridge_jdk13.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/jdk13/VMBridge_jdk13.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/jdk13/VMBridge_jdk13.java	1970-01-01 01:00:00.000000000 +0100
@@ -1,154 +0,0 @@
-/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.javascript.jdk13;
-
-import java.lang.reflect.AccessibleObject;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Member;
-import java.lang.reflect.Proxy;
-
-import org.mozilla.javascript.*;
-
-public class VMBridge_jdk13 extends VMBridge
-{
-    private ThreadLocal<Object[]> contextLocal = new ThreadLocal<Object[]>();
-
-    @Override
-    protected Object getThreadContextHelper()
-    {
-        // To make subsequent batch calls to getContext/setContext faster
-        // associate permanently one element array with contextLocal
-        // so getContext/setContext would need just to read/write the first
-        // array element.
-        // Note that it is necessary to use Object[], not Context[] to allow
-        // garbage collection of Rhino classes. For details see comments
-        // by Attila Szegedi in
-        // https://bugzilla.mozilla.org/show_bug.cgi?id=281067#c5
-
-        Object[] storage = contextLocal.get();
-        if (storage == null) {
-            storage = new Object[1];
-            contextLocal.set(storage);
-        }
-        return storage;
-    }
-
-    @Override
-    protected Context getContext(Object contextHelper)
-    {
-        Object[] storage = (Object[])contextHelper;
-        return (Context)storage[0];
-    }
-
-    @Override
-    protected void setContext(Object contextHelper, Context cx)
-    {
-        Object[] storage = (Object[])contextHelper;
-        storage[0] = cx;
-    }
-
-    @Override
-    protected ClassLoader getCurrentThreadClassLoader()
-    {
-        return Thread.currentThread().getContextClassLoader();
-    }
-
-    @Override
-    protected boolean tryToMakeAccessible(Object accessibleObject)
-    {
-        if (!(accessibleObject instanceof AccessibleObject)) {
-            return false;
-        }
-        AccessibleObject accessible = (AccessibleObject)accessibleObject;
-        if (accessible.isAccessible()) {
-            return true;
-        }
-        try {
-            accessible.setAccessible(true);
-        } catch (Exception ex) { }
-
-        return accessible.isAccessible();
-    }
-
-    @Override
-    protected Object getInterfaceProxyHelper(ContextFactory cf,
-                                             Class<?>[] interfaces)
-    {
-        // XXX: How to handle interfaces array withclasses from different
-        // class loaders? Using cf.getApplicationClassLoader() ?
-        ClassLoader loader = interfaces[0].getClassLoader();
-        Class<?> cl = Proxy.getProxyClass(loader, interfaces);
-        Constructor<?> c;
-        try {
-            c = cl.getConstructor(new Class[] { InvocationHandler.class });
-        } catch (NoSuchMethodException ex) {
-            // Should not happen
-            throw Kit.initCause(new IllegalStateException(), ex);
-        }
-        return c;
-    }
-
-    @Override
-    protected Object newInterfaceProxy(Object proxyHelper,
-                                       final ContextFactory cf,
-                                       final InterfaceAdapter adapter,
-                                       final Object target,
-                                       final Scriptable topScope)
-    {
-        Constructor<?> c = (Constructor<?>)proxyHelper;
-
-        InvocationHandler handler = new InvocationHandler() {
-                public Object invoke(Object proxy,
-                                     Method method,
-                                     Object[] args)
-                {
-                    // In addition to methods declared in the interface, proxies
-                    // also route some java.lang.Object methods through the
-                    // invocation handler.
-                    if (method.getDeclaringClass() == Object.class) {
-                        String methodName = method.getName();
-                        if (methodName.equals("equals")) {
-                            Object other = args[0];
-                            // Note: we could compare a proxy and its wrapped function
-                            // as equal here but that would break symmetry of equal().
-                            // The reason == suffices here is that proxies are cached
-                            // in ScriptableObject (see NativeJavaObject.coerceType())
-                            return Boolean.valueOf(proxy == other);
-                        }
-                        if (methodName.equals("hashCode")) {
-                            return Integer.valueOf(target.hashCode());
-                        }
-                        if (methodName.equals("toString")) {
-                            return "Proxy[" + target.toString() + "]";
-                        }
-                    }
-                    return adapter.invoke(cf, target, topScope, proxy, method, args);
-                }
-            };
-        Object proxy;
-        try {
-            proxy = c.newInstance(handler);
-        } catch (InvocationTargetException ex) {
-            throw Context.throwAsScriptRuntimeEx(ex);
-        } catch (IllegalAccessException ex) {
-            // Should not happen
-            throw Kit.initCause(new IllegalStateException(), ex);
-        } catch (InstantiationException ex) {
-            // Should not happen
-            throw Kit.initCause(new IllegalStateException(), ex);
-        }
-        return proxy;
-    }
-
-    @Override
-    protected boolean isVarArgs(Member member) {
-      return false;
-    }
-}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/jdk15/VMBridge_jdk15.java rhino-1.7.14/src/org/mozilla/javascript/jdk15/VMBridge_jdk15.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/jdk15/VMBridge_jdk15.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/jdk15/VMBridge_jdk15.java	1970-01-01 01:00:00.000000000 +0100
@@ -1,58 +0,0 @@
-/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.javascript.jdk15;
-
-import java.lang.reflect.Member;
-import java.lang.reflect.Method;
-import java.lang.reflect.Constructor;
-import java.util.Iterator;
-import org.mozilla.javascript.*;
-
-public class VMBridge_jdk15 extends org.mozilla.javascript.jdk13.VMBridge_jdk13
-{
-    public VMBridge_jdk15() throws SecurityException, InstantiationException {
-        try {
-            // Just try and see if we can access the isVarArgs method.
-            // We want to fail loading if the method does not exist
-            // so that we can load a bridge to an older JDK instead.
-            Method.class.getMethod("isVarArgs", (Class[]) null);
-        } catch (NoSuchMethodException e) {
-            // Throw a fitting exception that is handled by
-            // org.mozilla.javascript.Kit.newInstanceOrNull:
-            throw new InstantiationException(e.getMessage());
-        }
-    }
-
-    @Override
-    public boolean isVarArgs(Member member) {
-        if (member instanceof Method)
-            return ((Method) member).isVarArgs();
-        else if (member instanceof Constructor)
-            return ((Constructor<?>) member).isVarArgs();
-        else
-            return false;
-    }
-
-    /**
-     * If "obj" is a java.util.Iterator or a java.lang.Iterable, return a
-     * wrapping as a JavaScript Iterator. Otherwise, return null.
-     * This method is in VMBridge since Iterable is a JDK 1.5 addition.
-     */
-    @Override
-    public Iterator<?> getJavaIterator(Context cx, Scriptable scope, Object obj) {
-        if (obj instanceof Wrapper) {
-            Object unwrapped = ((Wrapper) obj).unwrap();
-            Iterator<?> iterator = null;
-            if (unwrapped instanceof Iterator)
-                iterator = (Iterator<?>) unwrapped;
-            if (unwrapped instanceof Iterable)
-                iterator = ((Iterable<?>)unwrapped).iterator();
-            return iterator;
-        }
-        return null;
-    }
-}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/jdk18/VMBridge_jdk18.java rhino-1.7.14/src/org/mozilla/javascript/jdk18/VMBridge_jdk18.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/jdk18/VMBridge_jdk18.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/jdk18/VMBridge_jdk18.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,143 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript.jdk18;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.ContextFactory;
+import org.mozilla.javascript.InterfaceAdapter;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.VMBridge;
+
+public class VMBridge_jdk18 extends VMBridge
+{
+    private static final ThreadLocal<Object[]> contextLocal = new ThreadLocal<Object[]>();
+
+    @Override
+    protected Object getThreadContextHelper()
+    {
+        // To make subsequent batch calls to getContext/setContext faster
+        // associate permanently one element array with contextLocal
+        // so getContext/setContext would need just to read/write the first
+        // array element.
+        // Note that it is necessary to use Object[], not Context[] to allow
+        // garbage collection of Rhino classes. For details see comments
+        // by Attila Szegedi in
+        // https://bugzilla.mozilla.org/show_bug.cgi?id=281067#c5
+
+        Object[] storage = contextLocal.get();
+        if (storage == null) {
+            storage = new Object[1];
+            contextLocal.set(storage);
+        }
+        return storage;
+    }
+
+    @Override
+    protected Context getContext(Object contextHelper)
+    {
+        Object[] storage = (Object[])contextHelper;
+        return (Context)storage[0];
+    }
+
+    @Override
+    protected void setContext(Object contextHelper, Context cx)
+    {
+        Object[] storage = (Object[])contextHelper;
+        storage[0] = cx;
+    }
+
+    @Override
+    protected boolean tryToMakeAccessible(AccessibleObject accessible)
+    {
+        if (accessible.isAccessible()) {
+            return true;
+        }
+        try {
+            accessible.setAccessible(true);
+        } catch (Exception ex) { }
+
+        return accessible.isAccessible();
+    }
+
+    @Override
+    protected Object getInterfaceProxyHelper(ContextFactory cf,
+                                             Class<?>[] interfaces)
+    {
+        // XXX: How to handle interfaces array withclasses from different
+        // class loaders? Using cf.getApplicationClassLoader() ?
+        ClassLoader loader = interfaces[0].getClassLoader();
+        Class<?> cl = Proxy.getProxyClass(loader, interfaces);
+        Constructor<?> c;
+        try {
+            c = cl.getConstructor(new Class[] { InvocationHandler.class });
+        } catch (NoSuchMethodException ex) {
+            // Should not happen
+            throw new IllegalStateException(ex);
+        }
+        return c;
+    }
+
+    @Override
+    protected Object newInterfaceProxy(Object proxyHelper,
+                                       final ContextFactory cf,
+                                       final InterfaceAdapter adapter,
+                                       final Object target,
+                                       final Scriptable topScope)
+    {
+        Constructor<?> c = (Constructor<?>)proxyHelper;
+
+        InvocationHandler handler = new InvocationHandler() {
+                @Override
+                public Object invoke(Object proxy,
+                                     Method method,
+                                     Object[] args)
+                {
+                    // In addition to methods declared in the interface, proxies
+                    // also route some java.lang.Object methods through the
+                    // invocation handler.
+                    if (method.getDeclaringClass() == Object.class) {
+                        String methodName = method.getName();
+                        if (methodName.equals("equals")) {
+                            Object other = args[0];
+                            // Note: we could compare a proxy and its wrapped function
+                            // as equal here but that would break symmetry of equal().
+                            // The reason == suffices here is that proxies are cached
+                            // in ScriptableObject (see NativeJavaObject.coerceType())
+                            return Boolean.valueOf(proxy == other);
+                        }
+                        if (methodName.equals("hashCode")) {
+                            return Integer.valueOf(target.hashCode());
+                        }
+                        if (methodName.equals("toString")) {
+                            return "Proxy[" + target.toString() + "]";
+                        }
+                    }
+                    return adapter.invoke(cf, target, topScope, proxy, method, args);
+                }
+            };
+        Object proxy;
+        try {
+            proxy = c.newInstance(handler);
+        } catch (InvocationTargetException ex) {
+            throw Context.throwAsScriptRuntimeEx(ex);
+        } catch (IllegalAccessException ex) {
+            // Should not happen
+            throw new IllegalStateException(ex);
+        } catch (InstantiationException ex) {
+            // Should not happen
+            throw new IllegalStateException(ex);
+        }
+        return proxy;
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/json/JsonParser.java rhino-1.7.14/src/org/mozilla/javascript/json/JsonParser.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/json/JsonParser.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/json/JsonParser.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,13 +6,13 @@
 
 package org.mozilla.javascript.json;
 
-import org.mozilla.javascript.Context;
-import org.mozilla.javascript.Scriptable;
-import org.mozilla.javascript.ScriptRuntime;
-
 import java.util.ArrayList;
 import java.util.List;
 
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.Scriptable;
+
 /**
  * This class converts a stream of JSON tokens into a JSON value.
  *
@@ -260,7 +260,7 @@
         throw new ParseException("Unterminated string literal");
     }
 
-    private int fromHex(char c) {
+    private static int fromHex(char c) {
         return c >= '0' && c <= '9' ? c - '0'
                 : c >= 'A' && c <= 'F' ? c - 'A' + 10
                 : c >= 'a' && c <= 'f' ? c - 'a' + 10
@@ -311,9 +311,8 @@
         final int ival = (int)dval;
         if (ival == dval) {
             return Integer.valueOf(ival);
-        } else {
-            return Double.valueOf(dval);
         }
+        return Double.valueOf(dval);
     }
 
     private ParseException numberError(int start, int end) {
@@ -394,15 +393,14 @@
         char c = src.charAt(pos++);
         if (c == token) {
             return;
-        } else {
-            throw new ParseException("Expected " + token + " found " + c);
         }
+        throw new ParseException("Expected " + token + " found " + c);
     }
 
     public static class ParseException extends Exception {
-        
-        static final long serialVersionUID = 4804542791749920772L;
-        
+
+        private static final long serialVersionUID = 4804542791749920772L;
+
         ParseException(String message) {
             super(message);
         }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/Kit.java rhino-1.7.14/src/org/mozilla/javascript/Kit.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/Kit.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/Kit.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,43 +6,20 @@
 
 package org.mozilla.javascript;
 
+import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
-import java.lang.reflect.Method;
 import java.util.Map;
 
-/**
- * Collection of utilities
- */
-
-public class Kit
-{
-    /**
-     * Reflection of Throwable.initCause(Throwable) from JDK 1.4
-     * or nul if it is not available.
-     */
-    private static Method Throwable_initCause = null;
-
-    static {
-        // Are we running on a JDK 1.4 or later system?
-        try {
-            Class<?> ThrowableClass = Kit.classOrNull("java.lang.Throwable");
-            Class<?>[] signature = { ThrowableClass };
-            Throwable_initCause
-                = ThrowableClass.getMethod("initCause", signature);
-        } catch (Exception ex) {
-            // Assume any exceptions means the method does not exist.
-        }
-    }
-
-    public static Class<?> classOrNull(String className)
-    {
+/** Collection of utilities */
+public class Kit {
+    public static Class<?> classOrNull(String className) {
         try {
             return Class.forName(className);
-        } catch  (ClassNotFoundException ex) {
-        } catch  (SecurityException ex) {
-        } catch  (LinkageError ex) {
+        } catch (ClassNotFoundException ex) {
+        } catch (SecurityException ex) {
+        } catch (LinkageError ex) {
         } catch (IllegalArgumentException e) {
             // Can be thrown if name has characters that a class name
             // can not contain
@@ -50,12 +27,8 @@
         return null;
     }
 
-    /**
-     * Attempt to load the class of the given name. Note that the type parameter
-     * isn't checked.
-     */
-    public static Class<?> classOrNull(ClassLoader loader, String className)
-    {
+    /** Attempt to load the class of the given name. Note that the type parameter isn't checked. */
+    public static Class<?> classOrNull(ClassLoader loader, String className) {
         try {
             return loader.loadClass(className);
         } catch (ClassNotFoundException ex) {
@@ -68,23 +41,19 @@
         return null;
     }
 
-    static Object newInstanceOrNull(Class<?> cl)
-    {
+    static Object newInstanceOrNull(Class<?> cl) {
         try {
             return cl.newInstance();
         } catch (SecurityException x) {
-        } catch  (LinkageError ex) {
+        } catch (LinkageError ex) {
         } catch (InstantiationException x) {
         } catch (IllegalAccessException x) {
         }
         return null;
     }
 
-    /**
-     * Check that testClass is accessible from the given loader.
-     */
-    static boolean testIfCanLoadRhinoClasses(ClassLoader loader)
-    {
+    /** Check that testClass is accessible from the given loader. */
+    static boolean testIfCanLoadRhinoClasses(ClassLoader loader) {
         Class<?> testClass = ScriptRuntime.ContextFactoryClass;
         Class<?> x = Kit.classOrNull(loader, testClass.getName());
         if (x != testClass) {
@@ -98,36 +67,18 @@
     }
 
     /**
-     * If initCause methods exists in Throwable, call
-     * <tt>ex.initCause(cause)</tt> or otherwise do nothing.
-     * @return The <tt>ex</tt> argument.
-     */
-    public static RuntimeException initCause(RuntimeException ex,
-                                             Throwable cause)
-    {
-        if (Throwable_initCause != null) {
-            Object[] args = { cause };
-            try {
-                Throwable_initCause.invoke(ex, args);
-            } catch (Exception e) {
-                // Ignore any exceptions
-            }
-        }
-        return ex;
-    }
-
-    /**
-     * If character <tt>c</tt> is a hexadecimal digit, return
-     * <tt>accumulator</tt> * 16 plus corresponding
-     * number. Otherise return -1.
+     * If character <code>c</code> is a hexadecimal digit, return <code>accumulator</code> * 16 plus
+     * corresponding number. Otherise return -1.
      */
-    public static int xDigitToInt(int c, int accumulator)
-    {
-        check: {
+    public static int xDigitToInt(int c, int accumulator) {
+        check:
+        {
             // Use 0..9 < A..Z < a..z
             if (c <= '9') {
                 c -= '0';
-                if (0 <= c) { break check; }
+                if (0 <= c) {
+                    break check;
+                }
             } else if (c <= 'F') {
                 if ('A' <= c) {
                     c -= ('A' - 10);
@@ -145,12 +96,12 @@
     }
 
     /**
-     * Add <i>listener</i> to <i>bag</i> of listeners.
-     * The function does not modify <i>bag</i> and return a new collection
-     * containing <i>listener</i> and all listeners from <i>bag</i>.
-     * Bag without listeners always represented as the null value.
-     * <p>
-     * Usage example:
+     * Add <i>listener</i> to <i>bag</i> of listeners. The function does not modify <i>bag</i> and
+     * return a new collection containing <i>listener</i> and all listeners from <i>bag</i>. Bag
+     * without listeners always represented as the null value.
+     *
+     * <p>Usage example:
+     *
      * <pre>
      *     private volatile Object changeListeners;
      *
@@ -187,22 +138,20 @@
      *
      * @param listener Listener to add to <i>bag</i>
      * @param bag Current collection of listeners.
-     * @return A new bag containing all listeners from <i>bag</i> and
-     *          <i>listener</i>.
+     * @return A new bag containing all listeners from <i>bag</i> and <i>listener</i>.
      * @see #removeListener(Object bag, Object listener)
      * @see #getListener(Object bag, int index)
      */
-    public static Object addListener(Object bag, Object listener)
-    {
+    public static Object addListener(Object bag, Object listener) {
         if (listener == null) throw new IllegalArgumentException();
         if (listener instanceof Object[]) throw new IllegalArgumentException();
 
         if (bag == null) {
             bag = listener;
         } else if (!(bag instanceof Object[])) {
-            bag = new Object[] { bag, listener };
+            bag = new Object[] {bag, listener};
         } else {
-            Object[] array = (Object[])bag;
+            Object[] array = (Object[]) bag;
             int L = array.length;
             // bag has at least 2 elements if it is array
             if (L < 2) throw new IllegalArgumentException();
@@ -216,30 +165,26 @@
     }
 
     /**
-     * Remove <i>listener</i> from <i>bag</i> of listeners.
-     * The function does not modify <i>bag</i> and return a new collection
-     * containing all listeners from <i>bag</i> except <i>listener</i>.
-     * If <i>bag</i> does not contain <i>listener</i>, the function returns
-     * <i>bag</i>.
-     * <p>
-     * For usage example, see {@link #addListener(Object bag, Object listener)}.
+     * Remove <i>listener</i> from <i>bag</i> of listeners. The function does not modify <i>bag</i>
+     * and return a new collection containing all listeners from <i>bag</i> except <i>listener</i>.
+     * If <i>bag</i> does not contain <i>listener</i>, the function returns <i>bag</i>.
+     *
+     * <p>For usage example, see {@link #addListener(Object bag, Object listener)}.
      *
      * @param listener Listener to remove from <i>bag</i>
      * @param bag Current collection of listeners.
-     * @return A new bag containing all listeners from <i>bag</i> except
-     *          <i>listener</i>.
+     * @return A new bag containing all listeners from <i>bag</i> except <i>listener</i>.
      * @see #addListener(Object bag, Object listener)
      * @see #getListener(Object bag, int index)
      */
-    public static Object removeListener(Object bag, Object listener)
-    {
+    public static Object removeListener(Object bag, Object listener) {
         if (listener == null) throw new IllegalArgumentException();
         if (listener instanceof Object[]) throw new IllegalArgumentException();
 
         if (bag == listener) {
             bag = null;
         } else if (bag instanceof Object[]) {
-            Object[] array = (Object[])bag;
+            Object[] array = (Object[]) bag;
             int L = array.length;
             // bag has at least 2 elements if it is array
             if (L < 2) throw new IllegalArgumentException();
@@ -268,10 +213,10 @@
     }
 
     /**
-     * Get listener at <i>index</i> position in <i>bag</i> or null if
-     * <i>index</i> equals to number of listeners in <i>bag</i>.
-     * <p>
-     * For usage example, see {@link #addListener(Object bag, Object listener)}.
+     * Get listener at <i>index</i> position in <i>bag</i> or null if <i>index</i> equals to number
+     * of listeners in <i>bag</i>.
+     *
+     * <p>For usage example, see {@link #addListener(Object bag, Object listener)}.
      *
      * @param bag Current collection of listeners.
      * @param index Index of the listener to access.
@@ -279,14 +224,11 @@
      * @see #addListener(Object bag, Object listener)
      * @see #removeListener(Object bag, Object listener)
      */
-    public static Object getListener(Object bag, int index)
-    {
+    public static Object getListener(Object bag, int index) {
         if (index == 0) {
-            if (bag == null)
-                return null;
-            if (!(bag instanceof Object[]))
-                return bag;
-            Object[] array = (Object[])bag;
+            if (bag == null) return null;
+            if (!(bag instanceof Object[])) return bag;
+            Object[] array = (Object[]) bag;
             // bag has at least 2 elements if it is array
             if (array.length < 2) throw new IllegalArgumentException();
             return array[0];
@@ -295,22 +237,20 @@
                 if (bag == null) throw new IllegalArgumentException();
                 return null;
             }
-            Object[] array = (Object[])bag;
+            Object[] array = (Object[]) bag;
             // the array access will check for index on its own
             return array[1];
         } else {
             // bag has to array
-            Object[] array = (Object[])bag;
+            Object[] array = (Object[]) bag;
             int L = array.length;
             if (L < 2) throw new IllegalArgumentException();
-            if (index == L)
-                return null;
+            if (index == L) return null;
             return array[index];
         }
     }
 
-    static Object initHash(Map<Object,Object> h, Object key, Object initialValue)
-    {
+    static Object initHash(Map<Object, Object> h, Object key, Object initialValue) {
         synchronized (h) {
             Object current = h.get(key);
             if (current == null) {
@@ -322,30 +262,25 @@
         return initialValue;
     }
 
-    private final static class ComplexKey
-    {
+    private static final class ComplexKey {
         private Object key1;
         private Object key2;
         private int hash;
 
-        ComplexKey(Object key1, Object key2)
-        {
+        ComplexKey(Object key1, Object key2) {
             this.key1 = key1;
             this.key2 = key2;
         }
 
         @Override
-        public boolean equals(Object anotherObj)
-        {
-            if (!(anotherObj instanceof ComplexKey))
-                return false;
-            ComplexKey another = (ComplexKey)anotherObj;
+        public boolean equals(Object anotherObj) {
+            if (!(anotherObj instanceof ComplexKey)) return false;
+            ComplexKey another = (ComplexKey) anotherObj;
             return key1.equals(another.key1) && key2.equals(another.key2);
         }
 
         @Override
-        public int hashCode()
-        {
+        public int hashCode() {
             if (hash == 0) {
                 hash = key1.hashCode() ^ key2.hashCode();
             }
@@ -353,48 +288,52 @@
         }
     }
 
-    public static Object makeHashKeyFromPair(Object key1, Object key2)
-    {
+    public static Object makeHashKeyFromPair(Object key1, Object key2) {
         if (key1 == null) throw new IllegalArgumentException();
         if (key2 == null) throw new IllegalArgumentException();
         return new ComplexKey(key1, key2);
     }
 
-    public static String readReader(Reader r)
-        throws IOException
-    {
-        char[] buffer = new char[512];
-        int cursor = 0;
-        for (;;) {
-            int n = r.read(buffer, cursor, buffer.length - cursor);
-            if (n < 0) { break; }
-            cursor += n;
-            if (cursor == buffer.length) {
-                char[] tmp = new char[buffer.length * 2];
-                System.arraycopy(buffer, 0, tmp, 0, cursor);
-                buffer = tmp;
+    public static String readReader(Reader reader) throws IOException {
+        try (BufferedReader in = new BufferedReader(reader)) {
+            char[] cbuf = new char[1024];
+            StringBuilder sb = new StringBuilder(1024);
+            int bytes_read;
+            while ((bytes_read = in.read(cbuf, 0, 1024)) != -1) {
+                sb.append(cbuf, 0, bytes_read);
             }
+            return sb.toString();
         }
-        return new String(buffer, 0, cursor);
     }
 
-    public static byte[] readStream(InputStream is, int initialBufferCapacity)
-        throws IOException
-    {
+    public static byte[] readStream(InputStream is, int initialBufferCapacity) throws IOException {
         if (initialBufferCapacity <= 0) {
             throw new IllegalArgumentException(
-                "Bad initialBufferCapacity: "+initialBufferCapacity);
+                    "Bad initialBufferCapacity: " + initialBufferCapacity);
         }
         byte[] buffer = new byte[initialBufferCapacity];
         int cursor = 0;
-        for (;;) {
+        for (; ; ) {
             int n = is.read(buffer, cursor, buffer.length - cursor);
-            if (n < 0) { break; }
+            if (n < 0) {
+                break;
+            }
             cursor += n;
             if (cursor == buffer.length) {
+                int readahead = -1;
+                if (cursor == initialBufferCapacity) {
+                    readahead = is.read();
+                    if (readahead < 0) { // Check for EOS
+                        return buffer;
+                    }
+                }
                 byte[] tmp = new byte[buffer.length * 2];
                 System.arraycopy(buffer, 0, tmp, 0, cursor);
                 buffer = tmp;
+                if (readahead != -1) {
+                    buffer[cursor++] = (byte) readahead;
+                    readahead = -1;
+                }
             }
         }
         if (cursor != buffer.length) {
@@ -406,14 +345,11 @@
     }
 
     /**
-     * Throws RuntimeException to indicate failed assertion.
-     * The function never returns and its return type is RuntimeException
-     * only to be able to write <tt>throw Kit.codeBug()</tt> if plain
-     * <tt>Kit.codeBug()</tt> triggers unreachable code error.
+     * Throws RuntimeException to indicate failed assertion. The function never returns and its
+     * return type is RuntimeException only to be able to write <code>throw Kit.codeBug()</code> if
+     * plain <code>Kit.codeBug()</code> triggers unreachable code error.
      */
-    public static RuntimeException codeBug()
-        throws RuntimeException
-    {
+    public static RuntimeException codeBug() throws RuntimeException {
         RuntimeException ex = new IllegalStateException("FAILED ASSERTION");
         // Print stack trace ASAP
         ex.printStackTrace(System.err);
@@ -421,14 +357,11 @@
     }
 
     /**
-     * Throws RuntimeException to indicate failed assertion.
-     * The function never returns and its return type is RuntimeException
-     * only to be able to write <tt>throw Kit.codeBug()</tt> if plain
-     * <tt>Kit.codeBug()</tt> triggers unreachable code error.
+     * Throws RuntimeException to indicate failed assertion. The function never returns and its
+     * return type is RuntimeException only to be able to write <code>throw Kit.codeBug()</code> if
+     * plain <code>Kit.codeBug()</code> triggers unreachable code error.
      */
-    public static RuntimeException codeBug(String msg)
-        throws RuntimeException
-    {
+    public static RuntimeException codeBug(String msg) throws RuntimeException {
         msg = "FAILED ASSERTION: " + msg;
         RuntimeException ex = new IllegalStateException(msg);
         // Print stack trace ASAP
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/LambdaConstructor.java rhino-1.7.14/src/org/mozilla/javascript/LambdaConstructor.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/LambdaConstructor.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/LambdaConstructor.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,195 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+/**
+ * This class implements a JavaScript function that may be used as a constructor by delegating to an
+ * interface that can be easily implemented as a lambda. The LambdaFunction class may be used to add
+ * functions to the prototype that are also implemented as lambdas.
+ *
+ * <p>In micro benchmarks (as of 2021) using this class to implement a built-in class is about 15%
+ * more efficient than using IdScriptableObject, and about 25% faster than using reflection via the
+ * ScriptableObject.defineClass() family of methods. Furthermore, it results in code that more
+ * directly maps to JavaScript idioms than either methods, it is much easier to implement than
+ * IdScriptableObject, and the lambda pattern makes it easier to maintain state in various ways that
+ * don't always map directly to the existing concepts.
+ */
+public class LambdaConstructor extends LambdaFunction {
+
+    private static final long serialVersionUID = 2691205302914111400L;
+
+    /** If this flag is set, the constructor may be invoked as an ordinary function */
+    public static final int CONSTRUCTOR_FUNCTION = 1;
+    /** If this flag is set, the constructor may be invoked using "new" */
+    public static final int CONSTRUCTOR_NEW = 1 << 1;
+    /** By default, the constructor may be invoked either way */
+    public static final int CONSTRUCTOR_DEFAULT = CONSTRUCTOR_FUNCTION | CONSTRUCTOR_NEW;
+
+    // Lambdas should not be serialized.
+    private final transient Constructable targetConstructor;
+    private final int flags;
+
+    /**
+     * Create a new function that may be used as a constructor. The new object will have the
+     * Function prototype and no parent. The caller is responsible for binding this object to the
+     * appropriate scope.
+     *
+     * @param scope scope of the calling context
+     * @param name name of the function
+     * @param length the arity of the function
+     * @param target an object that implements the function in Java. Since Constructable is a
+     *     single-function interface this will typically be implemented as a lambda.
+     */
+    public LambdaConstructor(Scriptable scope, String name, int length, Constructable target) {
+        super(scope, name, length, null);
+        this.targetConstructor = target;
+        this.flags = CONSTRUCTOR_DEFAULT;
+    }
+
+    /**
+     * Create a new function and control whether it may be invoked using new, as a function, or
+     * both.
+     */
+    public LambdaConstructor(
+            Scriptable scope, String name, int length, int flags, Constructable target) {
+        super(scope, name, length, null);
+        this.targetConstructor = target;
+        this.flags = flags;
+    }
+
+    @Override
+    public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        if ((flags & CONSTRUCTOR_FUNCTION) == 0) {
+            throw ScriptRuntime.typeErrorById("msg.constructor.no.function", getFunctionName());
+        }
+        return targetConstructor.construct(cx, scope, args);
+    }
+
+    @Override
+    public Scriptable construct(Context cx, Scriptable scope, Object[] args) {
+        if ((flags & CONSTRUCTOR_NEW) == 0) {
+            throw ScriptRuntime.typeErrorById("msg.no.new", getFunctionName());
+        }
+        Scriptable obj = targetConstructor.construct(cx, scope, args);
+        obj.setPrototype(getClassPrototype());
+        obj.setParentScope(scope);
+        return obj;
+    }
+
+    /**
+     * Define a function property on the prototype of the constructor using a LambdaFunction under
+     * the covers.
+     */
+    public void definePrototypeMethod(Scriptable scope, String name, int length, Callable target) {
+        LambdaFunction f = new LambdaFunction(scope, name, length, target);
+        ScriptableObject proto = getPrototypeScriptable();
+        proto.defineProperty(name, f, 0);
+    }
+
+    /**
+     * Define a function property on the prototype of the constructor using a LambdaFunction under
+     * the covers.
+     */
+    public void definePrototypeMethod(
+            Scriptable scope,
+            String name,
+            int length,
+            Callable target,
+            int attributes,
+            int propertyAttributes) {
+        LambdaFunction f = new LambdaFunction(scope, name, length, target);
+        f.setStandardPropertyAttributes(propertyAttributes);
+        ScriptableObject proto = getPrototypeScriptable();
+        proto.defineProperty(name, f, attributes);
+    }
+
+    /** Define a property that may be of any type on the prototype of this constructor. */
+    public void definePrototypeProperty(String name, Object value, int attributes) {
+        ScriptableObject proto = getPrototypeScriptable();
+        proto.defineProperty(name, value, attributes);
+    }
+
+    public void definePrototypeProperty(Symbol key, Object value, int attributes) {
+        ScriptableObject proto = getPrototypeScriptable();
+        proto.defineProperty(key, value, attributes);
+    }
+
+    /**
+     * Define a function property directly on the constructor that is implemented under the covers
+     * by a LambdaFunction.
+     *
+     * @param name the key to use to look up the new function property, and also the value to return
+     *     for the "name" property of the Function object
+     * @param length the value to return for the "length" property of the Function object
+     * @param target the target to call when the method is invoked
+     * @param attributes the attributes to set on the new property
+     */
+    public void defineConstructorMethod(
+            Scriptable scope, String name, int length, Callable target, int attributes) {
+        LambdaFunction f = new LambdaFunction(scope, name, length, target);
+        defineProperty(name, f, attributes);
+    }
+
+    /**
+     * Define a function property directly on the constructor that is implemented under the covers
+     * by a LambdaFunction.
+     *
+     * @param key the Symbol to use to look up the property
+     * @param name the value to return for the "name" property of the Function object
+     * @param length the value to return for the "length" property of the Function object
+     * @param target the target to call when the method is invoked
+     * @param attributes the attributes to set on the new property
+     */
+    public void defineConstructorMethod(
+            Scriptable scope,
+            Symbol key,
+            String name,
+            int length,
+            Callable target,
+            int attributes) {
+        LambdaFunction f = new LambdaFunction(scope, name, length, target);
+        defineProperty(key, f, attributes);
+    }
+
+    /**
+     * Define a function property directly on the constructor that is implemented under the covers
+     * by a LambdaFunction, and override the properties of its "name", "length", and "arity"
+     * properties.
+     */
+    public void defineConstructorMethod(
+            Scriptable scope,
+            String name,
+            int length,
+            Callable target,
+            int attributes,
+            int propertyAttributes) {
+        LambdaFunction f = new LambdaFunction(scope, name, length, target);
+        f.setStandardPropertyAttributes(propertyAttributes);
+        defineProperty(name, f, attributes);
+    }
+
+    /**
+     * A convenience method to convert JavaScript's "this" object into a target class and throw a
+     * TypeError if it does not match. This is useful for implementing lambda functions, as "this"
+     * in JavaScript doesn't necessarily map to an instance of the class.
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T convertThisObject(Scriptable thisObj, Class<T> targetClass) {
+        if (!targetClass.isInstance(thisObj)) {
+            throw ScriptRuntime.typeErrorById("msg.this.not.instance");
+        }
+        return (T) thisObj;
+    }
+
+    private ScriptableObject getPrototypeScriptable() {
+        Object prop = getPrototypeProperty();
+        if (!(prop instanceof ScriptableObject)) {
+            throw ScriptRuntime.typeError("Not properly a lambda constructor");
+        }
+        return (ScriptableObject) prop;
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/LambdaFunction.java rhino-1.7.14/src/org/mozilla/javascript/LambdaFunction.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/LambdaFunction.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/LambdaFunction.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,73 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+/**
+ * This class implements a single JavaScript function that has the prototype of the built-in
+ * Function class, and which is implemented using a single function that can easily be implemented
+ * using a lambda expression.
+ */
+public class LambdaFunction extends BaseFunction {
+
+    private static final long serialVersionUID = -8388132362854748293L;
+
+    // The target is expected to be a lambda -- lambdas should not be serialized.
+    private final transient Callable target;
+    private final String name;
+    private final int length;
+
+    /**
+     * Create a new function. The new object will have the Function prototype and no parent. The
+     * caller is responsible for binding this object to the appropriate scope.
+     *
+     * @param scope scope of the calling context
+     * @param name name of the function
+     * @param length the arity of the function
+     * @param target an object that implements the function in Java. Since Callable is a
+     *     single-function interface this will typically be implemented as a lambda.
+     */
+    public LambdaFunction(Scriptable scope, String name, int length, Callable target) {
+        this.target = target;
+        this.name = name;
+        this.length = length;
+        ScriptRuntime.setFunctionProtoAndParent(this, scope);
+        setupDefaultPrototype();
+    }
+
+    /** Create a new built-in function, with no name, and no default prototype. */
+    public LambdaFunction(Scriptable scope, int length, Callable target) {
+        this.target = target;
+        this.length = length;
+        this.name = "";
+        ScriptRuntime.setFunctionProtoAndParent(this, scope);
+    }
+
+    @Override
+    public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        return target.call(cx, scope, thisObj, args);
+    }
+
+    @Override
+    public Scriptable construct(Context cx, Scriptable scope, Object[] args) {
+        throw ScriptRuntime.typeErrorById("msg.no.new", getFunctionName());
+    }
+
+    @Override
+    public int getLength() {
+        return length;
+    }
+
+    @Override
+    public int getArity() {
+        return length;
+    }
+
+    @Override
+    public String getFunctionName() {
+        return name;
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/LambdaSlot.java rhino-1.7.14/src/org/mozilla/javascript/LambdaSlot.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/LambdaSlot.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/LambdaSlot.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,65 @@
+package org.mozilla.javascript;
+
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * This is a specialization of property access using some lambda functions. It behaves exactly like
+ * any other slot that has only a value, but instead of getting the value directly, it comes from
+ * calling the functions. This makes it different from GetterSlot, which lets the user see directly
+ * that there is a getter or a setter function involved. This makes this class useful for
+ * implementing properties that behave like any other JavaScript property but which are implemented
+ * using some native functionality without using reflection.
+ */
+public class LambdaSlot extends Slot {
+    private static final long serialVersionUID = -3046681698806493052L;
+
+    LambdaSlot(Slot oldSlot) {
+        super(oldSlot);
+    }
+
+    transient Supplier<Object> getter;
+    transient Consumer<Object> setter;
+
+    @Override
+    boolean isValueSlot() {
+        return false;
+    }
+
+    @Override
+    boolean isSetterSlot() {
+        return false;
+    }
+
+    @Override
+    ScriptableObject getPropertyDescriptor(Context cx, Scriptable scope) {
+        ScriptableObject desc = (ScriptableObject) cx.newObject(scope);
+        if (getter != null) {
+            desc.defineProperty("value", getter.get(), ScriptableObject.EMPTY);
+        } else {
+            desc.defineProperty("value", value, ScriptableObject.EMPTY);
+        }
+        desc.setCommonDescriptorProperties(getAttributes(), true);
+        return desc;
+    }
+
+    @Override
+    public boolean setValue(Object value, Scriptable owner, Scriptable start) {
+        if (setter != null) {
+            if (owner == start) {
+                setter.accept(value);
+                return true;
+            }
+            return false;
+        }
+        return super.setValue(value, owner, start);
+    }
+
+    @Override
+    public Object getValue(Scriptable start) {
+        if (getter != null) {
+            return getter.get();
+        }
+        return super.getValue(start);
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/LazilyLoadedCtor.java rhino-1.7.14/src/org/mozilla/javascript/LazilyLoadedCtor.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/LazilyLoadedCtor.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/LazilyLoadedCtor.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,7 +6,8 @@
 
 package org.mozilla.javascript;
 
-import java.lang.reflect.*;
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 
@@ -15,9 +16,8 @@
  *
  * <p> This improves startup time and average memory usage.
  */
-public final class LazilyLoadedCtor implements java.io.Serializable {
+public final class LazilyLoadedCtor implements Serializable {
     private static final long serialVersionUID = 1L;
-
     private static final int STATE_BEFORE_INIT = 0;
     private static final int STATE_INITIALIZING = 1;
     private static final int STATE_WITH_VALUE = 2;
@@ -85,16 +85,14 @@
         {
             return AccessController.doPrivileged(new PrivilegedAction<Object>()
             {
+                @Override
                 public Object run()
                 {
                     return buildValue0();
                 }
             });
         }
-        else
-        {
-            return buildValue0();
-        }
+        return buildValue0();
     }
 
     private Object buildValue0()
@@ -107,13 +105,11 @@
                 if (value != null) {
                     return value;
                 }
-                else {
-                    // cl has own static initializer which is expected
-                    // to set the property on its own.
-                    value = scope.get(propertyName, scope);
-                    if (value != Scriptable.NOT_FOUND)
-                        return value;
-                }
+                // cl has own static initializer which is expected
+                // to set the property on its own.
+                value = scope.get(propertyName, scope);
+                if (value != Scriptable.NOT_FOUND)
+                    return value;
             } catch (InvocationTargetException ex) {
                 Throwable target = ex.getTargetException();
                 if (target instanceof RuntimeException) {
@@ -129,7 +125,7 @@
     }
 
     @SuppressWarnings({"unchecked"})
-    private Class<? extends Scriptable> cast(Class<?> cl) {
+    private static Class<? extends Scriptable> cast(Class<?> cl) {
         return (Class<? extends Scriptable>)cl;
     }
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/LazyLoadSlot.java rhino-1.7.14/src/org/mozilla/javascript/LazyLoadSlot.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/LazyLoadSlot.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/LazyLoadSlot.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,25 @@
+package org.mozilla.javascript;
+
+/**
+ * This is a specialization of Slot to store values that are retrieved via calls to script
+ * functions. It's used to load built-in objects more efficiently.
+ */
+public class LazyLoadSlot extends Slot {
+    LazyLoadSlot(Slot oldSlot) {
+        super(oldSlot);
+    }
+
+    @Override
+    public Object getValue(Scriptable start) {
+        Object val = this.value;
+        if (val instanceof LazilyLoadedCtor) {
+            LazilyLoadedCtor initializer = (LazilyLoadedCtor) val;
+            try {
+                initializer.init();
+            } finally {
+                this.value = val = initializer.getValue();
+            }
+        }
+        return val;
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/MemberBox.java rhino-1.7.14/src/org/mozilla/javascript/MemberBox.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/MemberBox.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/MemberBox.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,93 +6,90 @@
 
 package org.mozilla.javascript;
 
-import java.lang.reflect.*;
-import java.io.*;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 
 /**
- * Wrappper class for Method and Constructor instances to cache
- * getParameterTypes() results, recover from IllegalAccessException
- * in some cases and provide serialization support.
+ * Wrapper class for Method and Constructor instances to cache getParameterTypes() results, recover
+ * from IllegalAccessException in some cases and provide serialization support.
  *
  * @author Igor Bukanov
  */
-
-final class MemberBox implements Serializable
-{
-    static final long serialVersionUID = 6358550398665688245L;
+final class MemberBox implements Serializable {
+    private static final long serialVersionUID = 6358550398665688245L;
 
     private transient Member memberObject;
     transient Class<?>[] argTypes;
-    transient Object delegateTo;
     transient boolean vararg;
 
+    transient Function asGetterFunction;
+    transient Function asSetterFunction;
+    transient Object delegateTo;
 
-    MemberBox(Method method)
-    {
+    MemberBox(Method method) {
         init(method);
     }
 
-    MemberBox(Constructor<?> constructor)
-    {
+    MemberBox(Constructor<?> constructor) {
         init(constructor);
     }
 
-    private void init(Method method)
-    {
+    private void init(Method method) {
         this.memberObject = method;
         this.argTypes = method.getParameterTypes();
-        this.vararg = VMBridge.instance.isVarArgs(method);
+        this.vararg = method.isVarArgs();
     }
 
-    private void init(Constructor<?> constructor)
-    {
+    private void init(Constructor<?> constructor) {
         this.memberObject = constructor;
         this.argTypes = constructor.getParameterTypes();
-        this.vararg = VMBridge.instance.isVarArgs(constructor);
+        this.vararg = constructor.isVarArgs();
     }
 
-    Method method()
-    {
-        return (Method)memberObject;
+    Method method() {
+        return (Method) memberObject;
     }
 
-    Constructor<?> ctor()
-    {
-        return (Constructor<?>)memberObject;
+    Constructor<?> ctor() {
+        return (Constructor<?>) memberObject;
     }
 
-    Member member()
-    {
+    Member member() {
         return memberObject;
     }
 
-    boolean isMethod()
-    {
+    boolean isMethod() {
         return memberObject instanceof Method;
     }
 
-    boolean isCtor()
-    {
+    boolean isCtor() {
         return memberObject instanceof Constructor;
     }
 
-    boolean isStatic()
-    {
+    boolean isStatic() {
         return Modifier.isStatic(memberObject.getModifiers());
     }
 
-    String getName()
-    {
+    boolean isPublic() {
+        return Modifier.isPublic(memberObject.getModifiers());
+    }
+
+    String getName() {
         return memberObject.getName();
     }
 
-    Class<?> getDeclaringClass()
-    {
+    Class<?> getDeclaringClass() {
         return memberObject.getDeclaringClass();
     }
 
-    String toJavaDeclaration()
-    {
+    String toJavaDeclaration() {
         StringBuilder sb = new StringBuilder();
         if (isMethod()) {
             Method method = method();
@@ -113,14 +110,97 @@
     }
 
     @Override
-    public String toString()
-    {
+    public String toString() {
         return memberObject.toString();
     }
 
-    Object invoke(Object target, Object[] args)
-    {
+    /** Function returned by calls to __lookupGetter__ */
+    Function asGetterFunction(final String name, final Scriptable scope) {
+        // Note: scope is the scriptable this function is related to; therefore this function
+        // is constant for this member box.
+        // Because of this we can cache the function in the attribute
+        if (asGetterFunction == null) {
+            asGetterFunction =
+                    new BaseFunction(scope, ScriptableObject.getFunctionPrototype(scope)) {
+                        @Override
+                        public Object call(
+                                Context cx,
+                                Scriptable callScope,
+                                Scriptable thisObj,
+                                Object[] originalArgs) {
+                            MemberBox nativeGetter = MemberBox.this;
+                            Object getterThis;
+                            Object[] args;
+                            if (nativeGetter.delegateTo == null) {
+                                getterThis = thisObj;
+                                args = ScriptRuntime.emptyArgs;
+                            } else {
+                                getterThis = nativeGetter.delegateTo;
+                                args = new Object[] {thisObj};
+                            }
+                            return nativeGetter.invoke(getterThis, args);
+                        }
+
+                        @Override
+                        public String getFunctionName() {
+                            return name;
+                        }
+                    };
+        }
+        return asGetterFunction;
+    }
+
+    /** Function returned by calls to __lookupSetter__ */
+    Function asSetterFunction(final String name, final Scriptable scope) {
+        // Note: scope is the scriptable this function is related to; therefore this function
+        // is constant for this member box.
+        // Because of this we can cache the function in the attribute
+        if (asSetterFunction == null) {
+            asSetterFunction =
+                    new BaseFunction(scope, ScriptableObject.getFunctionPrototype(scope)) {
+                        @Override
+                        public Object call(
+                                Context cx,
+                                Scriptable callScope,
+                                Scriptable thisObj,
+                                Object[] originalArgs) {
+                            MemberBox nativeSetter = MemberBox.this;
+                            Object setterThis;
+                            Object[] args;
+                            Object value =
+                                    originalArgs.length > 0 ? originalArgs[0] : Undefined.instance;
+                            if (nativeSetter.delegateTo == null) {
+                                setterThis = thisObj;
+                                args = new Object[] {value};
+                            } else {
+                                setterThis = nativeSetter.delegateTo;
+                                args = new Object[] {thisObj, value};
+                            }
+                            return nativeSetter.invoke(setterThis, args);
+                        }
+
+                        @Override
+                        public String getFunctionName() {
+                            return name;
+                        }
+                    };
+        }
+        return asSetterFunction;
+    }
+
+    Object invoke(Object target, Object[] args) {
         Method method = method();
+
+        // handle delegators
+        if (target instanceof Delegator) {
+            target = ((Delegator) target).getDelegee();
+        }
+        for (int i = 0; i < args.length; ++i) {
+            if (args[i] instanceof Delegator) {
+                args[i] = ((Delegator) args[i]).getDelegee();
+            }
+        }
+
         try {
             try {
                 return method.invoke(target, args);
@@ -143,16 +223,14 @@
             do {
                 e = ((InvocationTargetException) e).getTargetException();
             } while ((e instanceof InvocationTargetException));
-            if (e instanceof ContinuationPending)
-                throw (ContinuationPending) e;
+            if (e instanceof ContinuationPending) throw (ContinuationPending) e;
             throw Context.throwAsScriptRuntimeEx(e);
         } catch (Exception ex) {
             throw Context.throwAsScriptRuntimeEx(ex);
         }
     }
 
-    Object newInstance(Object[] args)
-    {
+    Object newInstance(Object[] args) {
         Constructor<?> ctor = ctor();
         try {
             try {
@@ -168,8 +246,7 @@
         }
     }
 
-    private static Method searchAccessibleMethod(Method method, Class<?>[] params)
-    {
+    private static Method searchAccessibleMethod(Method method, Class<?>[] params) {
         int modifiers = method.getModifiers();
         if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) {
             Class<?> c = method.getDeclaringClass();
@@ -182,23 +259,25 @@
                         try {
                             return intf.getMethod(name, params);
                         } catch (NoSuchMethodException ex) {
-                        } catch (SecurityException ex) {  }
+                        } catch (SecurityException ex) {
+                        }
                     }
                 }
-                for (;;) {
+                for (; ; ) {
                     c = c.getSuperclass();
-                    if (c == null) { break; }
+                    if (c == null) {
+                        break;
+                    }
                     if (Modifier.isPublic(c.getModifiers())) {
                         try {
                             Method m = c.getMethod(name, params);
                             int mModifiers = m.getModifiers();
-                            if (Modifier.isPublic(mModifiers)
-                                && !Modifier.isStatic(mModifiers))
-                            {
+                            if (Modifier.isPublic(mModifiers) && !Modifier.isStatic(mModifiers)) {
                                 return m;
                             }
                         } catch (NoSuchMethodException ex) {
-                        } catch (SecurityException ex) {  }
+                        } catch (SecurityException ex) {
+                        }
                     }
                 }
             }
@@ -206,21 +285,17 @@
         return null;
     }
 
-    private void readObject(ObjectInputStream in)
-        throws IOException, ClassNotFoundException
-    {
+    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
         in.defaultReadObject();
         Member member = readMember(in);
         if (member instanceof Method) {
-            init((Method)member);
+            init((Method) member);
         } else {
-            init((Constructor<?>)member);
+            init((Constructor<?>) member);
         }
     }
 
-    private void writeObject(ObjectOutputStream out)
-        throws IOException
-    {
+    private void writeObject(ObjectOutputStream out) throws IOException {
         out.defaultWriteObject();
         writeMember(out, memberObject);
     }
@@ -228,13 +303,10 @@
     /**
      * Writes a Constructor or Method object.
      *
-     * Methods and Constructors are not serializable, so we must serialize
-     * information about the class, the name, and the parameters and
-     * recreate upon deserialization.
+     * <p>Methods and Constructors are not serializable, so we must serialize information about the
+     * class, the name, and the parameters and recreate upon deserialization.
      */
-    private static void writeMember(ObjectOutputStream out, Member member)
-        throws IOException
-    {
+    private static void writeMember(ObjectOutputStream out, Member member) throws IOException {
         if (member == null) {
             out.writeBoolean(false);
             return;
@@ -252,14 +324,10 @@
         }
     }
 
-    /**
-     * Reads a Method or a Constructor from the stream.
-     */
+    /** Reads a Method or a Constructor from the stream. */
     private static Member readMember(ObjectInputStream in)
-        throws IOException, ClassNotFoundException
-    {
-        if (!in.readBoolean())
-            return null;
+            throws IOException, ClassNotFoundException {
+        if (!in.readBoolean()) return null;
         boolean isMethod = in.readBoolean();
         String name = (String) in.readObject();
         Class<?> declaring = (Class<?>) in.readObject();
@@ -267,9 +335,8 @@
         try {
             if (isMethod) {
                 return declaring.getMethod(name, parms);
-            } else {
-                return declaring.getConstructor(parms);
             }
+            return declaring.getConstructor(parms);
         } catch (NoSuchMethodException e) {
             throw new IOException("Cannot find member: " + e);
         }
@@ -290,15 +357,14 @@
     /**
      * Writes an array of parameter types to the stream.
      *
-     * Requires special handling because primitive types cannot be
-     * found upon deserialization by the default Java implementation.
+     * <p>Requires special handling because primitive types cannot be found upon deserialization by
+     * the default Java implementation.
      */
     private static void writeParameters(ObjectOutputStream out, Class<?>[] parms)
-        throws IOException
-    {
+            throws IOException {
         out.writeShort(parms.length);
-    outer:
-        for (int i=0; i < parms.length; i++) {
+        outer:
+        for (int i = 0; i < parms.length; i++) {
             Class<?> parm = parms[i];
             boolean primitive = parm.isPrimitive();
             out.writeBoolean(primitive);
@@ -306,25 +372,21 @@
                 out.writeObject(parm);
                 continue;
             }
-            for (int j=0; j < primitives.length; j++) {
+            for (int j = 0; j < primitives.length; j++) {
                 if (parm.equals(primitives[j])) {
                     out.writeByte(j);
                     continue outer;
                 }
             }
-            throw new IllegalArgumentException("Primitive " + parm +
-                                               " not found");
+            throw new IllegalArgumentException("Primitive " + parm + " not found");
         }
     }
 
-    /**
-     * Reads an array of parameter types from the stream.
-     */
+    /** Reads an array of parameter types from the stream. */
     private static Class<?>[] readParameters(ObjectInputStream in)
-        throws IOException, ClassNotFoundException
-    {
+            throws IOException, ClassNotFoundException {
         Class<?>[] result = new Class[in.readShort()];
-        for (int i=0; i < result.length; i++) {
+        for (int i = 0; i < result.length; i++) {
             if (!in.readBoolean()) {
                 result[i] = (Class<?>) in.readObject();
                 continue;
@@ -334,4 +396,3 @@
         return result;
     }
 }
-
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeArrayIterator.java rhino-1.7.14/src/org/mozilla/javascript/NativeArrayIterator.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeArrayIterator.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeArrayIterator.java	2022-01-06 22:57:21.000000000 +0100
@@ -7,9 +7,17 @@
 package org.mozilla.javascript;
 
 public final class NativeArrayIterator extends ES6Iterator {
+    public enum ARRAY_ITERATOR_TYPE {
+            ENTRIES,
+            KEYS,
+            VALUES
+    }
+
     private static final long serialVersionUID = 1L;
     private static final String ITERATOR_TAG = "ArrayIterator";
 
+    private ARRAY_ITERATOR_TYPE type;
+
     static void init(ScriptableObject scope, boolean sealed) {
         ES6Iterator.init(scope, sealed, new NativeArrayIterator(), ITERATOR_TAG);
     }
@@ -21,10 +29,11 @@
         super();
     }
 
-    public NativeArrayIterator(Scriptable scope, Scriptable arrayLike) {
-        super(scope);
+    public NativeArrayIterator(Scriptable scope, Scriptable arrayLike, ARRAY_ITERATOR_TYPE type) {
+        super(scope, ITERATOR_TAG);
         this.index = 0;
         this.arrayLike = arrayLike;
+        this.type = type;
     }
 
     @Override
@@ -39,10 +48,20 @@
 
     @Override
     protected Object nextValue(Context cx, Scriptable scope) {
-        Object value = arrayLike.get(index++, arrayLike);
-        if (value == ScriptableObject.NOT_FOUND) {
+        if (type == ARRAY_ITERATOR_TYPE.KEYS) {
+            return Integer.valueOf(index++);
+        }
+
+        Object value = arrayLike.get(index, arrayLike);
+        if (value == Scriptable.NOT_FOUND) {
             value = Undefined.instance;
         }
+
+        if (type == ARRAY_ITERATOR_TYPE.ENTRIES) {
+            value = cx.newArray(scope, new Object[] {Integer.valueOf(index), value});
+        }
+
+        index++;
         return value;
     }
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeArray.java rhino-1.7.14/src/org/mozilla/javascript/NativeArray.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeArray.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeArray.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,26 +6,30 @@
 
 package org.mozilla.javascript;
 
-import org.mozilla.javascript.regexp.NativeRegExp;
+import static org.mozilla.javascript.ScriptRuntimeES6.requireObjectCoercible;
 
+import java.io.Serializable;
+import java.util.AbstractList;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Comparator;
+import java.util.ConcurrentModificationException;
 import java.util.Iterator;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.NoSuchElementException;
-
-import static org.mozilla.javascript.ScriptRuntimeES6.requireObjectCoercible;
+import org.mozilla.javascript.regexp.NativeRegExp;
+import org.mozilla.javascript.xml.XMLObject;
 
 /**
  * This class implements the Array native object.
+ *
  * @author Norris Boyd
  * @author Mike McCabe
  */
-public class NativeArray extends IdScriptableObject implements List
-{
-    static final long serialVersionUID = 7331366857676127338L;
+public class NativeArray extends IdScriptableObject implements List {
+    private static final long serialVersionUID = 7331366857676127338L;
 
     /*
      * Optimization possibilities and open issues:
@@ -40,10 +44,9 @@
      */
 
     private static final Object ARRAY_TAG = "Array";
-    private static final Integer NEGATIVE_ONE = Integer.valueOf(-1);
+    private static final Long NEGATIVE_ONE = Long.valueOf(-1);
 
-    static void init(Scriptable scope, boolean sealed)
-    {
+    static void init(Scriptable scope, boolean sealed) {
         NativeArray obj = new NativeArray(0);
         obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
     }
@@ -56,39 +59,32 @@
         NativeArray.maximumInitialCapacity = maximumInitialCapacity;
     }
 
-    public NativeArray(long lengthArg)
-    {
+    public NativeArray(long lengthArg) {
         denseOnly = lengthArg <= maximumInitialCapacity;
         if (denseOnly) {
             int intLength = (int) lengthArg;
-            if (intLength < DEFAULT_INITIAL_CAPACITY)
-                intLength = DEFAULT_INITIAL_CAPACITY;
+            if (intLength < DEFAULT_INITIAL_CAPACITY) intLength = DEFAULT_INITIAL_CAPACITY;
             dense = new Object[intLength];
             Arrays.fill(dense, Scriptable.NOT_FOUND);
         }
         length = lengthArg;
     }
 
-    public NativeArray(Object[] array)
-    {
+    public NativeArray(Object[] array) {
         denseOnly = true;
         dense = array;
         length = array.length;
     }
 
     @Override
-    public String getClassName()
-    {
+    public String getClassName() {
         return "Array";
     }
 
-    private static final int
-        Id_length        =  1,
-        MAX_INSTANCE_ID  =  1;
+    private static final int Id_length = 1, MAX_INSTANCE_ID = 1;
 
     @Override
-    protected int getMaxInstanceId()
-    {
+    protected int getMaxInstanceId() {
         return MAX_INSTANCE_ID;
     }
 
@@ -100,8 +96,7 @@
     }
 
     @Override
-    protected int findInstanceIdInfo(String s)
-    {
+    protected int findInstanceIdInfo(String s) {
         if (s.equals("length")) {
             return instanceIdInfo(lengthAttr, Id_length);
         }
@@ -109,15 +104,15 @@
     }
 
     @Override
-    protected String getInstanceIdName(int id)
-    {
-        if (id == Id_length) { return "length"; }
+    protected String getInstanceIdName(int id) {
+        if (id == Id_length) {
+            return "length";
+        }
         return super.getInstanceIdName(id);
     }
 
     @Override
-    protected Object getInstanceIdValue(int id)
-    {
+    protected Object getInstanceIdValue(int id) {
         if (id == Id_length) {
             return ScriptRuntime.wrapNumber(length);
         }
@@ -125,67 +120,45 @@
     }
 
     @Override
-    protected void setInstanceIdValue(int id, Object value)
-    {
+    protected void setInstanceIdValue(int id, Object value) {
         if (id == Id_length) {
-            setLength(value); return;
+            setLength(value);
+            return;
         }
         super.setInstanceIdValue(id, value);
     }
 
     @Override
-    protected void fillConstructorProperties(IdFunctionObject ctor)
-    {
-        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_join,
-                "join", 1);
-        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_reverse,
-                "reverse", 0);
-        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_sort,
-                "sort", 1);
-        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_push,
-                "push", 1);
-        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_pop,
-                "pop", 0);
-        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_shift,
-                "shift", 0);
-        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_unshift,
-                "unshift", 1);
-        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_splice,
-                "splice", 2);
-        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_concat,
-                "concat", 1);
-        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_slice,
-                "slice", 2);
-        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_indexOf,
-                "indexOf", 1);
-        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_lastIndexOf,
-                "lastIndexOf", 1);
-        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_every,
-                "every", 1);
-        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_filter,
-                "filter", 1);
-        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_forEach,
-                "forEach", 1);
-        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_map,
-                "map", 1);
-        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_some,
-                "some", 1);
-        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_find,
-                "find", 1);
-        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_findIndex,
-                "findIndex", 1);
-        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_reduce,
-                "reduce", 1);
-        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_reduceRight,
-                "reduceRight", 1);
-        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_isArray,
-                "isArray", 1);
+    protected void fillConstructorProperties(IdFunctionObject ctor) {
+        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_join, "join", 1);
+        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_reverse, "reverse", 0);
+        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_sort, "sort", 1);
+        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_push, "push", 1);
+        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_pop, "pop", 0);
+        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_shift, "shift", 0);
+        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_unshift, "unshift", 1);
+        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_splice, "splice", 2);
+        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_concat, "concat", 1);
+        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_slice, "slice", 2);
+        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_indexOf, "indexOf", 1);
+        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_lastIndexOf, "lastIndexOf", 1);
+        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_every, "every", 1);
+        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_filter, "filter", 1);
+        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_forEach, "forEach", 1);
+        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_map, "map", 1);
+        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_some, "some", 1);
+        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_find, "find", 1);
+        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_findIndex, "findIndex", 1);
+        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_reduce, "reduce", 1);
+        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_reduceRight, "reduceRight", 1);
+        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_isArray, "isArray", 1);
+        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_of, "of", 0);
+        addIdFunctionProperty(ctor, ARRAY_TAG, ConstructorId_from, "from", 1);
         super.fillConstructorProperties(ctor);
     }
 
     @Override
-    protected void initPrototypeId(int id)
-    {
+    protected void initPrototypeId(int id) {
         if (id == SymbolId_iterator) {
             initPrototypeMethod(ARRAY_TAG, id, SymbolKey.ITERATOR, "[Symbol.iterator]", 0);
             return;
@@ -194,190 +167,325 @@
         String s, fnName = null;
         int arity;
         switch (id) {
-          case Id_constructor:    arity=1; s="constructor";    break;
-          case Id_toString:       arity=0; s="toString";       break;
-          case Id_toLocaleString: arity=0; s="toLocaleString"; break;
-          case Id_toSource:       arity=0; s="toSource";       break;
-          case Id_join:           arity=1; s="join";           break;
-          case Id_reverse:        arity=0; s="reverse";        break;
-          case Id_sort:           arity=1; s="sort";           break;
-          case Id_push:           arity=1; s="push";           break;
-          case Id_pop:            arity=0; s="pop";            break;
-          case Id_shift:          arity=0; s="shift";          break;
-          case Id_unshift:        arity=1; s="unshift";        break;
-          case Id_splice:         arity=2; s="splice";         break;
-          case Id_concat:         arity=1; s="concat";         break;
-          case Id_slice:          arity=2; s="slice";          break;
-          case Id_indexOf:        arity=1; s="indexOf";        break;
-          case Id_lastIndexOf:    arity=1; s="lastIndexOf";    break;
-          case Id_every:          arity=1; s="every";          break;
-          case Id_filter:         arity=1; s="filter";         break;
-          case Id_forEach:        arity=1; s="forEach";        break;
-          case Id_map:            arity=1; s="map";            break;
-          case Id_some:           arity=1; s="some";           break;
-          case Id_find:           arity=1; s="find";           break;
-          case Id_findIndex:      arity=1; s="findIndex";      break;
-          case Id_reduce:         arity=1; s="reduce";         break;
-          case Id_reduceRight:    arity=1; s="reduceRight";    break;
-          default: throw new IllegalArgumentException(String.valueOf(id));
+            case Id_constructor:
+                arity = 1;
+                s = "constructor";
+                break;
+            case Id_toString:
+                arity = 0;
+                s = "toString";
+                break;
+            case Id_toLocaleString:
+                arity = 0;
+                s = "toLocaleString";
+                break;
+            case Id_toSource:
+                arity = 0;
+                s = "toSource";
+                break;
+            case Id_join:
+                arity = 1;
+                s = "join";
+                break;
+            case Id_reverse:
+                arity = 0;
+                s = "reverse";
+                break;
+            case Id_sort:
+                arity = 1;
+                s = "sort";
+                break;
+            case Id_push:
+                arity = 1;
+                s = "push";
+                break;
+            case Id_pop:
+                arity = 0;
+                s = "pop";
+                break;
+            case Id_shift:
+                arity = 0;
+                s = "shift";
+                break;
+            case Id_unshift:
+                arity = 1;
+                s = "unshift";
+                break;
+            case Id_splice:
+                arity = 2;
+                s = "splice";
+                break;
+            case Id_concat:
+                arity = 1;
+                s = "concat";
+                break;
+            case Id_slice:
+                arity = 2;
+                s = "slice";
+                break;
+            case Id_indexOf:
+                arity = 1;
+                s = "indexOf";
+                break;
+            case Id_lastIndexOf:
+                arity = 1;
+                s = "lastIndexOf";
+                break;
+            case Id_every:
+                arity = 1;
+                s = "every";
+                break;
+            case Id_filter:
+                arity = 1;
+                s = "filter";
+                break;
+            case Id_forEach:
+                arity = 1;
+                s = "forEach";
+                break;
+            case Id_map:
+                arity = 1;
+                s = "map";
+                break;
+            case Id_some:
+                arity = 1;
+                s = "some";
+                break;
+            case Id_find:
+                arity = 1;
+                s = "find";
+                break;
+            case Id_findIndex:
+                arity = 1;
+                s = "findIndex";
+                break;
+            case Id_reduce:
+                arity = 1;
+                s = "reduce";
+                break;
+            case Id_reduceRight:
+                arity = 1;
+                s = "reduceRight";
+                break;
+            case Id_fill:
+                arity = 1;
+                s = "fill";
+                break;
+            case Id_keys:
+                arity = 0;
+                s = "keys";
+                break;
+            case Id_values:
+                arity = 0;
+                s = "values";
+                break;
+            case Id_entries:
+                arity = 0;
+                s = "entries";
+                break;
+            case Id_includes:
+                arity = 1;
+                s = "includes";
+                break;
+            case Id_copyWithin:
+                arity = 2;
+                s = "copyWithin";
+                break;
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
         }
 
         initPrototypeMethod(ARRAY_TAG, id, s, fnName, arity);
     }
 
     @Override
-    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
-                             Scriptable thisObj, Object[] args)
-    {
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         if (!f.hasTag(ARRAY_TAG)) {
             return super.execIdCall(f, cx, scope, thisObj, args);
         }
         int id = f.methodId();
-      again:
-        for (;;) {
+        again:
+        for (; ; ) {
             switch (id) {
-              case ConstructorId_join:
-              case ConstructorId_reverse:
-              case ConstructorId_sort:
-              case ConstructorId_push:
-              case ConstructorId_pop:
-              case ConstructorId_shift:
-              case ConstructorId_unshift:
-              case ConstructorId_splice:
-              case ConstructorId_concat:
-              case ConstructorId_slice:
-              case ConstructorId_indexOf:
-              case ConstructorId_lastIndexOf:
-              case ConstructorId_every:
-              case ConstructorId_filter:
-              case ConstructorId_forEach:
-              case ConstructorId_map:
-              case ConstructorId_some:
-              case ConstructorId_find:
-              case ConstructorId_findIndex:
-              case ConstructorId_reduce:
-              case ConstructorId_reduceRight: {
-                if (args.length > 0) {
-                    thisObj = ScriptRuntime.toObject(cx, scope, args[0]);
-                    Object[] newArgs = new Object[args.length-1];
-                    for (int i=0; i < newArgs.length; i++)
-                        newArgs[i] = args[i+1];
-                    args = newArgs;
-                }
-                id = -id;
-                continue again;
-              }
-
-              case ConstructorId_isArray:
-                return args.length > 0 && js_isArray(args[0]);
-
-              case Id_constructor: {
-                boolean inNewExpr = (thisObj == null);
-                if (!inNewExpr) {
-                    // IdFunctionObject.construct will set up parent, proto
-                    return f.construct(cx, scope, args);
-                }
-                return jsConstructor(cx, scope, args);
-              }
-
-              case Id_toString:
-                return toStringHelper(cx, scope, thisObj,
-                    cx.hasFeature(Context.FEATURE_TO_STRING_AS_SOURCE), false);
-
-              case Id_toLocaleString:
-                return toStringHelper(cx, scope, thisObj, false, true);
-
-              case Id_toSource:
-                return toStringHelper(cx, scope, thisObj, true, false);
-
-              case Id_join:
-                return js_join(cx, thisObj, args);
-
-              case Id_reverse:
-                return js_reverse(cx, thisObj, args);
-
-              case Id_sort:
-                return js_sort(cx, scope, thisObj, args);
-
-              case Id_push:
-                return js_push(cx, thisObj, args);
-
-              case Id_pop:
-                return js_pop(cx, thisObj, args);
-
-              case Id_shift:
-                return js_shift(cx, thisObj, args);
-
-              case Id_unshift:
-                return js_unshift(cx, thisObj, args);
-
-              case Id_splice:
-                return js_splice(cx, scope, thisObj, args);
-
-              case Id_concat:
-                return js_concat(cx, scope, thisObj, args);
-
-              case Id_slice:
-                return js_slice(cx, thisObj, args);
-
-              case Id_indexOf:
-                return js_indexOf(cx, thisObj, args);
-
-              case Id_lastIndexOf:
-                return js_lastIndexOf(cx, thisObj, args);
-
-              case Id_every:
-              case Id_filter:
-              case Id_forEach:
-              case Id_map:
-              case Id_some:
-              case Id_find:
-              case Id_findIndex:
-                return iterativeMethod(cx, f, scope, thisObj, args);
-              case Id_reduce:
-              case Id_reduceRight:
-                return reduceMethod(cx, id, scope, thisObj, args);
-
-              case SymbolId_iterator:
-                return new NativeArrayIterator(scope, thisObj);
-            }
-            throw new IllegalArgumentException("Array.prototype has no method: " + f.getFunctionName());
-        }
-    }
-
-    @Override
-    public Object get(int index, Scriptable start)
-    {
-        if (!denseOnly && isGetterOrSetter(null, index, false))
-            return super.get(index, start);
-        if (dense != null && 0 <= index && index < dense.length)
-            return dense[index];
+                case ConstructorId_join:
+                case ConstructorId_reverse:
+                case ConstructorId_sort:
+                case ConstructorId_push:
+                case ConstructorId_pop:
+                case ConstructorId_shift:
+                case ConstructorId_unshift:
+                case ConstructorId_splice:
+                case ConstructorId_concat:
+                case ConstructorId_slice:
+                case ConstructorId_indexOf:
+                case ConstructorId_lastIndexOf:
+                case ConstructorId_every:
+                case ConstructorId_filter:
+                case ConstructorId_forEach:
+                case ConstructorId_map:
+                case ConstructorId_some:
+                case ConstructorId_find:
+                case ConstructorId_findIndex:
+                case ConstructorId_reduce:
+                case ConstructorId_reduceRight:
+                    {
+                        // this is a small trick; we will handle all the ConstructorId_xxx calls
+                        // the same way the object calls are processed
+                        // so we adjust the args, inverting the id and
+                        // restarting the method selection
+                        // Attention: the implementations have to be aware of this
+                        if (args.length > 0) {
+                            thisObj = ScriptRuntime.toObject(cx, scope, args[0]);
+                            Object[] newArgs = new Object[args.length - 1];
+                            for (int i = 0; i < newArgs.length; i++) newArgs[i] = args[i + 1];
+                            args = newArgs;
+                        }
+                        id = -id;
+                        continue again;
+                    }
+
+                case ConstructorId_isArray:
+                    return Boolean.valueOf(args.length > 0 && js_isArray(args[0]));
+
+                case ConstructorId_of:
+                    {
+                        return js_of(cx, scope, thisObj, args);
+                    }
+
+                case ConstructorId_from:
+                    {
+                        return js_from(cx, scope, thisObj, args);
+                    }
+
+                case Id_constructor:
+                    {
+                        boolean inNewExpr = (thisObj == null);
+                        if (!inNewExpr) {
+                            // IdFunctionObject.construct will set up parent, proto
+                            return f.construct(cx, scope, args);
+                        }
+                        return jsConstructor(cx, scope, args);
+                    }
+
+                case Id_toString:
+                    return toStringHelper(
+                            cx,
+                            scope,
+                            thisObj,
+                            cx.hasFeature(Context.FEATURE_TO_STRING_AS_SOURCE),
+                            false);
+
+                case Id_toLocaleString:
+                    return toStringHelper(cx, scope, thisObj, false, true);
+
+                case Id_toSource:
+                    return toStringHelper(cx, scope, thisObj, true, false);
+
+                case Id_join:
+                    return js_join(cx, scope, thisObj, args);
+
+                case Id_reverse:
+                    return js_reverse(cx, scope, thisObj, args);
+
+                case Id_sort:
+                    return js_sort(cx, scope, thisObj, args);
+
+                case Id_push:
+                    return js_push(cx, scope, thisObj, args);
+
+                case Id_pop:
+                    return js_pop(cx, scope, thisObj, args);
+
+                case Id_shift:
+                    return js_shift(cx, scope, thisObj, args);
+
+                case Id_unshift:
+                    return js_unshift(cx, scope, thisObj, args);
+
+                case Id_splice:
+                    return js_splice(cx, scope, thisObj, args);
+
+                case Id_concat:
+                    return js_concat(cx, scope, thisObj, args);
+
+                case Id_slice:
+                    return js_slice(cx, scope, thisObj, args);
+
+                case Id_indexOf:
+                    return js_indexOf(cx, scope, thisObj, args);
+
+                case Id_lastIndexOf:
+                    return js_lastIndexOf(cx, scope, thisObj, args);
+
+                case Id_includes:
+                    return js_includes(cx, scope, thisObj, args);
+
+                case Id_fill:
+                    return js_fill(cx, scope, thisObj, args);
+
+                case Id_copyWithin:
+                    return js_copyWithin(cx, scope, thisObj, args);
+
+                case Id_every:
+                case Id_filter:
+                case Id_forEach:
+                case Id_map:
+                case Id_some:
+                case Id_find:
+                case Id_findIndex:
+                    return iterativeMethod(cx, f, scope, thisObj, args);
+                case Id_reduce:
+                case Id_reduceRight:
+                    return reduceMethod(cx, id, scope, thisObj, args);
+
+                case Id_keys:
+                    thisObj = ScriptRuntime.toObject(cx, scope, thisObj);
+                    return new NativeArrayIterator(
+                            scope, thisObj, NativeArrayIterator.ARRAY_ITERATOR_TYPE.KEYS);
+
+                case Id_entries:
+                    thisObj = ScriptRuntime.toObject(cx, scope, thisObj);
+                    return new NativeArrayIterator(
+                            scope, thisObj, NativeArrayIterator.ARRAY_ITERATOR_TYPE.ENTRIES);
+
+                case Id_values:
+                case SymbolId_iterator:
+                    thisObj = ScriptRuntime.toObject(cx, scope, thisObj);
+                    return new NativeArrayIterator(
+                            scope, thisObj, NativeArrayIterator.ARRAY_ITERATOR_TYPE.VALUES);
+            }
+            throw new IllegalArgumentException(
+                    "Array.prototype has no method: " + f.getFunctionName());
+        }
+    }
+
+    @Override
+    public Object get(int index, Scriptable start) {
+        if (!denseOnly && isGetterOrSetter(null, index, false)) return super.get(index, start);
+        if (dense != null && 0 <= index && index < dense.length) return dense[index];
         return super.get(index, start);
     }
 
     @Override
-    public boolean has(int index, Scriptable start)
-    {
-        if (!denseOnly && isGetterOrSetter(null, index, false))
-            return super.has(index, start);
-        if (dense != null && 0 <= index && index < dense.length)
-            return dense[index] != NOT_FOUND;
+    public boolean has(int index, Scriptable start) {
+        if (!denseOnly && isGetterOrSetter(null, index, false)) return super.has(index, start);
+        if (dense != null && 0 <= index && index < dense.length) return dense[index] != NOT_FOUND;
         return super.has(index, start);
     }
 
     private static long toArrayIndex(Object id) {
         if (id instanceof String) {
-            return toArrayIndex((String)id);
+            return toArrayIndex((String) id);
         } else if (id instanceof Number) {
-            return toArrayIndex(((Number)id).doubleValue());
+            return toArrayIndex(((Number) id).doubleValue());
         }
         return -1;
     }
 
     // if id is an array index (ECMA 15.4.0), return the number,
     // otherwise return -1L
-    private static long toArrayIndex(String id)
-    {
+    private static long toArrayIndex(String id) {
         long index = toArrayIndex(ScriptRuntime.toNumber(id));
         // Assume that ScriptRuntime.toString(index) is the same
         // as java.lang.Long.toString(index) for long
@@ -388,7 +496,7 @@
     }
 
     private static long toArrayIndex(double d) {
-        if (d == d) {
+        if (!Double.isNaN(d)) {
             long index = ScriptRuntime.toUint32(d);
             if (index == d && index != 4294967295L) {
                 return index;
@@ -398,59 +506,61 @@
     }
 
     private static int toDenseIndex(Object id) {
-      long index = toArrayIndex(id);
-      return 0 <= index && index < Integer.MAX_VALUE ? (int) index : -1;
+        long index = toArrayIndex(id);
+        return 0 <= index && index < Integer.MAX_VALUE ? (int) index : -1;
     }
 
     @Override
-    public void put(String id, Scriptable start, Object value)
-    {
+    public void put(String id, Scriptable start, Object value) {
         super.put(id, start, value);
         if (start == this) {
             // If the object is sealed, super will throw exception
             long index = toArrayIndex(id);
             if (index >= length) {
                 length = index + 1;
+                modCount++;
                 denseOnly = false;
             }
         }
     }
 
-    private boolean ensureCapacity(int capacity)
-    {
+    private boolean ensureCapacity(int capacity) {
         if (capacity > dense.length) {
             if (capacity > MAX_PRE_GROW_SIZE) {
                 denseOnly = false;
                 return false;
             }
-            capacity = Math.max(capacity, (int)(dense.length * GROW_FACTOR));
+            capacity = Math.max(capacity, (int) (dense.length * GROW_FACTOR));
             Object[] newDense = new Object[capacity];
             System.arraycopy(dense, 0, newDense, 0, dense.length);
-            Arrays.fill(newDense, dense.length, newDense.length,
-                        Scriptable.NOT_FOUND);
+            Arrays.fill(newDense, dense.length, newDense.length, Scriptable.NOT_FOUND);
             dense = newDense;
         }
         return true;
     }
 
     @Override
-    public void put(int index, Scriptable start, Object value)
-    {
-        if (start == this && !isSealed() && dense != null && 0 <= index &&
-            (denseOnly || !isGetterOrSetter(null, index, true)))
-        {
+    public void put(int index, Scriptable start, Object value) {
+        if (start == this
+                && !isSealed()
+                && dense != null
+                && 0 <= index
+                && (denseOnly || !isGetterOrSetter(null, index, true))) {
             if (!isExtensible() && this.length <= index) {
                 return;
             } else if (index < dense.length) {
                 dense[index] = value;
-                if (this.length <= index)
-                    this.length = (long)index + 1;
+                if (this.length <= index) {
+                    this.length = (long) index + 1;
+                    this.modCount++;
+                }
                 return;
-            } else if (denseOnly && index < dense.length * GROW_FACTOR &&
-                       ensureCapacity(index+1))
-            {
+            } else if (denseOnly
+                    && index < dense.length * GROW_FACTOR
+                    && ensureCapacity(index + 1)) {
                 dense[index] = value;
-                this.length = (long)index + 1;
+                this.length = (long) index + 1;
+                this.modCount++;
                 return;
             } else {
                 denseOnly = false;
@@ -461,17 +571,19 @@
             // only set the array length if given an array index (ECMA 15.4.0)
             if (this.length <= index) {
                 // avoid overflowing index!
-                this.length = (long)index + 1;
+                this.length = (long) index + 1;
+                this.modCount++;
             }
         }
     }
 
     @Override
-    public void delete(int index)
-    {
-        if (dense != null && 0 <= index && index < dense.length &&
-            !isSealed() && (denseOnly || !isGetterOrSetter(null, index, true)))
-        {
+    public void delete(int index) {
+        if (dense != null
+                && 0 <= index
+                && index < dense.length
+                && !isSealed()
+                && (denseOnly || !isGetterOrSetter(null, index, true))) {
             dense[index] = NOT_FOUND;
         } else {
             super.delete(index);
@@ -479,16 +591,19 @@
     }
 
     @Override
-    public Object[] getIds(boolean nonEnumerable, boolean getSymbols)
-    {
+    public Object[] getIds(boolean nonEnumerable, boolean getSymbols) {
         Object[] superIds = super.getIds(nonEnumerable, getSymbols);
-        if (dense == null) { return superIds; }
+        if (dense == null) {
+            return superIds;
+        }
         int N = dense.length;
         long currentLength = length;
         if (N > currentLength) {
-            N = (int)currentLength;
+            N = (int) currentLength;
+        }
+        if (N == 0) {
+            return superIds;
         }
-        if (N == 0) { return superIds; }
         int superLength = superIds.length;
         Object[] ids = new Object[N + superLength];
 
@@ -510,45 +625,43 @@
         return ids;
     }
 
-    public Integer[] getIndexIds() {
-      Object[] ids = getIds();
-      java.util.List<Integer> indices = new java.util.ArrayList<Integer>(ids.length);
-      for (Object id : ids) {
-        int int32Id = ScriptRuntime.toInt32(id);
-        if (int32Id >= 0 && ScriptRuntime.toString(int32Id).equals(ScriptRuntime.toString(id))) {
-          indices.add(int32Id);
+    public List<Integer> getIndexIds() {
+        Object[] ids = getIds();
+        List<Integer> indices = new ArrayList<Integer>(ids.length);
+        for (Object id : ids) {
+            int int32Id = ScriptRuntime.toInt32(id);
+            if (int32Id >= 0
+                    && ScriptRuntime.toString(int32Id).equals(ScriptRuntime.toString(id))) {
+                indices.add(Integer.valueOf(int32Id));
+            }
         }
-      }
-      return indices.toArray(new Integer[indices.size()]);
+        return indices;
     }
 
     @Override
-    public Object getDefaultValue(Class<?> hint)
-    {
+    public Object getDefaultValue(Class<?> hint) {
         if (hint == ScriptRuntime.NumberClass) {
             Context cx = Context.getContext();
-            if (cx.getLanguageVersion() == Context.VERSION_1_2)
-                return Long.valueOf(length);
+            if (cx.getLanguageVersion() == Context.VERSION_1_2) return Long.valueOf(length);
         }
         return super.getDefaultValue(hint);
     }
 
     private ScriptableObject defaultIndexPropertyDescriptor(Object value) {
-      Scriptable scope = getParentScope();
-      if (scope == null) scope = this;
-      ScriptableObject desc = new NativeObject();
-      ScriptRuntime.setBuiltinProtoAndParent(desc, scope, TopLevel.Builtins.Object);
-      desc.defineProperty("value", value, EMPTY);
-      desc.defineProperty("writable", true, EMPTY);
-      desc.defineProperty("enumerable", true, EMPTY);
-      desc.defineProperty("configurable", true, EMPTY);
-      return desc;
+        Scriptable scope = getParentScope();
+        if (scope == null) scope = this;
+        ScriptableObject desc = new NativeObject();
+        ScriptRuntime.setBuiltinProtoAndParent(desc, scope, TopLevel.Builtins.Object);
+        desc.defineProperty("value", value, EMPTY);
+        desc.defineProperty("writable", Boolean.TRUE, EMPTY);
+        desc.defineProperty("enumerable", Boolean.TRUE, EMPTY);
+        desc.defineProperty("configurable", Boolean.TRUE, EMPTY);
+        return desc;
     }
 
     @Override
     public int getAttributes(int index) {
-        if (dense != null && index >= 0 && index < dense.length
-                && dense[index] != NOT_FOUND) {
+        if (dense != null && index >= 0 && index < dense.length && dense[index] != NOT_FOUND) {
             return EMPTY;
         }
         return super.getAttributes(index);
@@ -556,64 +669,170 @@
 
     @Override
     protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) {
-      if (dense != null) {
-        int index = toDenseIndex(id);
-        if (0 <= index && index < dense.length && dense[index] != NOT_FOUND) {
-          Object value = dense[index];
-          return defaultIndexPropertyDescriptor(value);
-        }
-      }
-      return super.getOwnPropertyDescriptor(cx, id);
+        if (dense != null) {
+            int index = toDenseIndex(id);
+            if (0 <= index && index < dense.length && dense[index] != NOT_FOUND) {
+                Object value = dense[index];
+                return defaultIndexPropertyDescriptor(value);
+            }
+        }
+        return super.getOwnPropertyDescriptor(cx, id);
     }
 
     @Override
-    protected void defineOwnProperty(Context cx, Object id,
-                                     ScriptableObject desc,
-                                     boolean checkValid) {
-      if (dense != null) {
-        Object[] values = dense;
-        dense = null;
-        denseOnly = false;
-        for (int i = 0; i < values.length; i++) {
-          if (values[i] != NOT_FOUND) {
-            put(i, this, values[i]);
-          }
-        }
-      }
-      long index = toArrayIndex(id);
-      if (index >= length) {
-        length = index + 1;
-      }
-      super.defineOwnProperty(cx, id, desc, checkValid);
+    protected void defineOwnProperty(
+            Context cx, Object id, ScriptableObject desc, boolean checkValid) {
+        long index = toArrayIndex(id);
+        if (index >= length) {
+            length = index + 1;
+            modCount++;
+        }
+
+        if (index != -1 && dense != null) {
+            Object[] values = dense;
+            dense = null;
+            denseOnly = false;
+            for (int i = 0; i < values.length; i++) {
+                if (values[i] != NOT_FOUND) {
+                    if (!isExtensible()) {
+                        // Force creating a slot, before calling .put(...) on the next line, which
+                        // would otherwise fail on a array on which preventExtensions() has been
+                        // called
+                        setAttributes(i, 0);
+                    }
+                    put(i, this, values[i]);
+                }
+            }
+        }
+
+        super.defineOwnProperty(cx, id, desc, checkValid);
+
+        if (id instanceof String && ((String) id).equals("length")) {
+            lengthAttr =
+                    getAttributes("length"); // Update cached attributes value for length property
+        }
     }
 
-    /**
-     * See ECMA 15.4.1,2
-     */
-    private static Object jsConstructor(Context cx, Scriptable scope,
-                                        Object[] args)
-    {
-        if (args.length == 0)
-            return new NativeArray(0);
+    /** See ECMA 15.4.1,2 */
+    private static Object jsConstructor(Context cx, Scriptable scope, Object[] args) {
+        if (args.length == 0) return new NativeArray(0);
 
         // Only use 1 arg as first element for version 1.2; for
         // any other version (including 1.3) follow ECMA and use it as
         // a length.
         if (cx.getLanguageVersion() == Context.VERSION_1_2) {
             return new NativeArray(args);
-        } else {
-            Object arg0 = args[0];
-            if (args.length > 1 || !(arg0 instanceof Number)) {
-                return new NativeArray(args);
-            } else {
-                long len = ScriptRuntime.toUint32(arg0);
-                if (len != ((Number)arg0).doubleValue()) {
-                    String msg = ScriptRuntime.getMessage0("msg.arraylength.bad");
-                    throw ScriptRuntime.constructError("RangeError", msg);
+        }
+        Object arg0 = args[0];
+        if (args.length > 1 || !(arg0 instanceof Number)) {
+            return new NativeArray(args);
+        }
+        long len = ScriptRuntime.toUint32(arg0);
+        if (len != ((Number) arg0).doubleValue()) {
+            String msg = ScriptRuntime.getMessageById("msg.arraylength.bad");
+            throw ScriptRuntime.rangeError(msg);
+        }
+        return new NativeArray(len);
+    }
+
+    private static Scriptable callConstructorOrCreateArray(
+            Context cx, Scriptable scope, Scriptable arg, long length, boolean lengthAlways) {
+        Scriptable result = null;
+
+        if (arg instanceof Function) {
+            try {
+                final Object[] args =
+                        (lengthAlways || (length > 0))
+                                ? new Object[] {Long.valueOf(length)}
+                                : ScriptRuntime.emptyArgs;
+                result = ((Function) arg).construct(cx, scope, args);
+            } catch (EcmaError ee) {
+                if (!"TypeError".equals(ee.getName())) {
+                    throw ee;
                 }
-                return new NativeArray(len);
+                // If we get here then it is likely that the function we called is not really
+                // a constructor. Unfortunately there's no better way to tell in Rhino right now.
             }
         }
+
+        if (result == null) {
+            // "length" below is really a hint so don't worry if it's really large
+            result = cx.newArray(scope, (length > Integer.MAX_VALUE) ? 0 : (int) length);
+        }
+
+        return result;
+    }
+
+    private static Object js_from(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        final Scriptable items =
+                ScriptRuntime.toObject(scope, (args.length >= 1) ? args[0] : Undefined.instance);
+        Object mapArg = (args.length >= 2) ? args[1] : Undefined.instance;
+        Scriptable thisArg = Undefined.SCRIPTABLE_UNDEFINED;
+        final boolean mapping = !Undefined.isUndefined(mapArg);
+        Function mapFn = null;
+
+        if (mapping) {
+            if (!(mapArg instanceof Function)) {
+                throw ScriptRuntime.typeErrorById("msg.map.function.not");
+            }
+            mapFn = (Function) mapArg;
+            if (args.length >= 3) {
+                thisArg = ensureScriptable(args[2]);
+            }
+        }
+
+        Object iteratorProp = ScriptableObject.getProperty(items, SymbolKey.ITERATOR);
+        if (!(items instanceof NativeArray)
+                && (iteratorProp != Scriptable.NOT_FOUND)
+                && !Undefined.isUndefined(iteratorProp)) {
+            final Object iterator = ScriptRuntime.callIterator(items, cx, scope);
+            if (!Undefined.isUndefined(iterator)) {
+                final Scriptable result =
+                        callConstructorOrCreateArray(cx, scope, thisObj, 0, false);
+                long k = 0;
+                try (IteratorLikeIterable it = new IteratorLikeIterable(cx, scope, iterator)) {
+                    for (Object temp : it) {
+                        if (mapping) {
+                            temp =
+                                    mapFn.call(
+                                            cx,
+                                            scope,
+                                            thisArg,
+                                            new Object[] {temp, Long.valueOf(k)});
+                        }
+                        defineElem(cx, result, k, temp);
+                        k++;
+                    }
+                }
+                setLengthProperty(cx, result, k);
+                return result;
+            }
+        }
+
+        final long length = getLengthProperty(cx, items);
+        final Scriptable result = callConstructorOrCreateArray(cx, scope, thisObj, length, true);
+        for (long k = 0; k < length; k++) {
+            Object temp = getElem(cx, items, k);
+            if (mapping) {
+                temp = mapFn.call(cx, scope, thisArg, new Object[] {temp, Long.valueOf(k)});
+            }
+            defineElem(cx, result, k, temp);
+        }
+
+        setLengthProperty(cx, result, length);
+        return result;
+    }
+
+    private static Object js_of(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        final Scriptable result =
+                callConstructorOrCreateArray(cx, scope, thisObj, args.length, true);
+
+        for (int i = 0; i < args.length; i++) {
+            defineElem(cx, result, i, args[i]);
+        }
+        setLengthProperty(cx, result, args.length);
+
+        return result;
     }
 
     public long getLength() {
@@ -627,17 +846,16 @@
     }
 
     /**
-     * Change the value of the internal flag that determines whether all
-     * storage is handed by a dense backing array rather than an associative
-     * store.
+     * Change the value of the internal flag that determines whether all storage is handed by a
+     * dense backing array rather than an associative store.
+     *
      * @param denseOnly new value for denseOnly flag
-     * @throws IllegalArgumentException if an attempt is made to enable
-     *   denseOnly after it was disabled; NativeArray code is not written
-     *   to handle switching back to a dense representation
+     * @throws IllegalArgumentException if an attempt is made to enable denseOnly after it was
+     *     disabled; NativeArray code is not written to handle switching back to a dense
+     *     representation
      */
     void setDenseOnly(boolean denseOnly) {
-        if (denseOnly && !this.denseOnly)
-            throw new IllegalArgumentException();
+        if (denseOnly && !this.denseOnly) throw new IllegalArgumentException();
         this.denseOnly = denseOnly;
     }
 
@@ -655,8 +873,8 @@
         double d = ScriptRuntime.toNumber(val);
         long longVal = ScriptRuntime.toUint32(d);
         if (longVal != d) {
-            String msg = ScriptRuntime.getMessage0("msg.arraylength.bad");
-            throw ScriptRuntime.constructError("RangeError", msg);
+            String msg = ScriptRuntime.getMessageById("msg.arraylength.bad");
+            throw ScriptRuntime.rangeError(msg);
         }
 
         if (denseOnly) {
@@ -664,12 +882,13 @@
                 // downcast okay because denseOnly
                 Arrays.fill(dense, (int) longVal, dense.length, NOT_FOUND);
                 length = longVal;
+                modCount++;
                 return;
-            } else if (longVal < MAX_PRE_GROW_SIZE &&
-                       longVal < (length * GROW_FACTOR) &&
-                       ensureCapacity((int)longVal))
-            {
+            } else if (longVal < MAX_PRE_GROW_SIZE
+                    && longVal < (length * GROW_FACTOR)
+                    && ensureCapacity((int) longVal)) {
                 length = longVal;
+                modCount++;
                 return;
             } else {
                 denseOnly = false;
@@ -680,18 +899,16 @@
             if (length - longVal > 0x1000) {
                 // assume that the representation is sparse
                 Object[] e = getIds(); // will only find in object itself
-                for (int i=0; i < e.length; i++) {
+                for (int i = 0; i < e.length; i++) {
                     Object id = e[i];
                     if (id instanceof String) {
                         // > MAXINT will appear as string
-                        String strId = (String)id;
+                        String strId = (String) id;
                         long index = toArrayIndex(strId);
-                        if (index >= longVal)
-                            delete(strId);
+                        if (index >= longVal) delete(strId);
                     } else {
-                        int index = ((Integer)id).intValue();
-                        if (index >= longVal)
-                            delete(index);
+                        int index = ((Integer) id).intValue();
+                        if (index >= longVal) delete(index);
                     }
                 }
             } else {
@@ -702,6 +919,7 @@
             }
         }
         length = longVal;
+        modCount++;
     }
 
     /* Support for generic Array-ish objects.  Most of the Array
@@ -711,23 +929,37 @@
      * or its value is not convertible to a number.
      */
     static long getLengthProperty(Context cx, Scriptable obj) {
-        // These will both give numeric lengths within Uint32 range.
+        // These will give numeric lengths within Uint32 range.
         if (obj instanceof NativeString) {
-            return ((NativeString)obj).getLength();
-        } else if (obj instanceof NativeArray) {
-            return ((NativeArray)obj).getLength();
+            return ((NativeString) obj).getLength();
+        }
+        if (obj instanceof NativeArray) {
+            return ((NativeArray) obj).getLength();
+        }
+        if (obj instanceof XMLObject) {
+            Callable lengthFunc = (Callable) ((XMLObject) obj).get("length", obj);
+            return ((Number) lengthFunc.call(cx, obj, obj, ScriptRuntime.emptyArgs)).longValue();
         }
+
         Object len = ScriptableObject.getProperty(obj, "length");
         if (len == Scriptable.NOT_FOUND) {
             // toUint32(undefined) == 0
             return 0;
         }
-        return ScriptRuntime.toUint32(len);
+
+        double doubleLen = ScriptRuntime.toNumber(len);
+
+        // ToLength
+        if (doubleLen > NativeNumber.MAX_SAFE_INTEGER) {
+            return (long) NativeNumber.MAX_SAFE_INTEGER;
+        }
+        if (doubleLen < 0) {
+            return 0;
+        }
+        return (long) doubleLen;
     }
 
-    private static Object setLengthProperty(Context cx, Scriptable target,
-                                            long length)
-    {
+    private static Object setLengthProperty(Context cx, Scriptable target, long length) {
         Object len = ScriptRuntime.wrapNumber(length);
         ScriptableObject.putProperty(target, "length", len);
         return len;
@@ -739,13 +971,15 @@
      * functions... though this is probably premature optimization.
      */
     private static void deleteElem(Scriptable target, long index) {
-        int i = (int)index;
-        if (i == index) { target.delete(i); }
-        else { target.delete(Long.toString(index)); }
+        int i = (int) index;
+        if (i == index) {
+            target.delete(i);
+        } else {
+            target.delete(Long.toString(index));
+        }
     }
 
-    private static Object getElem(Context cx, Scriptable target, long index)
-    {
+    private static Object getElem(Context cx, Scriptable target, long index) {
         Object elem = getRawElem(target, index);
         return (elem != Scriptable.NOT_FOUND ? elem : Undefined.instance);
     }
@@ -754,36 +988,30 @@
     private static Object getRawElem(Scriptable target, long index) {
         if (index > Integer.MAX_VALUE) {
             return ScriptableObject.getProperty(target, Long.toString(index));
-        } else {
-            return ScriptableObject.getProperty(target, (int)index);
         }
+        return ScriptableObject.getProperty(target, (int) index);
     }
 
-    private static void defineElem(Context cx, Scriptable target, long index,
-                                   Object value)
-    {
+    private static void defineElem(Context cx, Scriptable target, long index, Object value) {
         if (index > Integer.MAX_VALUE) {
             String id = Long.toString(index);
             target.put(id, target, value);
         } else {
-            target.put((int)index, target, value);
+            target.put((int) index, target, value);
         }
     }
 
-    private static void setElem(Context cx, Scriptable target, long index,
-                                Object value)
-    {
+    private static void setElem(Context cx, Scriptable target, long index, Object value) {
         if (index > Integer.MAX_VALUE) {
             String id = Long.toString(index);
             ScriptableObject.putProperty(target, id, value);
         } else {
-            ScriptableObject.putProperty(target, (int)index, value);
+            ScriptableObject.putProperty(target, (int) index, value);
         }
     }
 
     // Similar as setElem(), but triggers deleteElem() if value is NOT_FOUND
-    private static void setRawElem(Context cx, Scriptable target, long index,
-                                   Object value) {
+    private static void setRawElem(Context cx, Scriptable target, long index, Object value) {
         if (value == NOT_FOUND) {
             deleteElem(target, index);
         } else {
@@ -791,15 +1019,14 @@
         }
     }
 
-    private static String toStringHelper(Context cx, Scriptable scope,
-                                         Scriptable thisObj,
-                                         boolean toSource, boolean toLocale)
-    {
+    private static String toStringHelper(
+            Context cx, Scriptable scope, Scriptable thisObj, boolean toSource, boolean toLocale) {
+        Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
+
         /* It's probably redundant to handle long lengths in this
          * function; StringBuilders are limited to 2^31 in java.
          */
-
-        long length = getLengthProperty(cx, thisObj);
+        long length = getLengthProperty(cx, o);
 
         StringBuilder result = new StringBuilder(256);
 
@@ -823,22 +1050,25 @@
             cx.iterating = new ObjToIntMap(31);
         } else {
             toplevel = false;
-            iterating = cx.iterating.has(thisObj);
+            iterating = cx.iterating.has(o);
         }
 
         // Make sure cx.iterating is set to null when done
         // so we don't leak memory
         try {
             if (!iterating) {
-                cx.iterating.put(thisObj, 0); // stop recursion.
+                // stop recursion
+                cx.iterating.put(o, 0);
+
                 // make toSource print null and undefined values in recent versions
-                boolean skipUndefinedAndNull = !toSource
-                        || cx.getLanguageVersion() < Context.VERSION_1_5;
+                boolean skipUndefinedAndNull =
+                        !toSource || cx.getLanguageVersion() < Context.VERSION_1_5;
                 for (i = 0; i < length; i++) {
                     if (i > 0) result.append(separator);
-                    Object elem = getRawElem(thisObj, i);
-                    if (elem == NOT_FOUND || (skipUndefinedAndNull &&
-                            (elem == null || elem == Undefined.instance))) {
+                    Object elem = getRawElem(o, i);
+                    if (elem == NOT_FOUND
+                            || (skipUndefinedAndNull
+                                    && (elem == null || elem == Undefined.instance))) {
                         haslast = false;
                         continue;
                     }
@@ -848,28 +1078,25 @@
                         result.append(ScriptRuntime.uneval(cx, scope, elem));
 
                     } else if (elem instanceof String) {
-                        String s = (String)elem;
-                        if (toSource) {
-                            result.append('\"');
-                            result.append(ScriptRuntime.escapeString(s));
-                            result.append('\"');
-                        } else {
-                            result.append(s);
-                        }
+                        result.append((String) elem);
 
                     } else {
                         if (toLocale) {
                             Callable fun;
                             Scriptable funThis;
-                            fun = ScriptRuntime.getPropFunctionAndThis(
-                                      elem, "toLocaleString", cx, scope);
+                            fun =
+                                    ScriptRuntime.getPropFunctionAndThis(
+                                            elem, "toLocaleString", cx, scope);
                             funThis = ScriptRuntime.lastStoredScriptable(cx);
-                            elem = fun.call(cx, scope, funThis,
-                                            ScriptRuntime.emptyArgs);
+                            elem = fun.call(cx, scope, funThis, ScriptRuntime.emptyArgs);
                         }
                         result.append(ScriptRuntime.toString(elem));
                     }
                 }
+
+                // processing of thisObj done, remove it from the recursion detector
+                // to allow thisObj to be again in the array later on
+                cx.iterating.remove(o);
             }
         } finally {
             if (toplevel) {
@@ -878,33 +1105,30 @@
         }
 
         if (toSource) {
-            //for [,,].length behavior; we want toString to be symmetric.
-            if (!haslast && i > 0)
-                result.append(", ]");
-            else
-                result.append(']');
+            // for [,,].length behavior; we want toString to be symmetric.
+            if (!haslast && i > 0) result.append(", ]");
+            else result.append(']');
         }
         return result.toString();
     }
 
-    /**
-     * See ECMA 15.4.4.3
-     */
-    private static String js_join(Context cx, Scriptable thisObj,
-                                  Object[] args)
-    {
-        long llength = getLengthProperty(cx, thisObj);
-        int length = (int)llength;
+    /** See ECMA 15.4.4.3 */
+    private static String js_join(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
+
+        long llength = getLengthProperty(cx, o);
+        int length = (int) llength;
         if (llength != length) {
-            throw Context.reportRuntimeError1(
-                "msg.arraylength.too.big", String.valueOf(llength));
+            throw Context.reportRuntimeErrorById(
+                    "msg.arraylength.too.big", String.valueOf(llength));
         }
         // if no args, use "," as separator
-        String separator = (args.length < 1 || args[0] == Undefined.instance)
-                           ? ","
-                           : ScriptRuntime.toString(args[0]);
-        if (thisObj instanceof NativeArray) {
-            NativeArray na = (NativeArray) thisObj;
+        String separator =
+                (args.length < 1 || args[0] == Undefined.instance)
+                        ? ","
+                        : ScriptRuntime.toString(args[0]);
+        if (o instanceof NativeArray) {
+            NativeArray na = (NativeArray) o;
             if (na.denseOnly) {
                 StringBuilder sb = new StringBuilder();
                 for (int i = 0; i < length; i++) {
@@ -913,9 +1137,9 @@
                     }
                     if (i < na.dense.length) {
                         Object temp = na.dense[i];
-                        if (temp != null && temp != Undefined.instance &&
-                            temp != Scriptable.NOT_FOUND)
-                        {
+                        if (temp != null
+                                && temp != Undefined.instance
+                                && temp != Scriptable.NOT_FOUND) {
                             sb.append(ScriptRuntime.toString(temp));
                         }
                     }
@@ -929,7 +1153,7 @@
         String[] buf = new String[length];
         int total_size = 0;
         for (int i = 0; i != length; i++) {
-            Object temp = getElem(cx, thisObj, i);
+            Object temp = getElem(cx, o, i);
             if (temp != null && temp != Undefined.instance) {
                 String str = ScriptRuntime.toString(temp);
                 total_size += str.length();
@@ -951,113 +1175,114 @@
         return sb.toString();
     }
 
-    /**
-     * See ECMA 15.4.4.4
-     */
-    private static Scriptable js_reverse(Context cx, Scriptable thisObj,
-                                         Object[] args)
-    {
-        if (thisObj instanceof NativeArray) {
-            NativeArray na = (NativeArray) thisObj;
+    /** See ECMA 15.4.4.4 */
+    private static Scriptable js_reverse(
+            Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
+
+        if (o instanceof NativeArray) {
+            NativeArray na = (NativeArray) o;
             if (na.denseOnly) {
-                for (int i=0, j=((int)na.length)-1; i < j; i++,j--) {
+                for (int i = 0, j = ((int) na.length) - 1; i < j; i++, j--) {
                     Object temp = na.dense[i];
                     na.dense[i] = na.dense[j];
                     na.dense[j] = temp;
                 }
-                return thisObj;
+                return o;
             }
         }
-        long len = getLengthProperty(cx, thisObj);
+        long len = getLengthProperty(cx, o);
 
         long half = len / 2;
-        for(long i=0; i < half; i++) {
+        for (long i = 0; i < half; i++) {
             long j = len - i - 1;
-            Object temp1 = getRawElem(thisObj, i);
-            Object temp2 = getRawElem(thisObj, j);
-            setRawElem(cx, thisObj, i, temp2);
-            setRawElem(cx, thisObj, j, temp1);
-        }
-        return thisObj;
+            Object temp1 = getRawElem(o, i);
+            Object temp2 = getRawElem(o, j);
+            setRawElem(cx, o, i, temp2);
+            setRawElem(cx, o, j, temp1);
+        }
+        return o;
     }
 
-    /**
-     * See ECMA 15.4.4.5
-     */
-    private static Scriptable js_sort(final Context cx, final Scriptable scope,
-            final Scriptable thisObj, final Object[] args)
-    {
+    /** See ECMA 15.4.4.5 */
+    private static Scriptable js_sort(
+            final Context cx,
+            final Scriptable scope,
+            final Scriptable thisObj,
+            final Object[] args) {
+        Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
+
         final Comparator<Object> comparator;
         if (args.length > 0 && Undefined.instance != args[0]) {
-            final Callable jsCompareFunction = ScriptRuntime
-                    .getValueFunctionAndThis(args[0], cx);
+            final Callable jsCompareFunction = ScriptRuntime.getValueFunctionAndThis(args[0], cx);
             final Scriptable funThis = ScriptRuntime.lastStoredScriptable(cx);
             final Object[] cmpBuf = new Object[2]; // Buffer for cmp arguments
-            comparator = new ElementComparator(
-                new Comparator<Object>() {
-                  public int compare(final Object x, final Object y) {
-                    // This comparator is invoked only for non-undefined objects
-                    cmpBuf[0] = x;
-                    cmpBuf[1] = y;
-                    Object ret = jsCompareFunction.call(cx, scope, funThis,
-                        cmpBuf);
-                    final double d = ScriptRuntime.toNumber(ret);
-                    if (d < 0) {
-                      return -1;
-                    } else if (d > 0) {
-                      return +1;
-                    }
-                    return 0; // ??? double and 0???
-                  }
-                });
+            comparator =
+                    new ElementComparator(
+                            new Comparator<Object>() {
+                                @Override
+                                public int compare(final Object x, final Object y) {
+                                    // This comparator is invoked only for non-undefined objects
+                                    cmpBuf[0] = x;
+                                    cmpBuf[1] = y;
+                                    Object ret = jsCompareFunction.call(cx, scope, funThis, cmpBuf);
+                                    double d = ScriptRuntime.toNumber(ret);
+                                    int cmp = Double.compare(d, 0);
+                                    if (cmp < 0) {
+                                        return -1;
+                                    } else if (cmp > 0) {
+                                        return +1;
+                                    }
+                                    return 0;
+                                }
+                            });
         } else {
             comparator = DEFAULT_COMPARATOR;
         }
 
-        long llength = getLengthProperty(cx, thisObj);
+        long llength = getLengthProperty(cx, o);
         final int length = (int) llength;
         if (llength != length) {
-            throw Context.reportRuntimeError1(
-                "msg.arraylength.too.big", String.valueOf(llength));
+            throw Context.reportRuntimeErrorById(
+                    "msg.arraylength.too.big", String.valueOf(llength));
         }
         // copy the JS array into a working array, so it can be
         // sorted cheaply.
         final Object[] working = new Object[length];
         for (int i = 0; i != length; ++i) {
-            working[i] = getRawElem(thisObj, i);
+            working[i] = getRawElem(o, i);
         }
 
-        Sorting.hybridSort(working, comparator);
+        Sorting.get().hybridSort(working, comparator);
 
         // copy the working array back into thisObj
         for (int i = 0; i < length; ++i) {
-            setRawElem(cx, thisObj, i, working[i]);
+            setRawElem(cx, o, i, working[i]);
         }
 
-        return thisObj;
+        return o;
     }
 
-    private static Object js_push(Context cx, Scriptable thisObj,
-                                  Object[] args)
-    {
-        if (thisObj instanceof NativeArray) {
-            NativeArray na = (NativeArray) thisObj;
-            if (na.denseOnly &&
-                na.ensureCapacity((int) na.length + args.length))
-            {
+    private static Object js_push(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
+
+        if (o instanceof NativeArray) {
+            NativeArray na = (NativeArray) o;
+            if (na.denseOnly && na.ensureCapacity((int) na.length + args.length)) {
                 for (int i = 0; i < args.length; i++) {
-                    na.dense[(int)na.length++] = args[i];
+                    na.dense[(int) na.length++] = args[i];
+                    na.modCount++;
                 }
                 return ScriptRuntime.wrapNumber(na.length);
             }
         }
-        long length = getLengthProperty(cx, thisObj);
+        long length = getLengthProperty(cx, o);
         for (int i = 0; i < args.length; i++) {
-            setElem(cx, thisObj, length + i, args[i]);
+            setElem(cx, o, length + i, args[i]);
         }
 
         length += args.length;
-        Object lengthObj = setLengthProperty(cx, thisObj, length);
+        Object lengthObj = setLengthProperty(cx, o, length);
 
         /*
          * If JS1.2, follow Perl4 by returning the last thing pushed.
@@ -1065,68 +1290,68 @@
          */
         if (cx.getLanguageVersion() == Context.VERSION_1_2)
             // if JS1.2 && no arguments, return undefined.
-            return args.length == 0
-                ? Undefined.instance
-                : args[args.length - 1];
+            return args.length == 0 ? Undefined.instance : args[args.length - 1];
 
-        else
-            return lengthObj;
+        return lengthObj;
     }
 
-    private static Object js_pop(Context cx, Scriptable thisObj,
-                                 Object[] args)
-    {
+    private static Object js_pop(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
+
         Object result;
-        if (thisObj instanceof NativeArray) {
-            NativeArray na = (NativeArray) thisObj;
+        if (o instanceof NativeArray) {
+            NativeArray na = (NativeArray) o;
             if (na.denseOnly && na.length > 0) {
                 na.length--;
-                result = na.dense[(int)na.length];
-                na.dense[(int)na.length] = NOT_FOUND;
+                na.modCount++;
+                result = na.dense[(int) na.length];
+                na.dense[(int) na.length] = NOT_FOUND;
                 return result;
             }
         }
-        long length = getLengthProperty(cx, thisObj);
+        long length = getLengthProperty(cx, o);
         if (length > 0) {
             length--;
 
             // Get the to-be-deleted property's value.
-            result = getElem(cx, thisObj, length);
+            result = getElem(cx, o, length);
 
             // We need to delete the last property, because 'thisObj' may not
             // have setLength which does that for us.
-            deleteElem(thisObj, length);
+            deleteElem(o, length);
         } else {
             result = Undefined.instance;
         }
         // necessary to match js even when length < 0; js pop will give a
         // length property to any target it is called on.
-        setLengthProperty(cx, thisObj, length);
+        setLengthProperty(cx, o, length);
 
         return result;
     }
 
-    private static Object js_shift(Context cx, Scriptable thisObj,
-                                   Object[] args)
-    {
-        if (thisObj instanceof NativeArray) {
-            NativeArray na = (NativeArray) thisObj;
+    private static Object js_shift(
+            Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
+
+        if (o instanceof NativeArray) {
+            NativeArray na = (NativeArray) o;
             if (na.denseOnly && na.length > 0) {
                 na.length--;
+                na.modCount++;
                 Object result = na.dense[0];
-                System.arraycopy(na.dense, 1, na.dense, 0, (int)na.length);
-                na.dense[(int)na.length] = NOT_FOUND;
+                System.arraycopy(na.dense, 1, na.dense, 0, (int) na.length);
+                na.dense[(int) na.length] = NOT_FOUND;
                 return result == NOT_FOUND ? Undefined.instance : result;
             }
         }
         Object result;
-        long length = getLengthProperty(cx, thisObj);
+        long length = getLengthProperty(cx, o);
         if (length > 0) {
             long i = 0;
             length--;
 
             // Get the to-be-deleted property's value.
-            result = getElem(cx, thisObj, i);
+            result = getElem(cx, o, i);
 
             /*
              * Slide down the array above the first element.  Leave i
@@ -1134,104 +1359,114 @@
              */
             if (length > 0) {
                 for (i = 1; i <= length; i++) {
-                    Object temp = getRawElem(thisObj, i);
-                    setRawElem(cx, thisObj, i - 1, temp);
+                    Object temp = getRawElem(o, i);
+                    setRawElem(cx, o, i - 1, temp);
                 }
             }
             // We need to delete the last property, because 'thisObj' may not
             // have setLength which does that for us.
-            deleteElem(thisObj, length);
+            deleteElem(o, length);
         } else {
             result = Undefined.instance;
         }
-        setLengthProperty(cx, thisObj, length);
+        setLengthProperty(cx, o, length);
         return result;
     }
 
-    private static Object js_unshift(Context cx, Scriptable thisObj,
-                                     Object[] args)
-    {
-        if (thisObj instanceof NativeArray) {
-            NativeArray na = (NativeArray) thisObj;
-            if (na.denseOnly &&
-                na.ensureCapacity((int)na.length + args.length))
-            {
-                System.arraycopy(na.dense, 0, na.dense, args.length,
-                                 (int) na.length);
+    private static Object js_unshift(
+            Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
+
+        if (o instanceof NativeArray) {
+            NativeArray na = (NativeArray) o;
+            if (na.denseOnly && na.ensureCapacity((int) na.length + args.length)) {
+                System.arraycopy(na.dense, 0, na.dense, args.length, (int) na.length);
                 for (int i = 0; i < args.length; i++) {
                     na.dense[i] = args[i];
                 }
                 na.length += args.length;
+                na.modCount++;
                 return ScriptRuntime.wrapNumber(na.length);
             }
         }
-        long length = getLengthProperty(cx, thisObj);
+        long length = getLengthProperty(cx, o);
         int argc = args.length;
 
-        if (args.length > 0) {
+        if (argc > 0) {
+            if (length + argc > NativeNumber.MAX_SAFE_INTEGER) {
+                throw ScriptRuntime.typeErrorById("msg.arraylength.too.big", length + argc);
+            }
+
             /*  Slide up the array to make room for args at the bottom */
             if (length > 0) {
                 for (long last = length - 1; last >= 0; last--) {
-                    Object temp = getRawElem(thisObj, last);
-                    setRawElem(cx, thisObj, last + argc, temp);
+                    Object temp = getRawElem(o, last);
+                    setRawElem(cx, o, last + argc, temp);
                 }
             }
 
             /* Copy from argv to the bottom of the array. */
             for (int i = 0; i < args.length; i++) {
-                setElem(cx, thisObj, i, args[i]);
+                setElem(cx, o, i, args[i]);
             }
         }
         /* Follow Perl by returning the new array length. */
-        length += args.length;
-        return setLengthProperty(cx, thisObj, length);
+        length += argc;
+        return setLengthProperty(cx, o, length);
     }
 
-    private static Object js_splice(Context cx, Scriptable scope,
-                                    Scriptable thisObj, Object[] args)
-    {
-      NativeArray na = null;
-      boolean denseMode = false;
-        if (thisObj instanceof NativeArray) {
-            na = (NativeArray) thisObj;
+    private static Object js_splice(
+            Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
+
+        NativeArray na = null;
+        boolean denseMode = false;
+        if (o instanceof NativeArray) {
+            na = (NativeArray) o;
             denseMode = na.denseOnly;
         }
 
         /* create an empty Array to return. */
         scope = getTopLevelScope(scope);
         int argc = args.length;
-        if (argc == 0)
-            return cx.newArray(scope, 0);
-        long length = getLengthProperty(cx, thisObj);
+        if (argc == 0) return cx.newArray(scope, 0);
+        long length = getLengthProperty(cx, o);
 
         /* Convert the first argument into a starting index. */
         long begin = toSliceIndex(ScriptRuntime.toInteger(args[0]), length);
         argc--;
 
         /* Convert the second argument into count */
-        long count;
+        long actualDeleteCount;
         if (args.length == 1) {
-            count = length - begin;
+            actualDeleteCount = length - begin;
         } else {
             double dcount = ScriptRuntime.toInteger(args[1]);
             if (dcount < 0) {
-                count = 0;
+                actualDeleteCount = 0;
             } else if (dcount > (length - begin)) {
-                count = length - begin;
+                actualDeleteCount = length - begin;
             } else {
-                count = (long)dcount;
+                actualDeleteCount = (long) dcount;
             }
             argc--;
         }
 
-        long end = begin + count;
+        long end = begin + actualDeleteCount;
+        long delta = argc - actualDeleteCount;
+
+        if (length + delta > NativeNumber.MAX_SAFE_INTEGER) {
+            throw ScriptRuntime.typeErrorById("msg.arraylength.too.big", length + delta);
+        }
+        if (actualDeleteCount > Integer.MAX_VALUE) {
+            String msg = ScriptRuntime.getMessageById("msg.arraylength.bad");
+            throw ScriptRuntime.rangeError(msg);
+        }
 
         /* If there are elements to remove, put them into the return value. */
         Object result;
-        if (count != 0) {
-            if (count == 1
-                && (cx.getLanguageVersion() == Context.VERSION_1_2))
-            {
+        if (actualDeleteCount != 0) {
+            if (actualDeleteCount == 1 && (cx.getLanguageVersion() == Context.VERSION_1_2)) {
                 /*
                  * JS lacks "list context", whereby in Perl one turns the
                  * single scalar that's spliced out into an array just by
@@ -1243,7 +1478,7 @@
                  * wrap in [] if necessary.  So JS1.3, default, and other
                  * versions all return an array of length 1 for uniformity.
                  */
-                result = getElem(cx, thisObj, begin);
+                result = getElem(cx, o, begin);
             } else {
                 if (denseMode) {
                     int intLen = (int) (end - begin);
@@ -1253,7 +1488,7 @@
                 } else {
                     Scriptable resultArray = cx.newArray(scope, 0);
                     for (long last = begin; last != end; last++) {
-                        Object temp = getRawElem(thisObj, last);
+                        Object temp = getRawElem(o, last);
                         if (temp != NOT_FOUND) {
                             setElem(cx, resultArray, last - begin, temp);
                         }
@@ -1263,7 +1498,7 @@
                     result = resultArray;
                 }
             }
-        } else { // (count == 0)
+        } else { // (actualDeleteCount == 0)
             if (cx.getLanguageVersion() == Context.VERSION_1_2) {
                 /* Emulate C JS1.2; if no elements are removed, return undefined. */
                 result = Undefined.instance;
@@ -1273,162 +1508,171 @@
         }
 
         /* Find the direction (up or down) to copy and make way for argv. */
-        long delta = argc - count;
-        if (denseMode && length + delta < Integer.MAX_VALUE &&
-            na.ensureCapacity((int) (length + delta)))
-        {
-            System.arraycopy(na.dense, (int) end, na.dense,
-                             (int) (begin + argc), (int) (length - end));
+        if (denseMode
+                && length + delta < Integer.MAX_VALUE
+                && na.ensureCapacity((int) (length + delta))) {
+            System.arraycopy(
+                    na.dense, (int) end, na.dense, (int) (begin + argc), (int) (length - end));
             if (argc > 0) {
                 System.arraycopy(args, 2, na.dense, (int) begin, argc);
             }
             if (delta < 0) {
-                Arrays.fill(na.dense, (int) (length + delta), (int) length,
-                            NOT_FOUND);
+                Arrays.fill(na.dense, (int) (length + delta), (int) length, NOT_FOUND);
             }
             na.length = length + delta;
+            na.modCount++;
             return result;
         }
 
         if (delta > 0) {
             for (long last = length - 1; last >= end; last--) {
-                Object temp = getRawElem(thisObj, last);
-                setRawElem(cx, thisObj, last + delta, temp);
+                Object temp = getRawElem(o, last);
+                setRawElem(cx, o, last + delta, temp);
             }
         } else if (delta < 0) {
             for (long last = end; last < length; last++) {
-                Object temp = getRawElem(thisObj, last);
-                setRawElem(cx, thisObj, last + delta, temp);
+                Object temp = getRawElem(o, last);
+                setRawElem(cx, o, last + delta, temp);
             }
-            for (long k = length + delta; k < length; ++k) {
-                deleteElem(thisObj, k);
+            // Do this backwards because some implementations might use a
+            // non-sparse array and therefore might not be able to handle
+            // deleting elements "in the middle". This makes us compatible
+            // with older Rhino releases.
+            for (long k = length - 1; k >= length + delta; --k) {
+                deleteElem(o, k);
             }
         }
 
         /* Copy from argv into the hole to complete the splice. */
         int argoffset = args.length - argc;
         for (int i = 0; i < argc; i++) {
-            setElem(cx, thisObj, begin + i, args[i + argoffset]);
+            setElem(cx, o, begin + i, args[i + argoffset]);
         }
 
         /* Update length in case we deleted elements from the end. */
-        setLengthProperty(cx, thisObj, length + delta);
+        setLengthProperty(cx, o, length + delta);
         return result;
     }
 
-    /*
-     * See Ecma 262v3 15.4.4.4
-     */
-    private static Scriptable js_concat(Context cx, Scriptable scope,
-                                        Scriptable thisObj, Object[] args)
-    {
-        // create an empty Array to return.
-        scope = getTopLevelScope(scope);
-        Scriptable result = cx.newArray(scope, 0);
-        if (thisObj instanceof NativeArray && result instanceof NativeArray) {
-            NativeArray denseThis = (NativeArray) thisObj;
-            NativeArray denseResult = (NativeArray) result;
-            if (denseThis.denseOnly && denseResult.denseOnly) {
-                // First calculate length of resulting array
-                boolean canUseDense = true;
-                int length = (int) denseThis.length;
-                for (int i = 0; i < args.length && canUseDense; i++) {
-                    if (args[i] instanceof NativeArray) {
-                        // only try to use dense approach for Array-like
-                        // objects that are actually NativeArrays
-                        final NativeArray arg = (NativeArray) args[i];
-                        canUseDense = arg.denseOnly;
-                        length += arg.length;
-                    } else {
-                        length++;
-                    }
-                }
-                if (canUseDense && denseResult.ensureCapacity(length)) {
-                    System.arraycopy(denseThis.dense, 0, denseResult.dense,
-                                     0, (int) denseThis.length);
-                    int cursor = (int) denseThis.length;
-                    for (int i = 0; i < args.length && canUseDense; i++) {
-                        if (args[i] instanceof NativeArray) {
-                            NativeArray arg = (NativeArray) args[i];
-                            System.arraycopy(arg.dense, 0,
-                                    denseResult.dense, cursor,
-                                    (int)arg.length);
-                            cursor += (int)arg.length;
-                        } else {
-                            denseResult.dense[cursor++] = args[i];
-                        }
-                    }
-                    denseResult.length = length;
-                    return result;
-                }
+    private static boolean isConcatSpreadable(Context cx, Scriptable scope, Object val) {
+        // First, look for the new @@isConcatSpreadable test as per ECMAScript 6 and up
+        if (val instanceof Scriptable) {
+            final Object spreadable =
+                    ScriptableObject.getProperty((Scriptable) val, SymbolKey.IS_CONCAT_SPREADABLE);
+            if ((spreadable != Scriptable.NOT_FOUND) && !Undefined.isUndefined(spreadable)) {
+                // If @@isConcatSpreadable was undefined, we have to fall back to testing for an
+                // array.
+                // Otherwise, we found some value
+                return ScriptRuntime.toBoolean(spreadable);
+            }
+        }
+
+        if (cx.getLanguageVersion() < Context.VERSION_ES6) {
+            // Otherwise, for older Rhino versions, fall back to the old algorithm, which treats
+            // things with
+            // the Array constructor as arrays. However, this is contrary to ES6!
+            final Function ctor = ScriptRuntime.getExistingCtor(cx, scope, "Array");
+            if (ScriptRuntime.instanceOf(val, ctor, cx)) {
+                return true;
             }
         }
 
-        long length;
-        long slot = 0;
+        // Otherwise, it's only spreadable if it's a native array
+        return js_isArray(val);
+    }
 
-        /* Put the target in the result array; only add it as an array
-         * if it looks like one.
-         */
-        if (js_isArray(thisObj)) {
-            length = getLengthProperty(cx, thisObj);
+    // Concat elements of "arg" into the destination, with optimizations for native,
+    // dense arrays.
+    private static long concatSpreadArg(
+            Context cx, Scriptable result, Scriptable arg, long offset) {
+        long srclen = getLengthProperty(cx, arg);
+        long newlen = srclen + offset;
 
-            // Copy from the target object into the result
-            for (slot = 0; slot < length; slot++) {
-                Object temp = getRawElem(thisObj, slot);
-                if (temp != NOT_FOUND) {
-                    defineElem(cx, result, slot, temp);
+        // First, optimize for a pair of native, dense arrays
+        if ((newlen <= Integer.MAX_VALUE) && (result instanceof NativeArray)) {
+            final NativeArray denseResult = (NativeArray) result;
+            if (denseResult.denseOnly && (arg instanceof NativeArray)) {
+                final NativeArray denseArg = (NativeArray) arg;
+                if (denseArg.denseOnly) {
+                    // Now we can optimize
+                    denseResult.ensureCapacity((int) newlen);
+                    System.arraycopy(
+                            denseArg.dense, 0, denseResult.dense, (int) offset, (int) srclen);
+                    return newlen;
                 }
+                // We could also optimize here if we are copying to a dense target from a non-dense
+                // native array. However, if the source array is very sparse then the result will be
+                // very bad -- so don't.
             }
-        } else {
-            defineElem(cx, result, slot++, thisObj);
         }
 
-        /* Copy from the arguments into the result.  If any argument
-         * has a numeric length property, treat it as an array and add
-         * elements separately; otherwise, just copy the argument.
-         */
-        for (int i = 0; i < args.length; i++) {
-            if (js_isArray(args[i])) {
-                // js_isArray => instanceof Scriptable
-                Scriptable arg = (Scriptable)args[i];
-                length = getLengthProperty(cx, arg);
-                for (long j = 0; j < length; j++, slot++) {
-                    Object temp = getRawElem(arg, j);
-                    if (temp != NOT_FOUND) {
-                        defineElem(cx, result, slot, temp);
-                    }
-                }
-            } else {
-                defineElem(cx, result, slot++, args[i]);
+        // If we get here then we have to do things the generic way
+        long dstpos = offset;
+        for (long srcpos = 0; srcpos < srclen; srcpos++, dstpos++) {
+            final Object temp = getRawElem(arg, srcpos);
+            if (temp != Scriptable.NOT_FOUND) {
+                defineElem(cx, result, dstpos, temp);
             }
         }
-        setLengthProperty(cx, result, slot);
+        return newlen;
+    }
+
+    private static long doConcat(
+            Context cx, Scriptable scope, Scriptable result, Object arg, long offset) {
+        if (isConcatSpreadable(cx, scope, arg)) {
+            return concatSpreadArg(cx, result, (Scriptable) arg, offset);
+        }
+        defineElem(cx, result, offset, arg);
+        return offset + 1;
+    }
+
+    /*
+     * See Ecma 262v3 15.4.4.4
+     */
+    private static Scriptable js_concat(
+            Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
+
+        // create an empty Array to return.
+        scope = getTopLevelScope(scope);
+        final Scriptable result = cx.newArray(scope, 0);
+
+        long length = doConcat(cx, scope, result, o, 0);
+        for (Object arg : args) {
+            length = doConcat(cx, scope, result, arg, length);
+        }
+
+        setLengthProperty(cx, result, length);
         return result;
     }
 
-    private Scriptable js_slice(Context cx, Scriptable thisObj,
-                                Object[] args)
-    {
-        Scriptable scope = getTopLevelScope(this);
-        Scriptable result = cx.newArray(scope, 0);
-        long length = getLengthProperty(cx, thisObj);
+    private static Scriptable js_slice(
+            Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
+
+        long len = getLengthProperty(cx, o);
 
         long begin, end;
         if (args.length == 0) {
             begin = 0;
-            end = length;
+            end = len;
         } else {
-            begin = toSliceIndex(ScriptRuntime.toInteger(args[0]), length);
+            begin = toSliceIndex(ScriptRuntime.toInteger(args[0]), len);
             if (args.length == 1 || args[1] == Undefined.instance) {
-                end = length;
+                end = len;
             } else {
-                end = toSliceIndex(ScriptRuntime.toInteger(args[1]), length);
+                end = toSliceIndex(ScriptRuntime.toInteger(args[1]), len);
             }
         }
 
+        if (end - begin > Integer.MAX_VALUE) {
+            String msg = ScriptRuntime.getMessageById("msg.arraylength.bad");
+            throw ScriptRuntime.rangeError(msg);
+        }
+
+        Scriptable result = cx.newArray(scope, 0);
         for (long slot = begin; slot < end; slot++) {
-            Object temp = getRawElem(thisObj, slot);
+            Object temp = getRawElem(o, slot);
             if (temp != NOT_FOUND) {
                 defineElem(cx, result, slot - begin, temp);
             }
@@ -1444,21 +1688,22 @@
             if (value + length < 0.0) {
                 result = 0;
             } else {
-                result = (long)(value + length);
+                result = (long) (value + length);
             }
         } else if (value > length) {
             result = length;
         } else {
-            result = (long)value;
+            result = (long) value;
         }
         return result;
     }
 
-    private static Object js_indexOf(Context cx, Scriptable thisObj,
-                                     Object[] args)
-    {
+    private static Object js_indexOf(
+            Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         Object compareTo = args.length > 0 ? args[0] : Undefined.instance;
-        long length = getLengthProperty(cx, thisObj);
+
+        Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
+        long length = getLengthProperty(cx, o);
         /*
          * From http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:indexOf
          * The index at which to begin the search. Defaults to 0, i.e. the
@@ -1474,34 +1719,31 @@
             // default
             start = 0;
         } else {
-            start = (long)ScriptRuntime.toInteger(args[1]);
+            start = (long) ScriptRuntime.toInteger(args[1]);
             if (start < 0) {
                 start += length;
-                if (start < 0)
-                    start = 0;
+                if (start < 0) start = 0;
             }
             if (start > length - 1) return NEGATIVE_ONE;
         }
-        if (thisObj instanceof NativeArray) {
-            NativeArray na = (NativeArray) thisObj;
+        if (o instanceof NativeArray) {
+            NativeArray na = (NativeArray) o;
             if (na.denseOnly) {
                 Scriptable proto = na.getPrototype();
-                for (int i=(int)start; i < length; i++) {
+                for (int i = (int) start; i < length; i++) {
                     Object val = na.dense[i];
                     if (val == NOT_FOUND && proto != null) {
                         val = ScriptableObject.getProperty(proto, i);
                     }
-                    if (val != NOT_FOUND &&
-                        ScriptRuntime.shallowEq(val, compareTo))
-                    {
+                    if (val != NOT_FOUND && ScriptRuntime.shallowEq(val, compareTo)) {
                         return Long.valueOf(i);
                     }
                 }
                 return NEGATIVE_ONE;
             }
         }
-        for (long i=start; i < length; i++) {
-            Object val = getRawElem(thisObj, i);
+        for (long i = start; i < length; i++) {
+            Object val = getRawElem(o, i);
             if (val != NOT_FOUND && ScriptRuntime.shallowEq(val, compareTo)) {
                 return Long.valueOf(i);
             }
@@ -1509,11 +1751,12 @@
         return NEGATIVE_ONE;
     }
 
-    private static Object js_lastIndexOf(Context cx, Scriptable thisObj,
-            Object[] args)
-    {
+    private static Object js_lastIndexOf(
+            Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         Object compareTo = args.length > 0 ? args[0] : Undefined.instance;
-        long length = getLengthProperty(cx, thisObj);
+
+        Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
+        long length = getLengthProperty(cx, o);
         /*
          * From http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:lastIndexOf
          * The index at which to start searching backwards. Defaults to the
@@ -1528,35 +1771,31 @@
         long start;
         if (args.length < 2) {
             // default
-            start = length-1;
+            start = length - 1;
         } else {
-            start = (long)ScriptRuntime.toInteger(args[1]);
-            if (start >= length)
-                start = length-1;
-            else if (start < 0)
-                start += length;
+            start = (long) ScriptRuntime.toInteger(args[1]);
+            if (start >= length) start = length - 1;
+            else if (start < 0) start += length;
             if (start < 0) return NEGATIVE_ONE;
         }
-        if (thisObj instanceof NativeArray) {
-            NativeArray na = (NativeArray) thisObj;
+        if (o instanceof NativeArray) {
+            NativeArray na = (NativeArray) o;
             if (na.denseOnly) {
                 Scriptable proto = na.getPrototype();
-                for (int i=(int)start; i >= 0; i--) {
+                for (int i = (int) start; i >= 0; i--) {
                     Object val = na.dense[i];
                     if (val == NOT_FOUND && proto != null) {
                         val = ScriptableObject.getProperty(proto, i);
                     }
-                    if (val != NOT_FOUND &&
-                        ScriptRuntime.shallowEq(val, compareTo))
-                    {
+                    if (val != NOT_FOUND && ScriptRuntime.shallowEq(val, compareTo)) {
                         return Long.valueOf(i);
                     }
                 }
                 return NEGATIVE_ONE;
             }
         }
-        for (long i=start; i >= 0; i--) {
-            Object val = getRawElem(thisObj, i);
+        for (long i = start; i >= 0; i--) {
+            Object val = getRawElem(o, i);
             if (val != NOT_FOUND && ScriptRuntime.shallowEq(val, compareTo)) {
                 return Long.valueOf(i);
             }
@@ -1564,26 +1803,202 @@
         return NEGATIVE_ONE;
     }
 
-    /**
-     * Implements the methods "every", "filter", "forEach", "map", and "some".
-     */
-    private static Object iterativeMethod(Context cx, IdFunctionObject idFunctionObject, Scriptable scope,
-                                          Scriptable thisObj, Object[] args)
-    {
-        int id = idFunctionObject.methodId();
+    /*
+       See ECMA-262 22.1.3.13
+    */
+    private static Boolean js_includes(
+            Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        Object compareTo = args.length > 0 ? args[0] : Undefined.instance;
 
-        if (Id_find == id || Id_findIndex == id) thisObj = requireObjectCoercible(cx, thisObj, idFunctionObject);
+        Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
+        long len = ScriptRuntime.toLength(new Object[] {getProperty(thisObj, "length")}, 0);
+        if (len == 0) return Boolean.FALSE;
+
+        long k;
+        if (args.length < 2) {
+            k = 0;
+        } else {
+            k = (long) ScriptRuntime.toInteger(args[1]);
+            if (k < 0) {
+                k += len;
+                if (k < 0) k = 0;
+            }
+            if (k > len - 1) return Boolean.FALSE;
+        }
+        if (o instanceof NativeArray) {
+            NativeArray na = (NativeArray) o;
+            if (na.denseOnly) {
+                Scriptable proto = na.getPrototype();
+                for (int i = (int) k; i < len; i++) {
+                    Object elementK = na.dense[i];
+                    if (elementK == NOT_FOUND && proto != null) {
+                        elementK = ScriptableObject.getProperty(proto, i);
+                    }
+                    if (elementK == NOT_FOUND) {
+                        elementK = Undefined.instance;
+                    }
+                    if (ScriptRuntime.sameZero(elementK, compareTo)) {
+                        return Boolean.TRUE;
+                    }
+                }
+                return Boolean.FALSE;
+            }
+        }
+        for (; k < len; k++) {
+            Object elementK = getRawElem(o, k);
+            if (elementK == NOT_FOUND) {
+                elementK = Undefined.instance;
+            }
+            if (ScriptRuntime.sameZero(elementK, compareTo)) {
+                return Boolean.TRUE;
+            }
+        }
+        return Boolean.FALSE;
+    }
+
+    private static Object js_fill(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
+        long len = getLengthProperty(cx, o);
+
+        long relativeStart = 0;
+        if (args.length >= 2) {
+            relativeStart = (long) ScriptRuntime.toInteger(args[1]);
+        }
+        final long k;
+        if (relativeStart < 0) {
+            k = Math.max((len + relativeStart), 0);
+        } else {
+            k = Math.min(relativeStart, len);
+        }
+
+        long relativeEnd = len;
+        if (args.length >= 3 && !Undefined.isUndefined(args[2])) {
+            relativeEnd = (long) ScriptRuntime.toInteger(args[2]);
+        }
+        final long fin;
+        if (relativeEnd < 0) {
+            fin = Math.max((len + relativeEnd), 0);
+        } else {
+            fin = Math.min(relativeEnd, len);
+        }
+
+        Object value = args.length > 0 ? args[0] : Undefined.instance;
+        for (long i = k; i < fin; i++) {
+            setRawElem(cx, thisObj, i, value);
+        }
+
+        return thisObj;
+    }
+
+    private static Object js_copyWithin(
+            Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
+        long len = getLengthProperty(cx, o);
+
+        Object targetArg = (args.length >= 1) ? args[0] : Undefined.instance;
+        long relativeTarget = (long) ScriptRuntime.toInteger(targetArg);
+        long to;
+        if (relativeTarget < 0) {
+            to = Math.max((len + relativeTarget), 0);
+        } else {
+            to = Math.min(relativeTarget, len);
+        }
+
+        Object startArg = (args.length >= 2) ? args[1] : Undefined.instance;
+        long relativeStart = (long) ScriptRuntime.toInteger(startArg);
+        long from;
+        if (relativeStart < 0) {
+            from = Math.max((len + relativeStart), 0);
+        } else {
+            from = Math.min(relativeStart, len);
+        }
+
+        long relativeEnd = len;
+        if (args.length >= 3 && !Undefined.isUndefined(args[2])) {
+            relativeEnd = (long) ScriptRuntime.toInteger(args[2]);
+        }
+        final long fin;
+        if (relativeEnd < 0) {
+            fin = Math.max((len + relativeEnd), 0);
+        } else {
+            fin = Math.min(relativeEnd, len);
+        }
+
+        long count = Math.min(fin - from, len - to);
+        int direction = 1;
+        if (from < to && to < from + count) {
+            direction = -1;
+            from = from + count - 1;
+            to = to + count - 1;
+        }
+
+        // Optimize for a native array. If properties were overridden with setters
+        // and other non-default options then we won't get here.
+        if ((o instanceof NativeArray) && (count <= Integer.MAX_VALUE)) {
+            NativeArray na = (NativeArray) o;
+            if (na.denseOnly) {
+                for (; count > 0; count--) {
+                    na.dense[(int) to] = na.dense[(int) from];
+                    from += direction;
+                    to += direction;
+                }
+
+                return thisObj;
+            }
+        }
+
+        for (; count > 0; count--) {
+            final Object temp = getRawElem(o, from);
+            if ((temp == Scriptable.NOT_FOUND) || Undefined.isUndefined(temp)) {
+                deleteElem(o, to);
+            } else {
+                setElem(cx, o, to, temp);
+            }
+
+            from += direction;
+            to += direction;
+        }
+
+        return thisObj;
+    }
+
+    /** Implements the methods "every", "filter", "forEach", "map", and "some". */
+    private static Object iterativeMethod(
+            Context cx,
+            IdFunctionObject idFunctionObject,
+            Scriptable scope,
+            Scriptable thisObj,
+            Object[] args) {
+        Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
+
+        // execIdCall(..) uses a trick for all the ConstructorId_xxx calls
+        // they are handled like object calls by adjusting the args list
+        // as a result we have to handle ConstructorId_xxx calls (negative id)
+        // the same way and always us the abs value of the id for method selection
+        int id = Math.abs(idFunctionObject.methodId());
+        if (Id_find == id || Id_findIndex == id) {
+            requireObjectCoercible(cx, o, idFunctionObject);
+        }
+
+        long length = getLengthProperty(cx, o);
+        if (id == Id_map && length > Integer.MAX_VALUE) {
+            String msg = ScriptRuntime.getMessageById("msg.arraylength.bad");
+            throw ScriptRuntime.rangeError(msg);
+        }
 
-        long length = getLengthProperty(cx, thisObj);
         Object callbackArg = args.length > 0 ? args[0] : Undefined.instance;
         if (callbackArg == null || !(callbackArg instanceof Function)) {
             throw ScriptRuntime.notFunctionError(callbackArg);
         }
-        if (cx.getLanguageVersion() >= Context.VERSION_ES6 && (callbackArg instanceof NativeRegExp)) {
-            // Previously, it was allowed to pass RegExp instance as a callback (it implements Function)
+        if (cx.getLanguageVersion() >= Context.VERSION_ES6
+                && (callbackArg instanceof NativeRegExp)) {
+            // Previously, it was allowed to pass RegExp instance as a callback (it implements
+            // Function)
             // But according to ES2015 21.2.6 Properties of RegExp Instances:
-            // > RegExp instances are ordinary objects that inherit properties from the RegExp prototype object.
-            // > RegExp instances have internal slots [[RegExpMatcher]], [[OriginalSource]], and [[OriginalFlags]].
+            // > RegExp instances are ordinary objects that inherit properties from the RegExp
+            // prototype object.
+            // > RegExp instances have internal slots [[RegExpMatcher]], [[OriginalSource]], and
+            // [[OriginalFlags]].
             // so, no [[Call]] for RegExp-s
             throw ScriptRuntime.notFunctionError(callbackArg);
         }
@@ -1602,10 +2017,10 @@
             int resultLength = id == Id_map ? (int) length : 0;
             array = cx.newArray(scope, resultLength);
         }
-        long j=0;
-        for (long i=0; i < length; i++) {
+        long j = 0;
+        for (long i = 0; i < length; i++) {
             Object[] innerArgs = new Object[3];
-            Object elem = getRawElem(thisObj, i);
+            Object elem = getRawElem(o, i);
             if (elem == Scriptable.NOT_FOUND) {
                 if (id == Id_find || id == Id_findIndex) {
                     elem = Undefined.instance;
@@ -1615,59 +2030,53 @@
             }
             innerArgs[0] = elem;
             innerArgs[1] = Long.valueOf(i);
-            innerArgs[2] = thisObj;
+            innerArgs[2] = o;
             Object result = f.call(cx, parent, thisArg, innerArgs);
             switch (id) {
-              case Id_every:
-                if (!ScriptRuntime.toBoolean(result))
-                    return Boolean.FALSE;
-                break;
-              case Id_filter:
-                if (ScriptRuntime.toBoolean(result))
-                    defineElem(cx, array, j++, innerArgs[0]);
-                break;
-              case Id_forEach:
-                break;
-              case Id_map:
-                defineElem(cx, array, i, result);
-                break;
-              case Id_some:
-                if (ScriptRuntime.toBoolean(result))
-                    return Boolean.TRUE;
-                break;
-              case Id_find:
-                if (ScriptRuntime.toBoolean(result))
-                    return elem;
-                break;
-              case Id_findIndex:
-                if (ScriptRuntime.toBoolean(result))
-                    return ScriptRuntime.wrapNumber(i);
-                break;
+                case Id_every:
+                    if (!ScriptRuntime.toBoolean(result)) return Boolean.FALSE;
+                    break;
+                case Id_filter:
+                    if (ScriptRuntime.toBoolean(result)) defineElem(cx, array, j++, innerArgs[0]);
+                    break;
+                case Id_forEach:
+                    break;
+                case Id_map:
+                    defineElem(cx, array, i, result);
+                    break;
+                case Id_some:
+                    if (ScriptRuntime.toBoolean(result)) return Boolean.TRUE;
+                    break;
+                case Id_find:
+                    if (ScriptRuntime.toBoolean(result)) return elem;
+                    break;
+                case Id_findIndex:
+                    if (ScriptRuntime.toBoolean(result)) return ScriptRuntime.wrapNumber(i);
+                    break;
             }
         }
         switch (id) {
-          case Id_every:
-            return Boolean.TRUE;
-          case Id_filter:
-          case Id_map:
-            return array;
-          case Id_some:
-            return Boolean.FALSE;
-          case Id_findIndex:
-            return ScriptRuntime.wrapNumber(-1);
-          case Id_forEach:
-          default:
-            return Undefined.instance;
+            case Id_every:
+                return Boolean.TRUE;
+            case Id_filter:
+            case Id_map:
+                return array;
+            case Id_some:
+                return Boolean.FALSE;
+            case Id_findIndex:
+                return ScriptRuntime.wrapNumber(-1);
+            case Id_forEach:
+            default:
+                return Undefined.instance;
         }
     }
 
-    /**
-     * Implements the methods "reduce" and "reduceRight".
-     */
-    private static Object reduceMethod(Context cx, int id, Scriptable scope,
-                                       Scriptable thisObj, Object[] args)
-    {
-        long length = getLengthProperty(cx, thisObj);
+    /** Implements the methods "reduce" and "reduceRight". */
+    private static Object reduceMethod(
+            Context cx, int id, Scriptable scope, Scriptable thisObj, Object[] args) {
+        Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
+
+        long length = getLengthProperty(cx, o);
         Object callbackArg = args.length > 0 ? args[0] : Undefined.instance;
         if (callbackArg == null || !(callbackArg instanceof Function)) {
             throw ScriptRuntime.notFunctionError(callbackArg);
@@ -1679,7 +2088,7 @@
         Object value = args.length > 1 ? args[1] : Scriptable.NOT_FOUND;
         for (long i = 0; i < length; i++) {
             long index = movingLeft ? i : (length - 1 - i);
-            Object elem = getRawElem(thisObj, index);
+            Object elem = getRawElem(o, index);
             if (elem == Scriptable.NOT_FOUND) {
                 continue;
             }
@@ -1687,13 +2096,13 @@
                 // no initial value passed, use first element found as inital value
                 value = elem;
             } else {
-                Object[] innerArgs = { value, elem, index, thisObj };
+                Object[] innerArgs = {value, elem, Long.valueOf(index), o};
                 value = f.call(cx, parent, parent, innerArgs);
             }
         }
         if (value == Scriptable.NOT_FOUND) {
             // reproduce spidermonkey error message
-            throw ScriptRuntime.typeError0("msg.empty.array.reduce");
+            throw ScriptRuntime.typeErrorById("msg.empty.array.reduce");
         }
         return value;
     }
@@ -1702,38 +2111,39 @@
         if (!(o instanceof Scriptable)) {
             return false;
         }
-        return "Array".equals(((Scriptable)o).getClassName());
+        return "Array".equals(((Scriptable) o).getClassName());
     }
 
     // methods to implement java.util.List
 
+    @Override
     public boolean contains(Object o) {
         return indexOf(o) > -1;
     }
 
+    @Override
     public Object[] toArray() {
         return toArray(ScriptRuntime.emptyArgs);
     }
 
+    @Override
     public Object[] toArray(Object[] a) {
-        long longLen = length;
-        if (longLen > Integer.MAX_VALUE) {
-            throw new IllegalStateException();
-        }
-        int len = (int) longLen;
-        Object[] array = a.length >= len ?
-                a : (Object[]) java.lang.reflect.Array
-                .newInstance(a.getClass().getComponentType(), len);
+        int len = size();
+        Object[] array =
+                a.length >= len
+                        ? a
+                        : (Object[])
+                                java.lang.reflect.Array.newInstance(
+                                        a.getClass().getComponentType(), len);
         for (int i = 0; i < len; i++) {
             array[i] = get(i);
         }
         return array;
     }
 
+    @Override
     public boolean containsAll(Collection c) {
-        for (Object aC : c)
-            if (!contains(aC))
-                return false;
+        for (Object aC : c) if (!contains(aC)) return false;
         return true;
     }
 
@@ -1741,7 +2151,8 @@
     public int size() {
         long longLen = length;
         if (longLen > Integer.MAX_VALUE) {
-            throw new IllegalStateException();
+            throw new IllegalStateException(
+                    "list.length (" + length + ") exceeds Integer.MAX_VALUE");
         }
         return (int) longLen;
     }
@@ -1765,16 +2176,14 @@
         }
     }
 
+    @Override
     public Object get(int index) {
         return get((long) index);
     }
 
+    @Override
     public int indexOf(Object o) {
-        long longLen = length;
-        if (longLen > Integer.MAX_VALUE) {
-            throw new IllegalStateException();
-        }
-        int len = (int) longLen;
+        int len = size();
         if (o == null) {
             for (int i = 0; i < len; i++) {
                 if (get(i) == null) {
@@ -1791,12 +2200,9 @@
         return -1;
     }
 
+    @Override
     public int lastIndexOf(Object o) {
-        long longLen = length;
-        if (longLen > Integer.MAX_VALUE) {
-            throw new IllegalStateException();
-        }
-        int len = (int) longLen;
+        int len = size();
         if (o == null) {
             for (int i = len - 1; i >= 0; i--) {
                 if (get(i) == null) {
@@ -1813,20 +2219,19 @@
         return -1;
     }
 
+    @Override
     public Iterator iterator() {
         return listIterator(0);
     }
 
+    @Override
     public ListIterator listIterator() {
         return listIterator(0);
     }
 
+    @Override
     public ListIterator listIterator(final int start) {
-        long longLen = length;
-        if (longLen > Integer.MAX_VALUE) {
-            throw new IllegalStateException();
-        }
-        final int len = (int) longLen;
+        final int len = size();
 
         if (start < 0 || start > len) {
             throw new IndexOutOfBoundsException("Index: " + start);
@@ -1835,98 +2240,146 @@
         return new ListIterator() {
 
             int cursor = start;
+            int modCount = NativeArray.this.modCount;
 
+            @Override
             public boolean hasNext() {
                 return cursor < len;
             }
 
+            @Override
             public Object next() {
+                checkModCount(modCount);
                 if (cursor == len) {
                     throw new NoSuchElementException();
                 }
                 return get(cursor++);
             }
 
+            @Override
             public boolean hasPrevious() {
                 return cursor > 0;
             }
 
+            @Override
             public Object previous() {
+                checkModCount(modCount);
                 if (cursor == 0) {
                     throw new NoSuchElementException();
                 }
                 return get(--cursor);
             }
 
+            @Override
             public int nextIndex() {
                 return cursor;
             }
 
+            @Override
             public int previousIndex() {
                 return cursor - 1;
             }
 
+            @Override
             public void remove() {
                 throw new UnsupportedOperationException();
             }
 
+            @Override
             public void add(Object o) {
                 throw new UnsupportedOperationException();
             }
 
+            @Override
             public void set(Object o) {
                 throw new UnsupportedOperationException();
             }
         };
     }
 
+    @Override
     public boolean add(Object o) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public boolean remove(Object o) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public boolean addAll(Collection c) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public boolean removeAll(Collection c) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public boolean retainAll(Collection c) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public void clear() {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public void add(int index, Object element) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public boolean addAll(int index, Collection c) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public Object set(int index, Object element) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public Object remove(int index) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public List subList(int fromIndex, int toIndex) {
-        throw new UnsupportedOperationException();
+        if (fromIndex < 0) throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
+        if (toIndex > size()) throw new IndexOutOfBoundsException("toIndex = " + toIndex);
+        if (fromIndex > toIndex)
+            throw new IllegalArgumentException(
+                    "fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
+
+        return new AbstractList() {
+            private int modCount = NativeArray.this.modCount;
+
+            @Override
+            public Object get(int index) {
+                checkModCount(modCount);
+                return NativeArray.this.get(index + fromIndex);
+            }
+
+            @Override
+            public int size() {
+                checkModCount(modCount);
+                return toIndex - fromIndex;
+            }
+        };
+    }
+
+    private void checkModCount(int modCount) {
+        if (this.modCount != modCount) {
+            throw new ConcurrentModificationException();
+        }
     }
 
     @Override
-    protected int findPrototypeId(Symbol k)
-    {
+    protected int findPrototypeId(Symbol k) {
         if (SymbolKey.ITERATOR.equals(k)) {
             return SymbolId_iterator;
         }
@@ -1938,201 +2391,247 @@
     private static final Comparator<Object> STRING_COMPARATOR = new StringLikeComparator();
     private static final Comparator<Object> DEFAULT_COMPARATOR = new ElementComparator();
 
-    public static final class StringLikeComparator
-      implements Comparator<Object> {
+    public static final class StringLikeComparator implements Comparator<Object>, Serializable {
 
-      public int compare(final Object x, final Object y) {
-        final String a = ScriptRuntime.toString(x);
-        final String b = ScriptRuntime.toString(y);
-        return a.compareTo(b);
-      }
-    }
-
-    public static final class ElementComparator
-      implements Comparator<Object> {
-
-      private final Comparator<Object> child;
-
-      public ElementComparator() {
-        child = STRING_COMPARATOR;
-      }
-
-      public ElementComparator(Comparator<Object> c) {
-        child = c;
-      }
-
-      public int compare(final Object x, final Object y) {
-        // Sort NOT_FOUND to very end, Undefined before that, exclusively, as per
-        // ECMA 22.1.3.25.1.
-        if (x == Undefined.instance) {
-          if (y == Undefined.instance) {
-            return 0;
-          }
-          if (y == NOT_FOUND) {
-            return -1;
-          }
-          return 1;
-        } else if (x == NOT_FOUND) {
-          return y == NOT_FOUND ? 0 : 1;
+        private static final long serialVersionUID = 5299017659728190979L;
+
+        @Override
+        public int compare(final Object x, final Object y) {
+            final String a = ScriptRuntime.toString(x);
+            final String b = ScriptRuntime.toString(y);
+            return a.compareTo(b);
         }
+    }
 
-        if (y == NOT_FOUND) {
-          return -1;
+    public static final class ElementComparator implements Comparator<Object>, Serializable {
+
+        private static final long serialVersionUID = -1189948017688708858L;
+
+        private final Comparator<Object> child;
+
+        public ElementComparator() {
+            child = STRING_COMPARATOR;
         }
-        if (y == Undefined.instance) {
-          return -1;
+
+        public ElementComparator(Comparator<Object> c) {
+            child = c;
         }
 
-        return child.compare(x, y);
-      }
-    }
+        @Override
+        public int compare(final Object x, final Object y) {
+            // Sort NOT_FOUND to very end, Undefined before that, exclusively, as per
+            // ECMA 22.1.3.25.1.
+            if (x == Undefined.instance) {
+                if (y == Undefined.instance) {
+                    return 0;
+                }
+                if (y == NOT_FOUND) {
+                    return -1;
+                }
+                return 1;
+            } else if (x == NOT_FOUND) {
+                return y == NOT_FOUND ? 0 : 1;
+            }
 
-// #string_id_map#
+            if (y == NOT_FOUND) {
+                return -1;
+            }
+            if (y == Undefined.instance) {
+                return -1;
+            }
+
+            return child.compare(x, y);
+        }
+    }
 
     @Override
-    protected int findPrototypeId(String s)
-    {
+    protected int findPrototypeId(String s) {
         int id;
-// #generated# Last update: 2016-03-04 20:46:26 GMT
-        L0: { id = 0; String X = null; int c;
-            L: switch (s.length()) {
-            case 3: c=s.charAt(0);
-                if (c=='m') { if (s.charAt(2)=='p' && s.charAt(1)=='a') {id=Id_map; break L0;} }
-                else if (c=='p') { if (s.charAt(2)=='p' && s.charAt(1)=='o') {id=Id_pop; break L0;} }
-                break L;
-            case 4: switch (s.charAt(2)) {
-                case 'i': X="join";id=Id_join; break L;
-                case 'm': X="some";id=Id_some; break L;
-                case 'n': X="find";id=Id_find; break L;
-                case 'r': X="sort";id=Id_sort; break L;
-                case 's': X="push";id=Id_push; break L;
-                } break L;
-            case 5: c=s.charAt(1);
-                if (c=='h') { X="shift";id=Id_shift; }
-                else if (c=='l') { X="slice";id=Id_slice; }
-                else if (c=='v') { X="every";id=Id_every; }
-                break L;
-            case 6: switch (s.charAt(0)) {
-                case 'c': X="concat";id=Id_concat; break L;
-                case 'f': X="filter";id=Id_filter; break L;
-                case 'r': X="reduce";id=Id_reduce; break L;
-                case 's': X="splice";id=Id_splice; break L;
-                } break L;
-            case 7: switch (s.charAt(0)) {
-                case 'f': X="forEach";id=Id_forEach; break L;
-                case 'i': X="indexOf";id=Id_indexOf; break L;
-                case 'r': X="reverse";id=Id_reverse; break L;
-                case 'u': X="unshift";id=Id_unshift; break L;
-                } break L;
-            case 8: c=s.charAt(3);
-                if (c=='o') { X="toSource";id=Id_toSource; }
-                else if (c=='t') { X="toString";id=Id_toString; }
-                break L;
-            case 9: X="findIndex";id=Id_findIndex; break L;
-            case 11: c=s.charAt(0);
-                if (c=='c') { X="constructor";id=Id_constructor; }
-                else if (c=='l') { X="lastIndexOf";id=Id_lastIndexOf; }
-                else if (c=='r') { X="reduceRight";id=Id_reduceRight; }
-                break L;
-            case 14: X="toLocaleString";id=Id_toLocaleString; break L;
-            }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-            break L0;
+        switch (s) {
+            case "constructor":
+                id = Id_constructor;
+                break;
+            case "toString":
+                id = Id_toString;
+                break;
+            case "toLocaleString":
+                id = Id_toLocaleString;
+                break;
+            case "toSource":
+                id = Id_toSource;
+                break;
+            case "join":
+                id = Id_join;
+                break;
+            case "reverse":
+                id = Id_reverse;
+                break;
+            case "sort":
+                id = Id_sort;
+                break;
+            case "push":
+                id = Id_push;
+                break;
+            case "pop":
+                id = Id_pop;
+                break;
+            case "shift":
+                id = Id_shift;
+                break;
+            case "unshift":
+                id = Id_unshift;
+                break;
+            case "splice":
+                id = Id_splice;
+                break;
+            case "concat":
+                id = Id_concat;
+                break;
+            case "slice":
+                id = Id_slice;
+                break;
+            case "indexOf":
+                id = Id_indexOf;
+                break;
+            case "lastIndexOf":
+                id = Id_lastIndexOf;
+                break;
+            case "every":
+                id = Id_every;
+                break;
+            case "filter":
+                id = Id_filter;
+                break;
+            case "forEach":
+                id = Id_forEach;
+                break;
+            case "map":
+                id = Id_map;
+                break;
+            case "some":
+                id = Id_some;
+                break;
+            case "find":
+                id = Id_find;
+                break;
+            case "findIndex":
+                id = Id_findIndex;
+                break;
+            case "reduce":
+                id = Id_reduce;
+                break;
+            case "reduceRight":
+                id = Id_reduceRight;
+                break;
+            case "fill":
+                id = Id_fill;
+                break;
+            case "keys":
+                id = Id_keys;
+                break;
+            case "values":
+                id = Id_values;
+                break;
+            case "entries":
+                id = Id_entries;
+                break;
+            case "includes":
+                id = Id_includes;
+                break;
+            case "copyWithin":
+                id = Id_copyWithin;
+                break;
+            default:
+                id = 0;
+                break;
         }
-// #/generated#
         return id;
     }
 
-    private static final int
-        Id_constructor          = 1,
-        Id_toString             = 2,
-        Id_toLocaleString       = 3,
-        Id_toSource             = 4,
-        Id_join                 = 5,
-        Id_reverse              = 6,
-        Id_sort                 = 7,
-        Id_push                 = 8,
-        Id_pop                  = 9,
-        Id_shift                = 10,
-        Id_unshift              = 11,
-        Id_splice               = 12,
-        Id_concat               = 13,
-        Id_slice                = 14,
-        Id_indexOf              = 15,
-        Id_lastIndexOf          = 16,
-        Id_every                = 17,
-        Id_filter               = 18,
-        Id_forEach              = 19,
-        Id_map                  = 20,
-        Id_some                 = 21,
-        Id_find                 = 22,
-        Id_findIndex            = 23,
-        Id_reduce               = 24,
-        Id_reduceRight          = 25,
-        SymbolId_iterator       = 26,
-
-        MAX_PROTOTYPE_ID        = SymbolId_iterator;
-
-// #/string_id_map#
-
-    private static final int
-        ConstructorId_join                 = -Id_join,
-        ConstructorId_reverse              = -Id_reverse,
-        ConstructorId_sort                 = -Id_sort,
-        ConstructorId_push                 = -Id_push,
-        ConstructorId_pop                  = -Id_pop,
-        ConstructorId_shift                = -Id_shift,
-        ConstructorId_unshift              = -Id_unshift,
-        ConstructorId_splice               = -Id_splice,
-        ConstructorId_concat               = -Id_concat,
-        ConstructorId_slice                = -Id_slice,
-        ConstructorId_indexOf              = -Id_indexOf,
-        ConstructorId_lastIndexOf          = -Id_lastIndexOf,
-        ConstructorId_every                = -Id_every,
-        ConstructorId_filter               = -Id_filter,
-        ConstructorId_forEach              = -Id_forEach,
-        ConstructorId_map                  = -Id_map,
-        ConstructorId_some                 = -Id_some,
-        ConstructorId_find                 = -Id_find,
-        ConstructorId_findIndex            = -Id_findIndex,
-        ConstructorId_reduce               = -Id_reduce,
-        ConstructorId_reduceRight          = -Id_reduceRight,
-        ConstructorId_isArray              = -26;
+    private static final int Id_constructor = 1,
+            Id_toString = 2,
+            Id_toLocaleString = 3,
+            Id_toSource = 4,
+            Id_join = 5,
+            Id_reverse = 6,
+            Id_sort = 7,
+            Id_push = 8,
+            Id_pop = 9,
+            Id_shift = 10,
+            Id_unshift = 11,
+            Id_splice = 12,
+            Id_concat = 13,
+            Id_slice = 14,
+            Id_indexOf = 15,
+            Id_lastIndexOf = 16,
+            Id_every = 17,
+            Id_filter = 18,
+            Id_forEach = 19,
+            Id_map = 20,
+            Id_some = 21,
+            Id_find = 22,
+            Id_findIndex = 23,
+            Id_reduce = 24,
+            Id_reduceRight = 25,
+            Id_fill = 26,
+            Id_keys = 27,
+            Id_values = 28,
+            Id_entries = 29,
+            Id_includes = 30,
+            Id_copyWithin = 31,
+            SymbolId_iterator = 32,
+            MAX_PROTOTYPE_ID = SymbolId_iterator;
+    private static final int ConstructorId_join = -Id_join,
+            ConstructorId_reverse = -Id_reverse,
+            ConstructorId_sort = -Id_sort,
+            ConstructorId_push = -Id_push,
+            ConstructorId_pop = -Id_pop,
+            ConstructorId_shift = -Id_shift,
+            ConstructorId_unshift = -Id_unshift,
+            ConstructorId_splice = -Id_splice,
+            ConstructorId_concat = -Id_concat,
+            ConstructorId_slice = -Id_slice,
+            ConstructorId_indexOf = -Id_indexOf,
+            ConstructorId_lastIndexOf = -Id_lastIndexOf,
+            ConstructorId_every = -Id_every,
+            ConstructorId_filter = -Id_filter,
+            ConstructorId_forEach = -Id_forEach,
+            ConstructorId_map = -Id_map,
+            ConstructorId_some = -Id_some,
+            ConstructorId_find = -Id_find,
+            ConstructorId_findIndex = -Id_findIndex,
+            ConstructorId_reduce = -Id_reduce,
+            ConstructorId_reduceRight = -Id_reduceRight,
+            ConstructorId_isArray = -26,
+            ConstructorId_of = -27,
+            ConstructorId_from = -28;
 
-    /**
-     * Internal representation of the JavaScript array's length property.
-     */
+    /** Internal representation of the JavaScript array's length property. */
     private long length;
 
-    /**
-     * Attributes of the array's length property
-     */
+    /** Attributes of the array's length property */
     private int lengthAttr = DONTENUM | PERMANENT;
 
+    /** modCount required for subList/iterators */
+    private transient int modCount;
+
     /**
-     * Fast storage for dense arrays. Sparse arrays will use the superclass's
-     * hashtable storage scheme.
+     * Fast storage for dense arrays. Sparse arrays will use the superclass's hashtable storage
+     * scheme.
      */
     private Object[] dense;
 
-    /**
-     * True if all numeric properties are stored in <code>dense</code>.
-     */
+    /** True if all numeric properties are stored in <code>dense</code>. */
     private boolean denseOnly;
 
-    /**
-     * The maximum size of <code>dense</code> that will be allocated initially.
-     */
+    /** The maximum size of <code>dense</code> that will be allocated initially. */
     private static int maximumInitialCapacity = 10000;
 
-    /**
-     * The default capacity for <code>dense</code>.
-     */
+    /** The default capacity for <code>dense</code>. */
     private static final int DEFAULT_INITIAL_CAPACITY = 10;
 
-    /**
-     * The factor to grow <code>dense</code> by.
-     */
+    /** The factor to grow <code>dense</code> by. */
     private static final double GROW_FACTOR = 1.5;
-    private static final int MAX_PRE_GROW_SIZE = (int)(Integer.MAX_VALUE / GROW_FACTOR);
+
+    private static final int MAX_PRE_GROW_SIZE = (int) (Integer.MAX_VALUE / GROW_FACTOR);
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeBigInt.java rhino-1.7.14/src/org/mozilla/javascript/NativeBigInt.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeBigInt.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeBigInt.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,223 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+import java.math.BigInteger;
+import java.util.Arrays;
+
+/** This class implements the BigInt native object. */
+final class NativeBigInt extends IdScriptableObject {
+    private static final long serialVersionUID = 1335609231306775449L;
+
+    private static final Object BIG_INT_TAG = "BigInt";
+
+    static void init(Scriptable scope, boolean sealed) {
+        NativeBigInt obj = new NativeBigInt(BigInteger.ZERO);
+        obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
+    }
+
+    NativeBigInt(BigInteger bigInt) {
+        bigIntValue = bigInt;
+    }
+
+    @Override
+    public String getClassName() {
+        return "BigInt";
+    }
+
+    @Override
+    protected void fillConstructorProperties(IdFunctionObject ctor) {
+        addIdFunctionProperty(ctor, BIG_INT_TAG, ConstructorId_asIntN, "asIntN", 2);
+        addIdFunctionProperty(ctor, BIG_INT_TAG, ConstructorId_asUintN, "asUintN", 2);
+
+        super.fillConstructorProperties(ctor);
+    }
+
+    @Override
+    protected void initPrototypeId(int id) {
+        if (id == SymbolId_toStringTag) {
+            initPrototypeValue(
+                    SymbolId_toStringTag,
+                    SymbolKey.TO_STRING_TAG,
+                    getClassName(),
+                    DONTENUM | READONLY);
+            return;
+        }
+
+        String s;
+        int arity;
+        switch (id) {
+            case Id_constructor:
+                arity = 1;
+                s = "constructor";
+                break;
+            case Id_toString:
+                arity = 0;
+                s = "toString";
+                break;
+            case Id_toLocaleString:
+                arity = 0;
+                s = "toLocaleString";
+                break;
+            case Id_toSource:
+                arity = 0;
+                s = "toSource";
+                break;
+            case Id_valueOf:
+                arity = 0;
+                s = "valueOf";
+                break;
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
+        }
+        initPrototypeMethod(BIG_INT_TAG, id, s, arity);
+    }
+
+    @Override
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        if (!f.hasTag(BIG_INT_TAG)) {
+            return super.execIdCall(f, cx, scope, thisObj, args);
+        }
+        int id = f.methodId();
+        if (id == Id_constructor) {
+            if (thisObj == null) {
+                // new BigInt(val) throws TypeError.
+                throw ScriptRuntime.typeErrorById("msg.not.ctor", BIG_INT_TAG);
+            }
+            BigInteger val = (args.length >= 1) ? ScriptRuntime.toBigInt(args[0]) : BigInteger.ZERO;
+            // BigInt(val) converts val to a BigInteger value.
+            return val;
+
+        } else if (id < Id_constructor) {
+            return execConstructorCall(id, args);
+        }
+
+        // The rest of BigInt.prototype methods require thisObj to be BigInt
+        BigInteger value = ensureType(thisObj, NativeBigInt.class, f).bigIntValue;
+
+        switch (id) {
+            case Id_toString:
+            case Id_toLocaleString:
+                {
+                    // toLocaleString is just an alias for toString for now
+                    int base =
+                            (args.length == 0 || args[0] == Undefined.instance)
+                                    ? 10
+                                    : ScriptRuntime.toInt32(args[0]);
+                    return ScriptRuntime.bigIntToString(value, base);
+                }
+
+            case Id_toSource:
+                return "(new BigInt(" + ScriptRuntime.toString(value) + "))";
+
+            case Id_valueOf:
+                return value;
+
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
+        }
+    }
+
+    private static Object execConstructorCall(int id, Object[] args) {
+        switch (id) {
+            case ConstructorId_asIntN:
+            case ConstructorId_asUintN:
+                {
+                    int bits =
+                            ScriptRuntime.toIndex(args.length < 1 ? Undefined.instance : args[0]);
+                    BigInteger bigInt =
+                            ScriptRuntime.toBigInt(args.length < 2 ? Undefined.instance : args[1]);
+
+                    if (bits == 0) {
+                        return BigInteger.ZERO;
+                    }
+
+                    byte[] bytes = bigInt.toByteArray();
+
+                    int newBytesLen = (bits / Byte.SIZE) + 1;
+                    if (newBytesLen > bytes.length) {
+                        return bigInt;
+                    }
+
+                    byte[] newBytes =
+                            Arrays.copyOfRange(bytes, bytes.length - newBytesLen, bytes.length);
+
+                    int mod = bits % Byte.SIZE;
+                    switch (id) {
+                        case ConstructorId_asIntN:
+                            if (mod == 0) {
+                                newBytes[0] = newBytes[1] < 0 ? (byte) -1 : 0;
+                            } else if ((newBytes[0] & (1 << (mod - 1))) != 0) {
+                                newBytes[0] |= -1 << mod;
+                            } else {
+                                newBytes[0] &= (1 << mod) - 1;
+                            }
+                            break;
+                        case ConstructorId_asUintN:
+                            newBytes[0] &= (1 << mod) - 1;
+                            break;
+                    }
+                    return new BigInteger(newBytes);
+                }
+
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
+        }
+    }
+
+    @Override
+    public String toString() {
+        return ScriptRuntime.bigIntToString(bigIntValue, 10);
+    }
+
+    @Override
+    protected int findPrototypeId(Symbol k) {
+        if (SymbolKey.TO_STRING_TAG.equals(k)) {
+            return SymbolId_toStringTag;
+        }
+        return 0;
+    }
+
+    @Override
+    protected int findPrototypeId(String s) {
+        int id;
+        switch (s) {
+            case "constructor":
+                id = Id_constructor;
+                break;
+            case "toString":
+                id = Id_toString;
+                break;
+            case "toLocaleString":
+                id = Id_toLocaleString;
+                break;
+            case "toSource":
+                id = Id_toSource;
+                break;
+            case "valueOf":
+                id = Id_valueOf;
+                break;
+            default:
+                id = 0;
+                break;
+        }
+        return id;
+    }
+
+    private static final int ConstructorId_asIntN = -1,
+            ConstructorId_asUintN = -2,
+            Id_constructor = 1,
+            Id_toString = 2,
+            Id_toLocaleString = 3,
+            Id_toSource = 4,
+            Id_valueOf = 5,
+            SymbolId_toStringTag = 6,
+            MAX_PROTOTYPE_ID = SymbolId_toStringTag;
+
+    private BigInteger bigIntValue;
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeBoolean.java rhino-1.7.14/src/org/mozilla/javascript/NativeBoolean.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeBoolean.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeBoolean.java	2022-01-06 22:57:21.000000000 +0100
@@ -7,30 +7,26 @@
 package org.mozilla.javascript;
 
 /**
- * This class implements the Boolean native object.
- * See ECMA 15.6.
+ * This class implements the Boolean native object. See ECMA 15.6.
+ *
  * @author Norris Boyd
  */
-final class NativeBoolean extends IdScriptableObject
-{
-    static final long serialVersionUID = -3716996899943880933L;
+final class NativeBoolean extends IdScriptableObject {
+    private static final long serialVersionUID = -3716996899943880933L;
 
     private static final Object BOOLEAN_TAG = "Boolean";
 
-    static void init(Scriptable scope, boolean sealed)
-    {
+    static void init(Scriptable scope, boolean sealed) {
         NativeBoolean obj = new NativeBoolean(false);
         obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
     }
 
-    NativeBoolean(boolean b)
-    {
+    NativeBoolean(boolean b) {
         booleanValue = b;
     }
 
     @Override
-    public String getClassName()
-    {
+    public String getClassName() {
         return "Boolean";
     }
 
@@ -38,30 +34,40 @@
     public Object getDefaultValue(Class<?> typeHint) {
         // This is actually non-ECMA, but will be proposed
         // as a change in round 2.
-        if (typeHint == ScriptRuntime.BooleanClass)
-            return ScriptRuntime.wrapBoolean(booleanValue);
+        if (typeHint == ScriptRuntime.BooleanClass) return ScriptRuntime.wrapBoolean(booleanValue);
         return super.getDefaultValue(typeHint);
     }
 
     @Override
-    protected void initPrototypeId(int id)
-    {
+    protected void initPrototypeId(int id) {
         String s;
         int arity;
         switch (id) {
-          case Id_constructor: arity=1; s="constructor"; break;
-          case Id_toString:    arity=0; s="toString";    break;
-          case Id_toSource:    arity=0; s="toSource";    break;
-          case Id_valueOf:     arity=0; s="valueOf";     break;
-          default: throw new IllegalArgumentException(String.valueOf(id));
+            case Id_constructor:
+                arity = 1;
+                s = "constructor";
+                break;
+            case Id_toString:
+                arity = 0;
+                s = "toString";
+                break;
+            case Id_toSource:
+                arity = 0;
+                s = "toSource";
+                break;
+            case Id_valueOf:
+                arity = 0;
+                s = "valueOf";
+                break;
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
         }
         initPrototypeMethod(BOOLEAN_TAG, id, s, arity);
     }
 
     @Override
-    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
-                             Scriptable thisObj, Object[] args)
-    {
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         if (!f.hasTag(BOOLEAN_TAG)) {
             return super.execIdCall(f, cx, scope, thisObj, args);
         }
@@ -72,10 +78,16 @@
             if (args.length == 0) {
                 b = false;
             } else {
-                b = args[0] instanceof ScriptableObject &&
-                        ((ScriptableObject) args[0]).avoidObjectDetection()
-                    ? true
-                    : ScriptRuntime.toBoolean(args[0]);
+                // see special handling in ScriptRuntime.toBoolean(Object)
+                // avoidObjectDetection() is used to implement document.all
+                // see Note on page
+                //
+                // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean
+                b =
+                        args[0] instanceof ScriptableObject
+                                        && ((ScriptableObject) args[0]).avoidObjectDetection()
+                                ? false
+                                : ScriptRuntime.toBoolean(args[0]);
             }
             if (thisObj == null) {
                 // new Boolean(val) creates a new boolean object.
@@ -86,56 +98,49 @@
         }
 
         // The rest of Boolean.prototype methods require thisObj to be Boolean
-
-        if (!(thisObj instanceof NativeBoolean))
-            throw incompatibleCallError(f);
-        boolean value = ((NativeBoolean)thisObj).booleanValue;
+        boolean value = ensureType(thisObj, NativeBoolean.class, f).booleanValue;
 
         switch (id) {
+            case Id_toString:
+                return value ? "true" : "false";
 
-          case Id_toString:
-            return value ? "true" : "false";
-
-          case Id_toSource:
-            return value ? "(new Boolean(true))" : "(new Boolean(false))";
+            case Id_toSource:
+                return value ? "(new Boolean(true))" : "(new Boolean(false))";
 
-          case Id_valueOf:
-            return ScriptRuntime.wrapBoolean(value);
+            case Id_valueOf:
+                return ScriptRuntime.wrapBoolean(value);
         }
         throw new IllegalArgumentException(String.valueOf(id));
     }
 
-// #string_id_map#
-
     @Override
-    protected int findPrototypeId(String s)
-    {
+    protected int findPrototypeId(String s) {
         int id;
-// #generated# Last update: 2007-05-09 08:15:31 EDT
-        L0: { id = 0; String X = null; int c;
-            int s_length = s.length();
-            if (s_length==7) { X="valueOf";id=Id_valueOf; }
-            else if (s_length==8) {
-                c=s.charAt(3);
-                if (c=='o') { X="toSource";id=Id_toSource; }
-                else if (c=='t') { X="toString";id=Id_toString; }
-            }
-            else if (s_length==11) { X="constructor";id=Id_constructor; }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-            break L0;
+        switch (s) {
+            case "constructor":
+                id = Id_constructor;
+                break;
+            case "toString":
+                id = Id_toString;
+                break;
+            case "toSource":
+                id = Id_toSource;
+                break;
+            case "valueOf":
+                id = Id_valueOf;
+                break;
+            default:
+                id = 0;
+                break;
         }
-// #/generated#
         return id;
     }
 
-    private static final int
-        Id_constructor          = 1,
-        Id_toString             = 2,
-        Id_toSource             = 3,
-        Id_valueOf              = 4,
-        MAX_PROTOTYPE_ID        = 4;
-
-// #/string_id_map#
+    private static final int Id_constructor = 1,
+            Id_toString = 2,
+            Id_toSource = 3,
+            Id_valueOf = 4,
+            MAX_PROTOTYPE_ID = 4;
 
     private boolean booleanValue;
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeCall.java rhino-1.7.14/src/org/mozilla/javascript/NativeCall.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeCall.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeCall.java	2022-01-06 22:57:21.000000000 +0100
@@ -16,7 +16,7 @@
  */
 public final class NativeCall extends IdScriptableObject
 {
-    static final long serialVersionUID = -7471457301304454454L;
+    private static final long serialVersionUID = -7471457301304454454L;
 
     private static final Object CALL_TAG = "Call";
 
@@ -61,10 +61,12 @@
             for (int i = paramCount; i < paramAndVarCount; ++i) {
                 String name = function.getParamOrVarName(i);
                 if (!super.has(name, this)) {
-                    if (function.getParamOrVarConst(i))
+                    if (function.getParamOrVarConst(i)) {
                         defineProperty(name, Undefined.instance, CONST);
-                    else
+                    } else if (!(function instanceof InterpretedFunction)
+                                || ((InterpretedFunction) function).hasFunctionNamed(name)) {
                         defineProperty(name, Undefined.instance, PERMANENT);
+                    }
                 }
             }
         }
@@ -105,7 +107,7 @@
         int id = f.methodId();
         if (id == Id_constructor) {
             if (thisObj != null) {
-                throw Context.reportRuntimeError1("msg.only.from.new", "Call");
+                throw Context.reportRuntimeErrorById("msg.only.from.new", "Call");
             }
             ScriptRuntime.checkDeprecated(cx, "Call");
             NativeCall result = new NativeCall();
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeCallSite.java rhino-1.7.14/src/org/mozilla/javascript/NativeCallSite.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeCallSite.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeCallSite.java	2022-01-06 22:57:21.000000000 +0100
@@ -7,242 +7,272 @@
 package org.mozilla.javascript;
 
 /**
- * This class is used by the V8 extension "Error.prepareStackTrace." It is passed to
- * that function, which may then use it to format the stack as it sees fit.
+ * This class is used by the V8 extension "Error.prepareStackTrace." It is passed to that function,
+ * which may then use it to format the stack as it sees fit.
  */
-
-public class NativeCallSite extends IdScriptableObject
-{
+public class NativeCallSite extends IdScriptableObject {
+    private static final long serialVersionUID = 2688372752566593594L;
     private static final String CALLSITE_TAG = "CallSite";
-
     private ScriptStackElement element;
 
-    static void init(Scriptable scope, boolean sealed)
-    {
+    static void init(Scriptable scope, boolean sealed) {
         NativeCallSite cs = new NativeCallSite();
         cs.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
     }
 
-    static NativeCallSite make(Scriptable scope, Scriptable ctorObj)
-    {
+    static NativeCallSite make(Scriptable scope, Scriptable ctorObj) {
         NativeCallSite cs = new NativeCallSite();
-        Scriptable proto = (Scriptable)(ctorObj.get("prototype", ctorObj));
+        Scriptable proto = (Scriptable) (ctorObj.get("prototype", ctorObj));
         cs.setParentScope(scope);
         cs.setPrototype(proto);
         return cs;
     }
 
-    private NativeCallSite()
-    {
-    }
+    private NativeCallSite() {}
 
-    void setElement(ScriptStackElement elt)
-    {
+    void setElement(ScriptStackElement elt) {
         this.element = elt;
     }
 
     @Override
-    public String getClassName()
-    {
+    public String getClassName() {
         return "CallSite";
     }
 
     @Override
-    protected void initPrototypeId(int id)
-    {
+    protected void initPrototypeId(int id) {
         String s;
         int arity;
         switch (id) {
-        case Id_constructor: arity = 0; s="constructor"; break;
-        case Id_getThis: arity = 0; s="getThis"; break;
-        case Id_getTypeName: arity = 0; s="getTypeName"; break;
-        case Id_getFunction: arity = 0; s="getFunction"; break;
-        case Id_getFunctionName: arity = 0; s="getFunctionName"; break;
-        case Id_getMethodName: arity = 0; s="getMethodName"; break;
-        case Id_getFileName: arity = 0; s="getFileName"; break;
-        case Id_getLineNumber: arity = 0; s="getLineNumber"; break;
-        case Id_getColumnNumber: arity = 0; s="getColumnNumber"; break;
-        case Id_getEvalOrigin: arity = 0; s="getEvalOrigin"; break;
-        case Id_isToplevel: arity = 0; s="isToplevel"; break;
-        case Id_isEval: arity = 0; s="isEval"; break;
-        case Id_isNative: arity = 0; s="isNative"; break;
-        case Id_isConstructor: arity = 0; s="isConstructor"; break;
-        case Id_toString: arity = 0; s="toString"; break;
-        default: throw new IllegalArgumentException(String.valueOf(id));
+            case Id_constructor:
+                arity = 0;
+                s = "constructor";
+                break;
+            case Id_getThis:
+                arity = 0;
+                s = "getThis";
+                break;
+            case Id_getTypeName:
+                arity = 0;
+                s = "getTypeName";
+                break;
+            case Id_getFunction:
+                arity = 0;
+                s = "getFunction";
+                break;
+            case Id_getFunctionName:
+                arity = 0;
+                s = "getFunctionName";
+                break;
+            case Id_getMethodName:
+                arity = 0;
+                s = "getMethodName";
+                break;
+            case Id_getFileName:
+                arity = 0;
+                s = "getFileName";
+                break;
+            case Id_getLineNumber:
+                arity = 0;
+                s = "getLineNumber";
+                break;
+            case Id_getColumnNumber:
+                arity = 0;
+                s = "getColumnNumber";
+                break;
+            case Id_getEvalOrigin:
+                arity = 0;
+                s = "getEvalOrigin";
+                break;
+            case Id_isToplevel:
+                arity = 0;
+                s = "isToplevel";
+                break;
+            case Id_isEval:
+                arity = 0;
+                s = "isEval";
+                break;
+            case Id_isNative:
+                arity = 0;
+                s = "isNative";
+                break;
+            case Id_isConstructor:
+                arity = 0;
+                s = "isConstructor";
+                break;
+            case Id_toString:
+                arity = 0;
+                s = "toString";
+                break;
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
         }
         initPrototypeMethod(CALLSITE_TAG, id, s, arity);
     }
 
     @Override
-    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
-                             Scriptable thisObj, Object[] args)
-    {
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         if (!f.hasTag(CALLSITE_TAG)) {
             return super.execIdCall(f, cx, scope, thisObj, args);
         }
         int id = f.methodId();
         switch (id) {
-        case Id_constructor:
-            return make(scope, f);
-        case Id_getFunctionName:
-            return getFunctionName(thisObj);
-        case Id_getFileName:
-            return getFileName(thisObj);
-        case Id_getLineNumber:
-            return getLineNumber(thisObj);
-        case Id_getThis:
-        case Id_getTypeName:
-        case Id_getFunction:
-        case Id_getColumnNumber:
-            return getUndefined();
-        case Id_getMethodName:
-            return getNull();
-        case Id_getEvalOrigin:
-        case Id_isEval:
-        case Id_isConstructor:
-        case Id_isNative:
-        case Id_isToplevel:
-            return getFalse();
-        case Id_toString:
-            return js_toString(thisObj);
-        default:
-            throw new IllegalArgumentException(String.valueOf(id));
+            case Id_constructor:
+                return make(scope, f);
+            case Id_getFunctionName:
+                return getFunctionName(thisObj);
+            case Id_getFileName:
+                return getFileName(thisObj);
+            case Id_getLineNumber:
+                return getLineNumber(thisObj);
+            case Id_getThis:
+            case Id_getTypeName:
+            case Id_getFunction:
+            case Id_getColumnNumber:
+                return Undefined.instance;
+            case Id_getMethodName:
+                return null;
+            case Id_getEvalOrigin:
+            case Id_isEval:
+            case Id_isConstructor:
+            case Id_isNative:
+            case Id_isToplevel:
+                return Boolean.FALSE;
+            case Id_toString:
+                return js_toString(thisObj);
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
         }
     }
 
     @Override
-    public String toString()
-    {
+    public String toString() {
         if (element == null) {
             return "";
         }
         return element.toString();
     }
 
-    private Object js_toString(Scriptable obj)
-    {
-        while(obj != null && !(obj instanceof NativeCallSite)) {
+    private static Object js_toString(Scriptable obj) {
+        while (obj != null && !(obj instanceof NativeCallSite)) {
             obj = obj.getPrototype();
         }
         if (obj == null) {
             return NOT_FOUND;
         }
-        NativeCallSite cs = (NativeCallSite)obj;
+        NativeCallSite cs = (NativeCallSite) obj;
         StringBuilder sb = new StringBuilder();
         cs.element.renderJavaStyle(sb);
         return sb.toString();
     }
 
-    private Object getUndefined()
-    {
-        return Undefined.instance;
-    }
-
-    private Object getNull()
-    {
-        return null;
-    }
-
-    private Object getFalse()
-    {
-        return Boolean.FALSE;
-    }
-
-    private Object getFunctionName(Scriptable obj)
-    {
-        while(obj != null && !(obj instanceof NativeCallSite)) {
+    private static Object getFunctionName(Scriptable obj) {
+        while (obj != null && !(obj instanceof NativeCallSite)) {
             obj = obj.getPrototype();
         }
         if (obj == null) {
             return NOT_FOUND;
         }
-        NativeCallSite cs = (NativeCallSite)obj;
+        NativeCallSite cs = (NativeCallSite) obj;
         return (cs.element == null ? null : cs.element.functionName);
     }
 
-    private Object getFileName(Scriptable obj)
-    {
-        while(obj != null && !(obj instanceof NativeCallSite)) {
+    private static Object getFileName(Scriptable obj) {
+        while (obj != null && !(obj instanceof NativeCallSite)) {
             obj = obj.getPrototype();
         }
         if (obj == null) {
             return NOT_FOUND;
         }
-        NativeCallSite cs = (NativeCallSite)obj;
+        NativeCallSite cs = (NativeCallSite) obj;
         return (cs.element == null ? null : cs.element.fileName);
     }
 
-    private Object getLineNumber(Scriptable obj)
-    {
-        while(obj != null && !(obj instanceof NativeCallSite)) {
+    private static Object getLineNumber(Scriptable obj) {
+        while (obj != null && !(obj instanceof NativeCallSite)) {
             obj = obj.getPrototype();
         }
         if (obj == null) {
             return NOT_FOUND;
         }
-        NativeCallSite cs = (NativeCallSite)obj;
+        NativeCallSite cs = (NativeCallSite) obj;
         if ((cs.element == null) || (cs.element.lineNumber < 0)) {
             return Undefined.instance;
         }
-        return cs.element.lineNumber;
+        return Integer.valueOf(cs.element.lineNumber);
     }
 
-// #string_id_map#
-
     @Override
-    protected int findPrototypeId(String s)
-    {
+    protected int findPrototypeId(String s) {
         int id;
-// #generated# Last update: 2015-03-02 23:42:12 PST
-        L0: { id = 0; String X = null; int c;
-            L: switch (s.length()) {
-            case 6: X="isEval";id=Id_isEval; break L;
-            case 7: X="getThis";id=Id_getThis; break L;
-            case 8: c=s.charAt(0);
-                if (c=='i') { X="isNative";id=Id_isNative; }
-                else if (c=='t') { X="toString";id=Id_toString; }
-                break L;
-            case 10: X="isToplevel";id=Id_isToplevel; break L;
-            case 11: switch (s.charAt(4)) {
-                case 'i': X="getFileName";id=Id_getFileName; break L;
-                case 't': X="constructor";id=Id_constructor; break L;
-                case 'u': X="getFunction";id=Id_getFunction; break L;
-                case 'y': X="getTypeName";id=Id_getTypeName; break L;
-                } break L;
-            case 13: switch (s.charAt(3)) {
-                case 'E': X="getEvalOrigin";id=Id_getEvalOrigin; break L;
-                case 'L': X="getLineNumber";id=Id_getLineNumber; break L;
-                case 'M': X="getMethodName";id=Id_getMethodName; break L;
-                case 'o': X="isConstructor";id=Id_isConstructor; break L;
-                } break L;
-            case 15: c=s.charAt(3);
-                if (c=='C') { X="getColumnNumber";id=Id_getColumnNumber; }
-                else if (c=='F') { X="getFunctionName";id=Id_getFunctionName; }
-                break L;
-            }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-            break L0;
+        switch (s) {
+            case "constructor":
+                id = Id_constructor;
+                break;
+            case "getThis":
+                id = Id_getThis;
+                break;
+            case "getTypeName":
+                id = Id_getTypeName;
+                break;
+            case "getFunction":
+                id = Id_getFunction;
+                break;
+            case "getFunctionName":
+                id = Id_getFunctionName;
+                break;
+            case "getMethodName":
+                id = Id_getMethodName;
+                break;
+            case "getFileName":
+                id = Id_getFileName;
+                break;
+            case "getLineNumber":
+                id = Id_getLineNumber;
+                break;
+            case "getColumnNumber":
+                id = Id_getColumnNumber;
+                break;
+            case "getEvalOrigin":
+                id = Id_getEvalOrigin;
+                break;
+            case "isToplevel":
+                id = Id_isToplevel;
+                break;
+            case "isEval":
+                id = Id_isEval;
+                break;
+            case "isNative":
+                id = Id_isNative;
+                break;
+            case "isConstructor":
+                id = Id_isConstructor;
+                break;
+            case "toString":
+                id = Id_toString;
+                break;
+            default:
+                id = 0;
+                break;
         }
-// #/generated#
         return id;
     }
 
-    private static final int
-      Id_constructor = 1,
-      Id_getThis = 2,
-      Id_getTypeName = 3,
-      Id_getFunction = 4,
-      Id_getFunctionName = 5,
-      Id_getMethodName = 6,
-      Id_getFileName = 7,
-      Id_getLineNumber = 8,
-      Id_getColumnNumber = 9,
-      Id_getEvalOrigin = 10,
-      Id_isToplevel = 11,
-      Id_isEval = 12,
-      Id_isNative = 13,
-      Id_isConstructor = 14,
-      Id_toString = 15,
-      MAX_PROTOTYPE_ID = 15;
-// #/string_id_map#
-}
\ No newline at end of file
+    private static final int Id_constructor = 1,
+            Id_getThis = 2,
+            Id_getTypeName = 3,
+            Id_getFunction = 4,
+            Id_getFunctionName = 5,
+            Id_getMethodName = 6,
+            Id_getFileName = 7,
+            Id_getLineNumber = 8,
+            Id_getColumnNumber = 9,
+            Id_getEvalOrigin = 10,
+            Id_isToplevel = 11,
+            Id_isEval = 12,
+            Id_isNative = 13,
+            Id_isConstructor = 14,
+            Id_toString = 15,
+            MAX_PROTOTYPE_ID = 15;
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeCollectionIterator.java rhino-1.7.14/src/org/mozilla/javascript/NativeCollectionIterator.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeCollectionIterator.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeCollectionIterator.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,78 @@
+package org.mozilla.javascript;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Collections;
+import java.util.Iterator;
+
+public class NativeCollectionIterator extends ES6Iterator {
+    private static final long serialVersionUID = 7094840979404373443L;
+    private String className;
+    private Type type;
+    private transient Iterator<Hashtable.Entry> iterator = Collections.emptyIterator();
+    enum Type { KEYS, VALUES, BOTH }
+
+    static void init(ScriptableObject scope, String tag, boolean sealed) {
+        ES6Iterator.init(scope, sealed, new NativeCollectionIterator(tag), tag);
+    }
+
+    public NativeCollectionIterator(String tag)
+    {
+        this.className = tag;
+        this.iterator = Collections.emptyIterator();
+        this.type = Type.BOTH;
+    }
+
+    public NativeCollectionIterator(
+        Scriptable scope, String className,
+        Type type, Iterator<Hashtable.Entry> iterator)
+    {
+        super(scope, className);
+        this.className = className;
+        this.iterator = iterator;
+        this.type = type;
+    }
+
+    @Override
+    public String getClassName() {
+        return className;
+    }
+
+    @Override
+    protected boolean isDone(Context cx, Scriptable scope) {
+        return !iterator.hasNext();
+    }
+
+    @Override
+    protected Object nextValue(Context cx, Scriptable scope) {
+        final Hashtable.Entry e = iterator.next();
+        switch (type) {
+            case KEYS:
+                return e.key;
+            case VALUES:
+                return e.value;
+            case BOTH:
+                return cx.newArray(scope, new Object[] { e.key, e.value });
+            default:
+                throw new AssertionError();
+        }
+    }
+
+    private void readObject(ObjectInputStream stream)
+        throws IOException, ClassNotFoundException
+    {
+        stream.defaultReadObject();
+        className = (String) stream.readObject();
+        type = (Type) stream.readObject();
+        iterator = Collections.emptyIterator();
+    }
+
+    private void writeObject(ObjectOutputStream stream)
+        throws IOException
+    {
+        stream.defaultWriteObject();
+        stream.writeObject(className);
+        stream.writeObject(type);
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeContinuation.java rhino-1.7.14/src/org/mozilla/javascript/NativeContinuation.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeContinuation.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeContinuation.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,102 +6,104 @@
 
 package org.mozilla.javascript;
 
-public final class NativeContinuation extends IdScriptableObject
-    implements Function
-{
-    static final long serialVersionUID = 1794167133757605367L;
+import java.util.Objects;
+
+public final class NativeContinuation extends IdScriptableObject implements Function {
+    private static final long serialVersionUID = 1794167133757605367L;
 
     private static final Object FTAG = "Continuation";
 
     private Object implementation;
 
-    public static void init(Context cx, Scriptable scope, boolean sealed)
-    {
+    public static void init(Context cx, Scriptable scope, boolean sealed) {
         NativeContinuation obj = new NativeContinuation();
         obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
     }
 
-    public Object getImplementation()
-    {
+    public Object getImplementation() {
         return implementation;
     }
 
-    public void initImplementation(Object implementation)
-    {
+    public void initImplementation(Object implementation) {
         this.implementation = implementation;
     }
 
     @Override
-    public String getClassName()
-    {
+    public String getClassName() {
         return "Continuation";
     }
 
-    public Scriptable construct(Context cx, Scriptable scope, Object[] args)
-    {
+    @Override
+    public Scriptable construct(Context cx, Scriptable scope, Object[] args) {
         throw Context.reportRuntimeError("Direct call is not supported");
     }
 
-    public Object call(Context cx, Scriptable scope, Scriptable thisObj,
-                       Object[] args)
-    {
+    @Override
+    public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         return Interpreter.restartContinuation(this, cx, scope, args);
     }
 
-    public static boolean isContinuationConstructor(IdFunctionObject f)
-    {
+    public static boolean isContinuationConstructor(IdFunctionObject f) {
         if (f.hasTag(FTAG) && f.methodId() == Id_constructor) {
             return true;
         }
         return false;
     }
 
+    /**
+     * Returns true if both continuations have equal implementations.
+     *
+     * @param c1 one continuation
+     * @param c2 another continuation
+     * @return true if the implementations of both continuations are equal, or they are both null.
+     * @throws NullPointerException if either continuation is null
+     */
+    public static boolean equalImplementations(NativeContinuation c1, NativeContinuation c2) {
+        return Objects.equals(c1.implementation, c2.implementation);
+    }
+
     @Override
-    protected void initPrototypeId(int id)
-    {
+    protected void initPrototypeId(int id) {
         String s;
         int arity;
         switch (id) {
-          case Id_constructor: arity=0; s="constructor"; break;
-          default: throw new IllegalArgumentException(String.valueOf(id));
+            case Id_constructor:
+                arity = 0;
+                s = "constructor";
+                break;
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
         }
         initPrototypeMethod(FTAG, id, s, arity);
     }
 
     @Override
-    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
-                             Scriptable thisObj, Object[] args)
-    {
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         if (!f.hasTag(FTAG)) {
             return super.execIdCall(f, cx, scope, thisObj, args);
         }
         int id = f.methodId();
         switch (id) {
-          case Id_constructor:
-            throw Context.reportRuntimeError("Direct call is not supported");
+            case Id_constructor:
+                throw Context.reportRuntimeError("Direct call is not supported");
         }
         throw new IllegalArgumentException(String.valueOf(id));
     }
 
-// #string_id_map#
-
     @Override
-    protected int findPrototypeId(String s)
-    {
+    protected int findPrototypeId(String s) {
         int id;
-// #generated# Last update: 2007-05-09 08:16:40 EDT
-        L0: { id = 0; String X = null;
-            if (s.length()==11) { X="constructor";id=Id_constructor; }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-            break L0;
+        switch (s) {
+            case "constructor":
+                id = Id_constructor;
+                break;
+            default:
+                id = 0;
+                break;
         }
-// #/generated#
         return id;
     }
 
-    private static final int
-        Id_constructor          = 1,
-        MAX_PROTOTYPE_ID        = 1;
-
-// #/string_id_map#
+    private static final int Id_constructor = 1, MAX_PROTOTYPE_ID = 1;
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeDate.java rhino-1.7.14/src/org/mozilla/javascript/NativeDate.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeDate.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeDate.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,432 +6,541 @@
 
 package org.mozilla.javascript;
 
-import java.util.Date;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
-
+import java.util.Date;
 import java.util.TimeZone;
 
 /**
- * This class implements the Date native object.
- * See ECMA 15.9.
- * @author Mike McCabe
+ * This class implements the Date native object. See ECMA 15.9.
  *
- * Significant parts of this code are adapted from the venerable jsdate.cpp (also Mozilla):
- * https://dxr.mozilla.org/mozilla-central/source/js/src/jsdate.cpp
+ * @author Mike McCabe
+ *     <p>Significant parts of this code are adapted from the venerable jsdate.cpp (also Mozilla):
+ *     https://dxr.mozilla.org/mozilla-central/source/js/src/jsdate.cpp
  */
-final class NativeDate extends IdScriptableObject
-{
-    static final long serialVersionUID = -8307438915861678966L;
+final class NativeDate extends IdScriptableObject {
+    private static final long serialVersionUID = -8307438915861678966L;
 
     private static final Object DATE_TAG = "Date";
 
     private static final String js_NaN_date_str = "Invalid Date";
 
-    static void init(Scriptable scope, boolean sealed)
-    {
+    static void init(Scriptable scope, boolean sealed) {
         NativeDate obj = new NativeDate();
         // Set the value of the prototype Date to NaN ('invalid date');
         obj.date = ScriptRuntime.NaN;
         obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
     }
 
-    private NativeDate()
-    {
-        if (thisTimeZone == null) {
-            // j.u.TimeZone is synchronized, so setting class statics from it
-            // should be OK.
-            thisTimeZone = TimeZone.getDefault();
-            LocalTZA = thisTimeZone.getRawOffset();
-        }
-    }
+    private NativeDate() {}
 
     @Override
-    public String getClassName()
-    {
+    public String getClassName() {
         return "Date";
     }
 
     @Override
-    public Object getDefaultValue(Class<?> typeHint)
-    {
-        if (typeHint == null)
-            typeHint = ScriptRuntime.StringClass;
+    public Object getDefaultValue(Class<?> typeHint) {
+        if (typeHint == null) typeHint = ScriptRuntime.StringClass;
         return super.getDefaultValue(typeHint);
     }
 
-    double getJSTimeValue()
-    {
+    double getJSTimeValue() {
         return date;
     }
 
     @Override
-    protected void fillConstructorProperties(IdFunctionObject ctor)
-    {
-        addIdFunctionProperty(ctor, DATE_TAG, ConstructorId_now,
-                              "now", 0);
-        addIdFunctionProperty(ctor, DATE_TAG, ConstructorId_parse,
-                              "parse", 1);
-        addIdFunctionProperty(ctor, DATE_TAG, ConstructorId_UTC,
-                              "UTC", 7);
+    protected void fillConstructorProperties(IdFunctionObject ctor) {
+        addIdFunctionProperty(ctor, DATE_TAG, ConstructorId_now, "now", 0);
+        addIdFunctionProperty(ctor, DATE_TAG, ConstructorId_parse, "parse", 1);
+        addIdFunctionProperty(ctor, DATE_TAG, ConstructorId_UTC, "UTC", 7);
         super.fillConstructorProperties(ctor);
     }
 
     @Override
-    protected void initPrototypeId(int id)
-    {
+    protected void initPrototypeId(int id) {
         String s;
         int arity;
         switch (id) {
-          case Id_constructor:        arity=7; s="constructor";        break;
-          case Id_toString:           arity=0; s="toString";           break;
-          case Id_toTimeString:       arity=0; s="toTimeString";       break;
-          case Id_toDateString:       arity=0; s="toDateString";       break;
-          case Id_toLocaleString:     arity=0; s="toLocaleString";     break;
-          case Id_toLocaleTimeString: arity=0; s="toLocaleTimeString"; break;
-          case Id_toLocaleDateString: arity=0; s="toLocaleDateString"; break;
-          case Id_toUTCString:        arity=0; s="toUTCString";        break;
-          case Id_toSource:           arity=0; s="toSource";           break;
-          case Id_valueOf:            arity=0; s="valueOf";            break;
-          case Id_getTime:            arity=0; s="getTime";            break;
-          case Id_getYear:            arity=0; s="getYear";            break;
-          case Id_getFullYear:        arity=0; s="getFullYear";        break;
-          case Id_getUTCFullYear:     arity=0; s="getUTCFullYear";     break;
-          case Id_getMonth:           arity=0; s="getMonth";           break;
-          case Id_getUTCMonth:        arity=0; s="getUTCMonth";        break;
-          case Id_getDate:            arity=0; s="getDate";            break;
-          case Id_getUTCDate:         arity=0; s="getUTCDate";         break;
-          case Id_getDay:             arity=0; s="getDay";             break;
-          case Id_getUTCDay:          arity=0; s="getUTCDay";          break;
-          case Id_getHours:           arity=0; s="getHours";           break;
-          case Id_getUTCHours:        arity=0; s="getUTCHours";        break;
-          case Id_getMinutes:         arity=0; s="getMinutes";         break;
-          case Id_getUTCMinutes:      arity=0; s="getUTCMinutes";      break;
-          case Id_getSeconds:         arity=0; s="getSeconds";         break;
-          case Id_getUTCSeconds:      arity=0; s="getUTCSeconds";      break;
-          case Id_getMilliseconds:    arity=0; s="getMilliseconds";    break;
-          case Id_getUTCMilliseconds: arity=0; s="getUTCMilliseconds"; break;
-          case Id_getTimezoneOffset:  arity=0; s="getTimezoneOffset";  break;
-          case Id_setTime:            arity=1; s="setTime";            break;
-          case Id_setMilliseconds:    arity=1; s="setMilliseconds";    break;
-          case Id_setUTCMilliseconds: arity=1; s="setUTCMilliseconds"; break;
-          case Id_setSeconds:         arity=2; s="setSeconds";         break;
-          case Id_setUTCSeconds:      arity=2; s="setUTCSeconds";      break;
-          case Id_setMinutes:         arity=3; s="setMinutes";         break;
-          case Id_setUTCMinutes:      arity=3; s="setUTCMinutes";      break;
-          case Id_setHours:           arity=4; s="setHours";           break;
-          case Id_setUTCHours:        arity=4; s="setUTCHours";        break;
-          case Id_setDate:            arity=1; s="setDate";            break;
-          case Id_setUTCDate:         arity=1; s="setUTCDate";         break;
-          case Id_setMonth:           arity=2; s="setMonth";           break;
-          case Id_setUTCMonth:        arity=2; s="setUTCMonth";        break;
-          case Id_setFullYear:        arity=3; s="setFullYear";        break;
-          case Id_setUTCFullYear:     arity=3; s="setUTCFullYear";     break;
-          case Id_setYear:            arity=1; s="setYear";            break;
-          case Id_toISOString:        arity=0; s="toISOString";        break;
-          case Id_toJSON:             arity=1; s="toJSON";             break;
-          default: throw new IllegalArgumentException(String.valueOf(id));
+            case Id_constructor:
+                arity = 7;
+                s = "constructor";
+                break;
+            case Id_toString:
+                arity = 0;
+                s = "toString";
+                break;
+            case Id_toTimeString:
+                arity = 0;
+                s = "toTimeString";
+                break;
+            case Id_toDateString:
+                arity = 0;
+                s = "toDateString";
+                break;
+            case Id_toLocaleString:
+                arity = 0;
+                s = "toLocaleString";
+                break;
+            case Id_toLocaleTimeString:
+                arity = 0;
+                s = "toLocaleTimeString";
+                break;
+            case Id_toLocaleDateString:
+                arity = 0;
+                s = "toLocaleDateString";
+                break;
+            case Id_toUTCString:
+                arity = 0;
+                s = "toUTCString";
+                break;
+            case Id_toSource:
+                arity = 0;
+                s = "toSource";
+                break;
+            case Id_valueOf:
+                arity = 0;
+                s = "valueOf";
+                break;
+            case Id_getTime:
+                arity = 0;
+                s = "getTime";
+                break;
+            case Id_getYear:
+                arity = 0;
+                s = "getYear";
+                break;
+            case Id_getFullYear:
+                arity = 0;
+                s = "getFullYear";
+                break;
+            case Id_getUTCFullYear:
+                arity = 0;
+                s = "getUTCFullYear";
+                break;
+            case Id_getMonth:
+                arity = 0;
+                s = "getMonth";
+                break;
+            case Id_getUTCMonth:
+                arity = 0;
+                s = "getUTCMonth";
+                break;
+            case Id_getDate:
+                arity = 0;
+                s = "getDate";
+                break;
+            case Id_getUTCDate:
+                arity = 0;
+                s = "getUTCDate";
+                break;
+            case Id_getDay:
+                arity = 0;
+                s = "getDay";
+                break;
+            case Id_getUTCDay:
+                arity = 0;
+                s = "getUTCDay";
+                break;
+            case Id_getHours:
+                arity = 0;
+                s = "getHours";
+                break;
+            case Id_getUTCHours:
+                arity = 0;
+                s = "getUTCHours";
+                break;
+            case Id_getMinutes:
+                arity = 0;
+                s = "getMinutes";
+                break;
+            case Id_getUTCMinutes:
+                arity = 0;
+                s = "getUTCMinutes";
+                break;
+            case Id_getSeconds:
+                arity = 0;
+                s = "getSeconds";
+                break;
+            case Id_getUTCSeconds:
+                arity = 0;
+                s = "getUTCSeconds";
+                break;
+            case Id_getMilliseconds:
+                arity = 0;
+                s = "getMilliseconds";
+                break;
+            case Id_getUTCMilliseconds:
+                arity = 0;
+                s = "getUTCMilliseconds";
+                break;
+            case Id_getTimezoneOffset:
+                arity = 0;
+                s = "getTimezoneOffset";
+                break;
+            case Id_setTime:
+                arity = 1;
+                s = "setTime";
+                break;
+            case Id_setMilliseconds:
+                arity = 1;
+                s = "setMilliseconds";
+                break;
+            case Id_setUTCMilliseconds:
+                arity = 1;
+                s = "setUTCMilliseconds";
+                break;
+            case Id_setSeconds:
+                arity = 2;
+                s = "setSeconds";
+                break;
+            case Id_setUTCSeconds:
+                arity = 2;
+                s = "setUTCSeconds";
+                break;
+            case Id_setMinutes:
+                arity = 3;
+                s = "setMinutes";
+                break;
+            case Id_setUTCMinutes:
+                arity = 3;
+                s = "setUTCMinutes";
+                break;
+            case Id_setHours:
+                arity = 4;
+                s = "setHours";
+                break;
+            case Id_setUTCHours:
+                arity = 4;
+                s = "setUTCHours";
+                break;
+            case Id_setDate:
+                arity = 1;
+                s = "setDate";
+                break;
+            case Id_setUTCDate:
+                arity = 1;
+                s = "setUTCDate";
+                break;
+            case Id_setMonth:
+                arity = 2;
+                s = "setMonth";
+                break;
+            case Id_setUTCMonth:
+                arity = 2;
+                s = "setUTCMonth";
+                break;
+            case Id_setFullYear:
+                arity = 3;
+                s = "setFullYear";
+                break;
+            case Id_setUTCFullYear:
+                arity = 3;
+                s = "setUTCFullYear";
+                break;
+            case Id_setYear:
+                arity = 1;
+                s = "setYear";
+                break;
+            case Id_toISOString:
+                arity = 0;
+                s = "toISOString";
+                break;
+            case Id_toJSON:
+                arity = 1;
+                s = "toJSON";
+                break;
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
         }
         initPrototypeMethod(DATE_TAG, id, s, arity);
     }
 
     @Override
-    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
-                             Scriptable thisObj, Object[] args)
-    {
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         if (!f.hasTag(DATE_TAG)) {
             return super.execIdCall(f, cx, scope, thisObj, args);
         }
         int id = f.methodId();
         switch (id) {
-          case ConstructorId_now:
-            return ScriptRuntime.wrapNumber(now());
+            case ConstructorId_now:
+                return ScriptRuntime.wrapNumber(now());
 
-          case ConstructorId_parse:
-            {
-                String dataStr = ScriptRuntime.toString(args, 0);
-                return ScriptRuntime.wrapNumber(date_parseString(dataStr));
-            }
-
-          case ConstructorId_UTC:
-            return ScriptRuntime.wrapNumber(jsStaticFunction_UTC(args));
-
-          case Id_constructor:
-            {
-                // if called as a function, just return a string
-                // representing the current time.
-                if (thisObj != null)
-                    return date_format(now(), Id_toString);
-                return jsConstructor(args);
-            }
-
-          case Id_toJSON:
-            {
-                final String toISOString = "toISOString";
-
-                Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
-                Object tv = ScriptRuntime.toPrimitive(o, ScriptRuntime.NumberClass);
-                if (tv instanceof Number) {
-                    double d = ((Number) tv).doubleValue();
-                    if (d != d || Double.isInfinite(d)) {
-                        return null;
-                    }
+            case ConstructorId_parse:
+                {
+                    String dataStr = ScriptRuntime.toString(args, 0);
+                    return ScriptRuntime.wrapNumber(date_parseString(dataStr));
                 }
-                Object toISO = ScriptableObject.getProperty(o, toISOString);
-                if (toISO == NOT_FOUND) {
-                    throw ScriptRuntime.typeError2("msg.function.not.found.in",
-                            toISOString,
-                            ScriptRuntime.toString(o));
-                }
-                if ( !(toISO instanceof Callable) ) {
-                    throw ScriptRuntime.typeError3("msg.isnt.function.in",
-                            toISOString,
-                            ScriptRuntime.toString(o),
-                            ScriptRuntime.toString(toISO));
-                }
-                Object result = ((Callable) toISO).call(cx, scope, o,
-                            ScriptRuntime.emptyArgs);
-                if ( !ScriptRuntime.isPrimitive(result) ) {
-                    throw ScriptRuntime.typeError1("msg.toisostring.must.return.primitive",
-                            ScriptRuntime.toString(result));
+
+            case ConstructorId_UTC:
+                return ScriptRuntime.wrapNumber(jsStaticFunction_UTC(args));
+
+            case Id_constructor:
+                {
+                    // if called as a function, just return a string
+                    // representing the current time.
+                    if (thisObj != null) return date_format(now(), Id_toString);
+                    return jsConstructor(args);
                 }
-                return result;
-            }
 
+            case Id_toJSON:
+                {
+                    final String toISOString = "toISOString";
+
+                    Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);
+                    Object tv = ScriptRuntime.toPrimitive(o, ScriptRuntime.NumberClass);
+                    if (tv instanceof Number) {
+                        double d = ((Number) tv).doubleValue();
+                        if (Double.isNaN(d) || Double.isInfinite(d)) {
+                            return null;
+                        }
+                    }
+                    Object toISO = ScriptableObject.getProperty(o, toISOString);
+                    if (toISO == NOT_FOUND) {
+                        throw ScriptRuntime.typeErrorById(
+                                "msg.function.not.found.in",
+                                toISOString,
+                                ScriptRuntime.toString(o));
+                    }
+                    if (!(toISO instanceof Callable)) {
+                        throw ScriptRuntime.typeErrorById(
+                                "msg.isnt.function.in",
+                                toISOString,
+                                ScriptRuntime.toString(o),
+                                ScriptRuntime.toString(toISO));
+                    }
+                    Object result = ((Callable) toISO).call(cx, scope, o, ScriptRuntime.emptyArgs);
+                    if (!ScriptRuntime.isPrimitive(result)) {
+                        throw ScriptRuntime.typeErrorById(
+                                "msg.toisostring.must.return.primitive",
+                                ScriptRuntime.toString(result));
+                    }
+                    return result;
+                }
         }
 
         // The rest of Date.prototype methods require thisObj to be Date
-
-        if (!(thisObj instanceof NativeDate))
-            throw incompatibleCallError(f);
-        NativeDate realThis = (NativeDate)thisObj;
+        NativeDate realThis = ensureType(thisObj, NativeDate.class, f);
         double t = realThis.date;
 
         switch (id) {
-
-          case Id_toString:
-          case Id_toTimeString:
-          case Id_toDateString:
-            if (t == t) {
-                return date_format(t, id);
-            }
-            return js_NaN_date_str;
-
-          case Id_toLocaleString:
-          case Id_toLocaleTimeString:
-          case Id_toLocaleDateString:
-            if (t == t) {
-                return toLocale_helper(t, id);
-            }
-            return js_NaN_date_str;
-
-          case Id_toUTCString:
-            if (t == t) {
-                return js_toUTCString(t);
-            }
-            return js_NaN_date_str;
-
-          case Id_toSource:
-            return "(new Date("+ScriptRuntime.toString(t)+"))";
-
-          case Id_valueOf:
-          case Id_getTime:
-            return ScriptRuntime.wrapNumber(t);
-
-          case Id_getYear:
-          case Id_getFullYear:
-          case Id_getUTCFullYear:
-            if (t == t) {
-                if (id != Id_getUTCFullYear) t = LocalTime(t);
-                t = YearFromTime(t);
-                if (id == Id_getYear) {
-                    if (cx.hasFeature(Context.FEATURE_NON_ECMA_GET_YEAR)) {
-                        if (1900 <= t && t < 2000) {
+            case Id_toString:
+            case Id_toTimeString:
+            case Id_toDateString:
+                if (!Double.isNaN(t)) {
+                    return date_format(t, id);
+                }
+                return js_NaN_date_str;
+
+            case Id_toLocaleString:
+            case Id_toLocaleTimeString:
+            case Id_toLocaleDateString:
+                if (!Double.isNaN(t)) {
+                    return toLocale_helper(t, id);
+                }
+                return js_NaN_date_str;
+
+            case Id_toUTCString:
+                if (!Double.isNaN(t)) {
+                    return js_toUTCString(t);
+                }
+                return js_NaN_date_str;
+
+            case Id_toSource:
+                return "(new Date(" + ScriptRuntime.toString(t) + "))";
+
+            case Id_valueOf:
+            case Id_getTime:
+                return ScriptRuntime.wrapNumber(t);
+
+            case Id_getYear:
+            case Id_getFullYear:
+            case Id_getUTCFullYear:
+                if (!Double.isNaN(t)) {
+                    if (id != Id_getUTCFullYear) t = LocalTime(t);
+                    t = YearFromTime(t);
+                    if (id == Id_getYear) {
+                        if (cx.hasFeature(Context.FEATURE_NON_ECMA_GET_YEAR)) {
+                            if (1900 <= t && t < 2000) {
+                                t -= 1900;
+                            }
+                        } else {
                             t -= 1900;
                         }
-                    } else {
-                        t -= 1900;
                     }
                 }
-            }
-            return ScriptRuntime.wrapNumber(t);
+                return ScriptRuntime.wrapNumber(t);
 
-          case Id_getMonth:
-          case Id_getUTCMonth:
-            if (t == t) {
-                if (id == Id_getMonth) t = LocalTime(t);
-                t = MonthFromTime(t);
-            }
-            return ScriptRuntime.wrapNumber(t);
-
-          case Id_getDate:
-          case Id_getUTCDate:
-            if (t == t) {
-                if (id == Id_getDate) t = LocalTime(t);
-                t = DateFromTime(t);
-            }
-            return ScriptRuntime.wrapNumber(t);
-
-          case Id_getDay:
-          case Id_getUTCDay:
-            if (t == t) {
-                if (id == Id_getDay) t = LocalTime(t);
-                t = WeekDay(t);
-            }
-            return ScriptRuntime.wrapNumber(t);
-
-          case Id_getHours:
-          case Id_getUTCHours:
-            if (t == t) {
-                if (id == Id_getHours) t = LocalTime(t);
-                t = HourFromTime(t);
-            }
-            return ScriptRuntime.wrapNumber(t);
-
-          case Id_getMinutes:
-          case Id_getUTCMinutes:
-            if (t == t) {
-                if (id == Id_getMinutes) t = LocalTime(t);
-                t = MinFromTime(t);
-            }
-            return ScriptRuntime.wrapNumber(t);
-
-          case Id_getSeconds:
-          case Id_getUTCSeconds:
-            if (t == t) {
-                if (id == Id_getSeconds) t = LocalTime(t);
-                t = SecFromTime(t);
-            }
-            return ScriptRuntime.wrapNumber(t);
-
-          case Id_getMilliseconds:
-          case Id_getUTCMilliseconds:
-            if (t == t) {
-                if (id == Id_getMilliseconds) t = LocalTime(t);
-                t = msFromTime(t);
-            }
-            return ScriptRuntime.wrapNumber(t);
-
-          case Id_getTimezoneOffset:
-            if (t == t) {
-                t = (t - LocalTime(t)) / msPerMinute;
-            }
-            return ScriptRuntime.wrapNumber(t);
-
-          case Id_setTime:
-            t = TimeClip(ScriptRuntime.toNumber(args, 0));
-            realThis.date = t;
-            return ScriptRuntime.wrapNumber(t);
-
-          case Id_setMilliseconds:
-          case Id_setUTCMilliseconds:
-          case Id_setSeconds:
-          case Id_setUTCSeconds:
-          case Id_setMinutes:
-          case Id_setUTCMinutes:
-          case Id_setHours:
-          case Id_setUTCHours:
-            t = makeTime(t, args, id);
-            realThis.date = t;
-            return ScriptRuntime.wrapNumber(t);
-
-          case Id_setDate:
-          case Id_setUTCDate:
-          case Id_setMonth:
-          case Id_setUTCMonth:
-          case Id_setFullYear:
-          case Id_setUTCFullYear:
-            t = makeDate(t, args, id);
-            realThis.date = t;
-            return ScriptRuntime.wrapNumber(t);
-
-          case Id_setYear:
-            {
-                double year = ScriptRuntime.toNumber(args, 0);
+            case Id_getMonth:
+            case Id_getUTCMonth:
+                if (!Double.isNaN(t)) {
+                    if (id == Id_getMonth) t = LocalTime(t);
+                    t = MonthFromTime(t);
+                }
+                return ScriptRuntime.wrapNumber(t);
+
+            case Id_getDate:
+            case Id_getUTCDate:
+                if (!Double.isNaN(t)) {
+                    if (id == Id_getDate) t = LocalTime(t);
+                    t = DateFromTime(t);
+                }
+                return ScriptRuntime.wrapNumber(t);
+
+            case Id_getDay:
+            case Id_getUTCDay:
+                if (!Double.isNaN(t)) {
+                    if (id == Id_getDay) t = LocalTime(t);
+                    t = WeekDay(t);
+                }
+                return ScriptRuntime.wrapNumber(t);
+
+            case Id_getHours:
+            case Id_getUTCHours:
+                if (!Double.isNaN(t)) {
+                    if (id == Id_getHours) t = LocalTime(t);
+                    t = HourFromTime(t);
+                }
+                return ScriptRuntime.wrapNumber(t);
+
+            case Id_getMinutes:
+            case Id_getUTCMinutes:
+                if (!Double.isNaN(t)) {
+                    if (id == Id_getMinutes) t = LocalTime(t);
+                    t = MinFromTime(t);
+                }
+                return ScriptRuntime.wrapNumber(t);
+
+            case Id_getSeconds:
+            case Id_getUTCSeconds:
+                if (!Double.isNaN(t)) {
+                    if (id == Id_getSeconds) t = LocalTime(t);
+                    t = SecFromTime(t);
+                }
+                return ScriptRuntime.wrapNumber(t);
+
+            case Id_getMilliseconds:
+            case Id_getUTCMilliseconds:
+                if (!Double.isNaN(t)) {
+                    if (id == Id_getMilliseconds) t = LocalTime(t);
+                    t = msFromTime(t);
+                }
+                return ScriptRuntime.wrapNumber(t);
+
+            case Id_getTimezoneOffset:
+                if (!Double.isNaN(t)) {
+                    t = (t - LocalTime(t)) / msPerMinute;
+                }
+                return ScriptRuntime.wrapNumber(t);
+
+            case Id_setTime:
+                t = TimeClip(ScriptRuntime.toNumber(args, 0));
+                realThis.date = t;
+                return ScriptRuntime.wrapNumber(t);
+
+            case Id_setMilliseconds:
+            case Id_setUTCMilliseconds:
+            case Id_setSeconds:
+            case Id_setUTCSeconds:
+            case Id_setMinutes:
+            case Id_setUTCMinutes:
+            case Id_setHours:
+            case Id_setUTCHours:
+                t = makeTime(t, args, id);
+                realThis.date = t;
+                return ScriptRuntime.wrapNumber(t);
+
+            case Id_setDate:
+            case Id_setUTCDate:
+            case Id_setMonth:
+            case Id_setUTCMonth:
+            case Id_setFullYear:
+            case Id_setUTCFullYear:
+                t = makeDate(t, args, id);
+                realThis.date = t;
+                return ScriptRuntime.wrapNumber(t);
 
-                if (year != year || Double.isInfinite(year)) {
-                    t = ScriptRuntime.NaN;
-                } else {
-                    if (t != t) {
-                        t = 0;
+            case Id_setYear:
+                {
+                    double year = ScriptRuntime.toNumber(args, 0);
+
+                    if (Double.isNaN(year) || Double.isInfinite(year)) {
+                        t = ScriptRuntime.NaN;
                     } else {
-                        t = LocalTime(t);
-                    }
+                        if (Double.isNaN(t)) {
+                            t = 0;
+                        } else {
+                            t = LocalTime(t);
+                        }
 
-                    if (year >= 0 && year <= 99)
-                        year += 1900;
+                        if (year >= 0 && year <= 99) year += 1900;
 
-                    double day = MakeDay(year, MonthFromTime(t),
-                                         DateFromTime(t));
-                    t = MakeDate(day, TimeWithinDay(t));
-                    t = internalUTC(t);
-                    t = TimeClip(t);
+                        double day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
+                        t = MakeDate(day, TimeWithinDay(t));
+                        t = internalUTC(t);
+                        t = TimeClip(t);
+                    }
                 }
-            }
-            realThis.date = t;
-            return ScriptRuntime.wrapNumber(t);
+                realThis.date = t;
+                return ScriptRuntime.wrapNumber(t);
 
-          case Id_toISOString:
-            if (t == t) {
-                return js_toISOString(t);
-            }
-            String msg = ScriptRuntime.getMessage0("msg.invalid.date");
-            throw ScriptRuntime.constructError("RangeError", msg);
+            case Id_toISOString:
+                if (!Double.isNaN(t)) {
+                    return js_toISOString(t);
+                }
+                String msg = ScriptRuntime.getMessageById("msg.invalid.date");
+                throw ScriptRuntime.rangeError(msg);
 
-          default: throw new IllegalArgumentException(String.valueOf(id));
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
         }
-
     }
 
     /* ECMA helper functions */
 
     private static final double HalfTimeDomain = 8.64e15;
-    private static final double HoursPerDay    = 24.0;
+    private static final double HoursPerDay = 24.0;
     private static final double MinutesPerHour = 60.0;
     private static final double SecondsPerMinute = 60.0;
-    private static final double msPerSecond    = 1000.0;
-    private static final double MinutesPerDay  = (HoursPerDay * MinutesPerHour);
-    private static final double SecondsPerDay  = (MinutesPerDay * SecondsPerMinute);
+    private static final double msPerSecond = 1000.0;
+    private static final double MinutesPerDay = (HoursPerDay * MinutesPerHour);
+    private static final double SecondsPerDay = (MinutesPerDay * SecondsPerMinute);
     private static final double SecondsPerHour = (MinutesPerHour * SecondsPerMinute);
-    private static final double msPerDay       = (SecondsPerDay * msPerSecond);
-    private static final double msPerHour      = (SecondsPerHour * msPerSecond);
-    private static final double msPerMinute    = (SecondsPerMinute * msPerSecond);
+    private static final double msPerDay = (SecondsPerDay * msPerSecond);
+    private static final double msPerHour = (SecondsPerHour * msPerSecond);
+    private static final double msPerMinute = (SecondsPerMinute * msPerSecond);
 
-    private static double Day(double t)
-    {
+    private static double Day(double t) {
         return Math.floor(t / msPerDay);
     }
 
-    private static double TimeWithinDay(double t)
-    {
+    private static double TimeWithinDay(double t) {
         double result;
         result = t % msPerDay;
-        if (result < 0)
-            result += msPerDay;
+        if (result < 0) result += msPerDay;
         return result;
     }
 
-    private static boolean IsLeapYear(int year)
-    {
+    private static boolean IsLeapYear(int year) {
         return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
     }
 
     /* math here has to be f.p, because we need
      *  floor((1968 - 1969) / 4) == -1
      */
-    private static double DayFromYear(double y)
-    {
-        return ((365 * ((y)-1970) + Math.floor(((y)-1969)/4.0)
-                 - Math.floor(((y)-1901)/100.0) + Math.floor(((y)-1601)/400.0)));
+    private static double DayFromYear(double y) {
+        return ((365 * ((y) - 1970)
+                + Math.floor(((y) - 1969) / 4.0)
+                - Math.floor(((y) - 1901) / 100.0)
+                + Math.floor(((y) - 1601) / 400.0)));
     }
 
-    private static double TimeFromYear(double y)
-    {
+    private static double TimeFromYear(double y) {
         return DayFromYear(y) * msPerDay;
     }
 
-    private static int YearFromTime(double t)
-    {
+    private static int YearFromTime(double t) {
         if (Double.isInfinite(t) || Double.isNaN(t)) {
             return 0;
         }
@@ -447,47 +556,45 @@
         if (t2 > t) {
             y--;
         } else {
-            if (t2 + msPerDay * DaysInYear(y) <= t)
-                y++;
+            if (t2 + msPerDay * DaysInYear(y) <= t) y++;
         }
-        return (int)y;
+        return (int) y;
     }
 
-    private static double DayFromMonth(int m, int year)
-    {
+    private static double DayFromMonth(int m, int year) {
         int day = m * 30;
 
-        if (m >= 7) { day += m / 2 - 1; }
-        else if (m >= 2) { day += (m - 1) / 2 - 1; }
-        else { day += m; }
+        if (m >= 7) {
+            day += m / 2 - 1;
+        } else if (m >= 2) {
+            day += (m - 1) / 2 - 1;
+        } else {
+            day += m;
+        }
 
-        if (m >= 2 && IsLeapYear(year)) { ++day; }
+        if (m >= 2 && IsLeapYear(year)) {
+            ++day;
+        }
 
         return day;
     }
 
-    private static double DaysInYear(double year)
-    {
+    private static double DaysInYear(double year) {
         if (Double.isInfinite(year) || Double.isNaN(year)) {
             return ScriptRuntime.NaN;
         }
-        return IsLeapYear((int)year) ? 366.0 : 365.0;
+        return IsLeapYear((int) year) ? 366.0 : 365.0;
     }
 
-    private static int DaysInMonth(int year, int month)
-    {
+    private static int DaysInMonth(int year, int month) {
         // month is 1-based for DaysInMonth!
-        if (month == 2)
-            return IsLeapYear(year) ? 29 : 28;
-        return month >= 8
-               ? 31 - (month & 1)
-               : 30 + (month & 1);
+        if (month == 2) return IsLeapYear(year) ? 29 : 28;
+        return month >= 8 ? 31 - (month & 1) : 30 + (month & 1);
     }
 
-    private static int MonthFromTime(double t)
-    {
+    private static int MonthFromTime(double t) {
         int year = YearFromTime(t);
-        int d = (int)(Day(t) - DayFromYear(year));
+        int d = (int) (Day(t) - DayFromYear(year));
 
         d -= 31 + 28;
         if (d < 0) {
@@ -495,8 +602,7 @@
         }
 
         if (IsLeapYear(year)) {
-            if (d == 0)
-                return 1; // 29 February
+            if (d == 0) return 1; // 29 February
             --d;
         }
 
@@ -504,27 +610,47 @@
         int estimate = d / 30; // approx number of month since March
         int mstart;
         switch (estimate) {
-            case 0: return 2;
-            case 1: mstart = 31; break;
-            case 2: mstart = 31+30; break;
-            case 3: mstart = 31+30+31; break;
-            case 4: mstart = 31+30+31+30; break;
-            case 5: mstart = 31+30+31+30+31; break;
-            case 6: mstart = 31+30+31+30+31+31; break;
-            case 7: mstart = 31+30+31+30+31+31+30; break;
-            case 8: mstart = 31+30+31+30+31+31+30+31; break;
-            case 9: mstart = 31+30+31+30+31+31+30+31+30; break;
-            case 10: return 11; //Late december
-            default: throw Kit.codeBug();
+            case 0:
+                return 2;
+            case 1:
+                mstart = 31;
+                break;
+            case 2:
+                mstart = 31 + 30;
+                break;
+            case 3:
+                mstart = 31 + 30 + 31;
+                break;
+            case 4:
+                mstart = 31 + 30 + 31 + 30;
+                break;
+            case 5:
+                mstart = 31 + 30 + 31 + 30 + 31;
+                break;
+            case 6:
+                mstart = 31 + 30 + 31 + 30 + 31 + 31;
+                break;
+            case 7:
+                mstart = 31 + 30 + 31 + 30 + 31 + 31 + 30;
+                break;
+            case 8:
+                mstart = 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31;
+                break;
+            case 9:
+                mstart = 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30;
+                break;
+            case 10:
+                return 11; // Late december
+            default:
+                throw Kit.codeBug();
         }
         // if d < mstart then real month since March == estimate - 1
         return (d >= mstart) ? estimate + 2 : estimate + 1;
     }
 
-    private static int DateFromTime(double t)
-    {
+    private static int DateFromTime(double t) {
         int year = YearFromTime(t);
-        int d = (int)(Day(t) - DayFromYear(year));
+        int d = (int) (Day(t) - DayFromYear(year));
 
         d -= 31 + 28;
         if (d < 0) {
@@ -532,27 +658,55 @@
         }
 
         if (IsLeapYear(year)) {
-            if (d == 0)
-                return 29; // 29 February
+            if (d == 0) return 29; // 29 February
             --d;
         }
 
         // d: date count from 1 March
         int mdays, mstart;
-        switch (Math.round(d / 30)) { // approx number of month since March
-            case 0: return d + 1;
-            case 1: mdays = 31; mstart = 31; break;
-            case 2: mdays = 30; mstart = 31+30; break;
-            case 3: mdays = 31; mstart = 31+30+31; break;
-            case 4: mdays = 30; mstart = 31+30+31+30; break;
-            case 5: mdays = 31; mstart = 31+30+31+30+31; break;
-            case 6: mdays = 31; mstart = 31+30+31+30+31+31; break;
-            case 7: mdays = 30; mstart = 31+30+31+30+31+31+30; break;
-            case 8: mdays = 31; mstart = 31+30+31+30+31+31+30+31; break;
-            case 9: mdays = 30; mstart = 31+30+31+30+31+31+30+31+30; break;
+        switch (d / 30) { // approx number of month since March
+            case 0:
+                return d + 1;
+            case 1:
+                mdays = 31;
+                mstart = 31;
+                break;
+            case 2:
+                mdays = 30;
+                mstart = 31 + 30;
+                break;
+            case 3:
+                mdays = 31;
+                mstart = 31 + 30 + 31;
+                break;
+            case 4:
+                mdays = 30;
+                mstart = 31 + 30 + 31 + 30;
+                break;
+            case 5:
+                mdays = 31;
+                mstart = 31 + 30 + 31 + 30 + 31;
+                break;
+            case 6:
+                mdays = 31;
+                mstart = 31 + 30 + 31 + 30 + 31 + 31;
+                break;
+            case 7:
+                mdays = 30;
+                mstart = 31 + 30 + 31 + 30 + 31 + 31 + 30;
+                break;
+            case 8:
+                mdays = 31;
+                mstart = 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31;
+                break;
+            case 9:
+                mdays = 30;
+                mstart = 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30;
+                break;
             case 10:
-                return d - (31+30+31+30+31+31+30+31+30) + 1; //Late december
-            default: throw Kit.codeBug();
+                return d - (31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30) + 1; // Late december
+            default:
+                throw Kit.codeBug();
         }
         d -= mstart;
         if (d < 0) {
@@ -560,25 +714,21 @@
             d += mdays;
         }
         return d + 1;
-     }
+    }
 
-    private static int WeekDay(double t)
-    {
+    private static int WeekDay(double t) {
         double result;
         result = Day(t) + 4;
         result = result % 7;
-        if (result < 0)
-            result += 7;
+        if (result < 0) result += 7;
         return (int) result;
     }
 
-    private static double now()
-    {
+    private static double now() {
         return System.currentTimeMillis();
     }
 
-    private static double DaylightSavingTA(double t)
-    {
+    private static double DaylightSavingTA(double t) {
         // Another workaround!  The JRE doesn't seem to know about DST
         // before year 1 AD, so we map to equivalent dates for the
         // purposes of finding DST. To be safe, we do this for years
@@ -589,10 +739,8 @@
             t = MakeDate(day, TimeWithinDay(t));
         }
         Date date = new Date((long) t);
-        if (thisTimeZone.inDaylightTime(date))
-            return msPerHour;
-        else
-            return 0;
+        if (thisTimeZone.inDaylightTime(date)) return msPerHour;
+        return 0;
     }
 
     /*
@@ -602,133 +750,128 @@
      * for determining DST; it hasn't been proven not to produce an
      * incorrect year for times near year boundaries.
      */
-    private static int EquivalentYear(int year)
-    {
+    private static int EquivalentYear(int year) {
         int day = (int) DayFromYear(year) + 4;
         day = day % 7;
-        if (day < 0)
-            day += 7;
+        if (day < 0) day += 7;
         // Years and leap years on which Jan 1 is a Sunday, Monday, etc.
         if (IsLeapYear(year)) {
             switch (day) {
-                case 0: return 1984;
-                case 1: return 1996;
-                case 2: return 1980;
-                case 3: return 1992;
-                case 4: return 1976;
-                case 5: return 1988;
-                case 6: return 1972;
+                case 0:
+                    return 1984;
+                case 1:
+                    return 1996;
+                case 2:
+                    return 1980;
+                case 3:
+                    return 1992;
+                case 4:
+                    return 1976;
+                case 5:
+                    return 1988;
+                case 6:
+                    return 1972;
             }
         } else {
             switch (day) {
-                case 0: return 1978;
-                case 1: return 1973;
-                case 2: return 1985;
-                case 3: return 1986;
-                case 4: return 1981;
-                case 5: return 1971;
-                case 6: return 1977;
+                case 0:
+                    return 1978;
+                case 1:
+                    return 1973;
+                case 2:
+                    return 1985;
+                case 3:
+                    return 1986;
+                case 4:
+                    return 1981;
+                case 5:
+                    return 1971;
+                case 6:
+                    return 1977;
             }
         }
         // Unreachable
         throw Kit.codeBug();
     }
 
-    private static double LocalTime(double t)
-    {
+    private static double LocalTime(double t) {
         return t + LocalTZA + DaylightSavingTA(t);
     }
 
-    private static double internalUTC(double t)
-    {
+    private static double internalUTC(double t) {
         return t - LocalTZA - DaylightSavingTA(t - LocalTZA);
     }
 
-    private static int HourFromTime(double t)
-    {
+    private static int HourFromTime(double t) {
         double result;
         result = Math.floor(t / msPerHour) % HoursPerDay;
-        if (result < 0)
-            result += HoursPerDay;
+        if (result < 0) result += HoursPerDay;
         return (int) result;
     }
 
-    private static int MinFromTime(double t)
-    {
+    private static int MinFromTime(double t) {
         double result;
         result = Math.floor(t / msPerMinute) % MinutesPerHour;
-        if (result < 0)
-            result += MinutesPerHour;
+        if (result < 0) result += MinutesPerHour;
         return (int) result;
     }
 
-    private static int SecFromTime(double t)
-    {
+    private static int SecFromTime(double t) {
         double result;
         result = Math.floor(t / msPerSecond) % SecondsPerMinute;
-        if (result < 0)
-            result += SecondsPerMinute;
+        if (result < 0) result += SecondsPerMinute;
         return (int) result;
     }
 
-    private static int msFromTime(double t)
-    {
+    private static int msFromTime(double t) {
         double result;
-        result =  t % msPerSecond;
-        if (result < 0)
-            result += msPerSecond;
+        result = t % msPerSecond;
+        if (result < 0) result += msPerSecond;
         return (int) result;
     }
 
-    private static double MakeTime(double hour, double min,
-                                   double sec, double ms)
-    {
-        return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec)
-            * msPerSecond + ms;
+    private static double MakeTime(double hour, double min, double sec, double ms) {
+        return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms;
     }
 
-    private static double MakeDay(double year, double month, double date)
-    {
+    private static double MakeDay(double year, double month, double date) {
         year += Math.floor(month / 12);
 
         month = month % 12;
-        if (month < 0)
-            month += 12;
+        if (month < 0) month += 12;
 
         double yearday = Math.floor(TimeFromYear(year) / msPerDay);
-        double monthday = DayFromMonth((int)month, (int)year);
+        double monthday = DayFromMonth((int) month, (int) year);
 
         return yearday + monthday + date - 1;
     }
 
-    private static double MakeDate(double day, double time)
-    {
+    private static double MakeDate(double day, double time) {
         return day * msPerDay + time;
     }
 
-    private static double TimeClip(double d)
-    {
-        if (d != d ||
-            d == Double.POSITIVE_INFINITY ||
-            d == Double.NEGATIVE_INFINITY ||
-            Math.abs(d) > HalfTimeDomain)
-        {
+    private static double TimeClip(double d) {
+        if (Double.isNaN(d)
+                || d == Double.POSITIVE_INFINITY
+                || d == Double.NEGATIVE_INFINITY
+                || Math.abs(d) > HalfTimeDomain) {
             return ScriptRuntime.NaN;
         }
-        if (d > 0.0)
-            return Math.floor(d + 0.);
-        else
-            return Math.ceil(d + 0.);
+        if (d > 0.0) return Math.floor(d + 0.);
+        return Math.ceil(d + 0.);
     }
 
     /* end of ECMA helper functions */
 
     /* find UTC time from given date... no 1900 correction! */
-    private static double date_msecFromDate(double year, double mon,
-                                            double mday, double hour,
-                                            double min, double sec,
-                                            double msec)
-    {
+    private static double date_msecFromDate(
+            double year,
+            double mon,
+            double mday,
+            double hour,
+            double min,
+            double sec,
+            double msec) {
         double day;
         double time;
         double result;
@@ -741,8 +884,8 @@
 
     /* compute the time in msec (unclipped) from the given args */
     private static final int MAXARGS = 7;
-    private static double date_msecFromArgs(Object[] args)
-    {
+
+    private static double date_msecFromArgs(Object[] args) {
         double array[] = new double[MAXARGS];
         int loop;
         double d;
@@ -750,7 +893,7 @@
         for (loop = 0; loop < MAXARGS; loop++) {
             if (loop < args.length) {
                 d = ScriptRuntime.toNumber(args[loop]);
-                if (d != d || Double.isInfinite(d)) {
+                if (Double.isNaN(d) || Double.isInfinite(d)) {
                     return ScriptRuntime.NaN;
                 }
                 array[loop] = ScriptRuntime.toInteger(args[loop]);
@@ -764,24 +907,26 @@
         }
 
         /* adjust 2-digit years into the 20th century */
-        if (array[0] >= 0 && array[0] <= 99)
-            array[0] += 1900;
+        if (array[0] >= 0 && array[0] <= 99) array[0] += 1900;
 
-        return date_msecFromDate(array[0], array[1], array[2],
-                                 array[3], array[4], array[5], array[6]);
+        return date_msecFromDate(
+                array[0], array[1], array[2], array[3], array[4], array[5], array[6]);
     }
 
-    private static double jsStaticFunction_UTC(Object[] args)
-    {
+    private static double jsStaticFunction_UTC(Object[] args) {
+        if (args.length == 0) {
+            return ScriptRuntime.NaN;
+        }
         return TimeClip(date_msecFromArgs(args));
     }
 
     /**
      * 15.9.1.15 Date Time String Format<br>
      * Parse input string according to simplified ISO-8601 Extended Format:
+     *
      * <ul>
-     * <li><code>YYYY-MM-DD'T'HH:mm:ss.sss'Z'</code></li>
-     * <li>or <code>YYYY-MM-DD'T'HH:mm:ss.sss[+-]hh:mm</code></li>
+     *   <li><code>YYYY-MM-DD'T'HH:mm:ss.sss'Z'</code>
+     *   <li>or <code>YYYY-MM-DD'T'HH:mm:ss.sss[+-]hh:mm</code>
      * </ul>
      */
     private static double parseISOString(String s) {
@@ -792,7 +937,7 @@
         final int TZHOUR = 7, TZMIN = 8;
         int state = YEAR;
         // default values per [15.9.1.15 Date Time String Format]
-        int[] values = { 1970, 1, 1, 0, 0, 0, 0, -1, -1 };
+        int[] values = {1970, 1, 1, 0, 0, 0, 0, -1, -1};
         int yearlen = 4, yearmod = 1, tzmod = 1;
         int i = 0, len = s.length();
         if (len != 0) {
@@ -808,7 +953,8 @@
                 state = HOUR;
             }
         }
-        loop: while (state != ERROR) {
+        loop:
+        while (state != ERROR) {
             int m = i + (state == YEAR ? yearlen : state == MSEC ? 3 : 2);
             if (m > len) {
                 state = ERROR;
@@ -818,7 +964,10 @@
             int value = 0;
             for (; i < m; ++i) {
                 char c = s.charAt(i);
-                if (c < '0' || c > '9') { state = ERROR; break loop; }
+                if (c < '0' || c > '9') {
+                    state = ERROR;
+                    break loop;
+                }
                 value = 10 * value + (c - '0');
             }
             values[state] = value;
@@ -826,9 +975,9 @@
             if (i == len) {
                 // reached EOF, check for end state
                 switch (state) {
-                case HOUR:
-                case TZHOUR:
-                    state = ERROR;
+                    case HOUR:
+                    case TZHOUR:
+                        state = ERROR;
                 }
                 break;
             }
@@ -839,49 +988,49 @@
                 values[TZHOUR] = 0;
                 values[TZMIN] = 0;
                 switch (state) {
-                case MIN:
-                case SEC:
-                case MSEC:
-                    break;
-                default:
-                    state = ERROR;
+                    case MIN:
+                    case SEC:
+                    case MSEC:
+                        break;
+                    default:
+                        state = ERROR;
                 }
                 break;
             }
 
             // state transition
             switch (state) {
-            case YEAR:
-            case MONTH:
-                state = (c == '-' ? state + 1 : c == 'T' ? HOUR : ERROR);
-                break;
-            case DAY:
-                state = (c == 'T' ? HOUR : ERROR);
-                break;
-            case HOUR:
-                state = (c == ':' ? MIN : ERROR);
-                break;
-            case TZHOUR:
-                // state = (c == ':' ? state + 1 : ERROR);
-                // Non-standard extension, https://bugzilla.mozilla.org/show_bug.cgi?id=682754
-                if (c != ':') {
-                    // back off by one and try to read without ':' separator
-                    i -= 1;
-                }
-                state = TZMIN;
-                break;
-            case MIN:
-                state = (c == ':' ? SEC : c == '+' || c == '-' ? TZHOUR : ERROR);
-                break;
-            case SEC:
-                state = (c == '.' ? MSEC : c == '+' || c == '-' ? TZHOUR : ERROR);
-                break;
-            case MSEC:
-                state = (c == '+' || c == '-' ? TZHOUR : ERROR);
-                break;
-            case TZMIN:
-                state = ERROR;
-                break;
+                case YEAR:
+                case MONTH:
+                    state = (c == '-' ? state + 1 : c == 'T' ? HOUR : ERROR);
+                    break;
+                case DAY:
+                    state = (c == 'T' ? HOUR : ERROR);
+                    break;
+                case HOUR:
+                    state = (c == ':' ? MIN : ERROR);
+                    break;
+                case TZHOUR:
+                    // state = (c == ':' ? state + 1 : ERROR);
+                    // Non-standard extension, https://bugzilla.mozilla.org/show_bug.cgi?id=682754
+                    if (c != ':') {
+                        // back off by one and try to read without ':' separator
+                        i -= 1;
+                    }
+                    state = TZMIN;
+                    break;
+                case MIN:
+                    state = (c == ':' ? SEC : c == '+' || c == '-' ? TZHOUR : ERROR);
+                    break;
+                case SEC:
+                    state = (c == '.' ? MSEC : c == '+' || c == '-' ? TZHOUR : ERROR);
+                    break;
+                case MSEC:
+                    state = (c == '+' || c == '-' ? TZHOUR : ERROR);
+                    break;
+                case TZMIN:
+                    state = ERROR;
+                    break;
             }
             if (state == TZHOUR) {
                 // save timezone modificator
@@ -889,7 +1038,8 @@
             }
         }
 
-        syntax: {
+        syntax:
+        {
             // error or unparsed characters
             if (state == ERROR || i != len) break syntax;
 
@@ -898,20 +1048,18 @@
             int hour = values[HOUR], min = values[MIN], sec = values[SEC], msec = values[MSEC];
             int tzhour = values[TZHOUR], tzmin = values[TZMIN];
             if (year > 275943 // ceil(1e8/365) + 1970 = 275943
-                || (month < 1 || month > 12)
-                || (day < 1 || day > DaysInMonth(year, month))
-                || hour > 24
-                || (hour == 24 && (min > 0 || sec > 0 || msec > 0))
-                || min > 59
-                || sec > 59
-                || tzhour > 23
-                || tzmin > 59
-               ) {
+                    || (month < 1 || month > 12)
+                    || (day < 1 || day > DaysInMonth(year, month))
+                    || hour > 24
+                    || (hour == 24 && (min > 0 || sec > 0 || msec > 0))
+                    || min > 59
+                    || sec > 59
+                    || tzhour > 23
+                    || tzmin > 59) {
                 break syntax;
             }
             // valid ISO-8601 format, compute date in milliseconds
-            double date = date_msecFromDate(year * yearmod, month - 1, day,
-                                            hour, min, sec, msec);
+            double date = date_msecFromDate(year * yearmod, month - 1, day, hour, min, sec, msec);
             if (tzhour == -1) {
                 // Spec says to use UTC timezone, the following bug report says
                 // that local timezone was meant to be used. Stick with spec for now.
@@ -929,10 +1077,9 @@
         return ScriptRuntime.NaN;
     }
 
-    private static double date_parseString(String s)
-    {
+    private static double date_parseString(String s) {
         double d = parseISOString(s);
-        if (d == d) {
+        if (!Double.isNaN(d)) {
             return d;
         }
 
@@ -964,16 +1111,14 @@
                 }
                 continue;
             }
-            if (c == '(') { /* comments) */
+            if (c == '(') {
+                /* comments) */
                 int depth = 1;
                 while (i < limit) {
                     c = s.charAt(i);
                     i++;
-                    if (c == '(')
-                        depth++;
-                    else if (c == ')')
-                        if (--depth <= 0)
-                            break;
+                    if (c == '(') depth++;
+                    else if (c == ')') if (--depth <= 0) break;
                 }
                 continue;
             }
@@ -991,51 +1136,35 @@
                 /* uses of seenplusminus allow : in TZA, so Java
                  * no-timezone style of GMT+4:30 works
                  */
-                if ((prevc == '+' || prevc == '-')/*  && year>=0 */) {
+                if ((prevc == '+' || prevc == '-') /*  && year>=0 */) {
                     /* make ':' case below change tzoffset */
                     seenplusminus = true;
 
                     /* offset */
-                    if (n < 24)
-                        n = n * 60; /* EG. "GMT-3" */
-                    else
-                        n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
-                    if (prevc == '+')       /* plus means east of GMT */
-                        n = -n;
-                    if (tzoffset != 0 && tzoffset != -1)
-                        return ScriptRuntime.NaN;
+                    if (n < 24) n = n * 60; /* EG. "GMT-3" */
+                    else n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
+                    if (prevc == '+') /* plus means east of GMT */ n = -n;
+                    if (tzoffset != 0 && tzoffset != -1) return ScriptRuntime.NaN;
                     tzoffset = n;
-                } else if (n >= 70  ||
-                           (prevc == '/' && mon >= 0 && mday >= 0
-                            && year < 0))
-                {
-                    if (year >= 0)
-                        return ScriptRuntime.NaN;
+                } else if (n >= 70 || (prevc == '/' && mon >= 0 && mday >= 0 && year < 0)) {
+                    if (year >= 0) return ScriptRuntime.NaN;
                     else if (c <= ' ' || c == ',' || c == '/' || i >= limit)
                         year = n < 100 ? n + 1900 : n;
-                    else
-                        return ScriptRuntime.NaN;
+                    else return ScriptRuntime.NaN;
                 } else if (c == ':') {
-                    if (hour < 0)
-                        hour = /*byte*/ n;
-                    else if (min < 0)
-                        min = /*byte*/ n;
-                    else
-                        return ScriptRuntime.NaN;
+                    if (hour < 0) hour = /*byte*/ n;
+                    else if (min < 0) min = /*byte*/ n;
+                    else return ScriptRuntime.NaN;
                 } else if (c == '/') {
-                    if (mon < 0)
-                        mon = /*byte*/ n-1;
-                    else if (mday < 0)
-                        mday = /*byte*/ n;
-                    else
-                        return ScriptRuntime.NaN;
+                    if (mon < 0) mon = /*byte*/ n - 1;
+                    else if (mday < 0) mday = /*byte*/ n;
+                    else return ScriptRuntime.NaN;
                 } else if (i < limit && c != ',' && c > ' ' && c != '-') {
                     return ScriptRuntime.NaN;
-                } else if (seenplusminus && n < 60) {  /* handle GMT-3:30 */
-                    if (tzoffset < 0)
-                        tzoffset -= n;
-                    else
-                        tzoffset += n;
+                } else if (seenplusminus && n < 60) {
+                    /* handle GMT-3:30 */
+                    if (tzoffset < 0) tzoffset -= n;
+                    else tzoffset += n;
                 } else if (hour >= 0 && min < 0) {
                     min = /*byte*/ n;
                 } else if (min >= 0 && sec < 0) {
@@ -1052,31 +1181,28 @@
                 int st = i - 1;
                 while (i < limit) {
                     c = s.charAt(i);
-                    if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
-                        break;
+                    if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'))) break;
                     i++;
                 }
                 int letterCount = i - st;
-                if (letterCount < 2)
-                    return ScriptRuntime.NaN;
-               /*
-                * Use ported code from jsdate.c rather than the locale-specific
-                * date-parsing code from Java, to keep js and rhino consistent.
-                * Is this the right strategy?
-                */
-                String wtb = "am;pm;"
-                            +"monday;tuesday;wednesday;thursday;friday;"
-                            +"saturday;sunday;"
-                            +"january;february;march;april;may;june;"
-                            +"july;august;september;october;november;december;"
-                            +"gmt;ut;utc;est;edt;cst;cdt;mst;mdt;pst;pdt;";
+                if (letterCount < 2) return ScriptRuntime.NaN;
+                /*
+                 * Use ported code from jsdate.c rather than the locale-specific
+                 * date-parsing code from Java, to keep js and rhino consistent.
+                 * Is this the right strategy?
+                 */
+                String wtb =
+                        "am;pm;"
+                                + "monday;tuesday;wednesday;thursday;friday;"
+                                + "saturday;sunday;"
+                                + "january;february;march;april;may;june;"
+                                + "july;august;september;october;november;december;"
+                                + "gmt;ut;utc;est;edt;cst;cdt;mst;mdt;pst;pdt;";
                 int index = 0;
-                for (int wtbOffset = 0; ;) {
+                for (int wtbOffset = 0; ; ) {
                     int wtbNext = wtb.indexOf(';', wtbOffset);
-                    if (wtbNext < 0)
-                        return ScriptRuntime.NaN;
-                    if (wtb.regionMatches(true, wtbOffset, s, st, letterCount))
-                        break;
+                    if (wtbNext < 0) return ScriptRuntime.NaN;
+                    if (wtb.regionMatches(true, wtbOffset, s, st, letterCount)) break;
                     wtbOffset = wtbNext + 1;
                     ++index;
                 }
@@ -1089,12 +1215,10 @@
                         return ScriptRuntime.NaN;
                     } else if (index == 0) {
                         // AM
-                        if (hour == 12)
-                            hour = 0;
+                        if (hour == 12) hour = 0;
                     } else {
                         // PM
-                        if (hour != 12)
-                            hour += 12;
+                        if (hour != 12) hour += 12;
                     }
                 } else if ((index -= 2) < 7) {
                     // ignore week days
@@ -1109,41 +1233,59 @@
                     index -= 12;
                     // timezones
                     switch (index) {
-                      case 0 /* gmt */: tzoffset = 0; break;
-                      case 1 /* ut */:  tzoffset = 0; break;
-                      case 2 /* utc */: tzoffset = 0; break;
-                      case 3 /* est */: tzoffset = 5 * 60; break;
-                      case 4 /* edt */: tzoffset = 4 * 60; break;
-                      case 5 /* cst */: tzoffset = 6 * 60; break;
-                      case 6 /* cdt */: tzoffset = 5 * 60; break;
-                      case 7 /* mst */: tzoffset = 7 * 60; break;
-                      case 8 /* mdt */: tzoffset = 6 * 60; break;
-                      case 9 /* pst */: tzoffset = 8 * 60; break;
-                      case 10 /* pdt */:tzoffset = 7 * 60; break;
-                      default: Kit.codeBug();
+                        case 0 /* gmt */:
+                            tzoffset = 0;
+                            break;
+                        case 1 /* ut */:
+                            tzoffset = 0;
+                            break;
+                        case 2 /* utc */:
+                            tzoffset = 0;
+                            break;
+                        case 3 /* est */:
+                            tzoffset = 5 * 60;
+                            break;
+                        case 4 /* edt */:
+                            tzoffset = 4 * 60;
+                            break;
+                        case 5 /* cst */:
+                            tzoffset = 6 * 60;
+                            break;
+                        case 6 /* cdt */:
+                            tzoffset = 5 * 60;
+                            break;
+                        case 7 /* mst */:
+                            tzoffset = 7 * 60;
+                            break;
+                        case 8 /* mdt */:
+                            tzoffset = 6 * 60;
+                            break;
+                        case 9 /* pst */:
+                            tzoffset = 8 * 60;
+                            break;
+                        case 10 /* pdt */:
+                            tzoffset = 7 * 60;
+                            break;
+                        default:
+                            Kit.codeBug();
                     }
                 }
             }
         }
-        if (year < 0 || mon < 0 || mday < 0)
-            return ScriptRuntime.NaN;
-        if (sec < 0)
-            sec = 0;
-        if (min < 0)
-            min = 0;
-        if (hour < 0)
-            hour = 0;
+        if (year < 0 || mon < 0 || mday < 0) return ScriptRuntime.NaN;
+        if (sec < 0) sec = 0;
+        if (min < 0) min = 0;
+        if (hour < 0) hour = 0;
 
         double msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
-        if (tzoffset == -1) { /* no time zone specified, have to use local */
+        if (tzoffset == -1) {
+            /* no time zone specified, have to use local */
             return internalUTC(msec);
-        } else {
-            return msec + tzoffset * msPerMinute;
         }
+        return msec + tzoffset * msPerMinute;
     }
 
-    private static String date_format(double t, int methodId)
-    {
+    private static String date_format(double t, int methodId) {
         StringBuilder result = new StringBuilder(60);
         double local = LocalTime(t);
 
@@ -1164,8 +1306,7 @@
                 year = -year;
             }
             append0PaddedUint(result, year, 4);
-            if (methodId != Id_toDateString)
-                result.append(' ');
+            if (methodId != Id_toDateString) result.append(' ');
         }
 
         if (methodId != Id_toDateString) {
@@ -1177,8 +1318,7 @@
 
             // offset from GMT in minutes.  The offset includes daylight
             // savings, if it applies.
-            int minutes = (int) Math.floor((LocalTZA + DaylightSavingTA(t))
-                                           / msPerMinute);
+            int minutes = (int) Math.floor((LocalTZA + DaylightSavingTA(t)) / msPerMinute);
             // map 510 minutes to 0830 hours
             int offset = (minutes / 60) * 100 + minutes % 60;
             if (offset > 0) {
@@ -1189,9 +1329,6 @@
             }
             append0PaddedUint(result, offset, 4);
 
-            if (timeZoneFormatter == null)
-                timeZoneFormatter = new SimpleDateFormat("zzz");
-
             // Find an equivalent year before getting the timezone
             // comment.  See DaylightSavingTA.
             if (t < 0.0) {
@@ -1210,8 +1347,7 @@
     }
 
     /* the javascript constructor */
-    private static Object jsConstructor(Object[] args)
-    {
+    private static Object jsConstructor(Object[] args) {
         NativeDate obj = new NativeDate();
 
         // if called as a constructor with no args,
@@ -1224,8 +1360,13 @@
         // if called with just one arg -
         if (args.length == 1) {
             Object arg0 = args[0];
-            if (arg0 instanceof Scriptable)
+            if (arg0 instanceof NativeDate) {
+                obj.date = ((NativeDate) arg0).date;
+                return obj;
+            }
+            if (arg0 instanceof Scriptable) {
                 arg0 = ((Scriptable) arg0).getDefaultValue(null);
+            }
             double date;
             if (arg0 instanceof CharSequence) {
                 // it's a string; parse it.
@@ -1240,41 +1381,27 @@
 
         double time = date_msecFromArgs(args);
 
-        if (!Double.isNaN(time) && !Double.isInfinite(time))
-            time = TimeClip(internalUTC(time));
+        if (!Double.isNaN(time) && !Double.isInfinite(time)) time = TimeClip(internalUTC(time));
 
         obj.date = time;
 
         return obj;
     }
 
-    private static String toLocale_helper(double t, int methodId)
-    {
+    private static String toLocale_helper(double t, int methodId) {
         DateFormat formatter;
         switch (methodId) {
-          case Id_toLocaleString:
-            if (localeDateTimeFormatter == null) {
-                localeDateTimeFormatter
-                    = DateFormat.getDateTimeInstance(DateFormat.LONG,
-                                                     DateFormat.LONG);
-            }
-            formatter = localeDateTimeFormatter;
-            break;
-          case Id_toLocaleTimeString:
-            if (localeTimeFormatter == null) {
-                localeTimeFormatter
-                    = DateFormat.getTimeInstance(DateFormat.LONG);
-            }
-            formatter = localeTimeFormatter;
-            break;
-          case Id_toLocaleDateString:
-            if (localeDateFormatter == null) {
-                localeDateFormatter
-                    = DateFormat.getDateInstance(DateFormat.LONG);
-            }
-            formatter = localeDateFormatter;
-            break;
-          default: throw new AssertionError(); // unreachable
+            case Id_toLocaleString:
+                formatter = localeDateTimeFormatter;
+                break;
+            case Id_toLocaleTimeString:
+                formatter = localeTimeFormatter;
+                break;
+            case Id_toLocaleDateString:
+                formatter = localeDateFormatter;
+                break;
+            default:
+                throw new AssertionError(); // unreachable
         }
 
         synchronized (formatter) {
@@ -1282,8 +1409,7 @@
         }
     }
 
-    private static String js_toUTCString(double date)
-    {
+    private static String js_toUTCString(double date) {
         StringBuilder result = new StringBuilder(60);
 
         appendWeekDayName(result, WeekDay(date));
@@ -1294,7 +1420,8 @@
         result.append(' ');
         int year = YearFromTime(date);
         if (year < 0) {
-            result.append('-'); year = -year;
+            result.append('-');
+            year = -year;
         }
         append0PaddedUint(result, year, 4);
         result.append(' ');
@@ -1315,6 +1442,7 @@
             result.append('-');
             append0PaddedUint(result, -year, 6);
         } else if (year > 9999) {
+            result.append('+');
             append0PaddedUint(result, year, 6);
         } else {
             append0PaddedUint(result, year, 4);
@@ -1335,16 +1463,17 @@
         return result.toString();
     }
 
-    private static void append0PaddedUint(StringBuilder sb, int i, int minWidth)
-    {
+    private static void append0PaddedUint(StringBuilder sb, int i, int minWidth) {
         if (i < 0) Kit.codeBug();
         int scale = 1;
         --minWidth;
         if (i >= 10) {
             if (i < 1000 * 1000 * 1000) {
-                for (;;) {
+                for (; ; ) {
                     int newScale = scale * 10;
-                    if (i < newScale) { break; }
+                    if (i < newScale) {
+                        break;
+                    }
                     --minWidth;
                     scale = newScale;
                 }
@@ -1359,37 +1488,35 @@
             --minWidth;
         }
         while (scale != 1) {
-            sb.append((char)('0' + (i / scale)));
+            sb.append((char) ('0' + (i / scale)));
             i %= scale;
             scale /= 10;
         }
-        sb.append((char)('0' + i));
+        sb.append((char) ('0' + i));
     }
 
-    private static void appendMonthName(StringBuilder sb, int index)
-    {
+    private static void appendMonthName(StringBuilder sb, int index) {
         // Take advantage of the fact that all month abbreviations
         // have the same length to minimize amount of strings runtime has
         // to keep in memory
-        String months = "Jan"+"Feb"+"Mar"+"Apr"+"May"+"Jun"
-                       +"Jul"+"Aug"+"Sep"+"Oct"+"Nov"+"Dec";
+        String months =
+                "Jan" + "Feb" + "Mar" + "Apr" + "May" + "Jun" + "Jul" + "Aug" + "Sep" + "Oct"
+                        + "Nov" + "Dec";
         index *= 3;
         for (int i = 0; i != 3; ++i) {
             sb.append(months.charAt(index + i));
         }
     }
 
-    private static void appendWeekDayName(StringBuilder sb, int index)
-    {
-        String days = "Sun"+"Mon"+"Tue"+"Wed"+"Thu"+"Fri"+"Sat";
+    private static void appendWeekDayName(StringBuilder sb, int index) {
+        String days = "Sun" + "Mon" + "Tue" + "Wed" + "Thu" + "Fri" + "Sat";
         index *= 3;
         for (int i = 0; i != 3; ++i) {
             sb.append(days.charAt(index + i));
         }
     }
 
-    private static double makeTime(double date, Object[] args, int methodId)
-    {
+    private static double makeTime(double date, Object[] args, int methodId) {
         if (args.length == 0) {
             /*
              * Satisfy the ECMA rule that if a function is called with
@@ -1406,36 +1533,36 @@
         int maxargs;
         boolean local = true;
         switch (methodId) {
-          case Id_setUTCMilliseconds:
-              local = false;
-            // fallthrough
-          case Id_setMilliseconds:
-            maxargs = 1;
-            break;
-
-          case Id_setUTCSeconds:
-              local = false;
-            // fallthrough
-          case Id_setSeconds:
-            maxargs = 2;
-            break;
-
-          case Id_setUTCMinutes:
-              local = false;
-            // fallthrough
-          case Id_setMinutes:
-            maxargs = 3;
-            break;
-
-          case Id_setUTCHours:
-              local = false;
-            // fallthrough
-          case Id_setHours:
-            maxargs = 4;
-            break;
+            case Id_setUTCMilliseconds:
+                local = false;
+                // fallthrough
+            case Id_setMilliseconds:
+                maxargs = 1;
+                break;
+
+            case Id_setUTCSeconds:
+                local = false;
+                // fallthrough
+            case Id_setSeconds:
+                maxargs = 2;
+                break;
+
+            case Id_setUTCMinutes:
+                local = false;
+                // fallthrough
+            case Id_setMinutes:
+                maxargs = 3;
+                break;
 
-          default:
-              throw Kit.codeBug();
+            case Id_setUTCHours:
+                local = false;
+                // fallthrough
+            case Id_setHours:
+                maxargs = 4;
+                break;
+
+            default:
+                throw Kit.codeBug();
         }
 
         boolean hasNaN = false;
@@ -1444,7 +1571,7 @@
         double[] nums = new double[4];
         for (int i = 0; i < numNums; i++) {
             double d = ScriptRuntime.toNumber(args[i]);
-            if (d != d || Double.isInfinite(d)) {
+            if (Double.isNaN(d) || Double.isInfinite(d)) {
                 hasNaN = true;
             } else {
                 nums[i] = ScriptRuntime.toInteger(d);
@@ -1453,50 +1580,38 @@
 
         // just return NaN if the date is already NaN,
         // limit checks that happen in MakeTime in ECMA.
-        if (hasNaN || date != date) {
+        if (hasNaN || Double.isNaN(date)) {
             return ScriptRuntime.NaN;
         }
 
         int i = 0, stop = numNums;
         double hour, min, sec, msec;
-        double lorutime;  /* Local or UTC version of date */
+        double lorutime; /* Local or UTC version of date */
+
+        if (local) lorutime = LocalTime(date);
+        else lorutime = date;
+
+        if (maxargs >= 4 && i < stop) hour = nums[i++];
+        else hour = HourFromTime(lorutime);
 
-        if (local)
-            lorutime = LocalTime(date);
-        else
-            lorutime = date;
-
-        if (maxargs >= 4 && i < stop)
-            hour = nums[i++];
-        else
-            hour = HourFromTime(lorutime);
-
-        if (maxargs >= 3 && i < stop)
-            min = nums[i++];
-        else
-            min = MinFromTime(lorutime);
-
-        if (maxargs >= 2 && i < stop)
-            sec = nums[i++];
-        else
-            sec = SecFromTime(lorutime);
-
-        if (maxargs >= 1 && i < stop)
-            msec = nums[i++];
-        else
-            msec = msFromTime(lorutime);
+        if (maxargs >= 3 && i < stop) min = nums[i++];
+        else min = MinFromTime(lorutime);
+
+        if (maxargs >= 2 && i < stop) sec = nums[i++];
+        else sec = SecFromTime(lorutime);
+
+        if (maxargs >= 1 && i < stop) msec = nums[i++];
+        else msec = msFromTime(lorutime);
 
         double time = MakeTime(hour, min, sec, msec);
         double result = MakeDate(Day(lorutime), time);
 
-        if (local)
-            result = internalUTC(result);
+        if (local) result = internalUTC(result);
 
         return TimeClip(result);
     }
 
-    private static double makeDate(double date, Object[] args, int methodId)
-    {
+    private static double makeDate(double date, Object[] args, int methodId) {
         /* see complaint about ECMA in date_MakeTime */
         if (args.length == 0) {
             return ScriptRuntime.NaN;
@@ -1505,29 +1620,29 @@
         int maxargs;
         boolean local = true;
         switch (methodId) {
-          case Id_setUTCDate:
-              local = false;
-            // fallthrough
-          case Id_setDate:
-              maxargs = 1;
-            break;
-
-          case Id_setUTCMonth:
-              local = false;
-            // fallthrough
-          case Id_setMonth:
-              maxargs = 2;
-            break;
-
-          case Id_setUTCFullYear:
-              local = false;
-            // fallthrough
-          case Id_setFullYear:
-              maxargs = 3;
-            break;
+            case Id_setUTCDate:
+                local = false;
+                // fallthrough
+            case Id_setDate:
+                maxargs = 1;
+                break;
+
+            case Id_setUTCMonth:
+                local = false;
+                // fallthrough
+            case Id_setMonth:
+                maxargs = 2;
+                break;
+
+            case Id_setUTCFullYear:
+                local = false;
+                // fallthrough
+            case Id_setFullYear:
+                maxargs = 3;
+                break;
 
-          default:
-              throw Kit.codeBug();
+            default:
+                throw Kit.codeBug();
         }
 
         boolean hasNaN = false;
@@ -1536,7 +1651,7 @@
         double[] nums = new double[3];
         for (int i = 0; i < numNums; i++) {
             double d = ScriptRuntime.toNumber(args[i]);
-            if (d != d || Double.isInfinite(d)) {
+            if (Double.isNaN(d) || Double.isInfinite(d)) {
                 hasNaN = true;
             } else {
                 nums[i] = ScriptRuntime.toInteger(d);
@@ -1550,237 +1665,256 @@
 
         int i = 0, stop = numNums;
         double year, month, day;
-        double lorutime;  /* Local or UTC version of date */
+        double lorutime; /* Local or UTC version of date */
 
         /* return NaN if date is NaN and we're not setting the year,
          * If we are, use 0 as the time. */
-        if (date != date) {
+        if (Double.isNaN(date)) {
             if (maxargs < 3) {
                 return ScriptRuntime.NaN;
-            } else {
-                lorutime = 0;
             }
+            lorutime = 0;
         } else {
-            if (local)
-                lorutime = LocalTime(date);
-            else
-                lorutime = date;
-        }
-
-        if (maxargs >= 3 && i < stop)
-            year = nums[i++];
-        else
-            year = YearFromTime(lorutime);
-
-        if (maxargs >= 2 && i < stop)
-            month = nums[i++];
-        else
-            month = MonthFromTime(lorutime);
-
-        if (maxargs >= 1 && i < stop)
-            day = nums[i++];
-        else
-            day = DateFromTime(lorutime);
+            if (local) lorutime = LocalTime(date);
+            else lorutime = date;
+        }
+
+        if (maxargs >= 3 && i < stop) year = nums[i++];
+        else year = YearFromTime(lorutime);
+
+        if (maxargs >= 2 && i < stop) month = nums[i++];
+        else month = MonthFromTime(lorutime);
+
+        if (maxargs >= 1 && i < stop) day = nums[i++];
+        else day = DateFromTime(lorutime);
 
         day = MakeDay(year, month, day); /* day within year */
         double result = MakeDate(day, TimeWithinDay(lorutime));
 
-        if (local)
-            result = internalUTC(result);
+        if (local) result = internalUTC(result);
 
         return TimeClip(result);
     }
 
-// #string_id_map#
-
     @Override
-    protected int findPrototypeId(String s)
-    {
+    protected int findPrototypeId(String s) {
         int id;
-// #generated# Last update: 2009-07-22 05:44:02 EST
-        L0: { id = 0; String X = null; int c;
-            L: switch (s.length()) {
-            case 6: c=s.charAt(0);
-                if (c=='g') { X="getDay";id=Id_getDay; }
-                else if (c=='t') { X="toJSON";id=Id_toJSON; }
-                break L;
-            case 7: switch (s.charAt(3)) {
-                case 'D': c=s.charAt(0);
-                    if (c=='g') { X="getDate";id=Id_getDate; }
-                    else if (c=='s') { X="setDate";id=Id_setDate; }
-                    break L;
-                case 'T': c=s.charAt(0);
-                    if (c=='g') { X="getTime";id=Id_getTime; }
-                    else if (c=='s') { X="setTime";id=Id_setTime; }
-                    break L;
-                case 'Y': c=s.charAt(0);
-                    if (c=='g') { X="getYear";id=Id_getYear; }
-                    else if (c=='s') { X="setYear";id=Id_setYear; }
-                    break L;
-                case 'u': X="valueOf";id=Id_valueOf; break L;
-                } break L;
-            case 8: switch (s.charAt(3)) {
-                case 'H': c=s.charAt(0);
-                    if (c=='g') { X="getHours";id=Id_getHours; }
-                    else if (c=='s') { X="setHours";id=Id_setHours; }
-                    break L;
-                case 'M': c=s.charAt(0);
-                    if (c=='g') { X="getMonth";id=Id_getMonth; }
-                    else if (c=='s') { X="setMonth";id=Id_setMonth; }
-                    break L;
-                case 'o': X="toSource";id=Id_toSource; break L;
-                case 't': X="toString";id=Id_toString; break L;
-                } break L;
-            case 9: X="getUTCDay";id=Id_getUTCDay; break L;
-            case 10: c=s.charAt(3);
-                if (c=='M') {
-                    c=s.charAt(0);
-                    if (c=='g') { X="getMinutes";id=Id_getMinutes; }
-                    else if (c=='s') { X="setMinutes";id=Id_setMinutes; }
-                }
-                else if (c=='S') {
-                    c=s.charAt(0);
-                    if (c=='g') { X="getSeconds";id=Id_getSeconds; }
-                    else if (c=='s') { X="setSeconds";id=Id_setSeconds; }
-                }
-                else if (c=='U') {
-                    c=s.charAt(0);
-                    if (c=='g') { X="getUTCDate";id=Id_getUTCDate; }
-                    else if (c=='s') { X="setUTCDate";id=Id_setUTCDate; }
-                }
-                break L;
-            case 11: switch (s.charAt(3)) {
-                case 'F': c=s.charAt(0);
-                    if (c=='g') { X="getFullYear";id=Id_getFullYear; }
-                    else if (c=='s') { X="setFullYear";id=Id_setFullYear; }
-                    break L;
-                case 'M': X="toGMTString";id=Id_toGMTString; break L;
-                case 'S': X="toISOString";id=Id_toISOString; break L;
-                case 'T': X="toUTCString";id=Id_toUTCString; break L;
-                case 'U': c=s.charAt(0);
-                    if (c=='g') {
-                        c=s.charAt(9);
-                        if (c=='r') { X="getUTCHours";id=Id_getUTCHours; }
-                        else if (c=='t') { X="getUTCMonth";id=Id_getUTCMonth; }
-                    }
-                    else if (c=='s') {
-                        c=s.charAt(9);
-                        if (c=='r') { X="setUTCHours";id=Id_setUTCHours; }
-                        else if (c=='t') { X="setUTCMonth";id=Id_setUTCMonth; }
-                    }
-                    break L;
-                case 's': X="constructor";id=Id_constructor; break L;
-                } break L;
-            case 12: c=s.charAt(2);
-                if (c=='D') { X="toDateString";id=Id_toDateString; }
-                else if (c=='T') { X="toTimeString";id=Id_toTimeString; }
-                break L;
-            case 13: c=s.charAt(0);
-                if (c=='g') {
-                    c=s.charAt(6);
-                    if (c=='M') { X="getUTCMinutes";id=Id_getUTCMinutes; }
-                    else if (c=='S') { X="getUTCSeconds";id=Id_getUTCSeconds; }
-                }
-                else if (c=='s') {
-                    c=s.charAt(6);
-                    if (c=='M') { X="setUTCMinutes";id=Id_setUTCMinutes; }
-                    else if (c=='S') { X="setUTCSeconds";id=Id_setUTCSeconds; }
-                }
-                break L;
-            case 14: c=s.charAt(0);
-                if (c=='g') { X="getUTCFullYear";id=Id_getUTCFullYear; }
-                else if (c=='s') { X="setUTCFullYear";id=Id_setUTCFullYear; }
-                else if (c=='t') { X="toLocaleString";id=Id_toLocaleString; }
-                break L;
-            case 15: c=s.charAt(0);
-                if (c=='g') { X="getMilliseconds";id=Id_getMilliseconds; }
-                else if (c=='s') { X="setMilliseconds";id=Id_setMilliseconds; }
-                break L;
-            case 17: X="getTimezoneOffset";id=Id_getTimezoneOffset; break L;
-            case 18: c=s.charAt(0);
-                if (c=='g') { X="getUTCMilliseconds";id=Id_getUTCMilliseconds; }
-                else if (c=='s') { X="setUTCMilliseconds";id=Id_setUTCMilliseconds; }
-                else if (c=='t') {
-                    c=s.charAt(8);
-                    if (c=='D') { X="toLocaleDateString";id=Id_toLocaleDateString; }
-                    else if (c=='T') { X="toLocaleTimeString";id=Id_toLocaleTimeString; }
-                }
-                break L;
-            }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-            break L0;
+        switch (s) {
+            case "constructor":
+                id = Id_constructor;
+                break;
+            case "toString":
+                id = Id_toString;
+                break;
+            case "toTimeString":
+                id = Id_toTimeString;
+                break;
+            case "toDateString":
+                id = Id_toDateString;
+                break;
+            case "toLocaleString":
+                id = Id_toLocaleString;
+                break;
+            case "toLocaleTimeString":
+                id = Id_toLocaleTimeString;
+                break;
+            case "toLocaleDateString":
+                id = Id_toLocaleDateString;
+                break;
+            case "toUTCString":
+                id = Id_toUTCString;
+                break;
+            case "toSource":
+                id = Id_toSource;
+                break;
+            case "valueOf":
+                id = Id_valueOf;
+                break;
+            case "getTime":
+                id = Id_getTime;
+                break;
+            case "getYear":
+                id = Id_getYear;
+                break;
+            case "getFullYear":
+                id = Id_getFullYear;
+                break;
+            case "getUTCFullYear":
+                id = Id_getUTCFullYear;
+                break;
+            case "getMonth":
+                id = Id_getMonth;
+                break;
+            case "getUTCMonth":
+                id = Id_getUTCMonth;
+                break;
+            case "getDate":
+                id = Id_getDate;
+                break;
+            case "getUTCDate":
+                id = Id_getUTCDate;
+                break;
+            case "getDay":
+                id = Id_getDay;
+                break;
+            case "getUTCDay":
+                id = Id_getUTCDay;
+                break;
+            case "getHours":
+                id = Id_getHours;
+                break;
+            case "getUTCHours":
+                id = Id_getUTCHours;
+                break;
+            case "getMinutes":
+                id = Id_getMinutes;
+                break;
+            case "getUTCMinutes":
+                id = Id_getUTCMinutes;
+                break;
+            case "getSeconds":
+                id = Id_getSeconds;
+                break;
+            case "getUTCSeconds":
+                id = Id_getUTCSeconds;
+                break;
+            case "getMilliseconds":
+                id = Id_getMilliseconds;
+                break;
+            case "getUTCMilliseconds":
+                id = Id_getUTCMilliseconds;
+                break;
+            case "getTimezoneOffset":
+                id = Id_getTimezoneOffset;
+                break;
+            case "setTime":
+                id = Id_setTime;
+                break;
+            case "setMilliseconds":
+                id = Id_setMilliseconds;
+                break;
+            case "setUTCMilliseconds":
+                id = Id_setUTCMilliseconds;
+                break;
+            case "setSeconds":
+                id = Id_setSeconds;
+                break;
+            case "setUTCSeconds":
+                id = Id_setUTCSeconds;
+                break;
+            case "setMinutes":
+                id = Id_setMinutes;
+                break;
+            case "setUTCMinutes":
+                id = Id_setUTCMinutes;
+                break;
+            case "setHours":
+                id = Id_setHours;
+                break;
+            case "setUTCHours":
+                id = Id_setUTCHours;
+                break;
+            case "setDate":
+                id = Id_setDate;
+                break;
+            case "setUTCDate":
+                id = Id_setUTCDate;
+                break;
+            case "setMonth":
+                id = Id_setMonth;
+                break;
+            case "setUTCMonth":
+                id = Id_setUTCMonth;
+                break;
+            case "setFullYear":
+                id = Id_setFullYear;
+                break;
+            case "setUTCFullYear":
+                id = Id_setUTCFullYear;
+                break;
+            case "setYear":
+                id = Id_setYear;
+                break;
+            case "toISOString":
+                id = Id_toISOString;
+                break;
+            case "toJSON":
+                id = Id_toJSON;
+                break;
+            case "toGMTString":
+                id = Id_toGMTString;
+                break;
+            default:
+                id = 0;
+                break;
         }
-// #/generated#
         return id;
     }
 
-    private static final int
-        ConstructorId_now       = -3,
-        ConstructorId_parse     = -2,
-        ConstructorId_UTC       = -1,
-
-        Id_constructor          =  1,
-        Id_toString             =  2,
-        Id_toTimeString         =  3,
-        Id_toDateString         =  4,
-        Id_toLocaleString       =  5,
-        Id_toLocaleTimeString   =  6,
-        Id_toLocaleDateString   =  7,
-        Id_toUTCString          =  8,
-        Id_toSource             =  9,
-        Id_valueOf              = 10,
-        Id_getTime              = 11,
-        Id_getYear              = 12,
-        Id_getFullYear          = 13,
-        Id_getUTCFullYear       = 14,
-        Id_getMonth             = 15,
-        Id_getUTCMonth          = 16,
-        Id_getDate              = 17,
-        Id_getUTCDate           = 18,
-        Id_getDay               = 19,
-        Id_getUTCDay            = 20,
-        Id_getHours             = 21,
-        Id_getUTCHours          = 22,
-        Id_getMinutes           = 23,
-        Id_getUTCMinutes        = 24,
-        Id_getSeconds           = 25,
-        Id_getUTCSeconds        = 26,
-        Id_getMilliseconds      = 27,
-        Id_getUTCMilliseconds   = 28,
-        Id_getTimezoneOffset    = 29,
-        Id_setTime              = 30,
-        Id_setMilliseconds      = 31,
-        Id_setUTCMilliseconds   = 32,
-        Id_setSeconds           = 33,
-        Id_setUTCSeconds        = 34,
-        Id_setMinutes           = 35,
-        Id_setUTCMinutes        = 36,
-        Id_setHours             = 37,
-        Id_setUTCHours          = 38,
-        Id_setDate              = 39,
-        Id_setUTCDate           = 40,
-        Id_setMonth             = 41,
-        Id_setUTCMonth          = 42,
-        Id_setFullYear          = 43,
-        Id_setUTCFullYear       = 44,
-        Id_setYear              = 45,
-        Id_toISOString          = 46,
-        Id_toJSON               = 47,
-
-        MAX_PROTOTYPE_ID        = Id_toJSON;
-
-    private static final int
-        Id_toGMTString  =  Id_toUTCString; // Alias, see Ecma B.2.6
-// #/string_id_map#
+    private static final int ConstructorId_now = -3,
+            ConstructorId_parse = -2,
+            ConstructorId_UTC = -1,
+            Id_constructor = 1,
+            Id_toString = 2,
+            Id_toTimeString = 3,
+            Id_toDateString = 4,
+            Id_toLocaleString = 5,
+            Id_toLocaleTimeString = 6,
+            Id_toLocaleDateString = 7,
+            Id_toUTCString = 8,
+            Id_toSource = 9,
+            Id_valueOf = 10,
+            Id_getTime = 11,
+            Id_getYear = 12,
+            Id_getFullYear = 13,
+            Id_getUTCFullYear = 14,
+            Id_getMonth = 15,
+            Id_getUTCMonth = 16,
+            Id_getDate = 17,
+            Id_getUTCDate = 18,
+            Id_getDay = 19,
+            Id_getUTCDay = 20,
+            Id_getHours = 21,
+            Id_getUTCHours = 22,
+            Id_getMinutes = 23,
+            Id_getUTCMinutes = 24,
+            Id_getSeconds = 25,
+            Id_getUTCSeconds = 26,
+            Id_getMilliseconds = 27,
+            Id_getUTCMilliseconds = 28,
+            Id_getTimezoneOffset = 29,
+            Id_setTime = 30,
+            Id_setMilliseconds = 31,
+            Id_setUTCMilliseconds = 32,
+            Id_setSeconds = 33,
+            Id_setUTCSeconds = 34,
+            Id_setMinutes = 35,
+            Id_setUTCMinutes = 36,
+            Id_setHours = 37,
+            Id_setUTCHours = 38,
+            Id_setDate = 39,
+            Id_setUTCDate = 40,
+            Id_setMonth = 41,
+            Id_setUTCMonth = 42,
+            Id_setFullYear = 43,
+            Id_setUTCFullYear = 44,
+            Id_setYear = 45,
+            Id_toISOString = 46,
+            Id_toJSON = 47,
+            MAX_PROTOTYPE_ID = Id_toJSON;
+
+    private static final int Id_toGMTString = Id_toUTCString; // Alias, see Ecma B.2.6
 
     /* cached values */
-    private static TimeZone thisTimeZone;
-    private static double LocalTZA;
-    private static DateFormat timeZoneFormatter;
-    private static DateFormat localeDateTimeFormatter;
-    private static DateFormat localeDateFormatter;
-    private static DateFormat localeTimeFormatter;
+    private static final TimeZone thisTimeZone = TimeZone.getDefault();
+    private static final double LocalTZA = thisTimeZone.getRawOffset();
+
+    // not thread safe
+    private static final DateFormat timeZoneFormatter = new SimpleDateFormat("zzz");
+    private static final DateFormat localeDateTimeFormatter =
+            new SimpleDateFormat("MMMM d, yyyy h:mm:ss a z");
+    private static final DateFormat localeDateFormatter = new SimpleDateFormat("MMMM d, yyyy");
+    private static final DateFormat localeTimeFormatter = new SimpleDateFormat("h:mm:ss a z");
 
     private double date;
 }
-
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeError.java rhino-1.7.14/src/org/mozilla/javascript/NativeError.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeError.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeError.java	2022-01-06 22:57:21.000000000 +0100
@@ -7,33 +7,17 @@
 package org.mozilla.javascript;
 
 import java.io.Serializable;
-import java.lang.reflect.Method;
 
 /**
- *
  * The class of error objects
  *
- *  ECMA 15.11
+ * <p>ECMA 15.11
  */
-final class NativeError extends IdScriptableObject
-{
-    static final long serialVersionUID = -5338413581437645187L;
+final class NativeError extends IdScriptableObject {
+    private static final long serialVersionUID = -5338413581437645187L;
 
     private static final Object ERROR_TAG = "Error";
 
-    private static final Method ERROR_DELEGATE_GET_STACK;
-    private static final Method ERROR_DELEGATE_SET_STACK;
-
-    static {
-        try {
-            // Pre-cache methods to be called via reflection
-            ERROR_DELEGATE_GET_STACK = NativeError.class.getMethod("getStackDelegated", Scriptable.class);
-            ERROR_DELEGATE_SET_STACK = NativeError.class.getMethod("setStackDelegated", Scriptable.class, Object.class);
-        } catch (NoSuchMethodException nsm) {
-            throw new RuntimeException(nsm);
-        }
-    }
-
     /** Default stack limit is set to "Infinity", here represented as a negative int */
     public static final int DEFAULT_STACK_LIMIT = -1;
 
@@ -41,24 +25,22 @@
     private static final String STACK_HIDE_KEY = "_stackHide";
 
     private RhinoException stackProvider;
+    private Object stack;
 
-    static void init(Scriptable scope, boolean sealed)
-    {
+    static void init(Scriptable scope, boolean sealed) {
         NativeError obj = new NativeError();
         ScriptableObject.putProperty(obj, "name", "Error");
         ScriptableObject.putProperty(obj, "message", "");
         ScriptableObject.putProperty(obj, "fileName", "");
-        ScriptableObject.putProperty(obj, "lineNumber", Integer.valueOf(0));
+        ScriptableObject.putProperty(obj, "lineNumber", 0);
         obj.setAttributes("name", ScriptableObject.DONTENUM);
         obj.setAttributes("message", ScriptableObject.DONTENUM);
         obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
         NativeCallSite.init(obj, sealed);
     }
 
-    static NativeError make(Context cx, Scriptable scope,
-                            IdFunctionObject ctorObj, Object[] args)
-    {
-        Scriptable proto = (Scriptable)(ctorObj.get("prototype", ctorObj));
+    static NativeError make(Context cx, Scriptable scope, IdFunctionObject ctorObj, Object[] args) {
+        Scriptable proto = (Scriptable) (ctorObj.get("prototype", ctorObj));
 
         NativeError obj = new NativeError();
         obj.setPrototype(proto);
@@ -66,16 +48,15 @@
 
         int arglen = args.length;
         if (arglen >= 1) {
-            if (args[0] != Undefined.instance) {
-                ScriptableObject.putProperty(obj, "message",
-                        ScriptRuntime.toString(args[0]));
+            if (!Undefined.isUndefined(args[0])) {
+                ScriptableObject.putProperty(obj, "message", ScriptRuntime.toString(args[0]));
+                obj.setAttributes("message", ScriptableObject.DONTENUM);
             }
             if (arglen >= 2) {
                 ScriptableObject.putProperty(obj, "fileName", args[1]);
                 if (arglen >= 3) {
                     int line = ScriptRuntime.toInt32(args[2]);
-                    ScriptableObject.putProperty(obj, "lineNumber",
-                            Integer.valueOf(line));
+                    ScriptableObject.putProperty(obj, "lineNumber", line);
                 }
             }
         }
@@ -83,10 +64,14 @@
     }
 
     @Override
-    protected void fillConstructorProperties(IdFunctionObject ctor)
-    {
-        addIdFunctionProperty(ctor, ERROR_TAG, ConstructorId_captureStackTrace,
-                                  "captureStackTrace", 2);
+    protected void fillConstructorProperties(IdFunctionObject ctor) {
+        addIdFunctionProperty(
+                ctor, ERROR_TAG, ConstructorId_captureStackTrace, "captureStackTrace", 2);
+
+        // Define a stack to be used even if this Error is never thrown.
+        // The big cost is in turning this into an actual stack trace, which is set up to
+        // be done lazily below.
+        stackProvider = new EvaluatorException("");
 
         // This is running on the global "Error" object. Associate an object there that can store
         // default stack trace, etc.
@@ -94,64 +79,79 @@
         ProtoProps protoProps = new ProtoProps();
         associateValue(ProtoProps.KEY, protoProps);
 
-        // Define constructor properties that delegate to the ProtoProps object.
-        ctor.defineProperty("stackTraceLimit", protoProps,
-                            ProtoProps.GET_STACK_LIMIT, ProtoProps.SET_STACK_LIMIT, 0);
-        ctor.defineProperty("prepareStackTrace", protoProps,
-                            ProtoProps.GET_PREPARE_STACK, ProtoProps.SET_PREPARE_STACK, 0);
+        ctor.defineProperty(
+                "stackTraceLimit",
+                protoProps::getStackTraceLimit,
+                protoProps::setStackTraceLimit,
+                0);
+        ctor.defineProperty(
+                "prepareStackTrace",
+                protoProps::getPrepareStackTrace,
+                protoProps::setPrepareStackTrace,
+                0);
 
         super.fillConstructorProperties(ctor);
     }
 
     @Override
-    public String getClassName()
-    {
+    public String getClassName() {
         return "Error";
     }
 
     @Override
-    public String toString()
-    {
+    public String toString() {
         // According to spec, Error.prototype.toString() may return undefined.
         Object toString = js_toString(this);
         return toString instanceof String ? (String) toString : super.toString();
     }
 
     @Override
-    protected void initPrototypeId(int id)
-    {
+    protected void initPrototypeId(int id) {
         String s;
         int arity;
         switch (id) {
-          case Id_constructor: arity=1; s="constructor"; break;
-          case Id_toString:    arity=0; s="toString";    break;
-          case Id_toSource:    arity=0; s="toSource";    break;
-          default: throw new IllegalArgumentException(String.valueOf(id));
+            case Id_constructor:
+                arity = 1;
+                s = "constructor";
+                break;
+            case Id_toString:
+                arity = 0;
+                s = "toString";
+                break;
+            case Id_toSource:
+                arity = 0;
+                s = "toSource";
+                break;
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
         }
         initPrototypeMethod(ERROR_TAG, id, s, arity);
     }
 
     @Override
-    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
-                             Scriptable thisObj, Object[] args)
-    {
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         if (!f.hasTag(ERROR_TAG)) {
             return super.execIdCall(f, cx, scope, thisObj, args);
         }
         int id = f.methodId();
         switch (id) {
-          case Id_constructor:
-            return make(cx, scope, f, args);
-
-          case Id_toString:
-            return js_toString(thisObj);
-
-          case Id_toSource:
-            return js_toSource(cx, scope, thisObj);
-
-          case ConstructorId_captureStackTrace:
-            js_captureStackTrace(cx, thisObj, args);
-            return Undefined.instance;
+            case Id_constructor:
+                NativeError err = make(cx, scope, f, args);
+                // All new Errors have a default exception installed so that
+                // there is a stack trace captured even if they are never thrown.
+                err.setStackProvider(new EvaluatorException(""));
+                return err;
+
+            case Id_toString:
+                return js_toString(thisObj);
+
+            case Id_toSource:
+                return js_toSource(cx, scope, thisObj);
+
+            case ConstructorId_captureStackTrace:
+                js_captureStackTrace(cx, scope, thisObj, args);
+                return Undefined.instance;
         }
         throw new IllegalArgumentException(String.valueOf(id));
     }
@@ -162,14 +162,15 @@
         // overwritable like an ordinary property. Hence this setup with
         // the getter and setter below.
         if (stackProvider == null) {
-            stackProvider = re;
-            defineProperty("stack", this,
-                           ERROR_DELEGATE_GET_STACK, ERROR_DELEGATE_SET_STACK,
-                           DONTENUM);
+            defineProperty("stack", this::getStackDelegated, this::setStackDelegated, DONTENUM);
         }
+        stackProvider = re;
     }
 
-    public Object getStackDelegated(Scriptable target) {
+    public Object getStackDelegated() {
+        if (stack != null) {
+            return stack;
+        }
         if (stackProvider == null) {
             return NOT_FOUND;
         }
@@ -177,79 +178,74 @@
         // Get the object where prototype stuff is stored.
         int limit = DEFAULT_STACK_LIMIT;
         Function prepare = null;
-        NativeError cons = (NativeError)getPrototype();
-        ProtoProps pp = (ProtoProps)cons.getAssociatedValue(ProtoProps.KEY);
+        NativeError cons = (NativeError) getPrototype();
+        ProtoProps pp = (ProtoProps) cons.getAssociatedValue(ProtoProps.KEY);
 
         if (pp != null) {
-            limit = pp.getStackTraceLimit();
-            prepare = pp.getPrepareStackTrace();
+            limit = pp.stackTraceLimit;
+            prepare = pp.prepareStackTrace;
         }
 
         // This key is only set by captureStackTrace
-        String hideFunc = (String)getAssociatedValue(STACK_HIDE_KEY);
-        ScriptStackElement[] stack = stackProvider.getScriptStack(limit, hideFunc);
+        String hideFunc = (String) getAssociatedValue(STACK_HIDE_KEY);
+        ScriptStackElement[] stackTrace = stackProvider.getScriptStack(limit, hideFunc);
 
         // Determine whether to format the stack trace ourselves, or call the user's code to do it
         Object value;
         if (prepare == null) {
-            value = RhinoException.formatStackTrace(stack, stackProvider.details());
+            value = RhinoException.formatStackTrace(stackTrace, stackProvider.details());
         } else {
-            value = callPrepareStack(prepare, stack);
+            value = callPrepareStack(prepare, stackTrace);
         }
-
-        // We store the stack as local property both to cache it
-        // and to make the property writable
-        setStackDelegated(target, value);
+        stack = value;
         return value;
     }
 
-    public void setStackDelegated(Scriptable target, Object value) {
-        target.delete("stack");
+    public void setStackDelegated(Object value) {
         stackProvider = null;
-        target.put("stack", target, value);
+        stack = value;
     }
 
-    private Object callPrepareStack(Function prepare, ScriptStackElement[] stack)
-    {
+    private Object callPrepareStack(Function prepare, ScriptStackElement[] stack) {
         Context cx = Context.getCurrentContext();
         Object[] elts = new Object[stack.length];
 
         // The "prepareStackTrace" function takes an array of CallSite objects.
         for (int i = 0; i < stack.length; i++) {
-            NativeCallSite site = (NativeCallSite)cx.newObject(this, "CallSite");
+            NativeCallSite site = (NativeCallSite) cx.newObject(this, "CallSite");
             site.setElement(stack[i]);
             elts[i] = site;
         }
 
         Scriptable eltArray = cx.newArray(this, elts);
-        return prepare.call(cx, prepare, this, new Object[] { this, eltArray });
+        return prepare.call(cx, prepare, this, new Object[] {this, eltArray});
     }
 
     private static Object js_toString(Scriptable thisObj) {
-        Object name = ScriptableObject.getProperty(thisObj, "name");
-        if (name == NOT_FOUND || name == Undefined.instance) {
+        Object nameObj = ScriptableObject.getProperty(thisObj, "name");
+        String name;
+        if (nameObj == NOT_FOUND || Undefined.isUndefined(nameObj)) {
             name = "Error";
         } else {
-            name = ScriptRuntime.toString(name);
+            name = ScriptRuntime.toString(nameObj);
         }
-        Object msg = ScriptableObject.getProperty(thisObj, "message");
-        if (msg == NOT_FOUND || msg == Undefined.instance) {
+        Object msgObj = ScriptableObject.getProperty(thisObj, "message");
+        String msg;
+        if (msgObj == NOT_FOUND || Undefined.isUndefined(msgObj)) {
             msg = "";
         } else {
-            msg = ScriptRuntime.toString(msg);
+            msg = ScriptRuntime.toString(msgObj);
         }
-        if (name.toString().length() == 0) {
+        if (name.isEmpty()) {
             return msg;
-        } else if (msg.toString().length() == 0) {
+        } else if (msg.isEmpty()) {
             return name;
         } else {
-            return ((String) name) + ": " + ((String) msg);
+            return name + ": " + msg;
         }
     }
 
-    private static String js_toSource(Context cx, Scriptable scope,
-                                      Scriptable thisObj)
-    {
+    private static String js_toSource(Context cx, Scriptable scope, Scriptable thisObj) {
         // Emulation of SpiderMonkey behavior
         Object name = ScriptableObject.getProperty(thisObj, "name");
         Object message = ScriptableObject.getProperty(thisObj, "message");
@@ -263,10 +259,7 @@
         }
         sb.append(ScriptRuntime.toString(name));
         sb.append("(");
-        if (message != NOT_FOUND
-            || fileName != NOT_FOUND
-            || lineNumber != NOT_FOUND)
-        {
+        if (message != NOT_FOUND || fileName != NOT_FOUND || lineNumber != NOT_FOUND) {
             if (message == NOT_FOUND) {
                 message = "";
             }
@@ -290,131 +283,100 @@
         return sb.toString();
     }
 
-    private static void js_captureStackTrace(Context cx, Scriptable thisObj, Object[] args)
-    {
-        ScriptableObject obj = (ScriptableObject)ScriptRuntime.toObjectOrNull(cx, args[0], thisObj);
+    private static void js_captureStackTrace(
+            Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        ScriptableObject obj = (ScriptableObject) ScriptRuntime.toObject(cx, scope, args[0]);
         Function func = null;
         if (args.length > 1) {
-            func = (Function)ScriptRuntime.toObjectOrNull(cx, args[1], thisObj);
+            func = (Function) ScriptRuntime.toObjectOrNull(cx, args[1], scope);
         }
 
         // Create a new error that will have the correct prototype so we can re-use "getStackTrace"
-        NativeError err = (NativeError)cx.newObject(thisObj, "Error");
+        NativeError err = (NativeError) cx.newObject(thisObj, "Error");
         // Wire it up so that it will have an actual exception with a stack trace
         err.setStackProvider(new EvaluatorException("[object Object]"));
 
         // Figure out if they passed a function used to hide part of the stack
         if (func != null) {
             Object funcName = func.get("name", func);
-            if ((funcName != null) && !Undefined.instance.equals(funcName)) {
+            if ((funcName != null) && !Undefined.isUndefined(funcName)) {
                 err.associateValue(STACK_HIDE_KEY, Context.toString(funcName));
             }
         }
 
-        // Define a property on the specified object to get that stack
-        // that delegates to our new error. Build the stack trace lazily
-        // using the "getStack" code from NativeError.
-        obj.defineProperty("stack", err,
-                           ERROR_DELEGATE_GET_STACK, ERROR_DELEGATE_SET_STACK, 0);
+        // from https://v8.dev/docs/stack-trace-api
+        // Error.captureStackTrace(error, constructorOpt)
+        // adds a stack property to the given error object that yields the stack trace
+        // at the time captureStackTrace was called. Stack traces collected through
+        // Error.captureStackTrace are immediately collected, formatted,
+        // and attached to the given error object.
+        obj.defineProperty("stack", err.get("stack"), ScriptableObject.DONTENUM);
     }
 
     @Override
-    protected int findPrototypeId(String s)
-    {
+    protected int findPrototypeId(String s) {
         int id;
-// #string_id_map#
-// #generated# Last update: 2007-05-09 08:15:45 EDT
-        L0: { id = 0; String X = null; int c;
-            int s_length = s.length();
-            if (s_length==8) {
-                c=s.charAt(3);
-                if (c=='o') { X="toSource";id=Id_toSource; }
-                else if (c=='t') { X="toString";id=Id_toString; }
-            }
-            else if (s_length==11) { X="constructor";id=Id_constructor; }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-            break L0;
+        switch (s) {
+            case "constructor":
+                id = Id_constructor;
+                break;
+            case "toString":
+                id = Id_toString;
+                break;
+            case "toSource":
+                id = Id_toSource;
+                break;
+            default:
+                id = 0;
+                break;
         }
-// #/generated#
         return id;
     }
 
-    private static final int
-        Id_constructor    = 1,
-        Id_toString       = 2,
-        Id_toSource       = 3,
-        ConstructorId_captureStackTrace = -1,
-
-        MAX_PROTOTYPE_ID  = 3;
-
-// #/string_id_map#
+    private static final int Id_constructor = 1,
+            Id_toString = 2,
+            Id_toSource = 3,
+            ConstructorId_captureStackTrace = -1,
+            MAX_PROTOTYPE_ID = 3;
 
     /**
-     * We will attch this object to the constructor and use it solely to store the constructor properties
-     * that are "global." We can't make them static because there can be many contexts in the same JVM.
+     * We will attch this object to the constructor and use it solely to store the constructor
+     * properties that are "global." We can't make them static because there can be many contexts in
+     * the same JVM.
      */
-    private static final class ProtoProps
-        implements Serializable
-    {
+    private static final class ProtoProps implements Serializable {
         static final String KEY = "_ErrorPrototypeProps";
 
-        static final Method GET_STACK_LIMIT;
-        static final Method SET_STACK_LIMIT;
-        static final Method GET_PREPARE_STACK;
-        static final Method SET_PREPARE_STACK;
-
-        static {
-            try {
-                GET_STACK_LIMIT = ProtoProps.class.getMethod("getStackTraceLimit", Scriptable.class);
-                SET_STACK_LIMIT = ProtoProps.class.getMethod("setStackTraceLimit", Scriptable.class, Object.class);
-                GET_PREPARE_STACK = ProtoProps.class.getMethod("getPrepareStackTrace", Scriptable.class);
-                SET_PREPARE_STACK = ProtoProps.class.getMethod("setPrepareStackTrace", Scriptable.class, Object.class);
-            } catch (NoSuchMethodException nsm) {
-                throw new RuntimeException(nsm);
-            }
-        }
-
         private static final long serialVersionUID = 1907180507775337939L;
 
-        private int stackTraceLimit = DEFAULT_STACK_LIMIT;
-        private Function prepareStackTrace;
+        int stackTraceLimit = DEFAULT_STACK_LIMIT;
+        Function prepareStackTrace;
 
-        public Object getStackTraceLimit(Scriptable thisObj) {
+        public Object getStackTraceLimit() {
             if (stackTraceLimit >= 0) {
                 return stackTraceLimit;
-            } else {
-                return Double.POSITIVE_INFINITY;
             }
+            return Double.POSITIVE_INFINITY;
         }
 
-        public int getStackTraceLimit() {
-            return stackTraceLimit;
-        }
-
-        public void setStackTraceLimit(Scriptable thisObj, Object value) {
+        public void setStackTraceLimit(Object value) {
             double limit = Context.toNumber(value);
             if (Double.isNaN(limit) || Double.isInfinite(limit)) {
                 stackTraceLimit = -1;
             } else {
-                stackTraceLimit = (int)limit;
+                stackTraceLimit = (int) limit;
             }
         }
 
-        public Object getPrepareStackTrace(Scriptable thisObj)
-        {
-            Object ps = getPrepareStackTrace();
-            return (ps == null ? Undefined.instance : ps);
-        }
-
-        public Function getPrepareStackTrace() {
-            return prepareStackTrace;
+        public Object getPrepareStackTrace() {
+            return (prepareStackTrace == null ? Undefined.instance : prepareStackTrace);
         }
 
-        public void setPrepareStackTrace(Scriptable thisObj, Object value) {
-            if ((value == null) || Undefined.instance.equals(value)) {
+        public void setPrepareStackTrace(Object value) {
+            if ((value == null) || Undefined.isUndefined(value)) {
                 prepareStackTrace = null;
             } else if (value instanceof Function) {
-                prepareStackTrace = (Function)value;
+                prepareStackTrace = (Function) value;
             }
         }
     }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeFunction.java rhino-1.7.14/src/org/mozilla/javascript/NativeFunction.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeFunction.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeFunction.java	2022-01-06 22:57:21.000000000 +0100
@@ -16,11 +16,16 @@
 public abstract class NativeFunction extends BaseFunction
 {
 
-    static final long serialVersionUID = 8713897114082216401L;
-    
+    private static final long serialVersionUID = 8713897114082216401L;
+
     public final void initScriptFunction(Context cx, Scriptable scope)
     {
-        ScriptRuntime.setFunctionProtoAndParent(this, scope);
+        initScriptFunction(cx, scope, isGeneratorFunction());
+    }
+
+    public final void initScriptFunction(Context cx, Scriptable scope, boolean es6GeneratorFunction)
+    {
+        ScriptRuntime.setFunctionProtoAndParent(this, scope, es6GeneratorFunction);
     }
 
     /**
@@ -34,11 +39,10 @@
         String encodedSource = getEncodedSource();
         if (encodedSource == null) {
             return super.decompile(indent, flags);
-        } else {
-            UintMap properties = new UintMap(1);
-            properties.put(Decompiler.INITIAL_INDENT_PROP, indent);
-            return Decompiler.decompile(encodedSource, flags, properties);
         }
+        UintMap properties = new UintMap(1);
+        properties.put(Decompiler.INITIAL_INDENT_PROP, indent);
+        return Decompiler.decompile(encodedSource, flags, properties);
     }
 
     @Override
@@ -117,14 +121,14 @@
 
     /**
      * Get parameter or variable name.
-     * If <tt>index < {@link #getParamCount()}</tt>, then return the name of the
+     * If <code>index < {@link #getParamCount()}</code>, then return the name of the
      * corresponding parameter. Otherwise return the name of variable.
      */
     protected abstract String getParamOrVarName(int index);
 
     /**
      * Get parameter or variable const-ness.
-     * If <tt>index < {@link #getParamCount()}</tt>, then return the const-ness
+     * If <code>index < {@link #getParamCount()}</code>, then return the const-ness
      * of the corresponding parameter. Otherwise return whether the variable is
      * const.
      */
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeGenerator.java rhino-1.7.14/src/org/mozilla/javascript/NativeGenerator.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeGenerator.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeGenerator.java	2022-01-06 22:57:21.000000000 +0100
@@ -43,14 +43,10 @@
         return prototype;
     }
 
-    /**
-     * Only for constructing the prototype object.
-     */
-    private NativeGenerator() { }
-
-    public NativeGenerator(Scriptable scope, NativeFunction function,
-                           Object savedState)
-    {
+    /** Only for constructing the prototype object. */
+    private NativeGenerator() {}
+
+    public NativeGenerator(Scriptable scope, NativeFunction function, Object savedState) {
         this.function = function;
         this.savedState = savedState;
         // Set parent and prototype properties. Since we don't have a
@@ -58,14 +54,12 @@
         // prototype in the top scope's associated value.
         Scriptable top = ScriptableObject.getTopLevelScope(scope);
         this.setParentScope(top);
-        NativeGenerator prototype = (NativeGenerator)
-            ScriptableObject.getTopScopeValue(top, GENERATOR_TAG);
+        NativeGenerator prototype =
+                (NativeGenerator) ScriptableObject.getTopScopeValue(top, GENERATOR_TAG);
         this.setPrototype(prototype);
     }
 
-    public static final int GENERATOR_SEND  = 0,
-                            GENERATOR_THROW = 1,
-                            GENERATOR_CLOSE = 2;
+    public static final int GENERATOR_SEND = 0, GENERATOR_THROW = 1, GENERATOR_CLOSE = 2;
 
     @Override
     public String getClassName() {
@@ -77,69 +71,76 @@
         String s;
         int arity;
         switch (id) {
-          case Id_close:          arity=1; s="close";          break;
-          case Id_next:           arity=1; s="next";           break;
-          case Id_send:           arity=0; s="send";           break;
-          case Id_throw:          arity=0; s="throw";          break;
-          case Id___iterator__:   arity=1; s="__iterator__";   break;
-          default: throw new IllegalArgumentException(String.valueOf(id));
+            case Id_close:
+                arity = 1;
+                s = "close";
+                break;
+            case Id_next:
+                arity = 1;
+                s = "next";
+                break;
+            case Id_send:
+                arity = 0;
+                s = "send";
+                break;
+            case Id_throw:
+                arity = 0;
+                s = "throw";
+                break;
+            case Id___iterator__:
+                arity = 1;
+                s = "__iterator__";
+                break;
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
         }
         initPrototypeMethod(GENERATOR_TAG, id, s, arity);
     }
 
     @Override
-    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
-                             Scriptable thisObj, Object[] args)
-    {
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         if (!f.hasTag(GENERATOR_TAG)) {
             return super.execIdCall(f, cx, scope, thisObj, args);
         }
         int id = f.methodId();
 
-        if (!(thisObj instanceof NativeGenerator))
-            throw incompatibleCallError(f);
-
-        NativeGenerator generator = (NativeGenerator) thisObj;
+        NativeGenerator generator = ensureType(thisObj, NativeGenerator.class, f);
 
         switch (id) {
+            case Id_close:
+                // need to run any pending finally clauses
+                return generator.resume(cx, scope, GENERATOR_CLOSE, new GeneratorClosedException());
 
-          case Id_close:
-            // need to run any pending finally clauses
-            return generator.resume(cx, scope, GENERATOR_CLOSE,
-                                    new GeneratorClosedException());
-
-          case Id_next:
-            // arguments to next() are ignored
-            generator.firstTime = false;
-            return generator.resume(cx, scope, GENERATOR_SEND,
-                                    Undefined.instance);
-
-          case Id_send: {
-            Object arg = args.length > 0 ? args[0] : Undefined.instance;
-            if (generator.firstTime && !arg.equals(Undefined.instance)) {
-                throw ScriptRuntime.typeError0("msg.send.newborn");
-            }
-            return generator.resume(cx, scope, GENERATOR_SEND, arg);
-          }
+            case Id_next:
+                // arguments to next() are ignored
+                generator.firstTime = false;
+                return generator.resume(cx, scope, GENERATOR_SEND, Undefined.instance);
+
+            case Id_send:
+                {
+                    Object arg = args.length > 0 ? args[0] : Undefined.instance;
+                    if (generator.firstTime && !arg.equals(Undefined.instance)) {
+                        throw ScriptRuntime.typeErrorById("msg.send.newborn");
+                    }
+                    return generator.resume(cx, scope, GENERATOR_SEND, arg);
+                }
 
-          case Id_throw:
-            return generator.resume(cx, scope, GENERATOR_THROW,
-                args.length > 0 ? args[0] : Undefined.instance);
+            case Id_throw:
+                return generator.resume(
+                        cx, scope, GENERATOR_THROW, args.length > 0 ? args[0] : Undefined.instance);
 
-          case Id___iterator__:
-            return thisObj;
+            case Id___iterator__:
+                return thisObj;
 
-          default:
-            throw new IllegalArgumentException(String.valueOf(id));
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
         }
     }
 
-    private Object resume(Context cx, Scriptable scope, int operation,
-                          Object value)
-    {
+    private Object resume(Context cx, Scriptable scope, int operation, Object value) {
         if (savedState == null) {
-            if (operation == GENERATOR_CLOSE)
-                return Undefined.instance;
+            if (operation == GENERATOR_CLOSE) return Undefined.instance;
             Object thrown;
             if (operation == GENERATOR_THROW) {
                 thrown = value;
@@ -150,15 +151,13 @@
         }
         try {
             synchronized (this) {
-              // generator execution is necessarily single-threaded and
-              // non-reentrant.
-              // See https://bugzilla.mozilla.org/show_bug.cgi?id=349263
-              if (locked)
-                  throw ScriptRuntime.typeError0("msg.already.exec.gen");
-              locked = true;
+                // generator execution is necessarily single-threaded and
+                // non-reentrant.
+                // See https://bugzilla.mozilla.org/show_bug.cgi?id=349263
+                if (locked) throw ScriptRuntime.typeErrorById("msg.already.exec.gen");
+                locked = true;
             }
-            return function.resumeGenerator(cx, scope, operation, savedState,
-                                            value);
+            return function.resumeGenerator(cx, scope, operation, savedState, value);
         } catch (GeneratorClosedException e) {
             // On closing a generator in the compile path, the generator
             // throws a special exception. This ensures execution of all pending
@@ -171,48 +170,45 @@
             throw e;
         } finally {
             synchronized (this) {
-              locked = false;
+                locked = false;
             }
-            if (operation == GENERATOR_CLOSE)
-                savedState = null;
+            if (operation == GENERATOR_CLOSE) savedState = null;
         }
     }
 
-// #string_id_map#
-
     @Override
     protected int findPrototypeId(String s) {
         int id;
-// #generated# Last update: 2007-06-14 13:13:03 EDT
-        L0: { id = 0; String X = null; int c;
-            int s_length = s.length();
-            if (s_length==4) {
-                c=s.charAt(0);
-                if (c=='n') { X="next";id=Id_next; }
-                else if (c=='s') { X="send";id=Id_send; }
-            }
-            else if (s_length==5) {
-                c=s.charAt(0);
-                if (c=='c') { X="close";id=Id_close; }
-                else if (c=='t') { X="throw";id=Id_throw; }
-            }
-            else if (s_length==12) { X="__iterator__";id=Id___iterator__; }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-            break L0;
+        switch (s) {
+            case "close":
+                id = Id_close;
+                break;
+            case "next":
+                id = Id_next;
+                break;
+            case "send":
+                id = Id_send;
+                break;
+            case "throw":
+                id = Id_throw;
+                break;
+            case "__iterator__":
+                id = Id___iterator__;
+                break;
+            default:
+                id = 0;
+                break;
         }
-// #/generated#
         return id;
     }
 
-    private static final int
-        Id_close                 = 1,
-        Id_next                  = 2,
-        Id_send                  = 3,
-        Id_throw                 = 4,
-        Id___iterator__          = 5,
-        MAX_PROTOTYPE_ID         = 5;
+    private static final int Id_close = 1,
+            Id_next = 2,
+            Id_send = 3,
+            Id_throw = 4,
+            Id___iterator__ = 5,
+            MAX_PROTOTYPE_ID = 5;
 
-// #/string_id_map#
     private NativeFunction function;
     private Object savedState;
     private String lineSource;
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeGlobal.java rhino-1.7.14/src/org/mozilla/javascript/NativeGlobal.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeGlobal.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeGlobal.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,24 +6,21 @@
 
 package org.mozilla.javascript;
 
-import java.io.Serializable;
-
-import org.mozilla.javascript.xml.XMLLib;
 import static org.mozilla.javascript.ScriptableObject.DONTENUM;
-import static org.mozilla.javascript.ScriptableObject.READONLY;
 import static org.mozilla.javascript.ScriptableObject.PERMANENT;
+import static org.mozilla.javascript.ScriptableObject.READONLY;
+
+import java.io.Serializable;
+import org.mozilla.javascript.xml.XMLLib;
 
 /**
- * This class implements the global native object (function and value
- * properties only).
+ * This class implements the global native object (function and value properties only).
  *
- * See ECMA 15.1.[12].
+ * <p>See ECMA 15.1.[12].
  *
  * @author Mike Shaver
  */
-
-public class NativeGlobal implements Serializable, IdFunctionCall
-{
+public class NativeGlobal implements Serializable, IdFunctionCall {
     static final long serialVersionUID = 6080442165748707530L;
 
     public static void init(Context cx, Scriptable scope, boolean sealed) {
@@ -33,51 +30,50 @@
             String name;
             int arity = 1;
             switch (id) {
-              case Id_decodeURI:
-                name = "decodeURI";
-                break;
-              case Id_decodeURIComponent:
-                name = "decodeURIComponent";
-                break;
-              case Id_encodeURI:
-                name = "encodeURI";
-                break;
-              case Id_encodeURIComponent:
-                name = "encodeURIComponent";
-                break;
-              case Id_escape:
-                name = "escape";
-                break;
-              case Id_eval:
-                name = "eval";
-                break;
-              case Id_isFinite:
-                name = "isFinite";
-                break;
-              case Id_isNaN:
-                name = "isNaN";
-                break;
-              case Id_isXMLName:
-                name = "isXMLName";
-                break;
-              case Id_parseFloat:
-                name = "parseFloat";
-                break;
-              case Id_parseInt:
-                name = "parseInt";
-                arity = 2;
-                break;
-              case Id_unescape:
-                name = "unescape";
-                break;
-              case Id_uneval:
-                name = "uneval";
-                break;
-              default:
-                  throw Kit.codeBug();
+                case Id_decodeURI:
+                    name = "decodeURI";
+                    break;
+                case Id_decodeURIComponent:
+                    name = "decodeURIComponent";
+                    break;
+                case Id_encodeURI:
+                    name = "encodeURI";
+                    break;
+                case Id_encodeURIComponent:
+                    name = "encodeURIComponent";
+                    break;
+                case Id_escape:
+                    name = "escape";
+                    break;
+                case Id_eval:
+                    name = "eval";
+                    break;
+                case Id_isFinite:
+                    name = "isFinite";
+                    break;
+                case Id_isNaN:
+                    name = "isNaN";
+                    break;
+                case Id_isXMLName:
+                    name = "isXMLName";
+                    break;
+                case Id_parseFloat:
+                    name = "parseFloat";
+                    break;
+                case Id_parseInt:
+                    name = "parseInt";
+                    arity = 2;
+                    break;
+                case Id_unescape:
+                    name = "unescape";
+                    break;
+                case Id_uneval:
+                    name = "uneval";
+                    break;
+                default:
+                    throw Kit.codeBug();
             }
-            IdFunctionObject f = new IdFunctionObject(obj, FTAG, id, name,
-                                                      arity, scope);
+            IdFunctionObject f = new IdFunctionObject(obj, FTAG, id, name, arity, scope);
             if (sealed) {
                 f.sealObject();
             }
@@ -85,20 +81,26 @@
         }
 
         ScriptableObject.defineProperty(
-            scope, "NaN", ScriptRuntime.NaNobj,
-            READONLY|DONTENUM|PERMANENT);
+                scope, "NaN", ScriptRuntime.NaNobj, READONLY | DONTENUM | PERMANENT);
         ScriptableObject.defineProperty(
-            scope, "Infinity",
-            ScriptRuntime.wrapNumber(Double.POSITIVE_INFINITY),
-            READONLY|DONTENUM|PERMANENT);
+                scope,
+                "Infinity",
+                ScriptRuntime.wrapNumber(Double.POSITIVE_INFINITY),
+                READONLY | DONTENUM | PERMANENT);
         ScriptableObject.defineProperty(
-            scope, "undefined", Undefined.instance,
-            READONLY|DONTENUM|PERMANENT);
+                scope, "undefined", Undefined.instance, READONLY | DONTENUM | PERMANENT);
+        ScriptableObject.defineProperty(scope, "globalThis", scope, DONTENUM);
 
         /*
             Each error constructor gets its own Error object as a prototype,
             with the 'name' property set to the name of the error.
         */
+        Scriptable nativeError =
+                ScriptableObject.ensureScriptable(ScriptableObject.getProperty(scope, "Error"));
+        Scriptable nativeErrorProto =
+                ScriptableObject.ensureScriptable(
+                        ScriptableObject.getProperty(nativeError, "prototype"));
+
         for (TopLevel.NativeErrors error : TopLevel.NativeErrors.values()) {
             if (error == TopLevel.NativeErrors.Error) {
                 // Error is initialized elsewhere and we should not overwrite it.
@@ -106,42 +108,47 @@
             }
             String name = error.name();
             ScriptableObject errorProto =
-              (ScriptableObject) ScriptRuntime.newBuiltinObject(cx, scope,
-                                                  TopLevel.Builtins.Error,
-                                                  ScriptRuntime.emptyArgs);
-            errorProto.put("name", errorProto, name);
-            errorProto.put("message", errorProto, "");
-            IdFunctionObject ctor = new IdFunctionObject(obj, FTAG,
-                                                         Id_new_CommonError,
-                                                         name, 1, scope);
+                    (ScriptableObject)
+                            ScriptRuntime.newBuiltinObject(
+                                    cx, scope, TopLevel.Builtins.Error, ScriptRuntime.emptyArgs);
+            errorProto.defineProperty("name", name, DONTENUM);
+            errorProto.defineProperty("message", "", DONTENUM);
+            IdFunctionObject ctor =
+                    new IdFunctionObject(obj, FTAG, Id_new_CommonError, name, 1, scope);
             ctor.markAsConstructor(errorProto);
+            ctor.setPrototype(nativeError);
             errorProto.put("constructor", errorProto, ctor);
             errorProto.setAttributes("constructor", ScriptableObject.DONTENUM);
+            errorProto.setPrototype(nativeErrorProto);
             if (sealed) {
                 errorProto.sealObject();
                 ctor.sealObject();
             }
+            ctor.setAttributes("name", DONTENUM | READONLY);
+            ctor.setAttributes("length", DONTENUM | READONLY);
             ctor.exportAsScopeProperty();
         }
     }
 
-    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
-                             Scriptable thisObj, Object[] args)
-    {
+    @Override
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         if (f.hasTag(FTAG)) {
             int methodId = f.methodId();
             switch (methodId) {
                 case Id_decodeURI:
-                case Id_decodeURIComponent: {
-                    String str = ScriptRuntime.toString(args, 0);
-                    return decode(str, methodId == Id_decodeURI);
-                }
+                case Id_decodeURIComponent:
+                    {
+                        String str = ScriptRuntime.toString(args, 0);
+                        return decode(str, methodId == Id_decodeURI);
+                    }
 
                 case Id_encodeURI:
-                case Id_encodeURIComponent: {
-                    String str = ScriptRuntime.toString(args, 0);
-                    return encode(str, methodId == Id_encodeURI);
-                }
+                case Id_encodeURIComponent:
+                    {
+                        String str = ScriptRuntime.toString(args, 0);
+                        return encode(str, methodId == Id_encodeURI);
+                    }
 
                 case Id_escape:
                     return js_escape(args);
@@ -149,32 +156,33 @@
                 case Id_eval:
                     return js_eval(cx, scope, args);
 
-                case Id_isFinite: {
-                    if (args.length < 1) {
-                        return Boolean.FALSE;
-                    }
-                    return NativeNumber.isFinite(args[0]);
-                }
+                case Id_isFinite:
+                    {
+                        if (args.length < 1) {
+                            return Boolean.FALSE;
+                        }
+                        return NativeNumber.isFinite(args[0]);
+                    }
 
-                case Id_isNaN: {
-                    // The global method isNaN, as per ECMA-262 15.1.2.6.
-                    boolean result;
-                    if (args.length < 1) {
-                        result = true;
-                    } else {
-                        double d = ScriptRuntime.toNumber(args[0]);
-                        result = (d != d);
+                case Id_isNaN:
+                    {
+                        // The global method isNaN, as per ECMA-262 15.1.2.6.
+                        boolean result;
+                        if (args.length < 1) {
+                            result = true;
+                        } else {
+                            double d = ScriptRuntime.toNumber(args[0]);
+                            result = Double.isNaN(d);
+                        }
+                        return ScriptRuntime.wrapBoolean(result);
                     }
-                    return ScriptRuntime.wrapBoolean(result);
-                }
 
-                case Id_isXMLName: {
-                    Object name = (args.length == 0)
-                                  ? Undefined.instance : args[0];
-                    XMLLib xmlLib = XMLLib.extractFromScope(scope);
-                    return ScriptRuntime.wrapBoolean(
-                        xmlLib.isXMLName(cx, name));
-                }
+                case Id_isXMLName:
+                    {
+                        Object name = (args.length == 0) ? Undefined.instance : args[0];
+                        XMLLib xmlLib = XMLLib.extractFromScope(scope);
+                        return ScriptRuntime.wrapBoolean(xmlLib.isXMLName(cx, name));
+                    }
 
                 case Id_parseFloat:
                     return js_parseFloat(args);
@@ -185,11 +193,11 @@
                 case Id_unescape:
                     return js_unescape(args);
 
-                case Id_uneval: {
-                    Object value = (args.length != 0)
-                                   ? args[0] : Undefined.instance;
-                    return ScriptRuntime.uneval(cx, scope, value);
-                }
+                case Id_uneval:
+                    {
+                        Object value = (args.length != 0) ? args[0] : Undefined.instance;
+                        return ScriptRuntime.uneval(cx, scope, value);
+                    }
 
                 case Id_new_CommonError:
                     // The implementation of all the ECMA error constructors
@@ -200,29 +208,24 @@
         throw f.unknown();
     }
 
-    /**
-     * The global method parseInt, as per ECMA-262 15.1.2.2.
-     */
+    /** The global method parseInt, as per ECMA-262 15.1.2.2. */
     static Object js_parseInt(Object[] args) {
         String s = ScriptRuntime.toString(args, 0);
         int radix = ScriptRuntime.toInt32(args, 1);
 
         int len = s.length();
-        if (len == 0)
-            return ScriptRuntime.NaNobj;
+        if (len == 0) return ScriptRuntime.NaNobj;
 
         boolean negative = false;
         int start = 0;
         char c;
         do {
             c = s.charAt(start);
-            if (!ScriptRuntime.isStrWhiteSpaceChar(c))
-                break;
+            if (!ScriptRuntime.isStrWhiteSpaceChar(c)) break;
             start++;
         } while (start < len);
 
-        if (c == '+' || (negative = (c == '-')))
-            start++;
+        if (c == '+' || (negative = (c == '-'))) start++;
 
         final int NO_RADIX = -1;
         if (radix == 0) {
@@ -230,26 +233,28 @@
         } else if (radix < 2 || radix > 36) {
             return ScriptRuntime.NaNobj;
         } else if (radix == 16 && len - start > 1 && s.charAt(start) == '0') {
-            c = s.charAt(start+1);
-            if (c == 'x' || c == 'X')
-                start += 2;
+            c = s.charAt(start + 1);
+            if (c == 'x' || c == 'X') start += 2;
         }
 
         if (radix == NO_RADIX) {
             radix = 10;
             if (len - start > 1 && s.charAt(start) == '0') {
-                c = s.charAt(start+1);
+                c = s.charAt(start + 1);
                 if (c == 'x' || c == 'X') {
                     radix = 16;
                     start += 2;
                 } else if ('0' <= c && c <= '9') {
-                    radix = 8;
-                    start++;
+                    Context cx = Context.getCurrentContext();
+                    if (cx == null || cx.getLanguageVersion() < Context.VERSION_1_5) {
+                        radix = 8;
+                        start++;
+                    }
                 }
             }
         }
 
-        double d = ScriptRuntime.stringToNumber(s, start, radix);
+        double d = ScriptRuntime.stringPrefixToNumber(s, start, radix);
         return ScriptRuntime.wrapNumber(negative ? -d : d);
     }
 
@@ -258,17 +263,15 @@
      *
      * @param args the arguments to parseFloat, ignoring args[>=1]
      */
-    static Object js_parseFloat(Object[] args)
-    {
-        if (args.length < 1)
-            return ScriptRuntime.NaNobj;
+    static Object js_parseFloat(Object[] args) {
+        if (args.length < 1) return ScriptRuntime.NaNobj;
 
         String s = ScriptRuntime.toString(args[0]);
         int len = s.length();
         int start = 0;
         // Scan forward to skip whitespace
         char c;
-        for (;;) {
+        for (; ; ) {
             if (start == len) {
                 return ScriptRuntime.NaNobj;
             }
@@ -290,7 +293,7 @@
 
         if (c == 'I') {
             // check for "Infinity"
-            if (i+8 <= len && s.regionMatches(i, "Infinity", 0, 8)) {
+            if (i + 8 <= len && s.regionMatches(i, "Infinity", 0, 8)) {
                 double d;
                 if (s.charAt(start) == '-') {
                     d = Double.NEGATIVE_INFINITY;
@@ -308,42 +311,50 @@
         boolean exponentValid = false;
         for (; i < len; i++) {
             switch (s.charAt(i)) {
-              case '.':
-                if (decimal != -1) // Only allow a single decimal point.
+                case '.':
+                    if (decimal != -1) // Only allow a single decimal point.
                     break;
-                decimal = i;
-                continue;
+                    decimal = i;
+                    continue;
 
-              case 'e':
-              case 'E':
-                if (exponent != -1) {
-                    break;
-                } else if (i == len - 1) {
-                    break;
-                }
-                exponent = i;
-                continue;
+                case 'e':
+                case 'E':
+                    if (exponent != -1) {
+                        break;
+                    } else if (i == len - 1) {
+                        break;
+                    }
+                    exponent = i;
+                    continue;
+
+                case '+':
+                case '-':
+                    // Only allow '+' or '-' after 'e' or 'E'
+                    if (exponent != i - 1) {
+                        break;
+                    } else if (i == len - 1) {
+                        --i;
+                        break;
+                    }
+                    continue;
+
+                case '0':
+                case '1':
+                case '2':
+                case '3':
+                case '4':
+                case '5':
+                case '6':
+                case '7':
+                case '8':
+                case '9':
+                    if (exponent != -1) {
+                        exponentValid = true;
+                    }
+                    continue;
 
-              case '+':
-              case '-':
-                 // Only allow '+' or '-' after 'e' or 'E'
-                if (exponent != i-1) {
-                    break;
-                } else if (i == len - 1) {
-                    --i;
+                default:
                     break;
-                }
-                continue;
-
-              case '0': case '1': case '2': case '3': case '4':
-              case '5': case '6': case '7': case '8': case '9':
-                if (exponent != -1) {
-                    exponentValid = true;
-                }
-                continue;
-
-              default:
-                break;
             }
             break;
         }
@@ -353,35 +364,29 @@
         s = s.substring(start, i);
         try {
             return Double.valueOf(s);
-        }
-        catch (NumberFormatException ex) {
+        } catch (NumberFormatException ex) {
             return ScriptRuntime.NaNobj;
         }
     }
 
     /**
      * The global method escape, as per ECMA-262 15.1.2.4.
-
-     * Includes code for the 'mask' argument supported by the C escape
-     * method, which used to be part of the browser imbedding.  Blame
-     * for the strange constant names should be directed there.
+     *
+     * <p>Includes code for the 'mask' argument supported by the C escape method, which used to be
+     * part of the browser embedding. Blame for the strange constant names should be directed there.
      */
-
-    private Object js_escape(Object[] args) {
-        final int
-            URL_XALPHAS = 1,
-            URL_XPALPHAS = 2,
-            URL_PATH = 4;
+    private static Object js_escape(Object[] args) {
+        final int URL_XALPHAS = 1, URL_XPALPHAS = 2, URL_PATH = 4;
 
         String s = ScriptRuntime.toString(args, 0);
 
         int mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;
         if (args.length > 1) { // the 'mask' argument.  Non-ECMA.
             double d = ScriptRuntime.toNumber(args[1]);
-            if (d != d || ((mask = (int) d) != d) ||
-                0 != (mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH)))
-            {
-                throw Context.reportRuntimeError0("msg.bad.esc.mask");
+            if (Double.isNaN(d)
+                    || ((mask = (int) d) != d)
+                    || 0 != (mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH))) {
+                throw Context.reportRuntimeErrorById("msg.bad.esc.mask");
             }
         }
 
@@ -389,13 +394,17 @@
         for (int k = 0, L = s.length(); k != L; ++k) {
             int c = s.charAt(k);
             if (mask != 0
-                && ((c >= '0' && c <= '9')
-                    || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
-                    || c == '@' || c == '*' || c == '_' || c == '-' || c == '.'
-                    || (0 != (mask & URL_PATH) && (c == '/' || c == '+'))))
-            {
+                    && ((c >= '0' && c <= '9')
+                            || (c >= 'A' && c <= 'Z')
+                            || (c >= 'a' && c <= 'z')
+                            || c == '@'
+                            || c == '*'
+                            || c == '_'
+                            || c == '-'
+                            || c == '.'
+                            || (0 != (mask & URL_PATH) && (c == '/' || c == '+')))) {
                 if (sb != null) {
-                    sb.append((char)c);
+                    sb.append((char) c);
                 }
             } else {
                 if (sb == null) {
@@ -422,7 +431,7 @@
                 for (int shift = (hexSize - 1) * 4; shift >= 0; shift -= 4) {
                     int digit = 0xf & (c >> shift);
                     int hc = (digit < 10) ? '0' + digit : 'A' - 10 + digit;
-                    sb.append((char)hc);
+                    sb.append((char) hc);
                 }
             }
         }
@@ -430,19 +439,15 @@
         return (sb == null) ? s : sb.toString();
     }
 
-    /**
-     * The global unescape method, as per ECMA-262 15.1.2.5.
-     */
-
-    private Object js_unescape(Object[] args)
-    {
+    /** The global unescape method, as per ECMA-262 15.1.2.5. */
+    private static Object js_unescape(Object[] args) {
         String s = ScriptRuntime.toString(args, 0);
         int firstEscapePos = s.indexOf('%');
         if (firstEscapePos >= 0) {
             int L = s.length();
             char[] buf = s.toCharArray();
             int destination = firstEscapePos;
-            for (int k = firstEscapePos; k != L;) {
+            for (int k = firstEscapePos; k != L; ) {
                 char c = buf[k];
                 ++k;
                 if (c == '%' && k != L) {
@@ -460,7 +465,7 @@
                             x = Kit.xDigitToInt(buf[i], x);
                         }
                         if (x >= 0) {
-                            c = (char)x;
+                            c = (char) x;
                             k = end;
                         }
                     }
@@ -474,19 +479,17 @@
     }
 
     /**
-     * This is an indirect call to eval, and thus uses the global environment.
-     * Direct calls are executed via ScriptRuntime.callSpecial().
+     * This is an indirect call to eval, and thus uses the global environment. Direct calls are
+     * executed via ScriptRuntime.callSpecial().
      */
-    private Object js_eval(Context cx, Scriptable scope, Object[] args)
-    {
+    private static Object js_eval(Context cx, Scriptable scope, Object[] args) {
         Scriptable global = ScriptableObject.getTopLevelScope(scope);
         return ScriptRuntime.evalSpecial(cx, global, global, args, "eval code", 1);
     }
 
-    static boolean isEvalFunction(Object functionObj)
-    {
+    static boolean isEvalFunction(Object functionObj) {
         if (functionObj instanceof IdFunctionObject) {
-            IdFunctionObject function = (IdFunctionObject)functionObj;
+            IdFunctionObject function = (IdFunctionObject) functionObj;
             if (function.hasTag(FTAG) && function.methodId() == Id_eval) {
                 return true;
             }
@@ -494,46 +497,40 @@
         return false;
     }
 
-    /**
-     * @deprecated Use {@link ScriptRuntime#constructError(String,String)}
-     * instead.
-     */
+    /** @deprecated Use {@link ScriptRuntime#constructError(String,String)} instead. */
     @Deprecated
-    public static EcmaError constructError(Context cx,
-                                           String error,
-                                           String message,
-                                           Scriptable scope)
-    {
+    public static EcmaError constructError(
+            Context cx, String error, String message, Scriptable scope) {
         return ScriptRuntime.constructError(error, message);
     }
 
     /**
-     * @deprecated Use
-     * {@link ScriptRuntime#constructError(String,String,String,int,String,int)}
-     * instead.
+     * @deprecated Use {@link ScriptRuntime#constructError(String,String,String,int,String,int)}
+     *     instead.
      */
     @Deprecated
-    public static EcmaError constructError(Context cx,
-                                           String error,
-                                           String message,
-                                           Scriptable scope,
-                                           String sourceName,
-                                           int lineNumber,
-                                           int columnNumber,
-                                           String lineSource)
-    {
-        return ScriptRuntime.constructError(error, message,
-                                            sourceName, lineNumber,
-                                            lineSource, columnNumber);
+    public static EcmaError constructError(
+            Context cx,
+            String error,
+            String message,
+            Scriptable scope,
+            String sourceName,
+            int lineNumber,
+            int columnNumber,
+            String lineSource) {
+        return ScriptRuntime.constructError(
+                error, message,
+                sourceName, lineNumber,
+                lineSource, columnNumber);
     }
 
     /*
-    *   ECMA 3, 15.1.3 URI Handling Function Properties
-    *
-    *   The following are implementations of the algorithms
-    *   given in the ECMA specification for the hidden functions
-    *   'Encode' and 'Decode'.
-    */
+     *   ECMA 3, 15.1.3 URI Handling Function Properties
+     *
+     *   The following are implementations of the algorithms
+     *   given in the ECMA specification for the hidden functions
+     *   'Encode' and 'Decode'.
+     */
     private static String encode(String str, boolean fullUri) {
         byte[] utf8buf = null;
         StringBuilder sb = null;
@@ -582,7 +579,7 @@
 
     private static char toHexChar(int i) {
         if (i >> 4 != 0) Kit.codeBug();
-        return (char)((i < 10) ? i + '0' : i - 10 + 'A');
+        return (char) ((i < 10) ? i + '0' : i - 10 + 'A');
     }
 
     private static int unHex(char c) {
@@ -610,7 +607,7 @@
         char[] buf = null;
         int bufTop = 0;
 
-        for (int k = 0, length = str.length(); k != length;) {
+        for (int k = 0, length = str.length(); k != length; ) {
             char C = str.charAt(k);
             if (C != '%') {
                 if (buf != null) {
@@ -626,13 +623,12 @@
                     bufTop = k;
                 }
                 int start = k;
-                if (k + 3 > length)
-                    throw uriError();
+                if (k + 3 > length) throw uriError();
                 int B = unHex(str.charAt(k + 1), str.charAt(k + 2));
                 if (B < 0) throw uriError();
                 k += 3;
                 if ((B & 0x80) == 0) {
-                    C = (char)B;
+                    C = (char) B;
                 } else {
                     // Decode UTF-8 sequence into ucs4Char and encode it into
                     // UTF-16
@@ -641,38 +637,39 @@
                         // First  UTF-8 should be ouside 0x80..0xBF
                         throw uriError();
                     } else if ((B & 0x20) == 0) {
-                        utf8Tail = 1; ucs4Char = B & 0x1F;
+                        utf8Tail = 1;
+                        ucs4Char = B & 0x1F;
                         minUcs4Char = 0x80;
                     } else if ((B & 0x10) == 0) {
-                        utf8Tail = 2; ucs4Char = B & 0x0F;
+                        utf8Tail = 2;
+                        ucs4Char = B & 0x0F;
                         minUcs4Char = 0x800;
                     } else if ((B & 0x08) == 0) {
-                        utf8Tail = 3; ucs4Char = B & 0x07;
+                        utf8Tail = 3;
+                        ucs4Char = B & 0x07;
                         minUcs4Char = 0x10000;
                     } else if ((B & 0x04) == 0) {
-                        utf8Tail = 4; ucs4Char = B & 0x03;
+                        utf8Tail = 4;
+                        ucs4Char = B & 0x03;
                         minUcs4Char = 0x200000;
                     } else if ((B & 0x02) == 0) {
-                        utf8Tail = 5; ucs4Char = B & 0x01;
+                        utf8Tail = 5;
+                        ucs4Char = B & 0x01;
                         minUcs4Char = 0x4000000;
                     } else {
                         // First UTF-8 can not be 0xFF or 0xFE
                         throw uriError();
                     }
-                    if (k + 3 * utf8Tail > length)
-                        throw uriError();
+                    if (k + 3 * utf8Tail > length) throw uriError();
                     for (int j = 0; j != utf8Tail; j++) {
-                        if (str.charAt(k) != '%')
-                            throw uriError();
+                        if (str.charAt(k) != '%') throw uriError();
                         B = unHex(str.charAt(k + 1), str.charAt(k + 2));
-                        if (B < 0 || (B & 0xC0) != 0x80)
-                            throw uriError();
+                        if (B < 0 || (B & 0xC0) != 0x80) throw uriError();
                         ucs4Char = (ucs4Char << 6) | (B & 0x3F);
                         k += 3;
                     }
                     // Check for overlongs and other should-not-present codes
-                    if (ucs4Char < minUcs4Char
-                            || (ucs4Char >= 0xD800 && ucs4Char <= 0xDFFF)) {
+                    if (ucs4Char < minUcs4Char || (ucs4Char >= 0xD800 && ucs4Char <= 0xDFFF)) {
                         ucs4Char = INVALID_UTF8;
                     } else if (ucs4Char == 0xFFFE || ucs4Char == 0xFFFF) {
                         ucs4Char = 0xFFFD;
@@ -682,11 +679,11 @@
                         if (ucs4Char > 0xFFFFF) {
                             throw uriError();
                         }
-                        char H = (char)((ucs4Char >>> 10) + 0xD800);
-                        C = (char)((ucs4Char & 0x3FF) + 0xDC00);
+                        char H = (char) ((ucs4Char >>> 10) + 0xD800);
+                        C = (char) ((ucs4Char & 0x3FF) + 0xDC00);
                         buf[bufTop++] = H;
                     } else {
-                        C = (char)ucs4Char;
+                        C = (char) ucs4Char;
                     }
                 }
                 if (fullUri && URI_DECODE_RESERVED.indexOf(C) >= 0) {
@@ -702,8 +699,7 @@
     }
 
     private static boolean encodeUnescaped(char c, boolean fullUri) {
-        if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')
-                || ('0' <= c && c <= '9')) {
+        if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || ('0' <= c && c <= '9')) {
             return true;
         }
         if ("-_.!~*'()".indexOf(c) >= 0) {
@@ -716,22 +712,21 @@
     }
 
     private static EcmaError uriError() {
-        return ScriptRuntime.constructError("URIError",
-                ScriptRuntime.getMessage0("msg.bad.uri"));
+        return ScriptRuntime.constructError(
+                "URIError", ScriptRuntime.getMessageById("msg.bad.uri"));
     }
 
     private static final String URI_DECODE_RESERVED = ";/?:@&=+$,#";
     private static final int INVALID_UTF8 = Integer.MAX_VALUE;
 
     /* Convert one UCS-4 char and write it into a UTF-8 buffer, which must be
-    * at least 6 bytes long.  Return the number of UTF-8 bytes of data written.
-    */
+     * at least 6 bytes long.  Return the number of UTF-8 bytes of data written.
+     */
     private static int oneUcs4ToUtf8Char(byte[] utf8Buffer, int ucs4Char) {
         int utf8Length = 1;
 
-        //JS_ASSERT(ucs4Char <= 0x7FFFFFFF);
-        if ((ucs4Char & ~0x7F) == 0)
-            utf8Buffer[0] = (byte)ucs4Char;
+        // JS_ASSERT(ucs4Char <= 0x7FFFFFFF);
+        if ((ucs4Char & ~0x7F) == 0) utf8Buffer[0] = (byte) ucs4Char;
         else {
             int i;
             int a = ucs4Char >>> 11;
@@ -742,32 +737,29 @@
             }
             i = utf8Length;
             while (--i > 0) {
-                utf8Buffer[i] = (byte)((ucs4Char & 0x3F) | 0x80);
+                utf8Buffer[i] = (byte) ((ucs4Char & 0x3F) | 0x80);
                 ucs4Char >>>= 6;
             }
-            utf8Buffer[0] = (byte)(0x100 - (1 << (8-utf8Length)) + ucs4Char);
+            utf8Buffer[0] = (byte) (0x100 - (1 << (8 - utf8Length)) + ucs4Char);
         }
         return utf8Length;
     }
 
     private static final Object FTAG = "Global";
 
-    private static final int
-        Id_decodeURI           =  1,
-        Id_decodeURIComponent  =  2,
-        Id_encodeURI           =  3,
-        Id_encodeURIComponent  =  4,
-        Id_escape              =  5,
-        Id_eval                =  6,
-        Id_isFinite            =  7,
-        Id_isNaN               =  8,
-        Id_isXMLName           =  9,
-        Id_parseFloat          = 10,
-        Id_parseInt            = 11,
-        Id_unescape            = 12,
-        Id_uneval              = 13,
-
-        LAST_SCOPE_FUNCTION_ID = 13,
-
-        Id_new_CommonError     = 14;
+    private static final int Id_decodeURI = 1,
+            Id_decodeURIComponent = 2,
+            Id_encodeURI = 3,
+            Id_encodeURIComponent = 4,
+            Id_escape = 5,
+            Id_eval = 6,
+            Id_isFinite = 7,
+            Id_isNaN = 8,
+            Id_isXMLName = 9,
+            Id_parseFloat = 10,
+            Id_parseInt = 11,
+            Id_unescape = 12,
+            Id_uneval = 13,
+            LAST_SCOPE_FUNCTION_ID = 13,
+            Id_new_CommonError = 14;
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeIterator.java rhino-1.7.14/src/org/mozilla/javascript/NativeIterator.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeIterator.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeIterator.java	2022-01-06 22:57:21.000000000 +0100
@@ -18,42 +18,44 @@
     private static final long serialVersionUID = -4136968203581667681L;
     private static final Object ITERATOR_TAG = "Iterator";
 
-    static void init(ScriptableObject scope, boolean sealed) {
+    static void init(Context cx, ScriptableObject scope, boolean sealed) {
         // Iterator
         NativeIterator iterator = new NativeIterator();
         iterator.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
 
         // Generator
-        NativeGenerator.init(scope, sealed);
+        if (cx.getLanguageVersion() >= Context.VERSION_ES6) {
+            ES6Generator.init(scope, sealed);
+        } else {
+            NativeGenerator.init(scope, sealed);
+        }
 
         // StopIteration
         NativeObject obj = new StopIteration();
         obj.setPrototype(getObjectPrototype(scope));
         obj.setParentScope(scope);
-        if (sealed) { obj.sealObject(); }
-        ScriptableObject.defineProperty(scope, STOP_ITERATION, obj,
-                                        ScriptableObject.DONTENUM);
+        if (sealed) {
+            obj.sealObject();
+        }
+        ScriptableObject.defineProperty(scope, STOP_ITERATION, obj, ScriptableObject.DONTENUM);
         // Use "associateValue" so that generators can continue to
         // throw StopIteration even if the property of the global
         // scope is replaced or deleted.
         scope.associateValue(ITERATOR_TAG, obj);
     }
 
-    /**
-     * Only for constructing the prototype object.
-     */
-    private NativeIterator() {
-    }
+    /** Only for constructing the prototype object. */
+    private NativeIterator() {}
 
     private NativeIterator(Object objectIterator) {
-      this.objectIterator = objectIterator;
+        this.objectIterator = objectIterator;
     }
 
     /**
-     * Get the value of the "StopIteration" object. Note that this value
-     * is stored in the top-level scope using "associateValue" so the
-     * value can still be found even if a script overwrites or deletes
-     * the global "StopIteration" property.
+     * Get the value of the "StopIteration" object. Note that this value is stored in the top-level
+     * scope using "associateValue" so the value can still be found even if a script overwrites or
+     * deletes the global "StopIteration" property.
+     *
      * @param scope a scope whose parent chain reaches a top-level scope
      * @return the StopIteration object
      */
@@ -65,9 +67,21 @@
     private static final String STOP_ITERATION = "StopIteration";
     public static final String ITERATOR_PROPERTY_NAME = "__iterator__";
 
-    static class StopIteration extends NativeObject {
+    public static class StopIteration extends NativeObject {
         private static final long serialVersionUID = 2485151085722377663L;
 
+        private Object value = Undefined.instance;
+
+        public StopIteration() {}
+
+        public StopIteration(Object val) {
+            this.value = val;
+        }
+
+        public Object getValue() {
+            return value;
+        }
+
         @Override
         public String getClassName() {
             return STOP_ITERATION;
@@ -92,18 +106,27 @@
         String s;
         int arity;
         switch (id) {
-          case Id_constructor:    arity=2; s="constructor";          break;
-          case Id_next:           arity=0; s="next";                 break;
-          case Id___iterator__:   arity=1; s=ITERATOR_PROPERTY_NAME; break;
-          default: throw new IllegalArgumentException(String.valueOf(id));
+            case Id_constructor:
+                arity = 2;
+                s = "constructor";
+                break;
+            case Id_next:
+                arity = 0;
+                s = "next";
+                break;
+            case Id___iterator__:
+                arity = 1;
+                s = ITERATOR_PROPERTY_NAME;
+                break;
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
         }
         initPrototypeMethod(ITERATOR_TAG, id, s, arity);
     }
 
     @Override
-    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
-                             Scriptable thisObj, Object[] args)
-    {
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         if (!f.hasTag(ITERATOR_TAG)) {
             return super.execIdCall(f, cx, scope, thisObj, args);
         }
@@ -113,35 +136,28 @@
             return jsConstructor(cx, scope, thisObj, args);
         }
 
-        if (!(thisObj instanceof NativeIterator))
-            throw incompatibleCallError(f);
-
-        NativeIterator iterator = (NativeIterator) thisObj;
+        NativeIterator iterator = ensureType(thisObj, NativeIterator.class, f);
 
         switch (id) {
+            case Id_next:
+                return iterator.next(cx, scope);
 
-          case Id_next:
-            return iterator.next(cx, scope);
-
-          case Id___iterator__:
-            /// XXX: what about argument? SpiderMonkey apparently ignores it
-            return thisObj;
+            case Id___iterator__:
+                /// XXX: what about argument? SpiderMonkey apparently ignores it
+                return thisObj;
 
-          default:
-            throw new IllegalArgumentException(String.valueOf(id));
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
         }
     }
 
     /* The JavaScript constructor */
-    private static Object jsConstructor(Context cx, Scriptable scope,
-                                        Scriptable thisObj, Object[] args)
-    {
-        if (args.length == 0 || args[0] == null ||
-            args[0] == Undefined.instance)
-        {
+    private static Object jsConstructor(
+            Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        if (args.length == 0 || args[0] == null || args[0] == Undefined.instance) {
             Object argument = args.length == 0 ? Undefined.instance : args[0];
-            throw ScriptRuntime.typeError1("msg.no.properties",
-                                           ScriptRuntime.toString(argument));
+            throw ScriptRuntime.typeErrorById(
+                    "msg.no.properties", ScriptRuntime.toString(argument));
         }
         Scriptable obj = ScriptRuntime.toObject(cx, scope, args[0]);
         boolean keyOnly = args.length > 1 && ScriptRuntime.toBoolean(args[1]);
@@ -151,18 +167,19 @@
             // For objects that implement java.lang.Iterable or
             // java.util.Iterator, have JavaScript Iterator call the underlying
             // iteration methods
-            Iterator<?> iterator =
-                VMBridge.instance.getJavaIterator(cx, scope, obj);
+            Iterator<?> iterator = getJavaIterator(obj);
             if (iterator != null) {
                 scope = ScriptableObject.getTopLevelScope(scope);
-                return cx.getWrapFactory().wrap(cx, scope,
-                        new WrappedJavaIterator(iterator, scope),
-                        WrappedJavaIterator.class);
+                return cx.getWrapFactory()
+                        .wrap(
+                                cx,
+                                scope,
+                                new WrappedJavaIterator(iterator, scope),
+                                WrappedJavaIterator.class);
             }
 
             // Otherwise, just call the runtime routine
-            Scriptable jsIterator = ScriptRuntime.toIterator(cx, scope, obj,
-                                                             keyOnly);
+            Scriptable jsIterator = ScriptRuntime.toIterator(cx, scope, obj, keyOnly);
             if (jsIterator != null) {
                 return jsIterator;
             }
@@ -170,13 +187,17 @@
 
         // Otherwise, just set up to iterate over the properties of the object.
         // Do not call __iterator__ method.
-        Object objectIterator = ScriptRuntime.enumInit(obj, cx, scope,
-            keyOnly ? ScriptRuntime.ENUMERATE_KEYS_NO_ITERATOR
-                    : ScriptRuntime.ENUMERATE_ARRAY_NO_ITERATOR);
+        Object objectIterator =
+                ScriptRuntime.enumInit(
+                        obj,
+                        cx,
+                        scope,
+                        keyOnly
+                                ? ScriptRuntime.ENUMERATE_KEYS_NO_ITERATOR
+                                : ScriptRuntime.ENUMERATE_ARRAY_NO_ITERATOR);
         ScriptRuntime.setEnumNumbers(objectIterator, true);
         NativeIterator result = new NativeIterator(objectIterator);
-        result.setPrototype(ScriptableObject.getClassPrototype(scope,
-                                result.getClassName()));
+        result.setPrototype(ScriptableObject.getClassPrototype(scope, result.getClassName()));
         result.setParentScope(scope);
         return result;
     }
@@ -185,14 +206,28 @@
         Boolean b = ScriptRuntime.enumNext(this.objectIterator);
         if (!b.booleanValue()) {
             // Out of values. Throw StopIteration.
-            throw new JavaScriptException(
-                NativeIterator.getStopIterationObject(scope), null, 0);
+            throw new JavaScriptException(NativeIterator.getStopIterationObject(scope), null, 0);
         }
         return ScriptRuntime.enumId(this.objectIterator, cx);
     }
 
-    static public class WrappedJavaIterator
-    {
+    /**
+     * If "obj" is a java.util.Iterator or a java.lang.Iterable, return a wrapping as a JavaScript
+     * Iterator. Otherwise, return null. This method is in VMBridge since Iterable is a JDK 1.5
+     * addition.
+     */
+    private static Iterator<?> getJavaIterator(Object obj) {
+        if (obj instanceof Wrapper) {
+            Object unwrapped = ((Wrapper) obj).unwrap();
+            Iterator<?> iterator = null;
+            if (unwrapped instanceof Iterator) iterator = (Iterator<?>) unwrapped;
+            if (unwrapped instanceof Iterable) iterator = ((Iterable<?>) unwrapped).iterator();
+            return iterator;
+        }
+        return null;
+    }
+
+    public static class WrappedJavaIterator {
         WrappedJavaIterator(Iterator<?> iterator, Scriptable scope) {
             this.iterator = iterator;
             this.scope = scope;
@@ -202,7 +237,7 @@
             if (!iterator.hasNext()) {
                 // Out of values. Throw StopIteration.
                 throw new JavaScriptException(
-                    NativeIterator.getStopIterationObject(scope), null, 0);
+                        NativeIterator.getStopIterationObject(scope), null, 0);
             }
             return iterator.next();
         }
@@ -215,32 +250,30 @@
         private Scriptable scope;
     }
 
-// #string_id_map#
-
     @Override
     protected int findPrototypeId(String s) {
         int id;
-// #generated# Last update: 2007-06-11 09:43:19 EDT
-        L0: { id = 0; String X = null;
-            int s_length = s.length();
-            if (s_length==4) { X="next";id=Id_next; }
-            else if (s_length==11) { X="constructor";id=Id_constructor; }
-            else if (s_length==12) { X="__iterator__";id=Id___iterator__; }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-            break L0;
+        switch (s) {
+            case "constructor":
+                id = Id_constructor;
+                break;
+            case "next":
+                id = Id_next;
+                break;
+            case "__iterator__":
+                id = Id___iterator__;
+                break;
+            default:
+                id = 0;
+                break;
         }
-// #/generated#
         return id;
     }
 
-    private static final int
-        Id_constructor           = 1,
-        Id_next                  = 2,
-        Id___iterator__          = 3,
-        MAX_PROTOTYPE_ID         = 3;
-
-// #/string_id_map#
+    private static final int Id_constructor = 1,
+            Id_next = 2,
+            Id___iterator__ = 3,
+            MAX_PROTOTYPE_ID = 3;
 
     private Object objectIterator;
 }
-
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeJavaArray.java rhino-1.7.14/src/org/mozilla/javascript/NativeJavaArray.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeJavaArray.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeJavaArray.java	2022-01-06 22:57:21.000000000 +0100
@@ -17,9 +17,11 @@
  * @see NativeJavaPackage
  */
 
-public class NativeJavaArray extends NativeJavaObject
+public class NativeJavaArray
+    extends NativeJavaObject
+    implements SymbolScriptable
 {
-    static final long serialVersionUID = -924022554283675333L;
+    private static final long serialVersionUID = -924022554283675333L;
 
     @Override
     public String getClassName() {
@@ -57,6 +59,11 @@
     }
 
     @Override
+    public boolean has(Symbol key, Scriptable start) {
+        return SymbolKey.IS_CONCAT_SPREADABLE.equals(key);
+    }
+
+    @Override
     public Object get(String id, Scriptable start) {
         if (id.equals("length"))
             return Integer.valueOf(length);
@@ -64,7 +71,7 @@
         if (result == NOT_FOUND &&
             !ScriptableObject.hasProperty(getPrototype(), id))
         {
-            throw Context.reportRuntimeError2(
+            throw Context.reportRuntimeErrorById(
                 "msg.java.member.not.found", array.getClass().getName(), id);
         }
         return result;
@@ -81,10 +88,18 @@
     }
 
     @Override
+    public Object get(Symbol key, Scriptable start) {
+        if (SymbolKey.IS_CONCAT_SPREADABLE.equals(key)) {
+            return Boolean.TRUE;
+        }
+        return Scriptable.NOT_FOUND;
+    }
+
+    @Override
     public void put(String id, Scriptable start, Object value) {
         // Ignore assignments to "length"--it's readonly.
         if (!id.equals("length"))
-            throw Context.reportRuntimeError1(
+            throw Context.reportRuntimeErrorById(
                 "msg.java.array.member.not.found", id);
     }
 
@@ -94,13 +109,18 @@
             Array.set(array, index, Context.jsToJava(value, cls));
         }
         else {
-            throw Context.reportRuntimeError2(
+            throw Context.reportRuntimeErrorById(
                 "msg.java.array.index.out.of.bounds", String.valueOf(index),
                 String.valueOf(length - 1));
         }
     }
 
     @Override
+    public void delete(Symbol key) {
+        // All symbols are read-only
+    }
+
+    @Override
     public Object getDefaultValue(Class<?> hint) {
         if (hint == null || hint == ScriptRuntime.StringClass)
             return array.toString();
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeJavaClass.java rhino-1.7.14/src/org/mozilla/javascript/NativeJavaClass.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeJavaClass.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeJavaClass.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,7 +6,8 @@
 
 package org.mozilla.javascript;
 
-import java.lang.reflect.*;
+import java.lang.reflect.Array;
+import java.lang.reflect.Modifier;
 import java.util.Map;
 
 /**
@@ -27,7 +28,7 @@
 
 public class NativeJavaClass extends NativeJavaObject implements Function
 {
-    static final long serialVersionUID = -6460763940409461664L;
+    private static final long serialVersionUID = -6460763940409461664L;
 
     // Special property for getting the underlying Java class object.
     static final String javaClassPropertyName = "__javaObject__";
@@ -126,6 +127,7 @@
         return this;
     }
 
+    @Override
     public Object call(Context cx, Scriptable scope, Scriptable thisObj,
                        Object[] args)
     {
@@ -147,6 +149,7 @@
         return construct(cx, scope, args);
     }
 
+    @Override
     public Scriptable construct(Context cx, Scriptable scope, Object[] args)
     {
         Class<?> classObject = getClassObject();
@@ -158,45 +161,44 @@
             int index = ctors.findCachedFunction(cx, args);
             if (index < 0) {
                 String sig = NativeJavaMethod.scriptSignature(args);
-                throw Context.reportRuntimeError2(
+                throw Context.reportRuntimeErrorById(
                     "msg.no.java.ctor", classObject.getName(), sig);
             }
 
             // Found the constructor, so try invoking it.
             return constructSpecific(cx, scope, args, ctors.methods[index]);
-        } else {
-            if (args.length == 0) {
-                throw Context.reportRuntimeError0("msg.adapter.zero.args");
+        }
+        if (args.length == 0) {
+            throw Context.reportRuntimeErrorById("msg.adapter.zero.args");
+        }
+        Scriptable topLevel = ScriptableObject.getTopLevelScope(this);
+        String msg = "";
+        try {
+            // When running on Android create an InterfaceAdapter since our
+            // bytecode generation won't work on Dalvik VM.
+            if ("Dalvik".equals(System.getProperty("java.vm.name"))
+                    && classObject.isInterface()) {
+                Object obj = createInterfaceAdapter(classObject,
+                        ScriptableObject.ensureScriptableObject(args[0]));
+                return cx.getWrapFactory().wrapAsJavaObject(cx, scope, obj, null);
             }
-            Scriptable topLevel = ScriptableObject.getTopLevelScope(this);
-            String msg = "";
-            try {
-                // When running on Android create an InterfaceAdapter since our
-                // bytecode generation won't work on Dalvik VM.
-                if ("Dalvik".equals(System.getProperty("java.vm.name"))
-                        && classObject.isInterface()) {
-                    Object obj = createInterfaceAdapter(classObject,
-                            ScriptableObject.ensureScriptableObject(args[0]));
-                    return cx.getWrapFactory().wrapAsJavaObject(cx, scope, obj, null);
-                }
-                // use JavaAdapter to construct a new class on the fly that
-                // implements/extends this interface/abstract class.
-                Object v = topLevel.get("JavaAdapter", topLevel);
-                if (v != NOT_FOUND) {
-                    Function f = (Function) v;
-                    // Args are (interface, js object)
-                    Object[] adapterArgs = { this, args[0] };
-                    return f.construct(cx, topLevel, adapterArgs);
-                }
-            } catch (Exception ex) {
-                // fall through to error
-                String m = ex.getMessage();
-                if (m != null)
-                    msg = m;
+            // use JavaAdapter to construct a new class on the fly that
+            // implements/extends this interface/abstract class.
+            Object v = topLevel.get("JavaAdapter", topLevel);
+            if (v != NOT_FOUND) {
+                Function f = (Function) v;
+                // Args are (interface, js object)
+                Object[] adapterArgs = { this, args[0] };
+                return f.construct(cx, topLevel, adapterArgs);
             }
-            throw Context.reportRuntimeError2(
-                "msg.cant.instantiate", msg, classObject.getName());
+        } catch (Exception ex) {
+            // fall through to error
+            String m = ex.getMessage();
+            if (m != null)
+                msg = m;
         }
+        throw Context.reportRuntimeErrorById(
+            "msg.cant.instantiate", msg, classObject.getName());
     }
 
     static Scriptable constructSpecific(Context cx, Scriptable scope,
@@ -302,9 +304,8 @@
             // loader that brought Rhino classes that Class.forName() would
             // use, but ClassLoader.getSystemClassLoader() is Java 2 only
             return Kit.classOrNull(nestedClassName);
-        } else {
-            return Kit.classOrNull(loader, nestedClassName);
         }
+        return Kit.classOrNull(loader, nestedClassName);
     }
 
     private Map<String,FieldAndMethods> staticFieldAndMethods;
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeJavaConstructor.java rhino-1.7.14/src/org/mozilla/javascript/NativeJavaConstructor.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeJavaConstructor.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeJavaConstructor.java	2022-01-06 22:57:21.000000000 +0100
@@ -22,7 +22,7 @@
 
 public class NativeJavaConstructor extends BaseFunction
 {
-    static final long serialVersionUID = -8149253217482668463L;
+    private static final long serialVersionUID = -8149253217482668463L;
 
     MemberBox ctor;
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeJavaList.java rhino-1.7.14/src/org/mozilla/javascript/NativeJavaList.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeJavaList.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeJavaList.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,186 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+package org.mozilla.javascript;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <code>NativeJavaList</code> is a wrapper for java objects implementing <code>java.util.List
+ * </code> interface. This wrapper delegates index based access in javascript (like <code>
+ * value[x] = 3</code>) to the according {@link List#get(int)}, {@link List#set(int, Object)} and
+ * {@link List#add(Object)} methods. This allows you to use java lists in many places like a
+ * javascript <code>Array</code>.
+ *
+ * <p>Supported functions:
+ *
+ * <ul>
+ *   <li>index based access is delegated to List.get/set/add. If <code>index >= length</code>,
+ *       the skipped elements will be filled with <code>null</code> values
+ *   <li>iterator support with <code>for...of</code> (provided by NativeJavaObject for all
+ *       iterables)
+ *   <li>when iterating with <code>for .. in</code> (or <code>for each .. in</code>) then <code>
+ *       getIds
+ *       </code> + index based access is used.
+ *   <li>reading and setting <code>length</code> property. When modifying the length property, the
+ *       list is either truncated or will be filled with <code>null</code> values up to <code>length
+ *       </code>
+ *   <li>deleting entries: <code>delete value[index]</code> will be equivalent with <code>
+ *       value[index] = null</code> and is implemented to provide array compatibility.
+ * </ul>
+ *
+ * <b>Important:</b> JavaList does not support sparse arrays. So setting the length property to a
+ * high value or writing to a high index may allocate a lot of memory.
+ *
+ * <p><b>Note:</b> Although <code>JavaList</code> looks like a javascript-<code>Array</code>, it is
+ * not an <code>
+ * Array</code>. Some methods behave very similar like <code>Array.indexOf</code> and <code>
+ * java.util.List.indexOf</code>, others are named differently like <code>Array.includes</code> vs.
+ * <code>java.util.List.contains</code>. Especially <code>forEach</code> is different in <code>Array
+ * </code> and <code>java.util.List</code>. Also deleting entries will set entries to <code>null
+ * </code> instead to <code>Undefined</code>
+ */
+public class NativeJavaList extends NativeJavaObject {
+
+    private static final long serialVersionUID = 660285467829047519L;
+
+    private List<Object> list;
+
+    @SuppressWarnings("unchecked")
+    public NativeJavaList(Scriptable scope, Object list) {
+        super(scope, list, list.getClass());
+        assert list instanceof List;
+        this.list = (List<Object>) list;
+    }
+
+    @Override
+    public String getClassName() {
+        return "JavaList";
+    }
+
+    @Override
+    public boolean has(String name, Scriptable start) {
+        if (name.equals("length")) {
+            return true;
+        }
+        return super.has(name, start);
+    }
+
+    @Override
+    public boolean has(int index, Scriptable start) {
+        if (isWithValidIndex(index)) {
+            return true;
+        }
+        return super.has(index, start);
+    }
+
+    public void delete(int index) {
+        if (isWithValidIndex(index)) {
+            list.set(index, null);
+        }
+    }
+
+    @Override
+    public boolean has(Symbol key, Scriptable start) {
+        if (SymbolKey.IS_CONCAT_SPREADABLE.equals(key)) {
+            return true;
+        }
+        return super.has(key, start);
+    }
+
+    @Override
+    public Object get(String name, Scriptable start) {
+        if ("length".equals(name)) {
+            return Integer.valueOf(list.size());
+        }
+        return super.get(name, start);
+    }
+
+    @Override
+    public Object get(int index, Scriptable start) {
+        if (isWithValidIndex(index)) {
+            Context cx = Context.getCurrentContext();
+            Object obj = list.get(index);
+            if (cx != null) {
+                return cx.getWrapFactory().wrap(cx, this, obj, obj == null ? null : obj.getClass());
+            }
+            return obj;
+        }
+        return Undefined.instance;
+    }
+
+    @Override
+    public Object get(Symbol key, Scriptable start) {
+        if (SymbolKey.IS_CONCAT_SPREADABLE.equals(key)) {
+            return Boolean.TRUE;
+        }
+        return super.get(key, start);
+    }
+
+    @Override
+    public void put(int index, Scriptable start, Object value) {
+        if (index >= 0) {
+            Object javaValue = Context.jsToJava(value, Object.class);
+            if (index == list.size()) {
+                list.add(javaValue); // use "add" at the end of list.
+            } else {
+                ensureCapacity(index + 1);
+                list.set(index, javaValue);
+            }
+            return;
+        }
+        super.put(index, start, value);
+    }
+
+    @Override
+    public void put(String name, Scriptable start, Object value) {
+        if (list != null && "length".equals(name)) {
+            setLength(value);
+            return;
+        }
+        super.put(name, start, value);
+    }
+
+    private void ensureCapacity(int minCapacity) {
+        if (minCapacity > list.size()) {
+            if (list instanceof ArrayList) {
+                ((ArrayList<?>) list).ensureCapacity(minCapacity);
+            }
+            while (minCapacity > list.size()) {
+                list.add(null);
+            }
+        }
+    }
+
+    private void setLength(Object val) {
+        double d = ScriptRuntime.toNumber(val);
+        long longVal = ScriptRuntime.toUint32(d);
+        if (longVal != d || longVal > Integer.MAX_VALUE) {
+            String msg = ScriptRuntime.getMessageById("msg.arraylength.bad");
+            throw ScriptRuntime.rangeError(msg);
+        }
+        if (longVal < list.size()) {
+            list.subList((int) longVal, list.size()).clear();
+        } else {
+            ensureCapacity((int) longVal);
+        }
+    }
+
+    @Override
+    public Object[] getIds() {
+        List<?> list = (List<?>) javaObject;
+        Object[] result = new Object[list.size()];
+        int i = list.size();
+        while (--i >= 0) {
+            result[i] = Integer.valueOf(i);
+        }
+        return result;
+    }
+
+    private boolean isWithValidIndex(int index) {
+        return index >= 0 && index < list.size();
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeJavaMap.java rhino-1.7.14/src/org/mozilla/javascript/NativeJavaMap.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeJavaMap.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeJavaMap.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,197 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+package org.mozilla.javascript;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * <code>NativeJavaMap</code> is a wrapper for java objects implementing <code>java.util.Map
+ * </code> interface. When {@link Context#FEATURE_ENABLE_JAVA_MAP_ACCESS} is enabled, property based
+ * access like <code>map[key]</code> is delegated to {@link Map#get(Object)} or {@link
+ * Map#put(Object, Object)} operations so that a <code>JavaMap</code> acts very similar to a
+ * javascript <code>Object</code> There is also an iterator to iterate over entries with <code>
+ * for .. of</code>.
+ *
+ * <p><b>Limitations:</b> The wrapped map should have <code>String</code> or <code>Integer</code> as
+ * key. Otherwise, property based access may not work properly.
+ */
+public class NativeJavaMap extends NativeJavaObject {
+
+    private static final long serialVersionUID = -3786257752907047381L;
+
+    private Map<Object, Object> map;
+
+    static void init(ScriptableObject scope, boolean sealed) {
+        NativeJavaMapIterator.init(scope, sealed);
+    }
+
+    @SuppressWarnings("unchecked")
+    public NativeJavaMap(Scriptable scope, Object map) {
+        super(scope, map, map.getClass());
+        assert map instanceof Map;
+        this.map = (Map<Object, Object>) map;
+    }
+
+    @Override
+    public String getClassName() {
+        return "JavaMap";
+    }
+
+    @Override
+    public boolean has(String name, Scriptable start) {
+        Context cx = Context.getCurrentContext();
+        if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) {
+            if (map.containsKey(name)) {
+                return true;
+            }
+        }
+        return super.has(name, start);
+    }
+
+    @Override
+    public boolean has(int index, Scriptable start) {
+        Context cx = Context.getCurrentContext();
+        if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) {
+            if (map.containsKey(Integer.valueOf(index))) {
+                return true;
+            }
+        }
+        return super.has(index, start);
+    }
+
+    @Override
+    public boolean has(Symbol key, Scriptable start) {
+        if (SymbolKey.ITERATOR.equals(key)) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public Object get(String name, Scriptable start) {
+        Context cx = Context.getCurrentContext();
+        if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) {
+            if (map.containsKey(name)) {
+                Object obj = map.get(name);
+                return cx.getWrapFactory().wrap(cx, this, obj, obj == null ? null : obj.getClass());
+            }
+        }
+        return super.get(name, start);
+    }
+
+    @Override
+    public Object get(int index, Scriptable start) {
+        Context cx = Context.getCurrentContext();
+        if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) {
+            if (map.containsKey(Integer.valueOf(index))) {
+                Object obj = map.get(Integer.valueOf(index));
+                return cx.getWrapFactory().wrap(cx, this, obj, obj == null ? null : obj.getClass());
+            }
+        }
+        return super.get(index, start);
+    }
+
+    @Override
+    public Object get(Symbol key, Scriptable start) {
+        if (SymbolKey.ITERATOR.equals(key)) {
+            return symbol_iterator;
+        }
+        return super.get(key, start);
+    }
+
+    @Override
+    public void put(String name, Scriptable start, Object value) {
+        Context cx = Context.getCurrentContext();
+        if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) {
+            map.put(name, Context.jsToJava(value, Object.class));
+        } else {
+            super.put(name, start, value);
+        }
+    }
+
+    @Override
+    public void put(int index, Scriptable start, Object value) {
+        Context cx = Context.getContext();
+        if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) {
+            map.put(Integer.valueOf(index), Context.jsToJava(value, Object.class));
+        } else {
+            super.put(index, start, value);
+        }
+    }
+
+    @Override
+    public Object[] getIds() {
+        Context cx = Context.getCurrentContext();
+        if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) {
+            List<Object> ids = new ArrayList<>(map.size());
+            for (Object key : map.keySet()) {
+                if (key instanceof Integer) {
+                    ids.add(key);
+                } else {
+                    ids.add(ScriptRuntime.toString(key));
+                }
+            }
+            return ids.toArray();
+        }
+        return super.getIds();
+    }
+
+    private static Callable symbol_iterator =
+            (Context cx, Scriptable scope, Scriptable thisObj, Object[] args) -> {
+                if (!(thisObj instanceof NativeJavaMap)) {
+                    throw ScriptRuntime.typeErrorById("msg.incompat.call", SymbolKey.ITERATOR);
+                }
+                return new NativeJavaMapIterator(scope, ((NativeJavaMap) thisObj).map);
+            };
+
+    private static final class NativeJavaMapIterator extends ES6Iterator {
+        private static final long serialVersionUID = 1L;
+        private static final String ITERATOR_TAG = "JavaMapIterator";
+
+        static void init(ScriptableObject scope, boolean sealed) {
+            ES6Iterator.init(scope, sealed, new NativeJavaMapIterator(), ITERATOR_TAG);
+        }
+
+        /** Only for constructing the prototype object. */
+        private NativeJavaMapIterator() {
+            super();
+        }
+
+        NativeJavaMapIterator(Scriptable scope, Map<Object, Object> map) {
+            super(scope, ITERATOR_TAG);
+            this.iterator = map.entrySet().iterator();
+        }
+
+        @Override
+        public String getClassName() {
+            return "Java Map Iterator";
+        }
+
+        @Override
+        protected boolean isDone(Context cx, Scriptable scope) {
+            return !iterator.hasNext();
+        }
+
+        @Override
+        protected Object nextValue(Context cx, Scriptable scope) {
+            if (!iterator.hasNext()) {
+                return cx.newArray(scope, new Object[] {Undefined.instance, Undefined.instance});
+            }
+            Map.Entry e = iterator.next();
+            return cx.newArray(scope, new Object[] {e.getKey(), e.getValue()});
+        }
+
+        @Override
+        protected String getTag() {
+            return ITERATOR_TAG;
+        }
+
+        private Iterator<Map.Entry<Object, Object>> iterator;
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeJavaMethod.java rhino-1.7.14/src/org/mozilla/javascript/NativeJavaMethod.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeJavaMethod.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeJavaMethod.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,55 +6,49 @@
 
 package org.mozilla.javascript;
 
-import java.lang.reflect.*;
+import java.lang.reflect.Array;
+import java.lang.reflect.Method;
 import java.util.Arrays;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
- * This class reflects Java methods into the JavaScript environment and
- * handles overloading of methods.
+ * This class reflects Java methods into the JavaScript environment and handles overloading of
+ * methods.
  *
  * @author Mike Shaver
  * @see NativeJavaArray
  * @see NativeJavaPackage
  * @see NativeJavaClass
  */
+public class NativeJavaMethod extends BaseFunction {
 
-public class NativeJavaMethod extends BaseFunction
-{
-    static final long serialVersionUID = -3440381785576412928L;
+    private static final long serialVersionUID = -3440381785576412928L;
 
-    NativeJavaMethod(MemberBox[] methods)
-    {
+    NativeJavaMethod(MemberBox[] methods) {
         this.functionName = methods[0].getName();
         this.methods = methods;
     }
 
-    NativeJavaMethod(MemberBox[] methods, String name)
-    {
+    NativeJavaMethod(MemberBox[] methods, String name) {
         this.functionName = name;
         this.methods = methods;
     }
 
-    NativeJavaMethod(MemberBox method, String name)
-    {
+    NativeJavaMethod(MemberBox method, String name) {
         this.functionName = name;
-        this.methods = new MemberBox[] { method };
+        this.methods = new MemberBox[] {method};
     }
 
-    public NativeJavaMethod(Method method, String name)
-    {
+    public NativeJavaMethod(Method method, String name) {
         this(new MemberBox(method), name);
     }
 
     @Override
-    public String getFunctionName()
-    {
+    public String getFunctionName() {
         return functionName;
     }
 
-    static String scriptSignature(Object[] values)
-    {
+    static String scriptSignature(Object[] values) {
         StringBuilder sig = new StringBuilder();
         for (int i = 0; i != values.length; ++i) {
             Object value = values[i];
@@ -72,7 +66,7 @@
                 if (value instanceof Undefined) {
                     s = "undefined";
                 } else if (value instanceof Wrapper) {
-                    Object wrapped = ((Wrapper)value).unwrap();
+                    Object wrapped = ((Wrapper) value).unwrap();
                     s = wrapped.getClass().getName();
                 } else if (value instanceof Function) {
                     s = "function";
@@ -92,8 +86,7 @@
     }
 
     @Override
-    String decompile(int indent, int flags)
-    {
+    String decompile(int indent, int flags) {
         StringBuilder sb = new StringBuilder();
         boolean justbody = (0 != (flags & Decompiler.ONLY_BODY_FLAG));
         if (!justbody) {
@@ -108,8 +101,7 @@
     }
 
     @Override
-    public String toString()
-    {
+    public String toString() {
         StringBuilder sb = new StringBuilder();
         for (int i = 0, N = methods.length; i != N; ++i) {
             // Check member type, we also use this for overloaded constructors
@@ -128,9 +120,7 @@
     }
 
     @Override
-    public Object call(Context cx, Scriptable scope, Scriptable thisObj,
-                       Object[] args)
-    {
+    public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         // Find a method that matches the types given.
         if (methods.length == 0) {
             throw new RuntimeException("No methods defined for call");
@@ -139,9 +129,8 @@
         int index = findCachedFunction(cx, args);
         if (index < 0) {
             Class<?> c = methods[0].method().getDeclaringClass();
-            String sig = c.getName() + '.' + getFunctionName() + '(' +
-                         scriptSignature(args) + ')';
-            throw Context.reportRuntimeError1("msg.java.no_such_method", sig);
+            String sig = c.getName() + '.' + getFunctionName() + '(' + scriptSignature(args) + ')';
+            throw Context.reportRuntimeErrorById("msg.java.no_such_method", sig);
         }
 
         MemberBox meth = methods[index];
@@ -150,7 +139,7 @@
         if (meth.vararg) {
             // marshall the explicit parameters
             Object[] newArgs = new Object[argTypes.length];
-            for (int i = 0; i < argTypes.length-1; i++) {
+            for (int i = 0; i < argTypes.length - 1; i++) {
                 newArgs[i] = Context.jsToJava(args[i], argTypes[i]);
             }
 
@@ -158,29 +147,24 @@
 
             // Handle special situation where a single variable parameter
             // is given and it is a Java or ECMA array or is null.
-            if (args.length == argTypes.length &&
-                (args[args.length-1] == null ||
-                 args[args.length-1] instanceof NativeArray ||
-                 args[args.length-1] instanceof NativeJavaArray))
-            {
+            if (args.length == argTypes.length
+                    && (args[args.length - 1] == null
+                            || args[args.length - 1] instanceof NativeArray
+                            || args[args.length - 1] instanceof NativeJavaArray)) {
                 // convert the ECMA array into a native array
-                varArgs = Context.jsToJava(args[args.length-1],
-                                           argTypes[argTypes.length - 1]);
+                varArgs = Context.jsToJava(args[args.length - 1], argTypes[argTypes.length - 1]);
             } else {
                 // marshall the variable parameters
-                Class<?> componentType = argTypes[argTypes.length - 1].
-                                         getComponentType();
-                varArgs = Array.newInstance(componentType,
-                                            args.length - argTypes.length + 1);
+                Class<?> componentType = argTypes[argTypes.length - 1].getComponentType();
+                varArgs = Array.newInstance(componentType, args.length - argTypes.length + 1);
                 for (int i = 0; i < Array.getLength(varArgs); i++) {
-                    Object value = Context.jsToJava(args[argTypes.length-1 + i],
-                                                    componentType);
+                    Object value = Context.jsToJava(args[argTypes.length - 1 + i], componentType);
                     Array.set(varArgs, i, value);
                 }
             }
 
             // add varargs
-            newArgs[argTypes.length-1] = varArgs;
+            newArgs[argTypes.length - 1] = varArgs;
             // replace the original args with the new one
             args = newArgs;
         } else {
@@ -199,18 +183,20 @@
         }
         Object javaObject;
         if (meth.isStatic()) {
-            javaObject = null;  // don't need an object
+            javaObject = null; // don't need an object
         } else {
             Scriptable o = thisObj;
             Class<?> c = meth.getDeclaringClass();
-            for (;;) {
+            for (; ; ) {
                 if (o == null) {
-                    throw Context.reportRuntimeError3(
-                        "msg.nonjava.method", getFunctionName(),
-                        ScriptRuntime.toString(thisObj), c.getName());
+                    throw Context.reportRuntimeErrorById(
+                            "msg.nonjava.method",
+                            getFunctionName(),
+                            ScriptRuntime.toString(thisObj),
+                            c.getName());
                 }
                 if (o instanceof Wrapper) {
-                    javaObject = ((Wrapper)o).unwrap();
+                    javaObject = ((Wrapper) o).unwrap();
                     if (c.isInstance(javaObject)) {
                         break;
                     }
@@ -226,20 +212,24 @@
         Class<?> staticType = meth.method().getReturnType();
 
         if (debug) {
-            Class<?> actualType = (retval == null) ? null
-                                                : retval.getClass();
-            System.err.println(" ----- Returned " + retval +
-                               " actual = " + actualType +
-                               " expect = " + staticType);
+            Class<?> actualType = (retval == null) ? null : retval.getClass();
+            System.err.println(
+                    " ----- Returned "
+                            + retval
+                            + " actual = "
+                            + actualType
+                            + " expect = "
+                            + staticType);
         }
 
-        Object wrapped = cx.getWrapFactory().wrap(cx, scope,
-                                                  retval, staticType);
+        Object wrapped =
+                cx.getWrapFactory()
+                        .wrap(
+                                cx, scope,
+                                retval, staticType);
         if (debug) {
-            Class<?> actualType = (wrapped == null) ? null
-                                                 : wrapped.getClass();
-            System.err.println(" ----- Wrapped as " + wrapped +
-                               " class = " + actualType);
+            Class<?> actualType = (wrapped == null) ? null : wrapped.getClass();
+            System.err.println(" ----- Wrapped as " + wrapped + " class = " + actualType);
         }
 
         if (wrapped == null && staticType == Void.TYPE) {
@@ -250,25 +240,17 @@
 
     int findCachedFunction(Context cx, Object[] args) {
         if (methods.length > 1) {
-            if (overloadCache != null) {
-                for (ResolvedOverload ovl : overloadCache) {
-                    if (ovl.matches(args)) {
-                        return ovl.index;
-                    }
+            for (ResolvedOverload ovl : overloadCache) {
+                if (ovl.matches(args)) {
+                    return ovl.index;
                 }
-            } else {
-                overloadCache = new CopyOnWriteArrayList<ResolvedOverload>();
             }
             int index = findFunction(cx, methods, args);
             // As a sanity measure, don't let the lookup cache grow longer
             // than twice the number of overloaded methods
             if (overloadCache.size() < methods.length * 2) {
-                synchronized (overloadCache) {
-                    ResolvedOverload ovl = new ResolvedOverload(args, index);
-                    if (!overloadCache.contains(ovl)) {
-                        overloadCache.add(0, ovl);
-                    }
-                }
+                ResolvedOverload ovl = new ResolvedOverload(args, index);
+                overloadCache.addIfAbsent(ovl);
             }
             return index;
         }
@@ -276,13 +258,10 @@
     }
 
     /**
-     * Find the index of the correct function to call given the set of methods
-     * or constructors and the arguments.
-     * If no function can be found to call, return -1.
+     * Find the index of the correct function to call given the set of methods or constructors and
+     * the arguments. If no function can be found to call, return -1.
      */
-    static int findFunction(Context cx,
-                            MemberBox[] methodsOrCtors, Object[] args)
-    {
+    static int findFunction(Context cx, MemberBox[] methodsOrCtors, Object[] args) {
         if (methodsOrCtors.length == 0) {
             return -1;
         } else if (methodsOrCtors.length == 1) {
@@ -292,7 +271,7 @@
 
             if (member.vararg) {
                 alength--;
-                if ( alength > args.length) {
+                if (alength > args.length) {
                     return -1;
                 }
             } else {
@@ -302,8 +281,7 @@
             }
             for (int j = 0; j != alength; ++j) {
                 if (!NativeJavaObject.canConvert(args[j], argTypes[j])) {
-                    if (debug) printDebug("Rejecting (args can't convert) ",
-                                          member, args);
+                    if (debug) printDebug("Rejecting (args can't convert) ", member, args);
                     return -1;
                 }
             }
@@ -315,14 +293,14 @@
         int[] extraBestFits = null;
         int extraBestFitsCount = 0;
 
-      search:
+        search:
         for (int i = 0; i < methodsOrCtors.length; i++) {
             MemberBox member = methodsOrCtors[i];
             Class<?>[] argTypes = member.argTypes;
             int alength = argTypes.length;
             if (member.vararg) {
                 alength--;
-                if ( alength > args.length) {
+                if (alength > args.length) {
                     continue search;
                 }
             } else {
@@ -332,8 +310,7 @@
             }
             for (int j = 0; j < alength; j++) {
                 if (!NativeJavaObject.canConvert(args[j], argTypes[j])) {
-                    if (debug) printDebug("Rejecting (args can't convert) ",
-                                          member, args);
+                    if (debug) printDebug("Rejecting (args can't convert) ", member, args);
                     continue search;
                 }
             }
@@ -346,9 +323,9 @@
                 // until extraBestFitsCount to avoid extraBestFits allocation
                 // in the most common case of no ambiguity
                 int betterCount = 0; // number of times member was prefered over
-                                     // best fits
-                int worseCount = 0;  // number of times best fits were prefered
-                                     // over member
+                // best fits
+                int worseCount = 0; // number of times best fits were prefered
+                // over member
                 for (int j = -1; j != extraBestFitsCount; ++j) {
                     int bestFitIndex;
                     if (j == -1) {
@@ -357,22 +334,21 @@
                         bestFitIndex = extraBestFits[j];
                     }
                     MemberBox bestFit = methodsOrCtors[bestFitIndex];
-                    if (cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS) &&
-                        (bestFit.member().getModifiers() & Modifier.PUBLIC) !=
-                            (member.member().getModifiers() & Modifier.PUBLIC))
-                    {
+                    if (cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)
+                            && bestFit.isPublic() != member.isPublic()) {
                         // When FEATURE_ENHANCED_JAVA_ACCESS gives us access
                         // to non-public members, continue to prefer public
                         // methods in overloading
-                        if ((bestFit.member().getModifiers() & Modifier.PUBLIC) == 0)
-                            ++betterCount;
-                        else
-                            ++worseCount;
+                        if (!bestFit.isPublic()) ++betterCount;
+                        else ++worseCount;
                     } else {
-                        int preference = preferSignature(args, argTypes,
-                                                         member.vararg,
-                                                         bestFit.argTypes,
-                                                         bestFit.vararg );
+                        int preference =
+                                preferSignature(
+                                        args,
+                                        argTypes,
+                                        member.vararg,
+                                        bestFit.argTypes,
+                                        bestFit.vararg);
                         if (preference == PREFERENCE_AMBIGUOUS) {
                             break;
                         } else if (preference == PREFERENCE_FIRST_ARG) {
@@ -386,26 +362,23 @@
                             // static methods of the class hierarchy, even if
                             // a derived class's parameters match exactly.
                             // We want to call the derived class's method.
-                            if (bestFit.isStatic() &&
-                                bestFit.getDeclaringClass().isAssignableFrom(
-                                       member.getDeclaringClass()))
-                            {
+                            if (bestFit.isStatic()
+                                    && bestFit.getDeclaringClass()
+                                            .isAssignableFrom(member.getDeclaringClass())) {
                                 // On some JVMs, Class.getMethods will return all
                                 // static methods of the class hierarchy, even if
                                 // a derived class's parameters match exactly.
                                 // We want to call the derived class's method.
-                                if (debug) printDebug(
-                                    "Substituting (overridden static)",
-                                    member, args);
+                                if (debug)
+                                    printDebug("Substituting (overridden static)", member, args);
                                 if (j == -1) {
                                     firstBestFit = i;
                                 } else {
                                     extraBestFits[j] = i;
                                 }
                             } else {
-                                if (debug) printDebug(
-                                    "Ignoring same signature member ",
-                                    member, args);
+                                if (debug)
+                                    printDebug("Ignoring same signature member ", member, args);
                             }
                             continue search;
                         }
@@ -413,18 +386,15 @@
                 }
                 if (betterCount == 1 + extraBestFitsCount) {
                     // member was prefered over all best fits
-                    if (debug) printDebug(
-                        "New first applicable ", member, args);
+                    if (debug) printDebug("New first applicable ", member, args);
                     firstBestFit = i;
                     extraBestFitsCount = 0;
                 } else if (worseCount == 1 + extraBestFitsCount) {
                     // all best fits were prefered over member, ignore it
-                    if (debug) printDebug(
-                        "Rejecting (all current bests better) ", member, args);
+                    if (debug) printDebug("Rejecting (all current bests better) ", member, args);
                 } else {
                     // some ambiguity was present, add member to best fit set
-                    if (debug) printDebug(
-                        "Added to best fit set ", member, args);
+                    if (debug) printDebug("Added to best fit set ", member, args);
                     if (extraBestFits == null) {
                         // Allocate maximum possible array
                         extraBestFits = new int[methodsOrCtors.length - 1];
@@ -461,38 +431,35 @@
         String memberClass = firstFitMember.getDeclaringClass().getName();
 
         if (methodsOrCtors[0].isCtor()) {
-            throw Context.reportRuntimeError3(
-                "msg.constructor.ambiguous",
-                memberName, scriptSignature(args), buf.toString());
-        } else {
-            throw Context.reportRuntimeError4(
-                "msg.method.ambiguous", memberClass,
-                memberName, scriptSignature(args), buf.toString());
+            throw Context.reportRuntimeErrorById(
+                    "msg.constructor.ambiguous", memberName, scriptSignature(args), buf.toString());
         }
+        throw Context.reportRuntimeErrorById(
+                "msg.method.ambiguous",
+                memberClass,
+                memberName,
+                scriptSignature(args),
+                buf.toString());
     }
 
     /** Types are equal */
-    private static final int PREFERENCE_EQUAL      = 0;
-    private static final int PREFERENCE_FIRST_ARG  = 1;
+    private static final int PREFERENCE_EQUAL = 0;
+
+    private static final int PREFERENCE_FIRST_ARG = 1;
     private static final int PREFERENCE_SECOND_ARG = 2;
     /** No clear "easy" conversion */
-    private static final int PREFERENCE_AMBIGUOUS  = 3;
+    private static final int PREFERENCE_AMBIGUOUS = 3;
 
     /**
-     * Determine which of two signatures is the closer fit.
-     * Returns one of PREFERENCE_EQUAL, PREFERENCE_FIRST_ARG,
-     * PREFERENCE_SECOND_ARG, or PREFERENCE_AMBIGUOUS.
+     * Determine which of two signatures is the closer fit. Returns one of PREFERENCE_EQUAL,
+     * PREFERENCE_FIRST_ARG, PREFERENCE_SECOND_ARG, or PREFERENCE_AMBIGUOUS.
      */
-    private static int preferSignature(Object[] args,
-                                       Class<?>[] sig1,
-                                       boolean vararg1,
-                                       Class<?>[] sig2,
-                                       boolean vararg2 )
-    {
+    private static int preferSignature(
+            Object[] args, Class<?>[] sig1, boolean vararg1, Class<?>[] sig2, boolean vararg2) {
         int totalPreference = 0;
         for (int j = 0; j < args.length; j++) {
-            Class<?> type1 = vararg1 && j >= sig1.length ? sig1[sig1.length-1] : sig1[j];
-            Class<?> type2 = vararg2 && j >= sig2.length ? sig2[sig2.length-1] : sig2[j];
+            Class<?> type1 = vararg1 && j >= sig1.length ? sig1[sig1.length - 1] : sig1[j];
+            Class<?> type2 = vararg2 && j >= sig2.length ? sig2[sig2.length - 1] : sig2[j];
             if (type1 == type2) {
                 continue;
             }
@@ -532,12 +499,9 @@
         return totalPreference;
     }
 
-
     private static final boolean debug = false;
 
-    private static void printDebug(String msg, MemberBox member,
-                                   Object[] args)
-    {
+    private static void printDebug(String msg, MemberBox member, Object[] args) {
         if (debug) {
             StringBuilder sb = new StringBuilder();
             sb.append(" ----- ");
@@ -557,7 +521,8 @@
 
     MemberBox[] methods;
     private String functionName;
-    private transient CopyOnWriteArrayList<ResolvedOverload> overloadCache;
+    private final transient CopyOnWriteArrayList<ResolvedOverload> overloadCache =
+            new CopyOnWriteArrayList<>();
 }
 
 class ResolvedOverload {
@@ -569,8 +534,7 @@
         types = new Class<?>[args.length];
         for (int i = 0, l = args.length; i < l; i++) {
             Object arg = args[i];
-            if (arg instanceof Wrapper)
-                arg = ((Wrapper)arg).unwrap();
+            if (arg instanceof Wrapper) arg = ((Wrapper) arg).unwrap();
             types[i] = arg == null ? null : arg.getClass();
         }
     }
@@ -581,8 +545,7 @@
         }
         for (int i = 0, l = args.length; i < l; i++) {
             Object arg = args[i];
-            if (arg instanceof Wrapper)
-                arg = ((Wrapper)arg).unwrap();
+            if (arg instanceof Wrapper) arg = ((Wrapper) arg).unwrap();
             if (arg == null) {
                 if (types[i] != null) return false;
             } else if (arg.getClass() != types[i]) {
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeJavaObject.java rhino-1.7.14/src/org/mozilla/javascript/NativeJavaObject.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeJavaObject.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeJavaObject.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,37 +6,46 @@
 
 package org.mozilla.javascript;
 
-import java.io.*;
-import java.lang.reflect.*;
-import java.util.Map;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.math.BigInteger;
 import java.util.Date;
+import java.util.Iterator;
+import java.util.Map;
 
 /**
- * This class reflects non-Array Java objects into the JavaScript environment.  It
- * reflect fields directly, and uses NativeJavaMethod objects to reflect (possibly
- * overloaded) methods.<p>
+ * This class reflects non-Array Java objects into the JavaScript environment. It reflect fields
+ * directly, and uses NativeJavaMethod objects to reflect (possibly overloaded) methods. It also
+ * provides iterator support for all iterable objects.
+ *
+ * <p>
  *
  * @author Mike Shaver
  * @see NativeJavaArray
  * @see NativeJavaPackage
  * @see NativeJavaClass
  */
+public class NativeJavaObject implements Scriptable, SymbolScriptable, Wrapper, Serializable {
+
+    private static final long serialVersionUID = -6948590651130498591L;
 
-public class NativeJavaObject implements Scriptable, Wrapper, Serializable
-{
-    static final long serialVersionUID = -6948590651130498591L;
-
-    public NativeJavaObject() { }
-
-    public NativeJavaObject(Scriptable scope, Object javaObject,
-                            Class<?> staticType)
-    {
+    static void init(ScriptableObject scope, boolean sealed) {
+        JavaIterableIterator.init(scope, sealed);
+    }
+
+    public NativeJavaObject() {}
+
+    public NativeJavaObject(Scriptable scope, Object javaObject, Class<?> staticType) {
         this(scope, javaObject, staticType, false);
     }
 
-    public NativeJavaObject(Scriptable scope, Object javaObject,
-                            Class<?> staticType, boolean isAdapter)
-    {
+    public NativeJavaObject(
+            Scriptable scope, Object javaObject, Class<?> staticType, boolean isAdapter) {
         this.parent = scope;
         this.javaObject = javaObject;
         this.staticType = staticType;
@@ -51,20 +60,29 @@
         } else {
             dynamicType = staticType;
         }
-        members = JavaMembers.lookupClass(parent, dynamicType, staticType,
-                                          isAdapter);
-        fieldAndMethods
-            = members.getFieldAndMethodsObjects(this, javaObject, false);
+        members = JavaMembers.lookupClass(parent, dynamicType, staticType, isAdapter);
+        fieldAndMethods = members.getFieldAndMethodsObjects(this, javaObject, false);
     }
 
+    @Override
     public boolean has(String name, Scriptable start) {
         return members.has(name, false);
     }
 
+    @Override
     public boolean has(int index, Scriptable start) {
         return false;
     }
 
+    @Override
+    public boolean has(Symbol key, Scriptable start) {
+        if (SymbolKey.ITERATOR.equals(key) && javaObject instanceof Iterable) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
     public Object get(String name, Scriptable start) {
         if (fieldAndMethods != null) {
             Object result = fieldAndMethods.get(name);
@@ -77,72 +95,98 @@
         return members.get(this, name, javaObject, false);
     }
 
+    @Override
+    public Object get(Symbol key, Scriptable start) {
+        if (SymbolKey.ITERATOR.equals(key) && javaObject instanceof Iterable) {
+            return symbol_iterator;
+        }
+        // Native Java objects have no Symbol members
+        return Scriptable.NOT_FOUND;
+    }
+
+    @Override
     public Object get(int index, Scriptable start) {
         throw members.reportMemberNotFound(Integer.toString(index));
     }
 
+    @Override
     public void put(String name, Scriptable start, Object value) {
         // We could be asked to modify the value of a property in the
         // prototype. Since we can't add a property to a Java object,
         // we modify it in the prototype rather than copy it down.
         if (prototype == null || members.has(name, false))
             members.put(this, name, javaObject, value, false);
-        else
-            prototype.put(name, prototype, value);
+        else prototype.put(name, prototype, value);
+    }
+
+    @Override
+    public void put(Symbol symbol, Scriptable start, Object value) {
+        // We could be asked to modify the value of a property in the
+        // prototype. Since we can't add a property to a Java object,
+        // we modify it in the prototype rather than copy it down.
+        String name = symbol.toString();
+        if (prototype == null || members.has(name, false)) {
+            members.put(this, name, javaObject, value, false);
+        } else if (prototype instanceof SymbolScriptable) {
+            ((SymbolScriptable) prototype).put(symbol, prototype, value);
+        }
     }
 
+    @Override
     public void put(int index, Scriptable start, Object value) {
         throw members.reportMemberNotFound(Integer.toString(index));
     }
 
+    @Override
     public boolean hasInstance(Scriptable value) {
         // This is an instance of a Java class, so always return false
         return false;
     }
 
-    public void delete(String name) {
-    }
+    @Override
+    public void delete(String name) {}
 
-    public void delete(int index) {
-    }
+    @Override
+    public void delete(Symbol key) {}
+
+    @Override
+    public void delete(int index) {}
 
+    @Override
     public Scriptable getPrototype() {
         if (prototype == null && javaObject instanceof String) {
             return TopLevel.getBuiltinPrototype(
-                    ScriptableObject.getTopLevelScope(parent),
-                    TopLevel.Builtins.String);
+                    ScriptableObject.getTopLevelScope(parent), TopLevel.Builtins.String);
         }
         return prototype;
     }
 
-    /**
-     * Sets the prototype of the object.
-     */
+    /** Sets the prototype of the object. */
+    @Override
     public void setPrototype(Scriptable m) {
         prototype = m;
     }
 
-    /**
-     * Returns the parent (enclosing) scope of the object.
-     */
+    /** Returns the parent (enclosing) scope of the object. */
+    @Override
     public Scriptable getParentScope() {
         return parent;
     }
 
-    /**
-     * Sets the parent (enclosing) scope of the object.
-     */
+    /** Sets the parent (enclosing) scope of the object. */
+    @Override
     public void setParentScope(Scriptable m) {
         parent = m;
     }
 
+    @Override
     public Object[] getIds() {
         return members.getIds(false);
     }
 
     /**
      * @deprecated Use {@link Context#getWrapFactory()} together with calling {@link
-     * WrapFactory#wrap(Context, Scriptable, Object, Class)}
+     *     WrapFactory#wrap(Context, Scriptable, Object, Class)}
      */
     @Deprecated
     public static Object wrap(Scriptable scope, Object obj, Class<?> staticType) {
@@ -151,21 +195,26 @@
         return cx.getWrapFactory().wrap(cx, scope, obj, staticType);
     }
 
+    @Override
     public Object unwrap() {
         return javaObject;
     }
 
+    @Override
     public String getClassName() {
         return "JavaObject";
     }
 
-    public Object getDefaultValue(Class<?> hint)
-    {
+    @Override
+    public Object getDefaultValue(Class<?> hint) {
         Object value;
         if (hint == null) {
             if (javaObject instanceof Boolean) {
                 hint = ScriptRuntime.BooleanClass;
             }
+            if (javaObject instanceof Number) {
+                hint = ScriptRuntime.NumberClass;
+            }
         }
         if (hint == null || hint == ScriptRuntime.StringClass) {
             value = javaObject.toString();
@@ -176,19 +225,21 @@
             } else if (hint == ScriptRuntime.NumberClass) {
                 converterName = "doubleValue";
             } else {
-                throw Context.reportRuntimeError0("msg.default.value");
+                throw Context.reportRuntimeErrorById("msg.default.value");
             }
             Object converterObject = get(converterName, this);
             if (converterObject instanceof Function) {
-                Function f = (Function)converterObject;
-                value = f.call(Context.getContext(), f.getParentScope(),
-                               this, ScriptRuntime.emptyArgs);
+                Function f = (Function) converterObject;
+                value =
+                        f.call(
+                                Context.getContext(),
+                                f.getParentScope(),
+                                this,
+                                ScriptRuntime.emptyArgs);
             } else {
-                if (hint == ScriptRuntime.NumberClass
-                    && javaObject instanceof Boolean)
-                {
-                    boolean b = ((Boolean)javaObject).booleanValue();
-                    value = ScriptRuntime.wrapNumber(b ? 1.0 : 0.0);
+                if (hint == ScriptRuntime.NumberClass && javaObject instanceof Boolean) {
+                    boolean b = ((Boolean) javaObject).booleanValue();
+                    value = b ? ScriptRuntime.wrapNumber(1.0) : ScriptRuntime.zeroObj;
                 } else {
                     value = javaObject.toString();
                 }
@@ -198,9 +249,9 @@
     }
 
     /**
-     * Determine whether we can/should convert between the given type and the
-     * desired one.  This should be superceded by a conversion-cost calculation
-     * function, but for now I'll hide behind precedent.
+     * Determine whether we can/should convert between the given type and the desired one. This
+     * should be superceded by a conversion-cost calculation function, but for now I'll hide behind
+     * precedent.
      */
     public static boolean canConvert(Object fromObj, Class<?> to) {
         int weight = getConversionWeight(fromObj, to);
@@ -208,174 +259,156 @@
         return (weight < CONVERSION_NONE);
     }
 
-    private static final int JSTYPE_UNDEFINED   = 0; // undefined type
-    private static final int JSTYPE_NULL        = 1; // null
-    private static final int JSTYPE_BOOLEAN     = 2; // boolean
-    private static final int JSTYPE_NUMBER      = 3; // number
-    private static final int JSTYPE_STRING      = 4; // string
-    private static final int JSTYPE_JAVA_CLASS  = 5; // JavaClass
+    private static final int JSTYPE_UNDEFINED = 0; // undefined type
+    private static final int JSTYPE_NULL = 1; // null
+    private static final int JSTYPE_BOOLEAN = 2; // boolean
+    private static final int JSTYPE_NUMBER = 3; // number
+    private static final int JSTYPE_STRING = 4; // string
+    private static final int JSTYPE_JAVA_CLASS = 5; // JavaClass
     private static final int JSTYPE_JAVA_OBJECT = 6; // JavaObject
-    private static final int JSTYPE_JAVA_ARRAY  = 7; // JavaArray
-    private static final int JSTYPE_OBJECT      = 8; // Scriptable
-
-    static final byte CONVERSION_TRIVIAL      = 1;
-    static final byte CONVERSION_NONTRIVIAL   = 0;
-    static final byte CONVERSION_NONE         = 99;
+    private static final int JSTYPE_JAVA_ARRAY = 7; // JavaArray
+    private static final int JSTYPE_OBJECT = 8; // Scriptable
+    private static final int JSTYPE_BIGINT = 9; // BigInt
+
+    static final byte CONVERSION_TRIVIAL = 1;
+    static final byte CONVERSION_NONTRIVIAL = 0;
+    static final byte CONVERSION_NONE = 99;
 
     /**
-     * Derive a ranking based on how "natural" the conversion is.
-     * The special value CONVERSION_NONE means no conversion is possible,
-     * and CONVERSION_NONTRIVIAL signals that more type conformance testing
-     * is required.
-     * Based on
-     * <a href="http://www.mozilla.org/js/liveconnect/lc3_method_overloading.html">
-     * "preferred method conversions" from Live Connect 3</a>
+     * Derive a ranking based on how "natural" the conversion is. The special value CONVERSION_NONE
+     * means no conversion is possible, and CONVERSION_NONTRIVIAL signals that more type conformance
+     * testing is required. Based on <a
+     * href="http://www.mozilla.org/js/liveconnect/lc3_method_overloading.html">"preferred method
+     * conversions" from Live Connect 3</a>
      */
     static int getConversionWeight(Object fromObj, Class<?> to) {
         int fromCode = getJSTypeCode(fromObj);
 
         switch (fromCode) {
+            case JSTYPE_UNDEFINED:
+                if (to == ScriptRuntime.StringClass || to == ScriptRuntime.ObjectClass) {
+                    return 1;
+                }
+                break;
 
-        case JSTYPE_UNDEFINED:
-            if (to == ScriptRuntime.StringClass ||
-                to == ScriptRuntime.ObjectClass) {
-                return 1;
-            }
-            break;
-
-        case JSTYPE_NULL:
-            if (!to.isPrimitive()) {
-                return 1;
-            }
-            break;
-
-        case JSTYPE_BOOLEAN:
-            // "boolean" is #1
-            if (to == Boolean.TYPE) {
-                return 1;
-            }
-            else if (to == ScriptRuntime.BooleanClass) {
-                return 2;
-            }
-            else if (to == ScriptRuntime.ObjectClass) {
-                return 3;
-            }
-            else if (to == ScriptRuntime.StringClass) {
-                return 4;
-            }
-            break;
+            case JSTYPE_NULL:
+                if (!to.isPrimitive()) {
+                    return 1;
+                }
+                break;
 
-        case JSTYPE_NUMBER:
-            if (to.isPrimitive()) {
-                if (to == Double.TYPE) {
+            case JSTYPE_BOOLEAN:
+                // "boolean" is #1
+                if (to == Boolean.TYPE) {
                     return 1;
+                } else if (to == ScriptRuntime.BooleanClass) {
+                    return 2;
+                } else if (to == ScriptRuntime.ObjectClass) {
+                    return 3;
+                } else if (to == ScriptRuntime.StringClass) {
+                    return 4;
                 }
-                else if (to != Boolean.TYPE) {
-                    return 1 + getSizeRank(to);
+                break;
+
+            case JSTYPE_NUMBER:
+            case JSTYPE_BIGINT:
+                if (to.isPrimitive()) {
+                    if (to == Double.TYPE) {
+                        return 1;
+                    } else if (to != Boolean.TYPE) {
+                        return 1 + getSizeRank(to);
+                    }
+                } else {
+                    if (to == ScriptRuntime.StringClass) {
+                        // native numbers are #1-8
+                        return 9;
+                    } else if (to == ScriptRuntime.BigIntegerClass) {
+                        return 10;
+                    } else if (to == ScriptRuntime.ObjectClass) {
+                        return 11;
+                    } else if (ScriptRuntime.NumberClass.isAssignableFrom(to)) {
+                        // "double" is #1
+                        return 2;
+                    }
                 }
-            }
-            else {
+                break;
+
+            case JSTYPE_STRING:
                 if (to == ScriptRuntime.StringClass) {
-                    // native numbers are #1-8
-                    return 9;
-                }
-                else if (to == ScriptRuntime.ObjectClass) {
-                    return 10;
-                }
-                else if (ScriptRuntime.NumberClass.isAssignableFrom(to)) {
-                    // "double" is #1
+                    return 1;
+                } else if (to.isInstance(fromObj)) {
                     return 2;
+                } else if (to.isPrimitive()) {
+                    if (to == Character.TYPE) {
+                        return 3;
+                    } else if (to != Boolean.TYPE) {
+                        return 4;
+                    }
                 }
-            }
-            break;
+                break;
 
-        case JSTYPE_STRING:
-            if (to == ScriptRuntime.StringClass) {
-                return 1;
-            }
-            else if (to.isInstance(fromObj)) {
-                return 2;
-            }
-            else if (to.isPrimitive()) {
-                if (to == Character.TYPE) {
+            case JSTYPE_JAVA_CLASS:
+                if (to == ScriptRuntime.ClassClass) {
+                    return 1;
+                } else if (to == ScriptRuntime.ObjectClass) {
                     return 3;
-                } else if (to != Boolean.TYPE) {
+                } else if (to == ScriptRuntime.StringClass) {
                     return 4;
                 }
-            }
-            break;
-
-        case JSTYPE_JAVA_CLASS:
-            if (to == ScriptRuntime.ClassClass) {
-                return 1;
-            }
-            else if (to == ScriptRuntime.ObjectClass) {
-                return 3;
-            }
-            else if (to == ScriptRuntime.StringClass) {
-                return 4;
-            }
-            break;
-
-        case JSTYPE_JAVA_OBJECT:
-        case JSTYPE_JAVA_ARRAY:
-            Object javaObj = fromObj;
-            if (javaObj instanceof Wrapper) {
-                javaObj = ((Wrapper)javaObj).unwrap();
-            }
-            if (to.isInstance(javaObj)) {
-                return CONVERSION_NONTRIVIAL;
-            }
-            if (to == ScriptRuntime.StringClass) {
-                return 2;
-            }
-            else if (to.isPrimitive() && to != Boolean.TYPE) {
-                return (fromCode == JSTYPE_JAVA_ARRAY)
-                       ? CONVERSION_NONE : 2 + getSizeRank(to);
-            }
-            break;
+                break;
 
-        case JSTYPE_OBJECT:
-            // Other objects takes #1-#3 spots
-            if (to != ScriptRuntime.ObjectClass && to.isInstance(fromObj)) {
-                // No conversion required, but don't apply for java.lang.Object
-                return 1;
-            }
-            if (to.isArray()) {
-                if (fromObj instanceof NativeArray) {
-                    // This is a native array conversion to a java array
-                    // Array conversions are all equal, and preferable to object
-                    // and string conversion, per LC3.
-                    return 2;
+            case JSTYPE_JAVA_OBJECT:
+            case JSTYPE_JAVA_ARRAY:
+                Object javaObj = fromObj;
+                if (javaObj instanceof Wrapper) {
+                    javaObj = ((Wrapper) javaObj).unwrap();
                 }
-            }
-            else if (to == ScriptRuntime.ObjectClass) {
-                return 3;
-            }
-            else if (to == ScriptRuntime.StringClass) {
-                return 4;
-            }
-            else if (to == ScriptRuntime.DateClass) {
-                if (fromObj instanceof NativeDate) {
-                    // This is a native date to java date conversion
-                    return 1;
+                if (to.isInstance(javaObj)) {
+                    return CONVERSION_NONTRIVIAL;
                 }
-            }
-            else if (to.isInterface()) {
+                if (to == ScriptRuntime.StringClass) {
+                    return 2;
+                } else if (to.isPrimitive() && to != Boolean.TYPE) {
+                    return (fromCode == JSTYPE_JAVA_ARRAY) ? CONVERSION_NONE : 2 + getSizeRank(to);
+                }
+                break;
 
-                if (fromObj instanceof NativeFunction) {
-                    // See comments in createInterfaceAdapter
+            case JSTYPE_OBJECT:
+                // Other objects takes #1-#3 spots
+                if (to != ScriptRuntime.ObjectClass && to.isInstance(fromObj)) {
+                    // No conversion required, but don't apply for java.lang.Object
                     return 1;
                 }
-                if (fromObj instanceof NativeObject) {
-                    return 2;
+                if (to.isArray()) {
+                    if (fromObj instanceof NativeArray) {
+                        // This is a native array conversion to a java array
+                        // Array conversions are all equal, and preferable to object
+                        // and string conversion, per LC3.
+                        return 2;
+                    }
+                } else if (to == ScriptRuntime.ObjectClass) {
+                    return 3;
+                } else if (to == ScriptRuntime.StringClass) {
+                    return 4;
+                } else if (to == ScriptRuntime.DateClass) {
+                    if (fromObj instanceof NativeDate) {
+                        // This is a native date to java date conversion
+                        return 1;
+                    }
+                } else if (to.isInterface()) {
+
+                    if (fromObj instanceof NativeFunction) {
+                        // See comments in createInterfaceAdapter
+                        return 1;
+                    }
+                    if (fromObj instanceof NativeObject) {
+                        return 2;
+                    }
+                    return 12;
+                } else if (to.isPrimitive() && to != Boolean.TYPE) {
+                    return 4 + getSizeRank(to);
                 }
-                return 12;
-            }
-            else if (to.isPrimitive() && to != Boolean.TYPE) {
-                return 4 + getSizeRank(to);
-            }
-            break;
+                break;
         }
 
         return CONVERSION_NONE;
@@ -384,29 +417,21 @@
     static int getSizeRank(Class<?> aType) {
         if (aType == Double.TYPE) {
             return 1;
-        }
-        else if (aType == Float.TYPE) {
+        } else if (aType == Float.TYPE) {
             return 2;
-        }
-        else if (aType == Long.TYPE) {
+        } else if (aType == Long.TYPE) {
             return 3;
-        }
-        else if (aType == Integer.TYPE) {
+        } else if (aType == Integer.TYPE) {
             return 4;
-        }
-        else if (aType == Short.TYPE) {
+        } else if (aType == Short.TYPE) {
             return 5;
-        }
-        else if (aType == Character.TYPE) {
+        } else if (aType == Character.TYPE) {
             return 6;
-        }
-        else if (aType == Byte.TYPE) {
+        } else if (aType == Byte.TYPE) {
             return 7;
-        }
-        else if (aType == Boolean.TYPE) {
+        } else if (aType == Boolean.TYPE) {
             return CONVERSION_NONE;
-        }
-        else {
+        } else {
             return 8;
         }
     }
@@ -414,249 +439,211 @@
     private static int getJSTypeCode(Object value) {
         if (value == null) {
             return JSTYPE_NULL;
-        }
-        else if (value == Undefined.instance) {
+        } else if (value == Undefined.instance) {
             return JSTYPE_UNDEFINED;
-        }
-        else if (value instanceof CharSequence) {
+        } else if (value instanceof CharSequence) {
             return JSTYPE_STRING;
-        }
-        else if (value instanceof Number) {
+        } else if (value instanceof BigInteger) {
+            return JSTYPE_BIGINT;
+        } else if (value instanceof Number) {
             return JSTYPE_NUMBER;
-        }
-        else if (value instanceof Boolean) {
+        } else if (value instanceof Boolean) {
             return JSTYPE_BOOLEAN;
-        }
-        else if (value instanceof Scriptable) {
+        } else if (value instanceof Scriptable) {
             if (value instanceof NativeJavaClass) {
                 return JSTYPE_JAVA_CLASS;
-            }
-            else if (value instanceof NativeJavaArray) {
+            } else if (value instanceof NativeJavaArray) {
                 return JSTYPE_JAVA_ARRAY;
-            }
-            else if (value instanceof Wrapper) {
+            } else if (value instanceof Wrapper) {
                 return JSTYPE_JAVA_OBJECT;
-            }
-            else {
+            } else {
                 return JSTYPE_OBJECT;
             }
-        }
-        else if (value instanceof Class) {
+        } else if (value instanceof Class) {
             return JSTYPE_JAVA_CLASS;
-        }
-        else {
+        } else {
             Class<?> valueClass = value.getClass();
             if (valueClass.isArray()) {
                 return JSTYPE_JAVA_ARRAY;
             }
-            else {
-                return JSTYPE_JAVA_OBJECT;
-            }
+            return JSTYPE_JAVA_OBJECT;
         }
     }
 
     /**
-     * Not intended for public use. Callers should use the
-     * public API Context.toType.
+     * Not intended for public use. Callers should use the public API Context.toType.
+     *
      * @deprecated as of 1.5 Release 4
      * @see org.mozilla.javascript.Context#jsToJava(Object, Class)
      */
     @Deprecated
-    public static Object coerceType(Class<?> type, Object value)
-    {
+    public static Object coerceType(Class<?> type, Object value) {
         return coerceTypeImpl(type, value);
     }
 
-    /**
-     * Type-munging for field setting and method invocation.
-     * Conforms to LC3 specification
-     */
-    static Object coerceTypeImpl(Class<?> type, Object value)
-    {
+    /** Type-munging for field setting and method invocation. Conforms to LC3 specification */
+    static Object coerceTypeImpl(Class<?> type, Object value) {
         if (value != null && value.getClass() == type) {
             return value;
         }
 
-        switch (getJSTypeCode(value)) {
-
-        case JSTYPE_NULL:
-            // raise error if type.isPrimitive()
-            if (type.isPrimitive()) {
-                reportConversionError(value, type);
-            }
-            return null;
+        int jsTypeCode = getJSTypeCode(value);
+        switch (jsTypeCode) {
+            case JSTYPE_NULL:
+                // raise error if type.isPrimitive()
+                if (type.isPrimitive()) {
+                    reportConversionError(value, type);
+                }
+                return null;
 
-        case JSTYPE_UNDEFINED:
-            if (type == ScriptRuntime.StringClass ||
-                type == ScriptRuntime.ObjectClass) {
-                return "undefined";
-            }
-            else {
+            case JSTYPE_UNDEFINED:
+                if (type == ScriptRuntime.StringClass || type == ScriptRuntime.ObjectClass) {
+                    return "undefined";
+                }
                 reportConversionError("undefined", type);
-            }
-            break;
-
-        case JSTYPE_BOOLEAN:
-            // Under LC3, only JS Booleans can be coerced into a Boolean value
-            if (type == Boolean.TYPE ||
-                type == ScriptRuntime.BooleanClass ||
-                type == ScriptRuntime.ObjectClass) {
-                return value;
-            }
-            else if (type == ScriptRuntime.StringClass) {
-                return value.toString();
-            }
-            else {
-                reportConversionError(value, type);
-            }
-            break;
-
-        case JSTYPE_NUMBER:
-            if (type == ScriptRuntime.StringClass) {
-                return ScriptRuntime.toString(value);
-            }
-            else if (type == ScriptRuntime.ObjectClass) {
-                return coerceToNumber(Double.TYPE, value);
-            }
-            else if ((type.isPrimitive() && type != Boolean.TYPE) ||
-                     ScriptRuntime.NumberClass.isAssignableFrom(type)) {
-                return coerceToNumber(type, value);
-            }
-            else {
-                reportConversionError(value, type);
-            }
-            break;
+                break;
 
-        case JSTYPE_STRING:
-            if (type == ScriptRuntime.StringClass || type.isInstance(value)) {
-                return value.toString();
-            }
-            else if (type == Character.TYPE
-                     || type == ScriptRuntime.CharacterClass)
-            {
-                // Special case for converting a single char string to a
-                // character
-                // Placed here because it applies *only* to JS strings,
-                // not other JS objects converted to strings
-                if (((CharSequence)value).length() == 1) {
-                    return Character.valueOf(((CharSequence)value).charAt(0));
+            case JSTYPE_BOOLEAN:
+                // Under LC3, only JS Booleans can be coerced into a Boolean value
+                if (type == Boolean.TYPE
+                        || type == ScriptRuntime.BooleanClass
+                        || type == ScriptRuntime.ObjectClass) {
+                    return value;
+                } else if (type == ScriptRuntime.StringClass) {
+                    return value.toString();
+                } else {
+                    reportConversionError(value, type);
                 }
-                else {
+                break;
+
+            case JSTYPE_NUMBER:
+            case JSTYPE_BIGINT:
+                if (type == ScriptRuntime.StringClass) {
+                    return ScriptRuntime.toString(value);
+                } else if (type == ScriptRuntime.ObjectClass) {
+                    Context context = Context.getCurrentContext();
+                    if ((context != null)
+                            && context.hasFeature(Context.FEATURE_INTEGER_WITHOUT_DECIMAL_PLACE)) {
+                        // to process numbers like 2.0 as 2 without decimal place
+                        long roundedValue = Math.round(toDouble(value));
+                        if (roundedValue == toDouble(value)) {
+                            return coerceToNumber(Long.TYPE, value);
+                        }
+                    }
+                    return coerceToNumber(
+                            jsTypeCode == JSTYPE_BIGINT ? BigInteger.class : Double.TYPE, value);
+                } else if ((type.isPrimitive() && type != Boolean.TYPE)
+                        || ScriptRuntime.NumberClass.isAssignableFrom(type)) {
                     return coerceToNumber(type, value);
+                } else {
+                    reportConversionError(value, type);
                 }
-            }
-            else if ((type.isPrimitive() && type != Boolean.TYPE)
-                     || ScriptRuntime.NumberClass.isAssignableFrom(type))
-            {
-                return coerceToNumber(type, value);
-            }
-            else {
-                reportConversionError(value, type);
-            }
-            break;
+                break;
 
-        case JSTYPE_JAVA_CLASS:
-            if (value instanceof Wrapper) {
-                value = ((Wrapper)value).unwrap();
-            }
+            case JSTYPE_STRING:
+                if (type == ScriptRuntime.StringClass || type.isInstance(value)) {
+                    return value.toString();
+                } else if (type == Character.TYPE || type == ScriptRuntime.CharacterClass) {
+                    // Special case for converting a single char string to a
+                    // character
+                    // Placed here because it applies *only* to JS strings,
+                    // not other JS objects converted to strings
+                    if (((CharSequence) value).length() == 1) {
+                        return Character.valueOf(((CharSequence) value).charAt(0));
+                    }
+                    return coerceToNumber(type, value);
+                } else if ((type.isPrimitive() && type != Boolean.TYPE)
+                        || ScriptRuntime.NumberClass.isAssignableFrom(type)) {
+                    return coerceToNumber(type, value);
+                } else {
+                    reportConversionError(value, type);
+                }
+                break;
 
-            if (type == ScriptRuntime.ClassClass ||
-                type == ScriptRuntime.ObjectClass) {
-                return value;
-            }
-            else if (type == ScriptRuntime.StringClass) {
-                return value.toString();
-            }
-            else {
-                reportConversionError(value, type);
-            }
-            break;
+            case JSTYPE_JAVA_CLASS:
+                if (value instanceof Wrapper) {
+                    value = ((Wrapper) value).unwrap();
+                }
 
-        case JSTYPE_JAVA_OBJECT:
-        case JSTYPE_JAVA_ARRAY:
-            if (value instanceof Wrapper) {
-              value = ((Wrapper)value).unwrap();
-            }
-            if (type.isPrimitive()) {
-                if (type == Boolean.TYPE) {
+                if (type == ScriptRuntime.ClassClass || type == ScriptRuntime.ObjectClass) {
+                    return value;
+                } else if (type == ScriptRuntime.StringClass) {
+                    return value.toString();
+                } else {
                     reportConversionError(value, type);
                 }
-                return coerceToNumber(type, value);
-            }
-            else {
-              if (type == ScriptRuntime.StringClass) {
-                    return value.toString();
+                break;
+
+            case JSTYPE_JAVA_OBJECT:
+            case JSTYPE_JAVA_ARRAY:
+                if (value instanceof Wrapper) {
+                    value = ((Wrapper) value).unwrap();
                 }
-                else {
-                    if (type.isInstance(value)) {
-                        return value;
-                    }
-                    else {
+                if (type.isPrimitive()) {
+                    if (type == Boolean.TYPE) {
                         reportConversionError(value, type);
                     }
+                    return coerceToNumber(type, value);
                 }
-            }
-            break;
-
-        case JSTYPE_OBJECT:
-            if (type == ScriptRuntime.StringClass) {
-                return ScriptRuntime.toString(value);
-            }
-            else if (type.isPrimitive()) {
-                if (type == Boolean.TYPE) {
-                    reportConversionError(value, type);
+                if (type == ScriptRuntime.StringClass) {
+                    return value.toString();
                 }
-                return coerceToNumber(type, value);
-            }
-            else if (type.isInstance(value)) {
-                return value;
-            }
-            else if (type == ScriptRuntime.DateClass
-                     && value instanceof NativeDate)
-            {
-                double time = ((NativeDate)value).getJSTimeValue();
-                // XXX: This will replace NaN by 0
-                return new Date((long)time);
-            }
-            else if (type.isArray() && value instanceof NativeArray) {
-                // Make a new java array, and coerce the JS array components
-                // to the target (component) type.
-                NativeArray array = (NativeArray) value;
-                long length = array.getLength();
-                Class<?> arrayType = type.getComponentType();
-                Object Result = Array.newInstance(arrayType, (int)length);
-                for (int i = 0 ; i < length ; ++i) {
-                    try  {
-                        Array.set(Result, i, coerceTypeImpl(
-                                arrayType, array.get(i, array)));
-                    }
-                    catch (EvaluatorException ee) {
-                        reportConversionError(value, type);
-                    }
+                if (type.isInstance(value)) {
+                    return value;
                 }
+                reportConversionError(value, type);
+                break;
 
-                return Result;
-            }
-            else if (value instanceof Wrapper) {
-                value = ((Wrapper)value).unwrap();
-                if (type.isInstance(value))
+            case JSTYPE_OBJECT:
+                if (type == ScriptRuntime.StringClass) {
+                    return ScriptRuntime.toString(value);
+                } else if (type.isPrimitive()) {
+                    if (type == Boolean.TYPE) {
+                        reportConversionError(value, type);
+                    }
+                    return coerceToNumber(type, value);
+                } else if (type.isInstance(value)) {
                     return value;
-                reportConversionError(value, type);
-            }
-            else if (type.isInterface() && (value instanceof NativeObject
-                    || value instanceof NativeFunction)) {
-                // Try to use function/object as implementation of Java interface.
-                return createInterfaceAdapter(type, (ScriptableObject) value);
-            } else {
-                reportConversionError(value, type);
-            }
-            break;
+                } else if (type == ScriptRuntime.DateClass && value instanceof NativeDate) {
+                    double time = ((NativeDate) value).getJSTimeValue();
+                    // XXX: This will replace NaN by 0
+                    return new Date((long) time);
+                } else if (type.isArray() && value instanceof NativeArray) {
+                    // Make a new java array, and coerce the JS array components
+                    // to the target (component) type.
+                    NativeArray array = (NativeArray) value;
+                    long length = array.getLength();
+                    Class<?> arrayType = type.getComponentType();
+                    Object Result = Array.newInstance(arrayType, (int) length);
+                    for (int i = 0; i < length; ++i) {
+                        try {
+                            Array.set(Result, i, coerceTypeImpl(arrayType, array.get(i, array)));
+                        } catch (EvaluatorException ee) {
+                            reportConversionError(value, type);
+                        }
+                    }
+
+                    return Result;
+                } else if (value instanceof Wrapper) {
+                    value = ((Wrapper) value).unwrap();
+                    if (type.isInstance(value)) return value;
+                    reportConversionError(value, type);
+                } else if (type.isInterface()
+                        && (value instanceof NativeObject
+                                || (value instanceof Callable
+                                        && value instanceof ScriptableObject))) {
+                    // Try to use function/object as implementation of Java interface.
+                    return createInterfaceAdapter(type, (ScriptableObject) value);
+                } else {
+                    reportConversionError(value, type);
+                }
+                break;
         }
 
         return value;
     }
 
-    protected static Object createInterfaceAdapter(Class<?>type, ScriptableObject so) {
+    protected static Object createInterfaceAdapter(Class<?> type, ScriptableObject so) {
         // XXX: Currently only instances of ScriptableObject are
         // supported since the resulting interface proxies should
         // be reused next time conversion is made and generic
@@ -676,8 +663,7 @@
         return glue;
     }
 
-    private static Object coerceToNumber(Class<?> type, Object value)
-    {
+    private static Object coerceToNumber(Class<?> type, Object value) {
         Class<?> valueClass = value.getClass();
 
         // Character
@@ -685,44 +671,49 @@
             if (valueClass == ScriptRuntime.CharacterClass) {
                 return value;
             }
-            return Character.valueOf((char)toInteger(value,
-                                                 ScriptRuntime.CharacterClass,
-                                                 Character.MIN_VALUE,
-                                                 Character.MAX_VALUE));
+            return Character.valueOf(
+                    (char)
+                            toInteger(
+                                    value,
+                                    ScriptRuntime.CharacterClass,
+                                    Character.MIN_VALUE,
+                                    Character.MAX_VALUE));
         }
 
         // Double, Float
-        if (type == ScriptRuntime.ObjectClass ||
-            type == ScriptRuntime.DoubleClass || type == Double.TYPE) {
-            return valueClass == ScriptRuntime.DoubleClass
-                ? value
-                : new Double(toDouble(value));
+        if (type == ScriptRuntime.ObjectClass
+                || type == ScriptRuntime.DoubleClass
+                || type == Double.TYPE) {
+            if (valueClass == ScriptRuntime.DoubleClass) {
+                return value;
+            }
+            return Double.valueOf(toDouble(value));
+        }
+
+        if (type == ScriptRuntime.BigIntegerClass) {
+            if (valueClass == ScriptRuntime.BigIntegerClass) {
+                return value;
+            }
+            return ScriptRuntime.toBigInt(value);
         }
 
         if (type == ScriptRuntime.FloatClass || type == Float.TYPE) {
             if (valueClass == ScriptRuntime.FloatClass) {
                 return value;
             }
-            else {
-                double number = toDouble(value);
-                if (Double.isInfinite(number) || Double.isNaN(number)
-                    || number == 0.0) {
-                    return new Float((float)number);
-                }
-                else {
-                    double absNumber = Math.abs(number);
-                    if (absNumber < Float.MIN_VALUE) {
-                        return new Float((number > 0.0) ? +0.0 : -0.0);
-                    }
-                    else if (absNumber > Float.MAX_VALUE) {
-                        return new Float((number > 0.0) ?
-                                         Float.POSITIVE_INFINITY :
-                                         Float.NEGATIVE_INFINITY);
-                    }
-                    else {
-                        return new Float((float)number);
-                    }
-                }
+            double number = toDouble(value);
+            if (Double.isInfinite(number) || Double.isNaN(number) || number == 0.0) {
+                return Float.valueOf((float) number);
+            }
+
+            double absNumber = Math.abs(number);
+            if (absNumber < Float.MIN_VALUE) {
+                return Float.valueOf((number > 0.0) ? +0.0f : -0.0f);
+            } else if (absNumber > Float.MAX_VALUE) {
+                return Float.valueOf(
+                        (number > 0.0) ? Float.POSITIVE_INFINITY : Float.NEGATIVE_INFINITY);
+            } else {
+                return Float.valueOf((float) number);
             }
         }
 
@@ -731,99 +722,87 @@
             if (valueClass == ScriptRuntime.IntegerClass) {
                 return value;
             }
-            else {
-                return Integer.valueOf((int)toInteger(value,
-                                                  ScriptRuntime.IntegerClass,
-                                                  Integer.MIN_VALUE,
-                                                  Integer.MAX_VALUE));
-            }
+            return Integer.valueOf(
+                    (int)
+                            toInteger(
+                                    value,
+                                    ScriptRuntime.IntegerClass,
+                                    Integer.MIN_VALUE,
+                                    Integer.MAX_VALUE));
         }
 
         if (type == ScriptRuntime.LongClass || type == Long.TYPE) {
             if (valueClass == ScriptRuntime.LongClass) {
                 return value;
-            } else {
-                /* Long values cannot be expressed exactly in doubles.
-                 * We thus use the largest and smallest double value that
-                 * has a value expressible as a long value. We build these
-                 * numerical values from their hexidecimal representations
-                 * to avoid any problems caused by attempting to parse a
-                 * decimal representation.
-                 */
-                final double max = Double.longBitsToDouble(0x43dfffffffffffffL);
-                final double min = Double.longBitsToDouble(0xc3e0000000000000L);
-                return Long.valueOf(toInteger(value,
-                                          ScriptRuntime.LongClass,
-                                          min,
-                                          max));
             }
+            /* Long values cannot be expressed exactly in doubles.
+             * We thus use the largest and smallest double value that
+             * has a value expressible as a long value. We build these
+             * numerical values from their hexidecimal representations
+             * to avoid any problems caused by attempting to parse a
+             * decimal representation.
+             */
+            final double max = Double.longBitsToDouble(0x43dfffffffffffffL);
+            final double min = Double.longBitsToDouble(0xc3e0000000000000L);
+            return Long.valueOf(toInteger(value, ScriptRuntime.LongClass, min, max));
         }
 
         if (type == ScriptRuntime.ShortClass || type == Short.TYPE) {
             if (valueClass == ScriptRuntime.ShortClass) {
                 return value;
             }
-            else {
-                return Short.valueOf((short)toInteger(value,
-                                                  ScriptRuntime.ShortClass,
-                                                  Short.MIN_VALUE,
-                                                  Short.MAX_VALUE));
-            }
+            return Short.valueOf(
+                    (short)
+                            toInteger(
+                                    value,
+                                    ScriptRuntime.ShortClass,
+                                    Short.MIN_VALUE,
+                                    Short.MAX_VALUE));
         }
 
         if (type == ScriptRuntime.ByteClass || type == Byte.TYPE) {
             if (valueClass == ScriptRuntime.ByteClass) {
                 return value;
             }
-            else {
-                return Byte.valueOf((byte)toInteger(value,
-                                                ScriptRuntime.ByteClass,
-                                                Byte.MIN_VALUE,
-                                                Byte.MAX_VALUE));
-            }
+            return Byte.valueOf(
+                    (byte)
+                            toInteger(
+                                    value,
+                                    ScriptRuntime.ByteClass,
+                                    Byte.MIN_VALUE,
+                                    Byte.MAX_VALUE));
         }
 
-        return new Double(toDouble(value));
+        return Double.valueOf(toDouble(value));
     }
 
-
-    private static double toDouble(Object value)
-    {
+    private static double toDouble(Object value) {
         if (value instanceof Number) {
-            return ((Number)value).doubleValue();
-        }
-        else if (value instanceof String) {
-            return ScriptRuntime.toNumber((String)value);
-        }
-        else if (value instanceof Scriptable) {
+            return ((Number) value).doubleValue();
+        } else if (value instanceof String) {
+            return ScriptRuntime.toNumber((String) value);
+        } else if (value instanceof Scriptable) {
             if (value instanceof Wrapper) {
                 // XXX: optimize tail-recursion?
-                return toDouble(((Wrapper)value).unwrap());
+                return toDouble(((Wrapper) value).unwrap());
             }
-            else {
-                return ScriptRuntime.toNumber(value);
-            }
-        }
-        else {
+            return ScriptRuntime.toNumber(value);
+        } else {
             Method meth;
             try {
-                meth = value.getClass().getMethod("doubleValue", (Class [])null);
-            }
-            catch (NoSuchMethodException e) {
+                meth = value.getClass().getMethod("doubleValue", (Class[]) null);
+            } catch (NoSuchMethodException e) {
                 meth = null;
-            }
-            catch (SecurityException e) {
+            } catch (SecurityException e) {
                 meth = null;
             }
             if (meth != null) {
                 try {
-                    return ((Number)meth.invoke(value, (Object [])null)).doubleValue();
-                }
-                catch (IllegalAccessException e) {
+                    return ((Number) meth.invoke(value, (Object[]) null)).doubleValue();
+                } catch (IllegalAccessException e) {
                     // XXX: ignore, or error message?
                     reportConversionError(value, Double.TYPE);
-                }
-                catch (InvocationTargetException e) {
+                } catch (InvocationTargetException e) {
                     // XXX: ignore, or error message?
                     reportConversionError(value, Double.TYPE);
                 }
@@ -832,9 +811,7 @@
         }
     }
 
-    private static long toInteger(Object value, Class<?> type,
-                                  double min, double max)
-    {
+    private static long toInteger(Object value, Class<?> type, double min, double max) {
         double d = toDouble(value);
 
         if (Double.isInfinite(d) || Double.isNaN(d)) {
@@ -844,8 +821,7 @@
 
         if (d > 0.0) {
             d = Math.floor(d);
-        }
-        else {
+        } else {
             d = Math.ceil(d);
         }
 
@@ -853,22 +829,19 @@
             // Convert to string first, for more readable message
             reportConversionError(ScriptRuntime.toString(value), type);
         }
-        return (long)d;
+        return (long) d;
     }
 
-    static void reportConversionError(Object value, Class<?> type)
-    {
+    static void reportConversionError(Object value, Class<?> type) {
         // It uses String.valueOf(value), not value.toString() since
         // value can be null, bug 282447.
-        throw Context.reportRuntimeError2(
-            "msg.conversion.not.allowed",
-            String.valueOf(value),
-            JavaMembers.javaSignature(type));
+        throw Context.reportRuntimeErrorById(
+                "msg.conversion.not.allowed",
+                String.valueOf(value),
+                JavaMembers.javaSignature(type));
     }
 
-    private void writeObject(ObjectOutputStream out)
-        throws IOException
-    {
+    private void writeObject(ObjectOutputStream out) throws IOException {
         out.defaultWriteObject();
 
         out.writeBoolean(isAdapter);
@@ -876,7 +849,7 @@
             if (adapter_writeAdapterObject == null) {
                 throw new IOException();
             }
-            Object[] args = { javaObject, out };
+            Object[] args = {javaObject, out};
             try {
                 adapter_writeAdapterObject.invoke(null, args);
             } catch (Exception ex) {
@@ -887,22 +860,19 @@
         }
 
         if (staticType != null) {
-            out.writeObject(staticType.getClass().getName());
+            out.writeObject(staticType.getName());
         } else {
             out.writeObject(null);
         }
     }
 
-    private void readObject(ObjectInputStream in)
-        throws IOException, ClassNotFoundException
-    {
+    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
         in.defaultReadObject();
 
         isAdapter = in.readBoolean();
         if (isAdapter) {
-            if (adapter_readAdapterObject == null)
-                throw new ClassNotFoundException();
-            Object[] args = { this, in };
+            if (adapter_readAdapterObject == null) throw new ClassNotFoundException();
+            Object[] args = {this, in};
             try {
                 javaObject = adapter_readAdapterObject.invoke(null, args);
             } catch (Exception ex) {
@@ -912,7 +882,7 @@
             javaObject = in.readObject();
         }
 
-        String className = (String)in.readObject();
+        String className = (String) in.readObject();
         if (className != null) {
             staticType = Class.forName(className);
         } else {
@@ -922,21 +892,73 @@
         initMembers();
     }
 
-    /**
-     * The prototype of this object.
-     */
+    private static Callable symbol_iterator =
+            (Context cx, Scriptable scope, Scriptable thisObj, Object[] args) -> {
+                if (!(thisObj instanceof NativeJavaObject)) {
+                    throw ScriptRuntime.typeErrorById("msg.incompat.call", SymbolKey.ITERATOR);
+                }
+                Object javaObject = ((NativeJavaObject) thisObj).javaObject;
+                if (!(javaObject instanceof Iterable)) {
+                    throw ScriptRuntime.typeErrorById("msg.incompat.call", SymbolKey.ITERATOR);
+                }
+                return new JavaIterableIterator(scope, (Iterable) javaObject);
+            };
+
+    private static final class JavaIterableIterator extends ES6Iterator {
+        private static final long serialVersionUID = 1L;
+        private static final String ITERATOR_TAG = "JavaIterableIterator";
+
+        static void init(ScriptableObject scope, boolean sealed) {
+            ES6Iterator.init(scope, sealed, new JavaIterableIterator(), ITERATOR_TAG);
+        }
+
+        /** Only for constructing the prototype object. */
+        private JavaIterableIterator() {
+            super();
+        }
+
+        JavaIterableIterator(Scriptable scope, Iterable iterable) {
+            super(scope, ITERATOR_TAG);
+            this.iterator = iterable.iterator();
+        }
+
+        @Override
+        public String getClassName() {
+            return "Java Iterable Iterator";
+        }
+
+        @Override
+        protected boolean isDone(Context cx, Scriptable scope) {
+            return !iterator.hasNext();
+        }
+
+        @Override
+        protected Object nextValue(Context cx, Scriptable scope) {
+            if (!iterator.hasNext()) {
+                return Undefined.instance;
+            }
+            return iterator.next();
+        }
+
+        @Override
+        protected String getTag() {
+            return ITERATOR_TAG;
+        }
+
+        private Iterator iterator;
+    }
+
+    /** The prototype of this object. */
     protected Scriptable prototype;
 
-    /**
-     * The parent scope of this object.
-     */
+    /** The parent scope of this object. */
     protected Scriptable parent;
 
     protected transient Object javaObject;
 
     protected transient Class<?> staticType;
     protected transient JavaMembers members;
-    private transient Map<String,FieldAndMethods> fieldAndMethods;
+    private transient Map<String, FieldAndMethods> fieldAndMethods;
     protected transient boolean isAdapter;
 
     private static final Object COERCED_INTERFACE_KEY = "Coerced Interface";
@@ -951,13 +973,11 @@
             try {
                 sig2[0] = ScriptRuntime.ObjectClass;
                 sig2[1] = Kit.classOrNull("java.io.ObjectOutputStream");
-                adapter_writeAdapterObject = cl.getMethod("writeAdapterObject",
-                                                          sig2);
+                adapter_writeAdapterObject = cl.getMethod("writeAdapterObject", sig2);
 
                 sig2[0] = ScriptRuntime.ScriptableClass;
                 sig2[1] = Kit.classOrNull("java.io.ObjectInputStream");
-                adapter_readAdapterObject = cl.getMethod("readAdapterObject",
-                                                         sig2);
+                adapter_readAdapterObject = cl.getMethod("readAdapterObject", sig2);
 
             } catch (NoSuchMethodException e) {
                 adapter_writeAdapterObject = null;
@@ -965,5 +985,4 @@
             }
         }
     }
-
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeJavaPackage.java rhino-1.7.14/src/org/mozilla/javascript/NativeJavaPackage.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeJavaPackage.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeJavaPackage.java	2022-01-06 22:57:21.000000000 +0100
@@ -26,7 +26,7 @@
 
 public class NativeJavaPackage extends ScriptableObject
 {
-    static final long serialVersionUID = 7445054382212031523L;
+    private static final long serialVersionUID = 7445054382212031523L;
 
     NativeJavaPackage(boolean internalUsage, String packageName,
                       ClassLoader classLoader)
@@ -76,7 +76,7 @@
 
     @Override
     public void put(int index, Scriptable start, Object value) {
-        throw Context.reportRuntimeError0("msg.pkg.int");
+        throw Context.reportRuntimeErrorById("msg.pkg.int");
     }
 
     @Override
@@ -96,15 +96,14 @@
         Object cached = super.get(name, this);
         if (cached != null && cached instanceof NativeJavaPackage) {
             return (NativeJavaPackage) cached;
-        } else {
-            String newPackage = packageName.length() == 0
-                                ? name
-                                : packageName + "." + name;
-            NativeJavaPackage pkg = new NativeJavaPackage(true, newPackage, classLoader);
-            ScriptRuntime.setObjectProtoAndParent(pkg, scope);
-            super.put(name, this, pkg);
-            return pkg;
         }
+        String newPackage = packageName.length() == 0
+                            ? name
+                            : packageName + "." + name;
+        NativeJavaPackage pkg = new NativeJavaPackage(true, newPackage, classLoader);
+        ScriptRuntime.setObjectProtoAndParent(pkg, scope);
+        super.put(name, this, pkg);
+        return pkg;
     }
 
     synchronized Object getPkgProperty(String name, Scriptable start,
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeJavaTopPackage.java rhino-1.7.14/src/org/mozilla/javascript/NativeJavaTopPackage.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeJavaTopPackage.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeJavaTopPackage.java	2022-01-06 22:57:21.000000000 +0100
@@ -22,7 +22,7 @@
 public class NativeJavaTopPackage
     extends NativeJavaPackage implements Function, IdFunctionCall
 {
-    static final long serialVersionUID = -1455787259477709999L;
+    private static final long serialVersionUID = -1455787259477709999L;
 
     // we know these are packages so we can skip the class check
     // note that this is ok even if the package isn't present.
@@ -42,12 +42,14 @@
         super(true, "", loader);
     }
 
+    @Override
     public Object call(Context cx, Scriptable scope, Scriptable thisObj,
                        Object[] args)
     {
         return construct(cx, scope, args);
     }
 
+    @Override
     public Scriptable construct(Context cx, Scriptable scope, Object[] args)
     {
         ClassLoader loader = null;
@@ -61,7 +63,7 @@
             }
         }
         if (loader == null) {
-            Context.reportRuntimeError0("msg.not.classloader");
+            Context.reportRuntimeErrorById("msg.not.classloader");
             return null;
         }
         NativeJavaPackage pkg = new NativeJavaPackage(true, "", loader);
@@ -111,6 +113,7 @@
         }
     }
 
+    @Override
     public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
                              Scriptable thisObj, Object[] args)
     {
@@ -145,7 +148,7 @@
                 offset = index+1;
             }
         }
-        throw Context.reportRuntimeError0("msg.not.java.obj");
+        throw Context.reportRuntimeErrorById("msg.not.java.obj");
     }
 
     private static final Object FTAG = "JavaTopPackage";
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeJSON.java rhino-1.7.14/src/org/mozilla/javascript/NativeJSON.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeJSON.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeJSON.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,57 +6,68 @@
 
 package org.mozilla.javascript;
 
-import org.mozilla.javascript.json.JsonParser;
-
-import java.util.Stack;
+import java.lang.reflect.Array;
+import java.math.BigInteger;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Iterator;
-import java.util.Arrays;
-import java.util.List;
+import java.util.LinkedHashSet;
 import java.util.LinkedList;
+import java.util.Map;
+import java.util.Stack;
+import org.mozilla.javascript.json.JsonParser;
+import org.mozilla.javascript.xml.XMLObject;
 
 /**
- * This class implements the JSON native object.
- * See ECMA 15.12.
+ * This class implements the JSON native object. See ECMA 15.12.
+ *
  * @author Matthew Crumley, Raphael Speyer
  */
-public final class NativeJSON extends IdScriptableObject
-{
-    static final long serialVersionUID = -4567599697595654984L;
+public final class NativeJSON extends IdScriptableObject {
+    private static final long serialVersionUID = -4567599697595654984L;
 
     private static final Object JSON_TAG = "JSON";
 
     private static final int MAX_STRINGIFY_GAP_LENGTH = 10;
 
-    static void init(Scriptable scope, boolean sealed)
-    {
+    static void init(Scriptable scope, boolean sealed) {
         NativeJSON obj = new NativeJSON();
         obj.activatePrototypeMap(MAX_ID);
         obj.setPrototype(getObjectPrototype(scope));
         obj.setParentScope(scope);
-        if (sealed) { obj.sealObject(); }
-        ScriptableObject.defineProperty(scope, "JSON", obj,
-                                        ScriptableObject.DONTENUM);
+        if (sealed) {
+            obj.sealObject();
+        }
+        ScriptableObject.defineProperty(scope, "JSON", obj, ScriptableObject.DONTENUM);
     }
 
-    private NativeJSON()
-    {
-    }
+    private NativeJSON() {}
 
     @Override
-    public String getClassName() { return "JSON"; }
+    public String getClassName() {
+        return "JSON";
+    }
 
     @Override
-    protected void initPrototypeId(int id)
-    {
+    protected void initPrototypeId(int id) {
         if (id <= LAST_METHOD_ID) {
             String name;
             int arity;
             switch (id) {
-              case Id_toSource:  arity = 0; name = "toSource";  break;
-              case Id_parse:     arity = 2; name = "parse";     break;
-              case Id_stringify: arity = 3; name = "stringify"; break;
-              default: throw new IllegalStateException(String.valueOf(id));
+                case Id_toSource:
+                    arity = 0;
+                    name = "toSource";
+                    break;
+                case Id_parse:
+                    arity = 2;
+                    name = "parse";
+                    break;
+                case Id_stringify:
+                    arity = 3;
+                    name = "stringify";
+                    break;
+                default:
+                    throw new IllegalStateException(String.valueOf(id));
             }
             initPrototypeMethod(JSON_TAG, id, name, arity);
         } else {
@@ -65,9 +76,8 @@
     }
 
     @Override
-    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
-                             Scriptable thisObj, Object[] args)
-    {
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         if (!f.hasTag(JSON_TAG)) {
             return super.execIdCall(f, cx, scope, thisObj, args);
         }
@@ -76,60 +86,63 @@
             case Id_toSource:
                 return "JSON";
 
-            case Id_parse: {
-                String jtext = ScriptRuntime.toString(args, 0);
-                Object reviver = null;
-                if (args.length > 1) {
-                    reviver = args[1];
-                }
-                if (reviver instanceof Callable) {
-                  return parse(cx, scope, jtext, (Callable) reviver);
-                } else {
-                  return parse(cx, scope, jtext);
+            case Id_parse:
+                {
+                    String jtext = ScriptRuntime.toString(args, 0);
+                    Object reviver = null;
+                    if (args.length > 1) {
+                        reviver = args[1];
+                    }
+                    if (reviver instanceof Callable) {
+                        return parse(cx, scope, jtext, (Callable) reviver);
+                    }
+                    return parse(cx, scope, jtext);
                 }
-            }
 
-            case Id_stringify: {
-                Object value = null, replacer = null, space = null;
-                switch (args.length) {
-                    case 3: space = args[2];
-                    /* fallthru */ case 2: replacer = args[1];
-                    /* fallthru */ case 1: value = args[0];
-                    /* fallthru */ case 0:
-                    /* fallthru */ default:
+            case Id_stringify:
+                {
+                    Object value = Undefined.instance, replacer = null, space = null;
+
+                    if (args.length > 0) {
+                        value = args[0];
+                        if (args.length > 1) {
+                            replacer = args[1];
+                            if (args.length > 2) {
+                                space = args[2];
+                            }
+                        }
+                    }
+
+                    return stringify(cx, scope, value, replacer, space);
                 }
-                return stringify(cx, scope, value, replacer, space);
-            }
 
-            default: throw new IllegalStateException(String.valueOf(methodId));
+            default:
+                throw new IllegalStateException(String.valueOf(methodId));
         }
     }
 
     private static Object parse(Context cx, Scriptable scope, String jtext) {
-      try {
-        return new JsonParser(cx, scope).parseValue(jtext);
-      } catch (JsonParser.ParseException ex) {
-        throw ScriptRuntime.constructError("SyntaxError", ex.getMessage());
-      }
-    }
-
-    public static Object parse(Context cx, Scriptable scope, String jtext,
-                               Callable reviver)
-    {
-      Object unfiltered = parse(cx, scope, jtext);
-      Scriptable root = cx.newObject(scope);
-      root.put("", root, unfiltered);
-      return walk(cx, scope, reviver, root, "");
-    }
-
-    private static Object walk(Context cx, Scriptable scope, Callable reviver,
-                               Scriptable holder, Object name)
-    {
+        try {
+            return new JsonParser(cx, scope).parseValue(jtext);
+        } catch (JsonParser.ParseException ex) {
+            throw ScriptRuntime.constructError("SyntaxError", ex.getMessage());
+        }
+    }
+
+    public static Object parse(Context cx, Scriptable scope, String jtext, Callable reviver) {
+        Object unfiltered = parse(cx, scope, jtext);
+        Scriptable root = cx.newObject(scope);
+        root.put("", root, unfiltered);
+        return walk(cx, scope, reviver, root, "");
+    }
+
+    private static Object walk(
+            Context cx, Scriptable scope, Callable reviver, Scriptable holder, Object name) {
         final Object property;
         if (name instanceof Number) {
-            property = holder.get( ((Number) name).intValue(), holder);
+            property = holder.get(((Number) name).intValue(), holder);
         } else {
-            property = holder.get( ((String) name), holder);
+            property = holder.get(((String) name), holder);
         }
 
         if (property instanceof Scriptable) {
@@ -148,7 +161,7 @@
                         }
                     } else {
                         int idx = (int) i;
-                        Object newElement = walk(cx, scope, reviver, val, idx);
+                        Object newElement = walk(cx, scope, reviver, val, Integer.valueOf(idx));
                         if (newElement == Undefined.instance) {
                             val.delete(idx);
                         } else {
@@ -161,34 +174,33 @@
                 for (Object p : keys) {
                     Object newElement = walk(cx, scope, reviver, val, p);
                     if (newElement == Undefined.instance) {
-                        if (p instanceof Number)
-                          val.delete(((Number) p).intValue());
-                        else
-                          val.delete((String) p);
+                        if (p instanceof Number) val.delete(((Number) p).intValue());
+                        else val.delete((String) p);
                     } else {
-                        if (p instanceof Number)
-                          val.put(((Number) p).intValue(), val, newElement);
-                        else
-                          val.put((String) p, val, newElement);
+                        if (p instanceof Number) val.put(((Number) p).intValue(), val, newElement);
+                        else val.put((String) p, val, newElement);
                     }
                 }
             }
         }
 
-        return reviver.call(cx, scope, holder, new Object[] { name, property });
+        return reviver.call(cx, scope, holder, new Object[] {name, property});
     }
 
     private static String repeat(char c, int count) {
-      char chars[] = new char[count];
-      Arrays.fill(chars, c);
-      return new String(chars);
+        char chars[] = new char[count];
+        Arrays.fill(chars, c);
+        return new String(chars);
     }
 
     private static class StringifyState {
-        StringifyState(Context cx, Scriptable scope, String indent, String gap,
-                       Callable replacer, List<Object> propertyList,
-                       Object space)
-        {
+        StringifyState(
+                Context cx,
+                Scriptable scope,
+                String indent,
+                String gap,
+                Callable replacer,
+                Object[] propertyList) {
             this.cx = cx;
             this.scope = scope;
 
@@ -196,46 +208,57 @@
             this.gap = gap;
             this.replacer = replacer;
             this.propertyList = propertyList;
-            this.space = space;
         }
 
-        Stack<Scriptable> stack = new Stack<Scriptable>();
+        Stack<Object> stack = new Stack<Object>();
         String indent;
         String gap;
         Callable replacer;
-        List<Object> propertyList;
-        Object space;
+        Object[] propertyList;
 
         Context cx;
         Scriptable scope;
     }
 
-    public static Object stringify(Context cx, Scriptable scope, Object value,
-                                   Object replacer, Object space)
-    {
+    public static Object stringify(
+            Context cx, Scriptable scope, Object value, Object replacer, Object space) {
         String indent = "";
         String gap = "";
 
-        List<Object> propertyList = null;
+        Object[] propertyList = null;
         Callable replacerFunction = null;
 
         if (replacer instanceof Callable) {
-          replacerFunction = (Callable) replacer;
+            replacerFunction = (Callable) replacer;
         } else if (replacer instanceof NativeArray) {
-          propertyList = new LinkedList<Object>();
-          NativeArray replacerArray = (NativeArray) replacer;
-          for (int i : replacerArray.getIndexIds()) {
-            Object v = replacerArray.get(i, replacerArray);
-            if (v instanceof String || v instanceof Number) {
-              propertyList.add(v);
-            } else if (v instanceof NativeString || v instanceof NativeNumber) {
-              propertyList.add(ScriptRuntime.toString(v));
+            LinkedHashSet<Object> propertySet = new LinkedHashSet<Object>();
+            NativeArray replacerArray = (NativeArray) replacer;
+            for (int i : replacerArray.getIndexIds()) {
+                Object v = replacerArray.get(i, replacerArray);
+                if (v instanceof String) {
+                    propertySet.add(v);
+                } else if (v instanceof Number
+                        || v instanceof NativeString
+                        || v instanceof NativeNumber) {
+                    // TODO: This should also apply to subclasses of NativeString and NativeNumber
+                    // once the class, extends, and super keywords are implemented
+                    propertySet.add(ScriptRuntime.toString(v));
+                }
+            }
+            // After items have been converted to strings and duplicates removed, transform to an
+            // array and convert indexed keys back to Integers as required for later processing
+            propertyList = new Object[propertySet.size()];
+            int i = 0;
+            for (Object prop : propertySet) {
+                ScriptRuntime.StringIdOrIndex idOrIndex = ScriptRuntime.toStringIdOrIndex(cx, prop);
+                // This will always be a String or Integer
+                propertyList[i++] =
+                        (idOrIndex.stringId == null) ? idOrIndex.index : idOrIndex.stringId;
             }
-          }
         }
 
         if (space instanceof NativeNumber) {
-            space = ScriptRuntime.toNumber(space);
+            space = Double.valueOf(ScriptRuntime.toNumber(space));
         } else if (space instanceof NativeString) {
             space = ScriptRuntime.toString(space);
         }
@@ -244,20 +267,15 @@
             int gapLength = (int) ScriptRuntime.toInteger(space);
             gapLength = Math.min(MAX_STRINGIFY_GAP_LENGTH, gapLength);
             gap = (gapLength > 0) ? repeat(' ', gapLength) : "";
-            space = gapLength;
         } else if (space instanceof String) {
             gap = (String) space;
             if (gap.length() > MAX_STRINGIFY_GAP_LENGTH) {
-              gap = gap.substring(0, MAX_STRINGIFY_GAP_LENGTH);
+                gap = gap.substring(0, MAX_STRINGIFY_GAP_LENGTH);
             }
         }
 
-        StringifyState state = new StringifyState(cx, scope,
-            indent,
-            gap,
-            replacerFunction,
-            propertyList,
-            space);
+        StringifyState state =
+                new StringifyState(cx, scope, indent, gap, replacerFunction, propertyList);
 
         ScriptableObject wrapper = new NativeObject();
         wrapper.setParentScope(scope);
@@ -266,10 +284,10 @@
         return str("", wrapper, state);
     }
 
-    private static Object str(Object key, Scriptable holder,
-                              StringifyState state)
-    {
+    private static Object str(Object key, Scriptable holder, StringifyState state) {
         Object value = null;
+        Object unwrappedJavaValue = null;
+
         if (key instanceof String) {
             value = getProperty(holder, (String) key);
         } else {
@@ -279,23 +297,43 @@
         if (value instanceof Scriptable && hasProperty((Scriptable) value, "toJSON")) {
             Object toJSON = getProperty((Scriptable) value, "toJSON");
             if (toJSON instanceof Callable) {
-                value = callMethod(state.cx, (Scriptable) value, "toJSON",
-                                   new Object[] { key });
+                value = callMethod(state.cx, (Scriptable) value, "toJSON", new Object[] {key});
+            }
+        } else if (value instanceof BigInteger) {
+            Scriptable bigInt = ScriptRuntime.toObject(state.cx, state.scope, value);
+            if (hasProperty(bigInt, "toJSON")) {
+                Object toJSON = getProperty(bigInt, "toJSON");
+                if (toJSON instanceof Callable) {
+                    value = callMethod(state.cx, bigInt, "toJSON", new Object[] {key});
+                }
             }
         }
 
         if (state.replacer != null) {
-            value = state.replacer.call(state.cx, state.scope, holder,
-                                        new Object[] { key, value });
+            value = state.replacer.call(state.cx, state.scope, holder, new Object[] {key, value});
         }
 
-
         if (value instanceof NativeNumber) {
-            value = ScriptRuntime.toNumber(value);
+            value = Double.valueOf(ScriptRuntime.toNumber(value));
         } else if (value instanceof NativeString) {
             value = ScriptRuntime.toString(value);
         } else if (value instanceof NativeBoolean) {
             value = ((NativeBoolean) value).getDefaultValue(ScriptRuntime.BooleanClass);
+        } else if (state.cx.getLanguageVersion() >= Context.VERSION_ES6
+                && value instanceof NativeBigInt) {
+            value = ((NativeBigInt) value).getDefaultValue(ScriptRuntime.BigIntegerClass);
+        } else if (value instanceof NativeJavaObject) {
+            unwrappedJavaValue = ((NativeJavaObject) value).unwrap();
+            if (!(unwrappedJavaValue instanceof Map
+                    || unwrappedJavaValue instanceof Collection
+                    || unwrappedJavaValue.getClass().isArray())) {
+                value = unwrappedJavaValue;
+            } else {
+                // Don't unwrap Java objects to be processed by jo() or ja()
+                unwrappedJavaValue = null;
+            }
+        } else if (value instanceof XMLObject) {
+            value = ((XMLObject) value).toString();
         }
 
         if (value == null) return "null";
@@ -307,23 +345,28 @@
         }
 
         if (value instanceof Number) {
+            if (value instanceof BigInteger) {
+                throw ScriptRuntime.typeErrorById("msg.json.cant.serialize", "BigInt");
+            }
             double d = ((Number) value).doubleValue();
-            if (d == d && d != Double.POSITIVE_INFINITY &&
-                d != Double.NEGATIVE_INFINITY)
-            {
+            if (!Double.isNaN(d)
+                    && d != Double.POSITIVE_INFINITY
+                    && d != Double.NEGATIVE_INFINITY) {
                 return ScriptRuntime.toString(value);
-            } else {
-                return "null";
             }
+            return "null";
+        }
+
+        if (unwrappedJavaValue != null) {
+            return javaToJSON(value, state);
         }
 
-        if (value instanceof Scriptable && !(value instanceof Callable)) {
-            if (value instanceof NativeArray) {
-                return ja((NativeArray) value, state);
+        if ((value instanceof Scriptable) && !(value instanceof Callable)) {
+            if (isObjectArrayLike(value)) {
+                return ja((Scriptable) value, state);
             }
             return jo((Scriptable) value, state);
         }
-
         return Undefined.instance;
     }
 
@@ -335,27 +378,61 @@
         if (!iter.hasNext()) return "";
         StringBuilder builder = new StringBuilder(iter.next().toString());
         while (iter.hasNext()) {
-            builder.append(delimiter).append(iter.next().toString());
+            builder.append(delimiter).append(iter.next());
         }
         return builder.toString();
     }
 
     private static String jo(Scriptable value, StringifyState state) {
-        if (state.stack.search(value) != -1) {
-            throw ScriptRuntime.typeError0("msg.cyclic.value");
+        Object trackValue = value, unwrapped = null;
+        if (value instanceof Wrapper) {
+            trackValue = unwrapped = ((Wrapper) value).unwrap();
+        }
+
+        if (state.stack.search(trackValue) != -1) {
+            throw ScriptRuntime.typeErrorById("msg.cyclic.value", trackValue.getClass().getName());
+        }
+        state.stack.push(trackValue);
+
+        if (unwrapped instanceof Map) {
+            Map<?, ?> map = (Map<?, ?>) unwrapped;
+            Scriptable nObj = state.cx.newObject(state.scope);
+            for (Map.Entry<?, ?> entry : map.entrySet()) {
+                if (entry.getKey() instanceof Symbol) continue;
+                Object wrappedValue = Context.javaToJS(entry.getValue(), state.scope, state.cx);
+                int attributes;
+                String key;
+                if (entry.getKey() instanceof String) {
+                    // Keys that are actually Strings are permanent and will not be
+                    // overridden by other objects having the same toString value.
+                    key = (String) entry.getKey();
+                    attributes = ScriptableObject.READONLY | ScriptableObject.PERMANENT;
+                } else {
+                    // To avoid duplicate keys in JSON, replace previous key having the same
+                    // toString value as the current object.
+                    key = entry.getKey().toString();
+                    attributes = ScriptableObject.EMPTY;
+                }
+                try {
+                    ScriptableObject.defineProperty(nObj, key, wrappedValue, attributes);
+                } catch (EcmaError error) {
+                    // ignore TypeErrors if we tried to rewrite the property for a
+                    // String key.
+                }
+            }
+            value = nObj;
         }
-        state.stack.push(value);
 
         String stepback = state.indent;
         state.indent = state.indent + state.gap;
         Object[] k = null;
         if (state.propertyList != null) {
-            k = state.propertyList.toArray();
+            k = state.propertyList;
         } else {
             k = value.getIds();
         }
 
-        List<Object> partial = new LinkedList<Object>();
+        Collection<Object> partial = new LinkedList<Object>();
 
         for (Object p : k) {
             Object strP = str(p, value, state);
@@ -379,8 +456,7 @@
             } else {
                 String separator = ",\n" + state.indent;
                 String properties = join(partial, separator);
-                finalValue = "{\n" + state.indent + properties + '\n' +
-                    stepback + '}';
+                finalValue = "{\n" + state.indent + properties + '\n' + stepback + '}';
             }
         }
 
@@ -389,23 +465,49 @@
         return finalValue;
     }
 
-    private static String ja(NativeArray value, StringifyState state) {
-        if (state.stack.search(value) != -1) {
-            throw ScriptRuntime.typeError0("msg.cyclic.value");
+    private static String ja(Scriptable value, StringifyState state) {
+        Object trackValue = value, unwrapped = null;
+        if (value instanceof Wrapper) {
+            trackValue = unwrapped = ((Wrapper) value).unwrap();
         }
-        state.stack.push(value);
+        if (state.stack.search(trackValue) != -1) {
+            throw ScriptRuntime.typeErrorById("msg.cyclic.value", trackValue.getClass().getName());
+        }
+        state.stack.push(trackValue);
 
         String stepback = state.indent;
         state.indent = state.indent + state.gap;
-        List<Object> partial = new LinkedList<Object>();
+        Collection<Object> partial = new LinkedList<Object>();
+
+        if (unwrapped != null) {
+            Object[] elements = null;
+            if (unwrapped.getClass().isArray()) {
+                int length = Array.getLength(unwrapped);
+                elements = new Object[length];
+                for (int i = 0; i < length; i++) {
+                    elements[i] = Context.javaToJS(Array.get(unwrapped, i), state.scope, state.cx);
+                }
+            } else if (unwrapped instanceof Collection) {
+                Collection<?> collection = (Collection<?>) unwrapped;
+                elements = new Object[collection.size()];
+                int i = 0;
+                for (Object o : collection) {
+                    elements[i++] = Context.javaToJS(o, state.scope, state.cx);
+                }
+            }
+            if (elements != null) {
+                value = state.cx.newArray(state.scope, elements);
+            }
+        }
+
+        long len = ((NativeArray) value).getLength();
 
-        long len = value.getLength();
         for (long index = 0; index < len; index++) {
             Object strP;
             if (index > Integer.MAX_VALUE) {
                 strP = str(Long.toString(index), value, state);
             } else {
-                strP = str((int) index, value, state);
+                strP = str(Integer.valueOf((int) index), value, state);
             }
             if (strP == Undefined.instance) {
                 partial.add("null");
@@ -434,7 +536,8 @@
     }
 
     private static String quote(String string) {
-        StringBuilder product = new StringBuilder(string.length()+2); // two extra chars for " on either side
+        StringBuilder product =
+                new StringBuilder(string.length() + 2); // two extra chars for " on either side
         product.append('"');
         int length = string.length();
         for (int i = 0; i < length; i++) {
@@ -464,10 +567,9 @@
                 default:
                     if (c < ' ') {
                         product.append("\\u");
-                        String hex = String.format("%04x", (int) c);
+                        String hex = String.format("%04x", Integer.valueOf(c));
                         product.append(hex);
-                    }
-                    else {
+                    } else {
                         product.append(c);
                     }
                     break;
@@ -477,31 +579,51 @@
         return product.toString();
     }
 
-// #string_id_map#
+    private static Object javaToJSON(Object value, StringifyState state) {
+        value = state.cx.getJavaToJSONConverter().apply(value);
+        value = Context.javaToJS(value, state.scope, state.cx);
+
+        ScriptableObject wrapper = new NativeObject();
+        wrapper.setParentScope(state.scope);
+        wrapper.setPrototype(ScriptableObject.getObjectPrototype(state.scope));
+        wrapper.defineProperty("", value, 0);
+        return str("", wrapper, state);
+    }
+
+    private static boolean isObjectArrayLike(Object o) {
+        if (o instanceof NativeArray) {
+            return true;
+        }
+        if (o instanceof NativeJavaObject) {
+            Object unwrapped = ((NativeJavaObject) o).unwrap();
+            return (unwrapped instanceof Collection) || (unwrapped.getClass().isArray());
+        }
+        return false;
+    }
 
     @Override
-    protected int findPrototypeId(String s)
-    {
+    protected int findPrototypeId(String s) {
         int id;
-// #generated# Last update: 2009-05-25 16:01:00 EDT
-        {   id = 0; String X = null;
-            L: switch (s.length()) {
-            case 5: X="parse";id=Id_parse; break L;
-            case 8: X="toSource";id=Id_toSource; break L;
-            case 9: X="stringify";id=Id_stringify; break L;
-            }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
+        switch (s) {
+            case "toSource":
+                id = Id_toSource;
+                break;
+            case "parse":
+                id = Id_parse;
+                break;
+            case "stringify":
+                id = Id_stringify;
+                break;
+            default:
+                id = 0;
+                break;
         }
-// #/generated#
         return id;
     }
 
-    private static final int
-        Id_toSource     = 1,
-        Id_parse        = 2,
-        Id_stringify    = 3,
-        LAST_METHOD_ID  = 3,
-        MAX_ID          = 3;
-
-// #/string_id_map#
+    private static final int Id_toSource = 1,
+            Id_parse = 2,
+            Id_stringify = 3,
+            LAST_METHOD_ID = 3,
+            MAX_ID = 3;
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeMap.java rhino-1.7.14/src/org/mozilla/javascript/NativeMap.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeMap.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeMap.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,352 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+import java.util.Iterator;
+
+public class NativeMap extends IdScriptableObject {
+    private static final long serialVersionUID = 1171922614280016891L;
+    private static final Object MAP_TAG = "Map";
+    static final String ITERATOR_TAG = "Map Iterator";
+
+    private final Hashtable entries = new Hashtable();
+
+    private boolean instanceOfMap = false;
+
+    static void init(Context cx, Scriptable scope, boolean sealed) {
+        NativeMap obj = new NativeMap();
+        obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, false);
+
+        ScriptableObject desc = (ScriptableObject) cx.newObject(scope);
+        desc.put("enumerable", desc, Boolean.FALSE);
+        desc.put("configurable", desc, Boolean.TRUE);
+        desc.put("get", desc, obj.get(NativeSet.GETSIZE, obj));
+        obj.defineOwnProperty(cx, "size", desc);
+
+        if (sealed) {
+            obj.sealObject();
+        }
+    }
+
+    @Override
+    public String getClassName() {
+        return "Map";
+    }
+
+    @Override
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        if (!f.hasTag(MAP_TAG)) {
+            return super.execIdCall(f, cx, scope, thisObj, args);
+        }
+        int id = f.methodId();
+        switch (id) {
+            case Id_constructor:
+                if (thisObj == null) {
+                    NativeMap nm = new NativeMap();
+                    nm.instanceOfMap = true;
+                    if (args.length > 0) {
+                        loadFromIterable(cx, scope, nm, key(args));
+                    }
+                    return nm;
+                }
+                throw ScriptRuntime.typeErrorById("msg.no.new", "Map");
+            case Id_set:
+                return realThis(thisObj, f)
+                        .js_set(key(args), args.length > 1 ? args[1] : Undefined.instance);
+            case Id_delete:
+                return realThis(thisObj, f).js_delete(key(args));
+            case Id_get:
+                return realThis(thisObj, f).js_get(key(args));
+            case Id_has:
+                return realThis(thisObj, f).js_has(key(args));
+            case Id_clear:
+                return realThis(thisObj, f).js_clear();
+            case Id_keys:
+                return realThis(thisObj, f).js_iterator(scope, NativeCollectionIterator.Type.KEYS);
+            case Id_values:
+                return realThis(thisObj, f)
+                        .js_iterator(scope, NativeCollectionIterator.Type.VALUES);
+            case Id_entries:
+                return realThis(thisObj, f).js_iterator(scope, NativeCollectionIterator.Type.BOTH);
+            case Id_forEach:
+                return realThis(thisObj, f)
+                        .js_forEach(
+                                cx,
+                                scope,
+                                args.length > 0 ? args[0] : Undefined.instance,
+                                args.length > 1 ? args[1] : Undefined.instance);
+            case SymbolId_getSize:
+                return realThis(thisObj, f).js_getSize();
+        }
+        throw new IllegalArgumentException("Map.prototype has no method: " + f.getFunctionName());
+    }
+
+    private Object js_set(Object k, Object v) {
+        // Special handling of "negative zero" from the spec.
+        Object key = k;
+        if ((key instanceof Number) && ((Number) key).doubleValue() == ScriptRuntime.negativeZero) {
+            key = ScriptRuntime.zeroObj;
+        }
+        entries.put(key, v);
+        return this;
+    }
+
+    private Object js_delete(Object arg) {
+        return Boolean.valueOf(entries.deleteEntry(arg));
+    }
+
+    private Object js_get(Object arg) {
+        final Hashtable.Entry entry = entries.getEntry(arg);
+        if (entry == null) {
+            return Undefined.instance;
+        }
+        return entry.value;
+    }
+
+    private Object js_has(Object arg) {
+        return Boolean.valueOf(entries.has(arg));
+    }
+
+    private Object js_getSize() {
+        return Integer.valueOf(entries.size());
+    }
+
+    private Object js_iterator(Scriptable scope, NativeCollectionIterator.Type type) {
+        return new NativeCollectionIterator(scope, ITERATOR_TAG, type, entries.iterator());
+    }
+
+    private Object js_clear() {
+        entries.clear();
+        return Undefined.instance;
+    }
+
+    private Object js_forEach(Context cx, Scriptable scope, Object arg1, Object arg2) {
+        if (!(arg1 instanceof Callable)) {
+            throw ScriptRuntime.typeErrorById(
+                    "msg.isnt.function", arg1, ScriptRuntime.typeof(arg1));
+        }
+        final Callable f = (Callable) arg1;
+
+        boolean isStrict = cx.isStrictMode();
+        Iterator<Hashtable.Entry> i = entries.iterator();
+        while (i.hasNext()) {
+            // Per spec must convert every time so that primitives are always regenerated...
+            Scriptable thisObj = ScriptRuntime.toObjectOrNull(cx, arg2, scope);
+
+            if (thisObj == null && !isStrict) {
+                thisObj = scope;
+            }
+            if (thisObj == null) {
+                thisObj = Undefined.SCRIPTABLE_UNDEFINED;
+            }
+
+            final Hashtable.Entry e = i.next();
+            f.call(cx, scope, thisObj, new Object[] {e.value, e.key, this});
+        }
+        return Undefined.instance;
+    }
+
+    /**
+     * If an "iterable" object was passed to the constructor, there are many many things to do...
+     * Make this static because NativeWeakMap has the exact same requirement.
+     */
+    static void loadFromIterable(Context cx, Scriptable scope, ScriptableObject map, Object arg1) {
+        if ((arg1 == null) || Undefined.instance.equals(arg1)) {
+            return;
+        }
+
+        // Call the "[Symbol.iterator]" property as a function.
+        final Object ito = ScriptRuntime.callIterator(arg1, cx, scope);
+        if (Undefined.instance.equals(ito)) {
+            // Per spec, ignore if the iterator is undefined
+            return;
+        }
+
+        // Find the "add" function of our own prototype, since it might have
+        // been replaced. Since we're not fully constructed yet, create a dummy instance
+        // so that we can get our own prototype.
+        Scriptable proto = ScriptableObject.getClassPrototype(scope, map.getClassName());
+        final Callable set = ScriptRuntime.getPropFunctionAndThis(proto, "set", cx, scope);
+        ScriptRuntime.lastStoredScriptable(cx);
+
+        ScriptRuntime.loadFromIterable(
+                cx,
+                scope,
+                arg1,
+                (key, value) -> {
+                    set.call(cx, scope, map, new Object[] {key, value});
+                });
+    }
+
+    private static NativeMap realThis(Scriptable thisObj, IdFunctionObject f) {
+        final NativeMap nm = ensureType(thisObj, NativeMap.class, f);
+        if (!nm.instanceOfMap) {
+            // Check for "Map internal data tag"
+            throw ScriptRuntime.typeErrorById("msg.incompat.call", f.getFunctionName());
+        }
+
+        return nm;
+    }
+
+    @Override
+    protected void initPrototypeId(int id) {
+        switch (id) {
+            case SymbolId_getSize:
+                initPrototypeMethod(MAP_TAG, id, NativeSet.GETSIZE, "get size", 0);
+                return;
+            case SymbolId_toStringTag:
+                initPrototypeValue(
+                        SymbolId_toStringTag,
+                        SymbolKey.TO_STRING_TAG,
+                        getClassName(),
+                        DONTENUM | READONLY);
+                return;
+                // fallthrough
+        }
+
+        String s, fnName = null;
+        int arity;
+        switch (id) {
+            case Id_constructor:
+                arity = 0;
+                s = "constructor";
+                break;
+            case Id_set:
+                arity = 2;
+                s = "set";
+                break;
+            case Id_get:
+                arity = 1;
+                s = "get";
+                break;
+            case Id_delete:
+                arity = 1;
+                s = "delete";
+                break;
+            case Id_has:
+                arity = 1;
+                s = "has";
+                break;
+            case Id_clear:
+                arity = 0;
+                s = "clear";
+                break;
+            case Id_keys:
+                arity = 0;
+                s = "keys";
+                break;
+            case Id_values:
+                arity = 0;
+                s = "values";
+                break;
+            case Id_entries:
+                arity = 0;
+                s = "entries";
+                break;
+            case Id_forEach:
+                arity = 1;
+                s = "forEach";
+                break;
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
+        }
+        initPrototypeMethod(MAP_TAG, id, s, fnName, arity);
+    }
+
+    @Override
+    protected int findPrototypeId(Symbol k) {
+        if (NativeSet.GETSIZE.equals(k)) {
+            return SymbolId_getSize;
+        }
+        if (SymbolKey.ITERATOR.equals(k)) {
+            // ECMA spec says that the "Symbol.iterator" property of the prototype has the
+            // "same value" as the "entries" property. We implement this by returning the
+            // ID of "entries" when the iterator symbol is accessed.
+            return Id_entries;
+        }
+        if (SymbolKey.TO_STRING_TAG.equals(k)) {
+            return SymbolId_toStringTag;
+        }
+        return 0;
+    }
+
+    @Override
+    protected int findPrototypeId(String s) {
+        int id;
+        switch (s) {
+            case "constructor":
+                id = Id_constructor;
+                break;
+            case "set":
+                id = Id_set;
+                break;
+            case "get":
+                id = Id_get;
+                break;
+            case "delete":
+                id = Id_delete;
+                break;
+            case "has":
+                id = Id_has;
+                break;
+            case "clear":
+                id = Id_clear;
+                break;
+            case "keys":
+                id = Id_keys;
+                break;
+            case "values":
+                id = Id_values;
+                break;
+            case "entries":
+                id = Id_entries;
+                break;
+            case "forEach":
+                id = Id_forEach;
+                break;
+            default:
+                id = 0;
+                break;
+        }
+        return id;
+    }
+
+    // Note that "SymbolId_iterator" is not present here. That's because the spec
+    // requires that it be the same value as the "entries" prototype property.
+    private static final int Id_constructor = 1,
+            Id_set = 2,
+            Id_get = 3,
+            Id_delete = 4,
+            Id_has = 5,
+            Id_clear = 6,
+            Id_keys = 7,
+            Id_values = 8,
+            Id_entries = 9,
+            Id_forEach = 10,
+            SymbolId_getSize = 11,
+            SymbolId_toStringTag = 12,
+            MAX_PROTOTYPE_ID = SymbolId_toStringTag;
+
+    /**
+     * Extracts the key from the first args entry if any and takes care of the Delegator. This is
+     * used by {@code NativeSet}, {@code NativeWeakMap}, and {@code NativeWekSet} also.
+     *
+     * @param args the args
+     * @return the first argument (de-delegated) or undefined if there is no element in args
+     */
+    static Object key(Object[] args) {
+        if (args.length > 0) {
+            Object key = args[0];
+            if (key instanceof Delegator) {
+                return ((Delegator) key).getDelegee();
+            }
+            return key;
+        }
+        return Undefined.instance;
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeMath.java rhino-1.7.14/src/org/mozilla/javascript/NativeMath.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeMath.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeMath.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,100 +6,237 @@
 
 package org.mozilla.javascript;
 
-import org.mozilla.javascript.typedarrays.Conversions;
-
 /**
- * This class implements the Math native object.
- * See ECMA 15.8.
+ * This class implements the Math native object. See ECMA 15.8.
+ *
  * @author Norris Boyd
  */
-
-final class NativeMath extends IdScriptableObject
-{
-    static final long serialVersionUID = -8838847185801131569L;
+final class NativeMath extends IdScriptableObject {
+    private static final long serialVersionUID = -8838847185801131569L;
 
     private static final Object MATH_TAG = "Math";
+    private static final double LOG2E = 1.4426950408889634;
+    private static final Double Double32 = Double.valueOf(32d);
 
-    static void init(Scriptable scope, boolean sealed)
-    {
+    static void init(Scriptable scope, boolean sealed) {
         NativeMath obj = new NativeMath();
         obj.activatePrototypeMap(MAX_ID);
         obj.setPrototype(getObjectPrototype(scope));
         obj.setParentScope(scope);
-        if (sealed) { obj.sealObject(); }
-        ScriptableObject.defineProperty(scope, "Math", obj,
-                                        ScriptableObject.DONTENUM);
+        if (sealed) {
+            obj.sealObject();
+        }
+        ScriptableObject.defineProperty(scope, "Math", obj, ScriptableObject.DONTENUM);
     }
 
-    private NativeMath()
-    {
-    }
+    private NativeMath() {}
 
     @Override
-    public String getClassName() { return "Math"; }
+    public String getClassName() {
+        return "Math";
+    }
 
     @Override
-    protected void initPrototypeId(int id)
-    {
+    protected void initPrototypeId(int id) {
         if (id <= LAST_METHOD_ID) {
             String name;
             int arity;
             switch (id) {
-              case Id_toSource: arity = 0; name = "toSource"; break;
-              case Id_abs:      arity = 1; name = "abs";      break;
-              case Id_acos:     arity = 1; name = "acos";     break;
-              case Id_asin:     arity = 1; name = "asin";     break;
-              case Id_atan:     arity = 1; name = "atan";     break;
-              case Id_atan2:    arity = 2; name = "atan2";    break;
-              case Id_cbrt:     arity = 1; name = "cbrt";     break;
-              case Id_ceil:     arity = 1; name = "ceil";     break;
-              case Id_cos:      arity = 1; name = "cos";      break;
-              case Id_cosh:     arity = 1; name = "cosh";     break;
-              case Id_exp:      arity = 1; name = "exp";      break;
-              case Id_expm1:    arity = 1; name = "expm1";    break;
-              case Id_floor:    arity = 1; name = "floor";    break;
-              case Id_hypot:    arity = 2; name = "hypot";    break;
-              case Id_imul:     arity = 2; name = "imul";     break;
-              case Id_log:      arity = 1; name = "log";      break;
-              case Id_log1p:    arity = 1; name = "log1p";    break;
-              case Id_log10:    arity = 1; name = "log10";    break;
-              case Id_max:      arity = 2; name = "max";      break;
-              case Id_min:      arity = 2; name = "min";      break;
-              case Id_pow:      arity = 2; name = "pow";      break;
-              case Id_random:   arity = 0; name = "random";   break;
-              case Id_round:    arity = 1; name = "round";    break;
-              case Id_sin:      arity = 1; name = "sin";      break;
-              case Id_sinh:     arity = 1; name = "sinh";     break;
-              case Id_sqrt:     arity = 1; name = "sqrt";     break;
-              case Id_tan:      arity = 1; name = "tan";      break;
-              case Id_tanh:     arity = 1; name = "tanh";     break;
-              case Id_trunc:    arity = 1; name = "trunc";    break;
-              default: throw new IllegalStateException(String.valueOf(id));
+                case Id_toSource:
+                    arity = 0;
+                    name = "toSource";
+                    break;
+                case Id_abs:
+                    arity = 1;
+                    name = "abs";
+                    break;
+                case Id_acos:
+                    arity = 1;
+                    name = "acos";
+                    break;
+                case Id_acosh:
+                    arity = 1;
+                    name = "acosh";
+                    break;
+                case Id_asin:
+                    arity = 1;
+                    name = "asin";
+                    break;
+                case Id_asinh:
+                    arity = 1;
+                    name = "asinh";
+                    break;
+                case Id_atan:
+                    arity = 1;
+                    name = "atan";
+                    break;
+                case Id_atanh:
+                    arity = 1;
+                    name = "atanh";
+                    break;
+                case Id_atan2:
+                    arity = 2;
+                    name = "atan2";
+                    break;
+                case Id_cbrt:
+                    arity = 1;
+                    name = "cbrt";
+                    break;
+                case Id_ceil:
+                    arity = 1;
+                    name = "ceil";
+                    break;
+                case Id_clz32:
+                    arity = 1;
+                    name = "clz32";
+                    break;
+                case Id_cos:
+                    arity = 1;
+                    name = "cos";
+                    break;
+                case Id_cosh:
+                    arity = 1;
+                    name = "cosh";
+                    break;
+                case Id_exp:
+                    arity = 1;
+                    name = "exp";
+                    break;
+                case Id_expm1:
+                    arity = 1;
+                    name = "expm1";
+                    break;
+                case Id_floor:
+                    arity = 1;
+                    name = "floor";
+                    break;
+                case Id_fround:
+                    arity = 1;
+                    name = "fround";
+                    break;
+                case Id_hypot:
+                    arity = 2;
+                    name = "hypot";
+                    break;
+                case Id_imul:
+                    arity = 2;
+                    name = "imul";
+                    break;
+                case Id_log:
+                    arity = 1;
+                    name = "log";
+                    break;
+                case Id_log1p:
+                    arity = 1;
+                    name = "log1p";
+                    break;
+                case Id_log10:
+                    arity = 1;
+                    name = "log10";
+                    break;
+                case Id_log2:
+                    arity = 1;
+                    name = "log2";
+                    break;
+                case Id_max:
+                    arity = 2;
+                    name = "max";
+                    break;
+                case Id_min:
+                    arity = 2;
+                    name = "min";
+                    break;
+                case Id_pow:
+                    arity = 2;
+                    name = "pow";
+                    break;
+                case Id_random:
+                    arity = 0;
+                    name = "random";
+                    break;
+                case Id_round:
+                    arity = 1;
+                    name = "round";
+                    break;
+                case Id_sign:
+                    arity = 1;
+                    name = "sign";
+                    break;
+                case Id_sin:
+                    arity = 1;
+                    name = "sin";
+                    break;
+                case Id_sinh:
+                    arity = 1;
+                    name = "sinh";
+                    break;
+                case Id_sqrt:
+                    arity = 1;
+                    name = "sqrt";
+                    break;
+                case Id_tan:
+                    arity = 1;
+                    name = "tan";
+                    break;
+                case Id_tanh:
+                    arity = 1;
+                    name = "tanh";
+                    break;
+                case Id_trunc:
+                    arity = 1;
+                    name = "trunc";
+                    break;
+                default:
+                    throw new IllegalStateException(String.valueOf(id));
             }
             initPrototypeMethod(MATH_TAG, id, name, arity);
         } else {
             String name;
             double x;
             switch (id) {
-              case Id_E:       x = Math.E;             name = "E";       break;
-              case Id_PI:      x = Math.PI;            name = "PI";      break;
-              case Id_LN10:    x = 2.302585092994046;  name = "LN10";    break;
-              case Id_LN2:     x = 0.6931471805599453; name = "LN2";     break;
-              case Id_LOG2E:   x = 1.4426950408889634; name = "LOG2E";   break;
-              case Id_LOG10E:  x = 0.4342944819032518; name = "LOG10E";  break;
-              case Id_SQRT1_2: x = 0.7071067811865476; name = "SQRT1_2"; break;
-              case Id_SQRT2:   x = 1.4142135623730951; name = "SQRT2";   break;
-              default: throw new IllegalStateException(String.valueOf(id));
+                case Id_E:
+                    x = Math.E;
+                    name = "E";
+                    break;
+                case Id_PI:
+                    x = Math.PI;
+                    name = "PI";
+                    break;
+                case Id_LN10:
+                    x = 2.302585092994046;
+                    name = "LN10";
+                    break;
+                case Id_LN2:
+                    x = 0.6931471805599453;
+                    name = "LN2";
+                    break;
+                case Id_LOG2E:
+                    x = LOG2E;
+                    name = "LOG2E";
+                    break;
+                case Id_LOG10E:
+                    x = 0.4342944819032518;
+                    name = "LOG10E";
+                    break;
+                case Id_SQRT1_2:
+                    x = 0.7071067811865476;
+                    name = "SQRT1_2";
+                    break;
+                case Id_SQRT2:
+                    x = 1.4142135623730951;
+                    name = "SQRT2";
+                    break;
+                default:
+                    throw new IllegalStateException(String.valueOf(id));
             }
-            initPrototypeValue(id, name, ScriptRuntime.wrapNumber(x),
-                               DONTENUM | READONLY | PERMANENT);
+            initPrototypeValue(
+                    id, name, ScriptRuntime.wrapNumber(x), DONTENUM | READONLY | PERMANENT);
         }
     }
 
     @Override
-    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
-                             Scriptable thisObj, Object[] args)
-    {
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         if (!f.hasTag(MATH_TAG)) {
             return super.execIdCall(f, cx, scope, thisObj, args);
         }
@@ -118,18 +255,54 @@
             case Id_acos:
             case Id_asin:
                 x = ScriptRuntime.toNumber(args, 0);
-                if (x == x && -1.0 <= x && x <= 1.0) {
+                if (!Double.isNaN(x) && -1.0 <= x && x <= 1.0) {
                     x = (methodId == Id_acos) ? Math.acos(x) : Math.asin(x);
                 } else {
                     x = Double.NaN;
                 }
                 break;
 
+            case Id_acosh:
+                x = ScriptRuntime.toNumber(args, 0);
+                if (!Double.isNaN(x)) {
+                    return Double.valueOf(Math.log(x + Math.sqrt(x * x - 1.0)));
+                }
+                return ScriptRuntime.NaNobj;
+
+            case Id_asinh:
+                x = ScriptRuntime.toNumber(args, 0);
+                if (Double.isInfinite(x)) {
+                    return Double.valueOf(x);
+                }
+                if (!Double.isNaN(x)) {
+                    if (x == 0) {
+                        if (1 / x > 0) {
+                            return ScriptRuntime.zeroObj;
+                        }
+                        return ScriptRuntime.negativeZeroObj;
+                    }
+                    return Double.valueOf(Math.log(x + Math.sqrt(x * x + 1.0)));
+                }
+                return ScriptRuntime.NaNobj;
+
             case Id_atan:
                 x = ScriptRuntime.toNumber(args, 0);
                 x = Math.atan(x);
                 break;
 
+            case Id_atanh:
+                x = ScriptRuntime.toNumber(args, 0);
+                if (!Double.isNaN(x) && -1.0 <= x && x <= 1.0) {
+                    if (x == 0) {
+                        if (1 / x > 0) {
+                            return ScriptRuntime.zeroObj;
+                        }
+                        return ScriptRuntime.negativeZeroObj;
+                    }
+                    return Double.valueOf(0.5 * Math.log((x + 1.0) / (x - 1.0)));
+                }
+                return ScriptRuntime.NaNobj;
+
             case Id_atan2:
                 x = ScriptRuntime.toNumber(args, 0);
                 x = Math.atan2(x, ScriptRuntime.toNumber(args, 1));
@@ -145,11 +318,20 @@
                 x = Math.ceil(x);
                 break;
 
+            case Id_clz32:
+                x = ScriptRuntime.toNumber(args, 0);
+                if (x == 0 || Double.isNaN(x) || Double.isInfinite(x)) {
+                    return Double32;
+                }
+                long n = ScriptRuntime.toUint32(x);
+                if (n == 0) {
+                    return Double32;
+                }
+                return Double.valueOf(31 - Math.floor(Math.log(n >>> 0) * LOG2E));
+
             case Id_cos:
                 x = ScriptRuntime.toNumber(args, 0);
-                x = (x == Double.POSITIVE_INFINITY
-                     || x == Double.NEGATIVE_INFINITY)
-                    ? Double.NaN : Math.cos(x);
+                x = Double.isInfinite(x) ? Double.NaN : Math.cos(x);
                 break;
 
             case Id_cosh:
@@ -163,9 +345,10 @@
 
             case Id_exp:
                 x = ScriptRuntime.toNumber(args, 0);
-                x = (x == Double.POSITIVE_INFINITY) ? x
-                    : (x == Double.NEGATIVE_INFINITY) ? 0.0
-                    : Math.exp(x);
+                x =
+                        (x == Double.POSITIVE_INFINITY)
+                                ? x
+                                : (x == Double.NEGATIVE_INFINITY) ? 0.0 : Math.exp(x);
                 break;
 
             case Id_expm1:
@@ -178,8 +361,15 @@
                 x = Math.floor(x);
                 break;
 
+            case Id_fround:
+                x = ScriptRuntime.toNumber(args, 0);
+                // Rely on Java to truncate down to a "float" here"
+                x = (float) x;
+                break;
+
             case Id_imul:
-                return js_imul(args);
+                x = js_imul(args);
+                break;
 
             case Id_log:
                 x = ScriptRuntime.toNumber(args, 0);
@@ -197,16 +387,17 @@
                 x = Math.log10(x);
                 break;
 
+            case Id_log2:
+                x = ScriptRuntime.toNumber(args, 0);
+                // Java's log(<0) = -Infinity; we need NaN
+                x = (x < 0) ? Double.NaN : Math.log(x) * LOG2E;
+                break;
+
             case Id_max:
             case Id_min:
-                x = (methodId == Id_max)
-                    ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
+                x = (methodId == Id_max) ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
                 for (int i = 0; i != args.length; ++i) {
                     double d = ScriptRuntime.toNumber(args[i]);
-                    if (d != d) {
-                        x = d; // NaN
-                        break;
-                    }
                     if (methodId == Id_max) {
                         // if (x < d) x = d; does not work due to -0.0 >= +0.0
                         x = Math.max(x, d);
@@ -227,9 +418,7 @@
 
             case Id_round:
                 x = ScriptRuntime.toNumber(args, 0);
-                if (x == x && x != Double.POSITIVE_INFINITY
-                    && x != Double.NEGATIVE_INFINITY)
-                {
+                if (!Double.isNaN(x) && !Double.isInfinite(x)) {
                     // Round only finite x
                     long l = Math.round(x);
                     if (l != 0) {
@@ -245,11 +434,22 @@
                 }
                 break;
 
+            case Id_sign:
+                x = ScriptRuntime.toNumber(args, 0);
+                if (!Double.isNaN(x)) {
+                    if (x == 0) {
+                        if (1 / x > 0) {
+                            return ScriptRuntime.zeroObj;
+                        }
+                        return ScriptRuntime.negativeZeroObj;
+                    }
+                    return Double.valueOf(Math.signum(x));
+                }
+                return ScriptRuntime.NaNobj;
+
             case Id_sin:
                 x = ScriptRuntime.toNumber(args, 0);
-                x = (x == Double.POSITIVE_INFINITY
-                     || x == Double.NEGATIVE_INFINITY)
-                    ? Double.NaN : Math.sin(x);
+                x = Double.isInfinite(x) ? Double.NaN : Math.sin(x);
                 break;
 
             case Id_sinh:
@@ -277,15 +477,16 @@
                 x = js_trunc(x);
                 break;
 
-            default: throw new IllegalStateException(String.valueOf(methodId));
+            default:
+                throw new IllegalStateException(String.valueOf(methodId));
         }
         return ScriptRuntime.wrapNumber(x);
     }
 
     // See Ecma 15.8.2.13
-    private double js_pow(double x, double y) {
+    private static double js_pow(double x, double y) {
         double result;
-        if (y != y) {
+        if (Double.isNaN(y)) {
             // y is NaN, result is always NaN
             result = y;
         } else if (y == 0) {
@@ -297,7 +498,7 @@
                 result = (y > 0) ? 0 : Double.POSITIVE_INFINITY;
             } else {
                 // x is -0, need to check if y is an odd integer
-                long y_long = (long)y;
+                long y_long = (long) y;
                 if (y_long == y && (y_long & 0x1) != 0) {
                     result = (y > 0) ? -0.0 : Double.NEGATIVE_INFINITY;
                 } else {
@@ -306,7 +507,7 @@
             }
         } else {
             result = Math.pow(x, y);
-            if (result != result) {
+            if (Double.isNaN(result)) {
                 // Check for broken Java implementations that gives NaN
                 // when they should return something else
                 if (y == Double.POSITIVE_INFINITY) {
@@ -324,7 +525,7 @@
                 } else if (x == Double.POSITIVE_INFINITY) {
                     result = (y > 0) ? Double.POSITIVE_INFINITY : 0.0;
                 } else if (x == Double.NEGATIVE_INFINITY) {
-                    long y_long = (long)y;
+                    long y_long = (long) y;
                     if (y_long == y && (y_long & 0x1) != 0) {
                         // y is odd integer
                         result = (y > 0) ? Double.NEGATIVE_INFINITY : -0.0;
@@ -337,165 +538,247 @@
         return result;
     }
 
-    // Based on code from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/hypot
-    private double js_hypot(Object[] args)
-    {
+    // Based on code from
+    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/hypot
+    private static double js_hypot(Object[] args) {
         if (args == null) {
             return 0.0;
         }
         double y = 0.0;
 
+        // Spec and tests say that any "Infinity" result takes precedence.
+        boolean hasNaN = false;
+        boolean hasInfinity = false;
+
         for (Object o : args) {
             double d = ScriptRuntime.toNumber(o);
-            if (d == ScriptRuntime.NaN) {
-                return d;
-            }
-            if ((d == Double.POSITIVE_INFINITY) || (d == Double.NEGATIVE_INFINITY)) {
-                return Double.POSITIVE_INFINITY;
+            if (Double.isNaN(d)) {
+                hasNaN = true;
+            } else if (Double.isInfinite(d)) {
+                hasInfinity = true;
+            } else {
+                y += d * d;
             }
-            y += d * d;
+        }
+
+        if (hasInfinity) {
+            return Double.POSITIVE_INFINITY;
+        }
+        if (hasNaN) {
+            return Double.NaN;
         }
         return Math.sqrt(y);
     }
 
-    private double js_trunc(double d)
-    {
+    private static double js_trunc(double d) {
         return ((d < 0.0) ? Math.ceil(d) : Math.floor(d));
     }
 
     // From EcmaScript 6 section 20.2.2.19
-    private Object js_imul(Object[] args)
-    {
-        if ((args == null) || (args.length < 2)) {
-            return ScriptRuntime.wrapNumber(ScriptRuntime.NaN);
+    private static int js_imul(Object[] args) {
+        if (args == null) {
+            return 0;
         }
 
-        long x = Conversions.toUint32(args[0]);
-        long y = Conversions.toUint32(args[1]);
-        long product = (x * y) % Conversions.THIRTYTWO_BIT;
-        long result = (product >= (1L << 31L)) ? (product - Conversions.THIRTYTWO_BIT) : product;
-        return ScriptRuntime.toNumber(result);
+        int x = ScriptRuntime.toInt32(args, 0);
+        int y = ScriptRuntime.toInt32(args, 1);
+        return x * y;
     }
 
-// #string_id_map#
-
     @Override
-    protected int findPrototypeId(String s)
-    {
+    protected int findPrototypeId(String s) {
         int id;
-// #generated# Last update: 2015-06-16 09:41:02 PDT
-        L0: { id = 0; String X = null; int c;
-            L: switch (s.length()) {
-            case 1: if (s.charAt(0)=='E') {id=Id_E; break L0;} break L;
-            case 2: if (s.charAt(0)=='P' && s.charAt(1)=='I') {id=Id_PI; break L0;} break L;
-            case 3: switch (s.charAt(0)) {
-                case 'L': if (s.charAt(2)=='2' && s.charAt(1)=='N') {id=Id_LN2; break L0;} break L;
-                case 'a': if (s.charAt(2)=='s' && s.charAt(1)=='b') {id=Id_abs; break L0;} break L;
-                case 'c': if (s.charAt(2)=='s' && s.charAt(1)=='o') {id=Id_cos; break L0;} break L;
-                case 'e': if (s.charAt(2)=='p' && s.charAt(1)=='x') {id=Id_exp; break L0;} break L;
-                case 'l': if (s.charAt(2)=='g' && s.charAt(1)=='o') {id=Id_log; break L0;} break L;
-                case 'm': c=s.charAt(2);
-                    if (c=='n') { if (s.charAt(1)=='i') {id=Id_min; break L0;} }
-                    else if (c=='x') { if (s.charAt(1)=='a') {id=Id_max; break L0;} }
-                    break L;
-                case 'p': if (s.charAt(2)=='w' && s.charAt(1)=='o') {id=Id_pow; break L0;} break L;
-                case 's': if (s.charAt(2)=='n' && s.charAt(1)=='i') {id=Id_sin; break L0;} break L;
-                case 't': if (s.charAt(2)=='n' && s.charAt(1)=='a') {id=Id_tan; break L0;} break L;
-                } break L;
-            case 4: switch (s.charAt(1)) {
-                case 'N': X="LN10";id=Id_LN10; break L;
-                case 'a': X="tanh";id=Id_tanh; break L;
-                case 'b': X="cbrt";id=Id_cbrt; break L;
-                case 'c': X="acos";id=Id_acos; break L;
-                case 'e': X="ceil";id=Id_ceil; break L;
-                case 'i': X="sinh";id=Id_sinh; break L;
-                case 'm': X="imul";id=Id_imul; break L;
-                case 'o': X="cosh";id=Id_cosh; break L;
-                case 'q': X="sqrt";id=Id_sqrt; break L;
-                case 's': X="asin";id=Id_asin; break L;
-                case 't': X="atan";id=Id_atan; break L;
-                } break L;
-            case 5: switch (s.charAt(0)) {
-                case 'L': X="LOG2E";id=Id_LOG2E; break L;
-                case 'S': X="SQRT2";id=Id_SQRT2; break L;
-                case 'a': X="atan2";id=Id_atan2; break L;
-                case 'e': X="expm1";id=Id_expm1; break L;
-                case 'f': X="floor";id=Id_floor; break L;
-                case 'h': X="hypot";id=Id_hypot; break L;
-                case 'l': c=s.charAt(4);
-                    if (c=='0') { X="log10";id=Id_log10; }
-                    else if (c=='p') { X="log1p";id=Id_log1p; }
-                    break L;
-                case 'r': X="round";id=Id_round; break L;
-                case 't': X="trunc";id=Id_trunc; break L;
-                } break L;
-            case 6: c=s.charAt(0);
-                if (c=='L') { X="LOG10E";id=Id_LOG10E; }
-                else if (c=='r') { X="random";id=Id_random; }
-                break L;
-            case 7: X="SQRT1_2";id=Id_SQRT1_2; break L;
-            case 8: X="toSource";id=Id_toSource; break L;
-            }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-            break L0;
+        switch (s) {
+            case "toSource":
+                id = Id_toSource;
+                break;
+            case "abs":
+                id = Id_abs;
+                break;
+            case "acos":
+                id = Id_acos;
+                break;
+            case "asin":
+                id = Id_asin;
+                break;
+            case "atan":
+                id = Id_atan;
+                break;
+            case "atan2":
+                id = Id_atan2;
+                break;
+            case "ceil":
+                id = Id_ceil;
+                break;
+            case "cos":
+                id = Id_cos;
+                break;
+            case "exp":
+                id = Id_exp;
+                break;
+            case "floor":
+                id = Id_floor;
+                break;
+            case "log":
+                id = Id_log;
+                break;
+            case "max":
+                id = Id_max;
+                break;
+            case "min":
+                id = Id_min;
+                break;
+            case "pow":
+                id = Id_pow;
+                break;
+            case "random":
+                id = Id_random;
+                break;
+            case "round":
+                id = Id_round;
+                break;
+            case "sin":
+                id = Id_sin;
+                break;
+            case "sqrt":
+                id = Id_sqrt;
+                break;
+            case "tan":
+                id = Id_tan;
+                break;
+            case "cbrt":
+                id = Id_cbrt;
+                break;
+            case "cosh":
+                id = Id_cosh;
+                break;
+            case "expm1":
+                id = Id_expm1;
+                break;
+            case "hypot":
+                id = Id_hypot;
+                break;
+            case "log1p":
+                id = Id_log1p;
+                break;
+            case "log10":
+                id = Id_log10;
+                break;
+            case "sinh":
+                id = Id_sinh;
+                break;
+            case "tanh":
+                id = Id_tanh;
+                break;
+            case "imul":
+                id = Id_imul;
+                break;
+            case "trunc":
+                id = Id_trunc;
+                break;
+            case "acosh":
+                id = Id_acosh;
+                break;
+            case "asinh":
+                id = Id_asinh;
+                break;
+            case "atanh":
+                id = Id_atanh;
+                break;
+            case "sign":
+                id = Id_sign;
+                break;
+            case "log2":
+                id = Id_log2;
+                break;
+            case "fround":
+                id = Id_fround;
+                break;
+            case "clz32":
+                id = Id_clz32;
+                break;
+            case "E":
+                id = Id_E;
+                break;
+            case "PI":
+                id = Id_PI;
+                break;
+            case "LN10":
+                id = Id_LN10;
+                break;
+            case "LN2":
+                id = Id_LN2;
+                break;
+            case "LOG2E":
+                id = Id_LOG2E;
+                break;
+            case "LOG10E":
+                id = Id_LOG10E;
+                break;
+            case "SQRT1_2":
+                id = Id_SQRT1_2;
+                break;
+            case "SQRT2":
+                id = Id_SQRT2;
+                break;
+            default:
+                id = 0;
+                break;
         }
-// #/generated#
         return id;
     }
 
-    private static final int
-        Id_toSource     =  1,
-        Id_abs          =  2,
-        Id_acos         =  3,
-        Id_asin         =  4,
-        Id_atan         =  5,
-        Id_atan2        =  6,
-        Id_ceil         =  7,
-        Id_cos          =  8,
-        Id_exp          =  9,
-        Id_floor        = 10,
-        Id_log          = 11,
-        Id_max          = 12,
-        Id_min          = 13,
-        Id_pow          = 14,
-        Id_random       = 15,
-        Id_round        = 16,
-        Id_sin          = 17,
-        Id_sqrt         = 18,
-        Id_tan          = 19,
-        Id_cbrt         = 20,
-        Id_cosh         = 21,
-        Id_expm1        = 22,
-        Id_hypot        = 23,
-        Id_log1p        = 24,
-        Id_log10        = 25,
-        Id_sinh         = 26,
-        Id_tanh         = 27,
-        Id_imul         = 28,
-        Id_trunc        = 29,
-
-        LAST_METHOD_ID  = Id_trunc;
-
-/* Missing from ES6:
-    acosh
-    asinh
-    atanh
-    clz32
-    fround
-    log2
- */
-
-    private static final int
-        Id_E            = LAST_METHOD_ID + 1,
-        Id_PI           = LAST_METHOD_ID + 2,
-        Id_LN10         = LAST_METHOD_ID + 3,
-        Id_LN2          = LAST_METHOD_ID + 4,
-        Id_LOG2E        = LAST_METHOD_ID + 5,
-        Id_LOG10E       = LAST_METHOD_ID + 6,
-        Id_SQRT1_2      = LAST_METHOD_ID + 7,
-        Id_SQRT2        = LAST_METHOD_ID + 8,
-
-        MAX_ID = LAST_METHOD_ID + 8;
-
-// #/string_id_map#
+    private static final int Id_toSource = 1,
+            Id_abs = 2,
+            Id_acos = 3,
+            Id_asin = 4,
+            Id_atan = 5,
+            Id_atan2 = 6,
+            Id_ceil = 7,
+            Id_cos = 8,
+            Id_exp = 9,
+            Id_floor = 10,
+            Id_log = 11,
+            Id_max = 12,
+            Id_min = 13,
+            Id_pow = 14,
+            Id_random = 15,
+            Id_round = 16,
+            Id_sin = 17,
+            Id_sqrt = 18,
+            Id_tan = 19,
+            Id_cbrt = 20,
+            Id_cosh = 21,
+            Id_expm1 = 22,
+            Id_hypot = 23,
+            Id_log1p = 24,
+            Id_log10 = 25,
+            Id_sinh = 26,
+            Id_tanh = 27,
+            Id_imul = 28,
+            Id_trunc = 29,
+            Id_acosh = 30,
+            Id_asinh = 31,
+            Id_atanh = 32,
+            Id_sign = 33,
+            Id_log2 = 34,
+            Id_fround = 35,
+            Id_clz32 = 36,
+            LAST_METHOD_ID = Id_clz32;
+
+    /* Missing from ES6:
+       clz32
+       fround
+       log2
+    */
+
+    private static final int Id_E = LAST_METHOD_ID + 1,
+            Id_PI = LAST_METHOD_ID + 2,
+            Id_LN10 = LAST_METHOD_ID + 3,
+            Id_LN2 = LAST_METHOD_ID + 4,
+            Id_LOG2E = LAST_METHOD_ID + 5,
+            Id_LOG10E = LAST_METHOD_ID + 6,
+            Id_SQRT1_2 = LAST_METHOD_ID + 7,
+            Id_SQRT2 = LAST_METHOD_ID + 8,
+            MAX_ID = LAST_METHOD_ID + 8;
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeNumber.java rhino-1.7.14/src/org/mozilla/javascript/NativeNumber.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeNumber.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeNumber.java	2022-01-06 22:57:21.000000000 +0100
@@ -9,104 +9,120 @@
 /**
  * This class implements the Number native object.
  *
- * See ECMA 15.7.
+ * <p>See ECMA 15.7.
  *
  * @author Norris Boyd
  */
-final class NativeNumber extends IdScriptableObject
-{
-    static final long serialVersionUID = 3504516769741512101L;
+final class NativeNumber extends IdScriptableObject {
+    private static final long serialVersionUID = 3504516769741512101L;
+
+    /** @see https://www.ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer */
+    public static final double MAX_SAFE_INTEGER = 9007199254740991.0; // Math.pow(2, 53) - 1
 
     private static final Object NUMBER_TAG = "Number";
 
     private static final int MAX_PRECISION = 100;
-    private static final double MAX_SAFE_INTEGER = Math.pow(2, 53) - 1;
     private static final double MIN_SAFE_INTEGER = -MAX_SAFE_INTEGER;
+    private static final double EPSILON = 2.220446049250313e-16; // Math.pow(2, -52)
 
-    static void init(Scriptable scope, boolean sealed)
-    {
+    static void init(Scriptable scope, boolean sealed) {
         NativeNumber obj = new NativeNumber(0.0);
         obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
     }
 
-    NativeNumber(double number)
-    {
+    NativeNumber(double number) {
         doubleValue = number;
     }
 
     @Override
-    public String getClassName()
-    {
+    public String getClassName() {
         return "Number";
     }
 
     @Override
-    protected void fillConstructorProperties(IdFunctionObject ctor)
-    {
-        final int attr = ScriptableObject.DONTENUM |
-                         ScriptableObject.PERMANENT |
-                         ScriptableObject.READONLY;
+    protected void fillConstructorProperties(IdFunctionObject ctor) {
+        final int attr =
+                ScriptableObject.DONTENUM | ScriptableObject.PERMANENT | ScriptableObject.READONLY;
 
         ctor.defineProperty("NaN", ScriptRuntime.NaNobj, attr);
-        ctor.defineProperty("POSITIVE_INFINITY",
-                            ScriptRuntime.wrapNumber(Double.POSITIVE_INFINITY),
-                            attr);
-        ctor.defineProperty("NEGATIVE_INFINITY",
-                            ScriptRuntime.wrapNumber(Double.NEGATIVE_INFINITY),
-                            attr);
-        ctor.defineProperty("MAX_VALUE",
-                            ScriptRuntime.wrapNumber(Double.MAX_VALUE),
-                            attr);
-        ctor.defineProperty("MIN_VALUE",
-                            ScriptRuntime.wrapNumber(Double.MIN_VALUE),
-                            attr);
-        ctor.defineProperty("MAX_SAFE_INTEGER",
-                            ScriptRuntime.wrapNumber(MAX_SAFE_INTEGER),
-                            attr);
-        ctor.defineProperty("MIN_SAFE_INTEGER",
-                            ScriptRuntime.wrapNumber(MIN_SAFE_INTEGER),
-                            attr);
+        ctor.defineProperty(
+                "POSITIVE_INFINITY", ScriptRuntime.wrapNumber(Double.POSITIVE_INFINITY), attr);
+        ctor.defineProperty(
+                "NEGATIVE_INFINITY", ScriptRuntime.wrapNumber(Double.NEGATIVE_INFINITY), attr);
+        ctor.defineProperty("MAX_VALUE", ScriptRuntime.wrapNumber(Double.MAX_VALUE), attr);
+        ctor.defineProperty("MIN_VALUE", ScriptRuntime.wrapNumber(Double.MIN_VALUE), attr);
+        ctor.defineProperty("MAX_SAFE_INTEGER", ScriptRuntime.wrapNumber(MAX_SAFE_INTEGER), attr);
+        ctor.defineProperty("MIN_SAFE_INTEGER", ScriptRuntime.wrapNumber(MIN_SAFE_INTEGER), attr);
+        ctor.defineProperty("EPSILON", ScriptRuntime.wrapNumber(EPSILON), attr);
 
         addIdFunctionProperty(ctor, NUMBER_TAG, ConstructorId_isFinite, "isFinite", 1);
         addIdFunctionProperty(ctor, NUMBER_TAG, ConstructorId_isNaN, "isNaN", 1);
         addIdFunctionProperty(ctor, NUMBER_TAG, ConstructorId_isInteger, "isInteger", 1);
         addIdFunctionProperty(ctor, NUMBER_TAG, ConstructorId_isSafeInteger, "isSafeInteger", 1);
-        addIdFunctionProperty(ctor, NUMBER_TAG, ConstructorId_parseFloat, "parseFloat", 1);
-        addIdFunctionProperty(ctor, NUMBER_TAG, ConstructorId_parseInt, "parseInt", 1);
+        Object parseFloat = ScriptRuntime.getTopLevelProp(ctor, "parseFloat");
+        if (parseFloat instanceof IdFunctionObject) {
+            ((IdFunctionObject) parseFloat).addAsProperty(ctor);
+        }
+        Object parseInt = ScriptRuntime.getTopLevelProp(ctor, "parseInt");
+        if (parseInt instanceof IdFunctionObject) {
+            ((IdFunctionObject) parseInt).addAsProperty(ctor);
+        }
 
         super.fillConstructorProperties(ctor);
     }
 
     @Override
-    protected void initPrototypeId(int id)
-    {
+    protected void initPrototypeId(int id) {
         String s;
         int arity;
         switch (id) {
-          case Id_constructor:    arity=1; s="constructor";    break;
-          case Id_toString:       arity=1; s="toString";       break;
-          case Id_toLocaleString: arity=1; s="toLocaleString"; break;
-          case Id_toSource:       arity=0; s="toSource";       break;
-          case Id_valueOf:        arity=0; s="valueOf";        break;
-          case Id_toFixed:        arity=1; s="toFixed";        break;
-          case Id_toExponential:  arity=1; s="toExponential";  break;
-          case Id_toPrecision:    arity=1; s="toPrecision";    break;
-          default: throw new IllegalArgumentException(String.valueOf(id));
+            case Id_constructor:
+                arity = 1;
+                s = "constructor";
+                break;
+            case Id_toString:
+                arity = 1;
+                s = "toString";
+                break;
+            case Id_toLocaleString:
+                arity = 1;
+                s = "toLocaleString";
+                break;
+            case Id_toSource:
+                arity = 0;
+                s = "toSource";
+                break;
+            case Id_valueOf:
+                arity = 0;
+                s = "valueOf";
+                break;
+            case Id_toFixed:
+                arity = 1;
+                s = "toFixed";
+                break;
+            case Id_toExponential:
+                arity = 1;
+                s = "toExponential";
+                break;
+            case Id_toPrecision:
+                arity = 1;
+                s = "toPrecision";
+                break;
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
         }
         initPrototypeMethod(NUMBER_TAG, id, s, arity);
     }
 
     @Override
-    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
-                             Scriptable thisObj, Object[] args)
-    {
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         if (!f.hasTag(NUMBER_TAG)) {
             return super.execIdCall(f, cx, scope, thisObj, args);
         }
         int id = f.methodId();
         if (id == Id_constructor) {
-            double val = (args.length >= 1)
-                ? ScriptRuntime.toNumber(args[0]) : 0.0;
+            double val = (args.length >= 1) ? ScriptRuntime.toNumeric(args[0]).doubleValue() : 0.0;
             if (thisObj == null) {
                 // new Number(val) creates a new Number object.
                 return new NativeNumber(val);
@@ -119,123 +135,117 @@
         }
 
         // The rest of Number.prototype methods require thisObj to be Number
-
-        if (!(thisObj instanceof NativeNumber))
-            throw incompatibleCallError(f);
-        double value = ((NativeNumber)thisObj).doubleValue;
+        double value = ensureType(thisObj, NativeNumber.class, f).doubleValue;
 
         switch (id) {
+            case Id_toString:
+            case Id_toLocaleString:
+                {
+                    // toLocaleString is just an alias for toString for now
+                    int base =
+                            (args.length == 0 || Undefined.isUndefined(args[0]))
+                                    ? 10
+                                    : ScriptRuntime.toInt32(args[0]);
+                    return ScriptRuntime.numberToString(value, base);
+                }
+
+            case Id_toSource:
+                return "(new Number(" + ScriptRuntime.toString(value) + "))";
+
+            case Id_valueOf:
+                return ScriptRuntime.wrapNumber(value);
+
+            case Id_toFixed:
+                int precisionMin = cx.version < Context.VERSION_ES6 ? -20 : 0;
+                return num_to(value, args, DToA.DTOSTR_FIXED, DToA.DTOSTR_FIXED, precisionMin, 0);
+
+            case Id_toExponential:
+                {
+                    // Handle special values before range check
+                    if (Double.isNaN(value)) {
+                        return "NaN";
+                    }
+                    if (Double.isInfinite(value)) {
+                        if (value >= 0) {
+                            return "Infinity";
+                        }
+                        return "-Infinity";
+                    }
+                    // General case
+                    return num_to(
+                            value,
+                            args,
+                            DToA.DTOSTR_STANDARD_EXPONENTIAL,
+                            DToA.DTOSTR_EXPONENTIAL,
+                            0,
+                            1);
+                }
+
+            case Id_toPrecision:
+                {
+                    // Undefined precision, fall back to ToString()
+                    if (args.length == 0 || Undefined.isUndefined(args[0])) {
+                        return ScriptRuntime.numberToString(value, 10);
+                    }
+                    // Handle special values before range check
+                    if (Double.isNaN(value)) {
+                        return "NaN";
+                    }
+                    if (Double.isInfinite(value)) {
+                        if (value >= 0) {
+                            return "Infinity";
+                        }
+                        return "-Infinity";
+                    }
+                    return num_to(value, args, DToA.DTOSTR_STANDARD, DToA.DTOSTR_PRECISION, 1, 0);
+                }
 
-          case Id_toString:
-          case Id_toLocaleString:
-            {
-                // toLocaleString is just an alias for toString for now
-                int base = (args.length == 0 || args[0] == Undefined.instance)
-                    ? 10 : ScriptRuntime.toInt32(args[0]);
-                return ScriptRuntime.numberToString(value, base);
-            }
-
-          case Id_toSource:
-            return "(new Number("+ScriptRuntime.toString(value)+"))";
-
-          case Id_valueOf:
-            return ScriptRuntime.wrapNumber(value);
-
-          case Id_toFixed:
-            return num_to(value, args, DToA.DTOSTR_FIXED,
-                          DToA.DTOSTR_FIXED, -20, 0);
-
-          case Id_toExponential: {
-              // Handle special values before range check
-              if(Double.isNaN(value)) {
-                  return "NaN";
-              }
-              if(Double.isInfinite(value)) {
-                  if(value >= 0) {
-                      return "Infinity";
-                  }
-                  else {
-                      return "-Infinity";
-                  }
-              }
-              // General case
-              return num_to(value, args, DToA.DTOSTR_STANDARD_EXPONENTIAL,
-                      DToA.DTOSTR_EXPONENTIAL, 0, 1);
-          }
-
-          case Id_toPrecision: {
-              // Undefined precision, fall back to ToString()
-              if(args.length == 0 || args[0] == Undefined.instance) {
-                  return ScriptRuntime.numberToString(value, 10);
-              }
-              // Handle special values before range check
-              if(Double.isNaN(value)) {
-                  return "NaN";
-              }
-              if(Double.isInfinite(value)) {
-                  if(value >= 0) {
-                      return "Infinity";
-                  }
-                  else {
-                      return "-Infinity";
-                  }
-              }
-              return num_to(value, args, DToA.DTOSTR_STANDARD,
-                      DToA.DTOSTR_PRECISION, 1, 0);
-          }
-
-          default: throw new IllegalArgumentException(String.valueOf(id));
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
         }
     }
 
-    private Object execConstructorCall(int id, Object[] args)
-    {
+    private static Object execConstructorCall(int id, Object[] args) {
         switch (id) {
-        case ConstructorId_isFinite:
-            if ((args.length == 0) || (Undefined.instance == args[0])) {
-                return false;
-            }
-            if (args[0] instanceof Number) {
-                // Match ES6 polyfill, which only works for "number" types
-                return isFinite(args[0]);
-            }
-            return false;
-
-        case ConstructorId_isNaN:
-            if ((args.length == 0) || (Undefined.instance == args[0])) {
-                return false;
-            }
-            if (args[0] instanceof Number) {
-                return isNaN((Number)args[0]);
-            }
-            return false;
+            case ConstructorId_isFinite:
+                if ((args.length == 0) || Undefined.isUndefined(args[0])) {
+                    return Boolean.FALSE;
+                }
+                if (args[0] instanceof Number) {
+                    // Match ES6 polyfill, which only works for "number" types
+                    return isFinite(args[0]);
+                }
+                return Boolean.FALSE;
+
+            case ConstructorId_isNaN:
+                if ((args.length == 0) || Undefined.isUndefined(args[0])) {
+                    return Boolean.FALSE;
+                }
+                if (args[0] instanceof Number) {
+                    return isNaN((Number) args[0]);
+                }
+                return Boolean.FALSE;
+
+            case ConstructorId_isInteger:
+                if ((args.length == 0) || Undefined.isUndefined(args[0])) {
+                    return Boolean.FALSE;
+                }
+                if (args[0] instanceof Number) {
+                    return Boolean.valueOf(isInteger((Number) args[0]));
+                }
+                return Boolean.FALSE;
+
+            case ConstructorId_isSafeInteger:
+                if ((args.length == 0) || (Undefined.instance == args[0])) {
+                    return Boolean.FALSE;
+                }
+                if (args[0] instanceof Number) {
+                    return Boolean.valueOf(isSafeInteger((Number) args[0]));
+                }
+                return Boolean.FALSE;
 
-        case ConstructorId_isInteger:
-            if ((args.length == 0) || (Undefined.instance == args[0])) {
-                return false;
-            }
-            if (args[0] instanceof Number) {
-                return isInteger((Number)args[0]);
-            }
-            return false;
-
-        case ConstructorId_isSafeInteger:
-            if ((args.length == 0) || (Undefined.instance == args[0])) {
-                return false;
-            }
-            if (args[0] instanceof Number) {
-                return isSafeInteger((Number)args[0]);
-            }
-            return false;
-
-        case ConstructorId_parseFloat:
-            return NativeGlobal.js_parseFloat(args);
-
-        case ConstructorId_parseInt:
-            return NativeGlobal.js_parseInt(args);
-
-        default:
-            throw new IllegalArgumentException(String.valueOf(id));
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
         }
     }
 
@@ -244,23 +254,26 @@
         return ScriptRuntime.numberToString(doubleValue, 10);
     }
 
-    private static String num_to(double val,
-                                 Object[] args,
-                                 int zeroArgMode, int oneArgMode,
-                                 int precisionMin, int precisionOffset)
-    {
+    private static String num_to(
+            double val,
+            Object[] args,
+            int zeroArgMode,
+            int oneArgMode,
+            int precisionMin,
+            int precisionOffset) {
         int precision;
         if (args.length == 0) {
             precision = 0;
             oneArgMode = zeroArgMode;
         } else {
             /* We allow a larger range of precision than
-               ECMA requires; this is permitted by ECMA. */
+            ECMA requires; this is permitted by ECMA. */
             double p = ScriptRuntime.toInteger(args[0]);
             if (p < precisionMin || p > MAX_PRECISION) {
-                String msg = ScriptRuntime.getMessage1(
-                    "msg.bad.precision", ScriptRuntime.toString(args[0]));
-                throw ScriptRuntime.constructError("RangeError", msg);
+                String msg =
+                        ScriptRuntime.getMessageById(
+                                "msg.bad.precision", ScriptRuntime.toString(args[0]));
+                throw ScriptRuntime.rangeError(msg);
             }
             precision = ScriptRuntime.toInt32(p);
         }
@@ -269,109 +282,101 @@
         return sb.toString();
     }
 
-    static Object isFinite(Object val)
-    {
+    static Object isFinite(Object val) {
         double d = ScriptRuntime.toNumber(val);
         Double nd = Double.valueOf(d);
         return ScriptRuntime.wrapBoolean(!nd.isInfinite() && !nd.isNaN());
     }
 
-    private Object isNaN(Number val)
-    {
-        Double nd = doubleVal(val);
-        return ScriptRuntime.toBoolean(isDoubleNan(nd));
-    }
-
-    private boolean isDoubleNan(Double d)
-    {
-        return d.isNaN();
-    }
+    private static Boolean isNaN(Number val) {
+        if (val instanceof Double) {
+            return Boolean.valueOf(((Double) val).isNaN());
+        }
 
-    private boolean isInteger(Number val)
-    {
-        Double nd = doubleVal(val);
-        return ScriptRuntime.toBoolean(isDoubleInteger(nd));
+        double d = val.doubleValue();
+        return Boolean.valueOf(Double.isNaN(d));
     }
 
-    private boolean isDoubleInteger(Double d)
-    {
-        return (!d.isInfinite() && !d.isNaN() &&
-                (Math.floor(d.doubleValue()) == d.doubleValue()));
+    private static boolean isInteger(Number val) {
+        if (val instanceof Double) {
+            return isDoubleInteger((Double) val);
+        }
+        return isDoubleInteger(val.doubleValue());
     }
 
-    private boolean isSafeInteger(Number val)
-    {
-        Double nd = doubleVal(val);
-        return ScriptRuntime.toBoolean(isDoubleSafeInteger(nd));
+    private static boolean isDoubleInteger(Double d) {
+        return !d.isInfinite() && !d.isNaN() && (Math.floor(d.doubleValue()) == d.doubleValue());
     }
 
-    private boolean isDoubleSafeInteger(Double d)
-    {
-        return (isDoubleInteger(d) &&
-                (d.doubleValue() <= MAX_SAFE_INTEGER) &&
-                (d.doubleValue() >= MIN_SAFE_INTEGER));
+    private static boolean isDoubleInteger(double d) {
+        return !Double.isInfinite(d) && !Double.isNaN(d) && (Math.floor(d) == d);
     }
 
-    private Double doubleVal(Number val)
-    {
+    private static boolean isSafeInteger(Number val) {
         if (val instanceof Double) {
-            return (Double)val;
-        } else {
-            double d = val.doubleValue();
-            return Double.valueOf(d);
+            return isDoubleSafeInteger((Double) val);
         }
+        return isDoubleSafeInteger(val.doubleValue());
+    }
+
+    private static boolean isDoubleSafeInteger(Double d) {
+        return isDoubleInteger(d)
+                && (d.doubleValue() <= MAX_SAFE_INTEGER)
+                && (d.doubleValue() >= MIN_SAFE_INTEGER);
     }
 
-// #string_id_map#
+    private static boolean isDoubleSafeInteger(double d) {
+        return isDoubleInteger(d) && (d <= MAX_SAFE_INTEGER) && (d >= MIN_SAFE_INTEGER);
+    }
 
     @Override
-    protected int findPrototypeId(String s)
-    {
+    protected int findPrototypeId(String s) {
         int id;
-// #generated# Last update: 2007-05-09 08:15:50 EDT
-        L0: { id = 0; String X = null; int c;
-            L: switch (s.length()) {
-            case 7: c=s.charAt(0);
-                if (c=='t') { X="toFixed";id=Id_toFixed; }
-                else if (c=='v') { X="valueOf";id=Id_valueOf; }
-                break L;
-            case 8: c=s.charAt(3);
-                if (c=='o') { X="toSource";id=Id_toSource; }
-                else if (c=='t') { X="toString";id=Id_toString; }
-                break L;
-            case 11: c=s.charAt(0);
-                if (c=='c') { X="constructor";id=Id_constructor; }
-                else if (c=='t') { X="toPrecision";id=Id_toPrecision; }
-                break L;
-            case 13: X="toExponential";id=Id_toExponential; break L;
-            case 14: X="toLocaleString";id=Id_toLocaleString; break L;
-            }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-            break L0;
+        switch (s) {
+            case "constructor":
+                id = Id_constructor;
+                break;
+            case "toString":
+                id = Id_toString;
+                break;
+            case "toLocaleString":
+                id = Id_toLocaleString;
+                break;
+            case "toSource":
+                id = Id_toSource;
+                break;
+            case "valueOf":
+                id = Id_valueOf;
+                break;
+            case "toFixed":
+                id = Id_toFixed;
+                break;
+            case "toExponential":
+                id = Id_toExponential;
+                break;
+            case "toPrecision":
+                id = Id_toPrecision;
+                break;
+            default:
+                id = 0;
+                break;
         }
-// #/generated#
         return id;
     }
 
-    private static final int
-        ConstructorId_isFinite       = -1,
-        ConstructorId_isNaN          = -2,
-        ConstructorId_isInteger      = -3,
-        ConstructorId_isSafeInteger  = -4,
-        ConstructorId_parseFloat     = -5,
-        ConstructorId_parseInt       = -6,
-
-        Id_constructor           = 1,
-        Id_toString              = 2,
-        Id_toLocaleString        = 3,
-        Id_toSource              = 4,
-        Id_valueOf               = 5,
-        Id_toFixed               = 6,
-        Id_toExponential         = 7,
-        Id_toPrecision           = 8,
-        MAX_PROTOTYPE_ID         = 8;
-
-// #/string_id_map#
+    private static final int ConstructorId_isFinite = -1,
+            ConstructorId_isNaN = -2,
+            ConstructorId_isInteger = -3,
+            ConstructorId_isSafeInteger = -4,
+            Id_constructor = 1,
+            Id_toString = 2,
+            Id_toLocaleString = 3,
+            Id_toSource = 4,
+            Id_valueOf = 5,
+            Id_toFixed = 6,
+            Id_toExponential = 7,
+            Id_toPrecision = 8,
+            MAX_PROTOTYPE_ID = 8;
 
     private double doubleValue;
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeObject.java rhino-1.7.14/src/org/mozilla/javascript/NativeObject.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeObject.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeObject.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,489 +6,702 @@
 
 package org.mozilla.javascript;
 
-import java.util.*;
+import java.util.AbstractCollection;
+import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import org.mozilla.javascript.ScriptRuntime.StringIdOrIndex;
 
 /**
- * This class implements the Object native object.
- * See ECMA 15.2.
+ * This class implements the Object native object. See ECMA 15.2.
+ *
  * @author Norris Boyd
  */
-public class NativeObject extends IdScriptableObject implements Map
-{
-    static final long serialVersionUID = -6345305608474346996L;
+public class NativeObject extends IdScriptableObject implements Map {
+    private static final long serialVersionUID = -6345305608474346996L;
 
     private static final Object OBJECT_TAG = "Object";
 
-    static void init(Scriptable scope, boolean sealed)
-    {
+    static void init(Scriptable scope, boolean sealed) {
         NativeObject obj = new NativeObject();
         obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
     }
 
     @Override
-    public String getClassName()
-    {
+    public String getClassName() {
         return "Object";
     }
 
     @Override
-    public String toString()
-    {
+    public String toString() {
         return ScriptRuntime.defaultObjectToString(this);
     }
 
     @Override
-    protected void fillConstructorProperties(IdFunctionObject ctor)
-    {
-        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_getPrototypeOf,
-                "getPrototypeOf", 1);
-        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_keys,
-                "keys", 1);
-        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_getOwnPropertyNames,
-                "getOwnPropertyNames", 1);
-        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_getOwnPropertySymbols,
-                "getOwnPropertySymbols", 1);
-        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_getOwnPropertyDescriptor,
-                "getOwnPropertyDescriptor", 2);
-        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_defineProperty,
-                "defineProperty", 3);
-        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_isExtensible,
-                "isExtensible", 1);
-        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_preventExtensions,
-                "preventExtensions", 1);
-        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_defineProperties,
-                "defineProperties", 2);
-        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_create,
-                "create", 2);
-        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_isSealed,
-                "isSealed", 1);
-        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_isFrozen,
-                "isFrozen", 1);
-        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_seal,
-                "seal", 1);
-        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_freeze,
-                "freeze", 1);
-        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_assign,
-                "assign", 2);
-        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_is,
-                "is", 2);
+    protected void fillConstructorProperties(IdFunctionObject ctor) {
+        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_getPrototypeOf, "getPrototypeOf", 1);
+        if (Context.getCurrentContext().version >= Context.VERSION_ES6) {
+            addIdFunctionProperty(
+                    ctor, OBJECT_TAG, ConstructorId_setPrototypeOf, "setPrototypeOf", 2);
+            addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_entries, "entries", 1);
+            addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_fromEntries, "fromEntries", 1);
+            addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_values, "values", 1);
+        }
+        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_keys, "keys", 1);
+        addIdFunctionProperty(
+                ctor, OBJECT_TAG, ConstructorId_getOwnPropertyNames, "getOwnPropertyNames", 1);
+        addIdFunctionProperty(
+                ctor, OBJECT_TAG, ConstructorId_getOwnPropertySymbols, "getOwnPropertySymbols", 1);
+        addIdFunctionProperty(
+                ctor,
+                OBJECT_TAG,
+                ConstructorId_getOwnPropertyDescriptor,
+                "getOwnPropertyDescriptor",
+                2);
+        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_defineProperty, "defineProperty", 3);
+        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_isExtensible, "isExtensible", 1);
+        addIdFunctionProperty(
+                ctor, OBJECT_TAG, ConstructorId_preventExtensions, "preventExtensions", 1);
+        addIdFunctionProperty(
+                ctor, OBJECT_TAG, ConstructorId_defineProperties, "defineProperties", 2);
+        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_create, "create", 2);
+        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_isSealed, "isSealed", 1);
+        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_isFrozen, "isFrozen", 1);
+        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_seal, "seal", 1);
+        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_freeze, "freeze", 1);
+        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_assign, "assign", 2);
+        addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_is, "is", 2);
         super.fillConstructorProperties(ctor);
     }
 
     @Override
-    protected void initPrototypeId(int id)
-    {
+    protected void initPrototypeId(int id) {
         String s;
         int arity;
         switch (id) {
-          case Id_constructor:    arity=1; s="constructor";    break;
-          case Id_toString:       arity=0; s="toString";       break;
-          case Id_toLocaleString: arity=0; s="toLocaleString"; break;
-          case Id_valueOf:        arity=0; s="valueOf";        break;
-          case Id_hasOwnProperty: arity=1; s="hasOwnProperty"; break;
-          case Id_propertyIsEnumerable:
-            arity=1; s="propertyIsEnumerable"; break;
-          case Id_isPrototypeOf:  arity=1; s="isPrototypeOf";  break;
-          case Id_toSource:       arity=0; s="toSource";       break;
-          case Id___defineGetter__:
-            arity=2; s="__defineGetter__";     break;
-          case Id___defineSetter__:
-            arity=2; s="__defineSetter__";     break;
-          case Id___lookupGetter__:
-            arity=1; s="__lookupGetter__";     break;
-          case Id___lookupSetter__:
-            arity=1; s="__lookupSetter__";     break;
-          default: throw new IllegalArgumentException(String.valueOf(id));
+            case Id_constructor:
+                arity = 1;
+                s = "constructor";
+                break;
+            case Id_toString:
+                arity = 0;
+                s = "toString";
+                break;
+            case Id_toLocaleString:
+                arity = 0;
+                s = "toLocaleString";
+                break;
+            case Id_valueOf:
+                arity = 0;
+                s = "valueOf";
+                break;
+            case Id_hasOwnProperty:
+                arity = 1;
+                s = "hasOwnProperty";
+                break;
+            case Id_propertyIsEnumerable:
+                arity = 1;
+                s = "propertyIsEnumerable";
+                break;
+            case Id_isPrototypeOf:
+                arity = 1;
+                s = "isPrototypeOf";
+                break;
+            case Id_toSource:
+                arity = 0;
+                s = "toSource";
+                break;
+            case Id___defineGetter__:
+                arity = 2;
+                s = "__defineGetter__";
+                break;
+            case Id___defineSetter__:
+                arity = 2;
+                s = "__defineSetter__";
+                break;
+            case Id___lookupGetter__:
+                arity = 1;
+                s = "__lookupGetter__";
+                break;
+            case Id___lookupSetter__:
+                arity = 1;
+                s = "__lookupSetter__";
+                break;
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
         }
         initPrototypeMethod(OBJECT_TAG, id, s, arity);
     }
 
     @Override
-    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
-                             Scriptable thisObj, Object[] args)
-    {
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         if (!f.hasTag(OBJECT_TAG)) {
             return super.execIdCall(f, cx, scope, thisObj, args);
         }
         int id = f.methodId();
         switch (id) {
-          case Id_constructor: {
-            if (thisObj != null) {
-                // BaseFunction.construct will set up parent, proto
-                return f.construct(cx, scope, args);
-            }
-            if (args.length == 0 || args[0] == null
-                || args[0] == Undefined.instance)
-            {
-                return new NativeObject();
-            }
-            return ScriptRuntime.toObject(cx, scope, args[0]);
-          }
+            case Id_constructor:
+                {
+                    if (thisObj != null) {
+                        // BaseFunction.construct will set up parent, proto
+                        return f.construct(cx, scope, args);
+                    }
+                    if (args.length == 0 || args[0] == null || Undefined.isUndefined(args[0])) {
+                        return new NativeObject();
+                    }
+                    return ScriptRuntime.toObject(cx, scope, args[0]);
+                }
 
-          case Id_toLocaleString: {
-            Object toString = ScriptableObject.getProperty(thisObj, "toString");
-            if(!(toString instanceof Callable)) {
-                throw ScriptRuntime.notFunctionError(toString);
-            }
-            Callable fun = (Callable)toString;
-            return fun.call(cx, scope, thisObj, ScriptRuntime.emptyArgs);
-          }
-
-          case Id_toString: {
-            if (cx.hasFeature(Context.FEATURE_TO_STRING_AS_SOURCE)) {
-                String s = ScriptRuntime.defaultObjectToSource(cx, scope,
-                                                               thisObj, args);
-                int L = s.length();
-                if (L != 0 && s.charAt(0) == '(' && s.charAt(L - 1) == ')') {
-                    // Strip () that surrounds toSource
-                    s = s.substring(1, L - 1);
+            case Id_toLocaleString:
+                {
+                    if (thisObj == null) {
+                        throw ScriptRuntime.notFunctionError(null);
+                    }
+                    Object toString = ScriptableObject.getProperty(thisObj, "toString");
+                    if (!(toString instanceof Callable)) {
+                        throw ScriptRuntime.notFunctionError(toString);
+                    }
+                    Callable fun = (Callable) toString;
+                    return fun.call(cx, scope, thisObj, ScriptRuntime.emptyArgs);
                 }
-                return s;
-            }
-            return ScriptRuntime.defaultObjectToString(thisObj);
-          }
 
-          case Id_valueOf:
-            return thisObj;
+            case Id_toString:
+                {
+                    if (cx.hasFeature(Context.FEATURE_TO_STRING_AS_SOURCE)) {
+                        String s =
+                                ScriptRuntime.defaultObjectToSource(
+                                        cx, scope,
+                                        thisObj, args);
+                        int L = s.length();
+                        if (L != 0 && s.charAt(0) == '(' && s.charAt(L - 1) == ')') {
+                            // Strip () that surrounds toSource
+                            s = s.substring(1, L - 1);
+                        }
+                        return s;
+                    }
+                    return ScriptRuntime.defaultObjectToString(thisObj);
+                }
 
-          case Id_hasOwnProperty: {
-              boolean result;
-              Object arg = args.length < 1 ? Undefined.instance : args[0];
-              if (arg instanceof Symbol) {
-                  result = ensureSymbolScriptable(thisObj).has((Symbol) arg, thisObj);
-              } else {
-                  String s = ScriptRuntime.toStringIdOrIndex(cx, arg);
-                  if (s == null) {
-                      int index = ScriptRuntime.lastIndexResult(cx);
-                      result = thisObj.has(index, thisObj);
-                  } else {
-                      result = thisObj.has(s, thisObj);
-                  }
-              }
-              return ScriptRuntime.wrapBoolean(result);
-          }
-
-          case Id_propertyIsEnumerable: {
-            boolean result;
-            Object arg = args.length < 1 ? Undefined.instance : args[0];
-
-            if (arg instanceof Symbol) {
-                result = ((SymbolScriptable)thisObj).has((Symbol)arg, thisObj);
-                if (result && thisObj instanceof ScriptableObject) {
-                    ScriptableObject so = (ScriptableObject)thisObj;
-                    int attrs = so.getAttributes((Symbol)arg);
-                    result = ((attrs & ScriptableObject.DONTENUM) == 0);
-                }
-            } else {
-                String s = ScriptRuntime.toStringIdOrIndex(cx, arg);
-                if (s == null) {
-                    int index = ScriptRuntime.lastIndexResult(cx);
-                    result = thisObj.has(index, thisObj);
-                    if (result && thisObj instanceof ScriptableObject) {
-                        ScriptableObject so = (ScriptableObject) thisObj;
-                        int attrs = so.getAttributes(index);
-                        result = ((attrs & ScriptableObject.DONTENUM) == 0);
-                    }
-                } else {
-                    result = thisObj.has(s, thisObj);
-                    if (result && thisObj instanceof ScriptableObject) {
-                        ScriptableObject so = (ScriptableObject) thisObj;
-                        int attrs = so.getAttributes(s);
-                        result = ((attrs & ScriptableObject.DONTENUM) == 0);
+            case Id_valueOf:
+                if (cx.getLanguageVersion() >= Context.VERSION_1_8
+                        && (thisObj == null || Undefined.isUndefined(thisObj))) {
+                    throw ScriptRuntime.typeErrorById(
+                            "msg." + (thisObj == null ? "null" : "undef") + ".to.object");
+                }
+                return thisObj;
+
+            case Id_hasOwnProperty:
+                {
+                    if (cx.getLanguageVersion() >= Context.VERSION_1_8
+                            && (thisObj == null || Undefined.isUndefined(thisObj))) {
+                        throw ScriptRuntime.typeErrorById(
+                                "msg." + (thisObj == null ? "null" : "undef") + ".to.object");
+                    }
+                    boolean result;
+                    Object arg = args.length < 1 ? Undefined.instance : args[0];
+                    if (arg instanceof Symbol) {
+                        result = ensureSymbolScriptable(thisObj).has((Symbol) arg, thisObj);
+                    } else {
+                        StringIdOrIndex s = ScriptRuntime.toStringIdOrIndex(cx, arg);
+                        if (s.stringId == null) {
+                            result = thisObj.has(s.index, thisObj);
+                        } else {
+                            result = thisObj.has(s.stringId, thisObj);
+                        }
                     }
+                    return ScriptRuntime.wrapBoolean(result);
                 }
-            }
-            return ScriptRuntime.wrapBoolean(result);
-          }
 
-          case Id_isPrototypeOf: {
-            boolean result = false;
-            if (args.length != 0 && args[0] instanceof Scriptable) {
-                Scriptable v = (Scriptable) args[0];
-                do {
-                    v = v.getPrototype();
-                    if (v == thisObj) {
-                        result = true;
-                        break;
+            case Id_propertyIsEnumerable:
+                {
+                    if (cx.getLanguageVersion() >= Context.VERSION_1_8
+                            && (thisObj == null || Undefined.isUndefined(thisObj))) {
+                        throw ScriptRuntime.typeErrorById(
+                                "msg." + (thisObj == null ? "null" : "undef") + ".to.object");
                     }
-                } while (v != null);
-            }
-            return ScriptRuntime.wrapBoolean(result);
-          }
 
-          case Id_toSource:
-            return ScriptRuntime.defaultObjectToSource(cx, scope, thisObj,
-                                                       args);
-          case Id___defineGetter__:
-          case Id___defineSetter__:
-            {
-                if (args.length < 2 || !(args[1] instanceof Callable)) {
-                    Object badArg = (args.length >= 2 ? args[1]
-                                     : Undefined.instance);
-                    throw ScriptRuntime.notFunctionError(badArg);
-                }
-                if (!(thisObj instanceof ScriptableObject)) {
-                    throw Context.reportRuntimeError2(
-                        "msg.extend.scriptable",
-                        thisObj.getClass().getName(),
-                        String.valueOf(args[0]));
-                }
-                ScriptableObject so = (ScriptableObject)thisObj;
-                String name = ScriptRuntime.toStringIdOrIndex(cx, args[0]);
-                int index = (name != null ? 0
-                             : ScriptRuntime.lastIndexResult(cx));
-                Callable getterOrSetter = (Callable)args[1];
-                boolean isSetter = (id == Id___defineSetter__);
-                so.setGetterOrSetter(name, index, getterOrSetter, isSetter);
-                if (so instanceof NativeArray)
-                    ((NativeArray)so).setDenseOnly(false);
-            }
-            return Undefined.instance;
+                    boolean result;
+                    Object arg = args.length < 1 ? Undefined.instance : args[0];
+
+                    if (arg instanceof Symbol) {
+                        result = ((SymbolScriptable) thisObj).has((Symbol) arg, thisObj);
+                        result = result && isEnumerable((Symbol) arg, thisObj);
+                    } else {
+                        StringIdOrIndex s = ScriptRuntime.toStringIdOrIndex(cx, arg);
+                        // When checking if a property is enumerable, a missing property should
+                        // return "false" instead of
+                        // throwing an exception.  See: https://github.com/mozilla/rhino/issues/415
+                        try {
+                            if (s.stringId == null) {
+                                result = thisObj.has(s.index, thisObj);
+                                result = result && isEnumerable(s.index, thisObj);
+                            } else {
+                                result = thisObj.has(s.stringId, thisObj);
+                                result = result && isEnumerable(s.stringId, thisObj);
+                            }
+                        } catch (EvaluatorException ee) {
+                            if (ee.getMessage()
+                                    .startsWith(
+                                            ScriptRuntime.getMessageById(
+                                                    "msg.prop.not.found",
+                                                    s.stringId == null
+                                                            ? Integer.toString(s.index)
+                                                            : s.stringId))) {
+                                result = false;
+                            } else {
+                                throw ee;
+                            }
+                        }
+                    }
+                    return ScriptRuntime.wrapBoolean(result);
+                }
+
+            case Id_isPrototypeOf:
+                {
+                    if (cx.getLanguageVersion() >= Context.VERSION_1_8
+                            && (thisObj == null || Undefined.isUndefined(thisObj))) {
+                        throw ScriptRuntime.typeErrorById(
+                                "msg." + (thisObj == null ? "null" : "undef") + ".to.object");
+                    }
+
+                    boolean result = false;
+                    if (args.length != 0 && args[0] instanceof Scriptable) {
+                        Scriptable v = (Scriptable) args[0];
+                        do {
+                            v = v.getPrototype();
+                            if (v == thisObj) {
+                                result = true;
+                                break;
+                            }
+                        } while (v != null);
+                    }
+                    return ScriptRuntime.wrapBoolean(result);
+                }
+
+            case Id_toSource:
+                return ScriptRuntime.defaultObjectToSource(cx, scope, thisObj, args);
+            case Id___defineGetter__:
+            case Id___defineSetter__:
+                {
+                    if (args.length < 2 || !(args[1] instanceof Callable)) {
+                        Object badArg = (args.length >= 2 ? args[1] : Undefined.instance);
+                        throw ScriptRuntime.notFunctionError(badArg);
+                    }
+                    if (!(thisObj instanceof ScriptableObject)) {
+                        throw Context.reportRuntimeErrorById(
+                                "msg.extend.scriptable",
+                                thisObj == null ? "null" : thisObj.getClass().getName(),
+                                String.valueOf(args[0]));
+                    }
+                    ScriptableObject so = (ScriptableObject) thisObj;
+                    StringIdOrIndex s = ScriptRuntime.toStringIdOrIndex(cx, args[0]);
+                    int index = s.stringId != null ? 0 : s.index;
+                    Callable getterOrSetter = (Callable) args[1];
+                    boolean isSetter = (id == Id___defineSetter__);
+                    so.setGetterOrSetter(s.stringId, index, getterOrSetter, isSetter);
+                    if (so instanceof NativeArray) ((NativeArray) so).setDenseOnly(false);
+                }
+                return Undefined.instance;
 
             case Id___lookupGetter__:
             case Id___lookupSetter__:
-              {
-                  if (args.length < 1 ||
-                      !(thisObj instanceof ScriptableObject))
-                      return Undefined.instance;
-
-                  ScriptableObject so = (ScriptableObject)thisObj;
-                  String name = ScriptRuntime.toStringIdOrIndex(cx, args[0]);
-                  int index = (name != null ? 0
-                               : ScriptRuntime.lastIndexResult(cx));
-                  boolean isSetter = (id == Id___lookupSetter__);
-                  Object gs;
-                  for (;;) {
-                      gs = so.getGetterOrSetter(name, index, isSetter);
-                      if (gs != null)
-                          break;
-                      // If there is no getter or setter for the object itself,
-                      // how about the prototype?
-                      Scriptable v = so.getPrototype();
-                      if (v == null)
-                          break;
-                      if (v instanceof ScriptableObject)
-                          so = (ScriptableObject)v;
-                      else
-                          break;
-                  }
-                  if (gs != null)
-                      return gs;
-              }
-              return Undefined.instance;
-
-          case ConstructorId_getPrototypeOf:
-              {
-                Object arg = args.length < 1 ? Undefined.instance : args[0];
-                Scriptable obj = getCompatibleObject(cx, scope, arg);
-                return obj.getPrototype();
-              }
-          case ConstructorId_keys:
-              {
-                Object arg = args.length < 1 ? Undefined.instance : args[0];
-                Scriptable obj = getCompatibleObject(cx, scope, arg);
-                Object[] ids = obj.getIds();
-                for (int i = 0; i < ids.length; i++) {
-                  ids[i] = ScriptRuntime.toString(ids[i]);
-                }
-                return cx.newArray(scope, ids);
-              }
-          case ConstructorId_getOwnPropertyNames:
-              {
-                Object arg = args.length < 1 ? Undefined.instance : args[0];
-                Scriptable s = getCompatibleObject(cx, scope, arg);
-                ScriptableObject obj = ensureScriptableObject(s);
-                Object[] ids = obj.getIds(true, false);
-                for (int i = 0; i < ids.length; i++) {
-                  ids[i] = ScriptRuntime.toString(ids[i]);
+                {
+                    if (args.length < 1 || !(thisObj instanceof ScriptableObject))
+                        return Undefined.instance;
+
+                    ScriptableObject so = (ScriptableObject) thisObj;
+                    StringIdOrIndex s = ScriptRuntime.toStringIdOrIndex(cx, args[0]);
+                    int index = s.stringId != null ? 0 : s.index;
+                    boolean isSetter = (id == Id___lookupSetter__);
+                    Object gs;
+                    for (; ; ) {
+                        gs = so.getGetterOrSetter(s.stringId, index, this, isSetter);
+                        if (gs != null) {
+                            break;
+                        }
+                        // If there is no getter or setter for the object itself,
+                        // how about the prototype?
+                        Scriptable v = so.getPrototype();
+                        if (v == null) {
+                            break;
+                        }
+                        if (v instanceof ScriptableObject) {
+                            so = (ScriptableObject) v;
+                        } else {
+                            break;
+                        }
+                    }
+                    if (gs != null) {
+                        return gs;
+                    }
+                }
+                return Undefined.instance;
+
+            case ConstructorId_getPrototypeOf:
+                {
+                    Object arg = args.length < 1 ? Undefined.instance : args[0];
+                    Scriptable obj = getCompatibleObject(cx, scope, arg);
+                    return obj.getPrototype();
+                }
+            case ConstructorId_setPrototypeOf:
+                {
+                    if (args.length < 2) {
+                        throw ScriptRuntime.typeErrorById(
+                                "msg.method.missing.parameter",
+                                "Object.setPrototypeOf",
+                                "2",
+                                Integer.toString(args.length));
+                    }
+                    Scriptable proto = (args[1] == null) ? null : ensureScriptable(args[1]);
+                    if (proto instanceof Symbol) {
+                        throw ScriptRuntime.typeErrorById(
+                                "msg.arg.not.object", ScriptRuntime.typeof(proto));
+                    }
+
+                    final Object arg0 = args[0];
+                    if (cx.getLanguageVersion() >= Context.VERSION_ES6) {
+                        ScriptRuntimeES6.requireObjectCoercible(cx, arg0, f);
+                    }
+                    if (!(arg0 instanceof ScriptableObject)) {
+                        return arg0;
+                    }
+                    ScriptableObject obj = (ScriptableObject) arg0;
+                    if (!obj.isExtensible()) {
+                        throw ScriptRuntime.typeErrorById("msg.not.extensible");
+                    }
+
+                    // cycle detection
+                    Scriptable prototypeProto = proto;
+                    while (prototypeProto != null) {
+                        if (prototypeProto == obj) {
+                            throw ScriptRuntime.typeErrorById(
+                                    "msg.object.cyclic.prototype", obj.getClass().getSimpleName());
+                        }
+                        prototypeProto = prototypeProto.getPrototype();
+                    }
+                    obj.setPrototype(proto);
+                    return obj;
+                }
+            case ConstructorId_keys:
+                {
+                    Object arg = args.length < 1 ? Undefined.instance : args[0];
+                    Scriptable obj = getCompatibleObject(cx, scope, arg);
+                    Object[] ids = obj.getIds();
+                    for (int i = 0; i < ids.length; i++) {
+                        ids[i] = ScriptRuntime.toString(ids[i]);
+                    }
+                    return cx.newArray(scope, ids);
+                }
+
+            case ConstructorId_entries:
+                {
+                    Object arg = args.length < 1 ? Undefined.instance : args[0];
+                    Scriptable obj = getCompatibleObject(cx, scope, arg);
+                    Object[] ids = obj.getIds();
+                    int j = 0;
+                    for (int i = 0; i < ids.length; i++) {
+                        if (ids[i] instanceof Integer) {
+                            int intId = (Integer) ids[i];
+                            if (obj.has(intId, obj) && isEnumerable(intId, obj)) {
+                                String stringId = ScriptRuntime.toString(ids[i]);
+                                Object[] entry = new Object[] {stringId, obj.get(intId, obj)};
+                                ids[j++] = cx.newArray(scope, entry);
+                            }
+                        } else {
+                            String stringId = ScriptRuntime.toString(ids[i]);
+                            if (obj.has(stringId, obj) && isEnumerable(stringId, obj)) {
+                                Object[] entry = new Object[] {stringId, obj.get(stringId, obj)};
+                                ids[j++] = cx.newArray(scope, entry);
+                            }
+                        }
+                    }
+                    if (j != ids.length) {
+                        ids = Arrays.copyOf(ids, j);
+                    }
+                    return cx.newArray(scope, ids);
+                }
+            case ConstructorId_fromEntries:
+                {
+                    Object arg = args.length < 1 ? Undefined.instance : args[0];
+                    arg = getCompatibleObject(cx, scope, arg);
+                    Scriptable obj = cx.newObject(scope);
+                    ScriptRuntime.loadFromIterable(
+                            cx,
+                            scope,
+                            arg,
+                            (key, value) -> {
+                                if (key instanceof Integer) {
+                                    obj.put((Integer) key, obj, value);
+                                } else if (key instanceof Symbol
+                                        && obj instanceof SymbolScriptable) {
+                                    ((SymbolScriptable) obj).put((Symbol) key, obj, value);
+                                } else {
+                                    obj.put(ScriptRuntime.toString(key), obj, value);
+                                }
+                            });
+                    return obj;
+                }
+            case ConstructorId_values:
+                {
+                    Object arg = args.length < 1 ? Undefined.instance : args[0];
+                    Scriptable obj = getCompatibleObject(cx, scope, arg);
+                    Object[] ids = obj.getIds();
+                    int j = 0;
+                    for (int i = 0; i < ids.length; i++) {
+                        if (ids[i] instanceof Integer) {
+                            int intId = (Integer) ids[i];
+                            if (obj.has(intId, obj) && isEnumerable(intId, obj)) {
+                                ids[j++] = obj.get(intId, obj);
+                            }
+                        } else {
+                            String stringId = ScriptRuntime.toString(ids[i]);
+                            // getter may remove keys
+                            if (obj.has(stringId, obj) && isEnumerable(stringId, obj)) {
+                                ids[j++] = obj.get(stringId, obj);
+                            }
+                        }
+                    }
+                    if (j != ids.length) {
+                        ids = Arrays.copyOf(ids, j);
+                    }
+                    return cx.newArray(scope, ids);
+                }
+            case ConstructorId_getOwnPropertyNames:
+                {
+                    Object arg = args.length < 1 ? Undefined.instance : args[0];
+                    Scriptable s = getCompatibleObject(cx, scope, arg);
+                    ScriptableObject obj = ensureScriptableObject(s);
+                    Object[] ids = obj.getIds(true, false);
+                    for (int i = 0; i < ids.length; i++) {
+                        ids[i] = ScriptRuntime.toString(ids[i]);
+                    }
+                    return cx.newArray(scope, ids);
                 }
-                return cx.newArray(scope, ids);
-              }
             case ConstructorId_getOwnPropertySymbols:
-            {
-                Object arg = args.length < 1 ? Undefined.instance : args[0];
-                Scriptable s = getCompatibleObject(cx, scope, arg);
-                ScriptableObject obj = ensureScriptableObject(s);
-                Object[] ids = obj.getIds(true, true);
-                ArrayList<Object> syms = new ArrayList<Object>();
-                for (int i = 0; i < ids.length; i++) {
-                    if (ids[i] instanceof Symbol) {
-                        syms.add(ids[i]);
+                {
+                    Object arg = args.length < 1 ? Undefined.instance : args[0];
+                    Scriptable s = getCompatibleObject(cx, scope, arg);
+                    ScriptableObject obj = ensureScriptableObject(s);
+                    Object[] ids = obj.getIds(true, true);
+                    ArrayList<Object> syms = new ArrayList<Object>();
+                    for (int i = 0; i < ids.length; i++) {
+                        if (ids[i] instanceof Symbol) {
+                            syms.add(ids[i]);
+                        }
                     }
+                    return cx.newArray(scope, syms.toArray());
                 }
-                return cx.newArray(scope, syms.toArray());
-            }
-          case ConstructorId_getOwnPropertyDescriptor:
-              {
-                Object arg = args.length < 1 ? Undefined.instance : args[0];
-                // TODO(norris): There's a deeper issue here if
-                // arg instanceof Scriptable. Should we create a new
-                // interface to admit the new ECMAScript 5 operations?
-                Scriptable s = getCompatibleObject(cx, scope, arg);
-                ScriptableObject obj = ensureScriptableObject(s);
-                Object nameArg = args.length < 2 ? Undefined.instance : args[1];
-                Scriptable desc = obj.getOwnPropertyDescriptor(cx, nameArg);
-                return desc == null ? Undefined.instance : desc;
-              }
-          case ConstructorId_defineProperty:
-              {
-                Object arg = args.length < 1 ? Undefined.instance : args[0];
-                ScriptableObject obj = ensureScriptableObject(arg);
-                Object name = args.length < 2 ? Undefined.instance : args[1];
-                Object descArg = args.length < 3 ? Undefined.instance : args[2];
-                ScriptableObject desc = ensureScriptableObject(descArg);
-                obj.defineOwnProperty(cx, name, desc);
-                return obj;
-              }
-          case ConstructorId_isExtensible:
-              {
-                Object arg = args.length < 1 ? Undefined.instance : args[0];
-                ScriptableObject obj = ensureScriptableObject(arg);
-                return Boolean.valueOf(obj.isExtensible());
-              }
-          case ConstructorId_preventExtensions:
-              {
-                Object arg = args.length < 1 ? Undefined.instance : args[0];
-                ScriptableObject obj = ensureScriptableObject(arg);
-                obj.preventExtensions();
-                return obj;
-              }
-          case ConstructorId_defineProperties:
-              {
-                Object arg = args.length < 1 ? Undefined.instance : args[0];
-                ScriptableObject obj = ensureScriptableObject(arg);
-                Object propsObj = args.length < 2 ? Undefined.instance : args[1];
-                Scriptable props = Context.toObject(propsObj, getParentScope());
-                obj.defineOwnProperties(cx, ensureScriptableObject(props));
-                return obj;
-              }
-          case ConstructorId_create:
-              {
-                Object arg = args.length < 1 ? Undefined.instance : args[0];
-                Scriptable obj = (arg == null) ? null : ensureScriptable(arg);
-
-                ScriptableObject newObject = new NativeObject();
-                newObject.setParentScope(getParentScope());
-                newObject.setPrototype(obj);
-
-                if (args.length > 1 && args[1] != Undefined.instance) {
-                  Scriptable props = Context.toObject(args[1], getParentScope());
-                  newObject.defineOwnProperties(cx, ensureScriptableObject(props));
-                }
-
-                return newObject;
-              }
-          case ConstructorId_isSealed:
-              {
-                Object arg = args.length < 1 ? Undefined.instance : args[0];
-                ScriptableObject obj = ensureScriptableObject(arg);
-
-                if (obj.isExtensible()) return Boolean.FALSE;
-
-                for (Object name: obj.getAllIds()) {
-                  Object configurable = obj.getOwnPropertyDescriptor(cx, name).get("configurable");
-                  if (Boolean.TRUE.equals(configurable))
-                    return Boolean.FALSE;
-                }
-
-                return Boolean.TRUE;
-              }
-          case ConstructorId_isFrozen:
-              {
-                Object arg = args.length < 1 ? Undefined.instance : args[0];
-                ScriptableObject obj = ensureScriptableObject(arg);
-
-                if (obj.isExtensible()) return Boolean.FALSE;
-
-                for (Object name: obj.getAllIds()) {
-                  ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, name);
-                  if (Boolean.TRUE.equals(desc.get("configurable")))
-                    return Boolean.FALSE;
-                  if (isDataDescriptor(desc) && Boolean.TRUE.equals(desc.get("writable")))
-                    return Boolean.FALSE;
-                }
-
-                return Boolean.TRUE;
-              }
-          case ConstructorId_seal:
-              {
-                Object arg = args.length < 1 ? Undefined.instance : args[0];
-                ScriptableObject obj = ensureScriptableObject(arg);
-
-                for (Object name: obj.getAllIds()) {
-                  ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, name);
-                  if (Boolean.TRUE.equals(desc.get("configurable"))) {
-                    desc.put("configurable", desc, Boolean.FALSE);
-                    obj.defineOwnProperty(cx, name, desc, false);
-                  }
-                }
-                obj.preventExtensions();
-
-                return obj;
-              }
-          case ConstructorId_freeze:
-              {
-                Object arg = args.length < 1 ? Undefined.instance : args[0];
-                ScriptableObject obj = ensureScriptableObject(arg);
-
-                for (Object name: obj.getAllIds()) {
-                  ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, name);
-                  if (isDataDescriptor(desc) && Boolean.TRUE.equals(desc.get("writable")))
-                    desc.put("writable", desc, Boolean.FALSE);
-                  if (Boolean.TRUE.equals(desc.get("configurable")))
-                    desc.put("configurable", desc, Boolean.FALSE);
-                  obj.defineOwnProperty(cx, name, desc, false);
-                }
-                obj.preventExtensions();
-
-                return obj;
-              }
-
-          case ConstructorId_assign:
-          {
-            if (args.length < 1) {
-              throw ScriptRuntime.typeError1("msg.incompat.call", "assign");
-            }
-            Scriptable t = ScriptRuntime.toObject(cx, thisObj, args[0]);
-            for (int i = 1; i < args.length; i++) {
-              if ((args[i] == null) || Undefined.instance.equals(args[i])) {
-                continue;
-              }
-              Scriptable s = ScriptRuntime.toObject(cx, thisObj, args[i]);
-              Object[] ids = s.getIds();
-              for (Object key : ids) {
-                if (key instanceof String) {
-                  Object val = s.get((String) key, t);
-                  if ((val != Scriptable.NOT_FOUND) && (val != Undefined.instance)) {
-                    t.put((String) key, t, val);
-                  }
-                } else if (key instanceof Number) {
-                  int ii = ScriptRuntime.toInt32(key);
-                  Object val = s.get(ii, t);
-                  if ((val != Scriptable.NOT_FOUND) && (val != Undefined.instance)) {
-                    t.put(ii, t, val);
-                  }
+            case ConstructorId_getOwnPropertyDescriptor:
+                {
+                    Object arg = args.length < 1 ? Undefined.instance : args[0];
+                    // TODO(norris): There's a deeper issue here if
+                    // arg instanceof Scriptable. Should we create a new
+                    // interface to admit the new ECMAScript 5 operations?
+                    Scriptable s = getCompatibleObject(cx, scope, arg);
+                    ScriptableObject obj = ensureScriptableObject(s);
+                    Object nameArg = args.length < 2 ? Undefined.instance : args[1];
+                    Scriptable desc = obj.getOwnPropertyDescriptor(cx, nameArg);
+                    return desc == null ? Undefined.instance : desc;
+                }
+            case ConstructorId_defineProperty:
+                {
+                    Object arg = args.length < 1 ? Undefined.instance : args[0];
+                    ScriptableObject obj = ensureScriptableObject(arg);
+                    Object name = args.length < 2 ? Undefined.instance : args[1];
+                    Object descArg = args.length < 3 ? Undefined.instance : args[2];
+                    ScriptableObject desc = ensureScriptableObject(descArg);
+                    obj.defineOwnProperty(cx, name, desc);
+                    return obj;
+                }
+            case ConstructorId_isExtensible:
+                {
+                    Object arg = args.length < 1 ? Undefined.instance : args[0];
+                    if (cx.getLanguageVersion() >= Context.VERSION_ES6
+                            && !(arg instanceof ScriptableObject)) {
+                        return Boolean.FALSE;
+                    }
+
+                    ScriptableObject obj = ensureScriptableObject(arg);
+                    return Boolean.valueOf(obj.isExtensible());
                 }
-              }
-            }
-            return t;
-          }
+            case ConstructorId_preventExtensions:
+                {
+                    Object arg = args.length < 1 ? Undefined.instance : args[0];
+                    if (cx.getLanguageVersion() >= Context.VERSION_ES6
+                            && !(arg instanceof ScriptableObject)) {
+                        return arg;
+                    }
+
+                    ScriptableObject obj = ensureScriptableObject(arg);
+                    obj.preventExtensions();
+                    return obj;
+                }
+            case ConstructorId_defineProperties:
+                {
+                    Object arg = args.length < 1 ? Undefined.instance : args[0];
+                    ScriptableObject obj = ensureScriptableObject(arg);
+                    Object propsObj = args.length < 2 ? Undefined.instance : args[1];
+                    Scriptable props = Context.toObject(propsObj, scope);
+                    obj.defineOwnProperties(cx, ensureScriptableObject(props));
+                    return obj;
+                }
+            case ConstructorId_create:
+                {
+                    Object arg = args.length < 1 ? Undefined.instance : args[0];
+                    Scriptable obj = (arg == null) ? null : ensureScriptable(arg);
+
+                    ScriptableObject newObject = new NativeObject();
+                    newObject.setParentScope(scope);
+                    newObject.setPrototype(obj);
+
+                    if (args.length > 1 && !Undefined.isUndefined(args[1])) {
+                        Scriptable props = Context.toObject(args[1], scope);
+                        newObject.defineOwnProperties(cx, ensureScriptableObject(props));
+                    }
 
-          case ConstructorId_is:
-          {
-            Object a1 = args.length < 1 ? Undefined.instance : args[0];
-            Object a2 = args.length < 2 ? Undefined.instance : args[1];
-            return ScriptRuntime.wrapBoolean(ScriptRuntime.same(a1, a2));
-          }
+                    return newObject;
+                }
+            case ConstructorId_isSealed:
+                {
+                    Object arg = args.length < 1 ? Undefined.instance : args[0];
+                    if (cx.getLanguageVersion() >= Context.VERSION_ES6
+                            && !(arg instanceof ScriptableObject)) {
+                        return Boolean.TRUE;
+                    }
 
+                    return AbstractEcmaObjectOperations.testIntegrityLevel(
+                            cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.SEALED);
+                }
+            case ConstructorId_isFrozen:
+                {
+                    Object arg = args.length < 1 ? Undefined.instance : args[0];
+                    if (cx.getLanguageVersion() >= Context.VERSION_ES6
+                            && !(arg instanceof ScriptableObject)) {
+                        return Boolean.TRUE;
+                    }
 
-          default:
-            throw new IllegalArgumentException(String.valueOf(id));
+                    return AbstractEcmaObjectOperations.testIntegrityLevel(
+                            cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.FROZEN);
+                }
+            case ConstructorId_seal:
+                {
+                    Object arg = args.length < 1 ? Undefined.instance : args[0];
+                    if (cx.getLanguageVersion() >= Context.VERSION_ES6
+                            && !(arg instanceof ScriptableObject)) {
+                        return arg;
+                    }
+
+                    AbstractEcmaObjectOperations.setIntegrityLevel(
+                            cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.SEALED);
+
+                    return arg;
+                }
+            case ConstructorId_freeze:
+                {
+                    Object arg = args.length < 1 ? Undefined.instance : args[0];
+                    if (cx.getLanguageVersion() >= Context.VERSION_ES6
+                            && !(arg instanceof ScriptableObject)) {
+                        return arg;
+                    }
+
+                    AbstractEcmaObjectOperations.setIntegrityLevel(
+                            cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.FROZEN);
+
+                    return arg;
+                }
+
+            case ConstructorId_assign:
+                {
+                    Scriptable targetObj;
+                    if (args.length > 0) {
+                        targetObj = ScriptRuntime.toObject(cx, scope, args[0]);
+                    } else {
+                        targetObj = ScriptRuntime.toObject(cx, scope, Undefined.instance);
+                    }
+                    for (int i = 1; i < args.length; i++) {
+                        if ((args[i] == null) || Undefined.isUndefined(args[i])) {
+                            continue;
+                        }
+                        Scriptable sourceObj = ScriptRuntime.toObject(cx, scope, args[i]);
+                        Object[] ids = sourceObj.getIds();
+                        for (Object key : ids) {
+                            if (targetObj instanceof ScriptableObject) {
+                                ScriptableObject desc =
+                                        ((ScriptableObject) targetObj)
+                                                .getOwnPropertyDescriptor(cx, key);
+                                if (desc != null
+                                        && isDataDescriptor(desc)
+                                        && isFalse(desc.get("writable"))) {
+                                    throw ScriptRuntime.typeErrorById(
+                                            "msg.change.value.with.writable.false", key);
+                                }
+                            }
+                            if (key instanceof String) {
+                                Object val = sourceObj.get((String) key, sourceObj);
+                                if ((val != Scriptable.NOT_FOUND) && !Undefined.isUndefined(val)) {
+                                    targetObj.put((String) key, targetObj, val);
+                                }
+                            } else if (key instanceof Number) {
+                                int ii = ScriptRuntime.toInt32(key);
+                                Object val = sourceObj.get(ii, sourceObj);
+                                if ((val != Scriptable.NOT_FOUND) && !Undefined.isUndefined(val)) {
+                                    targetObj.put(ii, targetObj, val);
+                                }
+                            }
+                        }
+                    }
+                    return targetObj;
+                }
+
+            case ConstructorId_is:
+                {
+                    Object a1 = args.length < 1 ? Undefined.instance : args[0];
+                    Object a2 = args.length < 2 ? Undefined.instance : args[1];
+                    return ScriptRuntime.wrapBoolean(ScriptRuntime.same(a1, a2));
+                }
+
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
+        }
+    }
+
+    private boolean isEnumerable(int index, Object obj) {
+        if (obj instanceof ScriptableObject) {
+            ScriptableObject so = (ScriptableObject) obj;
+            int attrs = so.getAttributes(index);
+            return (attrs & ScriptableObject.DONTENUM) == 0;
+        } else {
+            return true;
+        }
+    }
+
+    private boolean isEnumerable(String key, Object obj) {
+        if (obj instanceof ScriptableObject) {
+            ScriptableObject so = (ScriptableObject) obj;
+            int attrs = so.getAttributes(key);
+            return (attrs & ScriptableObject.DONTENUM) == 0;
+        } else {
+            return true;
+        }
+    }
+
+    private boolean isEnumerable(Symbol sym, Object obj) {
+        if (obj instanceof ScriptableObject) {
+            ScriptableObject so = (ScriptableObject) obj;
+            int attrs = so.getAttributes(sym);
+            return (attrs & ScriptableObject.DONTENUM) == 0;
+        } else {
+            return true;
         }
     }
 
-    private Scriptable getCompatibleObject(Context cx, Scriptable scope, Object arg)
-    {
+    private static Scriptable getCompatibleObject(Context cx, Scriptable scope, Object arg) {
         if (cx.getLanguageVersion() >= Context.VERSION_ES6) {
             Scriptable s = ScriptRuntime.toObject(cx, scope, arg);
             return ensureScriptable(s);
@@ -498,6 +711,7 @@
 
     // methods implementing java.util.Map
 
+    @Override
     public boolean containsKey(Object key) {
         if (key instanceof String) {
             return has((String) key, this);
@@ -507,16 +721,17 @@
         return false;
     }
 
+    @Override
     public boolean containsValue(Object value) {
         for (Object obj : values()) {
-            if (value == obj ||
-                    value != null && value.equals(obj)) {
+            if (value == obj || value != null && value.equals(obj)) {
                 return true;
             }
         }
         return false;
     }
 
+    @Override
     public Object remove(Object key) {
         Object value = get(key);
         if (key instanceof String) {
@@ -527,32 +742,36 @@
         return value;
     }
 
-
+    @Override
     public Set<Object> keySet() {
         return new KeySet();
     }
 
+    @Override
     public Collection<Object> values() {
         return new ValueCollection();
     }
 
+    @Override
     public Set<Map.Entry<Object, Object>> entrySet() {
         return new EntrySet();
     }
 
+    @Override
     public Object put(Object key, Object value) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public void putAll(Map m) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public void clear() {
         throw new UnsupportedOperationException();
     }
 
-
     class EntrySet extends AbstractSet<Entry<Object, Object>> {
         @Override
         public Iterator<Entry<Object, Object>> iterator() {
@@ -561,22 +780,27 @@
                 Object key = null;
                 int index = 0;
 
+                @Override
                 public boolean hasNext() {
                     return index < ids.length;
                 }
 
+                @Override
                 public Map.Entry<Object, Object> next() {
                     final Object ekey = key = ids[index++];
                     final Object value = get(key);
                     return new Map.Entry<Object, Object>() {
+                        @Override
                         public Object getKey() {
                             return ekey;
                         }
 
+                        @Override
                         public Object getValue() {
                             return value;
                         }
 
+                        @Override
                         public Object setValue(Object value) {
                             throw new UnsupportedOperationException();
                         }
@@ -588,13 +812,15 @@
                             }
                             Map.Entry<?, ?> e = (Map.Entry<?, ?>) other;
                             return (ekey == null ? e.getKey() == null : ekey.equals(e.getKey()))
-                                && (value == null ? e.getValue() == null : value.equals(e.getValue()));
+                                    && (value == null
+                                            ? e.getValue() == null
+                                            : value.equals(e.getValue()));
                         }
 
                         @Override
                         public int hashCode() {
-                            return (ekey == null ? 0 : ekey.hashCode()) ^
-                                   (value == null ? 0 : value.hashCode());
+                            return (ekey == null ? 0 : ekey.hashCode())
+                                    ^ (value == null ? 0 : value.hashCode());
                         }
 
                         @Override
@@ -604,6 +830,7 @@
                     };
                 }
 
+                @Override
                 public void remove() {
                     if (key == null) {
                         throw new IllegalStateException();
@@ -634,19 +861,22 @@
                 Object key;
                 int index = 0;
 
+                @Override
                 public boolean hasNext() {
                     return index < ids.length;
                 }
 
+                @Override
                 public Object next() {
                     try {
                         return (key = ids[index++]);
-                    } catch(ArrayIndexOutOfBoundsException e) {
+                    } catch (ArrayIndexOutOfBoundsException e) {
                         key = null;
                         throw new NoSuchElementException();
                     }
                 }
 
+                @Override
                 public void remove() {
                     if (key == null) {
                         throw new IllegalStateException();
@@ -654,7 +884,7 @@
                     NativeObject.this.remove(key);
                     key = null;
                 }
-           };
+            };
         }
 
         @Override
@@ -672,14 +902,17 @@
                 Object key;
                 int index = 0;
 
+                @Override
                 public boolean hasNext() {
                     return index < ids.length;
                 }
 
+                @Override
                 public Object next() {
                     return get((key = ids[index++]));
                 }
 
+                @Override
                 public void remove() {
                     if (key == null) {
                         throw new IllegalStateException();
@@ -696,79 +929,86 @@
         }
     }
 
-
-// #string_id_map#
-
     @Override
-    protected int findPrototypeId(String s)
-    {
+    protected int findPrototypeId(String s) {
         int id;
-// #generated# Last update: 2007-05-09 08:15:55 EDT
-        L0: { id = 0; String X = null; int c;
-            L: switch (s.length()) {
-            case 7: X="valueOf";id=Id_valueOf; break L;
-            case 8: c=s.charAt(3);
-                if (c=='o') { X="toSource";id=Id_toSource; }
-                else if (c=='t') { X="toString";id=Id_toString; }
-                break L;
-            case 11: X="constructor";id=Id_constructor; break L;
-            case 13: X="isPrototypeOf";id=Id_isPrototypeOf; break L;
-            case 14: c=s.charAt(0);
-                if (c=='h') { X="hasOwnProperty";id=Id_hasOwnProperty; }
-                else if (c=='t') { X="toLocaleString";id=Id_toLocaleString; }
-                break L;
-            case 16: c=s.charAt(2);
-                if (c=='d') {
-                    c=s.charAt(8);
-                    if (c=='G') { X="__defineGetter__";id=Id___defineGetter__; }
-                    else if (c=='S') { X="__defineSetter__";id=Id___defineSetter__; }
-                }
-                else if (c=='l') {
-                    c=s.charAt(8);
-                    if (c=='G') { X="__lookupGetter__";id=Id___lookupGetter__; }
-                    else if (c=='S') { X="__lookupSetter__";id=Id___lookupSetter__; }
-                }
-                break L;
-            case 20: X="propertyIsEnumerable";id=Id_propertyIsEnumerable; break L;
-            }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-            break L0;
+        switch (s) {
+            case "constructor":
+                id = Id_constructor;
+                break;
+            case "toString":
+                id = Id_toString;
+                break;
+            case "toLocaleString":
+                id = Id_toLocaleString;
+                break;
+            case "valueOf":
+                id = Id_valueOf;
+                break;
+            case "hasOwnProperty":
+                id = Id_hasOwnProperty;
+                break;
+            case "propertyIsEnumerable":
+                id = Id_propertyIsEnumerable;
+                break;
+            case "isPrototypeOf":
+                id = Id_isPrototypeOf;
+                break;
+            case "toSource":
+                id = Id_toSource;
+                break;
+            case "__defineGetter__":
+                id = Id___defineGetter__;
+                break;
+            case "__defineSetter__":
+                id = Id___defineSetter__;
+                break;
+            case "__lookupGetter__":
+                id = Id___lookupGetter__;
+                break;
+            case "__lookupSetter__":
+                id = Id___lookupSetter__;
+                break;
+            default:
+                id = 0;
+                break;
         }
-// #/generated#
         return id;
     }
 
-    private static final int
-        ConstructorId_getPrototypeOf = -1,
-        ConstructorId_keys = -2,
-        ConstructorId_getOwnPropertyNames = -3,
-        ConstructorId_getOwnPropertyDescriptor = -4,
-        ConstructorId_defineProperty = -5,
-        ConstructorId_isExtensible = -6,
-        ConstructorId_preventExtensions = -7,
-        ConstructorId_defineProperties= -8,
-        ConstructorId_create = -9,
-        ConstructorId_isSealed = -10,
-        ConstructorId_isFrozen = -11,
-        ConstructorId_seal = -12,
-        ConstructorId_freeze = -13,
-        ConstructorId_getOwnPropertySymbols = -14,
-        ConstructorId_assign = -15,
-        ConstructorId_is = -16,
-
-        Id_constructor           = 1,
-        Id_toString              = 2,
-        Id_toLocaleString        = 3,
-        Id_valueOf               = 4,
-        Id_hasOwnProperty        = 5,
-        Id_propertyIsEnumerable  = 6,
-        Id_isPrototypeOf         = 7,
-        Id_toSource              = 8,
-        Id___defineGetter__      = 9,
-        Id___defineSetter__      = 10,
-        Id___lookupGetter__      = 11,
-        Id___lookupSetter__      = 12,
-        MAX_PROTOTYPE_ID         = 12;
-
-// #/string_id_map#
+    private static final int ConstructorId_getPrototypeOf = -1,
+            ConstructorId_keys = -2,
+            ConstructorId_getOwnPropertyNames = -3,
+            ConstructorId_getOwnPropertyDescriptor = -4,
+            ConstructorId_defineProperty = -5,
+            ConstructorId_isExtensible = -6,
+            ConstructorId_preventExtensions = -7,
+            ConstructorId_defineProperties = -8,
+            ConstructorId_create = -9,
+            ConstructorId_isSealed = -10,
+            ConstructorId_isFrozen = -11,
+            ConstructorId_seal = -12,
+            ConstructorId_freeze = -13,
+            ConstructorId_getOwnPropertySymbols = -14,
+            ConstructorId_assign = -15,
+            ConstructorId_is = -16,
+
+            // ES6
+            ConstructorId_setPrototypeOf = -17,
+            ConstructorId_entries = -18,
+            ConstructorId_fromEntries = -19,
+            ConstructorId_values = -20,
+            Id_constructor = 1,
+            Id_toString = 2,
+            Id_toLocaleString = 3,
+            Id_valueOf = 4,
+            Id_hasOwnProperty = 5,
+            Id_propertyIsEnumerable = 6,
+            Id_isPrototypeOf = 7,
+            Id_toSource = 8,
+            Id___defineGetter__ = 9,
+            Id___defineSetter__ = 10,
+            Id___lookupGetter__ = 11,
+            Id___lookupSetter__ = 12,
+            MAX_PROTOTYPE_ID = 12;
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativePromise.java rhino-1.7.14/src/org/mozilla/javascript/NativePromise.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativePromise.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/NativePromise.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,798 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+import java.util.ArrayList;
+import org.mozilla.javascript.TopLevel.NativeErrors;
+
+public class NativePromise extends ScriptableObject {
+
+    enum State {
+        PENDING,
+        FULFILLED,
+        REJECTED
+    }
+
+    enum ReactionType {
+        FULFILL,
+        REJECT
+    }
+
+    private State state = State.PENDING;
+    private Object result = null;
+    private boolean handled = false;
+
+    private ArrayList<Reaction> fulfillReactions = new ArrayList<>();
+    private ArrayList<Reaction> rejectReactions = new ArrayList<>();
+
+    public static void init(Context cx, Scriptable scope, boolean sealed) {
+        LambdaConstructor constructor =
+                new LambdaConstructor(
+                        scope,
+                        "Promise",
+                        1,
+                        LambdaConstructor.CONSTRUCTOR_NEW,
+                        NativePromise::constructor);
+        constructor.setStandardPropertyAttributes(DONTENUM | READONLY);
+        constructor.setPrototypePropertyAttributes(DONTENUM | READONLY | PERMANENT);
+
+        constructor.defineConstructorMethod(
+                scope, "resolve", 1, NativePromise::resolve, DONTENUM, DONTENUM | READONLY);
+        constructor.defineConstructorMethod(
+                scope, "reject", 1, NativePromise::reject, DONTENUM, DONTENUM | READONLY);
+        constructor.defineConstructorMethod(
+                scope, "all", 1, NativePromise::all, DONTENUM, DONTENUM | READONLY);
+        constructor.defineConstructorMethod(
+                scope, "race", 1, NativePromise::race, DONTENUM, DONTENUM | READONLY);
+
+        ScriptableObject speciesDescriptor = (ScriptableObject) cx.newObject(scope);
+        ScriptableObject.putProperty(speciesDescriptor, "enumerable", false);
+        ScriptableObject.putProperty(speciesDescriptor, "configurable", true);
+        ScriptableObject.putProperty(
+                speciesDescriptor,
+                "get",
+                new LambdaFunction(
+                        scope,
+                        "get [Symbol.species]",
+                        0,
+                        (Context lcx, Scriptable lscope, Scriptable thisObj, Object[] args) ->
+                                constructor));
+        constructor.defineOwnProperty(cx, SymbolKey.SPECIES, speciesDescriptor, false);
+
+        constructor.definePrototypeMethod(
+                scope,
+                "then",
+                2,
+                (Context lcx, Scriptable lscope, Scriptable thisObj, Object[] args) -> {
+                    NativePromise self =
+                            LambdaConstructor.convertThisObject(thisObj, NativePromise.class);
+                    return self.then(lcx, lscope, constructor, args);
+                },
+                DONTENUM,
+                DONTENUM | READONLY);
+        constructor.definePrototypeMethod(
+                scope, "catch", 1, NativePromise::doCatch, DONTENUM, DONTENUM | READONLY);
+        constructor.definePrototypeMethod(
+                scope,
+                "finally",
+                1,
+                (Context lcx, Scriptable lscope, Scriptable thisObj, Object[] args) ->
+                        doFinally(lcx, lscope, thisObj, constructor, args),
+                DONTENUM,
+                DONTENUM | READONLY);
+
+        constructor.definePrototypeProperty(
+                SymbolKey.TO_STRING_TAG, "Promise", DONTENUM | READONLY);
+
+        ScriptableObject.defineProperty(scope, "Promise", constructor, DONTENUM);
+        if (sealed) {
+            constructor.sealObject();
+        }
+    }
+
+    private static Scriptable constructor(Context cx, Scriptable scope, Object[] args) {
+        if (args.length < 1 || !(args[0] instanceof Callable)) {
+            throw ScriptRuntime.typeErrorById("msg.function.expected");
+        }
+        Callable executor = (Callable) args[0];
+        NativePromise promise = new NativePromise();
+        ResolvingFunctions resolving = new ResolvingFunctions(scope, promise);
+
+        Scriptable thisObj = Undefined.SCRIPTABLE_UNDEFINED;
+        if (!cx.isStrictMode()) {
+            Scriptable tcs = cx.topCallScope;
+            if (tcs != null) {
+                thisObj = tcs;
+            }
+        }
+
+        try {
+            executor.call(cx, scope, thisObj, new Object[] {resolving.resolve, resolving.reject});
+        } catch (RhinoException re) {
+            resolving.reject.call(cx, scope, thisObj, new Object[] {getErrorObject(cx, scope, re)});
+        }
+
+        return promise;
+    }
+
+    @Override
+    public String getClassName() {
+        return "Promise";
+    }
+
+    Object getResult() {
+        return result;
+    }
+
+    // Promise.resolve
+    private static Object resolve(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        if (!ScriptRuntime.isObject(thisObj)) {
+            throw ScriptRuntime.typeErrorById("msg.arg.not.object", ScriptRuntime.typeof(thisObj));
+        }
+        Object arg = (args.length > 0 ? args[0] : Undefined.instance);
+        return resolveInternal(cx, scope, thisObj, arg);
+    }
+
+    // PromiseResolve abstract operation
+    private static Object resolveInternal(
+            Context cx, Scriptable scope, Object constructor, Object arg) {
+        if (arg instanceof NativePromise) {
+            Object argConstructor = ScriptRuntime.getObjectProp(arg, "constructor", cx, scope);
+            if (argConstructor == constructor) {
+                return arg;
+            }
+        }
+        Capability cap = new Capability(cx, scope, constructor);
+        cap.resolve.call(cx, scope, Undefined.SCRIPTABLE_UNDEFINED, new Object[] {arg});
+        return cap.promise;
+    }
+
+    // Promise.reject
+    private static Object reject(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        if (!ScriptRuntime.isObject(thisObj)) {
+            throw ScriptRuntime.typeErrorById("msg.arg.not.object", ScriptRuntime.typeof(thisObj));
+        }
+        Object arg = (args.length > 0 ? args[0] : Undefined.instance);
+        Capability cap = new Capability(cx, scope, thisObj);
+        cap.reject.call(cx, scope, Undefined.SCRIPTABLE_UNDEFINED, new Object[] {arg});
+        return cap.promise;
+    }
+
+    // Promise.all
+    private static Object all(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        Capability cap = new Capability(cx, scope, thisObj);
+        Object arg = (args.length > 0 ? args[0] : Undefined.instance);
+
+        IteratorLikeIterable iterable;
+        try {
+            Object maybeIterable = ScriptRuntime.callIterator(arg, cx, scope);
+            iterable = new IteratorLikeIterable(cx, scope, maybeIterable);
+        } catch (RhinoException re) {
+            cap.reject.call(
+                    cx,
+                    scope,
+                    Undefined.SCRIPTABLE_UNDEFINED,
+                    new Object[] {getErrorObject(cx, scope, re)});
+            return cap.promise;
+        }
+
+        IteratorLikeIterable.Itr iterator = iterable.iterator();
+        try {
+            PromiseAllResolver resolver = new PromiseAllResolver(iterator, thisObj, cap);
+            try {
+                return resolver.resolve(cx, scope);
+            } finally {
+                if (!iterator.isDone()) {
+                    iterable.close();
+                }
+            }
+        } catch (RhinoException re) {
+            cap.reject.call(
+                    cx,
+                    scope,
+                    Undefined.SCRIPTABLE_UNDEFINED,
+                    new Object[] {getErrorObject(cx, scope, re)});
+            return cap.promise;
+        }
+    }
+
+    // Promise.race
+    private static Object race(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        Capability cap = new Capability(cx, scope, thisObj);
+        Object arg = (args.length > 0 ? args[0] : Undefined.instance);
+
+        IteratorLikeIterable iterable;
+        try {
+            Object maybeIterable = ScriptRuntime.callIterator(arg, cx, scope);
+            iterable = new IteratorLikeIterable(cx, scope, maybeIterable);
+        } catch (RhinoException re) {
+            cap.reject.call(
+                    cx,
+                    scope,
+                    Undefined.SCRIPTABLE_UNDEFINED,
+                    new Object[] {getErrorObject(cx, scope, re)});
+            return cap.promise;
+        }
+
+        IteratorLikeIterable.Itr iterator = iterable.iterator();
+        try {
+            try {
+                return performRace(cx, scope, iterator, thisObj, cap);
+            } finally {
+                if (!iterator.isDone()) {
+                    iterable.close();
+                }
+            }
+        } catch (RhinoException re) {
+            cap.reject.call(
+                    cx,
+                    scope,
+                    Undefined.SCRIPTABLE_UNDEFINED,
+                    new Object[] {getErrorObject(cx, scope, re)});
+            return cap.promise;
+        }
+    }
+
+    private static Object performRace(
+            Context cx,
+            Scriptable scope,
+            IteratorLikeIterable.Itr iterator,
+            Scriptable thisObj,
+            Capability cap) {
+        Callable resolve = ScriptRuntime.getPropFunctionAndThis(thisObj, "resolve", cx, scope);
+        Scriptable localThis = ScriptRuntime.lastStoredScriptable(cx);
+
+        // Manually iterate for exception handling purposes
+        while (true) {
+            boolean hasNext;
+            Object nextVal = Undefined.instance;
+            boolean nextOk = false;
+            try {
+                hasNext = iterator.hasNext();
+                if (hasNext) {
+                    nextVal = iterator.next();
+                }
+                nextOk = true;
+            } finally {
+                if (!nextOk) {
+                    iterator.setDone(true);
+                }
+            }
+
+            if (!hasNext) {
+                return cap.promise;
+            }
+
+            // Call "resolve" to get the next promise in the chain
+            Object nextPromise = resolve.call(cx, scope, localThis, new Object[] {nextVal});
+
+            // And then call "then" on it.
+            // Logic in the resolution function ensures we don't deliver duplicate results
+            Callable thenFunc =
+                    ScriptRuntime.getPropFunctionAndThis(nextPromise, "then", cx, scope);
+            thenFunc.call(
+                    cx,
+                    scope,
+                    ScriptRuntime.lastStoredScriptable(cx),
+                    new Object[] {cap.resolve, cap.reject});
+        }
+    }
+
+    // Promise.prototype.then
+    private Object then(
+            Context cx, Scriptable scope, LambdaConstructor defaultConstructor, Object[] args) {
+        Constructable constructable =
+                AbstractEcmaObjectOperations.speciesConstructor(cx, this, defaultConstructor);
+        Capability capability = new Capability(cx, scope, constructable);
+
+        Callable onFulfilled = null;
+        if (args.length >= 1 && args[0] instanceof Callable) {
+            onFulfilled = (Callable) args[0];
+        }
+        Callable onRejected = null;
+        if (args.length >= 2 && args[1] instanceof Callable) {
+            onRejected = (Callable) args[1];
+        }
+
+        Reaction fulfillReaction = new Reaction(capability, ReactionType.FULFILL, onFulfilled);
+        Reaction rejectReaction = new Reaction(capability, ReactionType.REJECT, onRejected);
+
+        if (state == State.PENDING) {
+            fulfillReactions.add(fulfillReaction);
+            rejectReactions.add(rejectReaction);
+        } else if (state == State.FULFILLED) {
+            cx.enqueueMicrotask(() -> fulfillReaction.invoke(cx, scope, result));
+        } else {
+            assert (state == State.REJECTED);
+            if (!handled) {
+                cx.getUnhandledPromiseTracker().promiseHandled(this);
+            }
+            cx.enqueueMicrotask(() -> rejectReaction.invoke(cx, scope, result));
+        }
+        handled = true;
+        return capability.promise;
+    }
+
+    // Promise.prototype.catch
+    private static Object doCatch(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        Object arg = (args.length > 0 ? args[0] : Undefined.instance);
+        Scriptable coercedThis = ScriptRuntime.toObject(cx, scope, thisObj);
+        // No guarantee that the caller didn't change the prototype of "then"!
+        Callable thenFunc = ScriptRuntime.getPropFunctionAndThis(coercedThis, "then", cx, scope);
+        return thenFunc.call(
+                cx,
+                scope,
+                ScriptRuntime.lastStoredScriptable(cx),
+                new Object[] {Undefined.instance, arg});
+    }
+
+    // Promise.prototype.finally
+    private static Object doFinally(
+            Context cx,
+            Scriptable scope,
+            Scriptable thisObj,
+            LambdaConstructor defaultConstructor,
+            Object[] args) {
+        if (!ScriptRuntime.isObject(thisObj)) {
+            throw ScriptRuntime.typeErrorById("msg.arg.not.object", ScriptRuntime.typeof(thisObj));
+        }
+        Object onFinally = args.length > 0 ? args[0] : Undefined.SCRIPTABLE_UNDEFINED;
+        Object thenFinally = onFinally;
+        Object catchFinally = onFinally;
+        Constructable constructor =
+                AbstractEcmaObjectOperations.speciesConstructor(cx, thisObj, defaultConstructor);
+        if (onFinally instanceof Callable) {
+            Callable callableOnFinally = (Callable) thenFinally;
+            thenFinally = makeThenFinally(scope, constructor, callableOnFinally);
+            catchFinally = makeCatchFinally(scope, constructor, callableOnFinally);
+        }
+        Callable thenFunc = ScriptRuntime.getPropFunctionAndThis(thisObj, "then", cx, scope);
+        Scriptable to = ScriptRuntime.lastStoredScriptable(cx);
+        return thenFunc.call(cx, scope, to, new Object[] {thenFinally, catchFinally});
+    }
+
+    // Abstract "Then Finally Function"
+    private static Callable makeThenFinally(
+            Scriptable scope, Object constructor, Callable onFinally) {
+        return new LambdaFunction(
+                scope,
+                1,
+                (Context cx, Scriptable ls, Scriptable thisObj, Object[] args) -> {
+                    Object value = args.length > 0 ? args[0] : Undefined.instance;
+                    LambdaFunction valueThunk =
+                            new LambdaFunction(
+                                    scope,
+                                    0,
+                                    (Context vc, Scriptable vs, Scriptable vt, Object[] va) ->
+                                            value);
+                    Object result =
+                            onFinally.call(
+                                    cx,
+                                    ls,
+                                    Undefined.SCRIPTABLE_UNDEFINED,
+                                    ScriptRuntime.emptyArgs);
+                    Object promise = resolveInternal(cx, scope, constructor, result);
+                    Callable thenFunc =
+                            ScriptRuntime.getPropFunctionAndThis(promise, "then", cx, scope);
+                    return thenFunc.call(
+                            cx,
+                            scope,
+                            ScriptRuntime.lastStoredScriptable(cx),
+                            new Object[] {valueThunk});
+                });
+    }
+
+    // Abstract "Catch Finally Thrower"
+    private static Callable makeCatchFinally(
+            Scriptable scope, Object constructor, Callable onFinally) {
+        return new LambdaFunction(
+                scope,
+                1,
+                (Context cx, Scriptable ls, Scriptable thisObj, Object[] args) -> {
+                    Object reason = args.length > 0 ? args[0] : Undefined.instance;
+                    LambdaFunction reasonThrower =
+                            new LambdaFunction(
+                                    scope,
+                                    0,
+                                    (Context vc, Scriptable vs, Scriptable vt, Object[] va) -> {
+                                        throw new JavaScriptException(reason, null, 0);
+                                    });
+                    Object result =
+                            onFinally.call(
+                                    cx,
+                                    ls,
+                                    Undefined.SCRIPTABLE_UNDEFINED,
+                                    ScriptRuntime.emptyArgs);
+                    Object promise = resolveInternal(cx, scope, constructor, result);
+                    Callable thenFunc =
+                            ScriptRuntime.getPropFunctionAndThis(promise, "then", cx, scope);
+                    return thenFunc.call(
+                            cx,
+                            scope,
+                            ScriptRuntime.lastStoredScriptable(cx),
+                            new Object[] {reasonThrower});
+                });
+    }
+
+    // Abstract operation to fulfill a promise
+    private Object fulfillPromise(Context cx, Scriptable scope, Object value) {
+        assert (state == State.PENDING);
+        result = value;
+        ArrayList<Reaction> reactions = fulfillReactions;
+        fulfillReactions = new ArrayList<>();
+        if (!rejectReactions.isEmpty()) {
+            rejectReactions = new ArrayList<>();
+        }
+        state = State.FULFILLED;
+        for (Reaction r : reactions) {
+            cx.enqueueMicrotask(() -> r.invoke(cx, scope, value));
+        }
+        return Undefined.instance;
+    }
+
+    // Abstract operation to reject a promise.
+    private Object rejectPromise(Context cx, Scriptable scope, Object reason) {
+        assert (state == State.PENDING);
+        result = reason;
+        ArrayList<Reaction> reactions = rejectReactions;
+        rejectReactions = new ArrayList<>();
+        if (!fulfillReactions.isEmpty()) {
+            fulfillReactions = new ArrayList<>();
+        }
+        state = State.REJECTED;
+        cx.getUnhandledPromiseTracker().promiseRejected(this);
+        for (Reaction r : reactions) {
+            cx.enqueueMicrotask(() -> r.invoke(cx, scope, reason));
+        }
+        return Undefined.instance;
+    }
+
+    // Promise Resolve Thenable Job.
+    // This gets called by the "resolving func" as a microtask.
+    private void callThenable(Context cx, Scriptable scope, Object resolution, Callable thenFunc) {
+        ResolvingFunctions resolving = new ResolvingFunctions(scope, this);
+        Scriptable thisObj =
+                (resolution instanceof Scriptable
+                        ? (Scriptable) resolution
+                        : Undefined.SCRIPTABLE_UNDEFINED);
+        try {
+            thenFunc.call(cx, scope, thisObj, new Object[] {resolving.resolve, resolving.reject});
+        } catch (RhinoException re) {
+            resolving.reject.call(
+                    cx,
+                    scope,
+                    Undefined.SCRIPTABLE_UNDEFINED,
+                    new Object[] {getErrorObject(cx, scope, re)});
+        }
+    }
+
+    private static Object getErrorObject(Context cx, Scriptable scope, RhinoException re) {
+        if (re instanceof JavaScriptException) {
+            return ((JavaScriptException) re).getValue();
+        }
+
+        TopLevel.NativeErrors constructor = NativeErrors.Error;
+        if (re instanceof EcmaError) {
+            EcmaError ee = (EcmaError) re;
+            switch (ee.getName()) {
+                case "EvalError":
+                    constructor = NativeErrors.EvalError;
+                    break;
+                case "RangeError":
+                    constructor = NativeErrors.RangeError;
+                    break;
+                case "ReferenceError":
+                    constructor = NativeErrors.ReferenceError;
+                    break;
+                case "SyntaxError":
+                    constructor = NativeErrors.SyntaxError;
+                    break;
+                case "TypeError":
+                    constructor = NativeErrors.TypeError;
+                    break;
+                case "URIError":
+                    constructor = NativeErrors.URIError;
+                    break;
+                case "InternalError":
+                    constructor = NativeErrors.InternalError;
+                    break;
+                case "JavaException":
+                    constructor = NativeErrors.JavaException;
+                    break;
+                default:
+                    break;
+            }
+        }
+        return ScriptRuntime.newNativeError(cx, scope, constructor, new Object[] {re.getMessage()});
+    }
+
+    // Output of "CreateResolvingFunctions." Carries with it an "alreadyResolved" state,
+    // so we make it a separate object. This actually fires resolution functions on
+    // the passed callbacks.
+    private static class ResolvingFunctions {
+
+        private boolean alreadyResolved = false;
+        LambdaFunction resolve;
+        LambdaFunction reject;
+
+        ResolvingFunctions(Scriptable topScope, NativePromise promise) {
+            resolve =
+                    new LambdaFunction(
+                            topScope,
+                            1,
+                            (Context cx, Scriptable scope, Scriptable thisObj, Object[] args) ->
+                                    resolve(
+                                            cx,
+                                            scope,
+                                            promise,
+                                            (args.length > 0 ? args[0] : Undefined.instance)));
+            resolve.setStandardPropertyAttributes(DONTENUM | READONLY);
+            reject =
+                    new LambdaFunction(
+                            topScope,
+                            1,
+                            (Context cx, Scriptable scope, Scriptable thisObj, Object[] args) ->
+                                    reject(
+                                            cx,
+                                            scope,
+                                            promise,
+                                            (args.length > 0 ? args[0] : Undefined.instance)));
+            reject.setStandardPropertyAttributes(DONTENUM | READONLY);
+        }
+
+        private Object reject(Context cx, Scriptable scope, NativePromise promise, Object reason) {
+            if (alreadyResolved) {
+                return Undefined.instance;
+            }
+            alreadyResolved = true;
+            return promise.rejectPromise(cx, scope, reason);
+        }
+
+        private Object resolve(
+                Context cx, Scriptable scope, NativePromise promise, Object resolution) {
+            if (alreadyResolved) {
+                return Undefined.instance;
+            }
+            alreadyResolved = true;
+
+            if (resolution == promise) {
+                Object err =
+                        ScriptRuntime.newNativeError(
+                                cx,
+                                scope,
+                                NativeErrors.TypeError,
+                                new Object[] {"No promise self-resolution"});
+                return promise.rejectPromise(cx, scope, err);
+            }
+
+            if (!ScriptRuntime.isObject(resolution)) {
+                return promise.fulfillPromise(cx, scope, resolution);
+            }
+
+            Scriptable sresolution = ScriptableObject.ensureScriptable(resolution);
+            Object thenObj = ScriptableObject.getProperty(sresolution, "then");
+            if (!(thenObj instanceof Callable)) {
+                return promise.fulfillPromise(cx, scope, resolution);
+            }
+
+            cx.enqueueMicrotask(
+                    () -> promise.callThenable(cx, scope, resolution, (Callable) thenObj));
+            return Undefined.instance;
+        }
+    }
+
+    // "Promise Reaction" record. This is an input to the microtask.
+    private static class Reaction {
+        Capability capability;
+        ReactionType reaction = ReactionType.REJECT;
+        Callable handler;
+
+        Reaction(Capability cap, ReactionType type, Callable handler) {
+            this.capability = cap;
+            this.reaction = type;
+            this.handler = handler;
+        }
+
+        // Implementation of NewPromiseReactionJob
+        void invoke(Context cx, Scriptable scope, Object arg) {
+            try {
+                Object result = null;
+                if (handler == null) {
+                    switch (reaction) {
+                        case FULFILL:
+                            result = arg;
+                            break;
+                        case REJECT:
+                            capability.reject.call(
+                                    cx, scope, Undefined.SCRIPTABLE_UNDEFINED, new Object[] {arg});
+                            return;
+                    }
+                } else {
+                    result =
+                            handler.call(
+                                    cx, scope, Undefined.SCRIPTABLE_UNDEFINED, new Object[] {arg});
+                }
+                capability.resolve.call(
+                        cx, scope, Undefined.SCRIPTABLE_UNDEFINED, new Object[] {result});
+
+            } catch (RhinoException re) {
+                capability.reject.call(
+                        cx,
+                        scope,
+                        Undefined.SCRIPTABLE_UNDEFINED,
+                        new Object[] {getErrorObject(cx, scope, re)});
+            }
+        }
+    }
+
+    // "Promise Capability Record"
+    // This abstracts a promise from the specific native implementation by keeping track
+    // of the "resolve" and "reject" functions.
+    private static class Capability {
+        Object promise;
+        private Object rawResolve = Undefined.instance;
+        Callable resolve;
+        private Object rawReject = Undefined.instance;
+        Callable reject;
+
+        // Given an object that represents a constructor function, execute it as if it
+        // meets the "Promise" constructor pattern, which takes a function that will
+        // be called with "resolve" and "reject" functions.
+        Capability(Context topCx, Scriptable topScope, Object pc) {
+            if (!(pc instanceof Constructable)) {
+                throw ScriptRuntime.typeErrorById("msg.constructor.expected");
+            }
+            Constructable promiseConstructor = (Constructable) pc;
+            LambdaFunction executorFunc =
+                    new LambdaFunction(
+                            topScope,
+                            2,
+                            (Context cx, Scriptable scope, Scriptable thisObj, Object[] args) ->
+                                    executor(args));
+            executorFunc.setStandardPropertyAttributes(DONTENUM | READONLY);
+
+            promise = promiseConstructor.construct(topCx, topScope, new Object[] {executorFunc});
+
+            if (!(rawResolve instanceof Callable)) {
+                throw ScriptRuntime.typeErrorById("msg.function.expected");
+            }
+            resolve = (Callable) rawResolve;
+
+            if (!(rawReject instanceof Callable)) {
+                throw ScriptRuntime.typeErrorById("msg.function.expected");
+            }
+            reject = (Callable) rawReject;
+        }
+
+        private Object executor(Object[] args) {
+            if (!Undefined.isUndefined(rawResolve) || !Undefined.isUndefined(rawReject)) {
+                throw ScriptRuntime.typeErrorById("msg.promise.capability.state");
+            }
+            if (args.length > 0) {
+                rawResolve = args[0];
+            }
+            if (args.length > 1) {
+                rawReject = args[1];
+            }
+            return Undefined.instance;
+        }
+    }
+
+    // This object keeps track of the state necessary to execute Promise.all
+    private static class PromiseAllResolver {
+        // Limit the number of promises in Promise.all the same as it is in V8.
+        private static final int MAX_PROMISES = 1 << 21;
+
+        final ArrayList<Object> values = new ArrayList<>();
+        int remainingElements = 1;
+
+        IteratorLikeIterable.Itr iterator;
+        Scriptable thisObj;
+        Capability capability;
+
+        PromiseAllResolver(IteratorLikeIterable.Itr iter, Scriptable thisObj, Capability cap) {
+            this.iterator = iter;
+            this.thisObj = thisObj;
+            this.capability = cap;
+        }
+
+        Object resolve(Context topCx, Scriptable topScope) {
+            int index = 0;
+            // Do this first because we should catch any exception before
+            // invoking the iterator.
+            Callable resolve =
+                    ScriptRuntime.getPropFunctionAndThis(thisObj, "resolve", topCx, topScope);
+            Scriptable storedThis = ScriptRuntime.lastStoredScriptable(topCx);
+
+            // Iterate manually because we need to catch exceptions in a special way.
+            while (true) {
+                if (index == MAX_PROMISES) {
+                    throw ScriptRuntime.rangeErrorById("msg.promise.all.toobig");
+                }
+                boolean hasNext;
+                Object nextVal = Undefined.instance;
+                boolean nextOk = false;
+                try {
+                    hasNext = iterator.hasNext();
+                    if (hasNext) {
+                        nextVal = iterator.next();
+                    }
+                    nextOk = true;
+                } finally {
+                    if (!nextOk) {
+                        iterator.setDone(true);
+                    }
+                }
+
+                if (!hasNext) {
+                    if (--remainingElements == 0) {
+                        finalResolution(topCx, topScope);
+                    }
+                    return capability.promise;
+                }
+
+                values.add(Undefined.instance);
+
+                // Call "resolve" to get the next promise in the chain
+                Object nextPromise =
+                        resolve.call(topCx, topScope, storedThis, new Object[] {nextVal});
+
+                // Create a resolution func that will stash its result in the right place
+                PromiseElementResolver eltResolver = new PromiseElementResolver(index);
+                LambdaFunction resolveFunc =
+                        new LambdaFunction(
+                                topScope,
+                                1,
+                                (Context cx, Scriptable scope, Scriptable thisObj, Object[] args) ->
+                                        eltResolver.resolve(
+                                                cx,
+                                                scope,
+                                                (args.length > 0 ? args[0] : Undefined.instance),
+                                                this));
+                resolveFunc.setStandardPropertyAttributes(DONTENUM | READONLY);
+                remainingElements++;
+
+                // Call "then" on the promise with the resolution func
+                Callable thenFunc =
+                        ScriptRuntime.getPropFunctionAndThis(nextPromise, "then", topCx, topScope);
+                thenFunc.call(
+                        topCx,
+                        topScope,
+                        ScriptRuntime.lastStoredScriptable(topCx),
+                        new Object[] {resolveFunc, capability.reject});
+                index++;
+            }
+        }
+
+        void finalResolution(Context cx, Scriptable scope) {
+            Scriptable newArray = cx.newArray(scope, values.toArray());
+            capability.resolve.call(
+                    cx, scope, Undefined.SCRIPTABLE_UNDEFINED, new Object[] {newArray});
+        }
+    }
+
+    // This object keeps track of the state necessary to resolve one element in Promise.all
+    private static class PromiseElementResolver {
+
+        private boolean alreadyCalled = false;
+        private final int index;
+
+        PromiseElementResolver(int ix) {
+            this.index = ix;
+        }
+
+        Object resolve(Context cx, Scriptable scope, Object result, PromiseAllResolver resolver) {
+            if (alreadyCalled) {
+                return Undefined.instance;
+            }
+            alreadyCalled = true;
+            resolver.values.set(index, result);
+            if (--resolver.remainingElements == 0) {
+                resolver.finalResolution(cx, scope);
+            }
+            return Undefined.instance;
+        }
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeScript.java rhino-1.7.14/src/org/mozilla/javascript/NativeScript.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeScript.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeScript.java	2022-01-06 22:57:21.000000000 +0100
@@ -9,47 +9,37 @@
 /**
  * The JavaScript Script object.
  *
- * Note that the C version of the engine uses XDR as the format used
- * by freeze and thaw. Since this depends on the internal format of
- * structures in the C runtime, we cannot duplicate it.
+ * <p>Note that the C version of the engine uses XDR as the format used by freeze and thaw. Since
+ * this depends on the internal format of structures in the C runtime, we cannot duplicate it.
  *
- * Since we cannot replace 'this' as a result of the compile method,
- * will forward requests to execute to the nonnull 'script' field.
+ * <p>Since we cannot replace 'this' as a result of the compile method, will forward requests to
+ * execute to the nonnull 'script' field.
  *
  * @since 1.3
  * @author Norris Boyd
  */
-
-class NativeScript extends BaseFunction
-{
-    static final long serialVersionUID = -6795101161980121700L;
+class NativeScript extends BaseFunction {
+    private static final long serialVersionUID = -6795101161980121700L;
 
     private static final Object SCRIPT_TAG = "Script";
 
-    static void init(Scriptable scope, boolean sealed)
-    {
+    static void init(Scriptable scope, boolean sealed) {
         NativeScript obj = new NativeScript(null);
         obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
     }
 
-    private NativeScript(Script script)
-    {
+    private NativeScript(Script script) {
         this.script = script;
     }
 
-    /**
-     * Returns the name of this JavaScript class, "Script".
-     */
+    /** Returns the name of this JavaScript class, "Script". */
     @Override
-    public String getClassName()
-    {
+    public String getClassName() {
         return "Script";
     }
 
     @Override
-    public Object call(Context cx, Scriptable scope, Scriptable thisObj,
-                       Object[] args)
-    {
+    public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         if (script != null) {
             return script.exec(cx, scope);
         }
@@ -57,98 +47,104 @@
     }
 
     @Override
-    public Scriptable construct(Context cx, Scriptable scope, Object[] args)
-    {
-        throw Context.reportRuntimeError0("msg.script.is.not.constructor");
+    public Scriptable construct(Context cx, Scriptable scope, Object[] args) {
+        throw Context.reportRuntimeErrorById("msg.script.is.not.constructor");
     }
 
     @Override
-    public int getLength()
-    {
+    public int getLength() {
         return 0;
     }
 
     @Override
-    public int getArity()
-    {
+    public int getArity() {
         return 0;
     }
 
     @Override
-    String decompile(int indent, int flags)
-    {
+    String decompile(int indent, int flags) {
         if (script instanceof NativeFunction) {
-            return ((NativeFunction)script).decompile(indent, flags);
+            return ((NativeFunction) script).decompile(indent, flags);
         }
         return super.decompile(indent, flags);
     }
 
     @Override
-    protected void initPrototypeId(int id)
-    {
+    protected void initPrototypeId(int id) {
         String s;
         int arity;
         switch (id) {
-          case Id_constructor: arity=1; s="constructor"; break;
-          case Id_toString:    arity=0; s="toString";    break;
-          case Id_exec:        arity=0; s="exec";        break;
-          case Id_compile:     arity=1; s="compile";     break;
-          default: throw new IllegalArgumentException(String.valueOf(id));
+            case Id_constructor:
+                arity = 1;
+                s = "constructor";
+                break;
+            case Id_toString:
+                arity = 0;
+                s = "toString";
+                break;
+            case Id_exec:
+                arity = 0;
+                s = "exec";
+                break;
+            case Id_compile:
+                arity = 1;
+                s = "compile";
+                break;
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
         }
         initPrototypeMethod(SCRIPT_TAG, id, s, arity);
     }
 
     @Override
-    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
-                             Scriptable thisObj, Object[] args)
-    {
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         if (!f.hasTag(SCRIPT_TAG)) {
             return super.execIdCall(f, cx, scope, thisObj, args);
         }
         int id = f.methodId();
         switch (id) {
-          case Id_constructor: {
-            String source = (args.length == 0)
-                            ? ""
-                            : ScriptRuntime.toString(args[0]);
-            Script script = compile(cx, source);
-            NativeScript nscript = new NativeScript(script);
-            ScriptRuntime.setObjectProtoAndParent(nscript, scope);
-            return nscript;
-          }
-
-          case Id_toString: {
-            NativeScript real = realThis(thisObj, f);
-            Script realScript = real.script;
-            if (realScript == null) { return ""; }
-            return cx.decompileScript(realScript, 0);
-          }
-
-          case Id_exec: {
-            throw Context.reportRuntimeError1(
-                "msg.cant.call.indirect", "exec");
-          }
-
-          case Id_compile: {
-            NativeScript real = realThis(thisObj, f);
-            String source = ScriptRuntime.toString(args, 0);
-            real.script = compile(cx, source);
-            return real;
-          }
+            case Id_constructor:
+                {
+                    String source = (args.length == 0) ? "" : ScriptRuntime.toString(args[0]);
+                    Script script = compile(cx, source);
+                    NativeScript nscript = new NativeScript(script);
+                    ScriptRuntime.setObjectProtoAndParent(nscript, scope);
+                    return nscript;
+                }
+
+            case Id_toString:
+                {
+                    NativeScript real = realThis(thisObj, f);
+                    Script realScript = real.script;
+                    if (realScript == null) {
+                        return "";
+                    }
+                    return cx.decompileScript(realScript, 0);
+                }
+
+            case Id_exec:
+                {
+                    throw Context.reportRuntimeErrorById("msg.cant.call.indirect", "exec");
+                }
+
+            case Id_compile:
+                {
+                    NativeScript real = realThis(thisObj, f);
+                    String source = ScriptRuntime.toString(args, 0);
+                    real.script = compile(cx, source);
+                    return real;
+                }
         }
         throw new IllegalArgumentException(String.valueOf(id));
     }
 
-    private static NativeScript realThis(Scriptable thisObj, IdFunctionObject f)
-    {
-        if (!(thisObj instanceof NativeScript))
-            throw incompatibleCallError(f);
-        return (NativeScript)thisObj;
+    private static NativeScript realThis(Scriptable thisObj, IdFunctionObject f) {
+        return ensureType(thisObj, NativeScript.class, f);
     }
 
-    private static Script compile(Context cx, String source)
-    {
-        int[] linep = { 0 };
+    private static Script compile(Context cx, String source) {
+        int[] linep = {0};
         String filename = Context.getSourcePositionFromStack(linep);
         if (filename == null) {
             filename = "<Script object>";
@@ -156,40 +152,37 @@
         }
         ErrorReporter reporter;
         reporter = DefaultErrorReporter.forEval(cx.getErrorReporter());
-        return cx.compileString(source, null, reporter, filename,
-                                linep[0], null);
+        return cx.compileString(source, null, reporter, filename, linep[0], null);
     }
 
-// #string_id_map#
-
     @Override
-    protected int findPrototypeId(String s)
-    {
+    protected int findPrototypeId(String s) {
         int id;
-// #generated# Last update: 2007-05-09 08:16:01 EDT
-        L0: { id = 0; String X = null;
-            L: switch (s.length()) {
-            case 4: X="exec";id=Id_exec; break L;
-            case 7: X="compile";id=Id_compile; break L;
-            case 8: X="toString";id=Id_toString; break L;
-            case 11: X="constructor";id=Id_constructor; break L;
-            }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-            break L0;
+        switch (s) {
+            case "constructor":
+                id = Id_constructor;
+                break;
+            case "toString":
+                id = Id_toString;
+                break;
+            case "compile":
+                id = Id_compile;
+                break;
+            case "exec":
+                id = Id_exec;
+                break;
+            default:
+                id = 0;
+                break;
         }
-// #/generated#
         return id;
     }
 
-    private static final int
-        Id_constructor    = 1,
-        Id_toString       = 2,
-        Id_compile        = 3,
-        Id_exec           = 4,
-        MAX_PROTOTYPE_ID  = 4;
-
-// #/string_id_map#
+    private static final int Id_constructor = 1,
+            Id_toString = 2,
+            Id_compile = 3,
+            Id_exec = 4,
+            MAX_PROTOTYPE_ID = 4;
 
     private Script script;
 }
-
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeSet.java rhino-1.7.14/src/org/mozilla/javascript/NativeSet.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeSet.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeSet.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,312 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+import java.util.Iterator;
+
+public class NativeSet extends IdScriptableObject {
+    private static final long serialVersionUID = -8442212766987072986L;
+    private static final Object SET_TAG = "Set";
+    static final String ITERATOR_TAG = "Set Iterator";
+
+    static final SymbolKey GETSIZE = new SymbolKey("[Symbol.getSize]");
+
+    private final Hashtable entries = new Hashtable();
+
+    private boolean instanceOfSet = false;
+
+    static void init(Context cx, Scriptable scope, boolean sealed) {
+        NativeSet obj = new NativeSet();
+        obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, false);
+
+        ScriptableObject desc = (ScriptableObject) cx.newObject(scope);
+        desc.put("enumerable", desc, Boolean.FALSE);
+        desc.put("configurable", desc, Boolean.TRUE);
+        desc.put("get", desc, obj.get(GETSIZE, obj));
+        obj.defineOwnProperty(cx, "size", desc);
+
+        if (sealed) {
+            obj.sealObject();
+        }
+    }
+
+    @Override
+    public String getClassName() {
+        return "Set";
+    }
+
+    @Override
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        if (!f.hasTag(SET_TAG)) {
+            return super.execIdCall(f, cx, scope, thisObj, args);
+        }
+        final int id = f.methodId();
+        switch (id) {
+            case Id_constructor:
+                if (thisObj == null) {
+                    NativeSet ns = new NativeSet();
+                    ns.instanceOfSet = true;
+                    if (args.length > 0) {
+                        loadFromIterable(cx, scope, ns, NativeMap.key(args));
+                    }
+                    return ns;
+                } else {
+                    throw ScriptRuntime.typeErrorById("msg.no.new", "Set");
+                }
+            case Id_add:
+                return realThis(thisObj, f).js_add(NativeMap.key(args));
+            case Id_delete:
+                return realThis(thisObj, f).js_delete(NativeMap.key(args));
+            case Id_has:
+                return realThis(thisObj, f).js_has(NativeMap.key(args));
+            case Id_clear:
+                return realThis(thisObj, f).js_clear();
+            case Id_values:
+                return realThis(thisObj, f)
+                        .js_iterator(scope, NativeCollectionIterator.Type.VALUES);
+            case Id_entries:
+                return realThis(thisObj, f).js_iterator(scope, NativeCollectionIterator.Type.BOTH);
+            case Id_forEach:
+                return realThis(thisObj, f)
+                        .js_forEach(
+                                cx,
+                                scope,
+                                NativeMap.key(args),
+                                args.length > 1 ? args[1] : Undefined.instance);
+            case SymbolId_getSize:
+                return realThis(thisObj, f).js_getSize();
+        }
+        throw new IllegalArgumentException("Set.prototype has no method: " + f.getFunctionName());
+    }
+
+    private Object js_add(Object k) {
+        // Special handling of "negative zero" from the spec.
+        Object key = k;
+        if ((key instanceof Number) && ((Number) key).doubleValue() == ScriptRuntime.negativeZero) {
+            key = ScriptRuntime.zeroObj;
+        }
+        entries.put(key, key);
+        return this;
+    }
+
+    private Object js_delete(Object arg) {
+        return Boolean.valueOf(entries.deleteEntry(arg));
+    }
+
+    private Object js_has(Object arg) {
+        return Boolean.valueOf(entries.has(arg));
+    }
+
+    private Object js_clear() {
+        entries.clear();
+        return Undefined.instance;
+    }
+
+    private Object js_getSize() {
+        return Integer.valueOf(entries.size());
+    }
+
+    private Object js_iterator(Scriptable scope, NativeCollectionIterator.Type type) {
+        return new NativeCollectionIterator(scope, ITERATOR_TAG, type, entries.iterator());
+    }
+
+    private Object js_forEach(Context cx, Scriptable scope, Object arg1, Object arg2) {
+        if (!(arg1 instanceof Callable)) {
+            throw ScriptRuntime.notFunctionError(arg1);
+        }
+        final Callable f = (Callable) arg1;
+
+        boolean isStrict = cx.isStrictMode();
+        Iterator<Hashtable.Entry> i = entries.iterator();
+        while (i.hasNext()) {
+            // Per spec must convert every time so that primitives are always regenerated...
+            Scriptable thisObj = ScriptRuntime.toObjectOrNull(cx, arg2, scope);
+
+            if (thisObj == null && !isStrict) {
+                thisObj = scope;
+            }
+            if (thisObj == null) {
+                thisObj = Undefined.SCRIPTABLE_UNDEFINED;
+            }
+
+            final Hashtable.Entry e = i.next();
+            f.call(cx, scope, thisObj, new Object[] {e.value, e.value, this});
+        }
+        return Undefined.instance;
+    }
+
+    /**
+     * If an "iterable" object was passed to the constructor, there are many many things to do. This
+     * is common code with NativeWeakSet.
+     */
+    static void loadFromIterable(Context cx, Scriptable scope, ScriptableObject set, Object arg1) {
+        if ((arg1 == null) || Undefined.instance.equals(arg1)) {
+            return;
+        }
+
+        // Call the "[Symbol.iterator]" property as a function.
+        Object ito = ScriptRuntime.callIterator(arg1, cx, scope);
+        if (Undefined.instance.equals(ito)) {
+            // Per spec, ignore if the iterator returns undefined
+            return;
+        }
+
+        // Find the "add" function of our own prototype, since it might have
+        // been replaced. Since we're not fully constructed yet, create a dummy instance
+        // so that we can get our own prototype.
+        ScriptableObject dummy = ensureScriptableObject(cx.newObject(scope, set.getClassName()));
+        final Callable add =
+                ScriptRuntime.getPropFunctionAndThis(dummy.getPrototype(), "add", cx, scope);
+        // Clean up the value left around by the previous function
+        ScriptRuntime.lastStoredScriptable(cx);
+
+        // Finally, run through all the iterated values and add them!
+        try (IteratorLikeIterable it = new IteratorLikeIterable(cx, scope, ito)) {
+            for (Object val : it) {
+                final Object finalVal = val == Scriptable.NOT_FOUND ? Undefined.instance : val;
+                add.call(cx, scope, set, new Object[] {finalVal});
+            }
+        }
+    }
+
+    private static NativeSet realThis(Scriptable thisObj, IdFunctionObject f) {
+        final NativeSet ns = ensureType(thisObj, NativeSet.class, f);
+        if (!ns.instanceOfSet) {
+            // If we get here, then this object doesn't have the "Set internal data slot."
+            throw ScriptRuntime.typeErrorById("msg.incompat.call", f.getFunctionName());
+        }
+
+        return ns;
+    }
+
+    @Override
+    protected void initPrototypeId(int id) {
+        switch (id) {
+            case SymbolId_getSize:
+                initPrototypeMethod(SET_TAG, id, GETSIZE, "get size", 0);
+                return;
+            case SymbolId_toStringTag:
+                initPrototypeValue(
+                        SymbolId_toStringTag,
+                        SymbolKey.TO_STRING_TAG,
+                        getClassName(),
+                        DONTENUM | READONLY);
+                return;
+                // fallthrough
+        }
+
+        String s, fnName = null;
+        int arity;
+        switch (id) {
+            case Id_constructor:
+                arity = 0;
+                s = "constructor";
+                break;
+            case Id_add:
+                arity = 1;
+                s = "add";
+                break;
+            case Id_delete:
+                arity = 1;
+                s = "delete";
+                break;
+            case Id_has:
+                arity = 1;
+                s = "has";
+                break;
+            case Id_clear:
+                arity = 0;
+                s = "clear";
+                break;
+            case Id_entries:
+                arity = 0;
+                s = "entries";
+                break;
+            case Id_values:
+                arity = 0;
+                s = "values";
+                break;
+            case Id_forEach:
+                arity = 1;
+                s = "forEach";
+                break;
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
+        }
+        initPrototypeMethod(SET_TAG, id, s, fnName, arity);
+    }
+
+    @Override
+    protected int findPrototypeId(Symbol k) {
+        if (GETSIZE.equals(k)) {
+            return SymbolId_getSize;
+        }
+        if (SymbolKey.ITERATOR.equals(k)) {
+            return Id_values;
+        }
+        if (SymbolKey.TO_STRING_TAG.equals(k)) {
+            return SymbolId_toStringTag;
+        }
+        return 0;
+    }
+
+    @Override
+    protected int findPrototypeId(String s) {
+        int id;
+        switch (s) {
+            case "constructor":
+                id = Id_constructor;
+                break;
+            case "add":
+                id = Id_add;
+                break;
+            case "delete":
+                id = Id_delete;
+                break;
+            case "has":
+                id = Id_has;
+                break;
+            case "clear":
+                id = Id_clear;
+                break;
+            case "keys":
+                id = Id_keys;
+                break;
+            case "values":
+                id = Id_values;
+                break;
+            case "entries":
+                id = Id_entries;
+                break;
+            case "forEach":
+                id = Id_forEach;
+                break;
+            default:
+                id = 0;
+                break;
+        }
+        return id;
+    }
+
+    // Note that SymbolId_iterator is not present because it is required to have the
+    // same value as the "values" entry.
+    // Similarly, "keys" is supposed to have the same value as "values," which is why
+    // both have the same ID.
+    private static final int Id_constructor = 1,
+            Id_add = 2,
+            Id_delete = 3,
+            Id_has = 4,
+            Id_clear = 5,
+            Id_keys = 6,
+            Id_values = 6, // These are deliberately the same to match the spec
+            Id_entries = 7,
+            Id_forEach = 8,
+            SymbolId_getSize = 9,
+            SymbolId_toStringTag = 10,
+            MAX_PROTOTYPE_ID = SymbolId_toStringTag;
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeStringIterator.java rhino-1.7.14/src/org/mozilla/javascript/NativeStringIterator.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeStringIterator.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeStringIterator.java	2022-01-06 22:57:21.000000000 +0100
@@ -21,8 +21,8 @@
         super();
     }
 
-    NativeStringIterator(Scriptable scope, Scriptable stringLike) {
-        super(scope);
+    NativeStringIterator(Scriptable scope, Object stringLike) {
+        super(scope, ITERATOR_TAG);
         this.index = 0;
         this.string  = ScriptRuntime.toString(stringLike);
     }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeString.java rhino-1.7.14/src/org/mozilla/javascript/NativeString.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeString.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeString.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,34 +6,33 @@
 
 package org.mozilla.javascript;
 
-import org.mozilla.javascript.regexp.NativeRegExp;
+import static org.mozilla.javascript.ScriptRuntime.rangeError;
+import static org.mozilla.javascript.ScriptRuntimeES6.requireObjectCoercible;
 
 import java.text.Collator;
 import java.text.Normalizer;
-
-import static org.mozilla.javascript.ScriptRuntime.rangeError;
-import static org.mozilla.javascript.ScriptRuntimeES6.requireObjectCoercible;
+import java.util.Locale;
+import org.mozilla.javascript.ScriptRuntime.StringIdOrIndex;
+import org.mozilla.javascript.regexp.NativeRegExp;
 
 /**
  * This class implements the String native object.
  *
- * See ECMA 15.5.
+ * <p>See ECMA 15.5.
  *
- * String methods for dealing with regular expressions are
- * ported directly from C. Latest port is from version 1.40.12.19
- * in the JSFUN13_BRANCH.
+ * <p>String methods for dealing with regular expressions are ported directly from C. Latest port is
+ * from version 1.40.12.19 in the JSFUN13_BRANCH.
  *
  * @author Mike McCabe
  * @author Norris Boyd
+ * @author Ronald Brill
  */
-final class NativeString extends IdScriptableObject
-{
-    static final long serialVersionUID = 920268368584188687L;
+final class NativeString extends IdScriptableObject {
+    private static final long serialVersionUID = 920268368584188687L;
 
     private static final Object STRING_TAG = "String";
 
-    static void init(Scriptable scope, boolean sealed)
-    {
+    static void init(Scriptable scope, boolean sealed) {
         NativeString obj = new NativeString("");
         obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
     }
@@ -47,19 +46,15 @@
         return "String";
     }
 
-    private static final int
-        Id_length                    =  1,
-        MAX_INSTANCE_ID              =  1;
+    private static final int Id_length = 1, MAX_INSTANCE_ID = 1;
 
     @Override
-    protected int getMaxInstanceId()
-    {
+    protected int getMaxInstanceId() {
         return MAX_INSTANCE_ID;
     }
 
     @Override
-    protected int findInstanceIdInfo(String s)
-    {
+    protected int findInstanceIdInfo(String s) {
         if (s.equals("length")) {
             return instanceIdInfo(DONTENUM | READONLY | PERMANENT, Id_length);
         }
@@ -67,15 +62,15 @@
     }
 
     @Override
-    protected String getInstanceIdName(int id)
-    {
-        if (id == Id_length) { return "length"; }
+    protected String getInstanceIdName(int id) {
+        if (id == Id_length) {
+            return "length";
+        }
         return super.getInstanceIdName(id);
     }
 
     @Override
-    protected Object getInstanceIdValue(int id)
-    {
+    protected Object getInstanceIdValue(int id) {
         if (id == Id_length) {
             return ScriptRuntime.wrapInt(string.length());
         }
@@ -83,50 +78,34 @@
     }
 
     @Override
-    protected void fillConstructorProperties(IdFunctionObject ctor)
-    {
-        addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_fromCharCode,
-                "fromCharCode", 1);
-        addIdFunctionProperty(ctor, STRING_TAG,
-                ConstructorId_charAt, "charAt", 2);
-        addIdFunctionProperty(ctor, STRING_TAG,
-                ConstructorId_charCodeAt, "charCodeAt", 2);
-        addIdFunctionProperty(ctor, STRING_TAG,
-                ConstructorId_indexOf, "indexOf", 2);
-        addIdFunctionProperty(ctor, STRING_TAG,
-                ConstructorId_lastIndexOf, "lastIndexOf", 2);
-        addIdFunctionProperty(ctor, STRING_TAG,
-                ConstructorId_split, "split", 3);
-        addIdFunctionProperty(ctor, STRING_TAG,
-                ConstructorId_substring, "substring", 3);
-        addIdFunctionProperty(ctor, STRING_TAG,
-                ConstructorId_toLowerCase, "toLowerCase", 1);
-        addIdFunctionProperty(ctor, STRING_TAG,
-                ConstructorId_toUpperCase, "toUpperCase", 1);
-        addIdFunctionProperty(ctor, STRING_TAG,
-                ConstructorId_substr, "substr", 3);
-        addIdFunctionProperty(ctor, STRING_TAG,
-                ConstructorId_concat, "concat", 2);
-        addIdFunctionProperty(ctor, STRING_TAG,
-                ConstructorId_slice, "slice", 3);
-        addIdFunctionProperty(ctor, STRING_TAG,
-                ConstructorId_equalsIgnoreCase, "equalsIgnoreCase", 2);
-        addIdFunctionProperty(ctor, STRING_TAG,
-                ConstructorId_match, "match", 2);
-        addIdFunctionProperty(ctor, STRING_TAG,
-                ConstructorId_search, "search", 2);
-        addIdFunctionProperty(ctor, STRING_TAG,
-                ConstructorId_replace, "replace", 2);
-        addIdFunctionProperty(ctor, STRING_TAG,
-                ConstructorId_localeCompare, "localeCompare", 2);
-        addIdFunctionProperty(ctor, STRING_TAG,
-                ConstructorId_toLocaleLowerCase, "toLocaleLowerCase", 1);
+    protected void fillConstructorProperties(IdFunctionObject ctor) {
+        addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_fromCharCode, "fromCharCode", 1);
+        addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_fromCodePoint, "fromCodePoint", 1);
+        addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_raw, "raw", 1);
+        addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_charAt, "charAt", 2);
+        addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_charCodeAt, "charCodeAt", 2);
+        addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_indexOf, "indexOf", 2);
+        addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_lastIndexOf, "lastIndexOf", 2);
+        addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_split, "split", 3);
+        addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_substring, "substring", 3);
+        addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_toLowerCase, "toLowerCase", 1);
+        addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_toUpperCase, "toUpperCase", 1);
+        addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_substr, "substr", 3);
+        addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_concat, "concat", 2);
+        addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_slice, "slice", 3);
+        addIdFunctionProperty(
+                ctor, STRING_TAG, ConstructorId_equalsIgnoreCase, "equalsIgnoreCase", 2);
+        addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_match, "match", 2);
+        addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_search, "search", 2);
+        addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_replace, "replace", 2);
+        addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_localeCompare, "localeCompare", 2);
+        addIdFunctionProperty(
+                ctor, STRING_TAG, ConstructorId_toLocaleLowerCase, "toLocaleLowerCase", 1);
         super.fillConstructorProperties(ctor);
     }
 
     @Override
-    protected void initPrototypeId(int id)
-    {
+    protected void initPrototypeId(int id) {
         if (id == SymbolId_iterator) {
             initPrototypeMethod(STRING_TAG, id, SymbolKey.ITERATOR, "[Symbol.iterator]", 0);
             return;
@@ -135,66 +114,217 @@
         String s, fnName = null;
         int arity;
         switch (id) {
-          case Id_constructor:       arity=1; s="constructor";       break;
-          case Id_toString:          arity=0; s="toString";          break;
-          case Id_toSource:          arity=0; s="toSource";          break;
-          case Id_valueOf:           arity=0; s="valueOf";           break;
-          case Id_charAt:            arity=1; s="charAt";            break;
-          case Id_charCodeAt:        arity=1; s="charCodeAt";        break;
-          case Id_indexOf:           arity=1; s="indexOf";           break;
-          case Id_lastIndexOf:       arity=1; s="lastIndexOf";       break;
-          case Id_split:             arity=2; s="split";             break;
-          case Id_substring:         arity=2; s="substring";         break;
-          case Id_toLowerCase:       arity=0; s="toLowerCase";       break;
-          case Id_toUpperCase:       arity=0; s="toUpperCase";       break;
-          case Id_substr:            arity=2; s="substr";            break;
-          case Id_concat:            arity=1; s="concat";            break;
-          case Id_slice:             arity=2; s="slice";             break;
-          case Id_bold:              arity=0; s="bold";              break;
-          case Id_italics:           arity=0; s="italics";           break;
-          case Id_fixed:             arity=0; s="fixed";             break;
-          case Id_strike:            arity=0; s="strike";            break;
-          case Id_small:             arity=0; s="small";             break;
-          case Id_big:               arity=0; s="big";               break;
-          case Id_blink:             arity=0; s="blink";             break;
-          case Id_sup:               arity=0; s="sup";               break;
-          case Id_sub:               arity=0; s="sub";               break;
-          case Id_fontsize:          arity=0; s="fontsize";          break;
-          case Id_fontcolor:         arity=0; s="fontcolor";         break;
-          case Id_link:              arity=0; s="link";              break;
-          case Id_anchor:            arity=0; s="anchor";            break;
-          case Id_equals:            arity=1; s="equals";            break;
-          case Id_equalsIgnoreCase:  arity=1; s="equalsIgnoreCase";  break;
-          case Id_match:             arity=1; s="match";             break;
-          case Id_search:            arity=1; s="search";            break;
-          case Id_replace:           arity=2; s="replace";           break;
-          case Id_localeCompare:     arity=1; s="localeCompare";     break;
-          case Id_toLocaleLowerCase: arity=0; s="toLocaleLowerCase"; break;
-          case Id_toLocaleUpperCase: arity=0; s="toLocaleUpperCase"; break;
-          case Id_trim:              arity=0; s="trim";              break;
-          case Id_trimLeft:          arity=0; s="trimLeft";          break;
-          case Id_trimRight:         arity=0; s="trimRight";         break;
-          case Id_includes:          arity=1; s="includes";          break;
-          case Id_startsWith:        arity=1; s="startsWith";        break;
-          case Id_endsWith:          arity=1; s="endsWith";          break;
-          case Id_normalize:         arity=0; s="normalize";         break;
-          case Id_repeat:            arity=1; s="repeat";            break;
-          case Id_codePointAt:       arity=1; s="codePointAt";       break;
-          default: throw new IllegalArgumentException(String.valueOf(id));
+            case Id_constructor:
+                arity = 1;
+                s = "constructor";
+                break;
+            case Id_toString:
+                arity = 0;
+                s = "toString";
+                break;
+            case Id_toSource:
+                arity = 0;
+                s = "toSource";
+                break;
+            case Id_valueOf:
+                arity = 0;
+                s = "valueOf";
+                break;
+            case Id_charAt:
+                arity = 1;
+                s = "charAt";
+                break;
+            case Id_charCodeAt:
+                arity = 1;
+                s = "charCodeAt";
+                break;
+            case Id_indexOf:
+                arity = 1;
+                s = "indexOf";
+                break;
+            case Id_lastIndexOf:
+                arity = 1;
+                s = "lastIndexOf";
+                break;
+            case Id_split:
+                arity = 2;
+                s = "split";
+                break;
+            case Id_substring:
+                arity = 2;
+                s = "substring";
+                break;
+            case Id_toLowerCase:
+                arity = 0;
+                s = "toLowerCase";
+                break;
+            case Id_toUpperCase:
+                arity = 0;
+                s = "toUpperCase";
+                break;
+            case Id_substr:
+                arity = 2;
+                s = "substr";
+                break;
+            case Id_concat:
+                arity = 1;
+                s = "concat";
+                break;
+            case Id_slice:
+                arity = 2;
+                s = "slice";
+                break;
+            case Id_bold:
+                arity = 0;
+                s = "bold";
+                break;
+            case Id_italics:
+                arity = 0;
+                s = "italics";
+                break;
+            case Id_fixed:
+                arity = 0;
+                s = "fixed";
+                break;
+            case Id_strike:
+                arity = 0;
+                s = "strike";
+                break;
+            case Id_small:
+                arity = 0;
+                s = "small";
+                break;
+            case Id_big:
+                arity = 0;
+                s = "big";
+                break;
+            case Id_blink:
+                arity = 0;
+                s = "blink";
+                break;
+            case Id_sup:
+                arity = 0;
+                s = "sup";
+                break;
+            case Id_sub:
+                arity = 0;
+                s = "sub";
+                break;
+            case Id_fontsize:
+                arity = 0;
+                s = "fontsize";
+                break;
+            case Id_fontcolor:
+                arity = 0;
+                s = "fontcolor";
+                break;
+            case Id_link:
+                arity = 0;
+                s = "link";
+                break;
+            case Id_anchor:
+                arity = 0;
+                s = "anchor";
+                break;
+            case Id_equals:
+                arity = 1;
+                s = "equals";
+                break;
+            case Id_equalsIgnoreCase:
+                arity = 1;
+                s = "equalsIgnoreCase";
+                break;
+            case Id_match:
+                arity = 1;
+                s = "match";
+                break;
+            case Id_search:
+                arity = 1;
+                s = "search";
+                break;
+            case Id_replace:
+                arity = 2;
+                s = "replace";
+                break;
+            case Id_localeCompare:
+                arity = 1;
+                s = "localeCompare";
+                break;
+            case Id_toLocaleLowerCase:
+                arity = 0;
+                s = "toLocaleLowerCase";
+                break;
+            case Id_toLocaleUpperCase:
+                arity = 0;
+                s = "toLocaleUpperCase";
+                break;
+            case Id_trim:
+                arity = 0;
+                s = "trim";
+                break;
+            case Id_trimLeft:
+                arity = 0;
+                s = "trimLeft";
+                break;
+            case Id_trimRight:
+                arity = 0;
+                s = "trimRight";
+                break;
+            case Id_includes:
+                arity = 1;
+                s = "includes";
+                break;
+            case Id_startsWith:
+                arity = 1;
+                s = "startsWith";
+                break;
+            case Id_endsWith:
+                arity = 1;
+                s = "endsWith";
+                break;
+            case Id_normalize:
+                arity = 0;
+                s = "normalize";
+                break;
+            case Id_repeat:
+                arity = 1;
+                s = "repeat";
+                break;
+            case Id_codePointAt:
+                arity = 1;
+                s = "codePointAt";
+                break;
+            case Id_padStart:
+                arity = 1;
+                s = "padStart";
+                break;
+            case Id_padEnd:
+                arity = 1;
+                s = "padEnd";
+                break;
+            case Id_trimStart:
+                arity = 0;
+                s = "trimStart";
+                break;
+            case Id_trimEnd:
+                arity = 0;
+                s = "trimEnd";
+                break;
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
         }
         initPrototypeMethod(STRING_TAG, id, s, fnName, arity);
     }
 
     @Override
-    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
-                             Scriptable thisObj, Object[] args)
-    {
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         if (!f.hasTag(STRING_TAG)) {
             return super.execIdCall(f, cx, scope, thisObj, args);
         }
         int id = f.methodId();
-      again:
-        for(;;) {
+        again:
+        for (; ; ) {
             switch (id) {
                 case ConstructorId_charAt:
                 case ConstructorId_charCodeAt:
@@ -212,52 +342,80 @@
                 case ConstructorId_search:
                 case ConstructorId_replace:
                 case ConstructorId_localeCompare:
-                case ConstructorId_toLocaleLowerCase: {
-                    if (args.length > 0) {
-                        thisObj = ScriptRuntime.toObject(cx, scope,
-                            ScriptRuntime.toCharSequence(args[0]));
-                        Object[] newArgs = new Object[args.length - 1];
-                        for (int i = 0; i < newArgs.length; i++)
-                            newArgs[i] = args[i + 1];
-                        args = newArgs;
-                    } else {
-                        thisObj = ScriptRuntime.toObject(cx, scope,
-                            ScriptRuntime.toCharSequence(thisObj));
+                case ConstructorId_toLocaleLowerCase:
+                    {
+                        if (args.length > 0) {
+                            thisObj =
+                                    ScriptRuntime.toObject(
+                                            cx, scope, ScriptRuntime.toCharSequence(args[0]));
+                            Object[] newArgs = new Object[args.length - 1];
+                            for (int i = 0; i < newArgs.length; i++) newArgs[i] = args[i + 1];
+                            args = newArgs;
+                        } else {
+                            thisObj =
+                                    ScriptRuntime.toObject(
+                                            cx, scope, ScriptRuntime.toCharSequence(thisObj));
+                        }
+                        id = -id;
+                        continue again;
                     }
-                    id = -id;
-                    continue again;
-                }
 
-                case ConstructorId_fromCharCode: {
-                    int N = args.length;
-                    if (N < 1)
-                        return "";
-                    StringBuilder sb = new StringBuilder(N);
-                    for (int i = 0; i != N; ++i) {
-                        sb.append(ScriptRuntime.toUint16(args[i]));
+                case ConstructorId_fromCodePoint:
+                    {
+                        int n = args.length;
+                        if (n < 1) {
+                            return "";
+                        }
+                        int[] codePoints = new int[n];
+                        for (int i = 0; i != n; i++) {
+                            Object arg = args[i];
+                            int codePoint = ScriptRuntime.toInt32(arg);
+                            double num = ScriptRuntime.toNumber(arg);
+                            if (!ScriptRuntime.eqNumber(num, Integer.valueOf(codePoint))
+                                    || !Character.isValidCodePoint(codePoint)) {
+                                throw rangeError(
+                                        "Invalid code point " + ScriptRuntime.toString(arg));
+                            }
+                            codePoints[i] = codePoint;
+                        }
+                        return new String(codePoints, 0, n);
                     }
-                    return sb.toString();
-                }
 
-                case Id_constructor: {
-                    CharSequence s;
-                    if (args.length == 0) {
-                        s = "";
-                    } else if
-                        (ScriptRuntime.isSymbol(args[0]) &&
-                        (thisObj != null)) {
-                        // 19.4.3.2 et.al. Convert a symbol to a string with String() but not new String()
-                        s = args[0].toString();
-                    } else {
-                        s = ScriptRuntime.toCharSequence(args[0]);
-                    }
-                    if (thisObj == null) {
-                        // new String(val) creates a new String object.
-                        return new NativeString(s);
+                case ConstructorId_fromCharCode:
+                    {
+                        int n = args.length;
+                        if (n < 1) {
+                            return "";
+                        }
+                        char[] chars = new char[n];
+                        for (int i = 0; i != n; ++i) {
+                            chars[i] = ScriptRuntime.toUint16(args[i]);
+                        }
+                        return new String(chars);
+                    }
+
+                case ConstructorId_raw:
+                    return js_raw(cx, scope, args);
+
+                case Id_constructor:
+                    {
+                        CharSequence s;
+                        if (args.length == 0) {
+                            s = "";
+                        } else if (ScriptRuntime.isSymbol(args[0]) && (thisObj != null)) {
+                            // 19.4.3.2 et.al. Convert a symbol to a string with String() but not
+                            // new String()
+                            s = args[0].toString();
+                        } else {
+                            s = ScriptRuntime.toCharSequence(args[0]);
+                        }
+                        if (thisObj == null) {
+                            // new String(val) creates a new String object.
+                            return new NativeString(s);
+                        }
+                        // String(val) converts val to a string value.
+                        return s instanceof String ? s : s.toString();
                     }
-                    // String(val) converts val to a string value.
-                    return s instanceof String ? s : s.toString();
-                }
 
                 case Id_toString:
                 case Id_valueOf:
@@ -265,266 +423,341 @@
                     CharSequence cs = realThis(thisObj, f).string;
                     return cs instanceof String ? cs : cs.toString();
 
-                case Id_toSource: {
-                    CharSequence s = realThis(thisObj, f).string;
-                    return "(new String(\"" + ScriptRuntime.escapeString(s.toString()) + "\"))";
-                }
+                case Id_toSource:
+                    {
+                        CharSequence s = realThis(thisObj, f).string;
+                        return "(new String(\"" + ScriptRuntime.escapeString(s.toString()) + "\"))";
+                    }
 
                 case Id_charAt:
-                case Id_charCodeAt: {
-                    // See ECMA 15.5.4.[4,5]
-                    CharSequence target = ScriptRuntime.toCharSequence(thisObj);
-                    double pos = ScriptRuntime.toInteger(args, 0);
-                    if (pos < 0 || pos >= target.length()) {
-                        if (id == Id_charAt) return "";
-                        else return ScriptRuntime.NaNobj;
-                    }
-                    char c = target.charAt((int) pos);
-                    if (id == Id_charAt) return String.valueOf(c);
-                    else return ScriptRuntime.wrapInt(c);
-                }
+                case Id_charCodeAt:
+                    {
+                        // See ECMA 15.5.4.[4,5]
+                        CharSequence target =
+                                ScriptRuntime.toCharSequence(
+                                        requireObjectCoercible(cx, thisObj, f));
+                        double pos = ScriptRuntime.toInteger(args, 0);
+                        if (pos < 0 || pos >= target.length()) {
+                            if (id == Id_charAt) return "";
+                            return ScriptRuntime.NaNobj;
+                        }
+                        char c = target.charAt((int) pos);
+                        if (id == Id_charAt) return String.valueOf(c);
+                        return ScriptRuntime.wrapInt(c);
+                    }
 
                 case Id_indexOf:
-                    return ScriptRuntime.wrapInt(js_indexOf(Id_indexOf, ScriptRuntime.toString(thisObj), args));
+                    {
+                        String thisString =
+                                ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f));
+                        return ScriptRuntime.wrapInt(js_indexOf(Id_indexOf, thisString, args));
+                    }
 
                 case Id_includes:
                 case Id_startsWith:
                 case Id_endsWith:
-                    String s = ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f));
+                    String thisString =
+                            ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f));
                     if (args.length > 0 && args[0] instanceof NativeRegExp) {
-                        throw ScriptRuntime.typeError2("msg.first.arg.not.regexp", String.class.getSimpleName(), f.getFunctionName());
+                        throw ScriptRuntime.typeErrorById(
+                                "msg.first.arg.not.regexp",
+                                String.class.getSimpleName(),
+                                f.getFunctionName());
                     }
 
-                    int idx = js_indexOf(id, s, args);
+                    int idx = js_indexOf(id, thisString, args);
 
                     if (id == Id_includes) {
-                        return idx != -1;
-                    } else if (id == Id_startsWith) {
-                        return idx == 0;
-                    } else if (id == Id_endsWith) {
-                        return idx != -1;
+                        return Boolean.valueOf(idx != -1);
+                    }
+                    if (id == Id_startsWith) {
+                        return Boolean.valueOf(idx == 0);
+                    }
+                    if (id == Id_endsWith) {
+                        return Boolean.valueOf(idx != -1);
                     }
+                    // fallthrough
+
+                case Id_padStart:
+                case Id_padEnd:
+                    return js_pad(cx, thisObj, f, args, id == Id_padStart);
 
                 case Id_lastIndexOf:
-                    return ScriptRuntime.wrapInt(js_lastIndexOf(
-                        ScriptRuntime.toString(thisObj), args));
+                    {
+                        String thisStr =
+                                ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f));
+                        return ScriptRuntime.wrapInt(js_lastIndexOf(thisStr, args));
+                    }
 
                 case Id_split:
-                    return ScriptRuntime.checkRegExpProxy(cx).
-                        js_split(cx, scope, ScriptRuntime.toString(thisObj),
-                            args);
+                    {
+                        String thisStr =
+                                ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f));
+                        return ScriptRuntime.checkRegExpProxy(cx)
+                                .js_split(cx, scope, thisStr, args);
+                    }
 
                 case Id_substring:
-                    return js_substring(cx, ScriptRuntime.toCharSequence(thisObj), args);
+                    {
+                        CharSequence target =
+                                ScriptRuntime.toCharSequence(
+                                        requireObjectCoercible(cx, thisObj, f));
+                        return js_substring(cx, target, args);
+                    }
 
                 case Id_toLowerCase:
-                    // See ECMA 15.5.4.11
-                    return ScriptRuntime.toString(thisObj).toLowerCase(
-                        ScriptRuntime.ROOT_LOCALE);
+                    {
+                        // See ECMA 15.5.4.11
+                        String thisStr =
+                                ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f));
+                        return thisStr.toLowerCase(Locale.ROOT);
+                    }
 
                 case Id_toUpperCase:
-                    // See ECMA 15.5.4.12
-                    return ScriptRuntime.toString(thisObj).toUpperCase(
-                        ScriptRuntime.ROOT_LOCALE);
+                    {
+                        // See ECMA 15.5.4.12
+                        String thisStr =
+                                ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f));
+                        return thisStr.toUpperCase(Locale.ROOT);
+                    }
 
                 case Id_substr:
-                    return js_substr(ScriptRuntime.toCharSequence(thisObj), args);
+                    {
+                        CharSequence target =
+                                ScriptRuntime.toCharSequence(
+                                        requireObjectCoercible(cx, thisObj, f));
+                        return js_substr(target, args);
+                    }
 
                 case Id_concat:
-                    return js_concat(ScriptRuntime.toString(thisObj), args);
+                    {
+                        String thisStr =
+                                ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f));
+                        return js_concat(thisStr, args);
+                    }
 
                 case Id_slice:
-                    return js_slice(ScriptRuntime.toCharSequence(thisObj), args);
+                    {
+                        CharSequence target =
+                                ScriptRuntime.toCharSequence(
+                                        requireObjectCoercible(cx, thisObj, f));
+                        return js_slice(target, args);
+                    }
 
                 case Id_bold:
-                    return tagify(thisObj, "b", null, null);
+                    return tagify(cx, thisObj, f, "b", null, null);
 
                 case Id_italics:
-                    return tagify(thisObj, "i", null, null);
+                    return tagify(cx, thisObj, f, "i", null, null);
 
                 case Id_fixed:
-                    return tagify(thisObj, "tt", null, null);
+                    return tagify(cx, thisObj, f, "tt", null, null);
 
                 case Id_strike:
-                    return tagify(thisObj, "strike", null, null);
+                    return tagify(cx, thisObj, f, "strike", null, null);
 
                 case Id_small:
-                    return tagify(thisObj, "small", null, null);
+                    return tagify(cx, thisObj, f, "small", null, null);
 
                 case Id_big:
-                    return tagify(thisObj, "big", null, null);
+                    return tagify(cx, thisObj, f, "big", null, null);
 
                 case Id_blink:
-                    return tagify(thisObj, "blink", null, null);
+                    return tagify(cx, thisObj, f, "blink", null, null);
 
                 case Id_sup:
-                    return tagify(thisObj, "sup", null, null);
+                    return tagify(cx, thisObj, f, "sup", null, null);
 
                 case Id_sub:
-                    return tagify(thisObj, "sub", null, null);
+                    return tagify(cx, thisObj, f, "sub", null, null);
 
                 case Id_fontsize:
-                    return tagify(thisObj, "font", "size", args);
+                    return tagify(cx, thisObj, f, "font", "size", args);
 
                 case Id_fontcolor:
-                    return tagify(thisObj, "font", "color", args);
+                    return tagify(cx, thisObj, f, "font", "color", args);
 
                 case Id_link:
-                    return tagify(thisObj, "a", "href", args);
+                    return tagify(cx, thisObj, f, "a", "href", args);
 
                 case Id_anchor:
-                    return tagify(thisObj, "a", "name", args);
+                    return tagify(cx, thisObj, f, "a", "name", args);
 
                 case Id_equals:
-                case Id_equalsIgnoreCase: {
-                    String s1 = ScriptRuntime.toString(thisObj);
-                    String s2 = ScriptRuntime.toString(args, 0);
-                    return ScriptRuntime.wrapBoolean(
-                        (id == Id_equals) ? s1.equals(s2)
-                            : s1.equalsIgnoreCase(s2));
-                }
+                case Id_equalsIgnoreCase:
+                    {
+                        String s1 = ScriptRuntime.toString(thisObj);
+                        String s2 = ScriptRuntime.toString(args, 0);
+                        return ScriptRuntime.wrapBoolean(
+                                (id == Id_equals) ? s1.equals(s2) : s1.equalsIgnoreCase(s2));
+                    }
 
                 case Id_match:
                 case Id_search:
-                case Id_replace: {
-                    int actionType;
-                    if (id == Id_match) {
-                        actionType = RegExpProxy.RA_MATCH;
-                    } else if (id == Id_search) {
-                        actionType = RegExpProxy.RA_SEARCH;
-                    } else {
-                        actionType = RegExpProxy.RA_REPLACE;
+                case Id_replace:
+                    {
+                        int actionType;
+                        if (id == Id_match) {
+                            actionType = RegExpProxy.RA_MATCH;
+                        } else if (id == Id_search) {
+                            actionType = RegExpProxy.RA_SEARCH;
+                        } else {
+                            actionType = RegExpProxy.RA_REPLACE;
+                        }
+
+                        requireObjectCoercible(cx, thisObj, f);
+                        return ScriptRuntime.checkRegExpProxy(cx)
+                                .action(cx, scope, thisObj, args, actionType);
                     }
-                    return ScriptRuntime.checkRegExpProxy(cx).
-                        action(cx, scope, thisObj, args, actionType);
-                }
-                // ECMA-262 1 5.5.4.9
-                case Id_localeCompare: {
-                    // For now, create and configure a collator instance. I can't
-                    // actually imagine that this'd be slower than caching them
-                    // a la ClassCache, so we aren't trying to outsmart ourselves
-                    // with a caching mechanism for now.
-                    Collator collator = Collator.getInstance(cx.getLocale());
-                    collator.setStrength(Collator.IDENTICAL);
-                    collator.setDecomposition(Collator.CANONICAL_DECOMPOSITION);
-                    return ScriptRuntime.wrapNumber(collator.compare(
-                        ScriptRuntime.toString(thisObj),
-                        ScriptRuntime.toString(args, 0)));
-                }
-                case Id_toLocaleLowerCase: {
-                    return ScriptRuntime.toString(thisObj)
-                        .toLowerCase(cx.getLocale());
-                }
-                case Id_toLocaleUpperCase: {
-                    return ScriptRuntime.toString(thisObj)
-                        .toUpperCase(cx.getLocale());
-                }
-                case Id_trim: {
-                    String str = ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f));
-                    char[] chars = str.toCharArray();
-
-                    int start = 0;
-                    while (start < chars.length && ScriptRuntime.isJSWhitespaceOrLineTerminator(chars[start])) {
-                        start++;
+                    // ECMA-262 1 5.5.4.9
+                case Id_localeCompare:
+                    {
+                        // For now, create and configure a collator instance. I can't
+                        // actually imagine that this'd be slower than caching them
+                        // a la ClassCache, so we aren't trying to outsmart ourselves
+                        // with a caching mechanism for now.
+                        String thisStr =
+                                ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f));
+                        Collator collator = Collator.getInstance(cx.getLocale());
+                        collator.setStrength(Collator.IDENTICAL);
+                        collator.setDecomposition(Collator.CANONICAL_DECOMPOSITION);
+                        return ScriptRuntime.wrapNumber(
+                                collator.compare(thisStr, ScriptRuntime.toString(args, 0)));
                     }
-                    int end = chars.length;
-                    while (end > start && ScriptRuntime.isJSWhitespaceOrLineTerminator(chars[end - 1])) {
-                        end--;
+                case Id_toLocaleLowerCase:
+                    {
+                        String thisStr =
+                                ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f));
+                        return thisStr.toLowerCase(cx.getLocale());
                     }
+                case Id_toLocaleUpperCase:
+                    {
+                        String thisStr =
+                                ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f));
+                        return thisStr.toUpperCase(cx.getLocale());
+                    }
+                case Id_trim:
+                    {
+                        String str = ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f));
+                        char[] chars = str.toCharArray();
+
+                        int start = 0;
+                        while (start < chars.length
+                                && ScriptRuntime.isJSWhitespaceOrLineTerminator(chars[start])) {
+                            start++;
+                        }
+                        int end = chars.length;
+                        while (end > start
+                                && ScriptRuntime.isJSWhitespaceOrLineTerminator(chars[end - 1])) {
+                            end--;
+                        }
 
-                    return str.substring(start, end);
-                }
-                case Id_trimLeft: {
-                    String str = ScriptRuntime.toString(thisObj);
-                    char[] chars = str.toCharArray();
-
-                    int start = 0;
-                    while (start < chars.length && ScriptRuntime.isJSWhitespaceOrLineTerminator(chars[start])) {
-                        start++;
+                        return str.substring(start, end);
                     }
-                    int end = chars.length;
+                case Id_trimLeft:
+                case Id_trimStart:
+                    {
+                        String str = ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f));
+                        char[] chars = str.toCharArray();
+
+                        int start = 0;
+                        while (start < chars.length
+                                && ScriptRuntime.isJSWhitespaceOrLineTerminator(chars[start])) {
+                            start++;
+                        }
+                        int end = chars.length;
 
-                    return str.substring(start, end);
-                }
+                        return str.substring(start, end);
+                    }
                 case Id_trimRight:
-                {
-                    String str = ScriptRuntime.toString(thisObj);
-                    char[] chars = str.toCharArray();
+                case Id_trimEnd:
+                    {
+                        String str = ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f));
+                        char[] chars = str.toCharArray();
+
+                        int start = 0;
+
+                        int end = chars.length;
+                        while (end > start
+                                && ScriptRuntime.isJSWhitespaceOrLineTerminator(chars[end - 1])) {
+                            end--;
+                        }
 
-                    int start = 0;
-
-                    int end = chars.length;
-                    while (end > start && ScriptRuntime.isJSWhitespaceOrLineTerminator(chars[end - 1])) {
-                        end--;
+                        return str.substring(start, end);
                     }
-
-                    return str.substring(start, end);
-                }
                 case Id_normalize:
-                {
-                    String formStr = ScriptRuntime.toString(args, 0);
-
-                    Normalizer.Form form;
-                    if (Normalizer.Form.NFD.name().equals(formStr)) form = Normalizer.Form.NFD;
-                    else if (Normalizer.Form.NFKC.name().equals(formStr)) form = Normalizer.Form.NFKC;
-                    else if (Normalizer.Form.NFKD.name().equals(formStr)) form = Normalizer.Form.NFKD;
-                    else if (Normalizer.Form.NFC.name().equals(formStr) || args.length == 0) form = Normalizer.Form.NFC;
-                    else throw rangeError("The normalization form should be one of NFC, NFD, NFKC, NFKD");
-
-                    return Normalizer.normalize(ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f)), form);
-                }
+                    {
+                        if (args.length == 0 || Undefined.isUndefined(args[0])) {
+                            return Normalizer.normalize(
+                                    ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f)),
+                                    Normalizer.Form.NFC);
+                        }
+
+                        final String formStr = ScriptRuntime.toString(args, 0);
+
+                        final Normalizer.Form form;
+                        if (Normalizer.Form.NFD.name().equals(formStr)) form = Normalizer.Form.NFD;
+                        else if (Normalizer.Form.NFKC.name().equals(formStr))
+                            form = Normalizer.Form.NFKC;
+                        else if (Normalizer.Form.NFKD.name().equals(formStr))
+                            form = Normalizer.Form.NFKD;
+                        else if (Normalizer.Form.NFC.name().equals(formStr))
+                            form = Normalizer.Form.NFC;
+                        else
+                            throw rangeError(
+                                    "The normalization form should be one of 'NFC', 'NFD', 'NFKC', 'NFKD'.");
+
+                        return Normalizer.normalize(
+                                ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f)),
+                                form);
+                    }
 
                 case Id_repeat:
-                {
-                    return js_repeat(cx, thisObj, f, args);
-                }
+                    {
+                        return js_repeat(cx, thisObj, f, args);
+                    }
                 case Id_codePointAt:
-                {
-                    String str = ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f));
-                    double cnt = ScriptRuntime.toInteger(args, 0);
-
-                    return (cnt < 0 || cnt >= str.length())
-                        ? Undefined.instance
-                        : str.codePointAt((int) cnt);
-                }
-
-              case SymbolId_iterator:
-                  return new NativeStringIterator(scope, thisObj);
+                    {
+                        String str = ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f));
+                        double cnt = ScriptRuntime.toInteger(args, 0);
+
+                        return (cnt < 0 || cnt >= str.length())
+                                ? Undefined.instance
+                                : Integer.valueOf(str.codePointAt((int) cnt));
+                    }
 
+                case SymbolId_iterator:
+                    return new NativeStringIterator(scope, requireObjectCoercible(cx, thisObj, f));
             }
-            throw new IllegalArgumentException("String.prototype has no method: " + f.getFunctionName());
+            throw new IllegalArgumentException(
+                    "String.prototype has no method: " + f.getFunctionName());
         }
     }
 
-    private static NativeString realThis(Scriptable thisObj, IdFunctionObject f)
-    {
-        if (!(thisObj instanceof NativeString))
-            throw incompatibleCallError(f);
-        return (NativeString)thisObj;
+    private static NativeString realThis(Scriptable thisObj, IdFunctionObject f) {
+        return ensureType(thisObj, NativeString.class, f);
     }
 
     /*
      * HTML composition aids.
      */
-    private static String tagify(Object thisObj, String tag,
-                                 String attribute, Object[] args)
-    {
-        String str = ScriptRuntime.toString(thisObj);
+    private static String tagify(
+            Context cx,
+            Scriptable thisObj,
+            IdFunctionObject f,
+            String tag,
+            String attribute,
+            Object[] args) {
+        String str = ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f));
         StringBuilder result = new StringBuilder();
-        result.append('<');
-        result.append(tag);
-        if (attribute != null) {
-            result.append(' ');
-            result.append(attribute);
-            result.append("=\"");
-            result.append(ScriptRuntime.toString(args, 0));
-            result.append('"');
-        }
-        result.append('>');
-        result.append(str);
-        result.append("</");
-        result.append(tag);
-        result.append('>');
+        result.append('<').append(tag);
+
+        if (attribute != null && attribute.length() > 0) {
+            String attributeValue = ScriptRuntime.toString(args, 0);
+            attributeValue = attributeValue.replace("\"", """);
+            result.append(' ').append(attribute).append("=\"").append(attributeValue).append('"');
+        }
+        result.append('>').append(str).append("</").append(tag).append('>');
         return result.toString();
     }
 
@@ -534,7 +767,7 @@
 
     @Override
     public String toString() {
-        return string instanceof String ? (String)string : string.toString();
+        return string instanceof String ? (String) string : string.toString();
     }
 
     /* Make array-style property lookup work for strings.
@@ -565,8 +798,19 @@
     }
 
     @Override
-    protected Object[] getIds(boolean nonEnumerable, boolean getSymbols)
-    {
+    public int getAttributes(int index) {
+        if (0 <= index && index < string.length()) {
+            int attribs = READONLY | PERMANENT;
+            if (Context.getContext().getLanguageVersion() < Context.VERSION_ES6) {
+                attribs |= DONTENUM;
+            }
+            return attribs;
+        }
+        return super.getAttributes(index);
+    }
+
+    @Override
+    protected Object[] getIds(boolean nonEnumerable, boolean getSymbols) {
         // In ES6, Strings have entries in the property map for each character.
         Context cx = Context.getCurrentContext();
         if ((cx != null) && (cx.getLanguageVersion() >= Context.VERSION_ES6)) {
@@ -582,30 +826,64 @@
         return super.getIds(nonEnumerable, getSymbols);
     }
 
+    @Override
+    protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) {
+        if (!(id instanceof Symbol)
+                && (cx != null)
+                && (cx.getLanguageVersion() >= Context.VERSION_ES6)) {
+            StringIdOrIndex s = ScriptRuntime.toStringIdOrIndex(cx, id);
+            if (s.stringId == null && 0 <= s.index && s.index < string.length()) {
+                String value = String.valueOf(string.charAt(s.index));
+                return defaultIndexPropertyDescriptor(value);
+            }
+        }
+        return super.getOwnPropertyDescriptor(cx, id);
+    }
+
+    private ScriptableObject defaultIndexPropertyDescriptor(Object value) {
+        Scriptable scope = getParentScope();
+        if (scope == null) scope = this;
+        ScriptableObject desc = new NativeObject();
+        ScriptRuntime.setBuiltinProtoAndParent(desc, scope, TopLevel.Builtins.Object);
+        desc.defineProperty("value", value, EMPTY);
+        desc.defineProperty("writable", Boolean.FALSE, EMPTY);
+        desc.defineProperty("enumerable", Boolean.TRUE, EMPTY);
+        desc.defineProperty("configurable", Boolean.FALSE, EMPTY);
+        return desc;
+    }
+
     /*
      *
      * See ECMA 15.5.4.6.  Uses Java String.indexOf()
      * OPT to add - BMH searching from jsstr.c.
      */
-    private static int  js_indexOf(int methodId, String target, Object[] args) {
+    private static int js_indexOf(int methodId, String target, Object[] args) {
         String searchStr = ScriptRuntime.toString(args, 0);
         double position = ScriptRuntime.toInteger(args, 1);
 
-        if (position > target.length() && methodId != Id_startsWith && methodId != Id_endsWith) {
+        if (methodId != Id_startsWith && methodId != Id_endsWith && searchStr.length() == 0) {
+            return position > target.length() ? target.length() : (int) position;
+        }
+
+        if (methodId != Id_startsWith && methodId != Id_endsWith && position > target.length()) {
             return -1;
-        } else {
-            if (position < 0) position = 0;
-            else if (position > target.length()) position = target.length();
-            else if (methodId == Id_endsWith && (position != position  || position > target.length())) position = target.length();
-
-            if (Id_endsWith == methodId) {
-                if (args.length == 0 || args.length == 1 || (args.length == 2 && args[1] == Undefined.instance)) position = target.length();
-                return target.substring(0, (int)position).endsWith(searchStr) ? 0 : -1;
-            }
-            return methodId == Id_startsWith
-                    ? target.startsWith(searchStr, (int)position) ? 0 : -1
-                    : target.indexOf(searchStr, (int)position);
         }
+
+        if (position < 0) position = 0;
+        else if (position > target.length()) position = target.length();
+        else if (methodId == Id_endsWith && (Double.isNaN(position) || position > target.length()))
+            position = target.length();
+
+        if (Id_endsWith == methodId) {
+            if (args.length == 0
+                    || args.length == 1
+                    || (args.length == 2 && args[1] == Undefined.instance))
+                position = target.length();
+            return target.substring(0, (int) position).endsWith(searchStr) ? 0 : -1;
+        }
+        return methodId == Id_startsWith
+                ? target.startsWith(searchStr, (int) position) ? 0 : -1
+                : target.indexOf(searchStr, (int) position);
     }
 
     /*
@@ -617,38 +895,29 @@
         String search = ScriptRuntime.toString(args, 0);
         double end = ScriptRuntime.toNumber(args, 1);
 
-        if (end != end || end > target.length())
-            end = target.length();
-        else if (end < 0)
-            end = 0;
+        if (Double.isNaN(end) || end > target.length()) end = target.length();
+        else if (end < 0) end = 0;
 
-        return target.lastIndexOf(search, (int)end);
+        return target.lastIndexOf(search, (int) end);
     }
 
-
     /*
      * See ECMA 15.5.4.15
      */
-    private static CharSequence js_substring(Context cx, CharSequence target,
-                                       Object[] args)
-    {
+    private static CharSequence js_substring(Context cx, CharSequence target, Object[] args) {
         int length = target.length();
         double start = ScriptRuntime.toInteger(args, 0);
         double end;
 
-        if (start < 0)
-            start = 0;
-        else if (start > length)
-            start = length;
+        if (start < 0) start = 0;
+        else if (start > length) start = length;
 
         if (args.length <= 1 || args[1] == Undefined.instance) {
             end = length;
         } else {
             end = ScriptRuntime.toInteger(args[1]);
-            if (end < 0)
-                end = 0;
-            else if (end > length)
-                end = length;
+            if (end < 0) end = 0;
+            else if (end > length) end = length;
 
             // swap if end < start
             if (end < start) {
@@ -662,7 +931,7 @@
                 }
             }
         }
-        return target.subSequence((int)start, (int)end);
+        return target.subSequence((int) start, (int) end);
     }
 
     int getLength() {
@@ -673,8 +942,9 @@
      * Non-ECMA methods.
      */
     private static CharSequence js_substr(CharSequence target, Object[] args) {
-        if (args.length < 1)
+        if (args.length < 1) {
             return target;
+        }
 
         double begin = ScriptRuntime.toInteger(args[0]);
         double end;
@@ -682,24 +952,28 @@
 
         if (begin < 0) {
             begin += length;
-            if (begin < 0)
-                begin = 0;
+            if (begin < 0) begin = 0;
         } else if (begin > length) {
             begin = length;
         }
 
-        if (args.length == 1) {
-            end = length;
-        } else {
-            end = ScriptRuntime.toInteger(args[1]);
-            if (end < 0)
-                end = 0;
-            end += begin;
-            if (end > length)
-                end = length;
+        end = length;
+        if (args.length > 1) {
+            Object lengthArg = args[1];
+
+            if (!Undefined.isUndefined(lengthArg)) {
+                end = ScriptRuntime.toInteger(lengthArg);
+                if (end < 0) {
+                    end = 0;
+                }
+                end += begin;
+                if (end > length) {
+                    end = length;
+                }
+            }
         }
 
-        return target.subSequence((int)begin, (int)end);
+        return target.subSequence((int) begin, (int) end);
     }
 
     /*
@@ -707,8 +981,9 @@
      */
     private static String js_concat(String target, Object[] args) {
         int N = args.length;
-        if (N == 0) { return target; }
-        else if (N == 1) {
+        if (N == 0) {
+            return target;
+        } else if (N == 1) {
             String arg = ScriptRuntime.toString(args[0]);
             return target.concat(arg);
         }
@@ -737,8 +1012,7 @@
         int length = target.length();
         if (begin < 0) {
             begin += length;
-            if (begin < 0)
-                begin = 0;
+            if (begin < 0) begin = 0;
         } else if (begin > length) {
             begin = length;
         }
@@ -749,19 +1023,17 @@
             end = ScriptRuntime.toInteger(args[1]);
             if (end < 0) {
                 end += length;
-                if (end < 0)
-                    end = 0;
+                if (end < 0) end = 0;
             } else if (end > length) {
                 end = length;
             }
-            if (end < begin)
-                end = begin;
+            if (end < begin) end = begin;
         }
         return target.subSequence((int) begin, (int) end);
     }
 
-    private static String js_repeat(Context cx, Scriptable thisObj, IdFunctionObject f, Object[] args)
-    {
+    private static String js_repeat(
+            Context cx, Scriptable thisObj, IdFunctionObject f, Object[] args) {
         String str = ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f));
         double cnt = ScriptRuntime.toInteger(args, 0);
 
@@ -773,7 +1045,7 @@
             return "";
         }
 
-        long size = str.length() * (long)cnt;
+        long size = str.length() * (long) cnt;
         // Check for overflow
         if ((cnt > Integer.MAX_VALUE) || (size > Integer.MAX_VALUE)) {
             throw rangeError("Invalid size or count value");
@@ -783,7 +1055,7 @@
         retval.append(str);
 
         int i = 1;
-        int icnt = (int)cnt;
+        int icnt = (int) cnt;
         while (i <= (icnt / 2)) {
             retval.append(retval);
             i *= 2;
@@ -795,171 +1067,322 @@
         return retval.toString();
     }
 
+    /**
+     * @see https://www.ecma-international.org/ecma-262/8.0/#sec-string.prototype.padstart
+     * @see https://www.ecma-international.org/ecma-262/8.0/#sec-string.prototype.padend
+     */
+    private static String js_pad(
+            Context cx, Scriptable thisObj, IdFunctionObject f, Object[] args, boolean atStart) {
+        String pad = ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f));
+        long intMaxLength = ScriptRuntime.toLength(args, 0);
+        if (intMaxLength <= pad.length()) {
+            return pad;
+        }
+
+        String filler = " ";
+        if (args.length >= 2 && !Undefined.isUndefined(args[1])) {
+            filler = ScriptRuntime.toString(args[1]);
+            if (filler.length() < 1) {
+                return pad;
+            }
+        }
+
+        // cast is not really correct here
+        int fillLen = (int) (intMaxLength - pad.length());
+        StringBuilder concat = new StringBuilder();
+        do {
+            concat.append(filler);
+        } while (concat.length() < fillLen);
+        concat.setLength(fillLen);
+
+        if (atStart) {
+            return concat.append(pad).toString();
+        }
+
+        return concat.insert(0, pad).toString();
+    }
+
     @Override
-    protected int findPrototypeId(Symbol k)
-    {
+    protected int findPrototypeId(Symbol k) {
         if (SymbolKey.ITERATOR.equals(k)) {
             return SymbolId_iterator;
         }
         return 0;
     }
 
-// #string_id_map#
+    /**
+     *
+     *
+     * <h1>String.raw (template, ...substitutions)</h1>
+     *
+     * <p>22.1.2.4 String.raw [Draft ECMA-262 / April 28, 2021]
+     */
+    private static CharSequence js_raw(Context cx, Scriptable scope, Object[] args) {
+        /* step 1-2 */
+        Object arg0 = args.length > 0 ? args[0] : Undefined.instance;
+        Scriptable cooked = ScriptRuntime.toObject(cx, scope, arg0);
+        /* step 3 */
+        Object rawValue = ScriptRuntime.getObjectProp(cooked, "raw", cx);
+        Scriptable raw = ScriptRuntime.toObject(cx, scope, rawValue);
+        /* step 4-5 */
+        long rawLength = NativeArray.getLengthProperty(cx, raw);
+        if (rawLength > Integer.MAX_VALUE) {
+            throw ScriptRuntime.rangeError("raw.length > " + Integer.toString(Integer.MAX_VALUE));
+        }
+        int literalSegments = (int) rawLength;
+        if (literalSegments <= 0) return "";
+        /* step 6-7 */
+        StringBuilder elements = new StringBuilder();
+        int nextIndex = 0;
+        for (; ; ) {
+            /* step 8 a-i */
+            Object next;
+            next = ScriptRuntime.getObjectIndex(raw, nextIndex, cx);
+            String nextSeg = ScriptRuntime.toString(next);
+            elements.append(nextSeg);
+            nextIndex += 1;
+            if (nextIndex == literalSegments) {
+                break;
+            }
+
+            if (args.length > nextIndex) {
+                next = args[nextIndex];
+                String nextSub = ScriptRuntime.toString(next);
+                elements.append(nextSub);
+            }
+        }
+        return elements;
+    }
 
     @Override
-    protected int findPrototypeId(String s)
-    {
+    protected int findPrototypeId(String s) {
         int id;
-// #generated# Last update: 2016-03-04 20:51:44 GMT
-        L0: { id = 0; String X = null; int c;
-            L: switch (s.length()) {
-            case 3: c=s.charAt(2);
-                if (c=='b') { if (s.charAt(0)=='s' && s.charAt(1)=='u') {id=Id_sub; break L0;} }
-                else if (c=='g') { if (s.charAt(0)=='b' && s.charAt(1)=='i') {id=Id_big; break L0;} }
-                else if (c=='p') { if (s.charAt(0)=='s' && s.charAt(1)=='u') {id=Id_sup; break L0;} }
-                break L;
-            case 4: c=s.charAt(0);
-                if (c=='b') { X="bold";id=Id_bold; }
-                else if (c=='l') { X="link";id=Id_link; }
-                else if (c=='t') { X="trim";id=Id_trim; }
-                break L;
-            case 5: switch (s.charAt(4)) {
-                case 'd': X="fixed";id=Id_fixed; break L;
-                case 'e': X="slice";id=Id_slice; break L;
-                case 'h': X="match";id=Id_match; break L;
-                case 'k': X="blink";id=Id_blink; break L;
-                case 'l': X="small";id=Id_small; break L;
-                case 't': X="split";id=Id_split; break L;
-                } break L;
-            case 6: switch (s.charAt(1)) {
-                case 'e': c=s.charAt(0);
-                    if (c=='r') { X="repeat";id=Id_repeat; }
-                    else if (c=='s') { X="search";id=Id_search; }
-                    break L;
-                case 'h': X="charAt";id=Id_charAt; break L;
-                case 'n': X="anchor";id=Id_anchor; break L;
-                case 'o': X="concat";id=Id_concat; break L;
-                case 'q': X="equals";id=Id_equals; break L;
-                case 't': X="strike";id=Id_strike; break L;
-                case 'u': X="substr";id=Id_substr; break L;
-                } break L;
-            case 7: switch (s.charAt(1)) {
-                case 'a': X="valueOf";id=Id_valueOf; break L;
-                case 'e': X="replace";id=Id_replace; break L;
-                case 'n': X="indexOf";id=Id_indexOf; break L;
-                case 't': X="italics";id=Id_italics; break L;
-                } break L;
-            case 8: switch (s.charAt(6)) {
-                case 'c': X="toSource";id=Id_toSource; break L;
-                case 'e': X="includes";id=Id_includes; break L;
-                case 'f': X="trimLeft";id=Id_trimLeft; break L;
-                case 'n': X="toString";id=Id_toString; break L;
-                case 't': X="endsWith";id=Id_endsWith; break L;
-                case 'z': X="fontsize";id=Id_fontsize; break L;
-                } break L;
-            case 9: switch (s.charAt(0)) {
-                case 'f': X="fontcolor";id=Id_fontcolor; break L;
-                case 'n': X="normalize";id=Id_normalize; break L;
-                case 's': X="substring";id=Id_substring; break L;
-                case 't': X="trimRight";id=Id_trimRight; break L;
-                } break L;
-            case 10: c=s.charAt(0);
-                if (c=='c') { X="charCodeAt";id=Id_charCodeAt; }
-                else if (c=='s') { X="startsWith";id=Id_startsWith; }
-                break L;
-            case 11: switch (s.charAt(2)) {
-                case 'L': X="toLowerCase";id=Id_toLowerCase; break L;
-                case 'U': X="toUpperCase";id=Id_toUpperCase; break L;
-                case 'd': X="codePointAt";id=Id_codePointAt; break L;
-                case 'n': X="constructor";id=Id_constructor; break L;
-                case 's': X="lastIndexOf";id=Id_lastIndexOf; break L;
-                } break L;
-            case 13: X="localeCompare";id=Id_localeCompare; break L;
-            case 16: X="equalsIgnoreCase";id=Id_equalsIgnoreCase; break L;
-            case 17: c=s.charAt(8);
-                if (c=='L') { X="toLocaleLowerCase";id=Id_toLocaleLowerCase; }
-                else if (c=='U') { X="toLocaleUpperCase";id=Id_toLocaleUpperCase; }
-                break L;
-            }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-            break L0;
+        switch (s) {
+            case "constructor":
+                id = Id_constructor;
+                break;
+            case "toString":
+                id = Id_toString;
+                break;
+            case "toSource":
+                id = Id_toSource;
+                break;
+            case "valueOf":
+                id = Id_valueOf;
+                break;
+            case "charAt":
+                id = Id_charAt;
+                break;
+            case "charCodeAt":
+                id = Id_charCodeAt;
+                break;
+            case "indexOf":
+                id = Id_indexOf;
+                break;
+            case "lastIndexOf":
+                id = Id_lastIndexOf;
+                break;
+            case "split":
+                id = Id_split;
+                break;
+            case "substring":
+                id = Id_substring;
+                break;
+            case "toLowerCase":
+                id = Id_toLowerCase;
+                break;
+            case "toUpperCase":
+                id = Id_toUpperCase;
+                break;
+            case "substr":
+                id = Id_substr;
+                break;
+            case "concat":
+                id = Id_concat;
+                break;
+            case "slice":
+                id = Id_slice;
+                break;
+            case "bold":
+                id = Id_bold;
+                break;
+            case "italics":
+                id = Id_italics;
+                break;
+            case "fixed":
+                id = Id_fixed;
+                break;
+            case "strike":
+                id = Id_strike;
+                break;
+            case "small":
+                id = Id_small;
+                break;
+            case "big":
+                id = Id_big;
+                break;
+            case "blink":
+                id = Id_blink;
+                break;
+            case "sup":
+                id = Id_sup;
+                break;
+            case "sub":
+                id = Id_sub;
+                break;
+            case "fontsize":
+                id = Id_fontsize;
+                break;
+            case "fontcolor":
+                id = Id_fontcolor;
+                break;
+            case "link":
+                id = Id_link;
+                break;
+            case "anchor":
+                id = Id_anchor;
+                break;
+            case "equals":
+                id = Id_equals;
+                break;
+            case "equalsIgnoreCase":
+                id = Id_equalsIgnoreCase;
+                break;
+            case "match":
+                id = Id_match;
+                break;
+            case "search":
+                id = Id_search;
+                break;
+            case "replace":
+                id = Id_replace;
+                break;
+            case "localeCompare":
+                id = Id_localeCompare;
+                break;
+            case "toLocaleLowerCase":
+                id = Id_toLocaleLowerCase;
+                break;
+            case "toLocaleUpperCase":
+                id = Id_toLocaleUpperCase;
+                break;
+            case "trim":
+                id = Id_trim;
+                break;
+            case "trimLeft":
+                id = Id_trimLeft;
+                break;
+            case "trimRight":
+                id = Id_trimRight;
+                break;
+            case "includes":
+                id = Id_includes;
+                break;
+            case "startsWith":
+                id = Id_startsWith;
+                break;
+            case "endsWith":
+                id = Id_endsWith;
+                break;
+            case "normalize":
+                id = Id_normalize;
+                break;
+            case "repeat":
+                id = Id_repeat;
+                break;
+            case "codePointAt":
+                id = Id_codePointAt;
+                break;
+            case "padStart":
+                id = Id_padStart;
+                break;
+            case "padEnd":
+                id = Id_padEnd;
+                break;
+            case "trimStart":
+                id = Id_trimStart;
+                break;
+            case "trimEnd":
+                id = Id_trimEnd;
+                break;
+            default:
+                id = 0;
+                break;
         }
-// #/generated#
         return id;
     }
 
-    private static final int
-        ConstructorId_fromCharCode   = -1,
-
-        Id_constructor               = 1,
-        Id_toString                  = 2,
-        Id_toSource                  = 3,
-        Id_valueOf                   = 4,
-        Id_charAt                    = 5,
-        Id_charCodeAt                = 6,
-        Id_indexOf                   = 7,
-        Id_lastIndexOf               = 8,
-        Id_split                     = 9,
-        Id_substring                 = 10,
-        Id_toLowerCase               = 11,
-        Id_toUpperCase               = 12,
-        Id_substr                    = 13,
-        Id_concat                    = 14,
-        Id_slice                     = 15,
-        Id_bold                      = 16,
-        Id_italics                   = 17,
-        Id_fixed                     = 18,
-        Id_strike                    = 19,
-        Id_small                     = 20,
-        Id_big                       = 21,
-        Id_blink                     = 22,
-        Id_sup                       = 23,
-        Id_sub                       = 24,
-        Id_fontsize                  = 25,
-        Id_fontcolor                 = 26,
-        Id_link                      = 27,
-        Id_anchor                    = 28,
-        Id_equals                    = 29,
-        Id_equalsIgnoreCase          = 30,
-        Id_match                     = 31,
-        Id_search                    = 32,
-        Id_replace                   = 33,
-        Id_localeCompare             = 34,
-        Id_toLocaleLowerCase         = 35,
-        Id_toLocaleUpperCase         = 36,
-        Id_trim                      = 37,
-        Id_trimLeft                  = 38,
-        Id_trimRight                 = 39,
-        Id_includes                  = 40,
-        Id_startsWith                = 41,
-        Id_endsWith                  = 42,
-        Id_normalize                 = 43,
-        Id_repeat                    = 44,
-        Id_codePointAt               = 45,
-        SymbolId_iterator            = 46,
-        MAX_PROTOTYPE_ID             = SymbolId_iterator;
-
-// #/string_id_map#
-
-    private static final int
-        ConstructorId_charAt         = -Id_charAt,
-        ConstructorId_charCodeAt     = -Id_charCodeAt,
-        ConstructorId_indexOf        = -Id_indexOf,
-        ConstructorId_lastIndexOf    = -Id_lastIndexOf,
-        ConstructorId_split          = -Id_split,
-        ConstructorId_substring      = -Id_substring,
-        ConstructorId_toLowerCase    = -Id_toLowerCase,
-        ConstructorId_toUpperCase    = -Id_toUpperCase,
-        ConstructorId_substr         = -Id_substr,
-        ConstructorId_concat         = -Id_concat,
-        ConstructorId_slice          = -Id_slice,
-        ConstructorId_equalsIgnoreCase = -Id_equalsIgnoreCase,
-        ConstructorId_match          = -Id_match,
-        ConstructorId_search         = -Id_search,
-        ConstructorId_replace        = -Id_replace,
-        ConstructorId_localeCompare  = -Id_localeCompare,
-        ConstructorId_toLocaleLowerCase = -Id_toLocaleLowerCase;
+    private static final int ConstructorId_fromCharCode = -1,
+            ConstructorId_fromCodePoint = -2,
+            ConstructorId_raw = -3,
+            Id_constructor = 1,
+            Id_toString = 2,
+            Id_toSource = 3,
+            Id_valueOf = 4,
+            Id_charAt = 5,
+            Id_charCodeAt = 6,
+            Id_indexOf = 7,
+            Id_lastIndexOf = 8,
+            Id_split = 9,
+            Id_substring = 10,
+            Id_toLowerCase = 11,
+            Id_toUpperCase = 12,
+            Id_substr = 13,
+            Id_concat = 14,
+            Id_slice = 15,
+            Id_bold = 16,
+            Id_italics = 17,
+            Id_fixed = 18,
+            Id_strike = 19,
+            Id_small = 20,
+            Id_big = 21,
+            Id_blink = 22,
+            Id_sup = 23,
+            Id_sub = 24,
+            Id_fontsize = 25,
+            Id_fontcolor = 26,
+            Id_link = 27,
+            Id_anchor = 28,
+            Id_equals = 29,
+            Id_equalsIgnoreCase = 30,
+            Id_match = 31,
+            Id_search = 32,
+            Id_replace = 33,
+            Id_localeCompare = 34,
+            Id_toLocaleLowerCase = 35,
+            Id_toLocaleUpperCase = 36,
+            Id_trim = 37,
+            Id_trimLeft = 38,
+            Id_trimRight = 39,
+            Id_includes = 40,
+            Id_startsWith = 41,
+            Id_endsWith = 42,
+            Id_normalize = 43,
+            Id_repeat = 44,
+            Id_codePointAt = 45,
+            Id_padStart = 46,
+            Id_padEnd = 47,
+            SymbolId_iterator = 48,
+            Id_trimStart = 49,
+            Id_trimEnd = 50,
+            MAX_PROTOTYPE_ID = Id_trimEnd;
+    private static final int ConstructorId_charAt = -Id_charAt,
+            ConstructorId_charCodeAt = -Id_charCodeAt,
+            ConstructorId_indexOf = -Id_indexOf,
+            ConstructorId_lastIndexOf = -Id_lastIndexOf,
+            ConstructorId_split = -Id_split,
+            ConstructorId_substring = -Id_substring,
+            ConstructorId_toLowerCase = -Id_toLowerCase,
+            ConstructorId_toUpperCase = -Id_toUpperCase,
+            ConstructorId_substr = -Id_substr,
+            ConstructorId_concat = -Id_concat,
+            ConstructorId_slice = -Id_slice,
+            ConstructorId_equalsIgnoreCase = -Id_equalsIgnoreCase,
+            ConstructorId_match = -Id_match,
+            ConstructorId_search = -Id_search,
+            ConstructorId_replace = -Id_replace,
+            ConstructorId_localeCompare = -Id_localeCompare,
+            ConstructorId_toLocaleLowerCase = -Id_toLocaleLowerCase;
 
     private CharSequence string;
 }
-
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeSymbol.java rhino-1.7.14/src/org/mozilla/javascript/NativeSymbol.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeSymbol.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeSymbol.java	2022-01-06 22:57:21.000000000 +0100
@@ -10,15 +10,11 @@
 import java.util.Map;
 
 /**
- * This is an implementation of the standard "Symbol" type that implements
- * all of its weird properties. One of them is that some objects can have
- * an "internal data slot" that makes them a Symbol and others cannot.
+ * This is an implementation of the standard "Symbol" type that implements all of its weird
+ * properties. One of them is that some objects can have an "internal data slot" that makes them a
+ * Symbol and others cannot.
  */
-
-public class NativeSymbol
-    extends IdScriptableObject
-    implements Symbol
-{
+public class NativeSymbol extends IdScriptableObject implements Symbol {
     private static final long serialVersionUID = -589539749749830003L;
 
     public static final String CLASS_NAME = "Symbol";
@@ -32,7 +28,7 @@
 
     public static void init(Context cx, Scriptable scope, boolean sealed) {
         NativeSymbol obj = new NativeSymbol("");
-        ScriptableObject ctor = obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
+        ScriptableObject ctor = obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, false);
 
         cx.putThreadLocal(CONSTRUCTOR_SLOT, Boolean.TRUE);
         try {
@@ -40,7 +36,8 @@
             createStandardSymbol(cx, scope, ctor, "species", SymbolKey.SPECIES);
             createStandardSymbol(cx, scope, ctor, "toStringTag", SymbolKey.TO_STRING_TAG);
             createStandardSymbol(cx, scope, ctor, "hasInstance", SymbolKey.HAS_INSTANCE);
-            createStandardSymbol(cx, scope, ctor, "isConcatSpreadable", SymbolKey.IS_CONCAT_SPREADABLE);
+            createStandardSymbol(
+                    cx, scope, ctor, "isConcatSpreadable", SymbolKey.IS_CONCAT_SPREADABLE);
             createStandardSymbol(cx, scope, ctor, "isRegExp", SymbolKey.IS_REGEXP);
             createStandardSymbol(cx, scope, ctor, "toPrimitive", SymbolKey.TO_PRIMITIVE);
             createStandardSymbol(cx, scope, ctor, "match", SymbolKey.MATCH);
@@ -48,14 +45,26 @@
             createStandardSymbol(cx, scope, ctor, "search", SymbolKey.SEARCH);
             createStandardSymbol(cx, scope, ctor, "split", SymbolKey.SPLIT);
             createStandardSymbol(cx, scope, ctor, "unscopables", SymbolKey.UNSCOPABLES);
+
         } finally {
             cx.removeThreadLocal(CONSTRUCTOR_SLOT);
         }
+
+        if (sealed) {
+            // Can't seal until we have created all the stuff above!
+            ctor.sealObject();
+        }
     }
 
+    /**
+     * This has to be used only for constructing the prototype instance. This sets symbolData to
+     * null (see isSymbol() for more).
+     *
+     * @param desc the description
+     */
     private NativeSymbol(String desc) {
         this.key = new SymbolKey(desc);
-        this.symbolData = this;
+        this.symbolData = null;
     }
 
     private NativeSymbol(SymbolKey key) {
@@ -72,11 +81,10 @@
      * Use this when we need to create symbols internally because of the convoluted way we have to
      * construct them.
      */
-    public static NativeSymbol construct(Context cx, Scriptable scope, Object[] args)
-    {
+    public static NativeSymbol construct(Context cx, Scriptable scope, Object[] args) {
         cx.putThreadLocal(CONSTRUCTOR_SLOT, Boolean.TRUE);
         try {
-            return (NativeSymbol)cx.newObject(scope, CLASS_NAME, args);
+            return (NativeSymbol) cx.newObject(scope, CLASS_NAME, args);
         } finally {
             cx.removeThreadLocal(CONSTRUCTOR_SLOT);
         }
@@ -94,29 +102,29 @@
         addIdFunctionProperty(ctor, CLASS_NAME, ConstructorId_keyFor, "keyFor", 1);
     }
 
-    private static void createStandardSymbol(Context cx, Scriptable scope,
-                                             ScriptableObject ctor,
-                                             String name, SymbolKey key)
-    {
-        Scriptable sym = cx.newObject(scope, CLASS_NAME, new Object[] { name, key });
+    private static void createStandardSymbol(
+            Context cx, Scriptable scope, ScriptableObject ctor, String name, SymbolKey key) {
+        Scriptable sym = cx.newObject(scope, CLASS_NAME, new Object[] {name, key});
         ctor.defineProperty(name, sym, DONTENUM | READONLY | PERMANENT);
     }
 
-    // #string_id_map#
-
     @Override
     protected int findPrototypeId(String s) {
         int id = 0;
-//  #generated# Last update: 2016-01-26 16:39:41 PST
-        L0: { id = 0; String X = null;
-            int s_length = s.length();
-            if (s_length==7) { X="valueOf";id=Id_valueOf; }
-            else if (s_length==8) { X="toString";id=Id_toString; }
-            else if (s_length==11) { X="constructor";id=Id_constructor; }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-            break L0;
+        switch (s) {
+            case "constructor":
+                id = Id_constructor;
+                break;
+            case "toString":
+                id = Id_toString;
+                break;
+            case "valueOf":
+                id = Id_valueOf;
+                break;
+            default:
+                id = 0;
+                break;
         }
-//  #/generated#
         return id;
     }
 
@@ -130,86 +138,79 @@
         return 0;
     }
 
-    private static final int
-        ConstructorId_keyFor    = -2,
-        ConstructorId_for       = -1,
-        Id_constructor          = 1,
-        Id_toString             = 2,
-        Id_valueOf              = 4,
-        SymbolId_toStringTag    = 3,
-        SymbolId_toPrimitive    = 5,
-        MAX_PROTOTYPE_ID        = SymbolId_toPrimitive;
-
-    // #/string_id_map#
-
+    private static final int ConstructorId_keyFor = -2,
+            ConstructorId_for = -1,
+            Id_constructor = 1,
+            Id_toString = 2,
+            Id_valueOf = 4,
+            SymbolId_toStringTag = 3,
+            SymbolId_toPrimitive = 5,
+            MAX_PROTOTYPE_ID = SymbolId_toPrimitive;
 
     @Override
-    protected void initPrototypeId(int id)
-    {
-        String s = null;
-        int arity = -1;
+    protected void initPrototypeId(int id) {
         switch (id) {
-        case Id_constructor:
-            initPrototypeMethod(CLASS_NAME, id, "constructor", 1);
-            break;
-        case Id_toString:
-            initPrototypeMethod(CLASS_NAME, id, "toString", 0);
-            break;
-        case Id_valueOf:
-            initPrototypeMethod(CLASS_NAME, id, "valueOf", 0);
-            break;
-        case SymbolId_toStringTag:
-            initPrototypeValue(id, SymbolKey.TO_STRING_TAG, CLASS_NAME, DONTENUM | READONLY);
-            break;
-        case SymbolId_toPrimitive:
-            initPrototypeMethod(CLASS_NAME, id, SymbolKey.TO_PRIMITIVE, "Symbol.toPrimitive", 1);
-            break;
-        default:
-            super.initPrototypeId(id);
-            break;
+            case Id_constructor:
+                initPrototypeMethod(CLASS_NAME, id, "constructor", 0);
+                break;
+            case Id_toString:
+                initPrototypeMethod(CLASS_NAME, id, "toString", 0);
+                break;
+            case Id_valueOf:
+                initPrototypeMethod(CLASS_NAME, id, "valueOf", 0);
+                break;
+            case SymbolId_toStringTag:
+                initPrototypeValue(id, SymbolKey.TO_STRING_TAG, CLASS_NAME, DONTENUM | READONLY);
+                break;
+            case SymbolId_toPrimitive:
+                initPrototypeMethod(
+                        CLASS_NAME, id, SymbolKey.TO_PRIMITIVE, "Symbol.toPrimitive", 1);
+                break;
+            default:
+                super.initPrototypeId(id);
+                break;
         }
     }
 
     @Override
-    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         if (!f.hasTag(CLASS_NAME)) {
             return super.execIdCall(f, cx, scope, thisObj, args);
         }
         int id = f.methodId();
         switch (id) {
-        case ConstructorId_for:
-            return js_for(cx, scope, args);
-        case ConstructorId_keyFor:
-            return js_keyFor(cx, scope, args);
-
-        case Id_constructor:
-            if (thisObj == null) {
-                if (cx.getThreadLocal(CONSTRUCTOR_SLOT) == null) {
-                    // We should never get to this via "new".
-                    throw ScriptRuntime.typeError0("msg.no.symbol.new");
-                } else {
+            case ConstructorId_for:
+                return js_for(cx, scope, args);
+            case ConstructorId_keyFor:
+                return js_keyFor(cx, scope, args);
+
+            case Id_constructor:
+                if (thisObj == null) {
+                    if (cx.getThreadLocal(CONSTRUCTOR_SLOT) == null) {
+                        // We should never get to this via "new".
+                        throw ScriptRuntime.typeErrorById("msg.no.symbol.new");
+                    }
                     // Unless we are being called by our own internal "new"
                     return js_constructor(args);
                 }
-            } else {
                 return construct(cx, scope, args);
-            }
 
-        case Id_toString:
-            return getSelf(thisObj).toString();
-        case Id_valueOf:
-        case SymbolId_toPrimitive:
-            return getSelf(thisObj).js_valueOf();
-        default:
-            return super.execIdCall(f, cx, scope, thisObj, args);
+            case Id_toString:
+                return getSelf(cx, scope, thisObj).toString();
+            case Id_valueOf:
+            case SymbolId_toPrimitive:
+                return getSelf(cx, scope, thisObj).js_valueOf();
+            default:
+                return super.execIdCall(f, cx, scope, thisObj, args);
         }
     }
 
-    private NativeSymbol getSelf(Object thisObj) {
+    private static NativeSymbol getSelf(Context cx, Scriptable scope, Object thisObj) {
         try {
-            return (NativeSymbol)thisObj;
+            return (NativeSymbol) ScriptRuntime.toObject(cx, scope, thisObj);
         } catch (ClassCastException cce) {
-            throw ScriptRuntime.typeError1("msg.invalid.type", thisObj.getClass().getName());
+            throw ScriptRuntime.typeErrorById("msg.invalid.type", thisObj.getClass().getName());
         }
     }
 
@@ -229,7 +230,7 @@
             return new NativeSymbol((SymbolKey) args[1]);
         }
 
-        return new NativeSymbol(desc);
+        return new NativeSymbol(new SymbolKey(desc));
     }
 
     private Object js_valueOf() {
@@ -238,13 +239,16 @@
     }
 
     private Object js_for(Context cx, Scriptable scope, Object[] args) {
-        String name = (args.length > 0 ? ScriptRuntime.toString(args[0]) : ScriptRuntime.toString(Undefined.instance));
+        String name =
+                (args.length > 0
+                        ? ScriptRuntime.toString(args[0])
+                        : ScriptRuntime.toString(Undefined.instance));
 
         Map<String, NativeSymbol> table = getGlobalMap();
         NativeSymbol ret = table.get(name);
 
         if (ret == null) {
-            ret = construct(cx, scope, new Object[]{name});
+            ret = construct(cx, scope, new Object[] {name});
             table.put(name, ret);
         }
         return ret;
@@ -255,7 +259,7 @@
         if (!(s instanceof NativeSymbol)) {
             throw ScriptRuntime.throwCustomError(cx, scope, "TypeError", "Not a Symbol");
         }
-        NativeSymbol sym = (NativeSymbol)s;
+        NativeSymbol sym = (NativeSymbol) s;
 
         Map<String, NativeSymbol> table = getGlobalMap();
         for (Map.Entry<String, NativeSymbol> e : table.entrySet()) {
@@ -273,49 +277,50 @@
 
     // Symbol objects have a special property that one cannot add properties.
 
+    private static boolean isStrictMode() {
+        final Context cx = Context.getCurrentContext();
+        return (cx != null) && cx.isStrictMode();
+    }
+
     @Override
-    public void put(String name, Scriptable start, Object value)
-    {
+    public void put(String name, Scriptable start, Object value) {
         if (!isSymbol()) {
             super.put(name, start, value);
-        } else if (Context.getCurrentContext().isStrictMode()) {
-            throw ScriptRuntime.typeError0("msg.no.assign.symbol.strict");
+        } else if (isStrictMode()) {
+            throw ScriptRuntime.typeErrorById("msg.no.assign.symbol.strict");
         }
     }
 
     @Override
-    public void put(int index, Scriptable start, Object value)
-    {
+    public void put(int index, Scriptable start, Object value) {
         if (!isSymbol()) {
             super.put(index, start, value);
-        } else if (Context.getCurrentContext().isStrictMode()) {
-            throw ScriptRuntime.typeError0("msg.no.assign.symbol.strict");
+        } else if (isStrictMode()) {
+            throw ScriptRuntime.typeErrorById("msg.no.assign.symbol.strict");
         }
     }
 
     @Override
-    public void put(Symbol key, Scriptable start, Object value)
-    {
+    public void put(Symbol key, Scriptable start, Object value) {
         if (!isSymbol()) {
             super.put(key, start, value);
-        } else if (Context.getCurrentContext().isStrictMode()) {
-            throw ScriptRuntime.typeError0("msg.no.assign.symbol.strict");
+        } else if (isStrictMode()) {
+            throw ScriptRuntime.typeErrorById("msg.no.assign.symbol.strict");
         }
     }
 
     /**
-     * Object() on a Symbol constructs an object which is NOT a symbol, but which has an "internal data slot"
-     * that is. Furthermore, such an object has the Symbol prototype so this particular object is still used.
-     * Account for that here: an "Object" that was created from a Symbol has a different value of the slot.
+     * Object() on a Symbol constructs an object which is NOT a symbol, but which has an "internal
+     * data slot" that is. Furthermore, such an object has the Symbol prototype so this particular
+     * object is still used. Account for that here: an "Object" that was created from a Symbol has a
+     * different value of the slot.
      */
-    public boolean isSymbol()
-    {
+    public boolean isSymbol() {
         return (symbolData == this);
     }
 
     @Override
-    public String getTypeOf()
-    {
+    public String getTypeOf() {
         return (isSymbol() ? TYPE_NAME : super.getTypeOf());
     }
 
@@ -333,9 +338,11 @@
         return key;
     }
 
+    @SuppressWarnings("unchecked")
     private Map<String, NativeSymbol> getGlobalMap() {
-        ScriptableObject top = (ScriptableObject)getTopLevelScope(this);
-        Map<String, NativeSymbol> map = (Map<String, NativeSymbol>)top.getAssociatedValue(GLOBAL_TABLE_KEY);
+        ScriptableObject top = (ScriptableObject) getTopLevelScope(this);
+        Map<String, NativeSymbol> map =
+                (Map<String, NativeSymbol>) top.getAssociatedValue(GLOBAL_TABLE_KEY);
         if (map == null) {
             map = new HashMap<String, NativeSymbol>();
             top.associateValue(GLOBAL_TABLE_KEY, map);
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeWeakMap.java rhino-1.7.14/src/org/mozilla/javascript/NativeWeakMap.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeWeakMap.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeWeakMap.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,215 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.util.WeakHashMap;
+
+/**
+ * This is an implementation of the ES6 WeakMap class. As per the spec, keys must be ordinary
+ * objects. Since there is no defined "equality" for objects, comparisions are done strictly by
+ * object equality. Both ES6 and the java.util.WeakHashMap class have the same basic structure --
+ * entries are removed automatically when the sole remaining reference to the key is a weak
+ * reference. Therefore, we can use WeakHashMap as the basis of this implementation and preserve the
+ * same semantics.
+ */
+public class NativeWeakMap extends IdScriptableObject {
+    private static final long serialVersionUID = 8670434366883930453L;
+
+    private static final Object MAP_TAG = "WeakMap";
+
+    private boolean instanceOfWeakMap = false;
+
+    private transient WeakHashMap<Scriptable, Object> map = new WeakHashMap<>();
+
+    private static final Object NULL_VALUE = new Object();
+
+    static void init(Scriptable scope, boolean sealed) {
+        NativeWeakMap m = new NativeWeakMap();
+        m.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
+    }
+
+    @Override
+    public String getClassName() {
+        return "WeakMap";
+    }
+
+    @Override
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+
+        if (!f.hasTag(MAP_TAG)) {
+            return super.execIdCall(f, cx, scope, thisObj, args);
+        }
+        int id = f.methodId();
+        switch (id) {
+            case Id_constructor:
+                if (thisObj == null) {
+                    NativeWeakMap nm = new NativeWeakMap();
+                    nm.instanceOfWeakMap = true;
+                    if (args.length > 0) {
+                        NativeMap.loadFromIterable(cx, scope, nm, NativeMap.key(args));
+                    }
+                    return nm;
+                }
+                throw ScriptRuntime.typeErrorById("msg.no.new", "WeakMap");
+            case Id_delete:
+                return realThis(thisObj, f).js_delete(NativeMap.key(args));
+            case Id_get:
+                return realThis(thisObj, f).js_get(NativeMap.key(args));
+            case Id_has:
+                return realThis(thisObj, f).js_has(NativeMap.key(args));
+            case Id_set:
+                return realThis(thisObj, f)
+                        .js_set(
+                                NativeMap.key(args),
+                                args.length > 1 ? args[1] : Undefined.instance);
+        }
+        throw new IllegalArgumentException(
+                "WeakMap.prototype has no method: " + f.getFunctionName());
+    }
+
+    private Object js_delete(Object key) {
+        if (!ScriptRuntime.isObject(key)) {
+            return Boolean.FALSE;
+        }
+        return Boolean.valueOf(map.remove(key) != null);
+    }
+
+    private Object js_get(Object key) {
+        if (!ScriptRuntime.isObject(key)) {
+            return Undefined.instance;
+        }
+        Object result = map.get(key);
+        if (result == null) {
+            return Undefined.instance;
+        } else if (result == NULL_VALUE) {
+            return null;
+        }
+        return result;
+    }
+
+    private Object js_has(Object key) {
+        if (!ScriptRuntime.isObject(key)) {
+            return Boolean.FALSE;
+        }
+        return Boolean.valueOf(map.containsKey(key));
+    }
+
+    private Object js_set(Object key, Object v) {
+        // As the spec says, only a true "Object" can be the key to a WeakMap.
+        // Use the default object equality here. ScriptableObject does not override
+        // equals or hashCode, which means that in effect we are only keying on object identity.
+        // This is all correct according to the ECMAscript spec.
+        if (!ScriptRuntime.isObject(key)) {
+            throw ScriptRuntime.typeErrorById("msg.arg.not.object", ScriptRuntime.typeof(key));
+        }
+        // Map.get() does not distinguish between "not found" and a null value. So,
+        // replace true null here with a marker so that we can re-convert in "get".
+        final Object value = (v == null ? NULL_VALUE : v);
+        map.put((Scriptable) key, value);
+        return this;
+    }
+
+    private static NativeWeakMap realThis(Scriptable thisObj, IdFunctionObject f) {
+        final NativeWeakMap nm = ensureType(thisObj, NativeWeakMap.class, f);
+        if (!nm.instanceOfWeakMap) {
+            // Check for "Map internal data tag"
+            throw ScriptRuntime.typeErrorById("msg.incompat.call", f.getFunctionName());
+        }
+
+        return nm;
+    }
+
+    @Override
+    protected void initPrototypeId(int id) {
+        if (id == SymbolId_toStringTag) {
+            initPrototypeValue(
+                    SymbolId_toStringTag,
+                    SymbolKey.TO_STRING_TAG,
+                    getClassName(),
+                    DONTENUM | READONLY);
+            return;
+        }
+
+        String s, fnName = null;
+        int arity;
+        switch (id) {
+            case Id_constructor:
+                arity = 0;
+                s = "constructor";
+                break;
+            case Id_delete:
+                arity = 1;
+                s = "delete";
+                break;
+            case Id_get:
+                arity = 1;
+                s = "get";
+                break;
+            case Id_has:
+                arity = 1;
+                s = "has";
+                break;
+            case Id_set:
+                arity = 2;
+                s = "set";
+                break;
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
+        }
+        initPrototypeMethod(MAP_TAG, id, s, fnName, arity);
+    }
+
+    @Override
+    protected int findPrototypeId(Symbol k) {
+        if (SymbolKey.TO_STRING_TAG.equals(k)) {
+            return SymbolId_toStringTag;
+        }
+        return 0;
+    }
+
+    @Override
+    protected int findPrototypeId(String s) {
+        int id;
+        switch (s) {
+            case "constructor":
+                id = Id_constructor;
+                break;
+            case "delete":
+                id = Id_delete;
+                break;
+            case "get":
+                id = Id_get;
+                break;
+            case "has":
+                id = Id_has;
+                break;
+            case "set":
+                id = Id_set;
+                break;
+            default:
+                id = 0;
+                break;
+        }
+        return id;
+    }
+
+    private static final int Id_constructor = 1,
+            Id_delete = 2,
+            Id_get = 3,
+            Id_has = 4,
+            Id_set = 5,
+            SymbolId_toStringTag = 6,
+            MAX_PROTOTYPE_ID = SymbolId_toStringTag;
+
+    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
+        stream.defaultReadObject();
+        map = new WeakHashMap<>();
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeWeakSet.java rhino-1.7.14/src/org/mozilla/javascript/NativeWeakSet.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeWeakSet.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeWeakSet.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,184 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.util.WeakHashMap;
+
+/**
+ * This is an implementation of the ES6 WeakSet class. It is very similar to NativeWeakMap, with the
+ * exception being that it doesn't store any values. Java will GC the key only when there is no
+ * longer any reference to it other than the weak reference. That means that it is important that
+ * the "value" that we put in the WeakHashMap here is not one that contains the key.
+ */
+public class NativeWeakSet extends IdScriptableObject {
+    private static final long serialVersionUID = 2065753364224029534L;
+
+    private static final Object MAP_TAG = "WeakSet";
+
+    private boolean instanceOfWeakSet = false;
+
+    private transient WeakHashMap<Scriptable, Boolean> map = new WeakHashMap<>();
+
+    static void init(Scriptable scope, boolean sealed) {
+        NativeWeakSet m = new NativeWeakSet();
+        m.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
+    }
+
+    @Override
+    public String getClassName() {
+        return "WeakSet";
+    }
+
+    @Override
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+
+        if (!f.hasTag(MAP_TAG)) {
+            return super.execIdCall(f, cx, scope, thisObj, args);
+        }
+        int id = f.methodId();
+        switch (id) {
+            case Id_constructor:
+                if (thisObj == null) {
+                    NativeWeakSet ns = new NativeWeakSet();
+                    ns.instanceOfWeakSet = true;
+                    if (args.length > 0) {
+                        NativeSet.loadFromIterable(cx, scope, ns, NativeMap.key(args));
+                    }
+                    return ns;
+                }
+                throw ScriptRuntime.typeErrorById("msg.no.new", "WeakSet");
+            case Id_add:
+                return realThis(thisObj, f).js_add(NativeMap.key(args));
+            case Id_delete:
+                return realThis(thisObj, f).js_delete(NativeMap.key(args));
+            case Id_has:
+                return realThis(thisObj, f).js_has(NativeMap.key(args));
+        }
+        throw new IllegalArgumentException(
+                "WeakMap.prototype has no method: " + f.getFunctionName());
+    }
+
+    private Object js_add(Object key) {
+        // As the spec says, only a true "Object" can be the key to a WeakSet.
+        // Use the default object equality here. ScriptableObject does not override
+        // equals or hashCode, which means that in effect we are only keying on object identity.
+        // This is all correct according to the ECMAscript spec.
+        if (!ScriptRuntime.isObject(key)) {
+            throw ScriptRuntime.typeErrorById("msg.arg.not.object", ScriptRuntime.typeof(key));
+        }
+        // Add a value to the map, but don't make it the key -- otherwise the WeakHashMap
+        // will never GC anything.
+        map.put((Scriptable) key, Boolean.TRUE);
+        return this;
+    }
+
+    private Object js_delete(Object key) {
+        if (!ScriptRuntime.isObject(key)) {
+            return Boolean.FALSE;
+        }
+        return Boolean.valueOf(map.remove(key) != null);
+    }
+
+    private Object js_has(Object key) {
+        if (!ScriptRuntime.isObject(key)) {
+            return Boolean.FALSE;
+        }
+        return Boolean.valueOf(map.containsKey(key));
+    }
+
+    private static NativeWeakSet realThis(Scriptable thisObj, IdFunctionObject f) {
+        final NativeWeakSet ns = ensureType(thisObj, NativeWeakSet.class, f);
+        if (!ns.instanceOfWeakSet) {
+            // Check for "Set internal data tag"
+            throw ScriptRuntime.typeErrorById("msg.incompat.call", f.getFunctionName());
+        }
+
+        return ns;
+    }
+
+    @Override
+    protected void initPrototypeId(int id) {
+        if (id == SymbolId_toStringTag) {
+            initPrototypeValue(
+                    SymbolId_toStringTag,
+                    SymbolKey.TO_STRING_TAG,
+                    getClassName(),
+                    DONTENUM | READONLY);
+            return;
+        }
+
+        String s, fnName = null;
+        int arity;
+        switch (id) {
+            case Id_constructor:
+                arity = 0;
+                s = "constructor";
+                break;
+            case Id_add:
+                arity = 1;
+                s = "add";
+                break;
+            case Id_delete:
+                arity = 1;
+                s = "delete";
+                break;
+            case Id_has:
+                arity = 1;
+                s = "has";
+                break;
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
+        }
+        initPrototypeMethod(MAP_TAG, id, s, fnName, arity);
+    }
+
+    @Override
+    protected int findPrototypeId(Symbol k) {
+        if (SymbolKey.TO_STRING_TAG.equals(k)) {
+            return SymbolId_toStringTag;
+        }
+        return 0;
+    }
+
+    @Override
+    protected int findPrototypeId(String s) {
+        int id;
+        switch (s) {
+            case "constructor":
+                id = Id_constructor;
+                break;
+            case "add":
+                id = Id_add;
+                break;
+            case "delete":
+                id = Id_delete;
+                break;
+            case "has":
+                id = Id_has;
+                break;
+            default:
+                id = 0;
+                break;
+        }
+        return id;
+    }
+
+    private static final int Id_constructor = 1,
+            Id_add = 2,
+            Id_delete = 3,
+            Id_has = 4,
+            SymbolId_toStringTag = 5,
+            MAX_PROTOTYPE_ID = SymbolId_toStringTag;
+
+    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
+        stream.defaultReadObject();
+        map = new WeakHashMap<>();
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NativeWith.java rhino-1.7.14/src/org/mozilla/javascript/NativeWith.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NativeWith.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NativeWith.java	2022-01-06 22:57:21.000000000 +0100
@@ -14,12 +14,10 @@
  * It simply delegates every action to its prototype except
  * for operations on its parent.
  */
-public class NativeWith implements Scriptable, IdFunctionCall, Serializable {
-
+public class NativeWith implements Scriptable, SymbolScriptable, IdFunctionCall, Serializable {
     private static final long serialVersionUID = 1L;
 
-    static void init(Scriptable scope, boolean sealed)
-    {
+    static void init(Scriptable scope, boolean sealed) {
         NativeWith obj = new NativeWith();
 
         obj.setParentScope(scope);
@@ -42,34 +40,63 @@
         this.prototype = prototype;
     }
 
+    @Override
     public String getClassName() {
         return "With";
     }
 
+    @Override
     public boolean has(String id, Scriptable start)
     {
         return prototype.has(id, prototype);
     }
 
+    @Override
+    public boolean has(Symbol key, Scriptable start)
+    {
+        if (prototype instanceof SymbolScriptable) {
+            return ((SymbolScriptable)prototype).has(key, prototype);
+        }
+        return false;
+    }
+
+    @Override
     public boolean has(int index, Scriptable start)
     {
         return prototype.has(index, prototype);
     }
 
+    @Override
     public Object get(String id, Scriptable start)
     {
-        if (start == this)
+        if (start == this) {
             start = prototype;
+        }
         return prototype.get(id, start);
     }
 
+    @Override
+    public Object get(Symbol key, Scriptable start)
+    {
+        if (start == this) {
+            start = prototype;
+        }
+        if (prototype instanceof SymbolScriptable) {
+            return ((SymbolScriptable)prototype).get(key, start);
+        }
+        return Scriptable.NOT_FOUND;
+    }
+
+    @Override
     public Object get(int index, Scriptable start)
     {
-        if (start == this)
+        if (start == this) {
             start = prototype;
+        }
         return prototype.get(index, start);
     }
 
+    @Override
     public void put(String id, Scriptable start, Object value)
     {
         if (start == this)
@@ -77,6 +104,18 @@
         prototype.put(id, start, value);
     }
 
+    @Override
+    public void put(Symbol symbol, Scriptable start, Object value)
+    {
+        if (start == this) {
+            start = prototype;
+        }
+        if (prototype instanceof SymbolScriptable) {
+            ((SymbolScriptable)prototype).put(symbol, start, value);
+        }
+    }
+
+    @Override
     public void put(int index, Scriptable start, Object value)
     {
         if (start == this)
@@ -84,40 +123,58 @@
         prototype.put(index, start, value);
     }
 
+    @Override
     public void delete(String id)
     {
         prototype.delete(id);
     }
 
+    @Override
+    public void delete(Symbol key)
+    {
+        if (prototype instanceof SymbolScriptable) {
+            ((SymbolScriptable)prototype).delete(key);
+        }
+    }
+
+
+    @Override
     public void delete(int index)
     {
         prototype.delete(index);
     }
 
+    @Override
     public Scriptable getPrototype() {
         return prototype;
     }
 
+    @Override
     public void setPrototype(Scriptable prototype) {
         this.prototype = prototype;
     }
 
+    @Override
     public Scriptable getParentScope() {
         return parent;
     }
 
+    @Override
     public void setParentScope(Scriptable parent) {
         this.parent = parent;
     }
 
+    @Override
     public Object[] getIds() {
         return prototype.getIds();
     }
 
+    @Override
     public Object getDefaultValue(Class<?> typeHint) {
         return prototype.getDefaultValue(typeHint);
     }
 
+    @Override
     public boolean hasInstance(Scriptable value) {
         return prototype.hasInstance(value);
     }
@@ -131,12 +188,13 @@
         throw new IllegalStateException();
     }
 
+    @Override
     public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
                              Scriptable thisObj, Object[] args)
     {
         if (f.hasTag(FTAG)) {
             if (f.methodId() == Id_constructor) {
-                throw Context.reportRuntimeError1("msg.cant.call.indirect", "With");
+                throw Context.reportRuntimeErrorById("msg.cant.call.indirect", "With");
             }
         }
         throw f.unknown();
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/Node.java rhino-1.7.14/src/org/mozilla/javascript/Node.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/Node.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/Node.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,6 +6,9 @@
 
 package org.mozilla.javascript;
 
+import java.math.BigInteger;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
 import org.mozilla.javascript.ast.Comment;
 import org.mozilla.javascript.ast.FunctionNode;
 import org.mozilla.javascript.ast.Jump;
@@ -14,81 +17,70 @@
 import org.mozilla.javascript.ast.Scope;
 import org.mozilla.javascript.ast.ScriptNode;
 
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-
 /**
  * This class implements the root of the intermediate representation.
  *
  * @author Norris Boyd
  * @author Mike McCabe
  */
-public class Node implements Iterable<Node>
-{
-    public static final int
-        FUNCTION_PROP      =  1,
-        LOCAL_PROP         =  2,
-        LOCAL_BLOCK_PROP   =  3,
-        REGEXP_PROP        =  4,
-        CASEARRAY_PROP     =  5,
-
-    //  the following properties are defined and manipulated by the
-    //  optimizer -
-    //  TARGETBLOCK_PROP - the block referenced by a branch node
-    //  VARIABLE_PROP - the variable referenced by a BIND or NAME node
-    //  ISNUMBER_PROP - this node generates code on Number children and
-    //                  delivers a Number result (as opposed to Objects)
-    //  DIRECTCALL_PROP - this call node should emit code to test the function
-    //                    object against the known class and call direct if it
-    //                    matches.
-
-        TARGETBLOCK_PROP     =  6,
-        VARIABLE_PROP        =  7,
-        ISNUMBER_PROP        =  8,
-        DIRECTCALL_PROP      =  9,
-        SPECIALCALL_PROP     = 10,
-        SKIP_INDEXES_PROP    = 11, // array of skipped indexes of array literal
-        OBJECT_IDS_PROP      = 12, // array of properties for object literal
-        INCRDECR_PROP        = 13, // pre or post type of increment/decrement
-        CATCH_SCOPE_PROP     = 14, // index of catch scope block in catch
-        LABEL_ID_PROP        = 15, // label id: code generation uses it
-        MEMBER_TYPE_PROP     = 16, // type of element access operation
-        NAME_PROP            = 17, // property name
-        CONTROL_BLOCK_PROP   = 18, // flags a control block that can drop off
-        PARENTHESIZED_PROP   = 19, // expression is parenthesized
-        GENERATOR_END_PROP   = 20,
-        DESTRUCTURING_ARRAY_LENGTH = 21,
-        DESTRUCTURING_NAMES  = 22,
-        DESTRUCTURING_PARAMS = 23,
-        JSDOC_PROP           = 24,
-        EXPRESSION_CLOSURE_PROP = 25, // JS 1.8 expression closure pseudo-return
-        DESTRUCTURING_SHORTHAND = 26, // JS 1.8 destructuring shorthand
-        ARROW_FUNCTION_PROP  = 27,
-        LAST_PROP            = 27;
+public class Node implements Iterable<Node> {
+    public static final int FUNCTION_PROP = 1,
+            LOCAL_PROP = 2,
+            LOCAL_BLOCK_PROP = 3,
+            REGEXP_PROP = 4,
+            CASEARRAY_PROP = 5,
+
+            //  the following properties are defined and manipulated by the
+            //  optimizer -
+            //  TARGETBLOCK_PROP - the block referenced by a branch node
+            //  VARIABLE_PROP - the variable referenced by a BIND or NAME node
+            //  ISNUMBER_PROP - this node generates code on Number children and
+            //                  delivers a Number result (as opposed to Objects)
+            //  DIRECTCALL_PROP - this call node should emit code to test the function
+            //                    object against the known class and call direct if it
+            //                    matches.
+
+            TARGETBLOCK_PROP = 6,
+            VARIABLE_PROP = 7,
+            ISNUMBER_PROP = 8,
+            DIRECTCALL_PROP = 9,
+            SPECIALCALL_PROP = 10,
+            SKIP_INDEXES_PROP = 11, // array of skipped indexes of array literal
+            OBJECT_IDS_PROP = 12, // array of properties for object literal
+            INCRDECR_PROP = 13, // pre or post type of increment/decrement
+            CATCH_SCOPE_PROP = 14, // index of catch scope block in catch
+            LABEL_ID_PROP = 15, // label id: code generation uses it
+            MEMBER_TYPE_PROP = 16, // type of element access operation
+            NAME_PROP = 17, // property name
+            CONTROL_BLOCK_PROP = 18, // flags a control block that can drop off
+            PARENTHESIZED_PROP = 19, // expression is parenthesized
+            GENERATOR_END_PROP = 20,
+            DESTRUCTURING_ARRAY_LENGTH = 21,
+            DESTRUCTURING_NAMES = 22,
+            DESTRUCTURING_PARAMS = 23,
+            JSDOC_PROP = 24,
+            EXPRESSION_CLOSURE_PROP = 25, // JS 1.8 expression closure pseudo-return
+            SHORTHAND_PROPERTY_NAME = 26,
+            ARROW_FUNCTION_PROP = 27,
+            TEMPLATE_LITERAL_PROP = 28,
+            LAST_PROP = 28;
 
     // values of ISNUMBER_PROP to specify
     // which of the children are Number types
-    public static final int
-        BOTH = 0,
-        LEFT = 1,
-        RIGHT = 2;
-
-    public static final int    // values for SPECIALCALL_PROP
-        NON_SPECIALCALL  = 0,
-        SPECIALCALL_EVAL = 1,
-        SPECIALCALL_WITH = 2;
-
-    public static final int   // flags for INCRDECR_PROP
-        DECR_FLAG = 0x1,
-        POST_FLAG = 0x2;
-
-    public static final int   // flags for MEMBER_TYPE_PROP
-        PROPERTY_FLAG    = 0x1, // property access: element is valid name
-        ATTRIBUTE_FLAG   = 0x2, // x. at y or x.. at y
-        DESCENDANTS_FLAG = 0x4; // x..y or x.. at i
+    public static final int BOTH = 0, LEFT = 1, RIGHT = 2;
+    public static final int // values for SPECIALCALL_PROP
+            NON_SPECIALCALL = 0,
+            SPECIALCALL_EVAL = 1,
+            SPECIALCALL_WITH = 2;
+    public static final int // flags for INCRDECR_PROP
+            DECR_FLAG = 0x1,
+            POST_FLAG = 0x2;
+    public static final int // flags for MEMBER_TYPE_PROP
+            PROPERTY_FLAG = 0x1, // property access: element is valid name
+            ATTRIBUTE_FLAG = 0x2, // x. at y or x.. at y
+            DESCENDANTS_FLAG = 0x4; // x..y or x.. at i
 
-    private static class PropListItem
-    {
+    private static class PropListItem {
         PropListItem next;
         int type;
         int intValue;
@@ -163,9 +155,7 @@
         return type;
     }
 
-    /**
-     * Sets the node type and returns this node.
-     */
+    /** Sets the node type and returns this node. */
     public Node setType(int type) {
         this.type = type;
         return this;
@@ -173,29 +163,27 @@
 
     /**
      * Gets the JsDoc comment string attached to this node.
-     * @return the comment string or {@code null} if no JsDoc is attached to
-     *     this node
+     *
+     * @return the comment string or {@code null} if no JsDoc is attached to this node
      */
     public String getJsDoc() {
         Comment comment = getJsDocNode();
         if (comment != null) {
-          return comment.getValue();
+            return comment.getValue();
         }
         return null;
     }
 
     /**
      * Gets the JsDoc Comment object attached to this node.
-     * @return the Comment or {@code null} if no JsDoc is attached to
-     *     this node
+     *
+     * @return the Comment or {@code null} if no JsDoc is attached to this node
      */
     public Comment getJsDocNode() {
         return (Comment) getProp(JSDOC_PROP);
     }
 
-    /**
-     * Sets the JsDoc comment string attached to this node.
-     */
+    /** Sets the JsDoc comment string attached to this node. */
     public void setJsDocNode(Comment jsdocNode) {
         putProp(JSDOC_PROP, jsdocNode);
     }
@@ -217,13 +205,11 @@
     }
 
     public Node getChildBefore(Node child) {
-        if (child == first)
-            return null;
+        if (child == first) return null;
         Node n = first;
         while (n.next != child) {
             n = n.next;
-            if (n == null)
-                throw new RuntimeException("node is not a child");
+            if (n == null) throw new RuntimeException("node is not a child");
         }
         return n;
     }
@@ -273,13 +259,10 @@
         }
     }
 
-    /**
-     * Add 'child' before 'node'.
-     */
+    /** Add 'child' before 'node'. */
     public void addChildBefore(Node newChild, Node node) {
         if (newChild.next != null)
-            throw new RuntimeException(
-                      "newChild had siblings in addChildBefore");
+            throw new RuntimeException("newChild had siblings in addChildBefore");
         if (first == node) {
             newChild.next = first;
             first = newChild;
@@ -289,25 +272,19 @@
         addChildAfter(newChild, prev);
     }
 
-    /**
-     * Add 'child' after 'node'.
-     */
+    /** Add 'child' after 'node'. */
     public void addChildAfter(Node newChild, Node node) {
         if (newChild.next != null)
-            throw new RuntimeException(
-                      "newChild had siblings in addChildAfter");
+            throw new RuntimeException("newChild had siblings in addChildAfter");
         newChild.next = node.next;
         node.next = newChild;
-        if (last == node)
-            last = newChild;
+        if (last == node) last = newChild;
     }
 
     public void removeChild(Node child) {
         Node prev = getChildBefore(child);
-        if (prev == null)
-            first = first.next;
-        else
-            prev.next = child.next;
+        if (prev == null) first = first.next;
+        else prev.next = child.next;
         if (child == last) last = prev;
         child.next = null;
     }
@@ -320,8 +297,7 @@
             Node prev = getChildBefore(child);
             prev.next = newChild;
         }
-        if (child == last)
-            last = newChild;
+        if (child == last) last = newChild;
         child.next = null;
     }
 
@@ -329,8 +305,7 @@
         Node child = prevChild.next;
         newChild.next = child.next;
         prevChild.next = newChild;
-        if (child == last)
-            last = newChild;
+        if (child == last) last = newChild;
         child.next = null;
     }
 
@@ -341,12 +316,12 @@
     private static final Node NOT_SET = new Node(Token.ERROR);
 
     /**
-     * Iterates over the children of this Node.  Supports child removal.  Not
-     * thread-safe.  If anyone changes the child list before the iterator
-     * finishes, the results are undefined and probably bad.
+     * Iterates over the children of this Node. Supports child removal. Not thread-safe. If anyone
+     * changes the child list before the iterator finishes, the results are undefined and probably
+     * bad.
      */
     public class NodeIterator implements Iterator<Node> {
-        private Node cursor;  // points to node to be returned next
+        private Node cursor; // points to node to be returned next
         private Node prev = NOT_SET;
         private Node prev2;
         private boolean removed = false;
@@ -355,10 +330,12 @@
             cursor = Node.this.first;
         }
 
+        @Override
         public boolean hasNext() {
             return cursor != null;
         }
 
+        @Override
         public Node next() {
             if (cursor == null) {
                 throw new NoSuchElementException();
@@ -370,13 +347,13 @@
             return prev;
         }
 
+        @Override
         public void remove() {
             if (prev == NOT_SET) {
                 throw new IllegalStateException("next() has not been called");
             }
             if (removed) {
-                throw new IllegalStateException(
-                    "remove() already called for current element");
+                throw new IllegalStateException("remove() already called for current element");
             }
             if (prev == first) {
                 first = prev.next;
@@ -389,54 +366,78 @@
         }
     }
 
-    /**
-     * Returns an {@link java.util.Iterator} over the node's children.
-     */
+    /** Returns an {@link java.util.Iterator} over the node's children. */
+    @Override
     public Iterator<Node> iterator() {
         return new NodeIterator();
     }
 
-    private static final String propToString(int propType)
-    {
+    private static final String propToString(int propType) {
         if (Token.printTrees) {
             // If Context.printTrees is false, the compiler
             // can remove all these strings.
             switch (propType) {
-                case FUNCTION_PROP:        return "function";
-                case LOCAL_PROP:           return "local";
-                case LOCAL_BLOCK_PROP:     return "local_block";
-                case REGEXP_PROP:          return "regexp";
-                case CASEARRAY_PROP:       return "casearray";
-
-                case TARGETBLOCK_PROP:     return "targetblock";
-                case VARIABLE_PROP:        return "variable";
-                case ISNUMBER_PROP:        return "isnumber";
-                case DIRECTCALL_PROP:      return "directcall";
-
-                case SPECIALCALL_PROP:     return "specialcall";
-                case SKIP_INDEXES_PROP:    return "skip_indexes";
-                case OBJECT_IDS_PROP:      return "object_ids_prop";
-                case INCRDECR_PROP:        return "incrdecr_prop";
-                case CATCH_SCOPE_PROP:     return "catch_scope_prop";
-                case LABEL_ID_PROP:        return "label_id_prop";
-                case MEMBER_TYPE_PROP:     return "member_type_prop";
-                case NAME_PROP:            return "name_prop";
-                case CONTROL_BLOCK_PROP:   return "control_block_prop";
-                case PARENTHESIZED_PROP:   return "parenthesized_prop";
-                case GENERATOR_END_PROP:   return "generator_end";
+                case FUNCTION_PROP:
+                    return "function";
+                case LOCAL_PROP:
+                    return "local";
+                case LOCAL_BLOCK_PROP:
+                    return "local_block";
+                case REGEXP_PROP:
+                    return "regexp";
+                case CASEARRAY_PROP:
+                    return "casearray";
+
+                case TARGETBLOCK_PROP:
+                    return "targetblock";
+                case VARIABLE_PROP:
+                    return "variable";
+                case ISNUMBER_PROP:
+                    return "isnumber";
+                case DIRECTCALL_PROP:
+                    return "directcall";
+
+                case SPECIALCALL_PROP:
+                    return "specialcall";
+                case SKIP_INDEXES_PROP:
+                    return "skip_indexes";
+                case OBJECT_IDS_PROP:
+                    return "object_ids_prop";
+                case INCRDECR_PROP:
+                    return "incrdecr_prop";
+                case CATCH_SCOPE_PROP:
+                    return "catch_scope_prop";
+                case LABEL_ID_PROP:
+                    return "label_id_prop";
+                case MEMBER_TYPE_PROP:
+                    return "member_type_prop";
+                case NAME_PROP:
+                    return "name_prop";
+                case CONTROL_BLOCK_PROP:
+                    return "control_block_prop";
+                case PARENTHESIZED_PROP:
+                    return "parenthesized_prop";
+                case GENERATOR_END_PROP:
+                    return "generator_end";
                 case DESTRUCTURING_ARRAY_LENGTH:
-                                           return "destructuring_array_length";
-                case DESTRUCTURING_NAMES:  return "destructuring_names";
-                case DESTRUCTURING_PARAMS: return "destructuring_params";
+                    return "destructuring_array_length";
+                case DESTRUCTURING_NAMES:
+                    return "destructuring_names";
+                case DESTRUCTURING_PARAMS:
+                    return "destructuring_params";
+                case EXPRESSION_CLOSURE_PROP:
+                    return "expression_closure_prop";
+                case TEMPLATE_LITERAL_PROP:
+                    return "template_literal";
 
-                default: Kit.codeBug();
+                default:
+                    Kit.codeBug();
             }
         }
         return null;
     }
 
-    private PropListItem lookupProperty(int propType)
-    {
+    private PropListItem lookupProperty(int propType) {
         PropListItem x = propListHead;
         while (x != null && propType != x.type) {
             x = x.next;
@@ -444,8 +445,7 @@
         return x;
     }
 
-    private PropListItem ensureProperty(int propType)
-    {
+    private PropListItem ensureProperty(int propType) {
         PropListItem item = lookupProperty(propType);
         if (item == null) {
             item = new PropListItem();
@@ -456,15 +456,16 @@
         return item;
     }
 
-    public void removeProp(int propType)
-    {
+    public void removeProp(int propType) {
         PropListItem x = propListHead;
         if (x != null) {
             PropListItem prev = null;
             while (x.type != propType) {
                 prev = x;
                 x = x.next;
-                if (x == null) { return; }
+                if (x == null) {
+                    return;
+                }
             }
             if (prev == null) {
                 propListHead = x.next;
@@ -474,29 +475,31 @@
         }
     }
 
-    public Object getProp(int propType)
-    {
+    public Object getProp(int propType) {
         PropListItem item = lookupProperty(propType);
-        if (item == null) { return null; }
+        if (item == null) {
+            return null;
+        }
         return item.objectValue;
     }
 
-    public int getIntProp(int propType, int defaultValue)
-    {
+    public int getIntProp(int propType, int defaultValue) {
         PropListItem item = lookupProperty(propType);
-        if (item == null) { return defaultValue; }
+        if (item == null) {
+            return defaultValue;
+        }
         return item.intValue;
     }
 
-    public int getExistingIntProp(int propType)
-    {
+    public int getExistingIntProp(int propType) {
         PropListItem item = lookupProperty(propType);
-        if (item == null) { Kit.codeBug(); }
+        if (item == null) {
+            Kit.codeBug();
+        }
         return item.intValue;
     }
 
-    public void putProp(int propType, Object prop)
-    {
+    public void putProp(int propType, Object prop) {
         if (prop == null) {
             removeProp(propType);
         } else {
@@ -505,14 +508,14 @@
         }
     }
 
-    public void putIntProp(int propType, int prop)
-    {
+    public void putIntProp(int propType, int prop) {
         PropListItem item = ensureProperty(propType);
         item.intValue = prop;
     }
 
     /**
      * Return the line number recorded for this node.
+     *
      * @return the line number
      */
     public int getLineno() {
@@ -523,29 +526,38 @@
         this.lineno = lineno;
     }
 
-    /** Can only be called when <tt>getType() == Token.NUMBER</tt> */
+    /** Can only be called when <code>getType() == Token.NUMBER</code> */
     public final double getDouble() {
-        return ((NumberLiteral)this).getNumber();
+        return ((NumberLiteral) this).getNumber();
     }
 
     public final void setDouble(double number) {
-        ((NumberLiteral)this).setNumber(number);
+        ((NumberLiteral) this).setNumber(number);
+    }
+
+    /** Can only be called when <code>getType() == Token.BIGINT</code> */
+    public BigInteger getBigInt() {
+        throw new UnsupportedOperationException("Can only be called when Token.BIGINT");
+    }
+
+    public void setBigInt(BigInteger bigInt) {
+        throw new UnsupportedOperationException("Can only be called when Token.BIGINT");
     }
 
     /** Can only be called when node has String context. */
     public final String getString() {
-        return ((Name)this).getIdentifier();
+        return ((Name) this).getIdentifier();
     }
 
     /** Can only be called when node has String context. */
     public final void setString(String s) {
         if (s == null) Kit.codeBug();
-        ((Name)this).setIdentifier(s);
+        ((Name) this).setIdentifier(s);
     }
 
     /** Can only be called when node has String context. */
     public Scope getScope() {
-        return ((Name)this).getScope();
+        return ((Name) this).getScope();
     }
 
     /** Can only be called when node has String context. */
@@ -554,219 +566,207 @@
         if (!(this instanceof Name)) {
             throw Kit.codeBug();
         }
-        ((Name)this).setScope(s);
+        ((Name) this).setScope(s);
     }
 
-    public static Node newTarget()
-    {
+    public static Node newTarget() {
         return new Node(Token.TARGET);
     }
 
-    public final int labelId()
-    {
-        if (type != Token.TARGET && type != Token.YIELD) Kit.codeBug();
+    public final int labelId() {
+        if ((type != Token.TARGET) && (type != Token.YIELD) && (type != Token.YIELD_STAR)) {
+            Kit.codeBug();
+        }
         return getIntProp(LABEL_ID_PROP, -1);
     }
 
-    public void labelId(int labelId)
-    {
-        if (type != Token.TARGET  && type != Token.YIELD) Kit.codeBug();
+    public void labelId(int labelId) {
+        if ((type != Token.TARGET) && (type != Token.YIELD) && (type != Token.YIELD_STAR)) {
+            Kit.codeBug();
+        }
         putIntProp(LABEL_ID_PROP, labelId);
     }
 
-
     /**
-     * Does consistent-return analysis on the function body when strict mode is
-     * enabled.
+     * Does consistent-return analysis on the function body when strict mode is enabled.
+     *
+     * <p>function (x) { return (x+1) } is ok, but function (x) { if (x < 0) return (x+1); } is
+     * not becuase the function can potentially return a value when the condition is satisfied and
+     * if not, the function does not explicitly return value.
+     *
+     * <p>This extends to checking mismatches such as "return" and "return <value>" used in the same
+     * function. Warnings are not emitted if inconsistent returns exist in code that can be
+     * statically shown to be unreachable. Ex.
      *
-     *   function (x) { return (x+1) }
-     * is ok, but
-     *   function (x) { if (x < 0) return (x+1); }
-     * is not becuase the function can potentially return a value when the
-     * condition is satisfied and if not, the function does not explicitly
-     * return value.
-     *
-     * This extends to checking mismatches such as "return" and "return <value>"
-     * used in the same function. Warnings are not emitted if inconsistent
-     * returns exist in code that can be statically shown to be unreachable.
-     * Ex.
      * <pre>function (x) { while (true) { ... if (..) { return value } ... } }
      * </pre>
-     * emits no warning. However if the loop had a break statement, then a
-     * warning would be emitted.
      *
-     * The consistency analysis looks at control structures such as loops, ifs,
-     * switch, try-catch-finally blocks, examines the reachable code paths and
-     * warns the user about an inconsistent set of termination possibilities.
-     *
-     * Caveat: Since the parser flattens many control structures into almost
-     * straight-line code with gotos, it makes such analysis hard. Hence this
-     * analyser is written to taken advantage of patterns of code generated by
-     * the parser (for loops, try blocks and such) and does not do a full
-     * control flow analysis of the gotos and break/continue statements.
-     * Future changes to the parser will affect this analysis.
+     * emits no warning. However if the loop had a break statement, then a warning would be emitted.
+     *
+     * <p>The consistency analysis looks at control structures such as loops, ifs, switch,
+     * try-catch-finally blocks, examines the reachable code paths and warns the user about an
+     * inconsistent set of termination possibilities.
+     *
+     * <p>Caveat: Since the parser flattens many control structures into almost straight-line code
+     * with gotos, it makes such analysis hard. Hence this analyser is written to taken advantage of
+     * patterns of code generated by the parser (for loops, try blocks and such) and does not do a
+     * full control flow analysis of the gotos and break/continue statements. Future changes to the
+     * parser will affect this analysis.
      */
 
     /**
-     * These flags enumerate the possible ways a statement/function can
-     * terminate. These flags are used by endCheck() and by the Parser to
-     * detect inconsistent return usage.
-     *
-     * END_UNREACHED is reserved for code paths that are assumed to always be
-     * able to execute (example: throw, continue)
-     *
-     * END_DROPS_OFF indicates if the statement can transfer control to the
-     * next one. Statement such as return dont. A compound statement may have
-     * some branch that drops off control to the next statement.
-     *
-     * END_RETURNS indicates that the statement can return (without arguments)
-     * END_RETURNS_VALUE indicates that the statement can return a value.
-     *
-     * A compound statement such as
-     * if (condition) {
-     *   return value;
-     * }
-     * Will be detected as (END_DROPS_OFF | END_RETURN_VALUE) by endCheck()
+     * These flags enumerate the possible ways a statement/function can terminate. These flags are
+     * used by endCheck() and by the Parser to detect inconsistent return usage.
+     *
+     * <p>END_UNREACHED is reserved for code paths that are assumed to always be able to execute
+     * (example: throw, continue)
+     *
+     * <p>END_DROPS_OFF indicates if the statement can transfer control to the next one. Statement
+     * such as return dont. A compound statement may have some branch that drops off control to the
+     * next statement.
+     *
+     * <p>END_RETURNS indicates that the statement can return (without arguments) END_RETURNS_VALUE
+     * indicates that the statement can return a value.
+     *
+     * <p>A compound statement such as if (condition) { return value; } Will be detected as
+     * (END_DROPS_OFF | END_RETURN_VALUE) by endCheck()
      */
     public static final int END_UNREACHED = 0;
+
     public static final int END_DROPS_OFF = 1;
     public static final int END_RETURNS = 2;
     public static final int END_RETURNS_VALUE = 4;
     public static final int END_YIELDS = 8;
 
     /**
-     * Checks that every return usage in a function body is consistent with the
-     * requirements of strict-mode.
+     * Checks that every return usage in a function body is consistent with the requirements of
+     * strict-mode.
+     *
      * @return true if the function satisfies strict mode requirement.
      */
-    public boolean hasConsistentReturnUsage()
-    {
+    public boolean hasConsistentReturnUsage() {
         int n = endCheck();
-        return (n & END_RETURNS_VALUE) == 0 ||
-               (n & (END_DROPS_OFF|END_RETURNS|END_YIELDS)) == 0;
+        return (n & END_RETURNS_VALUE) == 0
+                || (n & (END_DROPS_OFF | END_RETURNS | END_YIELDS)) == 0;
     }
 
     /**
-     * Returns in the then and else blocks must be consistent with each other.
-     * If there is no else block, then the return statement can fall through.
+     * Returns in the then and else blocks must be consistent with each other. If there is no else
+     * block, then the return statement can fall through.
+     *
      * @return logical OR of END_* flags
      */
-    private int endCheckIf()
-    {
+    private int endCheckIf() {
         Node th, el;
         int rv = END_UNREACHED;
 
         th = next;
-        el = ((Jump)this).target;
+        el = ((Jump) this).target;
 
         rv = th.endCheck();
 
-        if (el != null)
-            rv |= el.endCheck();
-        else
-            rv |= END_DROPS_OFF;
+        if (el != null) rv |= el.endCheck();
+        else rv |= END_DROPS_OFF;
 
         return rv;
     }
 
     /**
-     * Consistency of return statements is checked between the case statements.
-     * If there is no default, then the switch can fall through. If there is a
-     * default,we check to see if all code paths in the default return or if
-     * there is a code path that can fall through.
+     * Consistency of return statements is checked between the case statements. If there is no
+     * default, then the switch can fall through. If there is a default,we check to see if all code
+     * paths in the default return or if there is a code path that can fall through.
+     *
      * @return logical OR of END_* flags
      */
-    private int endCheckSwitch()
-    {
+    private int endCheckSwitch() {
         int rv = END_UNREACHED;
 
         // examine the cases
-//         for (n = first.next; n != null; n = n.next)
-//         {
-//             if (n.type == Token.CASE) {
-//                 rv |= ((Jump)n).target.endCheck();
-//             } else
-//                 break;
-//         }
-
-//         // we don't care how the cases drop into each other
-//         rv &= ~END_DROPS_OFF;
-
-//         // examine the default
-//         n = ((Jump)this).getDefault();
-//         if (n != null)
-//             rv |= n.endCheck();
-//         else
-//             rv |= END_DROPS_OFF;
+        //         for (n = first.next; n != null; n = n.next)
+        //         {
+        //             if (n.type == Token.CASE) {
+        //                 rv |= ((Jump)n).target.endCheck();
+        //             } else
+        //                 break;
+        //         }
+
+        //         // we don't care how the cases drop into each other
+        //         rv &= ~END_DROPS_OFF;
+
+        //         // examine the default
+        //         n = ((Jump)this).getDefault();
+        //         if (n != null)
+        //             rv |= n.endCheck();
+        //         else
+        //             rv |= END_DROPS_OFF;
 
-//         // remove the switch block
-//         rv |= getIntProp(CONTROL_BLOCK_PROP, END_UNREACHED);
+        //         // remove the switch block
+        //         rv |= getIntProp(CONTROL_BLOCK_PROP, END_UNREACHED);
 
         return rv;
     }
 
     /**
-     * If the block has a finally, return consistency is checked in the
-     * finally block. If all code paths in the finally returns, then the
-     * returns in the try-catch blocks don't matter. If there is a code path
-     * that does not return or if there is no finally block, the returns
-     * of the try and catch blocks are checked for mismatch.
+     * If the block has a finally, return consistency is checked in the finally block. If all code
+     * paths in the finally returns, then the returns in the try-catch blocks don't matter. If there
+     * is a code path that does not return or if there is no finally block, the returns of the try
+     * and catch blocks are checked for mismatch.
+     *
      * @return logical OR of END_* flags
      */
-    private int endCheckTry()
-    {
+    private int endCheckTry() {
         int rv = END_UNREACHED;
 
         // a TryStatement isn't a jump - needs rewriting
 
         // check the finally if it exists
-//         n = ((Jump)this).getFinally();
-//         if(n != null) {
-//             rv = n.next.first.endCheck();
-//         } else {
-//             rv = END_DROPS_OFF;
-//         }
-
-//         // if the finally block always returns, then none of the returns
-//         // in the try or catch blocks matter
-//         if ((rv & END_DROPS_OFF) != 0) {
-//             rv &= ~END_DROPS_OFF;
-
-//             // examine the try block
-//             rv |= first.endCheck();
-
-//             // check each catch block
-//             n = ((Jump)this).target;
-//             if (n != null)
-//             {
-//                 // point to the first catch_scope
-//                 for (n = n.next.first; n != null; n = n.next.next)
-//                 {
-//                     // check the block of user code in the catch_scope
-//                     rv |= n.next.first.next.first.endCheck();
-//                 }
-//             }
-//         }
+        //         n = ((Jump)this).getFinally();
+        //         if(n != null) {
+        //             rv = n.next.first.endCheck();
+        //         } else {
+        //             rv = END_DROPS_OFF;
+        //         }
+
+        //         // if the finally block always returns, then none of the returns
+        //         // in the try or catch blocks matter
+        //         if ((rv & END_DROPS_OFF) != 0) {
+        //             rv &= ~END_DROPS_OFF;
+
+        //             // examine the try block
+        //             rv |= first.endCheck();
+
+        //             // check each catch block
+        //             n = ((Jump)this).target;
+        //             if (n != null)
+        //             {
+        //                 // point to the first catch_scope
+        //                 for (n = n.next.first; n != null; n = n.next.next)
+        //                 {
+        //                     // check the block of user code in the catch_scope
+        //                     rv |= n.next.first.next.first.endCheck();
+        //                 }
+        //             }
+        //         }
 
         return rv;
     }
 
     /**
-     * Return statement in the loop body must be consistent. The default
-     * assumption for any kind of a loop is that it will eventually terminate.
-     * The only exception is a loop with a constant true condition. Code that
-     * follows such a loop is examined only if one can statically determine
-     * that there is a break out of the loop.
+     * Return statement in the loop body must be consistent. The default assumption for any kind of
+     * a loop is that it will eventually terminate. The only exception is a loop with a constant
+     * true condition. Code that follows such a loop is examined only if one can statically
+     * determine that there is a break out of the loop.
+     *
      * <pre>
      *  for(<> ; <>; <>) {}
      *  for(<> in <> ) {}
      *  while(<>) { }
      *  do { } while(<>)
      * </pre>
+     *
      * @return logical OR of END_* flags
      */
-    private int endCheckLoop()
-    {
+    private int endCheckLoop() {
         Node n;
         int rv = END_UNREACHED;
 
@@ -778,15 +778,13 @@
         for (n = first; n.next != last; n = n.next) {
             /* skip */
         }
-        if (n.type != Token.IFEQ)
-            return END_DROPS_OFF;
+        if (n.type != Token.IFEQ) return END_DROPS_OFF;
 
         // The target's next is the loop body block
-        rv = ((Jump)n).target.next.endCheck();
+        rv = ((Jump) n).target.next.endCheck();
 
         // check to see if the loop condition is true
-        if (n.first.type == Token.TRUE)
-            rv &= ~END_DROPS_OFF;
+        if (n.first.type == Token.TRUE) rv &= ~END_DROPS_OFF;
 
         // look for effect of breaks
         rv |= getIntProp(CONTROL_BLOCK_PROP, END_UNREACHED);
@@ -795,20 +793,18 @@
     }
 
     /**
-     * A general block of code is examined statement by statement. If any
-     * statement (even compound ones) returns in all branches, then subsequent
-     * statements are not examined.
+     * A general block of code is examined statement by statement. If any statement (even compound
+     * ones) returns in all branches, then subsequent statements are not examined.
+     *
      * @return logical OR of END_* flags
      */
-    private int endCheckBlock()
-    {
+    private int endCheckBlock() {
         Node n;
         int rv = END_DROPS_OFF;
 
         // check each statment and if the statement can continue onto the next
         // one, then check the next statement
-        for (n=first; ((rv & END_DROPS_OFF) != 0) && n != null; n = n.next)
-        {
+        for (n = first; ((rv & END_DROPS_OFF) != 0) && n != null; n = n.next) {
             rv &= ~END_DROPS_OFF;
             rv |= n.endCheck();
         }
@@ -816,14 +812,13 @@
     }
 
     /**
-     * A labelled statement implies that there maybe a break to the label. The
-     * function processes the labelled statement and then checks the
-     * CONTROL_BLOCK_PROP property to see if there is ever a break to the
-     * particular label.
+     * A labelled statement implies that there maybe a break to the label. The function processes
+     * the labelled statement and then checks the CONTROL_BLOCK_PROP property to see if there is
+     * ever a break to the particular label.
+     *
      * @return logical OR of END_* flags
      */
-    private int endCheckLabel()
-    {
+    private int endCheckLabel() {
         int rv = END_UNREACHED;
 
         rv = next.endCheck();
@@ -833,39 +828,37 @@
     }
 
     /**
-     * When a break is encountered annotate the statement being broken
-     * out of by setting its CONTROL_BLOCK_PROP property.
+     * When a break is encountered annotate the statement being broken out of by setting its
+     * CONTROL_BLOCK_PROP property.
+     *
      * @return logical OR of END_* flags
      */
-    private int endCheckBreak()
-    {
+    private int endCheckBreak() {
         Node n = ((Jump) this).getJumpStatement();
         n.putIntProp(CONTROL_BLOCK_PROP, END_DROPS_OFF);
         return END_UNREACHED;
     }
 
     /**
-     * endCheck() examines the body of a function, doing a basic reachability
-     * analysis and returns a combination of flags END_* flags that indicate
-     * how the function execution can terminate. These constitute only the
-     * pessimistic set of termination conditions. It is possible that at
-     * runtime certain code paths will never be actually taken. Hence this
-     * analysis will flag errors in cases where there may not be errors.
+     * endCheck() examines the body of a function, doing a basic reachability analysis and returns a
+     * combination of flags END_* flags that indicate how the function execution can terminate.
+     * These constitute only the pessimistic set of termination conditions. It is possible that at
+     * runtime certain code paths will never be actually taken. Hence this analysis will flag errors
+     * in cases where there may not be errors.
+     *
      * @return logical OR of END_* flags
      */
-    private int endCheck()
-    {
-        switch(type)
-        {
+    private int endCheck() {
+        switch (type) {
             case Token.BREAK:
                 return endCheckBreak();
 
             case Token.EXPR_VOID:
-                if (this.first != null)
-                    return first.endCheck();
+                if (this.first != null) return first.endCheck();
                 return END_DROPS_OFF;
 
             case Token.YIELD:
+            case Token.YIELD_STAR:
                 return END_YIELDS;
 
             case Token.CONTINUE:
@@ -873,16 +866,12 @@
                 return END_UNREACHED;
 
             case Token.RETURN:
-                if (this.first != null)
-                    return END_RETURNS_VALUE;
-                else
-                    return END_RETURNS;
+                if (this.first != null) return END_RETURNS_VALUE;
+                return END_RETURNS;
 
             case Token.TARGET:
-                if (next != null)
-                    return next.endCheck();
-                else
-                    return END_DROPS_OFF;
+                if (next != null) return next.endCheck();
+                return END_DROPS_OFF;
 
             case Token.LOOP:
                 return endCheckLoop();
@@ -890,10 +879,9 @@
             case Token.LOCAL_BLOCK:
             case Token.BLOCK:
                 // there are several special kinds of blocks
-                if (first == null)
-                    return END_DROPS_OFF;
+                if (first == null) return END_DROPS_OFF;
 
-                switch(first.type) {
+                switch (first.type) {
                     case Token.LABEL:
                         return first.endCheckLabel();
 
@@ -915,114 +903,106 @@
         }
     }
 
-    public boolean hasSideEffects()
-    {
+    public boolean hasSideEffects() {
         switch (type) {
-          case Token.EXPR_VOID:
-          case Token.COMMA:
-            if (last != null)
-                return last.hasSideEffects();
-            else
+            case Token.EXPR_VOID:
+            case Token.COMMA:
+                if (last != null) return last.hasSideEffects();
                 return true;
 
-          case Token.HOOK:
-            if (first == null ||
-                first.next == null ||
-                first.next.next == null)
-                Kit.codeBug();
-            return first.next.hasSideEffects() &&
-                   first.next.next.hasSideEffects();
-
-          case Token.AND:
-          case Token.OR:
-            if (first == null || last == null)
-                Kit.codeBug();
-            return first.hasSideEffects() || last.hasSideEffects();
-
-          case Token.ERROR:         // Avoid cascaded error messages
-          case Token.EXPR_RESULT:
-          case Token.ASSIGN:
-          case Token.ASSIGN_ADD:
-          case Token.ASSIGN_SUB:
-          case Token.ASSIGN_MUL:
-          case Token.ASSIGN_DIV:
-          case Token.ASSIGN_MOD:
-          case Token.ASSIGN_BITOR:
-          case Token.ASSIGN_BITXOR:
-          case Token.ASSIGN_BITAND:
-          case Token.ASSIGN_LSH:
-          case Token.ASSIGN_RSH:
-          case Token.ASSIGN_URSH:
-          case Token.ENTERWITH:
-          case Token.LEAVEWITH:
-          case Token.RETURN:
-          case Token.GOTO:
-          case Token.IFEQ:
-          case Token.IFNE:
-          case Token.NEW:
-          case Token.DELPROP:
-          case Token.SETNAME:
-          case Token.SETPROP:
-          case Token.SETELEM:
-          case Token.CALL:
-          case Token.THROW:
-          case Token.RETHROW:
-          case Token.SETVAR:
-          case Token.CATCH_SCOPE:
-          case Token.RETURN_RESULT:
-          case Token.SET_REF:
-          case Token.DEL_REF:
-          case Token.REF_CALL:
-          case Token.TRY:
-          case Token.SEMI:
-          case Token.INC:
-          case Token.DEC:
-          case Token.IF:
-          case Token.ELSE:
-          case Token.SWITCH:
-          case Token.WHILE:
-          case Token.DO:
-          case Token.FOR:
-          case Token.BREAK:
-          case Token.CONTINUE:
-          case Token.VAR:
-          case Token.CONST:
-          case Token.LET:
-          case Token.LETEXPR:
-          case Token.WITH:
-          case Token.WITHEXPR:
-          case Token.CATCH:
-          case Token.FINALLY:
-          case Token.BLOCK:
-          case Token.LABEL:
-          case Token.TARGET:
-          case Token.LOOP:
-          case Token.JSR:
-          case Token.SETPROP_OP:
-          case Token.SETELEM_OP:
-          case Token.LOCAL_BLOCK:
-          case Token.SET_REF_OP:
-          case Token.YIELD:
-            return true;
+            case Token.HOOK:
+                if (first == null || first.next == null || first.next.next == null) Kit.codeBug();
+                return first.next.hasSideEffects() && first.next.next.hasSideEffects();
+
+            case Token.AND:
+            case Token.OR:
+                if (first == null || last == null) Kit.codeBug();
+                return first.hasSideEffects() || last.hasSideEffects();
+
+            case Token.ERROR: // Avoid cascaded error messages
+            case Token.EXPR_RESULT:
+            case Token.ASSIGN:
+            case Token.ASSIGN_ADD:
+            case Token.ASSIGN_SUB:
+            case Token.ASSIGN_MUL:
+            case Token.ASSIGN_DIV:
+            case Token.ASSIGN_MOD:
+            case Token.ASSIGN_BITOR:
+            case Token.ASSIGN_BITXOR:
+            case Token.ASSIGN_BITAND:
+            case Token.ASSIGN_LSH:
+            case Token.ASSIGN_RSH:
+            case Token.ASSIGN_URSH:
+            case Token.ENTERWITH:
+            case Token.LEAVEWITH:
+            case Token.RETURN:
+            case Token.GOTO:
+            case Token.IFEQ:
+            case Token.IFNE:
+            case Token.NEW:
+            case Token.DELPROP:
+            case Token.SETNAME:
+            case Token.SETPROP:
+            case Token.SETELEM:
+            case Token.CALL:
+            case Token.THROW:
+            case Token.RETHROW:
+            case Token.SETVAR:
+            case Token.CATCH_SCOPE:
+            case Token.RETURN_RESULT:
+            case Token.SET_REF:
+            case Token.DEL_REF:
+            case Token.REF_CALL:
+            case Token.TRY:
+            case Token.SEMI:
+            case Token.INC:
+            case Token.DEC:
+            case Token.IF:
+            case Token.ELSE:
+            case Token.SWITCH:
+            case Token.WHILE:
+            case Token.DO:
+            case Token.FOR:
+            case Token.BREAK:
+            case Token.CONTINUE:
+            case Token.VAR:
+            case Token.CONST:
+            case Token.LET:
+            case Token.LETEXPR:
+            case Token.WITH:
+            case Token.WITHEXPR:
+            case Token.CATCH:
+            case Token.FINALLY:
+            case Token.BLOCK:
+            case Token.LABEL:
+            case Token.TARGET:
+            case Token.LOOP:
+            case Token.JSR:
+            case Token.SETPROP_OP:
+            case Token.SETELEM_OP:
+            case Token.LOCAL_BLOCK:
+            case Token.SET_REF_OP:
+            case Token.YIELD:
+            case Token.YIELD_STAR:
+                return true;
 
-          default:
-            return false;
+            default:
+                return false;
         }
     }
 
     /**
      * Recursively unlabel every TARGET or YIELD node in the tree.
      *
-     * This is used and should only be used for inlining finally blocks where
-     * jsr instructions used to be. It is somewhat hackish, but implementing
-     * a clone() operation would take much, much more effort.
-     *
-     * This solution works for inlining finally blocks because you should never
-     * be writing any given block to the class file simultaneously. Therefore,
-     * an unlabeling will never occur in the middle of a block.
+     * <p>This is used and should only be used for inlining finally blocks where jsr instructions
+     * used to be. It is somewhat hackish, but implementing a clone() operation would take much,
+     * much more effort.
+     *
+     * <p>This solution works for inlining finally blocks because you should never be writing any
+     * given block to the class file simultaneously. Therefore, an unlabeling will never occur in
+     * the middle of a block.
      */
-    public void resetTargets()
-    {
+    public void resetTargets() {
         if (type == Token.FINALLY) {
             resetTargets_r();
         } else {
@@ -1030,9 +1010,8 @@
         }
     }
 
-    private void resetTargets_r()
-    {
-        if (type == Token.TARGET || type == Token.YIELD) {
+    private void resetTargets_r() {
+        if (type == Token.TARGET || type == Token.YIELD || type == Token.YIELD_STAR) {
             labelId(-1);
         }
         Node child = first;
@@ -1043,8 +1022,7 @@
     }
 
     @Override
-    public String toString()
-    {
+    public String toString() {
         if (Token.printTrees) {
             StringBuilder sb = new StringBuilder();
             toString(new ObjToIntMap(), sb);
@@ -1053,8 +1031,7 @@
         return String.valueOf(type);
     }
 
-    private void toString(ObjToIntMap printIds, StringBuilder sb)
-    {
+    private void toString(ObjToIntMap printIds, StringBuilder sb) {
         if (Token.printTrees) {
             sb.append(Token.name(type));
             if (this instanceof Name) {
@@ -1068,29 +1045,27 @@
                 }
             } else if (this instanceof Scope) {
                 if (this instanceof ScriptNode) {
-                    ScriptNode sof = (ScriptNode)this;
+                    ScriptNode sof = (ScriptNode) this;
                     if (this instanceof FunctionNode) {
-                        FunctionNode fn = (FunctionNode)this;
+                        FunctionNode fn = (FunctionNode) this;
                         sb.append(' ');
                         sb.append(fn.getName());
                     }
                     sb.append(" [source name: ");
                     sb.append(sof.getSourceName());
                     sb.append("] [encoded source length: ");
-                    sb.append(sof.getEncodedSourceEnd()
-                              - sof.getEncodedSourceStart());
+                    sb.append(sof.getEncodedSourceEnd() - sof.getEncodedSourceStart());
                     sb.append("] [base line: ");
                     sb.append(sof.getBaseLineno());
                     sb.append("] [end line: ");
                     sb.append(sof.getEndLineno());
                     sb.append(']');
                 }
-                if (((Scope)this).getSymbolTable() != null) {
+                if (((Scope) this).getSymbolTable() != null) {
                     sb.append(" [scope ");
                     appendPrintId(this, printIds, sb);
                     sb.append(": ");
-                    Iterator<String> iter =
-                        ((Scope) this).getSymbolTable().keySet().iterator();
+                    Iterator<String> iter = ((Scope) this).getSymbolTable().keySet().iterator();
                     while (iter.hasNext()) {
                         sb.append(iter.next());
                         sb.append(" ");
@@ -1098,7 +1073,7 @@
                     sb.append("]");
                 }
             } else if (this instanceof Jump) {
-                Jump jump = (Jump)this;
+                Jump jump = (Jump) this;
                 if (type == Token.BREAK || type == Token.CONTINUE) {
                     sb.append(" [label: ");
                     appendPrintId(jump.getJumpStatement(), printIds, sb);
@@ -1116,9 +1091,7 @@
                         appendPrintId(finallyTarget, printIds, sb);
                         sb.append(']');
                     }
-                } else if (type == Token.LABEL || type == Token.LOOP
-                           || type == Token.SWITCH)
-                {
+                } else if (type == Token.LABEL || type == Token.LOOP || type == Token.SWITCH) {
                     sb.append(" [break: ");
                     appendPrintId(jump.target, printIds, sb);
                     sb.append(']');
@@ -1135,6 +1108,9 @@
             } else if (type == Token.NUMBER) {
                 sb.append(' ');
                 sb.append(getDouble());
+            } else if (type == Token.BIGINT) {
+                sb.append(' ');
+                sb.append(getBigInt().toString());
             } else if (type == Token.TARGET) {
                 sb.append(' ');
                 appendPrintId(this, printIds, sb);
@@ -1151,59 +1127,59 @@
                 sb.append(": ");
                 String value;
                 switch (type) {
-                  case TARGETBLOCK_PROP : // can't add this as it recurses
-                    value = "target block property";
-                    break;
-                  case LOCAL_BLOCK_PROP :     // can't add this as it is dull
-                    value = "last local block";
-                    break;
-                  case ISNUMBER_PROP:
-                    switch (x.intValue) {
-                      case BOTH:
-                        value = "both";
+                    case TARGETBLOCK_PROP: // can't add this as it recurses
+                        value = "target block property";
                         break;
-                      case RIGHT:
-                        value = "right";
+                    case LOCAL_BLOCK_PROP: // can't add this as it is dull
+                        value = "last local block";
                         break;
-                      case LEFT:
-                        value = "left";
+                    case ISNUMBER_PROP:
+                        switch (x.intValue) {
+                            case BOTH:
+                                value = "both";
+                                break;
+                            case RIGHT:
+                                value = "right";
+                                break;
+                            case LEFT:
+                                value = "left";
+                                break;
+                            default:
+                                throw Kit.codeBug();
+                        }
                         break;
-                      default:
-                        throw Kit.codeBug();
-                    }
-                    break;
-                  case SPECIALCALL_PROP:
-                    switch (x.intValue) {
-                      case SPECIALCALL_EVAL:
-                        value = "eval";
+                    case SPECIALCALL_PROP:
+                        switch (x.intValue) {
+                            case SPECIALCALL_EVAL:
+                                value = "eval";
+                                break;
+                            case SPECIALCALL_WITH:
+                                value = "with";
+                                break;
+                            default:
+                                // NON_SPECIALCALL should not be stored
+                                throw Kit.codeBug();
+                        }
                         break;
-                      case SPECIALCALL_WITH:
-                        value = "with";
+                    case OBJECT_IDS_PROP:
+                        {
+                            Object[] a = (Object[]) x.objectValue;
+                            value = "[";
+                            for (int i = 0; i < a.length; i++) {
+                                value += a[i].toString();
+                                if (i + 1 < a.length) value += ", ";
+                            }
+                            value += "]";
+                            break;
+                        }
+                    default:
+                        Object obj = x.objectValue;
+                        if (obj != null) {
+                            value = obj.toString();
+                        } else {
+                            value = String.valueOf(x.intValue);
+                        }
                         break;
-                      default:
-                        // NON_SPECIALCALL should not be stored
-                        throw Kit.codeBug();
-                    }
-                    break;
-                  case OBJECT_IDS_PROP: {
-                    Object[] a = (Object[]) x.objectValue;
-                    value = "[";
-                    for (int i=0; i < a.length; i++) {
-                        value += a[i].toString();
-                        if (i+1 < a.length)
-                            value += ", ";
-                    }
-                    value += "]";
-                    break;
-                  }
-                  default :
-                    Object obj = x.objectValue;
-                    if (obj != null) {
-                        value = obj.toString();
-                    } else {
-                        value = String.valueOf(x.intValue);
-                    }
-                    break;
                 }
                 sb.append(value);
                 sb.append(']');
@@ -1220,10 +1196,8 @@
         return null;
     }
 
-    private static void toStringTreeHelper(ScriptNode treeTop, Node n,
-                                           ObjToIntMap printIds,
-                                           int level, StringBuilder sb)
-    {
+    private static void toStringTreeHelper(
+            ScriptNode treeTop, Node n, ObjToIntMap printIds, int level, StringBuilder sb) {
         if (Token.printTrees) {
             if (printIds == null) {
                 printIds = new ObjToIntMap();
@@ -1234,35 +1208,28 @@
             }
             n.toString(printIds, sb);
             sb.append('\n');
-            for (Node cursor = n.getFirstChild(); cursor != null;
-                 cursor = cursor.getNext())
-            {
+            for (Node cursor = n.getFirstChild(); cursor != null; cursor = cursor.getNext()) {
                 if (cursor.getType() == Token.FUNCTION) {
                     int fnIndex = cursor.getExistingIntProp(Node.FUNCTION_PROP);
                     FunctionNode fn = treeTop.getFunctionNode(fnIndex);
                     toStringTreeHelper(fn, fn, null, level + 1, sb);
                 } else {
-                    toStringTreeHelper(treeTop, cursor, printIds, level+1, sb);
+                    toStringTreeHelper(treeTop, cursor, printIds, level + 1, sb);
                 }
             }
         }
     }
 
-    private static void generatePrintIds(Node n, ObjToIntMap map)
-    {
+    private static void generatePrintIds(Node n, ObjToIntMap map) {
         if (Token.printTrees) {
             map.put(n, map.size());
-            for (Node cursor = n.getFirstChild(); cursor != null;
-                 cursor = cursor.getNext())
-            {
+            for (Node cursor = n.getFirstChild(); cursor != null; cursor = cursor.getNext()) {
                 generatePrintIds(cursor, map);
             }
         }
     }
 
-    private static void appendPrintId(Node n, ObjToIntMap printIds,
-                                      StringBuilder sb)
-    {
+    private static void appendPrintId(Node n, ObjToIntMap printIds, StringBuilder sb) {
         if (Token.printTrees) {
             if (n != null) {
                 int id = printIds.get(n, -1);
@@ -1277,16 +1244,15 @@
     }
 
     protected int type = Token.ERROR; // type of the node, e.g. Token.NAME
-    protected Node next;             // next sibling
-    protected Node first;    // first element of a linked list of children
-    protected Node last;     // last element of a linked list of children
+    protected Node next; // next sibling
+    protected Node first; // first element of a linked list of children
+    protected Node last; // last element of a linked list of children
     protected int lineno = -1;
 
     /**
-     * Linked list of properties. Since vast majority of nodes would have
-     * no more then 2 properties, linked list saves memory and provides
-     * fast lookup. If this does not holds, propListHead can be replaced
-     * by UintMap.
+     * Linked list of properties. Since vast majority of nodes would have no more then 2 properties,
+     * linked list saves memory and provides fast lookup. If this does not holds, propListHead can
+     * be replaced by UintMap.
      */
     protected PropListItem propListHead;
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/NodeTransformer.java rhino-1.7.14/src/org/mozilla/javascript/NodeTransformer.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/NodeTransformer.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/NodeTransformer.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,14 +6,14 @@
 
 package org.mozilla.javascript;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import org.mozilla.javascript.ast.FunctionNode;
 import org.mozilla.javascript.ast.Jump;
 import org.mozilla.javascript.ast.Scope;
 import org.mozilla.javascript.ast.ScriptNode;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * This class transforms a tree to a lower-level representation for codegen.
  *
@@ -28,18 +28,24 @@
     {
     }
 
-    public final void transform(ScriptNode tree)
+    public final void transform(ScriptNode tree, CompilerEnvirons env)
     {
-        transform(tree, false);
+        transform(tree, false, env);
     }
 
-    public final void transform(ScriptNode tree, boolean inStrictMode)
+    public final void transform(ScriptNode tree, boolean inStrictMode, CompilerEnvirons env)
     {
-        inStrictMode = inStrictMode || tree.isInStrictMode();
-        transformCompilationUnit(tree, inStrictMode);
+        boolean useStrictMode = inStrictMode;
+        // Support strict mode inside a function only for "ES6" language level
+        // and above. Otherwise, we will end up breaking backward compatibility for
+        // many existing scripts.
+        if ((env.getLanguageVersion() >= Context.VERSION_ES6) && tree.isInStrictMode()) {
+          useStrictMode = true;
+        }
+        transformCompilationUnit(tree, useStrictMode);
         for (int i = 0; i != tree.getFunctionCount(); ++i) {
             FunctionNode fn = tree.getFunctionNode(i);
-            transform(fn, inStrictMode);
+            transform(fn, useStrictMode, env);
         }
     }
 
@@ -148,6 +154,7 @@
                 break;
 
               case Token.YIELD:
+              case Token.YIELD_STAR:
                 ((FunctionNode)tree).addResumptionPoint(node);
                 break;
 
@@ -272,9 +279,8 @@
                       || ((FunctionNode)tree).requiresActivation();
                   node = visitLet(createWith, parent, previous, node);
                   break;
-                } else {
-                  // fall through to process let declaration...
                 }
+                // fall through to process let declaration...
               }
               /* fall through */
               case Token.CONST:
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ObjArray.java rhino-1.7.14/src/org/mozilla/javascript/ObjArray.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ObjArray.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ObjArray.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,10 +6,10 @@
 
 package org.mozilla.javascript;
 
-import java.io.Serializable;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
+import java.io.Serializable;
 
 /**
 Implementation of resizable array with focus on minimizing memory usage by storing few initial array elements in object fields. Can also be used as a stack.
@@ -17,7 +17,7 @@
 
 public class ObjArray implements Serializable
 {
-    static final long serialVersionUID = 4174889037736658296L;
+    private static final long serialVersionUID = 4174889037736658296L;
 
     public ObjArray() { }
 
@@ -174,21 +174,21 @@
             case 0:
                 if (N == 0) { f0 = value; break; }
                 tmp = f0; f0 = value; value = tmp;
-            /* fallthru */ case 1:
+            /* fall through */ case 1:
                 if (N == 1) { f1 = value; break; }
                 tmp = f1; f1 = value; value = tmp;
-            /* fallthru */ case 2:
+            /* fall through */ case 2:
                 if (N == 2) { f2 = value; break; }
                 tmp = f2; f2 = value; value = tmp;
-            /* fallthru */ case 3:
+            /* fall through */ case 3:
                 if (N == 3) { f3 = value; break; }
                 tmp = f3; f3 = value; value = tmp;
-            /* fallthru */ case 4:
+            /* fall through */ case 4:
                 if (N == 4) { f4 = value; break; }
                 tmp = f4; f4 = value; value = tmp;
 
                 index = FIELDS_STORE_SIZE;
-            /* fallthru */ default:
+            /* fall through */ default:
                 ensureCapacity(N + 1);
                 if (index != N) {
                     System.arraycopy(data, index - FIELDS_STORE_SIZE,
@@ -210,21 +210,21 @@
             case 0:
                 if (N == 0) { f0 = null; break; }
                 f0 = f1;
-            /* fallthru */ case 1:
+            /* fall through */ case 1:
                 if (N == 1) { f1 = null; break; }
                 f1 = f2;
-            /* fallthru */ case 2:
+            /* fall through */ case 2:
                 if (N == 2) { f2 = null; break; }
                 f2 = f3;
-            /* fallthru */ case 3:
+            /* fall through */ case 3:
                 if (N == 3) { f3 = null; break; }
                 f3 = f4;
-            /* fallthru */ case 4:
+            /* fall through */ case 4:
                 if (N == 4) { f4 = null; break; }
                 f4 = data[0];
 
                 index = FIELDS_STORE_SIZE;
-            /* fallthru */ default:
+            /* fall through */ default:
                 if (index != N) {
                     System.arraycopy(data, index - FIELDS_STORE_SIZE + 1,
                                      data, index - FIELDS_STORE_SIZE,
@@ -264,12 +264,12 @@
             default:
                 System.arraycopy(data, 0, array, offset + FIELDS_STORE_SIZE,
                                  N - FIELDS_STORE_SIZE);
-            /* fallthru */ case 5: array[offset + 4] = f4;
-            /* fallthru */ case 4: array[offset + 3] = f3;
-            /* fallthru */ case 3: array[offset + 2] = f2;
-            /* fallthru */ case 2: array[offset + 1] = f1;
-            /* fallthru */ case 1: array[offset + 0] = f0;
-            /* fallthru */ case 0: break;
+            /* fall through */ case 5: array[offset + 4] = f4;
+            /* fall through */ case 4: array[offset + 3] = f3;
+            /* fall through */ case 3: array[offset + 2] = f2;
+            /* fall through */ case 2: array[offset + 1] = f1;
+            /* fall through */ case 1: array[offset + 0] = f0;
+            /* fall through */ case 0: break;
         }
     }
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ObjToIntMap.java rhino-1.7.14/src/org/mozilla/javascript/ObjToIntMap.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ObjToIntMap.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ObjToIntMap.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,29 +6,26 @@
 
 package org.mozilla.javascript;
 
-import java.io.Serializable;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
+import java.io.Serializable;
 
 /**
- * Map to associate objects to integers.
- * The map does not synchronize any of its operation, so either use
- * it from a single thread or do own synchronization or perform all mutation
- * operations on one thread before passing the map to others
+ * Map to associate objects to integers. The map does not synchronize any of its operation, so
+ * either use it from a single thread or do own synchronization or perform all mutation operations
+ * on one thread before passing the map to others
  *
  * @author Igor Bukanov
- *
  */
+ at SuppressWarnings("unused")
+public class ObjToIntMap implements Serializable {
+    private static final long serialVersionUID = -1542220580748809402L;
 
-public class ObjToIntMap implements Serializable
-{
-    static final long serialVersionUID = -1542220580748809402L;
+    // Map implementation via hashtable,
+    // follows "The Art of Computer Programming" by Donald E. Knuth
 
-// Map implementation via hashtable,
-// follows "The Art of Computer Programming" by Donald E. Knuth
-
-// ObjToIntMap is a copy cat of ObjToIntMap with API adjusted to object keys
+    // ObjToIntMap is a copy cat of ObjToIntMap with API adjusted to object keys
 
     public static class Iterator {
 
@@ -57,7 +54,7 @@
             if (remaining == 0) {
                 remaining = -1;
                 cursor = -1;
-            }else {
+            } else {
                 for (++cursor; ; ++cursor) {
                     Object key = keys[cursor];
                     if (key != null && key != DELETED) {
@@ -70,7 +67,9 @@
 
         public Object getKey() {
             Object key = keys[cursor];
-            if (key == UniqueTag.NULL_VALUE) { key = null; }
+            if (key == UniqueTag.NULL_VALUE) {
+                key = null;
+            }
             return key;
         }
 
@@ -98,7 +97,7 @@
         // Table grow when number of stored keys >= 3/4 of max capacity
         int minimalCapacity = keyCountHint * 4 / 3;
         int i;
-        for (i = 2; (1 << i) < minimalCapacity; ++i) { }
+        for (i = 2; (1 << i) < minimalCapacity; ++i) {}
         power = i;
         if (check && power < 2) Kit.codeBug();
     }
@@ -112,16 +111,21 @@
     }
 
     public boolean has(Object key) {
-        if (key == null) { key = UniqueTag.NULL_VALUE; }
+        if (key == null) {
+            key = UniqueTag.NULL_VALUE;
+        }
         return 0 <= findIndex(key);
     }
 
     /**
      * Get integer value assigned with key.
+     *
      * @return key integer value or defaultValue if key is absent
      */
     public int get(Object key, int defaultValue) {
-        if (key == null) { key = UniqueTag.NULL_VALUE; }
+        if (key == null) {
+            key = UniqueTag.NULL_VALUE;
+        }
         int index = findIndex(key);
         if (0 <= index) {
             return values[index];
@@ -131,11 +135,14 @@
 
     /**
      * Get integer value assigned with key.
+     *
      * @return key integer value
      * @throws RuntimeException if key does not exist
      */
     public int getExisting(Object key) {
-        if (key == null) { key = UniqueTag.NULL_VALUE; }
+        if (key == null) {
+            key = UniqueTag.NULL_VALUE;
+        }
         int index = findIndex(key);
         if (0 <= index) {
             return values[index];
@@ -146,15 +153,16 @@
     }
 
     public void put(Object key, int value) {
-        if (key == null) { key = UniqueTag.NULL_VALUE; }
+        if (key == null) {
+            key = UniqueTag.NULL_VALUE;
+        }
         int index = ensureIndex(key);
         values[index] = value;
     }
 
     /**
-     * If table already contains a key that equals to keyArg, return that key
-     * while setting its value to zero, otherwise add keyArg with 0 value to
-     * the table and return it.
+     * If table already contains a key that equals to keyArg, return that key while setting its
+     * value to zero, otherwise add keyArg with 0 value to the table and return it.
      */
     public Object intern(Object keyArg) {
         boolean nullKey = false;
@@ -168,7 +176,9 @@
     }
 
     public void remove(Object key) {
-        if (key == null) { key = UniqueTag.NULL_VALUE; }
+        if (key == null) {
+            key = UniqueTag.NULL_VALUE;
+        }
         int index = findIndex(key);
         if (0 <= index) {
             keys[index] = DELETED;
@@ -208,7 +218,9 @@
         for (int i = 0; count != 0; ++i) {
             Object key = keys[i];
             if (key != null && key != DELETED) {
-                if (key == UniqueTag.NULL_VALUE) { key = null; }
+                if (key == UniqueTag.NULL_VALUE) {
+                    key = null;
+                }
                 array[offset] = key;
                 ++offset;
                 --count;
@@ -221,9 +233,7 @@
         if (shift >= 0) {
             return ((fraction >>> shift) & mask) | 1;
         }
-        else {
-            return (fraction & (mask >>> -shift)) | 1;
-        }
+        return (fraction & (mask >>> -shift)) | 1;
     }
 
     private int findIndex(Object key) {
@@ -234,16 +244,14 @@
             Object test = keys[index];
             if (test != null) {
                 int N = 1 << power;
-                if (test == key
-                    || (values[N + index] == hash && test.equals(key)))
-                {
+                if (test == key || (values[N + index] == hash && test.equals(key))) {
                     return index;
                 }
                 // Search in table after first failed attempt
                 int mask = N - 1;
                 int step = tableLookupStep(fraction, mask, power);
                 int n = 0;
-                for (;;) {
+                for (; ; ) {
                     if (check) {
                         if (n >= occupiedCount) Kit.codeBug();
                         ++n;
@@ -253,9 +261,7 @@
                     if (test == null) {
                         break;
                     }
-                    if (test == key
-                        || (values[N + index] == hash && test.equals(key)))
-                    {
+                    if (test == key || (values[N + index] == hash && test.equals(key))) {
                         return index;
                     }
                 }
@@ -264,8 +270,8 @@
         return -1;
     }
 
-// Insert key that is not present to table without deleted entries
-// and enough free space
+    // Insert key that is not present to table without deleted entries
+    // and enough free space
     private int insertNewKey(Object key, int hash) {
         if (check && occupiedCount != keyCount) Kit.codeBug();
         if (check && keyCount == 1 << power) Kit.codeBug();
@@ -297,8 +303,7 @@
             int N = 1 << power;
             keys = new Object[N];
             values = new int[2 * N];
-        }
-        else {
+        } else {
             // Check if removing deleted entries would free enough space
             if (keyCount * 2 >= occupiedCount) {
                 // Need to grow: less then half of deleted entries
@@ -325,7 +330,7 @@
         }
     }
 
-// Ensure key index creating one if necessary
+    // Ensure key index creating one if necessary
     private int ensureIndex(Object key) {
         int hash = key.hashCode();
         int index = -1;
@@ -336,9 +341,7 @@
             Object test = keys[index];
             if (test != null) {
                 int N = 1 << power;
-                if (test == key
-                    || (values[N + index] == hash && test.equals(key)))
-                {
+                if (test == key || (values[N + index] == hash && test.equals(key))) {
                     return index;
                 }
                 if (test == DELETED) {
@@ -349,7 +352,7 @@
                 int mask = N - 1;
                 int step = tableLookupStep(fraction, mask, power);
                 int n = 0;
-                for (;;) {
+                for (; ; ) {
                     if (check) {
                         if (n >= occupiedCount) Kit.codeBug();
                         ++n;
@@ -359,9 +362,7 @@
                     if (test == null) {
                         break;
                     }
-                    if (test == key
-                        || (values[N + index] == hash && test.equals(key)))
-                    {
+                    if (test == key || (values[N + index] == hash && test.equals(key))) {
                         return index;
                     }
                     if (test == DELETED && firstDeleted < 0) {
@@ -371,12 +372,10 @@
             }
         }
         // Inserting of new key
-        if (check && keys != null && keys[index] != null)
-            Kit.codeBug();
+        if (check && keys != null && keys[index] != null) Kit.codeBug();
         if (firstDeleted >= 0) {
             index = firstDeleted;
-        }
-        else {
+        } else {
             // Need to consume empty entry: check occupation level
             if (keys == null || occupiedCount * 4 >= (1 << power) * 3) {
                 // Too litle unused entries: rehash
@@ -391,9 +390,7 @@
         return index;
     }
 
-    private void writeObject(ObjectOutputStream out)
-        throws IOException
-    {
+    private void writeObject(ObjectOutputStream out) throws IOException {
         out.defaultWriteObject();
 
         int count = keyCount;
@@ -407,9 +404,7 @@
         }
     }
 
-    private void readObject(ObjectInputStream in)
-        throws IOException, ClassNotFoundException
-    {
+    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
         in.defaultReadObject();
 
         int writtenKeyCount = keyCount;
@@ -427,16 +422,16 @@
         }
     }
 
-// A == golden_ratio * (1 << 32) = ((sqrt(5) - 1) / 2) * (1 << 32)
-// See Knuth etc.
+    // A == golden_ratio * (1 << 32) = ((sqrt(5) - 1) / 2) * (1 << 32)
+    // See Knuth etc.
     private static final int A = 0x9e3779b9;
 
     private static final Object DELETED = new Object();
 
-// Structure of kyes and values arrays (N == 1 << power):
-// keys[0 <= i < N]: key value or null or DELETED mark
-// values[0 <= i < N]: value of key at keys[i]
-// values[N <= i < 2*N]: hash code of key at keys[i-N]
+    // Structure of kyes and values arrays (N == 1 << power):
+    // keys[0 <= i < N]: key value or null or DELETED mark
+    // values[0 <= i < N]: value of key at keys[i]
+    // values[N <= i < 2*N]: hash code of key at keys[i-N]
 
     private transient Object[] keys;
     private transient int[] values;
@@ -445,221 +440,221 @@
     private int keyCount;
     private transient int occupiedCount; // == keyCount + deleted_count
 
-// If true, enables consitency checks
+    // If true, enables consitency checks
     private static final boolean check = false;
 
-/* TEST START
+    /* TEST START
 
-    public static void main(String[] args) {
-        if (!check) {
-            System.err.println("Set check to true and re-run");
-            throw new RuntimeException("Set check to true and re-run");
-        }
-
-        ObjToIntMap map;
-        map = new ObjToIntMap(0);
-        testHash(map, 3);
-        map = new ObjToIntMap(0);
-        testHash(map, 10 * 1000);
-        map = new ObjToIntMap();
-        testHash(map, 10 * 1000);
-        map = new ObjToIntMap(30 * 1000);
-        testHash(map, 10 * 100);
-        map.clear();
-        testHash(map, 4);
-        map = new ObjToIntMap(0);
-        testHash(map, 10 * 100);
-    }
-
-    private static void testHash(ObjToIntMap map, int N) {
-        System.out.print("."); System.out.flush();
-        for (int i = 0; i != N; ++i) {
-            Object key = testKey(i);
-            check(-1 == map.get(key, -1));
-            map.put(key, i);
-            check(i == map.get(key, -1));
-        }
-
-        System.out.print("."); System.out.flush();
-        for (int i = 0; i != N; ++i) {
-            Object key = testKey(i);
-            map.put(key, i);
-            check(i == map.get(key, -1));
-        }
-
-        check(map.size() == N);
-
-        System.out.print("."); System.out.flush();
-        Object[] keys = map.getKeys();
-        check(keys.length == N);
-        for (int i = 0; i != N; ++i) {
-            Object key = keys[i];
-            check(map.has(key));
-        }
+        public static void main(String[] args) {
+            if (!check) {
+                System.err.println("Set check to true and re-run");
+                throw new RuntimeException("Set check to true and re-run");
+            }
+
+            ObjToIntMap map;
+            map = new ObjToIntMap(0);
+            testHash(map, 3);
+            map = new ObjToIntMap(0);
+            testHash(map, 10 * 1000);
+            map = new ObjToIntMap();
+            testHash(map, 10 * 1000);
+            map = new ObjToIntMap(30 * 1000);
+            testHash(map, 10 * 100);
+            map.clear();
+            testHash(map, 4);
+            map = new ObjToIntMap(0);
+            testHash(map, 10 * 100);
+        }
+
+        private static void testHash(ObjToIntMap map, int N) {
+            System.out.print("."); System.out.flush();
+            for (int i = 0; i != N; ++i) {
+                Object key = testKey(i);
+                check(-1 == map.get(key, -1));
+                map.put(key, i);
+                check(i == map.get(key, -1));
+            }
 
+            System.out.print("."); System.out.flush();
+            for (int i = 0; i != N; ++i) {
+                Object key = testKey(i);
+                map.put(key, i);
+                check(i == map.get(key, -1));
+            }
 
-        System.out.print("."); System.out.flush();
-        for (int i = 0; i != N; ++i) {
-            Object key = testKey(i);
-            check(i == map.get(key, -1));
-        }
+            check(map.size() == N);
 
-        int Nsqrt = -1;
-        for (int i = 0; ; ++i) {
-            if (i * i >= N) {
-                Nsqrt = i;
-                break;
+            System.out.print("."); System.out.flush();
+            Object[] keys = map.getKeys();
+            check(keys.length == N);
+            for (int i = 0; i != N; ++i) {
+                Object key = keys[i];
+                check(map.has(key));
             }
-        }
 
-        System.out.print("."); System.out.flush();
-        for (int i = 0; i != N; ++i) {
-            Object key = testKey(i * i);
-            map.put(key, i);
-            check(i == map.get(key, -1));
-        }
 
-        check(map.size() == 2 * N - Nsqrt);
+            System.out.print("."); System.out.flush();
+            for (int i = 0; i != N; ++i) {
+                Object key = testKey(i);
+                check(i == map.get(key, -1));
+            }
 
-        System.out.print("."); System.out.flush();
-        for (int i = 0; i != N; ++i) {
-            Object key = testKey(i * i);
-            check(i == map.get(key, -1));
-        }
+            int Nsqrt = -1;
+            for (int i = 0; ; ++i) {
+                if (i * i >= N) {
+                    Nsqrt = i;
+                    break;
+                }
+            }
 
-        System.out.print("."); System.out.flush();
-        for (int i = 0; i != N; ++i) {
-            Object key = testKey(-1 - i * i);
-            map.put(key, i);
-            check(i == map.get(key, -1));
-        }
+            System.out.print("."); System.out.flush();
+            for (int i = 0; i != N; ++i) {
+                Object key = testKey(i * i);
+                map.put(key, i);
+                check(i == map.get(key, -1));
+            }
 
-        check(map.size() == 3 * N - Nsqrt);
+            check(map.size() == 2 * N - Nsqrt);
 
-        System.out.print("."); System.out.flush();
-        for (int i = 0; i != N; ++i) {
-            Object key = testKey(-1 - i * i);
-            map.remove(key);
-            check(!map.has(key));
-        }
+            System.out.print("."); System.out.flush();
+            for (int i = 0; i != N; ++i) {
+                Object key = testKey(i * i);
+                check(i == map.get(key, -1));
+            }
 
-        check(map.size() == 2 * N - Nsqrt);
+            System.out.print("."); System.out.flush();
+            for (int i = 0; i != N; ++i) {
+                Object key = testKey(-1 - i * i);
+                map.put(key, i);
+                check(i == map.get(key, -1));
+            }
 
-        System.out.print("."); System.out.flush();
-        for (int i = 0; i != N; ++i) {
-            Object key = testKey(i * i);
-            check(i == map.get(key, -1));
-        }
+            check(map.size() == 3 * N - Nsqrt);
+
+            System.out.print("."); System.out.flush();
+            for (int i = 0; i != N; ++i) {
+                Object key = testKey(-1 - i * i);
+                map.remove(key);
+                check(!map.has(key));
+            }
 
-        System.out.print("."); System.out.flush();
-        for (int i = 0; i != N; ++i) {
-            Object key = testKey(i);
-            int j = intSqrt(i);
-            if (j * j == i) {
-                check(j == map.get(key, -1));
-            }else {
+            check(map.size() == 2 * N - Nsqrt);
+
+            System.out.print("."); System.out.flush();
+            for (int i = 0; i != N; ++i) {
+                Object key = testKey(i * i);
                 check(i == map.get(key, -1));
             }
-        }
 
-        System.out.print("."); System.out.flush();
-        for (int i = 0; i != N; ++i) {
-            Object key = testKey(i * i);
-            map.remove(key);
-            check(-2 == map.get(key, -2));
-        }
+            System.out.print("."); System.out.flush();
+            for (int i = 0; i != N; ++i) {
+                Object key = testKey(i);
+                int j = intSqrt(i);
+                if (j * j == i) {
+                    check(j == map.get(key, -1));
+                }else {
+                    check(i == map.get(key, -1));
+                }
+            }
 
-        System.out.print("."); System.out.flush();
-        for (int i = 0; i != N; ++i) {
-            Object key = testKey(i);
-            map.put(key, i);
-            check(i == map.get(key, -2));
-        }
+            System.out.print("."); System.out.flush();
+            for (int i = 0; i != N; ++i) {
+                Object key = testKey(i * i);
+                map.remove(key);
+                check(-2 == map.get(key, -2));
+            }
 
-        check(map.size() == N);
+            System.out.print("."); System.out.flush();
+            for (int i = 0; i != N; ++i) {
+                Object key = testKey(i);
+                map.put(key, i);
+                check(i == map.get(key, -2));
+            }
 
-        System.out.print("."); System.out.flush();
-        for (int i = 0; i != N; ++i) {
-            Object key = testKey(i);
-            check(i == map.get(key, -1));
-        }
+            check(map.size() == N);
 
-        System.out.print("."); System.out.flush();
-        ObjToIntMap copy = (ObjToIntMap)writeAndRead(map);
-        check(copy.size() == N);
+            System.out.print("."); System.out.flush();
+            for (int i = 0; i != N; ++i) {
+                Object key = testKey(i);
+                check(i == map.get(key, -1));
+            }
 
-        for (int i = 0; i != N; ++i) {
-            Object key = testKey(i);
-            check(i == copy.get(key, -1));
-        }
+            System.out.print("."); System.out.flush();
+            ObjToIntMap copy = (ObjToIntMap)writeAndRead(map);
+            check(copy.size() == N);
 
-        System.out.print("."); System.out.flush();
-        checkSameMaps(copy, map);
+            for (int i = 0; i != N; ++i) {
+                Object key = testKey(i);
+                check(i == copy.get(key, -1));
+            }
 
-        System.out.println(); System.out.flush();
-    }
+            System.out.print("."); System.out.flush();
+            checkSameMaps(copy, map);
 
-    private static void checkSameMaps(ObjToIntMap map1, ObjToIntMap map2) {
-        check(map1.size() == map2.size());
-        Object[] keys = map1.getKeys();
-        check(keys.length == map1.size());
-        for (int i = 0; i != keys.length; ++i) {
-            check(map1.get(keys[i], -1) == map2.get(keys[i], -1));
+            System.out.println(); System.out.flush();
         }
-    }
 
-    private static void check(boolean condition) {
-        if (!condition) Kit.codeBug();
-    }
+        private static void checkSameMaps(ObjToIntMap map1, ObjToIntMap map2) {
+            check(map1.size() == map2.size());
+            Object[] keys = map1.getKeys();
+            check(keys.length == map1.size());
+            for (int i = 0; i != keys.length; ++i) {
+                check(map1.get(keys[i], -1) == map2.get(keys[i], -1));
+            }
+        }
+
+        private static void check(boolean condition) {
+            if (!condition) Kit.codeBug();
+        }
 
-    private static Object[] testPool;
+        private static Object[] testPool;
 
-    private static Object testKey(int i) {
-        int MAX_POOL = 100;
-        if (0 <= i && i < MAX_POOL) {
-            if (testPool != null && testPool[i] != null) {
-                return testPool[i];
+        private static Object testKey(int i) {
+            int MAX_POOL = 100;
+            if (0 <= i && i < MAX_POOL) {
+                if (testPool != null && testPool[i] != null) {
+                    return testPool[i];
+                }
             }
-        }
-        Object x = new Double(i + 0.5);
-        if (0 <= i && i < MAX_POOL) {
-            if (testPool == null) {
-                testPool = new Object[MAX_POOL];
+            Object x = Double.valueOf(i + 0.5);
+            if (0 <= i && i < MAX_POOL) {
+                if (testPool == null) {
+                    testPool = new Object[MAX_POOL];
+                }
+                testPool[i] = x;
             }
-            testPool[i] = x;
+            return x;
         }
-        return x;
-    }
 
-    private static int intSqrt(int i) {
-        int approx = (int)Math.sqrt(i) + 1;
-        while (approx * approx > i) {
-            --approx;
+        private static int intSqrt(int i) {
+            int approx = (int)Math.sqrt(i) + 1;
+            while (approx * approx > i) {
+                --approx;
+            }
+            return approx;
         }
-        return approx;
-    }
 
-    private static Object writeAndRead(Object obj) {
-        try {
-            java.io.ByteArrayOutputStream
-                bos = new java.io.ByteArrayOutputStream();
-            java.io.ObjectOutputStream
-                out = new java.io.ObjectOutputStream(bos);
-            out.writeObject(obj);
-            out.close();
-            byte[] data = bos.toByteArray();
-            java.io.ByteArrayInputStream
-                bis = new java.io.ByteArrayInputStream(data);
-            java.io.ObjectInputStream
-                in = new java.io.ObjectInputStream(bis);
-            Object result = in.readObject();
-            in.close();
-            return result;
-        }catch (Exception ex) {
-            throw new RuntimeException("Unexpected");
+        private static Object writeAndRead(Object obj) {
+            try {
+                java.io.ByteArrayOutputStream
+                    bos = new java.io.ByteArrayOutputStream();
+                java.io.ObjectOutputStream
+                    out = new java.io.ObjectOutputStream(bos);
+                out.writeObject(obj);
+                out.close();
+                byte[] data = bos.toByteArray();
+                java.io.ByteArrayInputStream
+                    bis = new java.io.ByteArrayInputStream(data);
+                java.io.ObjectInputStream
+                    in = new java.io.ObjectInputStream(bis);
+                Object result = in.readObject();
+                in.close();
+                return result;
+            }catch (Exception ex) {
+                throw new RuntimeException("Unexpected");
+            }
         }
-    }
 
-// TEST END */
+    // TEST END */
 
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/optimizer/Block.java rhino-1.7.14/src/org/mozilla/javascript/optimizer/Block.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/optimizer/Block.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/optimizer/Block.java	2022-01-06 22:57:21.000000000 +0100
@@ -4,42 +4,50 @@
 
 package org.mozilla.javascript.optimizer;
 
-import org.mozilla.javascript.*;
-import org.mozilla.javascript.ast.Jump;
-
+import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.util.BitSet;
 import java.util.HashMap;
 import java.util.Map;
+import org.mozilla.javascript.Node;
+import org.mozilla.javascript.ObjArray;
+import org.mozilla.javascript.ObjToIntMap;
+import org.mozilla.javascript.Token;
+import org.mozilla.javascript.ast.Jump;
 
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
-class Block
-{
+class Block {
 
-    private static class FatBlock
-    {
+    private static class FatBlock {
 
-        private static Block[] reduceToArray(ObjToIntMap map)
-        {
+        private static Block[] reduceToArray(ObjToIntMap map) {
             Block[] result = null;
             if (!map.isEmpty()) {
                 result = new Block[map.size()];
                 int i = 0;
                 ObjToIntMap.Iterator iter = map.newIterator();
                 for (iter.start(); !iter.done(); iter.next()) {
-                    FatBlock fb = (FatBlock)(iter.getKey());
+                    FatBlock fb = (FatBlock) (iter.getKey());
                     result[i++] = fb.realBlock;
                 }
             }
             return result;
         }
 
-        void addSuccessor(FatBlock b)  { successors.put(b, 0); }
-        void addPredecessor(FatBlock b)  { predecessors.put(b, 0); }
+        void addSuccessor(FatBlock b) {
+            successors.put(b, 0);
+        }
 
-        Block[] getSuccessors() { return reduceToArray(successors); }
-        Block[] getPredecessors() { return reduceToArray(predecessors); }
+        void addPredecessor(FatBlock b) {
+            predecessors.put(b, 0);
+        }
+
+        Block[] getSuccessors() {
+            return reduceToArray(successors);
+        }
+
+        Block[] getPredecessors() {
+            return reduceToArray(predecessors);
+        }
 
         // all the Blocks that come immediately after this
         private ObjToIntMap successors = new ObjToIntMap();
@@ -49,14 +57,12 @@
         Block realBlock;
     }
 
-    Block(int startNodeIndex, int endNodeIndex)
-    {
+    Block(int startNodeIndex, int endNodeIndex) {
         itsStartNodeIndex = startNodeIndex;
         itsEndNodeIndex = endNodeIndex;
     }
 
-    static void runFlowAnalyzes(OptFunctionNode fn, Node[] statementNodes)
-    {
+    static void runFlowAnalyzes(OptFunctionNode fn, Node[] statementNodes) {
         int paramCount = fn.fnode.getParamCount();
         int varCount = fn.fnode.getParamAndVarCount();
         int[] varTypes = new int[varCount];
@@ -74,7 +80,12 @@
 
         if (DEBUG) {
             ++debug_blockCount;
-            System.out.println("-------------------"+fn.fnode.getFunctionName()+"  "+debug_blockCount+"--------");
+            System.out.println(
+                    "-------------------"
+                            + fn.fnode.getFunctionName()
+                            + "  "
+                            + debug_blockCount
+                            + "--------");
             System.out.println(fn.fnode.toStringTree(fn.fnode));
             System.out.println(toString(theBlocks, statementNodes));
         }
@@ -89,7 +100,7 @@
             }
             System.out.println("Variable Table, size = " + varCount);
             for (int i = 0; i != varCount; i++) {
-                System.out.println("["+i+"] type: "+varTypes[i]);
+                System.out.println("[" + i + "] type: " + varTypes[i]);
             }
         }
 
@@ -98,13 +109,11 @@
                 fn.setIsNumberVar(i);
             }
         }
-
     }
 
-    private static Block[] buildBlocks(Node[] statementNodes)
-    {
+    private static Block[] buildBlocks(Node[] statementNodes) {
         // a mapping from each target node to the block it begins
-        Map<Node,FatBlock> theTargetBlocks = new HashMap<Node,FatBlock>();
+        Map<Node, FatBlock> theTargetBlocks = new HashMap<Node, FatBlock>();
         ObjArray theBlocks = new ObjArray();
 
         // there's a block that starts at index 0
@@ -112,32 +121,32 @@
 
         for (int i = 0; i < statementNodes.length; i++) {
             switch (statementNodes[i].getType()) {
-                case Token.TARGET :
-                {
-                    if (i != beginNodeIndex) {
-                        FatBlock fb = newFatBlock(beginNodeIndex, i - 1);
+                case Token.TARGET:
+                    {
+                        if (i != beginNodeIndex) {
+                            FatBlock fb = newFatBlock(beginNodeIndex, i - 1);
+                            if (statementNodes[beginNodeIndex].getType() == Token.TARGET) {
+                                theTargetBlocks.put(statementNodes[beginNodeIndex], fb);
+                            }
+                            theBlocks.add(fb);
+                            // start the next block at this node
+                            beginNodeIndex = i;
+                        }
+                    }
+                    break;
+                case Token.IFNE:
+                case Token.IFEQ:
+                case Token.GOTO:
+                    {
+                        FatBlock fb = newFatBlock(beginNodeIndex, i);
                         if (statementNodes[beginNodeIndex].getType() == Token.TARGET) {
                             theTargetBlocks.put(statementNodes[beginNodeIndex], fb);
                         }
                         theBlocks.add(fb);
-                        // start the next block at this node
-                        beginNodeIndex = i;
+                        // start the next block at the next node
+                        beginNodeIndex = i + 1;
                     }
-                }
-                break;
-                case Token.IFNE :
-                case Token.IFEQ :
-                case Token.GOTO :
-                {
-                    FatBlock fb = newFatBlock(beginNodeIndex, i);
-                    if (statementNodes[beginNodeIndex].getType() == Token.TARGET) {
-                        theTargetBlocks.put(statementNodes[beginNodeIndex], fb);
-                    }
-                    theBlocks.add(fb);
-                    // start the next block at the next node
-                    beginNodeIndex = i + 1;
-                }
-                break;
+                    break;
             }
         }
 
@@ -152,22 +161,21 @@
         // build successor and predecessor links
 
         for (int i = 0; i < theBlocks.size(); i++) {
-            FatBlock fb = (FatBlock)(theBlocks.get(i));
+            FatBlock fb = (FatBlock) (theBlocks.get(i));
 
             Node blockEndNode = statementNodes[fb.realBlock.itsEndNodeIndex];
             int blockEndNodeType = blockEndNode.getType();
 
             if ((blockEndNodeType != Token.GOTO) && (i < (theBlocks.size() - 1))) {
-                FatBlock fallThruTarget = (FatBlock)(theBlocks.get(i + 1));
+                FatBlock fallThruTarget = (FatBlock) (theBlocks.get(i + 1));
                 fb.addSuccessor(fallThruTarget);
                 fallThruTarget.addPredecessor(fb);
             }
 
-
-            if ( (blockEndNodeType == Token.IFNE)
+            if ((blockEndNodeType == Token.IFNE)
                     || (blockEndNodeType == Token.IFEQ)
-                    || (blockEndNodeType == Token.GOTO) ) {
-                Node target = ((Jump)blockEndNode).target;
+                    || (blockEndNodeType == Token.GOTO)) {
+                Node target = ((Jump) blockEndNode).target;
                 FatBlock branchTargetBlock = theTargetBlocks.get(target);
                 target.putProp(Node.TARGETBLOCK_PROP, branchTargetBlock.realBlock);
                 fb.addSuccessor(branchTargetBlock);
@@ -178,7 +186,7 @@
         Block[] result = new Block[theBlocks.size()];
 
         for (int i = 0; i < theBlocks.size(); i++) {
-            FatBlock fb = (FatBlock)(theBlocks.get(i));
+            FatBlock fb = (FatBlock) (theBlocks.get(i));
             Block b = fb.realBlock;
             b.itsSuccessors = fb.getSuccessors();
             b.itsPredecessors = fb.getPredecessors();
@@ -189,15 +197,13 @@
         return result;
     }
 
-    private static FatBlock newFatBlock(int startNodeIndex, int endNodeIndex)
-    {
+    private static FatBlock newFatBlock(int startNodeIndex, int endNodeIndex) {
         FatBlock fb = new FatBlock();
         fb.realBlock = new Block(startNodeIndex, endNodeIndex);
         return fb;
     }
 
-    private static String toString(Block[] blockList, Node[] statementNodes)
-    {
+    private static String toString(Block[] blockList, Node[] statementNodes) {
         if (!DEBUG) return null;
 
         StringWriter sw = new StringWriter();
@@ -207,12 +213,16 @@
         for (int i = 0; i < blockList.length; i++) {
             Block b = blockList[i];
             pw.println("#" + b.itsBlockID);
-            pw.println("from " + b.itsStartNodeIndex
-                    + " "
-                    + statementNodes[b.itsStartNodeIndex].toString());
-            pw.println("thru " + b.itsEndNodeIndex
-                    + " "
-                    + statementNodes[b.itsEndNodeIndex].toString());
+            pw.println(
+                    "from "
+                            + b.itsStartNodeIndex
+                            + " "
+                            + statementNodes[b.itsStartNodeIndex].toString());
+            pw.println(
+                    "thru "
+                            + b.itsEndNodeIndex
+                            + " "
+                            + statementNodes[b.itsEndNodeIndex].toString());
             pw.print("Predecessors ");
             if (b.itsPredecessors != null) {
                 for (int j = 0; j < b.itsPredecessors.length; j++) {
@@ -235,22 +245,21 @@
         return sw.toString();
     }
 
-    private static void reachingDefDataFlow(OptFunctionNode fn, Node[] statementNodes,
-                                            Block theBlocks[], int[] varTypes)
-    {
-/*
-    initialize the liveOnEntry and liveOnExit sets, then discover the variables
-    that are def'd by each function, and those that are used before being def'd
-    (hence liveOnEntry)
-*/
+    private static void reachingDefDataFlow(
+            OptFunctionNode fn, Node[] statementNodes, Block theBlocks[], int[] varTypes) {
+        /*
+            initialize the liveOnEntry and liveOnExit sets, then discover the variables
+            that are def'd by each function, and those that are used before being def'd
+            (hence liveOnEntry)
+        */
         for (int i = 0; i < theBlocks.length; i++) {
             theBlocks[i].initLiveOnEntrySets(fn, statementNodes);
         }
-/*
-    this visits every block starting at the last, re-adding the predecessors of
-    any block whose inputs change as a result of the dataflow.
-    REMIND, better would be to visit in CFG postorder
-*/
+        /*
+            this visits every block starting at the last, re-adding the predecessors of
+            any block whose inputs change as a result of the dataflow.
+            REMIND, better would be to visit in CFG postorder
+        */
         boolean visit[] = new boolean[theBlocks.length];
         boolean doneOnce[] = new boolean[theBlocks.length];
         int vIndex = theBlocks.length - 1;
@@ -282,18 +291,17 @@
                 vIndex--;
             }
         }
-/*
-        if any variable is live on entry to block 0, we have to mark it as
-        not jRegable - since it means that someone is trying to access the
-        'undefined'-ness of that variable.
-*/
+        /*
+                if any variable is live on entry to block 0, we have to mark it as
+                not jRegable - since it means that someone is trying to access the
+                'undefined'-ness of that variable.
+        */
 
         theBlocks[0].markAnyTypeVariables(varTypes);
     }
 
-    private static void typeFlow(OptFunctionNode fn, Node[] statementNodes,
-                                 Block theBlocks[], int[] varTypes)
-    {
+    private static void typeFlow(
+            OptFunctionNode fn, Node[] statementNodes, Block theBlocks[], int[] varTypes) {
         boolean visit[] = new boolean[theBlocks.length];
         boolean doneOnce[] = new boolean[theBlocks.length];
         int vIndex = 0;
@@ -303,8 +311,7 @@
             if (visit[vIndex] || !doneOnce[vIndex]) {
                 doneOnce[vIndex] = true;
                 visit[vIndex] = false;
-                if (theBlocks[vIndex].doTypeFlow(fn, statementNodes, varTypes))
-                {
+                if (theBlocks[vIndex].doTypeFlow(fn, statementNodes, varTypes)) {
                     Block succ[] = theBlocks[vIndex].itsSuccessors;
                     if (succ != null) {
                         for (int i = 0; i < succ.length; i++) {
@@ -328,20 +335,17 @@
         }
     }
 
-    private static boolean assignType(int[] varTypes, int index, int type)
-    {
+    private static boolean assignType(int[] varTypes, int index, int type) {
         int prev = varTypes[index];
         return prev != (varTypes[index] |= type);
     }
 
-    private void markAnyTypeVariables(int[] varTypes)
-    {
+    private void markAnyTypeVariables(int[] varTypes) {
         for (int i = 0; i != varTypes.length; i++) {
             if (itsLiveOnEntrySet.get(i)) {
                 assignType(varTypes, i, Optimizer.AnyType);
             }
         }
-
     }
 
     /*
@@ -352,49 +356,46 @@
         The itsNotDefSet is built reversed then flipped later.
 
     */
-    private void lookForVariableAccess(OptFunctionNode fn, Node n)
-    {
+    private void lookForVariableAccess(OptFunctionNode fn, Node n) {
         switch (n.getType()) {
             case Token.TYPEOFNAME:
-            {
-                // TYPEOFNAME may be used with undefined names, which is why
-                // this is handled separately from GETVAR above.
-                int varIndex = fn.fnode.getIndexForNameNode(n);
-                if (varIndex > -1 && !itsNotDefSet.get(varIndex))
-                    itsUseBeforeDefSet.set(varIndex);
-            }
-            break;
-            case Token.DEC :
-            case Token.INC :
-            {
-                Node child = n.getFirstChild();
-                if (child.getType() == Token.GETVAR) {
-                    int varIndex = fn.getVarIndex(child);
-                    if (!itsNotDefSet.get(varIndex))
+                {
+                    // TYPEOFNAME may be used with undefined names, which is why
+                    // this is handled separately from GETVAR above.
+                    int varIndex = fn.fnode.getIndexForNameNode(n);
+                    if (varIndex > -1 && !itsNotDefSet.get(varIndex))
                         itsUseBeforeDefSet.set(varIndex);
-                    itsNotDefSet.set(varIndex);
-                } else {
-                    lookForVariableAccess(fn, child);
                 }
-            }
-            break;
-            case Token.SETVAR :
-            case Token.SETCONSTVAR :
-            {
-                Node lhs = n.getFirstChild();
-                Node rhs = lhs.getNext();
-                lookForVariableAccess(fn, rhs);
-                itsNotDefSet.set(fn.getVarIndex(n));
-            }
-            break;
-            case Token.GETVAR :
-            {
-                int varIndex = fn.getVarIndex(n);
-                if (!itsNotDefSet.get(varIndex))
-                    itsUseBeforeDefSet.set(varIndex);
-            }
-            break;
-            default :
+                break;
+            case Token.DEC:
+            case Token.INC:
+                {
+                    Node child = n.getFirstChild();
+                    if (child.getType() == Token.GETVAR) {
+                        int varIndex = fn.getVarIndex(child);
+                        if (!itsNotDefSet.get(varIndex)) itsUseBeforeDefSet.set(varIndex);
+                        itsNotDefSet.set(varIndex);
+                    } else {
+                        lookForVariableAccess(fn, child);
+                    }
+                }
+                break;
+            case Token.SETVAR:
+            case Token.SETCONSTVAR:
+                {
+                    Node lhs = n.getFirstChild();
+                    Node rhs = lhs.getNext();
+                    lookForVariableAccess(fn, rhs);
+                    itsNotDefSet.set(fn.getVarIndex(n));
+                }
+                break;
+            case Token.GETVAR:
+                {
+                    int varIndex = fn.getVarIndex(n);
+                    if (!itsNotDefSet.get(varIndex)) itsUseBeforeDefSet.set(varIndex);
+                }
+                break;
+            default:
                 Node child = n.getFirstChild();
                 while (child != null) {
                     lookForVariableAccess(fn, child);
@@ -409,8 +410,7 @@
         Then walk the trees looking for defs/uses of variables
         and build the def and useBeforeDef sets.
     */
-    private void initLiveOnEntrySets(OptFunctionNode fn, Node[] statementNodes)
-    {
+    private void initLiveOnEntrySets(OptFunctionNode fn, Node[] statementNodes) {
         int listLength = fn.getVarCount();
         itsUseBeforeDefSet = new BitSet(listLength);
         itsNotDefSet = new BitSet(listLength);
@@ -420,7 +420,7 @@
             Node n = statementNodes[i];
             lookForVariableAccess(fn, n);
         }
-        itsNotDefSet.flip(0, listLength);         // truth in advertising
+        itsNotDefSet.flip(0, listLength); // truth in advertising
     }
 
     /*
@@ -429,20 +429,20 @@
         liveOnEntry = liveOnExit - defsInThisBlock + useBeforeDefsInThisBlock
 
     */
-    private boolean doReachedUseDataFlow()
-    {
+    private boolean doReachedUseDataFlow() {
         itsLiveOnExitSet.clear();
         if (itsSuccessors != null) {
             for (int i = 0; i < itsSuccessors.length; i++) {
                 itsLiveOnExitSet.or(itsSuccessors[i].itsLiveOnEntrySet);
             }
         }
-        return updateEntrySet(itsLiveOnEntrySet, itsLiveOnExitSet,
-                              itsUseBeforeDefSet, itsNotDefSet);
+        return updateEntrySet(
+                itsLiveOnEntrySet, itsLiveOnExitSet,
+                itsUseBeforeDefSet, itsNotDefSet);
     }
 
-    private boolean updateEntrySet(BitSet entrySet, BitSet exitSet,
-                                   BitSet useBeforeDef, BitSet notDef) {
+    private static boolean updateEntrySet(
+            BitSet entrySet, BitSet exitSet, BitSet useBeforeDef, BitSet notDef) {
         int card = entrySet.cardinality();
         entrySet.or(exitSet);
         entrySet.and(notDef);
@@ -456,9 +456,7 @@
             Literals,
             Arithmetic operations - always return a Number
     */
-    private static int findExpressionType(OptFunctionNode fn, Node n,
-                                          int[] varTypes)
-    {
+    private static int findExpressionType(OptFunctionNode fn, Node n, int[] varTypes) {
         switch (n.getType()) {
             case Token.NUMBER:
                 return Optimizer.NumberType;
@@ -482,6 +480,7 @@
             case Token.MUL:
             case Token.DIV:
             case Token.MOD:
+            case Token.EXP:
             case Token.BITOR:
             case Token.BITXOR:
             case Token.BITAND:
@@ -527,25 +526,29 @@
             case Token.ARRAYCOMP:
             case Token.ARRAYLIT:
             case Token.OBJECTLIT:
+            case Token.TEMPLATE_LITERAL:
+            case Token.BIGINT:
                 return Optimizer.AnyType; // XXX: actually, we know it's not
-            // number, but no type yet for that
+                // number, but no type yet for that
 
-            case Token.ADD: {
-                // if the lhs & rhs are known to be numbers, we can be sure that's
-                // the result, otherwise it could be a string.
-                Node child = n.getFirstChild();
-                int lType = findExpressionType(fn, child, varTypes);
-                int rType = findExpressionType(fn, child.getNext(), varTypes);
-                return lType | rType;    // we're not distinguishing strings yet
-            }
+            case Token.ADD:
+                {
+                    // if the lhs & rhs are known to be numbers, we can be sure that's
+                    // the result, otherwise it could be a string.
+                    Node child = n.getFirstChild();
+                    int lType = findExpressionType(fn, child, varTypes);
+                    int rType = findExpressionType(fn, child.getNext(), varTypes);
+                    return lType | rType; // we're not distinguishing strings yet
+                }
 
-            case Token.HOOK: {
-                Node ifTrue = n.getFirstChild().getNext();
-                Node ifFalse = ifTrue.getNext();
-                int ifTrueType = findExpressionType(fn, ifTrue, varTypes);
-                int ifFalseType = findExpressionType(fn, ifFalse, varTypes);
-                return ifTrueType | ifFalseType;
-            }
+            case Token.HOOK:
+                {
+                    Node ifTrue = n.getFirstChild().getNext();
+                    Node ifFalse = ifTrue.getNext();
+                    int ifTrueType = findExpressionType(fn, ifTrue, varTypes);
+                    int ifFalseType = findExpressionType(fn, ifFalse, varTypes);
+                    return ifTrueType | ifFalseType;
+                }
 
             case Token.COMMA:
             case Token.SETVAR:
@@ -556,28 +559,27 @@
                 return findExpressionType(fn, n.getLastChild(), varTypes);
 
             case Token.AND:
-            case Token.OR: {
-                Node child = n.getFirstChild();
-                int lType = findExpressionType(fn, child, varTypes);
-                int rType = findExpressionType(fn, child.getNext(), varTypes);
-                return lType | rType;
-            }
+            case Token.OR:
+                {
+                    Node child = n.getFirstChild();
+                    int lType = findExpressionType(fn, child, varTypes);
+                    int rType = findExpressionType(fn, child.getNext(), varTypes);
+                    return lType | rType;
+                }
         }
 
         return Optimizer.AnyType;
     }
 
-    private static boolean findDefPoints(OptFunctionNode fn, Node n,
-                                         int[] varTypes)
-    {
+    private static boolean findDefPoints(OptFunctionNode fn, Node n, int[] varTypes) {
         boolean result = false;
         Node first = n.getFirstChild();
         for (Node next = first; next != null; next = next.getNext()) {
             result |= findDefPoints(fn, next, varTypes);
         }
         switch (n.getType()) {
-            case Token.DEC :
-            case Token.INC :
+            case Token.DEC:
+            case Token.INC:
                 if (first.getType() == Token.GETVAR) {
                     // theVar is a Number now
                     int i = fn.getVarIndex(first);
@@ -586,24 +588,22 @@
                     }
                 }
                 break;
-            case Token.SETVAR :
-            case Token.SETCONSTVAR : {
-                Node rValue = first.getNext();
-                int theType = findExpressionType(fn, rValue, varTypes);
-                int i = fn.getVarIndex(n);
-                if (!(n.getType() == Token.SETVAR
-                        && fn.fnode.getParamAndVarConst()[i])) {
-                    result |= assignType(varTypes, i, theType);
+            case Token.SETVAR:
+            case Token.SETCONSTVAR:
+                {
+                    Node rValue = first.getNext();
+                    int theType = findExpressionType(fn, rValue, varTypes);
+                    int i = fn.getVarIndex(n);
+                    if (!(n.getType() == Token.SETVAR && fn.fnode.getParamAndVarConst()[i])) {
+                        result |= assignType(varTypes, i, theType);
+                    }
+                    break;
                 }
-                break;
-            }
         }
         return result;
     }
 
-    private boolean doTypeFlow(OptFunctionNode fn, Node[] statementNodes,
-                               int[] varTypes)
-    {
+    private boolean doTypeFlow(OptFunctionNode fn, Node[] statementNodes, int[] varTypes) {
         boolean changed = false;
 
         for (int i = itsStartNodeIndex; i <= itsEndNodeIndex; i++) {
@@ -616,19 +616,14 @@
         return changed;
     }
 
-    private void printLiveOnEntrySet(OptFunctionNode fn)
-    {
+    private void printLiveOnEntrySet(OptFunctionNode fn) {
         if (DEBUG) {
             for (int i = 0; i < fn.getVarCount(); i++) {
                 String name = fn.fnode.getParamOrVarName(i);
-                if (itsUseBeforeDefSet.get(i))
-                    System.out.println(name + " is used before def'd");
-                if (itsNotDefSet.get(i))
-                    System.out.println(name + " is not def'd");
-                if (itsLiveOnEntrySet.get(i))
-                    System.out.println(name + " is live on entry");
-                if (itsLiveOnExitSet.get(i))
-                    System.out.println(name + " is live on exit");
+                if (itsUseBeforeDefSet.get(i)) System.out.println(name + " is used before def'd");
+                if (itsNotDefSet.get(i)) System.out.println(name + " is not def'd");
+                if (itsLiveOnEntrySet.get(i)) System.out.println(name + " is live on entry");
+                if (itsLiveOnExitSet.get(i)) System.out.println(name + " is live on exit");
             }
         }
     }
@@ -638,10 +633,10 @@
     // all the Blocks that come immediately before this
     private Block[] itsPredecessors;
 
-    private int itsStartNodeIndex;       // the Node at the start of the block
-    private int itsEndNodeIndex;         // the Node at the end of the block
+    private int itsStartNodeIndex; // the Node at the start of the block
+    private int itsEndNodeIndex; // the Node at the end of the block
 
-    private int itsBlockID;               // a unique index for each block
+    private int itsBlockID; // a unique index for each block
 
     // reaching def bit sets -
     private BitSet itsLiveOnEntrySet;
@@ -651,6 +646,4 @@
 
     static final boolean DEBUG = false;
     private static int debug_blockCount;
-
 }
-
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/optimizer/BodyCodegen.java rhino-1.7.14/src/org/mozilla/javascript/optimizer/BodyCodegen.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/optimizer/BodyCodegen.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/optimizer/BodyCodegen.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,4391 @@
+package org.mozilla.javascript.optimizer;
+
+import static org.mozilla.classfile.ClassFileWriter.ACC_PRIVATE;
+import static org.mozilla.classfile.ClassFileWriter.ACC_STATIC;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import org.mozilla.classfile.ByteCode;
+import org.mozilla.classfile.ClassFileWriter;
+import org.mozilla.javascript.CompilerEnvirons;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Kit;
+import org.mozilla.javascript.NativeGenerator;
+import org.mozilla.javascript.Node;
+import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.Token;
+import org.mozilla.javascript.ast.FunctionNode;
+import org.mozilla.javascript.ast.Jump;
+import org.mozilla.javascript.ast.ScriptNode;
+
+class BodyCodegen {
+    void generateBodyCode() {
+        isGenerator = Codegen.isGenerator(scriptOrFn);
+
+        // generate the body of the current function or script object
+        initBodyGeneration();
+
+        if (isGenerator) {
+            // All functions in the generated bytecode have a unique name. Every
+            // generator has a unique prefix followed by _gen
+            String type =
+                    "("
+                            + codegen.mainClassSignature
+                            + "Lorg/mozilla/javascript/Context;"
+                            + "Lorg/mozilla/javascript/Scriptable;"
+                            + "Ljava/lang/Object;"
+                            + "Ljava/lang/Object;I)Ljava/lang/Object;";
+            cfw.startMethod(
+                    codegen.getBodyMethodName(scriptOrFn) + "_gen",
+                    type,
+                    (short) (ACC_STATIC | ACC_PRIVATE));
+        } else {
+            cfw.startMethod(
+                    codegen.getBodyMethodName(scriptOrFn),
+                    codegen.getBodyMethodSignature(scriptOrFn),
+                    (short) (ACC_STATIC | ACC_PRIVATE));
+        }
+
+        generatePrologue();
+        Node treeTop;
+        if (fnCurrent != null) {
+            treeTop = scriptOrFn.getLastChild();
+        } else {
+            treeTop = scriptOrFn;
+        }
+        generateStatement(treeTop);
+        generateEpilogue();
+
+        cfw.stopMethod((short) (localsMax + 1));
+
+        if (isGenerator) {
+            // generate the user visible method which when invoked will
+            // return a generator object
+            generateGenerator();
+        }
+
+        if (literals != null) {
+            // literals list may grow while we're looping
+            for (int i = 0; i < literals.size(); i++) {
+                Node node = literals.get(i);
+                int type = node.getType();
+                switch (type) {
+                    case Token.OBJECTLIT:
+                        generateObjectLiteralFactory(node, i + 1);
+                        break;
+                    case Token.ARRAYLIT:
+                        generateArrayLiteralFactory(node, i + 1);
+                        break;
+                    default:
+                        Kit.codeBug(Token.typeToName(type));
+                }
+            }
+        }
+    }
+
+    // This creates a the user-facing function that returns a NativeGenerator
+    // object.
+    private void generateGenerator() {
+        cfw.startMethod(
+                codegen.getBodyMethodName(scriptOrFn),
+                codegen.getBodyMethodSignature(scriptOrFn),
+                (short) (ACC_STATIC | ACC_PRIVATE));
+
+        initBodyGeneration();
+        argsLocal = firstFreeLocal++;
+        localsMax = firstFreeLocal;
+
+        // get top level scope
+        if (fnCurrent != null) {
+            // Unless we're in a direct call use the enclosing scope
+            // of the function as our variable object.
+            cfw.addALoad(funObjLocal);
+            cfw.addInvoke(
+                    ByteCode.INVOKEINTERFACE,
+                    "org/mozilla/javascript/Scriptable",
+                    "getParentScope",
+                    "()Lorg/mozilla/javascript/Scriptable;");
+            cfw.addAStore(variableObjectLocal);
+        }
+
+        // generators are forced to have an activation record
+        cfw.addALoad(funObjLocal);
+        cfw.addALoad(variableObjectLocal);
+        cfw.addALoad(argsLocal);
+        cfw.addPush(scriptOrFn.isInStrictMode());
+        addScriptRuntimeInvoke(
+                "createFunctionActivation",
+                "(Lorg/mozilla/javascript/NativeFunction;"
+                        + "Lorg/mozilla/javascript/Scriptable;"
+                        + "[Ljava/lang/Object;"
+                        + "Z"
+                        + ")Lorg/mozilla/javascript/Scriptable;");
+        cfw.addAStore(variableObjectLocal);
+
+        // create a function object
+        cfw.add(ByteCode.NEW, codegen.mainClassName);
+        // Call function constructor
+        cfw.add(ByteCode.DUP);
+        cfw.addALoad(variableObjectLocal);
+        cfw.addALoad(contextLocal); // load 'cx'
+        cfw.addPush(scriptOrFnIndex);
+        cfw.addInvoke(
+                ByteCode.INVOKESPECIAL,
+                codegen.mainClassName,
+                "<init>",
+                Codegen.FUNCTION_CONSTRUCTOR_SIGNATURE);
+
+        generateNestedFunctionInits();
+
+        // create the NativeGenerator object that we return
+        cfw.addALoad(variableObjectLocal);
+        cfw.addALoad(thisObjLocal);
+        cfw.addLoadConstant(maxLocals);
+        cfw.addLoadConstant(maxStack);
+        addOptRuntimeInvoke(
+                "createNativeGenerator",
+                "(Lorg/mozilla/javascript/NativeFunction;"
+                        + "Lorg/mozilla/javascript/Scriptable;"
+                        + "Lorg/mozilla/javascript/Scriptable;II"
+                        + ")Lorg/mozilla/javascript/Scriptable;");
+
+        cfw.add(ByteCode.ARETURN);
+        cfw.stopMethod((short) (localsMax + 1));
+    }
+
+    private void generateNestedFunctionInits() {
+        int functionCount = scriptOrFn.getFunctionCount();
+        for (int i = 0; i != functionCount; i++) {
+            OptFunctionNode ofn = OptFunctionNode.get(scriptOrFn, i);
+            if (ofn.fnode.getFunctionType() == FunctionNode.FUNCTION_STATEMENT) {
+                visitFunction(ofn, FunctionNode.FUNCTION_STATEMENT);
+            }
+        }
+    }
+
+    private void initBodyGeneration() {
+        varRegisters = null;
+        if (scriptOrFn.getType() == Token.FUNCTION) {
+            fnCurrent = OptFunctionNode.get(scriptOrFn);
+            hasVarsInRegs = !fnCurrent.fnode.requiresActivation();
+            if (hasVarsInRegs) {
+                int n = fnCurrent.fnode.getParamAndVarCount();
+                if (n != 0) {
+                    varRegisters = new short[n];
+                }
+            }
+            inDirectCallFunction = fnCurrent.isTargetOfDirectCall();
+            if (inDirectCallFunction && !hasVarsInRegs) Codegen.badTree();
+        } else {
+            fnCurrent = null;
+            hasVarsInRegs = false;
+            inDirectCallFunction = false;
+        }
+
+        locals = new int[MAX_LOCALS];
+
+        funObjLocal = 0;
+        contextLocal = 1;
+        variableObjectLocal = 2;
+        thisObjLocal = 3;
+        localsMax = (short) 4; // number of parms + "this"
+        firstFreeLocal = 4;
+
+        popvLocal = -1;
+        argsLocal = -1;
+        itsZeroArgArray = -1;
+        itsOneArgArray = -1;
+        epilogueLabel = -1;
+        enterAreaStartLabel = -1;
+        generatorStateLocal = -1;
+    }
+
+    /** Generate the prologue for a function or script. */
+    private void generatePrologue() {
+        if (inDirectCallFunction) {
+            int directParameterCount = scriptOrFn.getParamCount();
+            // 0 is reserved for function Object 'this'
+            // 1 is reserved for context
+            // 2 is reserved for parentScope
+            // 3 is reserved for script 'this'
+            if (firstFreeLocal != 4) Kit.codeBug();
+            for (int i = 0; i != directParameterCount; ++i) {
+                varRegisters[i] = firstFreeLocal;
+                // 3 is 1 for Object parm and 2 for double parm
+                firstFreeLocal += 3;
+            }
+            if (!fnCurrent.getParameterNumberContext()) {
+                // make sure that all parameters are objects
+                itsForcedObjectParameters = true;
+                for (int i = 0; i != directParameterCount; ++i) {
+                    short reg = varRegisters[i];
+                    cfw.addALoad(reg);
+                    cfw.add(ByteCode.GETSTATIC, "java/lang/Void", "TYPE", "Ljava/lang/Class;");
+                    int isObjectLabel = cfw.acquireLabel();
+                    cfw.add(ByteCode.IF_ACMPNE, isObjectLabel);
+                    cfw.addDLoad(reg + 1);
+                    addDoubleWrap();
+                    cfw.addAStore(reg);
+                    cfw.markLabel(isObjectLabel);
+                }
+            }
+        }
+
+        if (fnCurrent != null) {
+            // Use the enclosing scope of the function as our variable object.
+            cfw.addALoad(funObjLocal);
+            cfw.addInvoke(
+                    ByteCode.INVOKEINTERFACE,
+                    "org/mozilla/javascript/Scriptable",
+                    "getParentScope",
+                    "()Lorg/mozilla/javascript/Scriptable;");
+            cfw.addAStore(variableObjectLocal);
+        }
+
+        // reserve 'args[]'
+        argsLocal = firstFreeLocal++;
+        localsMax = firstFreeLocal;
+
+        // Generate Generator specific prelude
+        if (isGenerator) {
+
+            // reserve 'args[]'
+            operationLocal = firstFreeLocal++;
+            localsMax = firstFreeLocal;
+
+            // Local 3 is a reference to a GeneratorState object. The rest
+            // of codegen expects local 3 to be a reference to the thisObj.
+            // So move the value in local 3 to generatorStateLocal, and load
+            // the saved thisObj from the GeneratorState object.
+            cfw.addALoad(thisObjLocal);
+            generatorStateLocal = firstFreeLocal++;
+            localsMax = firstFreeLocal;
+            cfw.add(ByteCode.CHECKCAST, OptRuntime.GeneratorState.CLASS_NAME);
+            cfw.add(ByteCode.DUP);
+            cfw.addAStore(generatorStateLocal);
+            cfw.add(
+                    ByteCode.GETFIELD,
+                    OptRuntime.GeneratorState.CLASS_NAME,
+                    OptRuntime.GeneratorState.thisObj_NAME,
+                    OptRuntime.GeneratorState.thisObj_TYPE);
+            cfw.addAStore(thisObjLocal);
+
+            if (epilogueLabel == -1) {
+                epilogueLabel = cfw.acquireLabel();
+            }
+
+            List<Node> targets = ((FunctionNode) scriptOrFn).getResumptionPoints();
+            if (targets != null) {
+                // get resumption point
+                generateGetGeneratorResumptionPoint();
+
+                // generate dispatch table
+                generatorSwitch = cfw.addTableSwitch(0, targets.size() + GENERATOR_START);
+                generateCheckForThrowOrClose(-1, false, GENERATOR_START);
+            }
+        }
+
+        // Compile RegExp and template literals if this is a script. For functions
+        // this is performed during instantiation in functionInit
+        if (fnCurrent == null) {
+            if (scriptOrFn.getRegexpCount() != 0) {
+                cfw.addALoad(contextLocal);
+                cfw.addInvoke(
+                        ByteCode.INVOKESTATIC,
+                        codegen.mainClassName,
+                        Codegen.REGEXP_INIT_METHOD_NAME,
+                        Codegen.REGEXP_INIT_METHOD_SIGNATURE);
+            }
+            if (scriptOrFn.getTemplateLiteralCount() != 0) {
+                cfw.addInvoke(
+                        ByteCode.INVOKESTATIC,
+                        codegen.mainClassName,
+                        Codegen.TEMPLATE_LITERAL_INIT_METHOD_NAME,
+                        Codegen.TEMPLATE_LITERAL_INIT_METHOD_SIGNATURE);
+            }
+        }
+
+        if (compilerEnv.isGenerateObserverCount()) saveCurrentCodeOffset();
+
+        // skip creating activation object or loading args for the body of a generator. The
+        // activation record required by a generator has already been created
+        // in generateGenerator().
+        if (isGenerator) return;
+
+        if (hasVarsInRegs) {
+            // No need to create activation. Pad arguments if need be.
+            int parmCount = scriptOrFn.getParamCount();
+            if (parmCount > 0 && !inDirectCallFunction) {
+                // Set up args array
+                // check length of arguments, pad if need be
+                cfw.addALoad(argsLocal);
+                cfw.add(ByteCode.ARRAYLENGTH);
+                cfw.addPush(parmCount);
+                int label = cfw.acquireLabel();
+                cfw.add(ByteCode.IF_ICMPGE, label);
+                cfw.addALoad(argsLocal);
+                cfw.addPush(parmCount);
+                addScriptRuntimeInvoke(
+                        "padArguments", "([Ljava/lang/Object;I" + ")[Ljava/lang/Object;");
+                cfw.addAStore(argsLocal);
+                cfw.markLabel(label);
+            }
+
+            int paramCount = fnCurrent.fnode.getParamCount();
+            int varCount = fnCurrent.fnode.getParamAndVarCount();
+            boolean[] constDeclarations = fnCurrent.fnode.getParamAndVarConst();
+
+            // REMIND - only need to initialize the vars that don't get a value
+            // before the next call and are used in the function
+            short firstUndefVar = -1;
+            for (int i = 0; i != varCount; ++i) {
+                short reg = -1;
+                if (i < paramCount) {
+                    if (!inDirectCallFunction) {
+                        reg = getNewWordLocal();
+                        cfw.addALoad(argsLocal);
+                        cfw.addPush(i);
+                        cfw.add(ByteCode.AALOAD);
+                        cfw.addAStore(reg);
+                    }
+                } else if (fnCurrent.isNumberVar(i)) {
+                    reg = getNewWordPairLocal(constDeclarations[i]);
+                    cfw.addPush(0.0);
+                    cfw.addDStore(reg);
+                } else {
+                    reg = getNewWordLocal(constDeclarations[i]);
+                    if (firstUndefVar == -1) {
+                        Codegen.pushUndefined(cfw);
+                        firstUndefVar = reg;
+                    } else {
+                        cfw.addALoad(firstUndefVar);
+                    }
+                    cfw.addAStore(reg);
+                }
+                if (reg >= 0) {
+                    if (constDeclarations[i]) {
+                        cfw.addPush(0);
+                        cfw.addIStore(reg + (fnCurrent.isNumberVar(i) ? 2 : 1));
+                    }
+                    varRegisters[i] = reg;
+                }
+
+                // Add debug table entry if we're generating debug info
+                if (compilerEnv.isGenerateDebugInfo()) {
+                    String name = fnCurrent.fnode.getParamOrVarName(i);
+                    String type = fnCurrent.isNumberVar(i) ? "D" : "Ljava/lang/Object;";
+                    int startPC = cfw.getCurrentCodeOffset();
+                    if (reg < 0) {
+                        reg = varRegisters[i];
+                    }
+                    cfw.addVariableDescriptor(name, type, startPC, reg);
+                }
+            }
+
+            // Skip creating activation object.
+            return;
+        }
+
+        String debugVariableName;
+        boolean isArrow = false;
+        if (scriptOrFn instanceof FunctionNode) {
+            isArrow = ((FunctionNode) scriptOrFn).getFunctionType() == FunctionNode.ARROW_FUNCTION;
+        }
+        if (fnCurrent != null) {
+            debugVariableName = "activation";
+            cfw.addALoad(funObjLocal);
+            cfw.addALoad(variableObjectLocal);
+            cfw.addALoad(argsLocal);
+            String methodName =
+                    isArrow ? "createArrowFunctionActivation" : "createFunctionActivation";
+            cfw.addPush(scriptOrFn.isInStrictMode());
+            addScriptRuntimeInvoke(
+                    methodName,
+                    "(Lorg/mozilla/javascript/NativeFunction;"
+                            + "Lorg/mozilla/javascript/Scriptable;"
+                            + "[Ljava/lang/Object;"
+                            + "Z"
+                            + ")Lorg/mozilla/javascript/Scriptable;");
+            cfw.addAStore(variableObjectLocal);
+            cfw.addALoad(contextLocal);
+            cfw.addALoad(variableObjectLocal);
+            addScriptRuntimeInvoke(
+                    "enterActivationFunction",
+                    "(Lorg/mozilla/javascript/Context;"
+                            + "Lorg/mozilla/javascript/Scriptable;"
+                            + ")V");
+        } else {
+            debugVariableName = "global";
+            cfw.addALoad(funObjLocal);
+            cfw.addALoad(thisObjLocal);
+            cfw.addALoad(contextLocal);
+            cfw.addALoad(variableObjectLocal);
+            cfw.addPush(0); // false to indicate it is not eval script
+            addScriptRuntimeInvoke(
+                    "initScript",
+                    "(Lorg/mozilla/javascript/NativeFunction;"
+                            + "Lorg/mozilla/javascript/Scriptable;"
+                            + "Lorg/mozilla/javascript/Context;"
+                            + "Lorg/mozilla/javascript/Scriptable;"
+                            + "Z"
+                            + ")V");
+        }
+
+        enterAreaStartLabel = cfw.acquireLabel();
+        epilogueLabel = cfw.acquireLabel();
+        cfw.markLabel(enterAreaStartLabel);
+
+        generateNestedFunctionInits();
+
+        // default is to generate debug info
+        if (compilerEnv.isGenerateDebugInfo()) {
+            cfw.addVariableDescriptor(
+                    debugVariableName,
+                    "Lorg/mozilla/javascript/Scriptable;",
+                    cfw.getCurrentCodeOffset(),
+                    variableObjectLocal);
+        }
+
+        if (fnCurrent == null) {
+            // OPT: use dataflow to prove that this assignment is dead
+            popvLocal = getNewWordLocal();
+            Codegen.pushUndefined(cfw);
+            cfw.addAStore(popvLocal);
+
+            int linenum = scriptOrFn.getEndLineno();
+            if (linenum != -1) cfw.addLineNumberEntry((short) linenum);
+
+        } else {
+            if (fnCurrent.itsContainsCalls0) {
+                itsZeroArgArray = getNewWordLocal();
+                cfw.add(
+                        ByteCode.GETSTATIC,
+                        "org/mozilla/javascript/ScriptRuntime",
+                        "emptyArgs",
+                        "[Ljava/lang/Object;");
+                cfw.addAStore(itsZeroArgArray);
+            }
+            if (fnCurrent.itsContainsCalls1) {
+                itsOneArgArray = getNewWordLocal();
+                cfw.addPush(1);
+                cfw.add(ByteCode.ANEWARRAY, "java/lang/Object");
+                cfw.addAStore(itsOneArgArray);
+            }
+        }
+    }
+
+    private void generateGetGeneratorResumptionPoint() {
+        cfw.addALoad(generatorStateLocal);
+        cfw.add(
+                ByteCode.GETFIELD,
+                OptRuntime.GeneratorState.CLASS_NAME,
+                OptRuntime.GeneratorState.resumptionPoint_NAME,
+                OptRuntime.GeneratorState.resumptionPoint_TYPE);
+    }
+
+    private void generateSetGeneratorResumptionPoint(int nextState) {
+        cfw.addALoad(generatorStateLocal);
+        cfw.addLoadConstant(nextState);
+        cfw.add(
+                ByteCode.PUTFIELD,
+                OptRuntime.GeneratorState.CLASS_NAME,
+                OptRuntime.GeneratorState.resumptionPoint_NAME,
+                OptRuntime.GeneratorState.resumptionPoint_TYPE);
+    }
+
+    private void generateGetGeneratorStackState() {
+        cfw.addALoad(generatorStateLocal);
+        addOptRuntimeInvoke("getGeneratorStackState", "(Ljava/lang/Object;)[Ljava/lang/Object;");
+    }
+
+    private void generateEpilogue() {
+        if (compilerEnv.isGenerateObserverCount()) addInstructionCount();
+        if (isGenerator) {
+            // generate locals initialization
+            Map<Node, int[]> liveLocals = ((FunctionNode) scriptOrFn).getLiveLocals();
+            if (liveLocals != null) {
+                List<Node> nodes = ((FunctionNode) scriptOrFn).getResumptionPoints();
+                for (int i = 0; i < nodes.size(); i++) {
+                    Node node = nodes.get(i);
+                    int[] live = liveLocals.get(node);
+                    if (live != null) {
+                        cfw.markTableSwitchCase(generatorSwitch, getNextGeneratorState(node));
+                        generateGetGeneratorLocalsState();
+                        for (int j = 0; j < live.length; j++) {
+                            cfw.add(ByteCode.DUP);
+                            cfw.addLoadConstant(j);
+                            cfw.add(ByteCode.AALOAD);
+                            cfw.addAStore(live[j]);
+                        }
+                        cfw.add(ByteCode.POP);
+                        cfw.add(ByteCode.GOTO, getTargetLabel(node));
+                    }
+                }
+            }
+
+            // generate dispatch tables for finally
+            if (finallys != null) {
+                for (Map.Entry<Node, FinallyReturnPoint> e : finallys.entrySet()) {
+                    if (e.getKey().getType() == Token.FINALLY) {
+                        FinallyReturnPoint ret = e.getValue();
+                        // the finally will jump here
+                        cfw.markLabel(ret.tableLabel, (short) 1);
+
+                        // start generating a dispatch table
+                        int startSwitch = cfw.addTableSwitch(0, ret.jsrPoints.size() - 1);
+                        int c = 0;
+                        cfw.markTableSwitchDefault(startSwitch);
+                        for (int i = 0; i < ret.jsrPoints.size(); i++) {
+                            // generate gotos back to the JSR location
+                            cfw.markTableSwitchCase(startSwitch, c);
+                            cfw.add(ByteCode.GOTO, ret.jsrPoints.get(i).intValue());
+                            c++;
+                        }
+                    }
+                }
+            }
+        }
+
+        if (epilogueLabel != -1) {
+            cfw.markLabel(epilogueLabel);
+        }
+
+        if (isGenerator) {
+            if (((FunctionNode) scriptOrFn).getResumptionPoints() != null) {
+                cfw.markTableSwitchDefault(generatorSwitch);
+            }
+
+            // change state for re-entry
+            generateSetGeneratorResumptionPoint(GENERATOR_TERMINATE);
+
+            // throw StopIteration. Needs scope as well as the generator state
+            cfw.addALoad(variableObjectLocal);
+            cfw.addALoad(generatorStateLocal);
+            addOptRuntimeInvoke("throwStopIteration", "(Ljava/lang/Object;Ljava/lang/Object;)V");
+
+            Codegen.pushUndefined(cfw);
+            cfw.add(ByteCode.ARETURN);
+
+        } else if (hasVarsInRegs) {
+            cfw.add(ByteCode.ARETURN);
+
+        } else if (fnCurrent == null) {
+            cfw.addALoad(popvLocal);
+            cfw.add(ByteCode.ARETURN);
+
+        } else {
+            generateActivationExit();
+            cfw.add(ByteCode.ARETURN);
+
+            // Generate catch block to catch all and rethrow to call exit code
+            // under exception propagation as well.
+
+            int finallyHandler = cfw.acquireLabel();
+            cfw.markHandler(finallyHandler);
+            short exceptionObject = getNewWordLocal();
+            cfw.addAStore(exceptionObject);
+
+            // Duplicate generateActivationExit() in the catch block since it
+            // takes less space then full-featured ByteCode.JSR/ByteCode.RET
+            generateActivationExit();
+
+            cfw.addALoad(exceptionObject);
+            releaseWordLocal(exceptionObject);
+            // rethrow
+            cfw.add(ByteCode.ATHROW);
+
+            // mark the handler
+            cfw.addExceptionHandler(
+                    enterAreaStartLabel, epilogueLabel, finallyHandler, null); // catch any
+        }
+    }
+
+    private void generateGetGeneratorLocalsState() {
+        cfw.addALoad(generatorStateLocal);
+        addOptRuntimeInvoke("getGeneratorLocalsState", "(Ljava/lang/Object;)[Ljava/lang/Object;");
+    }
+
+    private void generateSetGeneratorReturnValue() {
+        cfw.addALoad(generatorStateLocal);
+        cfw.add(ByteCode.SWAP);
+        addOptRuntimeInvoke("setGeneratorReturnValue", "(Ljava/lang/Object;Ljava/lang/Object;)V");
+    }
+
+    private void generateActivationExit() {
+        if (fnCurrent == null || hasVarsInRegs) throw Kit.codeBug();
+        cfw.addALoad(contextLocal);
+        addScriptRuntimeInvoke("exitActivationFunction", "(Lorg/mozilla/javascript/Context;)V");
+    }
+
+    private void generateStatement(Node node) {
+        updateLineNumber(node);
+        int type = node.getType();
+        Node child = node.getFirstChild();
+        switch (type) {
+            case Token.LOOP:
+            case Token.LABEL:
+            case Token.WITH:
+            case Token.SCRIPT:
+            case Token.BLOCK:
+            case Token.EMPTY:
+                // no-ops.
+                if (compilerEnv.isGenerateObserverCount()) {
+                    // Need to add instruction count even for no-ops to catch
+                    // cases like while (1) {}
+                    addInstructionCount(1);
+                }
+                while (child != null) {
+                    generateStatement(child);
+                    child = child.getNext();
+                }
+                break;
+
+            case Token.LOCAL_BLOCK:
+                {
+                    boolean prevLocal = inLocalBlock;
+                    inLocalBlock = true;
+                    int local = getNewWordLocal();
+                    if (isGenerator) {
+                        cfw.add(ByteCode.ACONST_NULL);
+                        cfw.addAStore(local);
+                    }
+                    node.putIntProp(Node.LOCAL_PROP, local);
+                    while (child != null) {
+                        generateStatement(child);
+                        child = child.getNext();
+                    }
+                    releaseWordLocal((short) local);
+                    node.removeProp(Node.LOCAL_PROP);
+                    inLocalBlock = prevLocal;
+                    break;
+                }
+
+            case Token.FUNCTION:
+                {
+                    int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
+                    OptFunctionNode ofn = OptFunctionNode.get(scriptOrFn, fnIndex);
+                    int t = ofn.fnode.getFunctionType();
+                    if (t == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) {
+                        visitFunction(ofn, t);
+                    } else {
+                        if (t != FunctionNode.FUNCTION_STATEMENT) {
+                            throw Codegen.badTree();
+                        }
+                    }
+                    break;
+                }
+
+            case Token.TRY:
+                visitTryCatchFinally((Jump) node, child);
+                break;
+
+            case Token.CATCH_SCOPE:
+                {
+                    // nothing stays on the stack on entry into a catch scope
+                    cfw.setStackTop((short) 0);
+
+                    int local = getLocalBlockRegister(node);
+                    int scopeIndex = node.getExistingIntProp(Node.CATCH_SCOPE_PROP);
+
+                    String name = child.getString(); // name of exception
+                    child = child.getNext();
+                    generateExpression(child, node); // load expression object
+                    if (scopeIndex == 0) {
+                        cfw.add(ByteCode.ACONST_NULL);
+                    } else {
+                        // Load previous catch scope object
+                        cfw.addALoad(local);
+                    }
+                    cfw.addPush(name);
+                    cfw.addALoad(contextLocal);
+                    cfw.addALoad(variableObjectLocal);
+
+                    addScriptRuntimeInvoke(
+                            "newCatchScope",
+                            "(Ljava/lang/Throwable;"
+                                    + "Lorg/mozilla/javascript/Scriptable;"
+                                    + "Ljava/lang/String;"
+                                    + "Lorg/mozilla/javascript/Context;"
+                                    + "Lorg/mozilla/javascript/Scriptable;"
+                                    + ")Lorg/mozilla/javascript/Scriptable;");
+                    cfw.addAStore(local);
+                }
+                break;
+
+            case Token.THROW:
+                generateExpression(child, node);
+                if (compilerEnv.isGenerateObserverCount()) addInstructionCount();
+                generateThrowJavaScriptException();
+                break;
+
+            case Token.RETHROW:
+                if (compilerEnv.isGenerateObserverCount()) addInstructionCount();
+                cfw.addALoad(getLocalBlockRegister(node));
+                cfw.add(ByteCode.ATHROW);
+                break;
+
+            case Token.RETURN_RESULT:
+            case Token.RETURN:
+                if (child != null) {
+                    generateExpression(child, node);
+                } else if (type == Token.RETURN) {
+                    Codegen.pushUndefined(cfw);
+                } else {
+                    if (popvLocal < 0) throw Codegen.badTree();
+                    cfw.addALoad(popvLocal);
+                }
+                if (isGenerator) {
+                    // Stash away the return value for use in the epilogue.
+                    generateSetGeneratorReturnValue();
+                }
+
+                if (compilerEnv.isGenerateObserverCount()) addInstructionCount();
+                if (epilogueLabel == -1) {
+                    if (!hasVarsInRegs) throw Codegen.badTree();
+                    epilogueLabel = cfw.acquireLabel();
+                }
+                cfw.add(ByteCode.GOTO, epilogueLabel);
+                break;
+
+            case Token.SWITCH:
+                if (compilerEnv.isGenerateObserverCount()) addInstructionCount();
+                visitSwitch((Jump) node, child);
+                break;
+
+            case Token.ENTERWITH:
+                generateExpression(child, node);
+                cfw.addALoad(contextLocal);
+                cfw.addALoad(variableObjectLocal);
+                addScriptRuntimeInvoke(
+                        "enterWith",
+                        "(Ljava/lang/Object;"
+                                + "Lorg/mozilla/javascript/Context;"
+                                + "Lorg/mozilla/javascript/Scriptable;"
+                                + ")Lorg/mozilla/javascript/Scriptable;");
+                cfw.addAStore(variableObjectLocal);
+                incReferenceWordLocal(variableObjectLocal);
+                break;
+
+            case Token.LEAVEWITH:
+                cfw.addALoad(variableObjectLocal);
+                addScriptRuntimeInvoke(
+                        "leaveWith",
+                        "(Lorg/mozilla/javascript/Scriptable;"
+                                + ")Lorg/mozilla/javascript/Scriptable;");
+                cfw.addAStore(variableObjectLocal);
+                decReferenceWordLocal(variableObjectLocal);
+                break;
+
+            case Token.ENUM_INIT_KEYS:
+            case Token.ENUM_INIT_VALUES:
+            case Token.ENUM_INIT_ARRAY:
+            case Token.ENUM_INIT_VALUES_IN_ORDER:
+                generateExpression(child, node);
+                cfw.addALoad(contextLocal);
+                cfw.addALoad(variableObjectLocal);
+                int enumType =
+                        type == Token.ENUM_INIT_KEYS
+                                ? ScriptRuntime.ENUMERATE_KEYS
+                                : type == Token.ENUM_INIT_VALUES
+                                        ? ScriptRuntime.ENUMERATE_VALUES
+                                        : type == Token.ENUM_INIT_VALUES_IN_ORDER
+                                                ? ScriptRuntime.ENUMERATE_VALUES_IN_ORDER
+                                                : ScriptRuntime.ENUMERATE_ARRAY;
+                cfw.addPush(enumType);
+                addScriptRuntimeInvoke(
+                        "enumInit",
+                        "(Ljava/lang/Object;"
+                                + "Lorg/mozilla/javascript/Context;"
+                                + "Lorg/mozilla/javascript/Scriptable;"
+                                + "I"
+                                + ")Ljava/lang/Object;");
+                cfw.addAStore(getLocalBlockRegister(node));
+                break;
+
+            case Token.EXPR_VOID:
+                if (child.getType() == Token.SETVAR) {
+                    /* special case this so as to avoid unnecessary
+                    load's & pop's */
+                    visitSetVar(child, child.getFirstChild(), false);
+                } else if (child.getType() == Token.SETCONSTVAR) {
+                    /* special case this so as to avoid unnecessary
+                    load's & pop's */
+                    visitSetConstVar(child, child.getFirstChild(), false);
+                } else if ((child.getType() == Token.YIELD)
+                        || (child.getType() == Token.YIELD_STAR)) {
+                    generateYieldPoint(child, false);
+                } else {
+                    generateExpression(child, node);
+                    if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) cfw.add(ByteCode.POP2);
+                    else cfw.add(ByteCode.POP);
+                }
+                break;
+
+            case Token.EXPR_RESULT:
+                generateExpression(child, node);
+                if (popvLocal < 0) {
+                    popvLocal = getNewWordLocal();
+                }
+                cfw.addAStore(popvLocal);
+                break;
+
+            case Token.TARGET:
+                {
+                    if (compilerEnv.isGenerateObserverCount()) addInstructionCount();
+                    int label = getTargetLabel(node);
+                    cfw.markLabel(label);
+                    if (compilerEnv.isGenerateObserverCount()) saveCurrentCodeOffset();
+                }
+                break;
+
+            case Token.JSR:
+            case Token.GOTO:
+            case Token.IFEQ:
+            case Token.IFNE:
+                if (compilerEnv.isGenerateObserverCount()) addInstructionCount();
+                visitGoto((Jump) node, type, child);
+                break;
+
+            case Token.FINALLY:
+                {
+                    // This is the non-exception case for a finally block. In
+                    // other words, since we inline finally blocks wherever
+                    // jsr was previously used, and jsr is only used when the
+                    // function is not a generator, we don't need to generate
+                    // this case if the function isn't a generator.
+                    if (!isGenerator) {
+                        break;
+                    }
+
+                    if (compilerEnv.isGenerateObserverCount()) saveCurrentCodeOffset();
+                    // there is exactly one value on the stack when enterring
+                    // finally blocks: the return address (or its int encoding)
+                    cfw.setStackTop((short) 1);
+
+                    // Save return address in a new local
+                    int finallyRegister = getNewWordLocal();
+
+                    int finallyStart = cfw.acquireLabel();
+                    int finallyEnd = cfw.acquireLabel();
+                    cfw.markLabel(finallyStart);
+
+                    generateIntegerWrap();
+                    cfw.addAStore(finallyRegister);
+
+                    while (child != null) {
+                        generateStatement(child);
+                        child = child.getNext();
+                    }
+
+                    cfw.addALoad(finallyRegister);
+                    cfw.add(ByteCode.CHECKCAST, "java/lang/Integer");
+                    generateIntegerUnwrap();
+                    FinallyReturnPoint ret = finallys.get(node);
+                    ret.tableLabel = cfw.acquireLabel();
+                    cfw.add(ByteCode.GOTO, ret.tableLabel);
+
+                    // After this GOTO we expect stack to be empty again!
+                    cfw.setStackTop((short) 0);
+
+                    releaseWordLocal((short) finallyRegister);
+                    cfw.markLabel(finallyEnd);
+                }
+                break;
+
+            case Token.DEBUGGER:
+                break;
+
+            default:
+                throw Codegen.badTree();
+        }
+    }
+
+    private void generateIntegerWrap() {
+        cfw.addInvoke(
+                ByteCode.INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
+    }
+
+    private void generateIntegerUnwrap() {
+        cfw.addInvoke(ByteCode.INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I");
+    }
+
+    private void generateThrowJavaScriptException() {
+        cfw.add(ByteCode.NEW, "org/mozilla/javascript/JavaScriptException");
+        cfw.add(ByteCode.DUP_X1);
+        cfw.add(ByteCode.SWAP);
+        cfw.addPush(scriptOrFn.getSourceName());
+        cfw.addPush(itsLineNumber);
+        cfw.addInvoke(
+                ByteCode.INVOKESPECIAL,
+                "org/mozilla/javascript/JavaScriptException",
+                "<init>",
+                "(Ljava/lang/Object;Ljava/lang/String;I)V");
+        cfw.add(ByteCode.ATHROW);
+    }
+
+    private int getNextGeneratorState(Node node) {
+        int nodeIndex = ((FunctionNode) scriptOrFn).getResumptionPoints().indexOf(node);
+        return nodeIndex + GENERATOR_YIELD_START;
+    }
+
+    private void generateExpression(Node node, Node parent) {
+        int type = node.getType();
+        Node child = node.getFirstChild();
+        switch (type) {
+            case Token.USE_STACK:
+                break;
+
+            case Token.FUNCTION:
+                if (fnCurrent != null || parent.getType() != Token.SCRIPT) {
+                    int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
+                    OptFunctionNode ofn = OptFunctionNode.get(scriptOrFn, fnIndex);
+                    int t = ofn.fnode.getFunctionType();
+                    if (t != FunctionNode.FUNCTION_EXPRESSION && t != FunctionNode.ARROW_FUNCTION) {
+                        throw Codegen.badTree();
+                    }
+                    visitFunction(ofn, t);
+                }
+                break;
+
+            case Token.NAME:
+                {
+                    cfw.addALoad(contextLocal);
+                    cfw.addALoad(variableObjectLocal);
+                    cfw.addPush(node.getString());
+                    addScriptRuntimeInvoke(
+                            "name",
+                            "(Lorg/mozilla/javascript/Context;"
+                                    + "Lorg/mozilla/javascript/Scriptable;"
+                                    + "Ljava/lang/String;"
+                                    + ")Ljava/lang/Object;");
+                }
+                break;
+
+            case Token.CALL:
+            case Token.NEW:
+                {
+                    int specialType = node.getIntProp(Node.SPECIALCALL_PROP, Node.NON_SPECIALCALL);
+                    if (specialType == Node.NON_SPECIALCALL) {
+                        OptFunctionNode target;
+                        target = (OptFunctionNode) node.getProp(Node.DIRECTCALL_PROP);
+
+                        if (target != null) {
+                            visitOptimizedCall(node, target, type, child);
+                        } else if (type == Token.CALL) {
+                            visitStandardCall(node, child);
+                        } else {
+                            visitStandardNew(node, child);
+                        }
+                    } else {
+                        visitSpecialCall(node, type, specialType, child);
+                    }
+                }
+                break;
+
+            case Token.REF_CALL:
+                generateFunctionAndThisObj(child, node);
+                // stack: ... functionObj thisObj
+                child = child.getNext();
+                generateCallArgArray(node, child, false);
+                cfw.addALoad(contextLocal);
+                addScriptRuntimeInvoke(
+                        "callRef",
+                        "(Lorg/mozilla/javascript/Callable;"
+                                + "Lorg/mozilla/javascript/Scriptable;"
+                                + "[Ljava/lang/Object;"
+                                + "Lorg/mozilla/javascript/Context;"
+                                + ")Lorg/mozilla/javascript/Ref;");
+                break;
+
+            case Token.NUMBER:
+                {
+                    double num = node.getDouble();
+                    if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
+                        cfw.addPush(num);
+                    } else {
+                        codegen.pushNumberAsObject(cfw, num);
+                    }
+                }
+                break;
+
+            case Token.BIGINT:
+                {
+                    byte[] bytes = node.getBigInt().toByteArray();
+                    cfw.add(ByteCode.NEW, "java/math/BigInteger");
+                    cfw.add(ByteCode.DUP);
+                    cfw.addPush(bytes.length);
+                    cfw.add(ByteCode.NEWARRAY, ByteCode.T_BYTE);
+                    for (int i = 0; i < bytes.length; i++) {
+                        cfw.add(ByteCode.DUP);
+                        cfw.addPush(i);
+                        cfw.add(ByteCode.BIPUSH, bytes[i]);
+                        cfw.add(ByteCode.BASTORE);
+                    }
+                    cfw.addInvoke(
+                            ByteCode.INVOKESPECIAL, "java/math/BigInteger", "<init>", "([B)V");
+                }
+                break;
+
+            case Token.STRING:
+                cfw.addPush(node.getString());
+                break;
+
+            case Token.THIS:
+                cfw.addALoad(thisObjLocal);
+                break;
+
+            case Token.THISFN:
+                cfw.add(ByteCode.ALOAD_0);
+                break;
+
+            case Token.NULL:
+                cfw.add(ByteCode.ACONST_NULL);
+                break;
+
+            case Token.TRUE:
+                cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;");
+                break;
+
+            case Token.FALSE:
+                cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;");
+                break;
+
+            case Token.REGEXP:
+                {
+                    // Create a new wrapper around precompiled regexp
+                    cfw.addALoad(contextLocal);
+                    cfw.addALoad(variableObjectLocal);
+                    int i = node.getExistingIntProp(Node.REGEXP_PROP);
+                    cfw.add(
+                            ByteCode.GETSTATIC,
+                            codegen.mainClassName,
+                            codegen.getCompiledRegexpName(scriptOrFn, i),
+                            "Ljava/lang/Object;");
+                    cfw.addInvoke(
+                            ByteCode.INVOKESTATIC,
+                            "org/mozilla/javascript/ScriptRuntime",
+                            "wrapRegExp",
+                            "(Lorg/mozilla/javascript/Context;"
+                                    + "Lorg/mozilla/javascript/Scriptable;"
+                                    + "Ljava/lang/Object;"
+                                    + ")Lorg/mozilla/javascript/Scriptable;");
+                }
+                break;
+
+            case Token.COMMA:
+                {
+                    Node next = child.getNext();
+                    while (next != null) {
+                        generateExpression(child, node);
+                        cfw.add(ByteCode.POP);
+                        child = next;
+                        next = next.getNext();
+                    }
+                    generateExpression(child, node);
+                    break;
+                }
+
+            case Token.ENUM_NEXT:
+            case Token.ENUM_ID:
+                {
+                    int local = getLocalBlockRegister(node);
+                    cfw.addALoad(local);
+                    if (type == Token.ENUM_NEXT) {
+                        addScriptRuntimeInvoke(
+                                "enumNext", "(Ljava/lang/Object;)Ljava/lang/Boolean;");
+                    } else {
+                        cfw.addALoad(contextLocal);
+                        addScriptRuntimeInvoke(
+                                "enumId",
+                                "(Ljava/lang/Object;"
+                                        + "Lorg/mozilla/javascript/Context;"
+                                        + ")Ljava/lang/Object;");
+                    }
+                    break;
+                }
+
+            case Token.ARRAYLIT:
+                visitArrayLiteral(node, child, false);
+                break;
+
+            case Token.OBJECTLIT:
+                visitObjectLiteral(node, child, false);
+                break;
+
+            case Token.NOT:
+                {
+                    int trueTarget = cfw.acquireLabel();
+                    int falseTarget = cfw.acquireLabel();
+                    int beyond = cfw.acquireLabel();
+                    generateIfJump(child, node, trueTarget, falseTarget);
+
+                    cfw.markLabel(trueTarget);
+                    cfw.add(
+                            ByteCode.GETSTATIC,
+                            "java/lang/Boolean",
+                            "FALSE",
+                            "Ljava/lang/Boolean;");
+                    cfw.add(ByteCode.GOTO, beyond);
+                    cfw.markLabel(falseTarget);
+                    cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;");
+                    cfw.markLabel(beyond);
+                    cfw.adjustStackTop(-1);
+                    break;
+                }
+
+            case Token.BITNOT:
+                visitBitNot(node, child);
+                break;
+
+            case Token.VOID:
+                generateExpression(child, node);
+                cfw.add(ByteCode.POP);
+                Codegen.pushUndefined(cfw);
+                break;
+
+            case Token.TYPEOF:
+                generateExpression(child, node);
+                addScriptRuntimeInvoke("typeof", "(Ljava/lang/Object;" + ")Ljava/lang/String;");
+                break;
+
+            case Token.TYPEOFNAME:
+                visitTypeofname(node);
+                break;
+
+            case Token.INC:
+            case Token.DEC:
+                visitIncDec(node);
+                break;
+
+            case Token.OR:
+            case Token.AND:
+                {
+                    generateExpression(child, node);
+                    cfw.add(ByteCode.DUP);
+                    addScriptRuntimeInvoke("toBoolean", "(Ljava/lang/Object;)Z");
+                    int falseTarget = cfw.acquireLabel();
+                    if (type == Token.AND) cfw.add(ByteCode.IFEQ, falseTarget);
+                    else cfw.add(ByteCode.IFNE, falseTarget);
+                    cfw.add(ByteCode.POP);
+                    generateExpression(child.getNext(), node);
+                    cfw.markLabel(falseTarget);
+                }
+                break;
+
+            case Token.HOOK:
+                {
+                    Node ifThen = child.getNext();
+                    Node ifElse = ifThen.getNext();
+                    generateExpression(child, node);
+                    addScriptRuntimeInvoke("toBoolean", "(Ljava/lang/Object;)Z");
+                    int elseTarget = cfw.acquireLabel();
+                    cfw.add(ByteCode.IFEQ, elseTarget);
+                    short stack = cfw.getStackTop();
+                    generateExpression(ifThen, node);
+                    int afterHook = cfw.acquireLabel();
+                    cfw.add(ByteCode.GOTO, afterHook);
+                    cfw.markLabel(elseTarget, stack);
+                    generateExpression(ifElse, node);
+                    cfw.markLabel(afterHook);
+                }
+                break;
+
+            case Token.ADD:
+                {
+                    generateExpression(child, node);
+                    generateExpression(child.getNext(), node);
+                    switch (node.getIntProp(Node.ISNUMBER_PROP, -1)) {
+                        case Node.BOTH:
+                            cfw.add(ByteCode.DADD);
+                            break;
+                        case Node.LEFT:
+                            cfw.addALoad(contextLocal);
+                            addOptRuntimeInvoke(
+                                    "add",
+                                    "(DLjava/lang/Object;Lorg/mozilla/javascript/Context;)Ljava/lang/Object;");
+                            break;
+                        case Node.RIGHT:
+                            cfw.addALoad(contextLocal);
+                            addOptRuntimeInvoke(
+                                    "add",
+                                    "(Ljava/lang/Object;DLorg/mozilla/javascript/Context;)Ljava/lang/Object;");
+                            break;
+                        default:
+                            cfw.addALoad(contextLocal);
+                            addScriptRuntimeInvoke(
+                                    "add",
+                                    "(Ljava/lang/Object;"
+                                            + "Ljava/lang/Object;"
+                                            + "Lorg/mozilla/javascript/Context;"
+                                            + ")Ljava/lang/Object;");
+                    }
+                }
+                break;
+
+            case Token.SUB:
+            case Token.MUL:
+            case Token.DIV:
+            case Token.MOD:
+                visitArithmetic(node, type, child, parent);
+                break;
+
+            case Token.EXP:
+                visitExponentiation(node, child, parent);
+                break;
+
+            case Token.BITOR:
+            case Token.BITXOR:
+            case Token.BITAND:
+            case Token.LSH:
+            case Token.RSH:
+            case Token.URSH:
+                visitBitOp(node, type, child);
+                break;
+
+            case Token.POS:
+                {
+                    int childNumberFlag = node.getIntProp(Node.ISNUMBER_PROP, -1);
+                    generateExpression(child, node);
+                    if (childNumberFlag == -1) {
+                        addObjectToDouble();
+                        addDoubleWrap();
+                    }
+                    break;
+                }
+
+            case Token.NEG:
+                {
+                    int childNumberFlag = node.getIntProp(Node.ISNUMBER_PROP, -1);
+                    generateExpression(child, node);
+                    if (childNumberFlag == -1) {
+                        addObjectToNumeric();
+                        addScriptRuntimeInvoke(
+                                "negate", "(Ljava/lang/Number;" + ")Ljava/lang/Number;");
+                    } else {
+                        cfw.add(ByteCode.DNEG);
+                    }
+                    break;
+                }
+
+            case Token.TO_DOUBLE:
+                // cnvt to double (not Double)
+                generateExpression(child, node);
+                addObjectToDouble();
+                break;
+
+            case Token.TO_OBJECT:
+                {
+                    // convert from double
+                    int prop = -1;
+                    if (child.getType() == Token.NUMBER) {
+                        prop = child.getIntProp(Node.ISNUMBER_PROP, -1);
+                    }
+                    if (prop != -1) {
+                        child.removeProp(Node.ISNUMBER_PROP);
+                        generateExpression(child, node);
+                        child.putIntProp(Node.ISNUMBER_PROP, prop);
+                    } else {
+                        generateExpression(child, node);
+                        addDoubleWrap();
+                    }
+                    break;
+                }
+
+            case Token.IN:
+            case Token.INSTANCEOF:
+            case Token.LE:
+            case Token.LT:
+            case Token.GE:
+            case Token.GT:
+                {
+                    int trueGOTO = cfw.acquireLabel();
+                    int falseGOTO = cfw.acquireLabel();
+                    visitIfJumpRelOp(node, child, trueGOTO, falseGOTO);
+                    addJumpedBooleanWrap(trueGOTO, falseGOTO);
+                    break;
+                }
+
+            case Token.EQ:
+            case Token.NE:
+            case Token.SHEQ:
+            case Token.SHNE:
+                {
+                    int trueGOTO = cfw.acquireLabel();
+                    int falseGOTO = cfw.acquireLabel();
+                    visitIfJumpEqOp(node, child, trueGOTO, falseGOTO);
+                    addJumpedBooleanWrap(trueGOTO, falseGOTO);
+                    break;
+                }
+
+            case Token.GETPROP:
+            case Token.GETPROPNOWARN:
+                visitGetProp(node, child);
+                break;
+
+            case Token.GETELEM:
+                generateExpression(child, node); // object
+                generateExpression(child.getNext(), node); // id
+                cfw.addALoad(contextLocal);
+                if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
+                    addScriptRuntimeInvoke(
+                            "getObjectIndex",
+                            "(Ljava/lang/Object;D"
+                                    + "Lorg/mozilla/javascript/Context;"
+                                    + ")Ljava/lang/Object;");
+                } else {
+                    cfw.addALoad(variableObjectLocal);
+                    addScriptRuntimeInvoke(
+                            "getObjectElem",
+                            "(Ljava/lang/Object;"
+                                    + "Ljava/lang/Object;"
+                                    + "Lorg/mozilla/javascript/Context;"
+                                    + "Lorg/mozilla/javascript/Scriptable;"
+                                    + ")Ljava/lang/Object;");
+                }
+                break;
+
+            case Token.GET_REF:
+                generateExpression(child, node); // reference
+                cfw.addALoad(contextLocal);
+                addScriptRuntimeInvoke(
+                        "refGet",
+                        "(Lorg/mozilla/javascript/Ref;"
+                                + "Lorg/mozilla/javascript/Context;"
+                                + ")Ljava/lang/Object;");
+                break;
+
+            case Token.GETVAR:
+                visitGetVar(node);
+                break;
+
+            case Token.SETVAR:
+                visitSetVar(node, child, true);
+                break;
+
+            case Token.SETNAME:
+                visitSetName(node, child);
+                break;
+
+            case Token.STRICT_SETNAME:
+                visitStrictSetName(node, child);
+                break;
+
+            case Token.SETCONST:
+                visitSetConst(node, child);
+                break;
+
+            case Token.SETCONSTVAR:
+                visitSetConstVar(node, child, true);
+                break;
+
+            case Token.SETPROP:
+            case Token.SETPROP_OP:
+                visitSetProp(type, node, child);
+                break;
+
+            case Token.SETELEM:
+            case Token.SETELEM_OP:
+                visitSetElem(type, node, child);
+                break;
+
+            case Token.SET_REF:
+            case Token.SET_REF_OP:
+                {
+                    generateExpression(child, node);
+                    child = child.getNext();
+                    if (type == Token.SET_REF_OP) {
+                        cfw.add(ByteCode.DUP);
+                        cfw.addALoad(contextLocal);
+                        addScriptRuntimeInvoke(
+                                "refGet",
+                                "(Lorg/mozilla/javascript/Ref;"
+                                        + "Lorg/mozilla/javascript/Context;"
+                                        + ")Ljava/lang/Object;");
+                    }
+                    generateExpression(child, node);
+                    cfw.addALoad(contextLocal);
+                    cfw.addALoad(variableObjectLocal);
+                    addScriptRuntimeInvoke(
+                            "refSet",
+                            "(Lorg/mozilla/javascript/Ref;"
+                                    + "Ljava/lang/Object;"
+                                    + "Lorg/mozilla/javascript/Context;"
+                                    + "Lorg/mozilla/javascript/Scriptable;"
+                                    + ")Ljava/lang/Object;");
+                }
+                break;
+
+            case Token.DEL_REF:
+                generateExpression(child, node);
+                cfw.addALoad(contextLocal);
+                addScriptRuntimeInvoke(
+                        "refDel",
+                        "(Lorg/mozilla/javascript/Ref;"
+                                + "Lorg/mozilla/javascript/Context;"
+                                + ")Ljava/lang/Object;");
+                break;
+
+            case Token.DELPROP:
+                boolean isName = child.getType() == Token.BINDNAME;
+                generateExpression(child, node);
+                child = child.getNext();
+                generateExpression(child, node);
+                cfw.addALoad(contextLocal);
+                cfw.addPush(isName);
+                addScriptRuntimeInvoke(
+                        "delete",
+                        "(Ljava/lang/Object;"
+                                + "Ljava/lang/Object;"
+                                + "Lorg/mozilla/javascript/Context;"
+                                + "Z)Ljava/lang/Object;");
+                break;
+
+            case Token.BINDNAME:
+                {
+                    while (child != null) {
+                        generateExpression(child, node);
+                        child = child.getNext();
+                    }
+                    // Generate code for "ScriptRuntime.bind(varObj, "s")"
+                    cfw.addALoad(contextLocal);
+                    cfw.addALoad(variableObjectLocal);
+                    cfw.addPush(node.getString());
+                    addScriptRuntimeInvoke(
+                            "bind",
+                            "(Lorg/mozilla/javascript/Context;"
+                                    + "Lorg/mozilla/javascript/Scriptable;"
+                                    + "Ljava/lang/String;"
+                                    + ")Lorg/mozilla/javascript/Scriptable;");
+                }
+                break;
+
+            case Token.LOCAL_LOAD:
+                cfw.addALoad(getLocalBlockRegister(node));
+                break;
+
+            case Token.REF_SPECIAL:
+                {
+                    String special = (String) node.getProp(Node.NAME_PROP);
+                    generateExpression(child, node);
+                    cfw.addPush(special);
+                    cfw.addALoad(contextLocal);
+                    cfw.addALoad(variableObjectLocal);
+                    addScriptRuntimeInvoke(
+                            "specialRef",
+                            "(Ljava/lang/Object;"
+                                    + "Ljava/lang/String;"
+                                    + "Lorg/mozilla/javascript/Context;"
+                                    + "Lorg/mozilla/javascript/Scriptable;"
+                                    + ")Lorg/mozilla/javascript/Ref;");
+                }
+                break;
+
+            case Token.REF_MEMBER:
+            case Token.REF_NS_MEMBER:
+            case Token.REF_NAME:
+            case Token.REF_NS_NAME:
+                {
+                    int memberTypeFlags = node.getIntProp(Node.MEMBER_TYPE_PROP, 0);
+                    // generate possible target, possible namespace and member
+                    do {
+                        generateExpression(child, node);
+                        child = child.getNext();
+                    } while (child != null);
+                    cfw.addALoad(contextLocal);
+                    String methodName, signature;
+                    switch (type) {
+                        case Token.REF_MEMBER:
+                            methodName = "memberRef";
+                            signature =
+                                    "(Ljava/lang/Object;"
+                                            + "Ljava/lang/Object;"
+                                            + "Lorg/mozilla/javascript/Context;"
+                                            + "I"
+                                            + ")Lorg/mozilla/javascript/Ref;";
+                            break;
+                        case Token.REF_NS_MEMBER:
+                            methodName = "memberRef";
+                            signature =
+                                    "(Ljava/lang/Object;"
+                                            + "Ljava/lang/Object;"
+                                            + "Ljava/lang/Object;"
+                                            + "Lorg/mozilla/javascript/Context;"
+                                            + "I"
+                                            + ")Lorg/mozilla/javascript/Ref;";
+                            break;
+                        case Token.REF_NAME:
+                            methodName = "nameRef";
+                            signature =
+                                    "(Ljava/lang/Object;"
+                                            + "Lorg/mozilla/javascript/Context;"
+                                            + "Lorg/mozilla/javascript/Scriptable;"
+                                            + "I"
+                                            + ")Lorg/mozilla/javascript/Ref;";
+                            cfw.addALoad(variableObjectLocal);
+                            break;
+                        case Token.REF_NS_NAME:
+                            methodName = "nameRef";
+                            signature =
+                                    "(Ljava/lang/Object;"
+                                            + "Ljava/lang/Object;"
+                                            + "Lorg/mozilla/javascript/Context;"
+                                            + "Lorg/mozilla/javascript/Scriptable;"
+                                            + "I"
+                                            + ")Lorg/mozilla/javascript/Ref;";
+                            cfw.addALoad(variableObjectLocal);
+                            break;
+                        default:
+                            throw Kit.codeBug();
+                    }
+                    cfw.addPush(memberTypeFlags);
+                    addScriptRuntimeInvoke(methodName, signature);
+                }
+                break;
+
+            case Token.DOTQUERY:
+                visitDotQuery(node, child);
+                break;
+
+            case Token.ESCXMLATTR:
+                generateExpression(child, node);
+                cfw.addALoad(contextLocal);
+                addScriptRuntimeInvoke(
+                        "escapeAttributeValue",
+                        "(Ljava/lang/Object;"
+                                + "Lorg/mozilla/javascript/Context;"
+                                + ")Ljava/lang/String;");
+                break;
+
+            case Token.ESCXMLTEXT:
+                generateExpression(child, node);
+                cfw.addALoad(contextLocal);
+                addScriptRuntimeInvoke(
+                        "escapeTextValue",
+                        "(Ljava/lang/Object;"
+                                + "Lorg/mozilla/javascript/Context;"
+                                + ")Ljava/lang/String;");
+                break;
+
+            case Token.DEFAULTNAMESPACE:
+                generateExpression(child, node);
+                cfw.addALoad(contextLocal);
+                addScriptRuntimeInvoke(
+                        "setDefaultNamespace",
+                        "(Ljava/lang/Object;"
+                                + "Lorg/mozilla/javascript/Context;"
+                                + ")Ljava/lang/Object;");
+                break;
+
+            case Token.YIELD:
+            case Token.YIELD_STAR:
+                generateYieldPoint(node, true);
+                break;
+
+            case Token.WITHEXPR:
+                {
+                    Node enterWith = child;
+                    Node with = enterWith.getNext();
+                    Node leaveWith = with.getNext();
+                    generateStatement(enterWith);
+                    generateExpression(with.getFirstChild(), with);
+                    generateStatement(leaveWith);
+                    break;
+                }
+
+            case Token.ARRAYCOMP:
+                {
+                    Node initStmt = child;
+                    Node expr = child.getNext();
+                    generateStatement(initStmt);
+                    generateExpression(expr, node);
+                    break;
+                }
+
+            case Token.TEMPLATE_LITERAL:
+                visitTemplateLiteral(node);
+                break;
+
+            default:
+                throw new RuntimeException("Unexpected node type " + type);
+        }
+    }
+
+    // Descend down the tree and return the first child that represents a yield
+    // point, or null.
+    private Node findNestedYield(Node node) {
+        Node child = node.getFirstChild();
+        while (child != null) {
+            if ((child.getType() == Token.YIELD) || (child.getType() == Token.YIELD_STAR)) {
+                return child;
+            }
+            Node grandchild = findNestedYield(child);
+            if (grandchild != null) {
+                return grandchild;
+            }
+            child = child.getNext();
+        }
+        return null;
+    }
+
+    private void generateYieldPoint(Node node, boolean exprContext) {
+        if (unnestedYields.containsKey(node)) {
+            // Yield was previously moved up via the "nestedYield" code below.
+            if (exprContext) {
+                cfw.addALoad(variableObjectLocal);
+                cfw.addLoadConstant(unnestedYields.get(node));
+                cfw.addALoad(contextLocal);
+                cfw.addALoad(variableObjectLocal);
+                addScriptRuntimeInvoke(
+                        "getObjectPropNoWarn",
+                        "(Ljava/lang/Object;Ljava/lang/String;Lorg/mozilla/javascript/Context;"
+                                + "Lorg/mozilla/javascript/Scriptable;)Ljava/lang/Object;");
+            }
+            return;
+        }
+
+        final Node nestedYield = findNestedYield(node);
+        if (nestedYield != null) {
+            // There is another "yield" nested below this one. Pull it up,
+            // execute it now, and assign its result to a uniquely-named constant.
+            // This is necessary because the recursive code to generate bytecode can't
+            // handle a yield that uses the result of a nested yield.
+            // Otherwise, expressions like "return yield(yield x)" return the wrong result.
+            generateYieldPoint(nestedYield, true);
+
+            final String nn = "__nested__yield__" + unnestedYieldCount;
+            unnestedYieldCount++;
+            cfw.addALoad(variableObjectLocal);
+            cfw.add(ByteCode.SWAP);
+            cfw.addLoadConstant(nn);
+            cfw.add(ByteCode.SWAP);
+            cfw.addALoad(contextLocal);
+
+            addScriptRuntimeInvoke(
+                    "setObjectProp",
+                    "(Lorg/mozilla/javascript/Scriptable;Ljava/lang/String;Ljava/lang/Object;"
+                            + "Lorg/mozilla/javascript/Context;)Ljava/lang/Object;");
+            cfw.add(ByteCode.POP);
+
+            unnestedYields.put(nestedYield, nn);
+        }
+
+        // Now keep on as normal. When we encounter the nested yield, we will replace it
+        // with a lookup of the constant.
+        generateLocalYieldPoint(node, exprContext);
+    }
+
+    private void generateLocalYieldPoint(Node node, boolean exprContext) {
+
+        // save stack state from the top to the bottom
+        final int top = cfw.getStackTop();
+        maxStack = maxStack > top ? maxStack : top;
+        if (top != 0) {
+            generateGetGeneratorStackState();
+            for (int i = 0; i < top; i++) {
+                cfw.add(ByteCode.DUP_X1);
+                cfw.add(ByteCode.SWAP);
+                cfw.addLoadConstant(i);
+                cfw.add(ByteCode.SWAP);
+                cfw.add(ByteCode.AASTORE);
+            }
+            // pop the array object
+            cfw.add(ByteCode.POP);
+        }
+
+        // generate the yield argument
+        Node child = node.getFirstChild();
+        if (child != null) generateExpression(child, node);
+        else Codegen.pushUndefined(cfw);
+
+        if (node.getType() == Token.YIELD_STAR) {
+            // We will replace the result with one that signifies we should have a generator
+            cfw.add(ByteCode.NEW, "org/mozilla/javascript/ES6Generator$YieldStarResult");
+            cfw.add(ByteCode.DUP_X1);
+            cfw.add(ByteCode.SWAP);
+            cfw.addInvoke(
+                    ByteCode.INVOKESPECIAL,
+                    "org/mozilla/javascript/ES6Generator$YieldStarResult",
+                    "<init>",
+                    "(Ljava/lang/Object;)V");
+        }
+
+        // change the resumption state
+        int nextState = getNextGeneratorState(node);
+        generateSetGeneratorResumptionPoint(nextState);
+
+        boolean hasLocals = generateSaveLocals(node);
+
+        cfw.add(ByteCode.ARETURN);
+
+        // We get back here after a "next" on the generator object itself
+
+        generateCheckForThrowOrClose(getTargetLabel(node), hasLocals, nextState);
+
+        // reconstruct the stack from the bottom to the top
+        if (top != 0) {
+            generateGetGeneratorStackState();
+            for (int i = (top - 1); i >= 0; i--) {
+                cfw.add(ByteCode.DUP);
+                cfw.addLoadConstant(i);
+                cfw.add(ByteCode.AALOAD);
+                cfw.add(ByteCode.SWAP);
+            }
+            cfw.add(ByteCode.POP);
+        }
+
+        // load return value from yield
+        if (exprContext) {
+            cfw.addALoad(argsLocal);
+        }
+    }
+
+    private void generateCheckForThrowOrClose(int label, boolean hasLocals, int nextState) {
+        int throwLabel = cfw.acquireLabel();
+        int closeLabel = cfw.acquireLabel();
+
+        // throw the user provided object, if the operation is .throw()
+        cfw.markLabel(throwLabel);
+        cfw.addALoad(argsLocal);
+        generateThrowJavaScriptException();
+
+        // throw our special internal exception if the generator is being closed
+        cfw.markLabel(closeLabel);
+        cfw.addALoad(argsLocal);
+        cfw.add(ByteCode.CHECKCAST, "java/lang/Throwable");
+        cfw.add(ByteCode.ATHROW);
+
+        // mark the re-entry point
+        // jump here after initializing the locals
+        if (label != -1) cfw.markLabel(label);
+        if (!hasLocals) {
+            // jump here directly if there are no locals
+            cfw.markTableSwitchCase(generatorSwitch, nextState);
+        }
+
+        // see if we need to dispatch for .close() or .throw()
+        cfw.addILoad(operationLocal);
+        cfw.addLoadConstant(NativeGenerator.GENERATOR_CLOSE);
+        cfw.add(ByteCode.IF_ICMPEQ, closeLabel);
+        cfw.addILoad(operationLocal);
+        cfw.addLoadConstant(NativeGenerator.GENERATOR_THROW);
+        cfw.add(ByteCode.IF_ICMPEQ, throwLabel);
+    }
+
+    private void visitTemplateLiteral(Node node) {
+        // create the template literal call-site object for tagged template literals,
+        // default template literals are already handled earlier in IRFactory
+        int index = node.getExistingIntProp(Node.TEMPLATE_LITERAL_PROP);
+        cfw.addALoad(contextLocal);
+        cfw.addALoad(variableObjectLocal);
+        cfw.add(
+                ByteCode.GETSTATIC,
+                codegen.mainClassName,
+                codegen.getTemplateLiteralName(scriptOrFn),
+                "[Ljava/lang/Object;");
+        cfw.addPush(index);
+        cfw.addInvoke(
+                ByteCode.INVOKESTATIC,
+                "org/mozilla/javascript/ScriptRuntime",
+                "getTemplateLiteralCallSite",
+                "(Lorg/mozilla/javascript/Context;"
+                        + "Lorg/mozilla/javascript/Scriptable;"
+                        + "[Ljava/lang/Object;I"
+                        + ")Lorg/mozilla/javascript/Scriptable;");
+    }
+
+    private void generateIfJump(Node node, Node parent, int trueLabel, int falseLabel) {
+        int type = node.getType();
+        Node child = node.getFirstChild();
+
+        switch (type) {
+            case Token.NOT:
+                generateIfJump(child, node, falseLabel, trueLabel);
+                break;
+
+            case Token.OR:
+            case Token.AND:
+                {
+                    int interLabel = cfw.acquireLabel();
+                    if (type == Token.AND) {
+                        generateIfJump(child, node, interLabel, falseLabel);
+                    } else {
+                        generateIfJump(child, node, trueLabel, interLabel);
+                    }
+                    cfw.markLabel(interLabel);
+                    child = child.getNext();
+                    generateIfJump(child, node, trueLabel, falseLabel);
+                    break;
+                }
+
+            case Token.IN:
+            case Token.INSTANCEOF:
+            case Token.LE:
+            case Token.LT:
+            case Token.GE:
+            case Token.GT:
+                visitIfJumpRelOp(node, child, trueLabel, falseLabel);
+                break;
+
+            case Token.EQ:
+            case Token.NE:
+            case Token.SHEQ:
+            case Token.SHNE:
+                visitIfJumpEqOp(node, child, trueLabel, falseLabel);
+                break;
+
+            default:
+                // Generate generic code for non-optimized jump
+                generateExpression(node, parent);
+                addScriptRuntimeInvoke("toBoolean", "(Ljava/lang/Object;)Z");
+                cfw.add(ByteCode.IFNE, trueLabel);
+                cfw.add(ByteCode.GOTO, falseLabel);
+        }
+    }
+
+    private void visitFunction(OptFunctionNode ofn, int functionType) {
+        int fnIndex = codegen.getIndex(ofn.fnode);
+        cfw.add(ByteCode.NEW, codegen.mainClassName);
+        // Call function constructor
+        cfw.add(ByteCode.DUP);
+        cfw.addALoad(variableObjectLocal);
+        cfw.addALoad(contextLocal); // load 'cx'
+        cfw.addPush(fnIndex);
+        cfw.addInvoke(
+                ByteCode.INVOKESPECIAL,
+                codegen.mainClassName,
+                "<init>",
+                Codegen.FUNCTION_CONSTRUCTOR_SIGNATURE);
+
+        if (functionType == FunctionNode.ARROW_FUNCTION) {
+            cfw.addALoad(contextLocal); // load 'cx'
+            cfw.addALoad(variableObjectLocal);
+            cfw.addALoad(thisObjLocal);
+            addOptRuntimeInvoke(
+                    "bindThis",
+                    "(Lorg/mozilla/javascript/NativeFunction;"
+                            + "Lorg/mozilla/javascript/Context;"
+                            + "Lorg/mozilla/javascript/Scriptable;"
+                            + "Lorg/mozilla/javascript/Scriptable;"
+                            + ")Lorg/mozilla/javascript/Function;");
+        }
+
+        if (functionType == FunctionNode.FUNCTION_EXPRESSION
+                || functionType == FunctionNode.ARROW_FUNCTION) {
+            // Leave closure object on stack and do not pass it to
+            // initFunction which suppose to connect statements to scope
+            return;
+        }
+        cfw.addPush(functionType);
+        cfw.addALoad(variableObjectLocal);
+        cfw.addALoad(contextLocal); // load 'cx'
+        addOptRuntimeInvoke(
+                "initFunction",
+                "(Lorg/mozilla/javascript/NativeFunction;"
+                        + "I"
+                        + "Lorg/mozilla/javascript/Scriptable;"
+                        + "Lorg/mozilla/javascript/Context;"
+                        + ")V");
+    }
+
+    private int getTargetLabel(Node target) {
+        int labelId = target.labelId();
+        if (labelId == -1) {
+            labelId = cfw.acquireLabel();
+            target.labelId(labelId);
+        }
+        return labelId;
+    }
+
+    private void visitGoto(Jump node, int type, Node child) {
+        Node target = node.target;
+        if (type == Token.IFEQ || type == Token.IFNE) {
+            if (child == null) throw Codegen.badTree();
+            int targetLabel = getTargetLabel(target);
+            int fallThruLabel = cfw.acquireLabel();
+            if (type == Token.IFEQ) generateIfJump(child, node, targetLabel, fallThruLabel);
+            else generateIfJump(child, node, fallThruLabel, targetLabel);
+            cfw.markLabel(fallThruLabel);
+        } else {
+            if (type == Token.JSR) {
+                if (isGenerator) {
+                    addGotoWithReturn(target);
+                } else {
+                    // This assumes that JSR is only ever used for finally
+                    inlineFinally(target);
+                }
+            } else {
+                addGoto(target, ByteCode.GOTO);
+            }
+        }
+    }
+
+    private void addGotoWithReturn(Node target) {
+        FinallyReturnPoint ret = finallys.get(target);
+        cfw.addLoadConstant(ret.jsrPoints.size());
+        addGoto(target, ByteCode.GOTO);
+        // Don't leave something on the stack here!
+        cfw.add(ByteCode.POP);
+        int retLabel = cfw.acquireLabel();
+        cfw.markLabel(retLabel);
+        ret.jsrPoints.add(Integer.valueOf(retLabel));
+    }
+
+    private void generateArrayLiteralFactory(Node node, int count) {
+        String methodName = codegen.getBodyMethodName(scriptOrFn) + "_literal" + count;
+        initBodyGeneration();
+        argsLocal = firstFreeLocal++;
+        localsMax = firstFreeLocal;
+        cfw.startMethod(
+                methodName,
+                "(Lorg/mozilla/javascript/Context;"
+                        + "Lorg/mozilla/javascript/Scriptable;"
+                        + "Lorg/mozilla/javascript/Scriptable;"
+                        + "[Ljava/lang/Object;"
+                        + ")Lorg/mozilla/javascript/Scriptable;",
+                ACC_PRIVATE);
+        visitArrayLiteral(node, node.getFirstChild(), true);
+        cfw.add(ByteCode.ARETURN);
+        cfw.stopMethod((short) (localsMax + 1));
+    }
+
+    private void generateObjectLiteralFactory(Node node, int count) {
+        String methodName = codegen.getBodyMethodName(scriptOrFn) + "_literal" + count;
+        initBodyGeneration();
+        argsLocal = firstFreeLocal++;
+        localsMax = firstFreeLocal;
+        cfw.startMethod(
+                methodName,
+                "(Lorg/mozilla/javascript/Context;"
+                        + "Lorg/mozilla/javascript/Scriptable;"
+                        + "Lorg/mozilla/javascript/Scriptable;"
+                        + "[Ljava/lang/Object;"
+                        + ")Lorg/mozilla/javascript/Scriptable;",
+                ACC_PRIVATE);
+        visitObjectLiteral(node, node.getFirstChild(), true);
+        cfw.add(ByteCode.ARETURN);
+        cfw.stopMethod((short) (localsMax + 1));
+    }
+
+    private void visitArrayLiteral(Node node, Node child, boolean topLevel) {
+        int count = 0;
+        for (Node cursor = child; cursor != null; cursor = cursor.getNext()) {
+            ++count;
+        }
+
+        // If code budget is tight swap out literals into separate method
+        if (!topLevel
+                && (count > 10 || cfw.getCurrentCodeOffset() > 30000)
+                && !hasVarsInRegs
+                && !isGenerator
+                && !inLocalBlock) {
+            if (literals == null) {
+                literals = new LinkedList<Node>();
+            }
+            literals.add(node);
+            String methodName =
+                    codegen.getBodyMethodName(scriptOrFn) + "_literal" + literals.size();
+            cfw.addALoad(funObjLocal);
+            cfw.addALoad(contextLocal);
+            cfw.addALoad(variableObjectLocal);
+            cfw.addALoad(thisObjLocal);
+            cfw.addALoad(argsLocal);
+            cfw.addInvoke(
+                    ByteCode.INVOKEVIRTUAL,
+                    codegen.mainClassName,
+                    methodName,
+                    "(Lorg/mozilla/javascript/Context;"
+                            + "Lorg/mozilla/javascript/Scriptable;"
+                            + "Lorg/mozilla/javascript/Scriptable;"
+                            + "[Ljava/lang/Object;"
+                            + ")Lorg/mozilla/javascript/Scriptable;");
+            return;
+        }
+
+        // load array to store array literal objects
+        if (isGenerator) {
+            // TODO: this is actually only necessary if the yield operation is
+            // a child of this array or its children (bug 757410)
+            for (int i = 0; i != count; ++i) {
+                generateExpression(child, node);
+                child = child.getNext();
+            }
+            addNewObjectArray(count);
+            for (int i = 0; i != count; ++i) {
+                cfw.add(ByteCode.DUP_X1);
+                cfw.add(ByteCode.SWAP);
+                cfw.addPush(count - i - 1);
+                cfw.add(ByteCode.SWAP);
+                cfw.add(ByteCode.AASTORE);
+            }
+        } else {
+            addNewObjectArray(count);
+            for (int i = 0; i != count; ++i) {
+                cfw.add(ByteCode.DUP);
+                cfw.addPush(i);
+                generateExpression(child, node);
+                cfw.add(ByteCode.AASTORE);
+                child = child.getNext();
+            }
+        }
+        int[] skipIndexes = (int[]) node.getProp(Node.SKIP_INDEXES_PROP);
+        if (skipIndexes == null) {
+            cfw.add(ByteCode.ACONST_NULL);
+            cfw.add(ByteCode.ICONST_0);
+        } else {
+            cfw.addPush(OptRuntime.encodeIntArray(skipIndexes));
+            cfw.addPush(skipIndexes.length);
+        }
+        cfw.addALoad(contextLocal);
+        cfw.addALoad(variableObjectLocal);
+        addOptRuntimeInvoke(
+                "newArrayLiteral",
+                "([Ljava/lang/Object;"
+                        + "Ljava/lang/String;"
+                        + "I"
+                        + "Lorg/mozilla/javascript/Context;"
+                        + "Lorg/mozilla/javascript/Scriptable;"
+                        + ")Lorg/mozilla/javascript/Scriptable;");
+    }
+
+    /** load array with property ids */
+    private void addLoadPropertyIds(Object[] properties, int count) {
+        addNewObjectArray(count);
+        for (int i = 0; i != count; ++i) {
+            cfw.add(ByteCode.DUP);
+            cfw.addPush(i);
+            Object id = properties[i];
+            if (id instanceof String) {
+                cfw.addPush((String) id);
+            } else {
+                cfw.addPush(((Integer) id).intValue());
+                addScriptRuntimeInvoke("wrapInt", "(I)Ljava/lang/Integer;");
+            }
+            cfw.add(ByteCode.AASTORE);
+        }
+    }
+
+    /** load array with property values */
+    private void addLoadPropertyValues(Node node, Node child, int count) {
+        if (isGenerator) {
+            // see bug 757410 for an explanation why we need to split this
+            for (int i = 0; i != count; ++i) {
+                int childType = child.getType();
+                if (childType == Token.GET || childType == Token.SET || childType == Token.METHOD) {
+                    generateExpression(child.getFirstChild(), node);
+                } else {
+                    generateExpression(child, node);
+                }
+                child = child.getNext();
+            }
+            addNewObjectArray(count);
+            for (int i = 0; i != count; ++i) {
+                cfw.add(ByteCode.DUP_X1);
+                cfw.add(ByteCode.SWAP);
+                cfw.addPush(count - i - 1);
+                cfw.add(ByteCode.SWAP);
+                cfw.add(ByteCode.AASTORE);
+            }
+        } else {
+            addNewObjectArray(count);
+            Node child2 = child;
+            for (int i = 0; i != count; ++i) {
+                cfw.add(ByteCode.DUP);
+                cfw.addPush(i);
+                int childType = child2.getType();
+                if (childType == Token.GET || childType == Token.SET || childType == Token.METHOD) {
+                    generateExpression(child2.getFirstChild(), node);
+                } else {
+                    generateExpression(child2, node);
+                }
+                cfw.add(ByteCode.AASTORE);
+                child2 = child2.getNext();
+            }
+        }
+    }
+
+    private void visitObjectLiteral(Node node, Node child, boolean topLevel) {
+        Object[] properties = (Object[]) node.getProp(Node.OBJECT_IDS_PROP);
+        int count = properties == null ? 0 : properties.length;
+
+        // If code budget is tight swap out literals into separate method
+        if (!topLevel
+                && (count > 10 || cfw.getCurrentCodeOffset() > 30000)
+                && !hasVarsInRegs
+                && !isGenerator
+                && !inLocalBlock) {
+            if (literals == null) {
+                literals = new LinkedList<Node>();
+            }
+            literals.add(node);
+            String methodName =
+                    codegen.getBodyMethodName(scriptOrFn) + "_literal" + literals.size();
+            cfw.addALoad(funObjLocal);
+            cfw.addALoad(contextLocal);
+            cfw.addALoad(variableObjectLocal);
+            cfw.addALoad(thisObjLocal);
+            cfw.addALoad(argsLocal);
+            cfw.addInvoke(
+                    ByteCode.INVOKEVIRTUAL,
+                    codegen.mainClassName,
+                    methodName,
+                    "(Lorg/mozilla/javascript/Context;"
+                            + "Lorg/mozilla/javascript/Scriptable;"
+                            + "Lorg/mozilla/javascript/Scriptable;"
+                            + "[Ljava/lang/Object;"
+                            + ")Lorg/mozilla/javascript/Scriptable;");
+            return;
+        }
+
+        if (isGenerator) {
+            // TODO: this is actually only necessary if the yield operation is
+            // a child of this object or its children (bug 757410)
+            addLoadPropertyValues(node, child, count);
+            addLoadPropertyIds(properties, count);
+            // swap property-values and property-ids arrays
+            cfw.add(ByteCode.SWAP);
+        } else {
+            addLoadPropertyIds(properties, count);
+            addLoadPropertyValues(node, child, count);
+        }
+
+        // check if object literal actually has any getters or setters
+        boolean hasGetterSetters = false;
+        Node child2 = child;
+        for (int i = 0; i != count; ++i) {
+            int childType = child2.getType();
+            if (childType == Token.GET || childType == Token.SET) {
+                hasGetterSetters = true;
+                break;
+            }
+            child2 = child2.getNext();
+        }
+        // create getter/setter flag array
+        if (hasGetterSetters) {
+            cfw.addPush(count);
+            cfw.add(ByteCode.NEWARRAY, ByteCode.T_INT);
+            child2 = child;
+            for (int i = 0; i != count; ++i) {
+                cfw.add(ByteCode.DUP);
+                cfw.addPush(i);
+                int childType = child2.getType();
+                if (childType == Token.GET) {
+                    cfw.add(ByteCode.ICONST_M1);
+                } else if (childType == Token.SET) {
+                    cfw.add(ByteCode.ICONST_1);
+                } else {
+                    cfw.add(ByteCode.ICONST_0);
+                }
+                cfw.add(ByteCode.IASTORE);
+                child2 = child2.getNext();
+            }
+        } else {
+            cfw.add(ByteCode.ACONST_NULL);
+        }
+
+        cfw.addALoad(contextLocal);
+        cfw.addALoad(variableObjectLocal);
+        addScriptRuntimeInvoke(
+                "newObjectLiteral",
+                "([Ljava/lang/Object;"
+                        + "[Ljava/lang/Object;"
+                        + "[I"
+                        + "Lorg/mozilla/javascript/Context;"
+                        + "Lorg/mozilla/javascript/Scriptable;"
+                        + ")Lorg/mozilla/javascript/Scriptable;");
+    }
+
+    private void visitSpecialCall(Node node, int type, int specialType, Node child) {
+        cfw.addALoad(contextLocal);
+
+        if (type == Token.NEW) {
+            generateExpression(child, node);
+            // stack: ... cx functionObj
+        } else {
+            generateFunctionAndThisObj(child, node);
+            // stack: ... cx functionObj thisObj
+        }
+        child = child.getNext();
+
+        generateCallArgArray(node, child, false);
+
+        String methodName;
+        String callSignature;
+
+        if (type == Token.NEW) {
+            methodName = "newObjectSpecial";
+            callSignature =
+                    "(Lorg/mozilla/javascript/Context;"
+                            + "Ljava/lang/Object;"
+                            + "[Ljava/lang/Object;"
+                            + "Lorg/mozilla/javascript/Scriptable;"
+                            + "Lorg/mozilla/javascript/Scriptable;"
+                            + "I" // call type
+                            + ")Ljava/lang/Object;";
+            cfw.addALoad(variableObjectLocal);
+            cfw.addALoad(thisObjLocal);
+            cfw.addPush(specialType);
+        } else {
+            methodName = "callSpecial";
+            callSignature =
+                    "(Lorg/mozilla/javascript/Context;"
+                            + "Lorg/mozilla/javascript/Callable;"
+                            + "Lorg/mozilla/javascript/Scriptable;"
+                            + "[Ljava/lang/Object;"
+                            + "Lorg/mozilla/javascript/Scriptable;"
+                            + "Lorg/mozilla/javascript/Scriptable;"
+                            + "I" // call type
+                            + "Ljava/lang/String;I" // filename, linenumber
+                            + ")Ljava/lang/Object;";
+            cfw.addALoad(variableObjectLocal);
+            cfw.addALoad(thisObjLocal);
+            cfw.addPush(specialType);
+            String sourceName = scriptOrFn.getSourceName();
+            cfw.addPush(sourceName == null ? "" : sourceName);
+            cfw.addPush(itsLineNumber);
+        }
+
+        addOptRuntimeInvoke(methodName, callSignature);
+    }
+
+    private void visitStandardCall(Node node, Node child) {
+        if (node.getType() != Token.CALL) throw Codegen.badTree();
+
+        Node firstArgChild = child.getNext();
+        int childType = child.getType();
+
+        String methodName;
+        String signature;
+
+        if (firstArgChild == null) {
+            if (childType == Token.NAME) {
+                // name() call
+                String name = child.getString();
+                cfw.addPush(name);
+                methodName = "callName0";
+                signature =
+                        "(Ljava/lang/String;"
+                                + "Lorg/mozilla/javascript/Context;"
+                                + "Lorg/mozilla/javascript/Scriptable;"
+                                + ")Ljava/lang/Object;";
+            } else if (childType == Token.GETPROP) {
+                // x.name() call
+                Node propTarget = child.getFirstChild();
+                generateExpression(propTarget, node);
+                Node id = propTarget.getNext();
+                String property = id.getString();
+                cfw.addPush(property);
+                methodName = "callProp0";
+                signature =
+                        "(Ljava/lang/Object;"
+                                + "Ljava/lang/String;"
+                                + "Lorg/mozilla/javascript/Context;"
+                                + "Lorg/mozilla/javascript/Scriptable;"
+                                + ")Ljava/lang/Object;";
+            } else if (childType == Token.GETPROPNOWARN) {
+                throw Kit.codeBug();
+            } else {
+                generateFunctionAndThisObj(child, node);
+                methodName = "call0";
+                signature =
+                        "(Lorg/mozilla/javascript/Callable;"
+                                + "Lorg/mozilla/javascript/Scriptable;"
+                                + "Lorg/mozilla/javascript/Context;"
+                                + "Lorg/mozilla/javascript/Scriptable;"
+                                + ")Ljava/lang/Object;";
+            }
+
+        } else if (childType == Token.NAME) {
+            // XXX: this optimization is only possible if name
+            // resolution
+            // is not affected by arguments evaluation and currently
+            // there are no checks for it
+            String name = child.getString();
+            generateCallArgArray(node, firstArgChild, false);
+            cfw.addPush(name);
+            methodName = "callName";
+            signature =
+                    "([Ljava/lang/Object;"
+                            + "Ljava/lang/String;"
+                            + "Lorg/mozilla/javascript/Context;"
+                            + "Lorg/mozilla/javascript/Scriptable;"
+                            + ")Ljava/lang/Object;";
+        } else {
+            int argCount = 0;
+            for (Node arg = firstArgChild; arg != null; arg = arg.getNext()) {
+                ++argCount;
+            }
+            generateFunctionAndThisObj(child, node);
+            // stack: ... functionObj thisObj
+            if (argCount == 1) {
+                generateExpression(firstArgChild, node);
+                methodName = "call1";
+                signature =
+                        "(Lorg/mozilla/javascript/Callable;"
+                                + "Lorg/mozilla/javascript/Scriptable;"
+                                + "Ljava/lang/Object;"
+                                + "Lorg/mozilla/javascript/Context;"
+                                + "Lorg/mozilla/javascript/Scriptable;"
+                                + ")Ljava/lang/Object;";
+            } else if (argCount == 2) {
+                generateExpression(firstArgChild, node);
+                generateExpression(firstArgChild.getNext(), node);
+                methodName = "call2";
+                signature =
+                        "(Lorg/mozilla/javascript/Callable;"
+                                + "Lorg/mozilla/javascript/Scriptable;"
+                                + "Ljava/lang/Object;"
+                                + "Ljava/lang/Object;"
+                                + "Lorg/mozilla/javascript/Context;"
+                                + "Lorg/mozilla/javascript/Scriptable;"
+                                + ")Ljava/lang/Object;";
+            } else {
+                generateCallArgArray(node, firstArgChild, false);
+                methodName = "callN";
+                signature =
+                        "(Lorg/mozilla/javascript/Callable;"
+                                + "Lorg/mozilla/javascript/Scriptable;"
+                                + "[Ljava/lang/Object;"
+                                + "Lorg/mozilla/javascript/Context;"
+                                + "Lorg/mozilla/javascript/Scriptable;"
+                                + ")Ljava/lang/Object;";
+            }
+        }
+
+        cfw.addALoad(contextLocal);
+        cfw.addALoad(variableObjectLocal);
+        addOptRuntimeInvoke(methodName, signature);
+    }
+
+    private void visitStandardNew(Node node, Node child) {
+        if (node.getType() != Token.NEW) throw Codegen.badTree();
+
+        Node firstArgChild = child.getNext();
+
+        generateExpression(child, node);
+        // stack: ... functionObj
+        cfw.addALoad(contextLocal);
+        cfw.addALoad(variableObjectLocal);
+        // stack: ... functionObj cx scope
+        generateCallArgArray(node, firstArgChild, false);
+        addScriptRuntimeInvoke(
+                "newObject",
+                "(Ljava/lang/Object;"
+                        + "Lorg/mozilla/javascript/Context;"
+                        + "Lorg/mozilla/javascript/Scriptable;"
+                        + "[Ljava/lang/Object;"
+                        + ")Lorg/mozilla/javascript/Scriptable;");
+    }
+
+    private void visitOptimizedCall(Node node, OptFunctionNode target, int type, Node child) {
+        Node firstArgChild = child.getNext();
+        String className = codegen.mainClassName;
+
+        short thisObjLocal = 0;
+        if (type == Token.NEW) {
+            generateExpression(child, node);
+        } else {
+            generateFunctionAndThisObj(child, node);
+            thisObjLocal = getNewWordLocal();
+            cfw.addAStore(thisObjLocal);
+        }
+        // stack: ... functionObj
+
+        int beyond = cfw.acquireLabel();
+        int regularCall = cfw.acquireLabel();
+
+        cfw.add(ByteCode.DUP);
+        cfw.add(ByteCode.INSTANCEOF, className);
+        cfw.add(ByteCode.IFEQ, regularCall);
+        cfw.add(ByteCode.CHECKCAST, className);
+        cfw.add(ByteCode.DUP);
+        cfw.add(ByteCode.GETFIELD, className, Codegen.ID_FIELD_NAME, "I");
+        cfw.addPush(codegen.getIndex(target.fnode));
+        cfw.add(ByteCode.IF_ICMPNE, regularCall);
+
+        // stack: ... directFunct
+        cfw.addALoad(contextLocal);
+        cfw.addALoad(variableObjectLocal);
+        // stack: ... directFunc cx scope
+
+        if (type == Token.NEW) {
+            cfw.add(ByteCode.ACONST_NULL);
+        } else {
+            cfw.addALoad(thisObjLocal);
+        }
+        // stack: ... directFunc cx scope thisObj
+        /*
+        Remember that directCall parameters are paired in 1 aReg and 1 dReg
+        If the argument is an incoming arg, just pass the orginal pair thru.
+        Else, if the argument is known to be typed 'Number', pass Void.TYPE
+        in the aReg and the number is the dReg
+        Else pass the JS object in the aReg and 0.0 in the dReg.
+        */
+        Node argChild = firstArgChild;
+        while (argChild != null) {
+            int dcp_register = nodeIsDirectCallParameter(argChild);
+            if (dcp_register >= 0) {
+                cfw.addALoad(dcp_register);
+                cfw.addDLoad(dcp_register + 1);
+            } else if (argChild.getIntProp(Node.ISNUMBER_PROP, -1) == Node.BOTH) {
+                cfw.add(ByteCode.GETSTATIC, "java/lang/Void", "TYPE", "Ljava/lang/Class;");
+                generateExpression(argChild, node);
+            } else {
+                generateExpression(argChild, node);
+                cfw.addPush(0.0);
+            }
+            argChild = argChild.getNext();
+        }
+
+        cfw.add(
+                ByteCode.GETSTATIC,
+                "org/mozilla/javascript/ScriptRuntime",
+                "emptyArgs",
+                "[Ljava/lang/Object;");
+        cfw.addInvoke(
+                ByteCode.INVOKESTATIC,
+                codegen.mainClassName,
+                (type == Token.NEW)
+                        ? codegen.getDirectCtorName(target.fnode)
+                        : codegen.getBodyMethodName(target.fnode),
+                codegen.getBodyMethodSignature(target.fnode));
+
+        cfw.add(ByteCode.GOTO, beyond);
+
+        cfw.markLabel(regularCall);
+        // stack: ... functionObj
+        cfw.addALoad(contextLocal);
+        cfw.addALoad(variableObjectLocal);
+        // stack: ... functionObj cx scope
+        if (type != Token.NEW) {
+            cfw.addALoad(thisObjLocal);
+            releaseWordLocal(thisObjLocal);
+            // stack: ... functionObj cx scope thisObj
+        }
+        // XXX: this will generate code for the child array the second time,
+        // so expression code generation better not to alter tree structure...
+        generateCallArgArray(node, firstArgChild, true);
+
+        if (type == Token.NEW) {
+            addScriptRuntimeInvoke(
+                    "newObject",
+                    "(Ljava/lang/Object;"
+                            + "Lorg/mozilla/javascript/Context;"
+                            + "Lorg/mozilla/javascript/Scriptable;"
+                            + "[Ljava/lang/Object;"
+                            + ")Lorg/mozilla/javascript/Scriptable;");
+        } else {
+            cfw.addInvoke(
+                    ByteCode.INVOKEINTERFACE,
+                    "org/mozilla/javascript/Callable",
+                    "call",
+                    "(Lorg/mozilla/javascript/Context;"
+                            + "Lorg/mozilla/javascript/Scriptable;"
+                            + "Lorg/mozilla/javascript/Scriptable;"
+                            + "[Ljava/lang/Object;"
+                            + ")Ljava/lang/Object;");
+        }
+
+        cfw.markLabel(beyond);
+    }
+
+    private void generateCallArgArray(Node node, Node argChild, boolean directCall) {
+        int argCount = 0;
+        for (Node child = argChild; child != null; child = child.getNext()) {
+            ++argCount;
+        }
+        // load array object to set arguments
+        if (argCount == 1 && itsOneArgArray >= 0) {
+            cfw.addALoad(itsOneArgArray);
+        } else {
+            addNewObjectArray(argCount);
+        }
+        // Copy arguments into it
+        for (int i = 0; i != argCount; ++i) {
+            // If we are compiling a generator an argument could be the result
+            // of a yield. In that case we will have an immediate on the stack
+            // which we need to avoid
+            if (!isGenerator) {
+                cfw.add(ByteCode.DUP);
+                cfw.addPush(i);
+            }
+
+            if (!directCall) {
+                generateExpression(argChild, node);
+            } else {
+                // If this has also been a directCall sequence, the Number
+                // flag will have remained set for any parameter so that
+                // the values could be copied directly into the outgoing
+                // args. Here we want to force it to be treated as not in
+                // a Number context, so we set the flag off.
+                int dcp_register = nodeIsDirectCallParameter(argChild);
+                if (dcp_register >= 0) {
+                    dcpLoadAsObject(dcp_register);
+                } else {
+                    generateExpression(argChild, node);
+                    int childNumberFlag = argChild.getIntProp(Node.ISNUMBER_PROP, -1);
+                    if (childNumberFlag == Node.BOTH) {
+                        addDoubleWrap();
+                    }
+                }
+            }
+
+            // When compiling generators, any argument to a method may be a
+            // yield expression. Hence we compile the argument first and then
+            // load the argument index and assign the value to the args array.
+            if (isGenerator) {
+                short tempLocal = getNewWordLocal();
+                cfw.addAStore(tempLocal);
+                cfw.add(ByteCode.CHECKCAST, "[Ljava/lang/Object;");
+                cfw.add(ByteCode.DUP);
+                cfw.addPush(i);
+                cfw.addALoad(tempLocal);
+                releaseWordLocal(tempLocal);
+            }
+
+            cfw.add(ByteCode.AASTORE);
+
+            argChild = argChild.getNext();
+        }
+    }
+
+    private void generateFunctionAndThisObj(Node node, Node parent) {
+        // Place on stack (function object, function this) pair
+        int type = node.getType();
+        switch (node.getType()) {
+            case Token.GETPROPNOWARN:
+                throw Kit.codeBug();
+
+            case Token.GETPROP:
+            case Token.GETELEM:
+                {
+                    Node target = node.getFirstChild();
+                    generateExpression(target, node);
+                    Node id = target.getNext();
+                    if (type == Token.GETPROP) {
+                        String property = id.getString();
+                        cfw.addPush(property);
+                        cfw.addALoad(contextLocal);
+                        cfw.addALoad(variableObjectLocal);
+                        addScriptRuntimeInvoke(
+                                "getPropFunctionAndThis",
+                                "(Ljava/lang/Object;"
+                                        + "Ljava/lang/String;"
+                                        + "Lorg/mozilla/javascript/Context;"
+                                        + "Lorg/mozilla/javascript/Scriptable;"
+                                        + ")Lorg/mozilla/javascript/Callable;");
+                    } else {
+                        generateExpression(id, node); // id
+                        if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) addDoubleWrap();
+                        cfw.addALoad(contextLocal);
+                        cfw.addALoad(variableObjectLocal);
+                        addScriptRuntimeInvoke(
+                                "getElemFunctionAndThis",
+                                "(Ljava/lang/Object;"
+                                        + "Ljava/lang/Object;"
+                                        + "Lorg/mozilla/javascript/Context;"
+                                        + "Lorg/mozilla/javascript/Scriptable;"
+                                        + ")Lorg/mozilla/javascript/Callable;");
+                    }
+                    break;
+                }
+
+            case Token.NAME:
+                {
+                    String name = node.getString();
+                    cfw.addPush(name);
+                    cfw.addALoad(contextLocal);
+                    cfw.addALoad(variableObjectLocal);
+                    addScriptRuntimeInvoke(
+                            "getNameFunctionAndThis",
+                            "(Ljava/lang/String;"
+                                    + "Lorg/mozilla/javascript/Context;"
+                                    + "Lorg/mozilla/javascript/Scriptable;"
+                                    + ")Lorg/mozilla/javascript/Callable;");
+                    break;
+                }
+
+            default: // including GETVAR
+                generateExpression(node, parent);
+                cfw.addALoad(contextLocal);
+                addScriptRuntimeInvoke(
+                        "getValueFunctionAndThis",
+                        "(Ljava/lang/Object;"
+                                + "Lorg/mozilla/javascript/Context;"
+                                + ")Lorg/mozilla/javascript/Callable;");
+                break;
+        }
+        // Get thisObj prepared by get(Name|Prop|Elem|Value)FunctionAndThis
+        cfw.addALoad(contextLocal);
+        addScriptRuntimeInvoke(
+                "lastStoredScriptable",
+                "(Lorg/mozilla/javascript/Context;" + ")Lorg/mozilla/javascript/Scriptable;");
+    }
+
+    private void updateLineNumber(Node node) {
+        itsLineNumber = node.getLineno();
+        if (itsLineNumber == -1) return;
+        cfw.addLineNumberEntry((short) itsLineNumber);
+    }
+
+    private void visitTryCatchFinally(Jump node, Node child) {
+        /* Save the variable object, in case there are with statements
+         * enclosed by the try block and we catch some exception.
+         * We'll restore it for the catch block so that catch block
+         * statements get the right scope.
+         */
+
+        // OPT we only need to do this if there are enclosed WITH
+        // statements; could statically check and omit this if there aren't any.
+
+        // XXX OPT Maybe instead do syntactic transforms to associate
+        // each 'with' with a try/finally block that does the exitwith.
+
+        short savedVariableObject = getNewWordLocal();
+        cfw.addALoad(variableObjectLocal);
+        cfw.addAStore(savedVariableObject);
+
+        /*
+         * Generate the code for the tree; most of the work is done in IRFactory
+         * and NodeTransformer;  Codegen just adds the java handlers for the
+         * javascript catch and finally clauses.  */
+
+        int startLabel = cfw.acquireLabel();
+        cfw.markLabel(startLabel, (short) 0);
+
+        Node catchTarget = node.target;
+        Node finallyTarget = node.getFinally();
+        int[] handlerLabels = new int[EXCEPTION_MAX];
+
+        exceptionManager.pushExceptionInfo(node);
+        if (catchTarget != null) {
+            handlerLabels[JAVASCRIPT_EXCEPTION] = cfw.acquireLabel();
+            handlerLabels[EVALUATOR_EXCEPTION] = cfw.acquireLabel();
+            handlerLabels[ECMAERROR_EXCEPTION] = cfw.acquireLabel();
+            Context cx = Context.getCurrentContext();
+            if (cx != null && cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)) {
+                handlerLabels[THROWABLE_EXCEPTION] = cfw.acquireLabel();
+            }
+        }
+        if (finallyTarget != null) {
+            handlerLabels[FINALLY_EXCEPTION] = cfw.acquireLabel();
+        }
+        exceptionManager.setHandlers(handlerLabels, startLabel);
+
+        // create a table for the equivalent of JSR returns
+        if (isGenerator && finallyTarget != null) {
+            FinallyReturnPoint ret = new FinallyReturnPoint();
+            if (finallys == null) {
+                finallys = new HashMap<Node, FinallyReturnPoint>();
+            }
+            // add the finally target to hashtable
+            finallys.put(finallyTarget, ret);
+            // add the finally node as well to the hash table
+            finallys.put(finallyTarget.getNext(), ret);
+        }
+
+        while (child != null) {
+            if (child == catchTarget) {
+                int catchLabel = getTargetLabel(catchTarget);
+                exceptionManager.removeHandler(JAVASCRIPT_EXCEPTION, catchLabel);
+                exceptionManager.removeHandler(EVALUATOR_EXCEPTION, catchLabel);
+                exceptionManager.removeHandler(ECMAERROR_EXCEPTION, catchLabel);
+                exceptionManager.removeHandler(THROWABLE_EXCEPTION, catchLabel);
+            }
+            generateStatement(child);
+            child = child.getNext();
+        }
+
+        // control flow skips the handlers
+        int realEnd = cfw.acquireLabel();
+        cfw.add(ByteCode.GOTO, realEnd);
+
+        int exceptionLocal = getLocalBlockRegister(node);
+        // javascript handler; unwrap exception and GOTO to javascript
+        // catch area.
+        if (catchTarget != null) {
+            // get the label to goto
+            int catchLabel = catchTarget.labelId();
+
+            // If the function is a generator, then handlerLabels will consist
+            // of zero labels. generateCatchBlock will create its own label
+            // in this case. The extra parameter for the label is added for
+            // the case of non-generator functions that inline finally blocks.
+
+            generateCatchBlock(
+                    JAVASCRIPT_EXCEPTION,
+                    savedVariableObject,
+                    catchLabel,
+                    exceptionLocal,
+                    handlerLabels[JAVASCRIPT_EXCEPTION]);
+            /*
+             * catch WrappedExceptions, see if they are wrapped
+             * JavaScriptExceptions. Otherwise, rethrow.
+             */
+            generateCatchBlock(
+                    EVALUATOR_EXCEPTION,
+                    savedVariableObject,
+                    catchLabel,
+                    exceptionLocal,
+                    handlerLabels[EVALUATOR_EXCEPTION]);
+
+            /*
+                we also need to catch EcmaErrors and feed the
+                associated error object to the handler
+            */
+            generateCatchBlock(
+                    ECMAERROR_EXCEPTION,
+                    savedVariableObject,
+                    catchLabel,
+                    exceptionLocal,
+                    handlerLabels[ECMAERROR_EXCEPTION]);
+
+            Context cx = Context.getCurrentContext();
+            if (cx != null && cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)) {
+                generateCatchBlock(
+                        THROWABLE_EXCEPTION,
+                        savedVariableObject,
+                        catchLabel,
+                        exceptionLocal,
+                        handlerLabels[THROWABLE_EXCEPTION]);
+            }
+        }
+
+        // finally handler; catch all exceptions, store to a local; JSR to
+        // the finally, then re-throw.
+        if (finallyTarget != null) {
+            int finallyHandler = cfw.acquireLabel();
+            int finallyEnd = cfw.acquireLabel();
+            cfw.markHandler(finallyHandler);
+            if (!isGenerator) {
+                cfw.markLabel(handlerLabels[FINALLY_EXCEPTION]);
+            }
+            cfw.addAStore(exceptionLocal);
+
+            // reset the variable object local
+            cfw.addALoad(savedVariableObject);
+            cfw.addAStore(variableObjectLocal);
+
+            // get the label to JSR to
+            int finallyLabel = finallyTarget.labelId();
+            if (isGenerator) addGotoWithReturn(finallyTarget);
+            else {
+                inlineFinally(finallyTarget, handlerLabels[FINALLY_EXCEPTION], finallyEnd);
+            }
+
+            // rethrow
+            cfw.addALoad(exceptionLocal);
+            if (isGenerator) cfw.add(ByteCode.CHECKCAST, "java/lang/Throwable");
+            cfw.add(ByteCode.ATHROW);
+
+            cfw.markLabel(finallyEnd);
+            // mark the handler
+            if (isGenerator) {
+                cfw.addExceptionHandler(
+                        startLabel, finallyLabel, finallyHandler, null); // catch any
+            }
+        }
+        releaseWordLocal(savedVariableObject);
+        cfw.markLabel(realEnd);
+
+        if (!isGenerator) {
+            exceptionManager.popExceptionInfo();
+        }
+    }
+
+    private static final int JAVASCRIPT_EXCEPTION = 0;
+    private static final int EVALUATOR_EXCEPTION = 1;
+    private static final int ECMAERROR_EXCEPTION = 2;
+    private static final int THROWABLE_EXCEPTION = 3;
+    // Finally catch-alls are technically Throwable, but we want a distinction
+    // for the exception manager and we want to use a null string instead of
+    // an explicit Throwable string.
+    private static final int FINALLY_EXCEPTION = 4;
+    private static final int EXCEPTION_MAX = 5;
+
+    private void generateCatchBlock(
+            int exceptionType,
+            short savedVariableObject,
+            int catchLabel,
+            int exceptionLocal,
+            int handler) {
+        if (handler == 0) {
+            handler = cfw.acquireLabel();
+        }
+        cfw.markHandler(handler);
+
+        // MS JVM gets cranky if the exception object is left on the stack
+        cfw.addAStore(exceptionLocal);
+
+        // reset the variable object local
+        cfw.addALoad(savedVariableObject);
+        cfw.addAStore(variableObjectLocal);
+
+        cfw.add(ByteCode.GOTO, catchLabel);
+    }
+
+    private static String exceptionTypeToName(int exceptionType) {
+        if (exceptionType == JAVASCRIPT_EXCEPTION) {
+            return "org/mozilla/javascript/JavaScriptException";
+        } else if (exceptionType == EVALUATOR_EXCEPTION) {
+            return "org/mozilla/javascript/EvaluatorException";
+        } else if (exceptionType == ECMAERROR_EXCEPTION) {
+            return "org/mozilla/javascript/EcmaError";
+        } else if (exceptionType == THROWABLE_EXCEPTION) {
+            return "java/lang/Throwable";
+        } else if (exceptionType == FINALLY_EXCEPTION) {
+            return null;
+        } else {
+            throw Kit.codeBug();
+        }
+    }
+
+    /**
+     * Manages placement of exception handlers for non-generator functions.
+     *
+     * <p>For generator functions, there are mechanisms put into place to emulate jsr by using a
+     * goto with a return label. That is one mechanism for implementing finally blocks. The other,
+     * which is implemented by Sun, involves duplicating the finally block where jsr instructions
+     * would normally be. However, inlining finally blocks causes problems with translating
+     * exception handlers. Instead of having one big bytecode range for each exception, we now have
+     * to skip over the inlined finally blocks. This class is meant to help implement this.
+     *
+     * <p>Every time a try block is encountered during translation, exception information should be
+     * pushed into the manager, which is treated as a stack. The addHandler() and setHandlers()
+     * methods may be used to register exceptionHandlers for the try block; removeHandler() is used
+     * to reverse the operation. At the end of the try/catch/finally, the exception state for it
+     * should be popped.
+     *
+     * <p>The important function here is markInlineFinally. This finds which finally block on the
+     * exception state stack is being inlined and skips the proper exception handlers until the
+     * finally block is generated.
+     */
+    private class ExceptionManager {
+        ExceptionManager() {
+            exceptionInfo = new LinkedList<ExceptionInfo>();
+        }
+
+        /**
+         * Push a new try block onto the exception information stack.
+         *
+         * @param node an exception handling node (node.getType() == Token.TRY)
+         */
+        void pushExceptionInfo(Jump node) {
+            Node fBlock = getFinallyAtTarget(node.getFinally());
+            ExceptionInfo ei = new ExceptionInfo(node, fBlock);
+            exceptionInfo.add(ei);
+        }
+
+        /**
+         * Register an exception handler for the try block at the top of the exception information
+         * stack.
+         *
+         * @param exceptionType one of the integer constants representing an exception type
+         * @param handlerLabel the label of the exception handler
+         * @param startLabel the label where the exception handling begins
+         */
+        void addHandler(int exceptionType, int handlerLabel, int startLabel) {
+            ExceptionInfo top = getTop();
+            top.handlerLabels[exceptionType] = handlerLabel;
+            top.exceptionStarts[exceptionType] = startLabel;
+        }
+
+        /**
+         * Register multiple exception handlers for the top try block. If the exception type maps to
+         * a zero label, then it is ignored.
+         *
+         * @param handlerLabels a map from integer constants representing an exception type to the
+         *     label of the exception handler
+         * @param startLabel the label where all of the exception handling begins
+         */
+        void setHandlers(int[] handlerLabels, int startLabel) {
+            for (int i = 0; i < handlerLabels.length; i++) {
+                if (handlerLabels[i] != 0) {
+                    addHandler(i, handlerLabels[i], startLabel);
+                }
+            }
+        }
+
+        /**
+         * Remove an exception handler for the top try block.
+         *
+         * @param exceptionType one of the integer constants representing an exception type
+         * @param endLabel a label representing the end of the last bytecode that should be handled
+         *     by the exception
+         * @returns the label of the exception handler associated with the exception type
+         */
+        int removeHandler(int exceptionType, int endLabel) {
+            ExceptionInfo top = getTop();
+            if (top.handlerLabels[exceptionType] != 0) {
+                int handlerLabel = top.handlerLabels[exceptionType];
+                endCatch(top, exceptionType, endLabel);
+                top.handlerLabels[exceptionType] = 0;
+                return handlerLabel;
+            }
+            return 0;
+        }
+
+        /** Remove the top try block from the exception information stack. */
+        void popExceptionInfo() {
+            exceptionInfo.removeLast();
+        }
+
+        /**
+         * Mark the start of an inlined finally block.
+         *
+         * <p>When a finally block is inlined, any exception handlers that are lexically inside of
+         * its try block should not cover the range of the exception block. We scan from the
+         * innermost try block outward until we find the try block that matches the finally block.
+         * For any block whose exception handlers that aren't currently stopped by a finally block,
+         * we stop the handlers at the beginning of the finally block and set it as the finally
+         * block that has stopped the handlers. This prevents other inlined finally blocks from
+         * prematurely ending skip ranges and creating bad exception handler ranges.
+         *
+         * @param finallyBlock the finally block that is being inlined
+         * @param finallyStart the label of the beginning of the inlined code
+         */
+        void markInlineFinallyStart(Node finallyBlock, int finallyStart) {
+            // Traverse the stack in LIFO order until the try block
+            // corresponding to the finally block has been reached. We must
+            // traverse backwards because the earlier exception handlers in
+            // the exception handler table have priority when determining which
+            // handler to use. Therefore, we start with the most nested try
+            // block and move outward.
+            ListIterator<ExceptionInfo> iter = exceptionInfo.listIterator(exceptionInfo.size());
+            while (iter.hasPrevious()) {
+                ExceptionInfo ei = iter.previous();
+                for (int i = 0; i < EXCEPTION_MAX; i++) {
+                    if (ei.handlerLabels[i] != 0 && ei.currentFinally == null) {
+                        endCatch(ei, i, finallyStart);
+                        ei.exceptionStarts[i] = 0;
+                        ei.currentFinally = finallyBlock;
+                    }
+                }
+                if (ei.finallyBlock == finallyBlock) {
+                    break;
+                }
+            }
+        }
+
+        /**
+         * Mark the end of an inlined finally block.
+         *
+         * <p>For any set of exception handlers that have been stopped by the inlined block, resume
+         * exception handling at the end of the finally block.
+         *
+         * @param finallyBlock the finally block that is being inlined
+         * @param finallyEnd the label of the end of the inlined code
+         */
+        void markInlineFinallyEnd(Node finallyBlock, int finallyEnd) {
+            ListIterator<ExceptionInfo> iter = exceptionInfo.listIterator(exceptionInfo.size());
+            while (iter.hasPrevious()) {
+                ExceptionInfo ei = iter.previous();
+                for (int i = 0; i < EXCEPTION_MAX; i++) {
+                    if (ei.handlerLabels[i] != 0 && ei.currentFinally == finallyBlock) {
+                        ei.exceptionStarts[i] = finallyEnd;
+                        ei.currentFinally = null;
+                    }
+                }
+                if (ei.finallyBlock == finallyBlock) {
+                    break;
+                }
+            }
+        }
+
+        /**
+         * Mark off the end of a bytecode chunk that should be handled by an exceptionHandler.
+         *
+         * <p>The caller of this method must appropriately mark the start of the next bytecode chunk
+         * or remove the handler.
+         */
+        private void endCatch(ExceptionInfo ei, int exceptionType, int catchEnd) {
+            if (ei.exceptionStarts[exceptionType] == 0) {
+                throw new IllegalStateException("bad exception start");
+            }
+
+            int currentStart = ei.exceptionStarts[exceptionType];
+            int currentStartPC = cfw.getLabelPC(currentStart);
+            int catchEndPC = cfw.getLabelPC(catchEnd);
+            if (currentStartPC != catchEndPC) {
+                cfw.addExceptionHandler(
+                        ei.exceptionStarts[exceptionType],
+                        catchEnd,
+                        ei.handlerLabels[exceptionType],
+                        exceptionTypeToName(exceptionType));
+            }
+        }
+
+        private ExceptionInfo getTop() {
+            return exceptionInfo.getLast();
+        }
+
+        private class ExceptionInfo {
+            ExceptionInfo(Jump node, Node finallyBlock) {
+                this.finallyBlock = finallyBlock;
+                handlerLabels = new int[EXCEPTION_MAX];
+                exceptionStarts = new int[EXCEPTION_MAX];
+                currentFinally = null;
+            }
+
+            Node finallyBlock;
+            int[] handlerLabels;
+            int[] exceptionStarts;
+            // The current finally block that has temporarily ended the
+            // exception handler ranges
+            Node currentFinally;
+        }
+
+        // A stack of try/catch block information ordered by lexical scoping
+        private LinkedList<ExceptionInfo> exceptionInfo;
+    }
+
+    private ExceptionManager exceptionManager = new ExceptionManager();
+
+    /**
+     * Inline a FINALLY node into the method bytecode.
+     *
+     * <p>This method takes a label that points to the real start of the finally block as
+     * implemented in the bytecode. This is because in some cases, the finally block really starts
+     * before any of the code in the Node. For example, the catch-all-rethrow finally block has a
+     * few instructions prior to the finally block made by the user.
+     *
+     * <p>In addition, an end label that should be unmarked is given as a method parameter. It is
+     * the responsibility of any callers of this method to mark the label.
+     *
+     * <p>The start and end labels of the finally block are used to exclude the inlined block from
+     * the proper exception handler. For example, an inlined finally block should not be handled by
+     * a catch-all-rethrow.
+     *
+     * @param finallyTarget a TARGET node directly preceding a FINALLY node or a FINALLY node itself
+     * @param finallyStart a pre-marked label that indicates the actual start of the finally block
+     *     in the bytecode.
+     * @param finallyEnd an unmarked label that will indicate the actual end of the finally block in
+     *     the bytecode.
+     */
+    private void inlineFinally(Node finallyTarget, int finallyStart, int finallyEnd) {
+        Node fBlock = getFinallyAtTarget(finallyTarget);
+        fBlock.resetTargets();
+        Node child = fBlock.getFirstChild();
+        exceptionManager.markInlineFinallyStart(fBlock, finallyStart);
+        while (child != null) {
+            generateStatement(child);
+            child = child.getNext();
+        }
+        exceptionManager.markInlineFinallyEnd(fBlock, finallyEnd);
+    }
+
+    private void inlineFinally(Node finallyTarget) {
+        int finallyStart = cfw.acquireLabel();
+        int finallyEnd = cfw.acquireLabel();
+        cfw.markLabel(finallyStart);
+        inlineFinally(finallyTarget, finallyStart, finallyEnd);
+        cfw.markLabel(finallyEnd);
+    }
+
+    /**
+     * Get a FINALLY node at a point in the IR.
+     *
+     * <p>This is strongly dependent on the generated IR. If the node is a TARGET, it only check the
+     * next node to see if it is a FINALLY node.
+     */
+    private static Node getFinallyAtTarget(Node node) {
+        if (node == null) {
+            return null;
+        }
+        if (node.getType() == Token.FINALLY) {
+            return node;
+        }
+        if (node.getType() == Token.TARGET) {
+            Node fBlock = node.getNext();
+            if (fBlock != null && fBlock.getType() == Token.FINALLY) {
+                return fBlock;
+            }
+        }
+        throw Kit.codeBug("bad finally target");
+    }
+
+    private boolean generateSaveLocals(Node node) {
+        int count = 0;
+        for (int i = 0; i < firstFreeLocal; i++) {
+            if (locals[i] != 0) count++;
+        }
+
+        if (count == 0) {
+            ((FunctionNode) scriptOrFn).addLiveLocals(node, null);
+            return false;
+        }
+
+        // calculate the max locals
+        maxLocals = maxLocals > count ? maxLocals : count;
+
+        // create a locals list
+        int[] ls = new int[count];
+        int s = 0;
+        for (int i = 0; i < firstFreeLocal; i++) {
+            if (locals[i] != 0) {
+                ls[s] = i;
+                s++;
+            }
+        }
+
+        // save the locals
+        ((FunctionNode) scriptOrFn).addLiveLocals(node, ls);
+
+        // save locals
+        generateGetGeneratorLocalsState();
+        for (int i = 0; i < count; i++) {
+            cfw.add(ByteCode.DUP);
+            cfw.addLoadConstant(i);
+            cfw.addALoad(ls[i]);
+            cfw.add(ByteCode.AASTORE);
+        }
+        // pop the array off the stack
+        cfw.add(ByteCode.POP);
+
+        return true;
+    }
+
+    private void visitSwitch(Jump switchNode, Node child) {
+        // See comments in IRFactory.createSwitch() for description
+        // of SWITCH node
+
+        generateExpression(child, switchNode);
+        // save selector value
+        short selector = getNewWordLocal();
+        cfw.addAStore(selector);
+
+        for (Jump caseNode = (Jump) child.getNext();
+                caseNode != null;
+                caseNode = (Jump) caseNode.getNext()) {
+            if (caseNode.getType() != Token.CASE) throw Codegen.badTree();
+            Node test = caseNode.getFirstChild();
+            generateExpression(test, caseNode);
+            cfw.addALoad(selector);
+            addScriptRuntimeInvoke(
+                    "shallowEq", "(Ljava/lang/Object;" + "Ljava/lang/Object;" + ")Z");
+            addGoto(caseNode.target, ByteCode.IFNE);
+        }
+        releaseWordLocal(selector);
+    }
+
+    private void visitTypeofname(Node node) {
+        if (hasVarsInRegs) {
+            int varIndex = fnCurrent.fnode.getIndexForNameNode(node);
+            if (varIndex >= 0) {
+                if (fnCurrent.isNumberVar(varIndex)) {
+                    cfw.addPush("number");
+                } else if (varIsDirectCallParameter(varIndex)) {
+                    int dcp_register = varRegisters[varIndex];
+                    cfw.addALoad(dcp_register);
+                    cfw.add(ByteCode.GETSTATIC, "java/lang/Void", "TYPE", "Ljava/lang/Class;");
+                    int isNumberLabel = cfw.acquireLabel();
+                    cfw.add(ByteCode.IF_ACMPEQ, isNumberLabel);
+                    short stack = cfw.getStackTop();
+                    cfw.addALoad(dcp_register);
+                    addScriptRuntimeInvoke("typeof", "(Ljava/lang/Object;" + ")Ljava/lang/String;");
+                    int beyond = cfw.acquireLabel();
+                    cfw.add(ByteCode.GOTO, beyond);
+                    cfw.markLabel(isNumberLabel, stack);
+                    cfw.addPush("number");
+                    cfw.markLabel(beyond);
+                } else {
+                    cfw.addALoad(varRegisters[varIndex]);
+                    addScriptRuntimeInvoke("typeof", "(Ljava/lang/Object;" + ")Ljava/lang/String;");
+                }
+                return;
+            }
+        }
+        cfw.addALoad(variableObjectLocal);
+        cfw.addPush(node.getString());
+        addScriptRuntimeInvoke(
+                "typeofName",
+                "(Lorg/mozilla/javascript/Scriptable;"
+                        + "Ljava/lang/String;"
+                        + ")Ljava/lang/String;");
+    }
+
+    /**
+     * Save the current code offset. This saved code offset is used to compute instruction counts in
+     * subsequent calls to {@link #addInstructionCount()}.
+     */
+    private void saveCurrentCodeOffset() {
+        savedCodeOffset = cfw.getCurrentCodeOffset();
+    }
+
+    /**
+     * Generate calls to ScriptRuntime.addInstructionCount to keep track of executed instructions
+     * and call <code>observeInstructionCount()</code> if a threshold is exceeded.<br>
+     * Calculates the count from getCurrentCodeOffset - savedCodeOffset
+     */
+    private void addInstructionCount() {
+        int count = cfw.getCurrentCodeOffset() - savedCodeOffset;
+        // TODO we used to return for count == 0 but that broke the following:
+        //    while(true) continue; (see bug 531600)
+        // To be safe, we now always count at least 1 instruction when invoked.
+        addInstructionCount(Math.max(count, 1));
+    }
+
+    /**
+     * Generate calls to ScriptRuntime.addInstructionCount to keep track of executed instructions
+     * and call <code>observeInstructionCount()</code> if a threshold is exceeded.<br>
+     * Takes the count as a parameter - used to add monitoring to loops and other blocks that don't
+     * have any ops - this allows for monitoring/killing of while(true) loops and such.
+     */
+    private void addInstructionCount(int count) {
+        cfw.addALoad(contextLocal);
+        cfw.addPush(count);
+        addScriptRuntimeInvoke("addInstructionCount", "(Lorg/mozilla/javascript/Context;" + "I)V");
+    }
+
+    private void visitIncDec(Node node) {
+        int incrDecrMask = node.getExistingIntProp(Node.INCRDECR_PROP);
+        Node child = node.getFirstChild();
+        switch (child.getType()) {
+            case Token.GETVAR:
+                if (!hasVarsInRegs) Kit.codeBug();
+                boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
+                int varIndex = fnCurrent.getVarIndex(child);
+                short reg = varRegisters[varIndex];
+                boolean[] constDeclarations = fnCurrent.fnode.getParamAndVarConst();
+                if (constDeclarations[varIndex]) {
+                    if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
+                        int offset = varIsDirectCallParameter(varIndex) ? 1 : 0;
+                        cfw.addDLoad(reg + offset);
+                        if (!post) {
+                            cfw.addPush(1.0);
+                            if ((incrDecrMask & Node.DECR_FLAG) == 0) {
+                                cfw.add(ByteCode.DADD);
+                            } else {
+                                cfw.add(ByteCode.DSUB);
+                            }
+                        }
+                    } else {
+                        if (varIsDirectCallParameter(varIndex)) {
+                            dcpLoadAsObject(reg);
+                        } else {
+                            cfw.addALoad(reg);
+                        }
+                        if (post) {
+                            cfw.add(ByteCode.DUP);
+                            addObjectToDouble();
+                            cfw.add(ByteCode.POP2);
+                        } else {
+                            addObjectToDouble();
+                            cfw.addPush(1.0);
+                            if ((incrDecrMask & Node.DECR_FLAG) == 0) {
+                                cfw.add(ByteCode.DADD);
+                            } else {
+                                cfw.add(ByteCode.DSUB);
+                            }
+                            addDoubleWrap();
+                        }
+                    }
+                    break;
+                }
+                if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
+                    int offset = varIsDirectCallParameter(varIndex) ? 1 : 0;
+                    cfw.addDLoad(reg + offset);
+                    if (post) {
+                        cfw.add(ByteCode.DUP2);
+                    }
+                    cfw.addPush(1.0);
+                    if ((incrDecrMask & Node.DECR_FLAG) == 0) {
+                        cfw.add(ByteCode.DADD);
+                    } else {
+                        cfw.add(ByteCode.DSUB);
+                    }
+                    if (!post) {
+                        cfw.add(ByteCode.DUP2);
+                    }
+                    cfw.addDStore(reg + offset);
+                } else {
+                    if (varIsDirectCallParameter(varIndex)) {
+                        dcpLoadAsObject(reg);
+                    } else {
+                        cfw.addALoad(reg);
+                    }
+                    addObjectToDouble();
+                    if (post) {
+                        cfw.add(ByteCode.DUP2);
+                    }
+                    cfw.addPush(1.0);
+                    if ((incrDecrMask & Node.DECR_FLAG) == 0) {
+                        cfw.add(ByteCode.DADD);
+                    } else {
+                        cfw.add(ByteCode.DSUB);
+                    }
+                    addDoubleWrap();
+                    if (!post) {
+                        cfw.add(ByteCode.DUP);
+                    }
+                    cfw.addAStore(reg);
+                    if (post) {
+                        addDoubleWrap();
+                    }
+                }
+                break;
+            case Token.NAME:
+                cfw.addALoad(variableObjectLocal);
+                cfw.addPush(child.getString()); // push name
+                cfw.addALoad(contextLocal);
+                cfw.addPush(incrDecrMask);
+                addScriptRuntimeInvoke(
+                        "nameIncrDecr",
+                        "(Lorg/mozilla/javascript/Scriptable;"
+                                + "Ljava/lang/String;"
+                                + "Lorg/mozilla/javascript/Context;"
+                                + "I)Ljava/lang/Object;");
+                break;
+            case Token.GETPROPNOWARN:
+                throw Kit.codeBug();
+            case Token.GETPROP:
+                {
+                    Node getPropChild = child.getFirstChild();
+                    generateExpression(getPropChild, node);
+                    generateExpression(getPropChild.getNext(), node);
+                    cfw.addALoad(contextLocal);
+                    cfw.addALoad(variableObjectLocal);
+                    cfw.addPush(incrDecrMask);
+                    addScriptRuntimeInvoke(
+                            "propIncrDecr",
+                            "(Ljava/lang/Object;"
+                                    + "Ljava/lang/String;"
+                                    + "Lorg/mozilla/javascript/Context;"
+                                    + "Lorg/mozilla/javascript/Scriptable;"
+                                    + "I)Ljava/lang/Object;");
+                    break;
+                }
+            case Token.GETELEM:
+                {
+                    Node elemChild = child.getFirstChild();
+                    generateExpression(elemChild, node);
+                    generateExpression(elemChild.getNext(), node);
+                    cfw.addALoad(contextLocal);
+                    cfw.addALoad(variableObjectLocal);
+                    cfw.addPush(incrDecrMask);
+                    if (elemChild.getNext().getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
+                        addOptRuntimeInvoke(
+                                "elemIncrDecr",
+                                "(Ljava/lang/Object;"
+                                        + "D"
+                                        + "Lorg/mozilla/javascript/Context;"
+                                        + "Lorg/mozilla/javascript/Scriptable;"
+                                        + "I"
+                                        + ")Ljava/lang/Object;");
+                    } else {
+                        addScriptRuntimeInvoke(
+                                "elemIncrDecr",
+                                "(Ljava/lang/Object;"
+                                        + "Ljava/lang/Object;"
+                                        + "Lorg/mozilla/javascript/Context;"
+                                        + "Lorg/mozilla/javascript/Scriptable;"
+                                        + "I"
+                                        + ")Ljava/lang/Object;");
+                    }
+                    break;
+                }
+            case Token.GET_REF:
+                {
+                    Node refChild = child.getFirstChild();
+                    generateExpression(refChild, node);
+                    cfw.addALoad(contextLocal);
+                    cfw.addALoad(variableObjectLocal);
+                    cfw.addPush(incrDecrMask);
+                    addScriptRuntimeInvoke(
+                            "refIncrDecr",
+                            "(Lorg/mozilla/javascript/Ref;"
+                                    + "Lorg/mozilla/javascript/Context;"
+                                    + "Lorg/mozilla/javascript/Scriptable;"
+                                    + "I)Ljava/lang/Object;");
+                    break;
+                }
+            default:
+                Codegen.badTree();
+        }
+    }
+
+    private static boolean isArithmeticNode(Node node) {
+        int type = node.getType();
+        return (type == Token.SUB)
+                || (type == Token.MOD)
+                || (type == Token.DIV)
+                || (type == Token.MUL);
+    }
+
+    private void visitArithmetic(Node node, int type, Node child, Node parent) {
+        int childNumberFlag = node.getIntProp(Node.ISNUMBER_PROP, -1);
+        if (childNumberFlag != -1) {
+            generateExpression(child, node);
+            generateExpression(child.getNext(), node);
+
+            switch (type) {
+                case Token.SUB:
+                    cfw.add(ByteCode.DSUB);
+                    break;
+                case Token.MUL:
+                    cfw.add(ByteCode.DMUL);
+                    break;
+                case Token.DIV:
+                    cfw.add(ByteCode.DDIV);
+                    break;
+                case Token.MOD:
+                    cfw.add(ByteCode.DREM);
+                    break;
+                default:
+                    throw Kit.codeBug(Token.typeToName(type));
+            }
+        } else {
+            generateExpression(child, node);
+            if (!isArithmeticNode(child)) addObjectToNumeric();
+            generateExpression(child.getNext(), node);
+            if (!isArithmeticNode(child.getNext())) addObjectToNumeric();
+
+            switch (type) {
+                case Token.SUB:
+                    addScriptRuntimeInvoke(
+                            "subtract", "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;");
+                    break;
+                case Token.MUL:
+                    addScriptRuntimeInvoke(
+                            "multiply", "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;");
+                    break;
+                case Token.DIV:
+                    addScriptRuntimeInvoke(
+                            "divide", "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;");
+                    break;
+                case Token.MOD:
+                    addScriptRuntimeInvoke(
+                            "remainder",
+                            "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;");
+                    break;
+                default:
+                    throw Kit.codeBug(Token.typeToName(type));
+            }
+        }
+    }
+
+    private void visitExponentiation(Node node, Node child, Node parent) {
+        int childNumberFlag = node.getIntProp(Node.ISNUMBER_PROP, -1);
+        if (childNumberFlag != -1) {
+            generateExpression(child, node);
+            generateExpression(child.getNext(), node);
+            cfw.addInvoke(ByteCode.INVOKESTATIC, "java/lang/Math", "pow", "(DD)D");
+        } else {
+            generateExpression(child, node);
+            generateExpression(child.getNext(), node);
+
+            short reg = getNewWordLocal();
+            cfw.addAStore(reg);
+            addObjectToNumeric();
+            cfw.addALoad(reg);
+            addObjectToNumeric();
+
+            addScriptRuntimeInvoke(
+                    "exponentiate", "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;");
+        }
+    }
+
+    private void visitBitNot(Node node, Node child) {
+        int childNumberFlag = node.getIntProp(Node.ISNUMBER_PROP, -1);
+        generateExpression(child, node);
+        if (childNumberFlag == -1) {
+            addObjectToNumeric();
+            addScriptRuntimeInvoke("bitwiseNOT", "(Ljava/lang/Number;)Ljava/lang/Number;");
+        } else {
+            addScriptRuntimeInvoke("toInt32", "(D)I");
+            cfw.addPush(-1); // implement ~a as (a ^ -1)
+            cfw.add(ByteCode.IXOR);
+            cfw.add(ByteCode.I2D);
+        }
+    }
+
+    private void visitBitOp(Node node, int type, Node child) {
+        int childNumberFlag = node.getIntProp(Node.ISNUMBER_PROP, -1);
+        generateExpression(child, node);
+
+        // special-case URSH; work with the target arg as a long, so
+        // that we can return a 32-bit unsigned value, and call
+        // toUint32 instead of toInt32.
+        if (type == Token.URSH) {
+            addScriptRuntimeInvoke("toUint32", "(Ljava/lang/Object;)J");
+            generateExpression(child.getNext(), node);
+            addScriptRuntimeInvoke("toInt32", "(Ljava/lang/Object;)I");
+            // Looks like we need to explicitly mask the shift to 5 bits -
+            // LUSHR takes 6 bits.
+            cfw.addPush(31);
+            cfw.add(ByteCode.IAND);
+            cfw.add(ByteCode.LUSHR);
+            cfw.add(ByteCode.L2D);
+            addDoubleWrap();
+            return;
+        }
+        if (childNumberFlag == -1) {
+            addObjectToNumeric();
+            generateExpression(child.getNext(), node);
+            addObjectToNumeric();
+
+            switch (type) {
+                case Token.BITOR:
+                    addScriptRuntimeInvoke(
+                            "bitwiseOR",
+                            "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;");
+                    break;
+                case Token.BITXOR:
+                    addScriptRuntimeInvoke(
+                            "bitwiseXOR",
+                            "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;");
+                    break;
+                case Token.BITAND:
+                    addScriptRuntimeInvoke(
+                            "bitwiseAND",
+                            "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;");
+                    break;
+                case Token.RSH:
+                    addScriptRuntimeInvoke(
+                            "signedRightShift",
+                            "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;");
+                    break;
+                case Token.LSH:
+                    addScriptRuntimeInvoke(
+                            "leftShift",
+                            "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;");
+                    break;
+                default:
+                    throw Kit.codeBug(Token.typeToName(type));
+            }
+        } else {
+            addScriptRuntimeInvoke("toInt32", "(D)I");
+            generateExpression(child.getNext(), node);
+            addScriptRuntimeInvoke("toInt32", "(D)I");
+
+            switch (type) {
+                case Token.BITOR:
+                    cfw.add(ByteCode.IOR);
+                    break;
+                case Token.BITXOR:
+                    cfw.add(ByteCode.IXOR);
+                    break;
+                case Token.BITAND:
+                    cfw.add(ByteCode.IAND);
+                    break;
+                case Token.RSH:
+                    cfw.add(ByteCode.ISHR);
+                    break;
+                case Token.LSH:
+                    cfw.add(ByteCode.ISHL);
+                    break;
+                default:
+                    throw Kit.codeBug(Token.typeToName(type));
+            }
+            cfw.add(ByteCode.I2D);
+        }
+    }
+
+    private int nodeIsDirectCallParameter(Node node) {
+        if (node.getType() == Token.GETVAR && inDirectCallFunction && !itsForcedObjectParameters) {
+            int varIndex = fnCurrent.getVarIndex(node);
+            if (fnCurrent.isParameter(varIndex)) {
+                return varRegisters[varIndex];
+            }
+        }
+        return -1;
+    }
+
+    private boolean varIsDirectCallParameter(int varIndex) {
+        return fnCurrent.isParameter(varIndex)
+                && inDirectCallFunction
+                && !itsForcedObjectParameters;
+    }
+
+    private void genSimpleCompare(int type, int trueGOTO, int falseGOTO) {
+        if (trueGOTO == -1) throw Codegen.badTree();
+        switch (type) {
+            case Token.LE:
+                cfw.add(ByteCode.DCMPG);
+                cfw.add(ByteCode.IFLE, trueGOTO);
+                break;
+            case Token.GE:
+                cfw.add(ByteCode.DCMPL);
+                cfw.add(ByteCode.IFGE, trueGOTO);
+                break;
+            case Token.LT:
+                cfw.add(ByteCode.DCMPG);
+                cfw.add(ByteCode.IFLT, trueGOTO);
+                break;
+            case Token.GT:
+                cfw.add(ByteCode.DCMPL);
+                cfw.add(ByteCode.IFGT, trueGOTO);
+                break;
+            default:
+                throw Codegen.badTree();
+        }
+        if (falseGOTO != -1) cfw.add(ByteCode.GOTO, falseGOTO);
+    }
+
+    private void visitIfJumpRelOp(Node node, Node child, int trueGOTO, int falseGOTO) {
+        if (trueGOTO == -1 || falseGOTO == -1) throw Codegen.badTree();
+        int type = node.getType();
+        Node rChild = child.getNext();
+        if (type == Token.INSTANCEOF || type == Token.IN) {
+            generateExpression(child, node);
+            generateExpression(rChild, node);
+            cfw.addALoad(contextLocal);
+            addScriptRuntimeInvoke(
+                    (type == Token.INSTANCEOF) ? "instanceOf" : "in",
+                    "(Ljava/lang/Object;"
+                            + "Ljava/lang/Object;"
+                            + "Lorg/mozilla/javascript/Context;"
+                            + ")Z");
+            cfw.add(ByteCode.IFNE, trueGOTO);
+            cfw.add(ByteCode.GOTO, falseGOTO);
+            return;
+        }
+        int childNumberFlag = node.getIntProp(Node.ISNUMBER_PROP, -1);
+        int left_dcp_register = nodeIsDirectCallParameter(child);
+        int right_dcp_register = nodeIsDirectCallParameter(rChild);
+        if (childNumberFlag != -1) {
+            // Force numeric context on both parameters and optimize
+            // direct call case as Optimizer currently does not handle it
+
+            if (childNumberFlag != Node.RIGHT) {
+                // Left already has number content
+                generateExpression(child, node);
+            } else if (left_dcp_register != -1) {
+                dcpLoadAsNumber(left_dcp_register);
+            } else {
+                generateExpression(child, node);
+                addObjectToDouble();
+            }
+
+            if (childNumberFlag != Node.LEFT) {
+                // Right already has number content
+                generateExpression(rChild, node);
+            } else if (right_dcp_register != -1) {
+                dcpLoadAsNumber(right_dcp_register);
+            } else {
+                generateExpression(rChild, node);
+                addObjectToDouble();
+            }
+
+            genSimpleCompare(type, trueGOTO, falseGOTO);
+
+        } else {
+            if (left_dcp_register != -1 && right_dcp_register != -1) {
+                // Generate code to dynamically check for number content
+                // if both operands are dcp
+                short stack = cfw.getStackTop();
+                int leftIsNotNumber = cfw.acquireLabel();
+                cfw.addALoad(left_dcp_register);
+                cfw.add(ByteCode.GETSTATIC, "java/lang/Void", "TYPE", "Ljava/lang/Class;");
+                cfw.add(ByteCode.IF_ACMPNE, leftIsNotNumber);
+                cfw.addDLoad(left_dcp_register + 1);
+                dcpLoadAsNumber(right_dcp_register);
+                genSimpleCompare(type, trueGOTO, falseGOTO);
+                if (stack != cfw.getStackTop()) throw Codegen.badTree();
+
+                cfw.markLabel(leftIsNotNumber);
+                int rightIsNotNumber = cfw.acquireLabel();
+                cfw.addALoad(right_dcp_register);
+                cfw.add(ByteCode.GETSTATIC, "java/lang/Void", "TYPE", "Ljava/lang/Class;");
+                cfw.add(ByteCode.IF_ACMPNE, rightIsNotNumber);
+                cfw.addALoad(left_dcp_register);
+                addObjectToDouble();
+                cfw.addDLoad(right_dcp_register + 1);
+                genSimpleCompare(type, trueGOTO, falseGOTO);
+                if (stack != cfw.getStackTop()) throw Codegen.badTree();
+
+                cfw.markLabel(rightIsNotNumber);
+                // Load both register as objects to call generic cmp_*
+                cfw.addALoad(left_dcp_register);
+                cfw.addALoad(right_dcp_register);
+
+            } else {
+                generateExpression(child, node);
+                generateExpression(rChild, node);
+            }
+
+            cfw.addPush(type);
+            addScriptRuntimeInvoke("compare", "(Ljava/lang/Object;" + "Ljava/lang/Object;" + "I)Z");
+            cfw.add(ByteCode.IFNE, trueGOTO);
+            cfw.add(ByteCode.GOTO, falseGOTO);
+        }
+    }
+
+    private void visitIfJumpEqOp(Node node, Node child, int trueGOTO, int falseGOTO) {
+        if (trueGOTO == -1 || falseGOTO == -1) throw Codegen.badTree();
+
+        short stackInitial = cfw.getStackTop();
+        int type = node.getType();
+        Node rChild = child.getNext();
+
+        // Optimize if one of operands is null
+        if (child.getType() == Token.NULL || rChild.getType() == Token.NULL) {
+            // eq is symmetric in this case
+            if (child.getType() == Token.NULL) {
+                child = rChild;
+            }
+            generateExpression(child, node);
+            if (type == Token.SHEQ || type == Token.SHNE) {
+                int testCode = (type == Token.SHEQ) ? ByteCode.IFNULL : ByteCode.IFNONNULL;
+                cfw.add(testCode, trueGOTO);
+            } else {
+                if (type != Token.EQ) {
+                    // swap false/true targets for !=
+                    if (type != Token.NE) throw Codegen.badTree();
+                    int tmp = trueGOTO;
+                    trueGOTO = falseGOTO;
+                    falseGOTO = tmp;
+                }
+                cfw.add(ByteCode.DUP);
+                int undefCheckLabel = cfw.acquireLabel();
+                cfw.add(ByteCode.IFNONNULL, undefCheckLabel);
+                short stack = cfw.getStackTop();
+                cfw.add(ByteCode.POP);
+                cfw.add(ByteCode.GOTO, trueGOTO);
+                cfw.markLabel(undefCheckLabel, stack);
+                Codegen.pushUndefined(cfw);
+                cfw.add(ByteCode.IF_ACMPEQ, trueGOTO);
+            }
+            cfw.add(ByteCode.GOTO, falseGOTO);
+        } else {
+            int child_dcp_register = nodeIsDirectCallParameter(child);
+            if (child_dcp_register != -1 && rChild.getType() == Token.TO_OBJECT) {
+                Node convertChild = rChild.getFirstChild();
+                if (convertChild.getType() == Token.NUMBER) {
+                    cfw.addALoad(child_dcp_register);
+                    cfw.add(ByteCode.GETSTATIC, "java/lang/Void", "TYPE", "Ljava/lang/Class;");
+                    int notNumbersLabel = cfw.acquireLabel();
+                    cfw.add(ByteCode.IF_ACMPNE, notNumbersLabel);
+                    cfw.addDLoad(child_dcp_register + 1);
+                    cfw.addPush(convertChild.getDouble());
+                    cfw.add(ByteCode.DCMPL);
+                    if (type == Token.EQ) cfw.add(ByteCode.IFEQ, trueGOTO);
+                    else cfw.add(ByteCode.IFNE, trueGOTO);
+                    cfw.add(ByteCode.GOTO, falseGOTO);
+                    cfw.markLabel(notNumbersLabel);
+                    // fall thru into generic handling
+                }
+            }
+
+            generateExpression(child, node);
+            generateExpression(rChild, node);
+
+            String name;
+            int testCode;
+            switch (type) {
+                case Token.EQ:
+                    name = "eq";
+                    testCode = ByteCode.IFNE;
+                    break;
+                case Token.NE:
+                    name = "eq";
+                    testCode = ByteCode.IFEQ;
+                    break;
+                case Token.SHEQ:
+                    name = "shallowEq";
+                    testCode = ByteCode.IFNE;
+                    break;
+                case Token.SHNE:
+                    name = "shallowEq";
+                    testCode = ByteCode.IFEQ;
+                    break;
+                default:
+                    throw Codegen.badTree();
+            }
+            addScriptRuntimeInvoke(name, "(Ljava/lang/Object;" + "Ljava/lang/Object;" + ")Z");
+            cfw.add(testCode, trueGOTO);
+            cfw.add(ByteCode.GOTO, falseGOTO);
+        }
+        if (stackInitial != cfw.getStackTop()) throw Codegen.badTree();
+    }
+
+    private void visitSetName(Node node, Node child) {
+        String name = node.getFirstChild().getString();
+        while (child != null) {
+            generateExpression(child, node);
+            child = child.getNext();
+        }
+        cfw.addALoad(contextLocal);
+        cfw.addALoad(variableObjectLocal);
+        cfw.addPush(name);
+        addScriptRuntimeInvoke(
+                "setName",
+                "(Lorg/mozilla/javascript/Scriptable;"
+                        + "Ljava/lang/Object;"
+                        + "Lorg/mozilla/javascript/Context;"
+                        + "Lorg/mozilla/javascript/Scriptable;"
+                        + "Ljava/lang/String;"
+                        + ")Ljava/lang/Object;");
+    }
+
+    private void visitStrictSetName(Node node, Node child) {
+        String name = node.getFirstChild().getString();
+        while (child != null) {
+            generateExpression(child, node);
+            child = child.getNext();
+        }
+        cfw.addALoad(contextLocal);
+        cfw.addALoad(variableObjectLocal);
+        cfw.addPush(name);
+        addScriptRuntimeInvoke(
+                "strictSetName",
+                "(Lorg/mozilla/javascript/Scriptable;"
+                        + "Ljava/lang/Object;"
+                        + "Lorg/mozilla/javascript/Context;"
+                        + "Lorg/mozilla/javascript/Scriptable;"
+                        + "Ljava/lang/String;"
+                        + ")Ljava/lang/Object;");
+    }
+
+    private void visitSetConst(Node node, Node child) {
+        String name = node.getFirstChild().getString();
+        while (child != null) {
+            generateExpression(child, node);
+            child = child.getNext();
+        }
+        cfw.addALoad(contextLocal);
+        cfw.addPush(name);
+        addScriptRuntimeInvoke(
+                "setConst",
+                "(Lorg/mozilla/javascript/Scriptable;"
+                        + "Ljava/lang/Object;"
+                        + "Lorg/mozilla/javascript/Context;"
+                        + "Ljava/lang/String;"
+                        + ")Ljava/lang/Object;");
+    }
+
+    private void visitGetVar(Node node) {
+        if (!hasVarsInRegs) Kit.codeBug();
+        int varIndex = fnCurrent.getVarIndex(node);
+        short reg = varRegisters[varIndex];
+        if (varIsDirectCallParameter(varIndex)) {
+            // Remember that here the isNumber flag means that we
+            // want to use the incoming parameter in a Number
+            // context, so test the object type and convert the
+            //  value as necessary.
+            if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
+                dcpLoadAsNumber(reg);
+            } else {
+                dcpLoadAsObject(reg);
+            }
+        } else if (fnCurrent.isNumberVar(varIndex)) {
+            cfw.addDLoad(reg);
+        } else {
+            cfw.addALoad(reg);
+        }
+    }
+
+    private void visitSetVar(Node node, Node child, boolean needValue) {
+        if (!hasVarsInRegs) Kit.codeBug();
+        int varIndex = fnCurrent.getVarIndex(node);
+        generateExpression(child.getNext(), node);
+        boolean isNumber = (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1);
+        short reg = varRegisters[varIndex];
+        boolean[] constDeclarations = fnCurrent.fnode.getParamAndVarConst();
+        if (constDeclarations[varIndex]) {
+            if (!needValue) {
+                if (isNumber) cfw.add(ByteCode.POP2);
+                else cfw.add(ByteCode.POP);
+            }
+        } else if (varIsDirectCallParameter(varIndex)) {
+            if (isNumber) {
+                if (needValue) cfw.add(ByteCode.DUP2);
+                cfw.addALoad(reg);
+                cfw.add(ByteCode.GETSTATIC, "java/lang/Void", "TYPE", "Ljava/lang/Class;");
+                int isNumberLabel = cfw.acquireLabel();
+                int beyond = cfw.acquireLabel();
+                cfw.add(ByteCode.IF_ACMPEQ, isNumberLabel);
+                short stack = cfw.getStackTop();
+                addDoubleWrap();
+                cfw.addAStore(reg);
+                cfw.add(ByteCode.GOTO, beyond);
+                cfw.markLabel(isNumberLabel, stack);
+                cfw.addDStore(reg + 1);
+                cfw.markLabel(beyond);
+            } else {
+                if (needValue) cfw.add(ByteCode.DUP);
+                cfw.addAStore(reg);
+            }
+        } else {
+            boolean isNumberVar = fnCurrent.isNumberVar(varIndex);
+            if (isNumber) {
+                if (isNumberVar) {
+                    cfw.addDStore(reg);
+                    if (needValue) cfw.addDLoad(reg);
+                } else {
+                    if (needValue) cfw.add(ByteCode.DUP2);
+                    // Cannot save number in variable since !isNumberVar,
+                    // so convert to object
+                    addDoubleWrap();
+                    cfw.addAStore(reg);
+                }
+            } else {
+                if (isNumberVar) Kit.codeBug();
+                cfw.addAStore(reg);
+                if (needValue) cfw.addALoad(reg);
+            }
+        }
+    }
+
+    private void visitSetConstVar(Node node, Node child, boolean needValue) {
+        if (!hasVarsInRegs) Kit.codeBug();
+        int varIndex = fnCurrent.getVarIndex(node);
+        generateExpression(child.getNext(), node);
+        boolean isNumber = (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1);
+        short reg = varRegisters[varIndex];
+        int beyond = cfw.acquireLabel();
+        int noAssign = cfw.acquireLabel();
+        if (isNumber) {
+            cfw.addILoad(reg + 2);
+            cfw.add(ByteCode.IFNE, noAssign);
+            short stack = cfw.getStackTop();
+            cfw.addPush(1);
+            cfw.addIStore(reg + 2);
+            cfw.addDStore(reg);
+            if (needValue) {
+                cfw.addDLoad(reg);
+                cfw.markLabel(noAssign, stack);
+            } else {
+                cfw.add(ByteCode.GOTO, beyond);
+                cfw.markLabel(noAssign, stack);
+                cfw.add(ByteCode.POP2);
+            }
+        } else {
+            cfw.addILoad(reg + 1);
+            cfw.add(ByteCode.IFNE, noAssign);
+            short stack = cfw.getStackTop();
+            cfw.addPush(1);
+            cfw.addIStore(reg + 1);
+            cfw.addAStore(reg);
+            if (needValue) {
+                cfw.addALoad(reg);
+                cfw.markLabel(noAssign, stack);
+            } else {
+                cfw.add(ByteCode.GOTO, beyond);
+                cfw.markLabel(noAssign, stack);
+                cfw.add(ByteCode.POP);
+            }
+        }
+        cfw.markLabel(beyond);
+    }
+
+    private void visitGetProp(Node node, Node child) {
+        generateExpression(child, node); // object
+        Node nameChild = child.getNext();
+        generateExpression(nameChild, node); // the name
+        if (node.getType() == Token.GETPROPNOWARN) {
+            cfw.addALoad(contextLocal);
+            cfw.addALoad(variableObjectLocal);
+            addScriptRuntimeInvoke(
+                    "getObjectPropNoWarn",
+                    "(Ljava/lang/Object;"
+                            + "Ljava/lang/String;"
+                            + "Lorg/mozilla/javascript/Context;"
+                            + "Lorg/mozilla/javascript/Scriptable;"
+                            + ")Ljava/lang/Object;");
+            return;
+        }
+        /*
+            for 'this.foo' we call getObjectProp(Scriptable...) which can
+            skip some casting overhead.
+        */
+        int childType = child.getType();
+        if (childType == Token.THIS && nameChild.getType() == Token.STRING) {
+            cfw.addALoad(contextLocal);
+            addScriptRuntimeInvoke(
+                    "getObjectProp",
+                    "(Lorg/mozilla/javascript/Scriptable;"
+                            + "Ljava/lang/String;"
+                            + "Lorg/mozilla/javascript/Context;"
+                            + ")Ljava/lang/Object;");
+        } else {
+            cfw.addALoad(contextLocal);
+            cfw.addALoad(variableObjectLocal);
+            addScriptRuntimeInvoke(
+                    "getObjectProp",
+                    "(Ljava/lang/Object;"
+                            + "Ljava/lang/String;"
+                            + "Lorg/mozilla/javascript/Context;"
+                            + "Lorg/mozilla/javascript/Scriptable;"
+                            + ")Ljava/lang/Object;");
+        }
+    }
+
+    private void visitSetProp(int type, Node node, Node child) {
+        Node objectChild = child;
+        generateExpression(child, node);
+        child = child.getNext();
+        if (type == Token.SETPROP_OP) {
+            cfw.add(ByteCode.DUP);
+        }
+        Node nameChild = child;
+        generateExpression(child, node);
+        child = child.getNext();
+        if (type == Token.SETPROP_OP) {
+            // stack: ... object object name -> ... object name object name
+            cfw.add(ByteCode.DUP_X1);
+            // for 'this.foo += ...' we call thisGet which can skip some
+            // casting overhead.
+            if (objectChild.getType() == Token.THIS && nameChild.getType() == Token.STRING) {
+                cfw.addALoad(contextLocal);
+                addScriptRuntimeInvoke(
+                        "getObjectProp",
+                        "(Lorg/mozilla/javascript/Scriptable;"
+                                + "Ljava/lang/String;"
+                                + "Lorg/mozilla/javascript/Context;"
+                                + ")Ljava/lang/Object;");
+            } else {
+                cfw.addALoad(contextLocal);
+                cfw.addALoad(variableObjectLocal);
+                addScriptRuntimeInvoke(
+                        "getObjectProp",
+                        "(Ljava/lang/Object;"
+                                + "Ljava/lang/String;"
+                                + "Lorg/mozilla/javascript/Context;"
+                                + "Lorg/mozilla/javascript/Scriptable;"
+                                + ")Ljava/lang/Object;");
+            }
+        }
+        generateExpression(child, node);
+        cfw.addALoad(contextLocal);
+        cfw.addALoad(variableObjectLocal);
+        addScriptRuntimeInvoke(
+                "setObjectProp",
+                "(Ljava/lang/Object;"
+                        + "Ljava/lang/String;"
+                        + "Ljava/lang/Object;"
+                        + "Lorg/mozilla/javascript/Context;"
+                        + "Lorg/mozilla/javascript/Scriptable;"
+                        + ")Ljava/lang/Object;");
+    }
+
+    private void visitSetElem(int type, Node node, Node child) {
+        generateExpression(child, node);
+        child = child.getNext();
+        if (type == Token.SETELEM_OP) {
+            cfw.add(ByteCode.DUP);
+        }
+        generateExpression(child, node);
+        child = child.getNext();
+        boolean indexIsNumber = (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1);
+        if (type == Token.SETELEM_OP) {
+            if (indexIsNumber) {
+                // stack: ... object object number
+                //        -> ... object number object number
+                cfw.add(ByteCode.DUP2_X1);
+                cfw.addALoad(contextLocal);
+                cfw.addALoad(variableObjectLocal);
+                addScriptRuntimeInvoke(
+                        "getObjectIndex",
+                        "(Ljava/lang/Object;D"
+                                + "Lorg/mozilla/javascript/Context;"
+                                + "Lorg/mozilla/javascript/Scriptable;"
+                                + ")Ljava/lang/Object;");
+            } else {
+                // stack: ... object object indexObject
+                //        -> ... object indexObject object indexObject
+                cfw.add(ByteCode.DUP_X1);
+                cfw.addALoad(contextLocal);
+                cfw.addALoad(variableObjectLocal);
+                addScriptRuntimeInvoke(
+                        "getObjectElem",
+                        "(Ljava/lang/Object;"
+                                + "Ljava/lang/Object;"
+                                + "Lorg/mozilla/javascript/Context;"
+                                + "Lorg/mozilla/javascript/Scriptable;"
+                                + ")Ljava/lang/Object;");
+            }
+        }
+        generateExpression(child, node);
+        cfw.addALoad(contextLocal);
+        cfw.addALoad(variableObjectLocal);
+        if (indexIsNumber) {
+            addScriptRuntimeInvoke(
+                    "setObjectIndex",
+                    "(Ljava/lang/Object;"
+                            + "D"
+                            + "Ljava/lang/Object;"
+                            + "Lorg/mozilla/javascript/Context;"
+                            + "Lorg/mozilla/javascript/Scriptable;"
+                            + ")Ljava/lang/Object;");
+        } else {
+            addScriptRuntimeInvoke(
+                    "setObjectElem",
+                    "(Ljava/lang/Object;"
+                            + "Ljava/lang/Object;"
+                            + "Ljava/lang/Object;"
+                            + "Lorg/mozilla/javascript/Context;"
+                            + "Lorg/mozilla/javascript/Scriptable;"
+                            + ")Ljava/lang/Object;");
+        }
+    }
+
+    private void visitDotQuery(Node node, Node child) {
+        updateLineNumber(node);
+        generateExpression(child, node);
+        cfw.addALoad(variableObjectLocal);
+        addScriptRuntimeInvoke(
+                "enterDotQuery",
+                "(Ljava/lang/Object;"
+                        + "Lorg/mozilla/javascript/Scriptable;"
+                        + ")Lorg/mozilla/javascript/Scriptable;");
+        cfw.addAStore(variableObjectLocal);
+
+        // add push null/pop with label in between to simplify code for loop
+        // continue when it is necessary to pop the null result from
+        // updateDotQuery
+        cfw.add(ByteCode.ACONST_NULL);
+        int queryLoopStart = cfw.acquireLabel();
+        cfw.markLabel(queryLoopStart); // loop continue jumps here
+        cfw.add(ByteCode.POP);
+
+        generateExpression(child.getNext(), node);
+        addScriptRuntimeInvoke("toBoolean", "(Ljava/lang/Object;)Z");
+        cfw.addALoad(variableObjectLocal);
+        addScriptRuntimeInvoke(
+                "updateDotQuery",
+                "(Z" + "Lorg/mozilla/javascript/Scriptable;" + ")Ljava/lang/Object;");
+        cfw.add(ByteCode.DUP);
+        cfw.add(ByteCode.IFNULL, queryLoopStart);
+        // stack: ... non_null_result_of_updateDotQuery
+        cfw.addALoad(variableObjectLocal);
+        addScriptRuntimeInvoke(
+                "leaveDotQuery",
+                "(Lorg/mozilla/javascript/Scriptable;" + ")Lorg/mozilla/javascript/Scriptable;");
+        cfw.addAStore(variableObjectLocal);
+    }
+
+    private static int getLocalBlockRegister(Node node) {
+        Node localBlock = (Node) node.getProp(Node.LOCAL_BLOCK_PROP);
+        int localSlot = localBlock.getExistingIntProp(Node.LOCAL_PROP);
+        return localSlot;
+    }
+
+    private void dcpLoadAsNumber(int dcp_register) {
+        cfw.addALoad(dcp_register);
+        cfw.add(ByteCode.GETSTATIC, "java/lang/Void", "TYPE", "Ljava/lang/Class;");
+        int isNumberLabel = cfw.acquireLabel();
+        cfw.add(ByteCode.IF_ACMPEQ, isNumberLabel);
+        short stack = cfw.getStackTop();
+        cfw.addALoad(dcp_register);
+        addObjectToDouble();
+        int beyond = cfw.acquireLabel();
+        cfw.add(ByteCode.GOTO, beyond);
+        cfw.markLabel(isNumberLabel, stack);
+        cfw.addDLoad(dcp_register + 1);
+        cfw.markLabel(beyond);
+    }
+
+    private void dcpLoadAsObject(int dcp_register) {
+        cfw.addALoad(dcp_register);
+        cfw.add(ByteCode.GETSTATIC, "java/lang/Void", "TYPE", "Ljava/lang/Class;");
+        int isNumberLabel = cfw.acquireLabel();
+        cfw.add(ByteCode.IF_ACMPEQ, isNumberLabel);
+        short stack = cfw.getStackTop();
+        cfw.addALoad(dcp_register);
+        int beyond = cfw.acquireLabel();
+        cfw.add(ByteCode.GOTO, beyond);
+        cfw.markLabel(isNumberLabel, stack);
+        cfw.addDLoad(dcp_register + 1);
+        addDoubleWrap();
+        cfw.markLabel(beyond);
+    }
+
+    private void addGoto(Node target, int jumpcode) {
+        int targetLabel = getTargetLabel(target);
+        cfw.add(jumpcode, targetLabel);
+    }
+
+    private void addObjectToDouble() {
+        addScriptRuntimeInvoke("toNumber", "(Ljava/lang/Object;)D");
+    }
+
+    private void addObjectToNumeric() {
+        addScriptRuntimeInvoke("toNumeric", "(Ljava/lang/Object;)Ljava/lang/Number;");
+    }
+
+    private void addNewObjectArray(int size) {
+        if (size == 0) {
+            if (itsZeroArgArray >= 0) {
+                cfw.addALoad(itsZeroArgArray);
+            } else {
+                cfw.add(
+                        ByteCode.GETSTATIC,
+                        "org/mozilla/javascript/ScriptRuntime",
+                        "emptyArgs",
+                        "[Ljava/lang/Object;");
+            }
+        } else {
+            cfw.addPush(size);
+            cfw.add(ByteCode.ANEWARRAY, "java/lang/Object");
+        }
+    }
+
+    private void addScriptRuntimeInvoke(String methodName, String methodSignature) {
+        cfw.addInvoke(
+                ByteCode.INVOKESTATIC,
+                "org.mozilla.javascript.ScriptRuntime",
+                methodName,
+                methodSignature);
+    }
+
+    private void addOptRuntimeInvoke(String methodName, String methodSignature) {
+        cfw.addInvoke(
+                ByteCode.INVOKESTATIC,
+                "org/mozilla/javascript/optimizer/OptRuntime",
+                methodName,
+                methodSignature);
+    }
+
+    private void addJumpedBooleanWrap(int trueLabel, int falseLabel) {
+        cfw.markLabel(falseLabel);
+        int skip = cfw.acquireLabel();
+        cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;");
+        cfw.add(ByteCode.GOTO, skip);
+        cfw.markLabel(trueLabel);
+        cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;");
+        cfw.markLabel(skip);
+        cfw.adjustStackTop(-1); // only have 1 of true/false
+    }
+
+    private void addDoubleWrap() {
+        addOptRuntimeInvoke("wrapDouble", "(D)Ljava/lang/Double;");
+    }
+
+    /**
+     * Const locals use an extra slot to hold the has-been-assigned-once flag at runtime.
+     *
+     * @param isConst true iff the variable is const
+     * @return the register for the word pair (double/long)
+     */
+    private short getNewWordPairLocal(boolean isConst) {
+        return getNewWordIntern(isConst ? 3 : 2);
+    }
+
+    private short getNewWordLocal(boolean isConst) {
+        return getNewWordIntern(isConst ? 2 : 1);
+    }
+
+    private short getNewWordLocal() {
+        return getNewWordIntern(1);
+    }
+
+    private short getNewWordIntern(int count) {
+        assert count >= 1 && count <= 3;
+
+        int[] locals = this.locals;
+        int result = -1;
+        if (count > 1) {
+            // we need 'count' consecutive free slots
+            OUTER:
+            for (int i = firstFreeLocal; i + count <= MAX_LOCALS; ) {
+                for (int j = 0; j < count; ++j) {
+                    if (locals[i + j] != 0) {
+                        i += j + 1;
+                        continue OUTER;
+                    }
+                }
+                result = i;
+                break;
+            }
+        } else {
+            result = firstFreeLocal;
+        }
+
+        if (result != -1) {
+            locals[result] = 1;
+            if (count > 1) locals[result + 1] = 1;
+            if (count > 2) locals[result + 2] = 1;
+
+            if (result == firstFreeLocal) {
+                for (int i = result + count; i < MAX_LOCALS; i++) {
+                    if (locals[i] == 0) {
+                        firstFreeLocal = (short) i;
+                        if (localsMax < firstFreeLocal) localsMax = firstFreeLocal;
+                        return (short) result;
+                    }
+                }
+            } else {
+                return (short) result;
+            }
+        }
+
+        throw Context.reportRuntimeError("Program too complex (out of locals)");
+    }
+
+    // This is a valid call only for a local that is allocated by default.
+    private void incReferenceWordLocal(short local) {
+        locals[local]++;
+    }
+
+    // This is a valid call only for a local that is allocated by default.
+    private void decReferenceWordLocal(short local) {
+        locals[local]--;
+    }
+
+    private void releaseWordLocal(short local) {
+        if (local < firstFreeLocal) firstFreeLocal = local;
+        locals[local] = 0;
+    }
+
+    static final int GENERATOR_TERMINATE = -1;
+    static final int GENERATOR_START = 0;
+    static final int GENERATOR_YIELD_START = 1;
+
+    ClassFileWriter cfw;
+    Codegen codegen;
+    CompilerEnvirons compilerEnv;
+    ScriptNode scriptOrFn;
+    public int scriptOrFnIndex;
+    private int savedCodeOffset;
+
+    private OptFunctionNode fnCurrent;
+
+    private static final int MAX_LOCALS = 1024;
+    private int[] locals;
+    private short firstFreeLocal;
+    private short localsMax;
+
+    private int itsLineNumber;
+
+    private boolean hasVarsInRegs;
+    private short[] varRegisters;
+    private boolean inDirectCallFunction;
+    private boolean itsForcedObjectParameters;
+    private int enterAreaStartLabel;
+    private int epilogueLabel;
+    private boolean inLocalBlock;
+
+    // special known locals. If you add a new local here, be sure
+    // to initialize it to -1 in initBodyGeneration
+    private short variableObjectLocal;
+    private short popvLocal;
+    private short contextLocal;
+    private short argsLocal;
+    private short operationLocal;
+    private short thisObjLocal;
+    private short funObjLocal;
+    private short itsZeroArgArray;
+    private short itsOneArgArray;
+    private short generatorStateLocal;
+
+    private boolean isGenerator;
+    private int generatorSwitch;
+    private int maxLocals = 0;
+    private int maxStack = 0;
+
+    private Map<Node, FinallyReturnPoint> finallys;
+    private List<Node> literals;
+
+    static class FinallyReturnPoint {
+        public List<Integer> jsrPoints = new ArrayList<Integer>();
+        public int tableLabel = 0;
+    }
+
+    private int unnestedYieldCount = 0;
+    private IdentityHashMap<Node, String> unnestedYields = new IdentityHashMap<>();
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/optimizer/ClassCompiler.java rhino-1.7.14/src/org/mozilla/javascript/optimizer/ClassCompiler.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/optimizer/ClassCompiler.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/optimizer/ClassCompiler.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,7 +6,12 @@
 
 package org.mozilla.javascript.optimizer;
 
-import org.mozilla.javascript.*;
+import org.mozilla.javascript.CompilerEnvirons;
+import org.mozilla.javascript.IRFactory;
+import org.mozilla.javascript.JavaAdapter;
+import org.mozilla.javascript.ObjToIntMap;
+import org.mozilla.javascript.Parser;
+import org.mozilla.javascript.ScriptRuntime;
 import org.mozilla.javascript.ast.AstRoot;
 import org.mozilla.javascript.ast.FunctionNode;
 import org.mozilla.javascript.ast.ScriptNode;
@@ -34,10 +39,10 @@
     /**
      * Set the class name to use for main method implementation.
      * The class must have a method matching
-     * <tt>public static void main(Script sc, String[] args)</tt>, it will be
-     * called when <tt>main(String[] args)</tt> is called in the generated
+     * <code>public static void main(Script sc, String[] args)</code>, it will be
+     * called when <code>main(String[] args)</code> is called in the generated
      * class. The class name should be fully qulified name and include the
-     * package name like in <tt>org.foo.Bar<tt>.
+     * package name like in <code>org.foo.Bar</code>.
      */
     public void setMainMethodClass(String className)
     {
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/optimizer/Codegen.java rhino-1.7.14/src/org/mozilla/javascript/optimizer/Codegen.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/optimizer/Codegen.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/optimizer/Codegen.java	2022-01-06 22:57:21.000000000 +0100
@@ -4,59 +4,78 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-
 package org.mozilla.javascript.optimizer;
 
-import org.mozilla.javascript.*;
-import org.mozilla.javascript.ast.FunctionNode;
-import org.mozilla.javascript.ast.Jump;
-import org.mozilla.javascript.ast.Name;
-import org.mozilla.javascript.ast.ScriptNode;
-import org.mozilla.classfile.*;
-
-import java.util.*;
-import java.lang.reflect.Constructor;
-
 import static org.mozilla.classfile.ClassFileWriter.ACC_FINAL;
 import static org.mozilla.classfile.ClassFileWriter.ACC_PRIVATE;
+import static org.mozilla.classfile.ClassFileWriter.ACC_PROTECTED;
 import static org.mozilla.classfile.ClassFileWriter.ACC_PUBLIC;
 import static org.mozilla.classfile.ClassFileWriter.ACC_STATIC;
 import static org.mozilla.classfile.ClassFileWriter.ACC_VOLATILE;
 
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.mozilla.classfile.ByteCode;
+import org.mozilla.classfile.ClassFileWriter;
+import org.mozilla.javascript.CompilerEnvirons;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Evaluator;
+import org.mozilla.javascript.Function;
+import org.mozilla.javascript.GeneratedClassLoader;
+import org.mozilla.javascript.Kit;
+import org.mozilla.javascript.NativeFunction;
+import org.mozilla.javascript.ObjArray;
+import org.mozilla.javascript.ObjToIntMap;
+import org.mozilla.javascript.RhinoException;
+import org.mozilla.javascript.Script;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.SecurityController;
+import org.mozilla.javascript.Token;
+import org.mozilla.javascript.ast.FunctionNode;
+import org.mozilla.javascript.ast.Name;
+import org.mozilla.javascript.ast.ScriptNode;
+import org.mozilla.javascript.ast.TemplateCharacters;
+
 /**
  * This class generates code for a given IR tree.
  *
  * @author Norris Boyd
  * @author Roger Lawrence
  */
-
-public class Codegen implements Evaluator
-{
+public class Codegen implements Evaluator {
+    @Override
     public void captureStackInfo(RhinoException ex) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public String getSourcePositionFromStack(Context cx, int[] linep) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public String getPatchedStack(RhinoException ex, String nativeStackTrace) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public List<String> getScriptStack(RhinoException ex) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public void setEvalScriptFlag(Script script) {
         throw new UnsupportedOperationException();
     }
 
-    public Object compile(CompilerEnvirons compilerEnv,
-                          ScriptNode tree,
-                          String encodedSource,
-                          boolean returnFunction)
-    {
+    @Override
+    public Object compile(
+            CompilerEnvirons compilerEnv,
+            ScriptNode tree,
+            String encodedSource,
+            boolean returnFunction) {
         int serial;
         synchronized (globalLock) {
             serial = ++globalSerialClassCounter;
@@ -64,67 +83,59 @@
 
         String baseName = "c";
         if (tree.getSourceName().length() > 0) {
-          baseName = tree.getSourceName().replaceAll("\\W", "_");
-          if (!Character.isJavaIdentifierStart(baseName.charAt(0))) {
-            baseName = "_" + baseName;
-          }
+            baseName = tree.getSourceName().replaceAll("\\W", "_");
+            if (!Character.isJavaIdentifierStart(baseName.charAt(0))) {
+                baseName = "_" + baseName;
+            }
         }
 
         String mainClassName = "org.mozilla.javascript.gen." + baseName + "_" + serial;
 
-        byte[] mainClassBytes = compileToClassFile(compilerEnv, mainClassName,
-                                                   tree, encodedSource,
-                                                   returnFunction);
+        byte[] mainClassBytes =
+                compileToClassFile(compilerEnv, mainClassName, tree, encodedSource, returnFunction);
 
-        return new Object[] { mainClassName, mainClassBytes };
+        return new Object[] {mainClassName, mainClassBytes};
     }
 
-    public Script createScriptObject(Object bytecode,
-                                     Object staticSecurityDomain)
-    {
+    @Override
+    public Script createScriptObject(Object bytecode, Object staticSecurityDomain) {
         Class<?> cl = defineClass(bytecode, staticSecurityDomain);
 
         Script script;
         try {
-            script = (Script)cl.newInstance();
+            script = (Script) cl.newInstance();
         } catch (Exception ex) {
-            throw new RuntimeException
-                ("Unable to instantiate compiled class:" + ex.toString());
+            throw new RuntimeException("Unable to instantiate compiled class:" + ex.toString());
         }
         return script;
     }
 
-    public Function createFunctionObject(Context cx, Scriptable scope,
-                                         Object bytecode,
-                                         Object staticSecurityDomain)
-    {
+    @Override
+    public Function createFunctionObject(
+            Context cx, Scriptable scope, Object bytecode, Object staticSecurityDomain) {
         Class<?> cl = defineClass(bytecode, staticSecurityDomain);
 
         NativeFunction f;
         try {
-            Constructor<?>ctor = cl.getConstructors()[0];
-            Object[] initArgs = { scope, cx, Integer.valueOf(0) };
-            f = (NativeFunction)ctor.newInstance(initArgs);
+            Constructor<?> ctor = cl.getConstructors()[0];
+            Object[] initArgs = {scope, cx, Integer.valueOf(0)};
+            f = (NativeFunction) ctor.newInstance(initArgs);
         } catch (Exception ex) {
-            throw new RuntimeException
-                ("Unable to instantiate compiled class:"+ex.toString());
+            throw new RuntimeException("Unable to instantiate compiled class:" + ex.toString());
         }
         return f;
     }
 
-    private Class<?> defineClass(Object bytecode,
-                                 Object staticSecurityDomain)
-    {
-        Object[] nameBytesPair = (Object[])bytecode;
-        String className = (String)nameBytesPair[0];
-        byte[] classBytes = (byte[])nameBytesPair[1];
+    private Class<?> defineClass(Object bytecode, Object staticSecurityDomain) {
+        Object[] nameBytesPair = (Object[]) bytecode;
+        String className = (String) nameBytesPair[0];
+        byte[] classBytes = (byte[]) nameBytesPair[1];
 
         // The generated classes in this case refer only to Rhino classes
         // which must be accessible through this class loader
         ClassLoader rhinoLoader = getClass().getClassLoader();
         GeneratedClassLoader loader;
-        loader = SecurityController.createLoader(rhinoLoader,
-                                                 staticSecurityDomain);
+        loader = SecurityController.createLoader(rhinoLoader, staticSecurityDomain);
         Exception e;
         try {
             Class<?> cl = loader.defineClass(className, classBytes);
@@ -138,12 +149,12 @@
         throw new RuntimeException("Malformed optimizer package " + e);
     }
 
-    public byte[] compileToClassFile(CompilerEnvirons compilerEnv,
-                                     String mainClassName,
-                                     ScriptNode scriptOrFn,
-                                     String encodedSource,
-                                     boolean returnFunction)
-    {
+    public byte[] compileToClassFile(
+            CompilerEnvirons compilerEnv,
+            String mainClassName,
+            ScriptNode scriptOrFn,
+            String encodedSource,
+            boolean returnFunction) {
         this.compilerEnv = compilerEnv;
 
         transform(scriptOrFn);
@@ -159,52 +170,32 @@
         initScriptNodesData(scriptOrFn);
 
         this.mainClassName = mainClassName;
-        this.mainClassSignature
-            = ClassFileWriter.classNameToSignature(mainClassName);
+        this.mainClassSignature = ClassFileWriter.classNameToSignature(mainClassName);
 
-        try {
-            return generateCode(encodedSource);
-        } catch (ClassFileWriter.ClassFileFormatException e) {
-            throw reportClassFileFormatException(scriptOrFn, e.getMessage());
-        }
-    }
-
-    private RuntimeException reportClassFileFormatException(
-        ScriptNode scriptOrFn,
-        String message)
-    {
-        String msg = scriptOrFn instanceof FunctionNode
-        ? ScriptRuntime.getMessage2("msg.while.compiling.fn",
-            ((FunctionNode)scriptOrFn).getFunctionName(), message)
-        : ScriptRuntime.getMessage1("msg.while.compiling.script", message);
-        return Context.reportRuntimeError(msg, scriptOrFn.getSourceName(),
-            scriptOrFn.getLineno(), null, 0);
+        return generateCode(encodedSource);
     }
 
-    private void transform(ScriptNode tree)
-    {
+    private void transform(ScriptNode tree) {
         initOptFunctions_r(tree);
 
         int optLevel = compilerEnv.getOptimizationLevel();
 
-        Map<String,OptFunctionNode> possibleDirectCalls = null;
+        Map<String, OptFunctionNode> possibleDirectCalls = null;
         if (optLevel > 0) {
-           /*
-            * Collect all of the contained functions into a hashtable
-            * so that the call optimizer can access the class name & parameter
-            * count for any call it encounters
-            */
+            /*
+             * Collect all of the contained functions into a hashtable
+             * so that the call optimizer can access the class name & parameter
+             * count for any call it encounters
+             */
             if (tree.getType() == Token.SCRIPT) {
                 int functionCount = tree.getFunctionCount();
                 for (int i = 0; i != functionCount; ++i) {
                     OptFunctionNode ofn = OptFunctionNode.get(tree, i);
-                    if (ofn.fnode.getFunctionType()
-                        == FunctionNode.FUNCTION_STATEMENT)
-                    {
+                    if (ofn.fnode.getFunctionType() == FunctionNode.FUNCTION_STATEMENT) {
                         String name = ofn.fnode.getName();
                         if (name.length() != 0) {
                             if (possibleDirectCalls == null) {
-                                possibleDirectCalls = new HashMap<String,OptFunctionNode>();
+                                possibleDirectCalls = new HashMap<String, OptFunctionNode>();
                             }
                             possibleDirectCalls.put(name, ofn);
                         }
@@ -217,17 +208,15 @@
             directCallTargets = new ObjArray();
         }
 
-        OptTransformer ot = new OptTransformer(possibleDirectCalls,
-                                               directCallTargets);
-        ot.transform(tree);
+        OptTransformer ot = new OptTransformer(possibleDirectCalls, directCallTargets);
+        ot.transform(tree, compilerEnv);
 
         if (optLevel > 0) {
             (new Optimizer()).optimize(tree);
         }
     }
 
-    private static void initOptFunctions_r(ScriptNode scriptOrFn)
-    {
+    private static void initOptFunctions_r(ScriptNode scriptOrFn) {
         for (int i = 0, N = scriptOrFn.getFunctionCount(); i != N; ++i) {
             FunctionNode fn = scriptOrFn.getFunctionNode(i);
             new OptFunctionNode(fn);
@@ -235,8 +224,7 @@
         }
     }
 
-    private void initScriptNodesData(ScriptNode scriptOrFn)
-    {
+    private void initScriptNodesData(ScriptNode scriptOrFn) {
         ObjArray x = new ObjArray();
         collectScriptNodes_r(scriptOrFn, x);
 
@@ -250,9 +238,7 @@
         }
     }
 
-    private static void collectScriptNodes_r(ScriptNode n,
-                                                 ObjArray x)
-    {
+    private static void collectScriptNodes_r(ScriptNode n, ObjArray x) {
         x.add(n);
         int nestedCount = n.getFunctionCount();
         for (int i = 0; i != nestedCount; ++i) {
@@ -260,8 +246,7 @@
         }
     }
 
-    private byte[] generateCode(String encodedSource)
-    {
+    private byte[] generateCode(String encodedSource) {
         boolean hasScript = (scriptOrFnNodes[0].getType() == Token.SCRIPT);
         boolean hasFunctions = (scriptOrFnNodes.length > 1 || !hasScript);
         boolean isStrictMode = scriptOrFnNodes[0].isInStrictMode();
@@ -271,9 +256,7 @@
             sourceFile = scriptOrFnNodes[0].getSourceName();
         }
 
-        ClassFileWriter cfw = new ClassFileWriter(mainClassName,
-                                                  SUPER_CLASS_NAME,
-                                                  sourceFile);
+        ClassFileWriter cfw = new ClassFileWriter(mainClassName, SUPER_CLASS_NAME, sourceFile);
         cfw.addField(ID_FIELD_NAME, "I", ACC_PRIVATE);
 
         if (hasFunctions) {
@@ -303,11 +286,7 @@
             bodygen.scriptOrFn = n;
             bodygen.scriptOrFnIndex = i;
 
-            try {
-                bodygen.generateBodyCode();
-            } catch (ClassFileWriter.ClassFileFormatException e) {
-                throw reportClassFileFormatException(n, e.getMessage());
-            }
+            bodygen.generateBodyCode();
 
             if (n.getType() == Token.FUNCTION) {
                 OptFunctionNode ofn = OptFunctionNode.get(n);
@@ -319,28 +298,28 @@
         }
 
         emitRegExpInit(cfw);
+        emitTemplateLiteralInit(cfw);
         emitConstantDudeInitializers(cfw);
 
         return cfw.toByteArray();
     }
 
-    private void emitDirectConstructor(ClassFileWriter cfw,
-                                       OptFunctionNode ofn)
-    {
-/*
-    we generate ..
-        Scriptable directConstruct(<directCallArgs>) {
-            Scriptable newInstance = createObject(cx, scope);
-            Object val = <body-name>(cx, scope, newInstance, <directCallArgs>);
-            if (val instanceof Scriptable) {
-                return (Scriptable) val;
-            }
-            return newInstance;
-        }
-*/
-        cfw.startMethod(getDirectCtorName(ofn.fnode),
-                        getBodyMethodSignature(ofn.fnode),
-                        (short)(ACC_STATIC | ACC_PRIVATE));
+    private void emitDirectConstructor(ClassFileWriter cfw, OptFunctionNode ofn) {
+        /*
+            we generate ..
+                Scriptable directConstruct(<directCallArgs>) {
+                    Scriptable newInstance = createObject(cx, scope);
+                    Object val = <body-name>(cx, scope, newInstance, <directCallArgs>);
+                    if (val instanceof Scriptable) {
+                        return (Scriptable) val;
+                    }
+                    return newInstance;
+                }
+        */
+        cfw.startMethod(
+                getDirectCtorName(ofn.fnode),
+                getBodyMethodSignature(ofn.fnode),
+                (short) (ACC_STATIC | ACC_PRIVATE));
 
         int argCount = ofn.fnode.getParamCount();
         int firstLocal = (4 + argCount * 3) + 1;
@@ -348,12 +327,13 @@
         cfw.addALoad(0); // this
         cfw.addALoad(1); // cx
         cfw.addALoad(2); // scope
-        cfw.addInvoke(ByteCode.INVOKEVIRTUAL,
-                      "org/mozilla/javascript/BaseFunction",
-                      "createObject",
-                      "(Lorg/mozilla/javascript/Context;"
-                      +"Lorg/mozilla/javascript/Scriptable;"
-                      +")Lorg/mozilla/javascript/Scriptable;");
+        cfw.addInvoke(
+                ByteCode.INVOKEVIRTUAL,
+                "org/mozilla/javascript/BaseFunction",
+                "createObject",
+                "(Lorg/mozilla/javascript/Context;"
+                        + "Lorg/mozilla/javascript/Scriptable;"
+                        + ")Lorg/mozilla/javascript/Scriptable;");
         cfw.addAStore(firstLocal);
 
         cfw.addALoad(0);
@@ -365,10 +345,11 @@
             cfw.addDLoad(5 + (i * 3));
         }
         cfw.addALoad(4 + argCount * 3);
-        cfw.addInvoke(ByteCode.INVOKESTATIC,
-                      mainClassName,
-                      getBodyMethodName(ofn.fnode),
-                      getBodyMethodSignature(ofn.fnode));
+        cfw.addInvoke(
+                ByteCode.INVOKESTATIC,
+                mainClassName,
+                getBodyMethodName(ofn.fnode),
+                getBodyMethodSignature(ofn.fnode));
         int exitLabel = cfw.acquireLabel();
         cfw.add(ByteCode.DUP); // make a copy of direct call result
         cfw.add(ByteCode.INSTANCEOF, "org/mozilla/javascript/Scriptable");
@@ -381,13 +362,11 @@
         cfw.addALoad(firstLocal);
         cfw.add(ByteCode.ARETURN);
 
-        cfw.stopMethod((short)(firstLocal + 1));
+        cfw.stopMethod((short) (firstLocal + 1));
     }
 
-    static boolean isGenerator(ScriptNode node)
-    {
-        return (node.getType() == Token.FUNCTION ) &&
-                ((FunctionNode)node).isGenerator();
+    static boolean isGenerator(ScriptNode node) {
+        return (node.getType() == Token.FUNCTION) && ((FunctionNode) node).isGenerator();
     }
 
     // How dispatch to generators works:
@@ -402,25 +381,23 @@
     // method corresponding to the generator body. As a matter of convention
     // the generator body is given the name of the generator activation function
     // appended by "_gen".
-    private void generateResumeGenerator(ClassFileWriter cfw)
-    {
+    private void generateResumeGenerator(ClassFileWriter cfw) {
         boolean hasGenerators = false;
-        for (int i=0; i < scriptOrFnNodes.length; i++) {
-            if (isGenerator(scriptOrFnNodes[i]))
-                hasGenerators = true;
+        for (int i = 0; i < scriptOrFnNodes.length; i++) {
+            if (isGenerator(scriptOrFnNodes[i])) hasGenerators = true;
         }
 
         // if there are no generators defined, we don't implement a
         // resumeGenerator(). The base class provides a default implementation.
-        if (!hasGenerators)
-            return;
+        if (!hasGenerators) return;
 
-        cfw.startMethod("resumeGenerator",
-                        "(Lorg/mozilla/javascript/Context;" +
-                        "Lorg/mozilla/javascript/Scriptable;" +
-                        "ILjava/lang/Object;" +
-                        "Ljava/lang/Object;)Ljava/lang/Object;",
-                        (short)(ACC_PUBLIC | ACC_FINAL));
+        cfw.startMethod(
+                "resumeGenerator",
+                "(Lorg/mozilla/javascript/Context;"
+                        + "Lorg/mozilla/javascript/Scriptable;"
+                        + "ILjava/lang/Object;"
+                        + "Ljava/lang/Object;)Ljava/lang/Object;",
+                (short) (ACC_PUBLIC | ACC_FINAL));
 
         // load arguments for dispatch to the corresponding *_gen method
         cfw.addALoad(0);
@@ -439,18 +416,17 @@
 
         for (int i = 0; i < scriptOrFnNodes.length; i++) {
             ScriptNode n = scriptOrFnNodes[i];
-            cfw.markTableSwitchCase(startSwitch, i, (short)6);
+            cfw.markTableSwitchCase(startSwitch, i, (short) 6);
             if (isGenerator(n)) {
-                String type = "(" +
-                              mainClassSignature +
-                              "Lorg/mozilla/javascript/Context;" +
-                              "Lorg/mozilla/javascript/Scriptable;" +
-                              "Ljava/lang/Object;" +
-                              "Ljava/lang/Object;I)Ljava/lang/Object;";
-                cfw.addInvoke(ByteCode.INVOKESTATIC,
-                              mainClassName,
-                              getBodyMethodName(n) + "_gen",
-                              type);
+                String type =
+                        "("
+                                + mainClassSignature
+                                + "Lorg/mozilla/javascript/Context;"
+                                + "Lorg/mozilla/javascript/Scriptable;"
+                                + "Ljava/lang/Object;"
+                                + "Ljava/lang/Object;I)Ljava/lang/Object;";
+                cfw.addInvoke(
+                        ByteCode.INVOKESTATIC, mainClassName, getBodyMethodName(n) + "_gen", type);
                 cfw.add(ByteCode.ARETURN);
             } else {
                 cfw.add(ByteCode.GOTO, endlabel);
@@ -461,19 +437,18 @@
         pushUndefined(cfw);
         cfw.add(ByteCode.ARETURN);
 
-
         // this method uses as many locals as there are arguments (hence 6)
-        cfw.stopMethod((short)6);
+        cfw.stopMethod((short) 6);
     }
 
-    private void generateCallMethod(ClassFileWriter cfw, boolean isStrictMode)
-    {
-        cfw.startMethod("call",
-                        "(Lorg/mozilla/javascript/Context;" +
-                        "Lorg/mozilla/javascript/Scriptable;" +
-                        "Lorg/mozilla/javascript/Scriptable;" +
-                        "[Ljava/lang/Object;)Ljava/lang/Object;",
-                        (short)(ACC_PUBLIC | ACC_FINAL));
+    private void generateCallMethod(ClassFileWriter cfw, boolean isStrictMode) {
+        cfw.startMethod(
+                "call",
+                "(Lorg/mozilla/javascript/Context;"
+                        + "Lorg/mozilla/javascript/Scriptable;"
+                        + "Lorg/mozilla/javascript/Scriptable;"
+                        + "[Ljava/lang/Object;)Ljava/lang/Object;",
+                (short) (ACC_PUBLIC | ACC_FINAL));
 
         // Generate code for:
         // if (!ScriptRuntime.hasTopCall(cx)) {
@@ -481,12 +456,12 @@
         // }
 
         int nonTopCallLabel = cfw.acquireLabel();
-        cfw.addALoad(1); //cx
-        cfw.addInvoke(ByteCode.INVOKESTATIC,
-                      "org/mozilla/javascript/ScriptRuntime",
-                      "hasTopCall",
-                      "(Lorg/mozilla/javascript/Context;"
-                      +")Z");
+        cfw.addALoad(1); // cx
+        cfw.addInvoke(
+                ByteCode.INVOKESTATIC,
+                "org/mozilla/javascript/ScriptRuntime",
+                "hasTopCall",
+                "(Lorg/mozilla/javascript/Context;" + ")Z");
         cfw.add(ByteCode.IFNE, nonTopCallLabel);
         cfw.addALoad(0);
         cfw.addALoad(1);
@@ -494,16 +469,17 @@
         cfw.addALoad(3);
         cfw.addALoad(4);
         cfw.addPush(isStrictMode);
-        cfw.addInvoke(ByteCode.INVOKESTATIC,
-                      "org/mozilla/javascript/ScriptRuntime",
-                      "doTopCall",
-                      "(Lorg/mozilla/javascript/Callable;"
-                      +"Lorg/mozilla/javascript/Context;"
-                      +"Lorg/mozilla/javascript/Scriptable;"
-                      +"Lorg/mozilla/javascript/Scriptable;"
-                      +"[Ljava/lang/Object;"
-                      +"Z"
-                      +")Ljava/lang/Object;");
+        cfw.addInvoke(
+                ByteCode.INVOKESTATIC,
+                "org/mozilla/javascript/ScriptRuntime",
+                "doTopCall",
+                "(Lorg/mozilla/javascript/Callable;"
+                        + "Lorg/mozilla/javascript/Context;"
+                        + "Lorg/mozilla/javascript/Scriptable;"
+                        + "Lorg/mozilla/javascript/Scriptable;"
+                        + "[Ljava/lang/Object;"
+                        + "Z"
+                        + ")Ljava/lang/Object;");
         cfw.add(ByteCode.ARETURN);
         cfw.markLabel(nonTopCallLabel);
 
@@ -534,8 +510,7 @@
                     cfw.markTableSwitchDefault(switchStart);
                     switchStackTop = cfw.getStackTop();
                 } else {
-                    cfw.markTableSwitchCase(switchStart, i - 1,
-                                            switchStackTop);
+                    cfw.markTableSwitchCase(switchStart, i - 1, switchStackTop);
                 }
             }
             if (n.getType() == Token.FUNCTION) {
@@ -568,45 +543,44 @@
                     }
                 }
             }
-            cfw.addInvoke(ByteCode.INVOKESTATIC,
-                          mainClassName,
-                          getBodyMethodName(n),
-                          getBodyMethodSignature(n));
+            cfw.addInvoke(
+                    ByteCode.INVOKESTATIC,
+                    mainClassName,
+                    getBodyMethodName(n),
+                    getBodyMethodSignature(n));
             cfw.add(ByteCode.ARETURN);
         }
-        cfw.stopMethod((short)5);
+        cfw.stopMethod((short) 5);
         // 5: this, cx, scope, js this, args[]
     }
 
-    private void generateMain(ClassFileWriter cfw)
-    {
-        cfw.startMethod("main", "([Ljava/lang/String;)V",
-                        (short)(ACC_PUBLIC | ACC_STATIC));
+    private void generateMain(ClassFileWriter cfw) {
+        cfw.startMethod("main", "([Ljava/lang/String;)V", (short) (ACC_PUBLIC | ACC_STATIC));
 
         // load new ScriptImpl()
         cfw.add(ByteCode.NEW, cfw.getClassName());
         cfw.add(ByteCode.DUP);
-        cfw.addInvoke(ByteCode.INVOKESPECIAL, cfw.getClassName(),
-                      "<init>", "()V");
-         // load 'args'
+        cfw.addInvoke(ByteCode.INVOKESPECIAL, cfw.getClassName(), "<init>", "()V");
+        // load 'args'
         cfw.add(ByteCode.ALOAD_0);
         // Call mainMethodClass.main(Script script, String[] args)
-        cfw.addInvoke(ByteCode.INVOKESTATIC,
-                      mainMethodClass,
-                      "main",
-                      "(Lorg/mozilla/javascript/Script;[Ljava/lang/String;)V");
+        cfw.addInvoke(
+                ByteCode.INVOKESTATIC,
+                mainMethodClass,
+                "main",
+                "(Lorg/mozilla/javascript/Script;[Ljava/lang/String;)V");
         cfw.add(ByteCode.RETURN);
         // 1 = String[] args
-        cfw.stopMethod((short)1);
+        cfw.stopMethod((short) 1);
     }
 
-    private void generateExecute(ClassFileWriter cfw)
-    {
-        cfw.startMethod("exec",
-                        "(Lorg/mozilla/javascript/Context;"
-                        +"Lorg/mozilla/javascript/Scriptable;"
-                        +")Ljava/lang/Object;",
-                        (short)(ACC_PUBLIC | ACC_FINAL));
+    private static void generateExecute(ClassFileWriter cfw) {
+        cfw.startMethod(
+                "exec",
+                "(Lorg/mozilla/javascript/Context;"
+                        + "Lorg/mozilla/javascript/Scriptable;"
+                        + ")Ljava/lang/Object;",
+                (short) (ACC_PUBLIC | ACC_FINAL));
 
         final int CONTEXT_ARG = 1;
         final int SCOPE_ARG = 2;
@@ -616,27 +590,33 @@
         cfw.addALoad(SCOPE_ARG);
         cfw.add(ByteCode.DUP);
         cfw.add(ByteCode.ACONST_NULL);
-        cfw.addInvoke(ByteCode.INVOKEVIRTUAL,
-                      cfw.getClassName(),
-                      "call",
-                      "(Lorg/mozilla/javascript/Context;"
-                      +"Lorg/mozilla/javascript/Scriptable;"
-                      +"Lorg/mozilla/javascript/Scriptable;"
-                      +"[Ljava/lang/Object;"
-                      +")Ljava/lang/Object;");
+        cfw.addInvoke(
+                ByteCode.INVOKEVIRTUAL,
+                cfw.getClassName(),
+                "call",
+                "(Lorg/mozilla/javascript/Context;"
+                        + "Lorg/mozilla/javascript/Scriptable;"
+                        + "Lorg/mozilla/javascript/Scriptable;"
+                        + "[Ljava/lang/Object;"
+                        + ")Ljava/lang/Object;");
+
+        cfw.addALoad(CONTEXT_ARG);
+        cfw.addInvoke(
+                ByteCode.INVOKEVIRTUAL,
+                "org.mozilla.javascript.Context",
+                "processMicrotasks",
+                "()V");
 
         cfw.add(ByteCode.ARETURN);
         // 3 = this + context + scope
-        cfw.stopMethod((short)3);
+        cfw.stopMethod((short) 3);
     }
 
-    private void generateScriptCtor(ClassFileWriter cfw)
-    {
+    private static void generateScriptCtor(ClassFileWriter cfw) {
         cfw.startMethod("<init>", "()V", ACC_PUBLIC);
 
         cfw.addLoadThis();
-        cfw.addInvoke(ByteCode.INVOKESPECIAL, SUPER_CLASS_NAME,
-                      "<init>", "()V");
+        cfw.addInvoke(ByteCode.INVOKESPECIAL, SUPER_CLASS_NAME, "<init>", "()V");
         // set id to 0
         cfw.addLoadThis();
         cfw.addPush(0);
@@ -644,19 +624,17 @@
 
         cfw.add(ByteCode.RETURN);
         // 1 parameter = this
-        cfw.stopMethod((short)1);
+        cfw.stopMethod((short) 1);
     }
 
-    private void generateFunctionConstructor(ClassFileWriter cfw)
-    {
+    private void generateFunctionConstructor(ClassFileWriter cfw) {
         final int SCOPE_ARG = 1;
         final int CONTEXT_ARG = 2;
         final int ID_ARG = 3;
 
         cfw.startMethod("<init>", FUNCTION_CONSTRUCTOR_SIGNATURE, ACC_PUBLIC);
         cfw.addALoad(0);
-        cfw.addInvoke(ByteCode.INVOKESPECIAL, SUPER_CLASS_NAME,
-                      "<init>", "()V");
+        cfw.addInvoke(ByteCode.INVOKESPECIAL, SUPER_CLASS_NAME, "<init>", "()V");
 
         cfw.addLoadThis();
         cfw.addILoad(ID_ARG);
@@ -686,57 +664,64 @@
                     cfw.markTableSwitchDefault(switchStart);
                     switchStackTop = cfw.getStackTop();
                 } else {
-                    cfw.markTableSwitchCase(switchStart, i - 1 - start,
-                                            switchStackTop);
+                    cfw.markTableSwitchCase(switchStart, i - 1 - start, switchStackTop);
                 }
             }
             OptFunctionNode ofn = OptFunctionNode.get(scriptOrFnNodes[i]);
-            cfw.addInvoke(ByteCode.INVOKESPECIAL,
-                          mainClassName,
-                          getFunctionInitMethodName(ofn),
-                          FUNCTION_INIT_SIGNATURE);
+            cfw.addInvoke(
+                    ByteCode.INVOKESPECIAL,
+                    mainClassName,
+                    getFunctionInitMethodName(ofn),
+                    FUNCTION_INIT_SIGNATURE);
             cfw.add(ByteCode.RETURN);
         }
 
         // 4 = this + scope + context + id
-        cfw.stopMethod((short)4);
+        cfw.stopMethod((short) 4);
     }
 
-    private void generateFunctionInit(ClassFileWriter cfw,
-                                      OptFunctionNode ofn)
-    {
+    private void generateFunctionInit(ClassFileWriter cfw, OptFunctionNode ofn) {
         final int CONTEXT_ARG = 1;
         final int SCOPE_ARG = 2;
-        cfw.startMethod(getFunctionInitMethodName(ofn),
-                        FUNCTION_INIT_SIGNATURE,
-                        (short)(ACC_PRIVATE | ACC_FINAL));
+        cfw.startMethod(
+                getFunctionInitMethodName(ofn),
+                FUNCTION_INIT_SIGNATURE,
+                (short) (ACC_PRIVATE | ACC_FINAL));
 
         // Call NativeFunction.initScriptFunction
         cfw.addLoadThis();
         cfw.addALoad(CONTEXT_ARG);
         cfw.addALoad(SCOPE_ARG);
-        cfw.addInvoke(ByteCode.INVOKEVIRTUAL,
-                      "org/mozilla/javascript/NativeFunction",
-                      "initScriptFunction",
-                      "(Lorg/mozilla/javascript/Context;"
-                      +"Lorg/mozilla/javascript/Scriptable;"
-                      +")V");
+        cfw.addInvoke(
+                ByteCode.INVOKEVIRTUAL,
+                "org/mozilla/javascript/NativeFunction",
+                "initScriptFunction",
+                "(Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + ")V");
 
         // precompile all regexp literals
         if (ofn.fnode.getRegexpCount() != 0) {
             cfw.addALoad(CONTEXT_ARG);
-            cfw.addInvoke(ByteCode.INVOKESTATIC, mainClassName,
-                          REGEXP_INIT_METHOD_NAME, REGEXP_INIT_METHOD_SIGNATURE);
+            cfw.addInvoke(
+                    ByteCode.INVOKESTATIC,
+                    mainClassName,
+                    REGEXP_INIT_METHOD_NAME,
+                    REGEXP_INIT_METHOD_SIGNATURE);
+        }
+        // emit all template literals
+        if (ofn.fnode.getTemplateLiteralCount() != 0) {
+            cfw.addInvoke(
+                    ByteCode.INVOKESTATIC,
+                    mainClassName,
+                    TEMPLATE_LITERAL_INIT_METHOD_NAME,
+                    TEMPLATE_LITERAL_INIT_METHOD_SIGNATURE);
         }
 
         cfw.add(ByteCode.RETURN);
         // 3 = (scriptThis/functionRef) + scope + context
-        cfw.stopMethod((short)3);
+        cfw.stopMethod((short) 3);
     }
 
-    private void generateNativeFunctionOverrides(ClassFileWriter cfw,
-                                                 String encodedSource)
-    {
+    private void generateNativeFunctionOverrides(ClassFileWriter cfw, String encodedSource) {
         // Override NativeFunction.getLanguageVersion() with
         // public int getLanguageVersion() { return <version-constant>; }
 
@@ -746,18 +731,19 @@
         cfw.add(ByteCode.IRETURN);
 
         // 1: this and no argument or locals
-        cfw.stopMethod((short)1);
+        cfw.stopMethod((short) 1);
 
         // The rest of NativeFunction overrides require specific code for each
         // script/function id
 
-        final int Do_getFunctionName      = 0;
-        final int Do_getParamCount        = 1;
-        final int Do_getParamAndVarCount  = 2;
-        final int Do_getParamOrVarName    = 3;
-        final int Do_getEncodedSource     = 4;
-        final int Do_getParamOrVarConst   = 5;
-        final int SWITCH_COUNT            = 6;
+        final int Do_getFunctionName = 0;
+        final int Do_getParamCount = 1;
+        final int Do_getParamAndVarCount = 2;
+        final int Do_getParamOrVarName = 3;
+        final int Do_getEncodedSource = 4;
+        final int Do_getParamOrVarConst = 5;
+        final int Do_isGeneratorFunction = 6;
+        final int SWITCH_COUNT = 7;
 
         for (int methodIndex = 0; methodIndex != SWITCH_COUNT; ++methodIndex) {
             if (methodIndex == Do_getEncodedSource && encodedSource == null) {
@@ -771,39 +757,37 @@
 
             short methodLocals;
             switch (methodIndex) {
-              case Do_getFunctionName:
-                methodLocals = 1; // Only this
-                cfw.startMethod("getFunctionName", "()Ljava/lang/String;",
-                                ACC_PUBLIC);
-                break;
-              case Do_getParamCount:
-                methodLocals = 1; // Only this
-                cfw.startMethod("getParamCount", "()I",
-                                ACC_PUBLIC);
-                break;
-              case Do_getParamAndVarCount:
-                methodLocals = 1; // Only this
-                cfw.startMethod("getParamAndVarCount", "()I",
-                                ACC_PUBLIC);
-                break;
-              case Do_getParamOrVarName:
-                methodLocals = 1 + 1; // this + paramOrVarIndex
-                cfw.startMethod("getParamOrVarName", "(I)Ljava/lang/String;",
-                                ACC_PUBLIC);
-                break;
-              case Do_getParamOrVarConst:
-                methodLocals = 1 + 1 + 1; // this + paramOrVarName
-                cfw.startMethod("getParamOrVarConst", "(I)Z",
-                                ACC_PUBLIC);
-                break;
-              case Do_getEncodedSource:
-                methodLocals = 1; // Only this
-                cfw.startMethod("getEncodedSource", "()Ljava/lang/String;",
-                                ACC_PUBLIC);
-                cfw.addPush(encodedSource);
-                break;
-              default:
-                throw Kit.codeBug();
+                case Do_getFunctionName:
+                    methodLocals = 1; // Only this
+                    cfw.startMethod("getFunctionName", "()Ljava/lang/String;", ACC_PUBLIC);
+                    break;
+                case Do_getParamCount:
+                    methodLocals = 1; // Only this
+                    cfw.startMethod("getParamCount", "()I", ACC_PUBLIC);
+                    break;
+                case Do_getParamAndVarCount:
+                    methodLocals = 1; // Only this
+                    cfw.startMethod("getParamAndVarCount", "()I", ACC_PUBLIC);
+                    break;
+                case Do_getParamOrVarName:
+                    methodLocals = 1 + 1; // this + paramOrVarIndex
+                    cfw.startMethod("getParamOrVarName", "(I)Ljava/lang/String;", ACC_PUBLIC);
+                    break;
+                case Do_getParamOrVarConst:
+                    methodLocals = 1 + 1 + 1; // this + paramOrVarName
+                    cfw.startMethod("getParamOrVarConst", "(I)Z", ACC_PUBLIC);
+                    break;
+                case Do_getEncodedSource:
+                    methodLocals = 1; // Only this
+                    cfw.startMethod("getEncodedSource", "()Ljava/lang/String;", ACC_PUBLIC);
+                    cfw.addPush(encodedSource);
+                    break;
+                case Do_isGeneratorFunction:
+                    methodLocals = 1; // Only this
+                    cfw.startMethod("isGeneratorFunction", "()Z", ACC_PROTECTED);
+                    break;
+                default:
+                    throw Kit.codeBug();
             }
 
             int count = scriptOrFnNodes.length;
@@ -814,8 +798,7 @@
                 // Generate switch but only if there is more then one
                 // script/function
                 cfw.addLoadThis();
-                cfw.add(ByteCode.GETFIELD, cfw.getClassName(),
-                        ID_FIELD_NAME, "I");
+                cfw.add(ByteCode.GETFIELD, cfw.getClassName(), ID_FIELD_NAME, "I");
 
                 // do switch from 1 .. count - 1 mapping 0 to the default case
                 switchStart = cfw.addTableSwitch(1, count - 1);
@@ -829,77 +812,74 @@
                         switchStackTop = cfw.getStackTop();
                     }
                 } else {
-                    cfw.markTableSwitchCase(switchStart, i - 1,
-                                            switchStackTop);
+                    cfw.markTableSwitchCase(switchStart, i - 1, switchStackTop);
                 }
 
                 // Impelemnet method-specific switch code
                 switch (methodIndex) {
-                  case Do_getFunctionName:
-                    // Push function name
-                    if (n.getType() == Token.SCRIPT) {
-                        cfw.addPush("");
-                    } else {
-                        String name = ((FunctionNode)n).getName();
-                        cfw.addPush(name);
-                    }
-                    cfw.add(ByteCode.ARETURN);
-                    break;
+                    case Do_getFunctionName:
+                        // Push function name
+                        if (n.getType() == Token.SCRIPT) {
+                            cfw.addPush("");
+                        } else {
+                            String name = ((FunctionNode) n).getName();
+                            cfw.addPush(name);
+                        }
+                        cfw.add(ByteCode.ARETURN);
+                        break;
 
-                  case Do_getParamCount:
-                    // Push number of defined parameters
-                    cfw.addPush(n.getParamCount());
-                    cfw.add(ByteCode.IRETURN);
-                    break;
+                    case Do_getParamCount:
+                        // Push number of defined parameters
+                        cfw.addPush(n.getParamCount());
+                        cfw.add(ByteCode.IRETURN);
+                        break;
 
-                  case Do_getParamAndVarCount:
-                    // Push number of defined parameters and declared variables
-                    cfw.addPush(n.getParamAndVarCount());
-                    cfw.add(ByteCode.IRETURN);
-                    break;
+                    case Do_getParamAndVarCount:
+                        // Push number of defined parameters and declared variables
+                        cfw.addPush(n.getParamAndVarCount());
+                        cfw.add(ByteCode.IRETURN);
+                        break;
 
-                  case Do_getParamOrVarName:
-                    // Push name of parameter using another switch
-                    // over paramAndVarCount
-                    int paramAndVarCount = n.getParamAndVarCount();
-                    if (paramAndVarCount == 0) {
-                        // The runtime should never call the method in this
-                        // case but to make bytecode verifier happy return null
-                        // as throwing execption takes more code
-                        cfw.add(ByteCode.ACONST_NULL);
-                        cfw.add(ByteCode.ARETURN);
-                    } else if (paramAndVarCount == 1) {
-                        // As above do not check for valid index but always
-                        // return the name of the first param
-                        cfw.addPush(n.getParamOrVarName(0));
-                        cfw.add(ByteCode.ARETURN);
-                    } else {
-                        // Do switch over getParamOrVarName
-                        cfw.addILoad(1); // param or var index
-                        // do switch from 1 .. paramAndVarCount - 1 mapping 0
-                        // to the default case
-                        int paramSwitchStart = cfw.addTableSwitch(
-                                                   1, paramAndVarCount - 1);
-                        for (int j = 0; j != paramAndVarCount; ++j) {
-                            if (cfw.getStackTop() != 0) Kit.codeBug();
-                            String s = n.getParamOrVarName(j);
-                            if (j == 0) {
-                                cfw.markTableSwitchDefault(paramSwitchStart);
-                            } else {
-                                cfw.markTableSwitchCase(paramSwitchStart, j - 1,
-                                                        0);
-                            }
-                            cfw.addPush(s);
+                    case Do_getParamOrVarName:
+                        // Push name of parameter using another switch
+                        // over paramAndVarCount
+                        int paramAndVarCount = n.getParamAndVarCount();
+                        if (paramAndVarCount == 0) {
+                            // The runtime should never call the method in this
+                            // case but to make bytecode verifier happy return null
+                            // as throwing execption takes more code
+                            cfw.add(ByteCode.ACONST_NULL);
                             cfw.add(ByteCode.ARETURN);
+                        } else if (paramAndVarCount == 1) {
+                            // As above do not check for valid index but always
+                            // return the name of the first param
+                            cfw.addPush(n.getParamOrVarName(0));
+                            cfw.add(ByteCode.ARETURN);
+                        } else {
+                            // Do switch over getParamOrVarName
+                            cfw.addILoad(1); // param or var index
+                            // do switch from 1 .. paramAndVarCount - 1 mapping 0
+                            // to the default case
+                            int paramSwitchStart = cfw.addTableSwitch(1, paramAndVarCount - 1);
+                            for (int j = 0; j != paramAndVarCount; ++j) {
+                                if (cfw.getStackTop() != 0) Kit.codeBug();
+                                String s = n.getParamOrVarName(j);
+                                if (j == 0) {
+                                    cfw.markTableSwitchDefault(paramSwitchStart);
+                                } else {
+                                    cfw.markTableSwitchCase(paramSwitchStart, j - 1, 0);
+                                }
+                                cfw.addPush(s);
+                                cfw.add(ByteCode.ARETURN);
+                            }
                         }
-                    }
-                    break;
+                        break;
 
                     case Do_getParamOrVarConst:
                         // Push name of parameter using another switch
                         // over paramAndVarCount
                         paramAndVarCount = n.getParamAndVarCount();
-                        boolean [] constness = n.getParamAndVarConst();
+                        boolean[] constness = n.getParamAndVarConst();
                         if (paramAndVarCount == 0) {
                             // The runtime should never call the method in this
                             // case but to make bytecode verifier happy return null
@@ -916,36 +896,45 @@
                             cfw.addILoad(1); // param or var index
                             // do switch from 1 .. paramAndVarCount - 1 mapping 0
                             // to the default case
-                            int paramSwitchStart = cfw.addTableSwitch(
-                                                       1, paramAndVarCount - 1);
+                            int paramSwitchStart = cfw.addTableSwitch(1, paramAndVarCount - 1);
                             for (int j = 0; j != paramAndVarCount; ++j) {
                                 if (cfw.getStackTop() != 0) Kit.codeBug();
                                 if (j == 0) {
                                     cfw.markTableSwitchDefault(paramSwitchStart);
                                 } else {
-                                    cfw.markTableSwitchCase(paramSwitchStart, j - 1,
-                                                            0);
+                                    cfw.markTableSwitchCase(paramSwitchStart, j - 1, 0);
                                 }
                                 cfw.addPush(constness[j]);
                                 cfw.add(ByteCode.IRETURN);
                             }
                         }
-                      break;
+                        break;
 
-                  case Do_getEncodedSource:
-                    // Push number encoded source start and end
-                    // to prepare for encodedSource.substring(start, end)
-                    cfw.addPush(n.getEncodedSourceStart());
-                    cfw.addPush(n.getEncodedSourceEnd());
-                    cfw.addInvoke(ByteCode.INVOKEVIRTUAL,
-                                  "java/lang/String",
-                                  "substring",
-                                  "(II)Ljava/lang/String;");
-                    cfw.add(ByteCode.ARETURN);
-                    break;
+                    case Do_isGeneratorFunction:
+                        // Push a boolean if it's a generator
+                        if (n instanceof FunctionNode) {
+                            cfw.addPush(((FunctionNode) n).isES6Generator());
+                        } else {
+                            cfw.addPush(false);
+                        }
+                        cfw.add(ByteCode.IRETURN);
+                        break;
 
-                  default:
-                    throw Kit.codeBug();
+                    case Do_getEncodedSource:
+                        // Push number encoded source start and end
+                        // to prepare for encodedSource.substring(start, end)
+                        cfw.addPush(n.getEncodedSourceStart());
+                        cfw.addPush(n.getEncodedSourceEnd());
+                        cfw.addInvoke(
+                                ByteCode.INVOKEVIRTUAL,
+                                "java/lang/String",
+                                "substring",
+                                "(II)Ljava/lang/String;");
+                        cfw.add(ByteCode.ARETURN);
+                        break;
+
+                    default:
+                        throw Kit.codeBug();
                 }
             }
 
@@ -953,8 +942,7 @@
         }
     }
 
-    private void emitRegExpInit(ClassFileWriter cfw)
-    {
+    private void emitRegExpInit(ClassFileWriter cfw) {
         // precompile all regexp literals
 
         int totalRegCount = 0;
@@ -965,10 +953,11 @@
             return;
         }
 
-        cfw.startMethod(REGEXP_INIT_METHOD_NAME, REGEXP_INIT_METHOD_SIGNATURE,
-                (short)(ACC_STATIC | ACC_PRIVATE));
-        cfw.addField("_reInitDone", "Z",
-                     (short)(ACC_STATIC | ACC_PRIVATE | ACC_VOLATILE));
+        cfw.startMethod(
+                REGEXP_INIT_METHOD_NAME,
+                REGEXP_INIT_METHOD_SIGNATURE,
+                (short) (ACC_STATIC | ACC_PRIVATE));
+        cfw.addField("_reInitDone", "Z", (short) (ACC_STATIC | ACC_PRIVATE | ACC_VOLATILE));
         cfw.add(ByteCode.GETSTATIC, mainClassName, "_reInitDone", "Z");
         int doInit = cfw.acquireLabel();
         cfw.add(ByteCode.IFEQ, doInit);
@@ -977,11 +966,11 @@
 
         // get regexp proxy and store it in local slot 1
         cfw.addALoad(0); // context
-        cfw.addInvoke(ByteCode.INVOKESTATIC,
-                      "org/mozilla/javascript/ScriptRuntime",
-                      "checkRegExpProxy",
-                      "(Lorg/mozilla/javascript/Context;"
-                      +")Lorg/mozilla/javascript/RegExpProxy;");
+        cfw.addInvoke(
+                ByteCode.INVOKESTATIC,
+                "org/mozilla/javascript/ScriptRuntime",
+                "checkRegExpProxy",
+                "(Lorg/mozilla/javascript/Context;" + ")Lorg/mozilla/javascript/RegExpProxy;");
         cfw.addAStore(1); // proxy
 
         // We could apply double-checked locking here but concurrency
@@ -994,8 +983,7 @@
                 String reFieldType = "Ljava/lang/Object;";
                 String reString = n.getRegexpString(j);
                 String reFlags = n.getRegexpFlags(j);
-                cfw.addField(reFieldName, reFieldType,
-                             (short)(ACC_STATIC | ACC_PRIVATE));
+                cfw.addField(reFieldName, reFieldType, (short) (ACC_STATIC | ACC_PRIVATE));
                 cfw.addALoad(1); // proxy
                 cfw.addALoad(0); // context
                 cfw.addPush(reString);
@@ -1004,83 +992,170 @@
                 } else {
                     cfw.addPush(reFlags);
                 }
-                cfw.addInvoke(ByteCode.INVOKEINTERFACE,
-                              "org/mozilla/javascript/RegExpProxy",
-                              "compileRegExp",
-                              "(Lorg/mozilla/javascript/Context;"
-                              +"Ljava/lang/String;Ljava/lang/String;"
-                              +")Ljava/lang/Object;");
-                cfw.add(ByteCode.PUTSTATIC, mainClassName,
-                        reFieldName, reFieldType);
+                cfw.addInvoke(
+                        ByteCode.INVOKEINTERFACE,
+                        "org/mozilla/javascript/RegExpProxy",
+                        "compileRegExp",
+                        "(Lorg/mozilla/javascript/Context;"
+                                + "Ljava/lang/String;Ljava/lang/String;"
+                                + ")Ljava/lang/Object;");
+                cfw.add(ByteCode.PUTSTATIC, mainClassName, reFieldName, reFieldType);
             }
         }
 
         cfw.addPush(1);
         cfw.add(ByteCode.PUTSTATIC, mainClassName, "_reInitDone", "Z");
         cfw.add(ByteCode.RETURN);
-        cfw.stopMethod((short)2);
+        cfw.stopMethod((short) 2);
     }
 
-    private void emitConstantDudeInitializers(ClassFileWriter cfw)
-    {
-        int N = itsConstantListSize;
-        if (N == 0)
+    /**
+     * Overview:
+     *
+     * <pre>
+     * for each fn in functions(script) do
+     *   let field = []
+     *   for each templateLiteral in templateLiterals(fn) do
+     *     let values = concat([[cooked(s), raw(s)] | s <- strings(templateLiteral)])
+     *     field.push(values)
+     *   end
+     *   class[getTemplateLiteralName(fn)] = field
+     * end
+     * </pre>
+     */
+    private void emitTemplateLiteralInit(ClassFileWriter cfw) {
+        // emit all template literals
+
+        int totalTemplateLiteralCount = 0;
+        for (ScriptNode n : scriptOrFnNodes) {
+            totalTemplateLiteralCount += n.getTemplateLiteralCount();
+        }
+        if (totalTemplateLiteralCount == 0) {
             return;
+        }
+
+        cfw.startMethod(
+                TEMPLATE_LITERAL_INIT_METHOD_NAME,
+                TEMPLATE_LITERAL_INIT_METHOD_SIGNATURE,
+                (short) (ACC_STATIC | ACC_PRIVATE));
+        cfw.addField("_qInitDone", "Z", (short) (ACC_STATIC | ACC_PRIVATE | ACC_VOLATILE));
 
-        cfw.startMethod("<clinit>", "()V", (short)(ACC_STATIC | ACC_FINAL));
+        cfw.add(ByteCode.GETSTATIC, mainClassName, "_qInitDone", "Z");
+        int doInit = cfw.acquireLabel();
+        cfw.add(ByteCode.IFEQ, doInit);
+        cfw.add(ByteCode.RETURN);
+        cfw.markLabel(doInit);
+
+        // We could apply double-checked locking here but concurrency
+        // shouldn't be a problem in practice
+        for (ScriptNode n : scriptOrFnNodes) {
+            int qCount = n.getTemplateLiteralCount();
+            if (qCount == 0) continue;
+            String qFieldName = getTemplateLiteralName(n);
+            String qFieldType = "[Ljava/lang/Object;";
+            cfw.addField(qFieldName, qFieldType, (short) (ACC_STATIC | ACC_PRIVATE));
+            cfw.addPush(qCount);
+            cfw.add(ByteCode.ANEWARRAY, "java/lang/Object");
+            for (int j = 0; j < qCount; ++j) {
+                List<TemplateCharacters> strings = n.getTemplateLiteralStrings(j);
+                cfw.add(ByteCode.DUP);
+                cfw.addPush(j);
+                cfw.addPush(strings.size() * 2);
+                cfw.add(ByteCode.ANEWARRAY, "java/lang/String");
+                int k = 0;
+                for (TemplateCharacters s : strings) {
+                    // cooked value
+                    cfw.add(ByteCode.DUP);
+                    cfw.addPush(k++);
+                    if (s.getValue() != null) {
+                        cfw.addPush(s.getValue());
+                    } else {
+                        cfw.add(ByteCode.ACONST_NULL);
+                    }
+                    cfw.add(ByteCode.AASTORE);
+                    // raw value
+                    cfw.add(ByteCode.DUP);
+                    cfw.addPush(k++);
+                    cfw.addPush(s.getRawValue());
+                    cfw.add(ByteCode.AASTORE);
+                }
+                cfw.add(ByteCode.AASTORE);
+            }
+            cfw.add(ByteCode.PUTSTATIC, mainClassName, qFieldName, qFieldType);
+        }
+
+        cfw.addPush(true);
+        cfw.add(ByteCode.PUTSTATIC, mainClassName, "_qInitDone", "Z");
+        cfw.add(ByteCode.RETURN);
+        cfw.stopMethod((short) 0);
+    }
+
+    private void emitConstantDudeInitializers(ClassFileWriter cfw) {
+        int N = itsConstantListSize;
+        if (N == 0) return;
+
+        cfw.startMethod("<clinit>", "()V", (short) (ACC_STATIC | ACC_FINAL));
 
         double[] array = itsConstantList;
         for (int i = 0; i != N; ++i) {
             double num = array[i];
             String constantName = "_k" + i;
             String constantType = getStaticConstantWrapperType(num);
-            cfw.addField(constantName, constantType,
-                        (short)(ACC_STATIC | ACC_PRIVATE));
-            int inum = (int)num;
+            cfw.addField(constantName, constantType, (short) (ACC_STATIC | ACC_PRIVATE));
+            int inum = (int) num;
             if (inum == num) {
                 cfw.addPush(inum);
-                cfw.addInvoke(ByteCode.INVOKESTATIC, "java/lang/Integer",
-                              "valueOf", "(I)Ljava/lang/Integer;");
+                cfw.addInvoke(
+                        ByteCode.INVOKESTATIC,
+                        "java/lang/Integer",
+                        "valueOf",
+                        "(I)Ljava/lang/Integer;");
             } else {
                 cfw.addPush(num);
                 addDoubleWrap(cfw);
             }
-            cfw.add(ByteCode.PUTSTATIC, mainClassName,
-                    constantName, constantType);
+            cfw.add(ByteCode.PUTSTATIC, mainClassName, constantName, constantType);
         }
 
         cfw.add(ByteCode.RETURN);
-        cfw.stopMethod((short)0);
+        cfw.stopMethod((short) 0);
     }
 
-    void pushNumberAsObject(ClassFileWriter cfw, double num)
-    {
+    void pushNumberAsObject(ClassFileWriter cfw, double num) {
         if (num == 0.0) {
             if (1 / num > 0) {
                 // +0.0
-                cfw.add(ByteCode.GETSTATIC,
-                        "org/mozilla/javascript/optimizer/OptRuntime",
-                        "zeroObj", "Ljava/lang/Double;");
+                cfw.add(
+                        ByteCode.GETSTATIC,
+                        "org/mozilla/javascript/ScriptRuntime",
+                        "zeroObj",
+                        "Ljava/lang/Double;");
             } else {
                 cfw.addPush(num);
                 addDoubleWrap(cfw);
             }
 
         } else if (num == 1.0) {
-            cfw.add(ByteCode.GETSTATIC,
+            cfw.add(
+                    ByteCode.GETSTATIC,
                     "org/mozilla/javascript/optimizer/OptRuntime",
-                    "oneObj", "Ljava/lang/Double;");
+                    "oneObj",
+                    "Ljava/lang/Double;");
             return;
 
         } else if (num == -1.0) {
-            cfw.add(ByteCode.GETSTATIC,
+            cfw.add(
+                    ByteCode.GETSTATIC,
                     "org/mozilla/javascript/optimizer/OptRuntime",
-                    "minusOneObj", "Ljava/lang/Double;");
+                    "minusOneObj",
+                    "Ljava/lang/Double;");
 
-        } else if (num != num) {
-            cfw.add(ByteCode.GETSTATIC,
+        } else if (Double.isNaN(num)) {
+            cfw.add(
+                    ByteCode.GETSTATIC,
                     "org/mozilla/javascript/ScriptRuntime",
-                    "NaNobj", "Ljava/lang/Double;");
+                    "NaNobj",
+                    "Ljava/lang/Double;");
 
         } else if (itsConstantListSize >= 2000) {
             // There appears to be a limit in the JVM on either the number
@@ -1112,75 +1187,70 @@
             }
             String constantName = "_k" + index;
             String constantType = getStaticConstantWrapperType(num);
-            cfw.add(ByteCode.GETSTATIC, mainClassName,
-                    constantName, constantType);
+            cfw.add(ByteCode.GETSTATIC, mainClassName, constantName, constantType);
         }
     }
 
-    private static void addDoubleWrap(ClassFileWriter cfw)
-    {
-        cfw.addInvoke(ByteCode.INVOKESTATIC,
-                      "org/mozilla/javascript/optimizer/OptRuntime",
-                      "wrapDouble", "(D)Ljava/lang/Double;");
+    private static void addDoubleWrap(ClassFileWriter cfw) {
+        cfw.addInvoke(
+                ByteCode.INVOKESTATIC,
+                "org/mozilla/javascript/optimizer/OptRuntime",
+                "wrapDouble",
+                "(D)Ljava/lang/Double;");
     }
 
-    private static String getStaticConstantWrapperType(double num)
-    {
-        int inum = (int)num;
+    private static String getStaticConstantWrapperType(double num) {
+        int inum = (int) num;
         if (inum == num) {
             return "Ljava/lang/Integer;";
-        } else {
-            return "Ljava/lang/Double;";
         }
+        return "Ljava/lang/Double;";
     }
-    static void pushUndefined(ClassFileWriter cfw)
-    {
-        cfw.add(ByteCode.GETSTATIC, "org/mozilla/javascript/Undefined",
-                "instance", "Ljava/lang/Object;");
+
+    static void pushUndefined(ClassFileWriter cfw) {
+        cfw.add(
+                ByteCode.GETSTATIC,
+                "org/mozilla/javascript/Undefined",
+                "instance",
+                "Ljava/lang/Object;");
     }
 
-    int getIndex(ScriptNode n)
-    {
+    int getIndex(ScriptNode n) {
         return scriptOrFnIndexes.getExisting(n);
     }
 
-    String getDirectCtorName(ScriptNode n)
-    {
+    String getDirectCtorName(ScriptNode n) {
         return "_n" + getIndex(n);
     }
 
-    String getBodyMethodName(ScriptNode n)
-    {
+    String getBodyMethodName(ScriptNode n) {
         return "_c_" + cleanName(n) + "_" + getIndex(n);
     }
 
-    /**
-     * Gets a Java-compatible "informative" name for the the ScriptOrFnNode
-     */
-    String cleanName(final ScriptNode n)
-    {
-      String result = "";
-      if (n instanceof FunctionNode) {
-        Name name = ((FunctionNode) n).getFunctionName();
-        if (name == null) {
-          result = "anonymous";
+    /** Gets a Java-compatible "informative" name for the the ScriptOrFnNode */
+    String cleanName(final ScriptNode n) {
+        String result = "";
+        if (n instanceof FunctionNode) {
+            Name name = ((FunctionNode) n).getFunctionName();
+            if (name == null) {
+                result = "anonymous";
+            } else {
+                result = name.getIdentifier();
+            }
         } else {
-          result = name.getIdentifier();
+            result = "script";
         }
-      } else {
-        result = "script";
-      }
-      return result;
+        return result;
     }
 
-    String getBodyMethodSignature(ScriptNode n)
-    {
+    String getBodyMethodSignature(ScriptNode n) {
         StringBuilder sb = new StringBuilder();
         sb.append('(');
         sb.append(mainClassSignature);
-        sb.append("Lorg/mozilla/javascript/Context;"
-                  +"Lorg/mozilla/javascript/Scriptable;"
-                  +"Lorg/mozilla/javascript/Scriptable;");
+        sb.append(
+                "Lorg/mozilla/javascript/Context;"
+                        + "Lorg/mozilla/javascript/Scriptable;"
+                        + "Lorg/mozilla/javascript/Scriptable;");
         if (n.getType() == Token.FUNCTION) {
             OptFunctionNode ofn = OptFunctionNode.get(n);
             if (ofn.isTargetOfDirectCall()) {
@@ -1194,46 +1264,43 @@
         return sb.toString();
     }
 
-    String getFunctionInitMethodName(OptFunctionNode ofn)
-    {
-        return "_i"+getIndex(ofn.fnode);
+    String getFunctionInitMethodName(OptFunctionNode ofn) {
+        return "_i" + getIndex(ofn.fnode);
     }
 
-    String getCompiledRegexpName(ScriptNode n, int regexpIndex)
-    {
-        return "_re"+getIndex(n)+"_"+regexpIndex;
+    String getCompiledRegexpName(ScriptNode n, int regexpIndex) {
+        return "_re" + getIndex(n) + "_" + regexpIndex;
     }
 
-    static RuntimeException badTree()
-    {
+    String getTemplateLiteralName(ScriptNode n) {
+        return "_q" + getIndex(n);
+    }
+
+    static RuntimeException badTree() {
         throw new RuntimeException("Bad tree in codegen");
     }
 
-     public void setMainMethodClass(String className)
-     {
-         mainMethodClass = className;
-     }
+    public void setMainMethodClass(String className) {
+        mainMethodClass = className;
+    }
 
-     static final String DEFAULT_MAIN_METHOD_CLASS
-        = "org.mozilla.javascript.optimizer.OptRuntime";
+    static final String DEFAULT_MAIN_METHOD_CLASS = "org.mozilla.javascript.optimizer.OptRuntime";
 
-    private static final String SUPER_CLASS_NAME
-        = "org.mozilla.javascript.NativeFunction";
+    private static final String SUPER_CLASS_NAME = "org.mozilla.javascript.NativeFunction";
 
     static final String ID_FIELD_NAME = "_id";
 
     static final String REGEXP_INIT_METHOD_NAME = "_reInit";
-    static final String REGEXP_INIT_METHOD_SIGNATURE
-        =  "(Lorg/mozilla/javascript/Context;)V";
+    static final String REGEXP_INIT_METHOD_SIGNATURE = "(Lorg/mozilla/javascript/Context;)V";
+
+    static final String TEMPLATE_LITERAL_INIT_METHOD_NAME = "_qInit";
+    static final String TEMPLATE_LITERAL_INIT_METHOD_SIGNATURE = "()V";
 
-    static final String FUNCTION_INIT_SIGNATURE
-        =  "(Lorg/mozilla/javascript/Context;"
-           +"Lorg/mozilla/javascript/Scriptable;"
-           +")V";
-
-   static final String FUNCTION_CONSTRUCTOR_SIGNATURE
-        = "(Lorg/mozilla/javascript/Scriptable;"
-          +"Lorg/mozilla/javascript/Context;I)V";
+    static final String FUNCTION_INIT_SIGNATURE =
+            "(Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + ")V";
+
+    static final String FUNCTION_CONSTRUCTOR_SIGNATURE =
+            "(Lorg/mozilla/javascript/Scriptable;" + "Lorg/mozilla/javascript/Context;I)V";
 
     private static final Object globalLock = new Object();
     private static int globalSerialClassCounter;
@@ -1252,4276 +1319,3 @@
     private double[] itsConstantList;
     private int itsConstantListSize;
 }
-
-
-class BodyCodegen
-{
-    void generateBodyCode()
-    {
-        isGenerator = Codegen.isGenerator(scriptOrFn);
-
-        // generate the body of the current function or script object
-        initBodyGeneration();
-
-        if (isGenerator) {
-
-            // All functions in the generated bytecode have a unique name. Every
-            // generator has a unique prefix followed by _gen
-            String type = "(" +
-                          codegen.mainClassSignature +
-                          "Lorg/mozilla/javascript/Context;" +
-                          "Lorg/mozilla/javascript/Scriptable;" +
-                          "Ljava/lang/Object;" +
-                          "Ljava/lang/Object;I)Ljava/lang/Object;";
-            cfw.startMethod(codegen.getBodyMethodName(scriptOrFn) + "_gen",
-                    type,
-                    (short)(ACC_STATIC | ACC_PRIVATE));
-        } else {
-            cfw.startMethod(codegen.getBodyMethodName(scriptOrFn),
-                    codegen.getBodyMethodSignature(scriptOrFn),
-                    (short)(ACC_STATIC | ACC_PRIVATE));
-        }
-
-        generatePrologue();
-        Node treeTop;
-        if (fnCurrent != null) {
-            treeTop = scriptOrFn.getLastChild();
-        } else {
-            treeTop = scriptOrFn;
-        }
-        generateStatement(treeTop);
-        generateEpilogue();
-
-        cfw.stopMethod((short)(localsMax + 1));
-
-        if (isGenerator) {
-            // generate the user visible method which when invoked will
-            // return a generator object
-            generateGenerator();
-        }
-
-        if (literals != null) {
-            // literals list may grow while we're looping
-            for (int i = 0; i < literals.size(); i++) {
-                Node node = literals.get(i);
-                int type = node.getType();
-                switch (type) {
-                    case Token.OBJECTLIT:
-                        generateObjectLiteralFactory(node, i + 1);
-                        break;
-                    case Token.ARRAYLIT:
-                        generateArrayLiteralFactory(node, i + 1);
-                        break;
-                    default:
-                        Kit.codeBug(Token.typeToName(type));
-                }
-            }
-        }
-
-    }
-
-    // This creates a the user-facing function that returns a NativeGenerator
-    // object.
-    private void generateGenerator()
-    {
-        cfw.startMethod(codegen.getBodyMethodName(scriptOrFn),
-                        codegen.getBodyMethodSignature(scriptOrFn),
-                        (short)(ACC_STATIC | ACC_PRIVATE));
-
-        initBodyGeneration();
-        argsLocal = firstFreeLocal++;
-        localsMax = firstFreeLocal;
-
-        // get top level scope
-        if (fnCurrent != null)
-        {
-            // Unless we're in a direct call use the enclosing scope
-            // of the function as our variable object.
-            cfw.addALoad(funObjLocal);
-            cfw.addInvoke(ByteCode.INVOKEINTERFACE,
-                          "org/mozilla/javascript/Scriptable",
-                          "getParentScope",
-                          "()Lorg/mozilla/javascript/Scriptable;");
-            cfw.addAStore(variableObjectLocal);
-        }
-
-        // generators are forced to have an activation record
-        cfw.addALoad(funObjLocal);
-        cfw.addALoad(variableObjectLocal);
-        cfw.addALoad(argsLocal);
-        cfw.addPush(scriptOrFn.isInStrictMode());
-        addScriptRuntimeInvoke("createFunctionActivation",
-                               "(Lorg/mozilla/javascript/NativeFunction;"
-                               +"Lorg/mozilla/javascript/Scriptable;"
-                               +"[Ljava/lang/Object;"
-                               +"Z"
-                               +")Lorg/mozilla/javascript/Scriptable;");
-        cfw.addAStore(variableObjectLocal);
-
-        // create a function object
-        cfw.add(ByteCode.NEW, codegen.mainClassName);
-        // Call function constructor
-        cfw.add(ByteCode.DUP);
-        cfw.addALoad(variableObjectLocal);
-        cfw.addALoad(contextLocal);           // load 'cx'
-        cfw.addPush(scriptOrFnIndex);
-        cfw.addInvoke(ByteCode.INVOKESPECIAL, codegen.mainClassName,
-                      "<init>", Codegen.FUNCTION_CONSTRUCTOR_SIGNATURE);
-
-        generateNestedFunctionInits();
-
-        // create the NativeGenerator object that we return
-        cfw.addALoad(variableObjectLocal);
-        cfw.addALoad(thisObjLocal);
-        cfw.addLoadConstant(maxLocals);
-        cfw.addLoadConstant(maxStack);
-        addOptRuntimeInvoke("createNativeGenerator",
-                               "(Lorg/mozilla/javascript/NativeFunction;"
-                               +"Lorg/mozilla/javascript/Scriptable;"
-                               +"Lorg/mozilla/javascript/Scriptable;II"
-                               +")Lorg/mozilla/javascript/Scriptable;");
-
-        cfw.add(ByteCode.ARETURN);
-        cfw.stopMethod((short)(localsMax + 1));
-    }
-
-    private void generateNestedFunctionInits()
-    {
-        int functionCount = scriptOrFn.getFunctionCount();
-        for (int i = 0; i != functionCount; i++) {
-            OptFunctionNode ofn = OptFunctionNode.get(scriptOrFn, i);
-            if (ofn.fnode.getFunctionType()
-                    == FunctionNode.FUNCTION_STATEMENT)
-            {
-                visitFunction(ofn, FunctionNode.FUNCTION_STATEMENT);
-            }
-        }
-    }
-
-    private void initBodyGeneration()
-    {
-        varRegisters = null;
-        if (scriptOrFn.getType() == Token.FUNCTION) {
-            fnCurrent = OptFunctionNode.get(scriptOrFn);
-            hasVarsInRegs = !fnCurrent.fnode.requiresActivation();
-            if (hasVarsInRegs) {
-                int n = fnCurrent.fnode.getParamAndVarCount();
-                if (n != 0) {
-                    varRegisters = new short[n];
-                }
-            }
-            inDirectCallFunction = fnCurrent.isTargetOfDirectCall();
-            if (inDirectCallFunction && !hasVarsInRegs) Codegen.badTree();
-        } else {
-            fnCurrent = null;
-            hasVarsInRegs = false;
-            inDirectCallFunction = false;
-        }
-
-        locals = new int[MAX_LOCALS];
-
-        funObjLocal = 0;
-        contextLocal = 1;
-        variableObjectLocal = 2;
-        thisObjLocal = 3;
-        localsMax = (short) 4;  // number of parms + "this"
-        firstFreeLocal = 4;
-
-        popvLocal = -1;
-        argsLocal = -1;
-        itsZeroArgArray = -1;
-        itsOneArgArray = -1;
-        epilogueLabel = -1;
-        enterAreaStartLabel = -1;
-        generatorStateLocal = -1;
-    }
-
-    /**
-     * Generate the prologue for a function or script.
-     */
-    private void generatePrologue()
-    {
-        if (inDirectCallFunction) {
-            int directParameterCount = scriptOrFn.getParamCount();
-            // 0 is reserved for function Object 'this'
-            // 1 is reserved for context
-            // 2 is reserved for parentScope
-            // 3 is reserved for script 'this'
-            if (firstFreeLocal != 4) Kit.codeBug();
-            for (int i = 0; i != directParameterCount; ++i) {
-                varRegisters[i] = firstFreeLocal;
-                // 3 is 1 for Object parm and 2 for double parm
-                firstFreeLocal += 3;
-            }
-            if (!fnCurrent.getParameterNumberContext()) {
-                // make sure that all parameters are objects
-                itsForcedObjectParameters = true;
-                for (int i = 0; i != directParameterCount; ++i) {
-                    short reg = varRegisters[i];
-                    cfw.addALoad(reg);
-                    cfw.add(ByteCode.GETSTATIC,
-                            "java/lang/Void",
-                            "TYPE",
-                            "Ljava/lang/Class;");
-                    int isObjectLabel = cfw.acquireLabel();
-                    cfw.add(ByteCode.IF_ACMPNE, isObjectLabel);
-                    cfw.addDLoad(reg + 1);
-                    addDoubleWrap();
-                    cfw.addAStore(reg);
-                    cfw.markLabel(isObjectLabel);
-                }
-            }
-        }
-
-        if (fnCurrent != null) {
-            // Use the enclosing scope of the function as our variable object.
-            cfw.addALoad(funObjLocal);
-            cfw.addInvoke(ByteCode.INVOKEINTERFACE,
-                          "org/mozilla/javascript/Scriptable",
-                          "getParentScope",
-                          "()Lorg/mozilla/javascript/Scriptable;");
-            cfw.addAStore(variableObjectLocal);
-        }
-
-        // reserve 'args[]'
-        argsLocal = firstFreeLocal++;
-        localsMax = firstFreeLocal;
-
-        // Generate Generator specific prelude
-        if (isGenerator) {
-
-            // reserve 'args[]'
-            operationLocal = firstFreeLocal++;
-            localsMax = firstFreeLocal;
-
-            // Local 3 is a reference to a GeneratorState object. The rest
-            // of codegen expects local 3 to be a reference to the thisObj.
-            // So move the value in local 3 to generatorStateLocal, and load
-            // the saved thisObj from the GeneratorState object.
-            cfw.addALoad(thisObjLocal);
-            generatorStateLocal = firstFreeLocal++;
-            localsMax = firstFreeLocal;
-            cfw.add(ByteCode.CHECKCAST, OptRuntime.GeneratorState.CLASS_NAME);
-            cfw.add(ByteCode.DUP);
-            cfw.addAStore(generatorStateLocal);
-            cfw.add(ByteCode.GETFIELD,
-                    OptRuntime.GeneratorState.CLASS_NAME,
-                    OptRuntime.GeneratorState.thisObj_NAME,
-                    OptRuntime.GeneratorState.thisObj_TYPE);
-            cfw.addAStore(thisObjLocal);
-
-            if (epilogueLabel == -1) {
-                epilogueLabel = cfw.acquireLabel();
-            }
-
-            List<Node> targets = ((FunctionNode)scriptOrFn).getResumptionPoints();
-            if (targets != null) {
-                // get resumption point
-                generateGetGeneratorResumptionPoint();
-
-                // generate dispatch table
-                generatorSwitch = cfw.addTableSwitch(0,
-                    targets.size() + GENERATOR_START);
-                generateCheckForThrowOrClose(-1, false, GENERATOR_START);
-            }
-        }
-
-        // Compile RegExp literals if this is a script. For functions
-        // this is performed during instantiation in functionInit
-        if (fnCurrent == null && scriptOrFn.getRegexpCount() != 0) {
-            cfw.addALoad(contextLocal);
-            cfw.addInvoke(ByteCode.INVOKESTATIC, codegen.mainClassName,
-                          Codegen.REGEXP_INIT_METHOD_NAME,
-                          Codegen.REGEXP_INIT_METHOD_SIGNATURE);
-        }
-
-        if (compilerEnv.isGenerateObserverCount())
-            saveCurrentCodeOffset();
-
-        if (hasVarsInRegs) {
-            // No need to create activation. Pad arguments if need be.
-            int parmCount = scriptOrFn.getParamCount();
-            if (parmCount > 0 && !inDirectCallFunction) {
-                // Set up args array
-                // check length of arguments, pad if need be
-                cfw.addALoad(argsLocal);
-                cfw.add(ByteCode.ARRAYLENGTH);
-                cfw.addPush(parmCount);
-                int label = cfw.acquireLabel();
-                cfw.add(ByteCode.IF_ICMPGE, label);
-                cfw.addALoad(argsLocal);
-                cfw.addPush(parmCount);
-                addScriptRuntimeInvoke("padArguments",
-                                       "([Ljava/lang/Object;I"
-                                       +")[Ljava/lang/Object;");
-                cfw.addAStore(argsLocal);
-                cfw.markLabel(label);
-            }
-
-            int paramCount = fnCurrent.fnode.getParamCount();
-            int varCount = fnCurrent.fnode.getParamAndVarCount();
-            boolean [] constDeclarations = fnCurrent.fnode.getParamAndVarConst();
-
-            // REMIND - only need to initialize the vars that don't get a value
-            // before the next call and are used in the function
-            short firstUndefVar = -1;
-            for (int i = 0; i != varCount; ++i) {
-                short reg = -1;
-                if (i < paramCount) {
-                    if (!inDirectCallFunction) {
-                        reg = getNewWordLocal();
-                        cfw.addALoad(argsLocal);
-                        cfw.addPush(i);
-                        cfw.add(ByteCode.AALOAD);
-                        cfw.addAStore(reg);
-                    }
-                } else if (fnCurrent.isNumberVar(i)) {
-                    reg = getNewWordPairLocal(constDeclarations[i]);
-                    cfw.addPush(0.0);
-                    cfw.addDStore(reg);
-                } else {
-                    reg = getNewWordLocal(constDeclarations[i]);
-                    if (firstUndefVar == -1) {
-                        Codegen.pushUndefined(cfw);
-                        firstUndefVar = reg;
-                    } else {
-                        cfw.addALoad(firstUndefVar);
-                    }
-                    cfw.addAStore(reg);
-                }
-                if (reg >= 0) {
-                    if (constDeclarations[i]) {
-                        cfw.addPush(0);
-                        cfw.addIStore(reg + (fnCurrent.isNumberVar(i) ? 2 : 1));
-                    }
-                    varRegisters[i] = reg;
-                }
-
-                // Add debug table entry if we're generating debug info
-                if (compilerEnv.isGenerateDebugInfo()) {
-                    String name = fnCurrent.fnode.getParamOrVarName(i);
-                    String type = fnCurrent.isNumberVar(i)
-                                      ? "D" : "Ljava/lang/Object;";
-                    int startPC = cfw.getCurrentCodeOffset();
-                    if (reg < 0) {
-                        reg = varRegisters[i];
-                    }
-                    cfw.addVariableDescriptor(name, type, startPC, reg);
-                }
-            }
-
-            // Skip creating activation object.
-            return;
-        }
-
-        // skip creating activation object for the body of a generator. The
-        // activation record required by a generator has already been created
-        // in generateGenerator().
-        if (isGenerator)
-            return;
-
-
-        String debugVariableName;
-        boolean isArrow = false;
-        if (scriptOrFn instanceof FunctionNode) {
-            isArrow = ((FunctionNode)scriptOrFn).getFunctionType() == FunctionNode.ARROW_FUNCTION;
-        }
-        if (fnCurrent != null) {
-            debugVariableName = "activation";
-            cfw.addALoad(funObjLocal);
-            cfw.addALoad(variableObjectLocal);
-            cfw.addALoad(argsLocal);
-            String methodName = isArrow ? "createArrowFunctionActivation" : "createFunctionActivation";
-            cfw.addPush(scriptOrFn.isInStrictMode());
-            addScriptRuntimeInvoke(methodName,
-                                   "(Lorg/mozilla/javascript/NativeFunction;"
-                                   +"Lorg/mozilla/javascript/Scriptable;"
-                                   +"[Ljava/lang/Object;"
-                                   +"Z"
-                                   +")Lorg/mozilla/javascript/Scriptable;");
-            cfw.addAStore(variableObjectLocal);
-            cfw.addALoad(contextLocal);
-            cfw.addALoad(variableObjectLocal);
-            addScriptRuntimeInvoke("enterActivationFunction",
-                                   "(Lorg/mozilla/javascript/Context;"
-                                   +"Lorg/mozilla/javascript/Scriptable;"
-                                   +")V");
-        } else {
-            debugVariableName = "global";
-            cfw.addALoad(funObjLocal);
-            cfw.addALoad(thisObjLocal);
-            cfw.addALoad(contextLocal);
-            cfw.addALoad(variableObjectLocal);
-            cfw.addPush(0); // false to indicate it is not eval script
-            addScriptRuntimeInvoke("initScript",
-                                   "(Lorg/mozilla/javascript/NativeFunction;"
-                                   +"Lorg/mozilla/javascript/Scriptable;"
-                                   +"Lorg/mozilla/javascript/Context;"
-                                   +"Lorg/mozilla/javascript/Scriptable;"
-                                   +"Z"
-                                   +")V");
-        }
-
-        enterAreaStartLabel = cfw.acquireLabel();
-        epilogueLabel = cfw.acquireLabel();
-        cfw.markLabel(enterAreaStartLabel);
-
-        generateNestedFunctionInits();
-
-        // default is to generate debug info
-        if (compilerEnv.isGenerateDebugInfo()) {
-            cfw.addVariableDescriptor(debugVariableName,
-                    "Lorg/mozilla/javascript/Scriptable;",
-                    cfw.getCurrentCodeOffset(), variableObjectLocal);
-        }
-
-        if (fnCurrent == null) {
-            // OPT: use dataflow to prove that this assignment is dead
-            popvLocal = getNewWordLocal();
-            Codegen.pushUndefined(cfw);
-            cfw.addAStore(popvLocal);
-
-            int linenum = scriptOrFn.getEndLineno();
-            if (linenum != -1)
-              cfw.addLineNumberEntry((short)linenum);
-
-        } else {
-            if (fnCurrent.itsContainsCalls0) {
-                itsZeroArgArray = getNewWordLocal();
-                cfw.add(ByteCode.GETSTATIC,
-                        "org/mozilla/javascript/ScriptRuntime",
-                        "emptyArgs", "[Ljava/lang/Object;");
-                cfw.addAStore(itsZeroArgArray);
-            }
-            if (fnCurrent.itsContainsCalls1) {
-                itsOneArgArray = getNewWordLocal();
-                cfw.addPush(1);
-                cfw.add(ByteCode.ANEWARRAY, "java/lang/Object");
-                cfw.addAStore(itsOneArgArray);
-            }
-        }
-    }
-
-    private void generateGetGeneratorResumptionPoint()
-    {
-        cfw.addALoad(generatorStateLocal);
-        cfw.add(ByteCode.GETFIELD,
-                OptRuntime.GeneratorState.CLASS_NAME,
-                OptRuntime.GeneratorState.resumptionPoint_NAME,
-                OptRuntime.GeneratorState.resumptionPoint_TYPE);
-    }
-
-    private void generateSetGeneratorResumptionPoint(int nextState)
-    {
-        cfw.addALoad(generatorStateLocal);
-        cfw.addLoadConstant(nextState);
-        cfw.add(ByteCode.PUTFIELD,
-                OptRuntime.GeneratorState.CLASS_NAME,
-                OptRuntime.GeneratorState.resumptionPoint_NAME,
-                OptRuntime.GeneratorState.resumptionPoint_TYPE);
-    }
-
-    private void generateGetGeneratorStackState()
-    {
-        cfw.addALoad(generatorStateLocal);
-        addOptRuntimeInvoke("getGeneratorStackState",
-                    "(Ljava/lang/Object;)[Ljava/lang/Object;");
-    }
-
-    private void generateEpilogue()
-    {
-        if (compilerEnv.isGenerateObserverCount())
-            addInstructionCount();
-        if (isGenerator) {
-            // generate locals initialization
-            Map<Node,int[]> liveLocals = ((FunctionNode)scriptOrFn).getLiveLocals();
-            if (liveLocals != null) {
-                List<Node> nodes = ((FunctionNode)scriptOrFn).getResumptionPoints();
-                for (int i = 0; i < nodes.size(); i++) {
-                    Node node = nodes.get(i);
-                    int[] live = liveLocals.get(node);
-                    if (live != null) {
-                        cfw.markTableSwitchCase(generatorSwitch,
-                            getNextGeneratorState(node));
-                        generateGetGeneratorLocalsState();
-                        for (int j = 0; j < live.length; j++) {
-                                cfw.add(ByteCode.DUP);
-                                cfw.addLoadConstant(j);
-                                cfw.add(ByteCode.AALOAD);
-                                cfw.addAStore(live[j]);
-                        }
-                        cfw.add(ByteCode.POP);
-                        cfw.add(ByteCode.GOTO, getTargetLabel(node));
-                    }
-                }
-            }
-
-            // generate dispatch tables for finally
-            if (finallys != null) {
-                for (Node n: finallys.keySet()) {
-                    if (n.getType() == Token.FINALLY) {
-                        FinallyReturnPoint ret = finallys.get(n);
-                        // the finally will jump here
-                        cfw.markLabel(ret.tableLabel, (short)1);
-
-                        // start generating a dispatch table
-                        int startSwitch = cfw.addTableSwitch(0,
-                                            ret.jsrPoints.size() - 1);
-                        int c = 0;
-                        cfw.markTableSwitchDefault(startSwitch);
-                        for (int i = 0; i < ret.jsrPoints.size(); i++) {
-                            // generate gotos back to the JSR location
-                            cfw.markTableSwitchCase(startSwitch, c);
-                            cfw.add(ByteCode.GOTO,
-                                    ret.jsrPoints.get(i).intValue());
-                            c++;
-                        }
-                    }
-                }
-            }
-        }
-
-        if (epilogueLabel != -1) {
-            cfw.markLabel(epilogueLabel);
-        }
-
-        if (hasVarsInRegs) {
-            cfw.add(ByteCode.ARETURN);
-            return;
-        } else if (isGenerator) {
-            if (((FunctionNode)scriptOrFn).getResumptionPoints() != null) {
-                cfw.markTableSwitchDefault(generatorSwitch);
-            }
-
-            // change state for re-entry
-            generateSetGeneratorResumptionPoint(GENERATOR_TERMINATE);
-
-            // throw StopIteration
-            cfw.addALoad(variableObjectLocal);
-            addOptRuntimeInvoke("throwStopIteration",
-                    "(Ljava/lang/Object;)V");
-
-            Codegen.pushUndefined(cfw);
-            cfw.add(ByteCode.ARETURN);
-
-        } else if (fnCurrent == null) {
-            cfw.addALoad(popvLocal);
-            cfw.add(ByteCode.ARETURN);
-        } else {
-            generateActivationExit();
-            cfw.add(ByteCode.ARETURN);
-
-            // Generate catch block to catch all and rethrow to call exit code
-            // under exception propagation as well.
-
-            int finallyHandler = cfw.acquireLabel();
-            cfw.markHandler(finallyHandler);
-            short exceptionObject = getNewWordLocal();
-            cfw.addAStore(exceptionObject);
-
-            // Duplicate generateActivationExit() in the catch block since it
-            // takes less space then full-featured ByteCode.JSR/ByteCode.RET
-            generateActivationExit();
-
-            cfw.addALoad(exceptionObject);
-            releaseWordLocal(exceptionObject);
-            // rethrow
-            cfw.add(ByteCode.ATHROW);
-
-            // mark the handler
-            cfw.addExceptionHandler(enterAreaStartLabel, epilogueLabel,
-                                    finallyHandler, null); // catch any
-        }
-    }
-
-    private void generateGetGeneratorLocalsState() {
-        cfw.addALoad(generatorStateLocal);
-        addOptRuntimeInvoke("getGeneratorLocalsState",
-                                "(Ljava/lang/Object;)[Ljava/lang/Object;");
-    }
-
-    private void generateActivationExit()
-    {
-        if (fnCurrent == null || hasVarsInRegs) throw Kit.codeBug();
-        cfw.addALoad(contextLocal);
-        addScriptRuntimeInvoke("exitActivationFunction",
-                               "(Lorg/mozilla/javascript/Context;)V");
-    }
-
-    private void generateStatement(Node node)
-    {
-        updateLineNumber(node);
-        int type = node.getType();
-        Node child = node.getFirstChild();
-        switch (type) {
-              case Token.LOOP:
-              case Token.LABEL:
-              case Token.WITH:
-              case Token.SCRIPT:
-              case Token.BLOCK:
-              case Token.EMPTY:
-                // no-ops.
-                if (compilerEnv.isGenerateObserverCount()) {
-                    // Need to add instruction count even for no-ops to catch
-                    // cases like while (1) {}
-                    addInstructionCount(1);
-                }
-                while (child != null) {
-                    generateStatement(child);
-                    child = child.getNext();
-                }
-                break;
-
-              case Token.LOCAL_BLOCK: {
-                boolean prevLocal = inLocalBlock;
-                inLocalBlock = true;
-                int local = getNewWordLocal();
-                if (isGenerator) {
-                    cfw.add(ByteCode.ACONST_NULL);
-                    cfw.addAStore(local);
-                }
-                node.putIntProp(Node.LOCAL_PROP, local);
-                while (child != null) {
-                    generateStatement(child);
-                    child = child.getNext();
-                }
-                releaseWordLocal((short)local);
-                node.removeProp(Node.LOCAL_PROP);
-                inLocalBlock = prevLocal;
-                break;
-              }
-
-              case Token.FUNCTION: {
-                int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
-                OptFunctionNode ofn = OptFunctionNode.get(scriptOrFn, fnIndex);
-                int t = ofn.fnode.getFunctionType();
-                if (t == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) {
-                    visitFunction(ofn, t);
-                } else {
-                    if (t != FunctionNode.FUNCTION_STATEMENT) {
-                        throw Codegen.badTree();
-                    }
-                }
-                break;
-              }
-
-              case Token.TRY:
-                visitTryCatchFinally((Jump)node, child);
-                break;
-
-              case Token.CATCH_SCOPE:
-                {
-                    // nothing stays on the stack on entry into a catch scope
-                    cfw.setStackTop((short) 0);
-
-                    int local = getLocalBlockRegister(node);
-                    int scopeIndex
-                        = node.getExistingIntProp(Node.CATCH_SCOPE_PROP);
-
-                    String name = child.getString(); // name of exception
-                    child = child.getNext();
-                    generateExpression(child, node); // load expression object
-                    if (scopeIndex == 0) {
-                        cfw.add(ByteCode.ACONST_NULL);
-                    } else {
-                        // Load previous catch scope object
-                        cfw.addALoad(local);
-                    }
-                    cfw.addPush(name);
-                    cfw.addALoad(contextLocal);
-                    cfw.addALoad(variableObjectLocal);
-
-                    addScriptRuntimeInvoke(
-                        "newCatchScope",
-                        "(Ljava/lang/Throwable;"
-                        +"Lorg/mozilla/javascript/Scriptable;"
-                        +"Ljava/lang/String;"
-                        +"Lorg/mozilla/javascript/Context;"
-                        +"Lorg/mozilla/javascript/Scriptable;"
-                        +")Lorg/mozilla/javascript/Scriptable;");
-                    cfw.addAStore(local);
-                }
-                break;
-
-              case Token.THROW:
-                generateExpression(child, node);
-                if (compilerEnv.isGenerateObserverCount())
-                    addInstructionCount();
-                generateThrowJavaScriptException();
-                break;
-
-              case Token.RETHROW:
-                if (compilerEnv.isGenerateObserverCount())
-                    addInstructionCount();
-                cfw.addALoad(getLocalBlockRegister(node));
-                cfw.add(ByteCode.ATHROW);
-                break;
-
-              case Token.RETURN_RESULT:
-              case Token.RETURN:
-                if (!isGenerator) {
-                    if (child != null) {
-                        generateExpression(child, node);
-                    } else if (type == Token.RETURN) {
-                        Codegen.pushUndefined(cfw);
-                    } else {
-                        if (popvLocal < 0) throw Codegen.badTree();
-                        cfw.addALoad(popvLocal);
-                    }
-                }
-                if (compilerEnv.isGenerateObserverCount())
-                    addInstructionCount();
-                if (epilogueLabel == -1) {
-                    if (!hasVarsInRegs) throw Codegen.badTree();
-                    epilogueLabel = cfw.acquireLabel();
-                }
-                cfw.add(ByteCode.GOTO, epilogueLabel);
-                break;
-
-              case Token.SWITCH:
-                if (compilerEnv.isGenerateObserverCount())
-                    addInstructionCount();
-                visitSwitch((Jump)node, child);
-                break;
-
-              case Token.ENTERWITH:
-                generateExpression(child, node);
-                cfw.addALoad(contextLocal);
-                cfw.addALoad(variableObjectLocal);
-                addScriptRuntimeInvoke(
-                    "enterWith",
-                    "(Ljava/lang/Object;"
-                    +"Lorg/mozilla/javascript/Context;"
-                    +"Lorg/mozilla/javascript/Scriptable;"
-                    +")Lorg/mozilla/javascript/Scriptable;");
-                cfw.addAStore(variableObjectLocal);
-                incReferenceWordLocal(variableObjectLocal);
-                break;
-
-              case Token.LEAVEWITH:
-                cfw.addALoad(variableObjectLocal);
-                addScriptRuntimeInvoke(
-                    "leaveWith",
-                    "(Lorg/mozilla/javascript/Scriptable;"
-                    +")Lorg/mozilla/javascript/Scriptable;");
-                cfw.addAStore(variableObjectLocal);
-                decReferenceWordLocal(variableObjectLocal);
-                break;
-
-              case Token.ENUM_INIT_KEYS:
-              case Token.ENUM_INIT_VALUES:
-              case Token.ENUM_INIT_ARRAY:
-              case Token.ENUM_INIT_VALUES_IN_ORDER:
-                generateExpression(child, node);
-                cfw.addALoad(contextLocal);
-                cfw.addALoad(variableObjectLocal);
-                int enumType = type == Token.ENUM_INIT_KEYS
-                                   ? ScriptRuntime.ENUMERATE_KEYS :
-                               type == Token.ENUM_INIT_VALUES
-                                   ? ScriptRuntime.ENUMERATE_VALUES :
-                               type == Token.ENUM_INIT_VALUES_IN_ORDER
-                                   ? ScriptRuntime.ENUMERATE_VALUES_IN_ORDER :
-                               ScriptRuntime.ENUMERATE_ARRAY;
-                cfw.addPush(enumType);
-                addScriptRuntimeInvoke("enumInit",
-                                       "(Ljava/lang/Object;"
-                                       +"Lorg/mozilla/javascript/Context;"
-                                       +"Lorg/mozilla/javascript/Scriptable;"
-                                       +"I"
-                                       +")Ljava/lang/Object;");
-                cfw.addAStore(getLocalBlockRegister(node));
-                break;
-
-              case Token.EXPR_VOID:
-                if (child.getType() == Token.SETVAR) {
-                    /* special case this so as to avoid unnecessary
-                    load's & pop's */
-                    visitSetVar(child, child.getFirstChild(), false);
-                }
-                else if (child.getType() == Token.SETCONSTVAR) {
-                    /* special case this so as to avoid unnecessary
-                    load's & pop's */
-                    visitSetConstVar(child, child.getFirstChild(), false);
-                }
-                else if (child.getType() == Token.YIELD) {
-                    generateYieldPoint(child, false);
-                }
-                else {
-                    generateExpression(child, node);
-                    if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1)
-                        cfw.add(ByteCode.POP2);
-                    else
-                        cfw.add(ByteCode.POP);
-                }
-                break;
-
-              case Token.EXPR_RESULT:
-                generateExpression(child, node);
-                if (popvLocal < 0) {
-                    popvLocal = getNewWordLocal();
-                }
-                cfw.addAStore(popvLocal);
-                break;
-
-              case Token.TARGET:
-                {
-                    if (compilerEnv.isGenerateObserverCount())
-                        addInstructionCount();
-                    int label = getTargetLabel(node);
-                    cfw.markLabel(label);
-                    if (compilerEnv.isGenerateObserverCount())
-                        saveCurrentCodeOffset();
-                }
-                break;
-
-              case Token.JSR:
-              case Token.GOTO:
-              case Token.IFEQ:
-              case Token.IFNE:
-                if (compilerEnv.isGenerateObserverCount())
-                    addInstructionCount();
-                visitGoto((Jump)node, type, child);
-                break;
-
-              case Token.FINALLY:
-                {
-                    // This is the non-exception case for a finally block. In
-                    // other words, since we inline finally blocks wherever
-                    // jsr was previously used, and jsr is only used when the
-                    // function is not a generator, we don't need to generate
-                    // this case if the function isn't a generator.
-                    if (!isGenerator) {
-                        break;
-                    }
-
-                    if (compilerEnv.isGenerateObserverCount())
-                        saveCurrentCodeOffset();
-                    // there is exactly one value on the stack when enterring
-                    // finally blocks: the return address (or its int encoding)
-                    cfw.setStackTop((short)1);
-
-                    // Save return address in a new local
-                    int finallyRegister = getNewWordLocal();
-
-                    int finallyStart = cfw.acquireLabel();
-                    int finallyEnd = cfw.acquireLabel();
-                    cfw.markLabel(finallyStart);
-
-                    generateIntegerWrap();
-                    cfw.addAStore(finallyRegister);
-
-                    while (child != null) {
-                        generateStatement(child);
-                        child = child.getNext();
-                    }
-
-                    cfw.addALoad(finallyRegister);
-                    cfw.add(ByteCode.CHECKCAST, "java/lang/Integer");
-                    generateIntegerUnwrap();
-                    FinallyReturnPoint ret = finallys.get(node);
-                    ret.tableLabel = cfw.acquireLabel();
-                    cfw.add(ByteCode.GOTO, ret.tableLabel);
-
-                    releaseWordLocal((short)finallyRegister);
-                    cfw.markLabel(finallyEnd);
-                }
-                break;
-
-              case Token.DEBUGGER:
-                break;
-
-              default:
-                throw Codegen.badTree();
-        }
-
-    }
-
-    private void generateIntegerWrap()
-    {
-        cfw.addInvoke(ByteCode.INVOKESTATIC, "java/lang/Integer", "valueOf",
-                "(I)Ljava/lang/Integer;");
-    }
-
-
-    private void generateIntegerUnwrap()
-    {
-        cfw.addInvoke(ByteCode.INVOKEVIRTUAL, "java/lang/Integer",
-                "intValue", "()I");
-    }
-
-
-    private void generateThrowJavaScriptException()
-    {
-        cfw.add(ByteCode.NEW,
-                        "org/mozilla/javascript/JavaScriptException");
-        cfw.add(ByteCode.DUP_X1);
-        cfw.add(ByteCode.SWAP);
-        cfw.addPush(scriptOrFn.getSourceName());
-        cfw.addPush(itsLineNumber);
-        cfw.addInvoke(
-                    ByteCode.INVOKESPECIAL,
-                    "org/mozilla/javascript/JavaScriptException",
-                    "<init>",
-                    "(Ljava/lang/Object;Ljava/lang/String;I)V");
-        cfw.add(ByteCode.ATHROW);
-    }
-
-    private int getNextGeneratorState(Node node)
-    {
-        int nodeIndex = ((FunctionNode)scriptOrFn).getResumptionPoints()
-                .indexOf(node);
-        return nodeIndex + GENERATOR_YIELD_START;
-    }
-
-    private void generateExpression(Node node, Node parent)
-    {
-        int type = node.getType();
-        Node child = node.getFirstChild();
-        switch (type) {
-              case Token.USE_STACK:
-                break;
-
-              case Token.FUNCTION:
-                if (fnCurrent != null || parent.getType() != Token.SCRIPT) {
-                    int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
-                    OptFunctionNode ofn = OptFunctionNode.get(scriptOrFn,
-                                                             fnIndex);
-                    int t = ofn.fnode.getFunctionType();
-                    if (t != FunctionNode.FUNCTION_EXPRESSION &&
-                        t != FunctionNode.ARROW_FUNCTION) {
-                        throw Codegen.badTree();
-                    }
-                    visitFunction(ofn, t);
-                }
-                break;
-
-              case Token.NAME:
-                {
-                    cfw.addALoad(contextLocal);
-                    cfw.addALoad(variableObjectLocal);
-                    cfw.addPush(node.getString());
-                    addScriptRuntimeInvoke(
-                        "name",
-                        "(Lorg/mozilla/javascript/Context;"
-                        +"Lorg/mozilla/javascript/Scriptable;"
-                        +"Ljava/lang/String;"
-                        +")Ljava/lang/Object;");
-                }
-                break;
-
-              case Token.CALL:
-              case Token.NEW:
-                {
-                    int specialType = node.getIntProp(Node.SPECIALCALL_PROP,
-                                                      Node.NON_SPECIALCALL);
-                    if (specialType == Node.NON_SPECIALCALL) {
-                        OptFunctionNode target;
-                        target = (OptFunctionNode)node.getProp(
-                                     Node.DIRECTCALL_PROP);
-
-                        if (target != null) {
-                            visitOptimizedCall(node, target, type, child);
-                        } else if (type == Token.CALL) {
-                            visitStandardCall(node, child);
-                        } else {
-                            visitStandardNew(node, child);
-                        }
-                    } else {
-                        visitSpecialCall(node, type, specialType, child);
-                    }
-                }
-                break;
-
-              case Token.REF_CALL:
-                generateFunctionAndThisObj(child, node);
-                // stack: ... functionObj thisObj
-                child = child.getNext();
-                generateCallArgArray(node, child, false);
-                cfw.addALoad(contextLocal);
-                addScriptRuntimeInvoke(
-                    "callRef",
-                    "(Lorg/mozilla/javascript/Callable;"
-                    +"Lorg/mozilla/javascript/Scriptable;"
-                    +"[Ljava/lang/Object;"
-                    +"Lorg/mozilla/javascript/Context;"
-                    +")Lorg/mozilla/javascript/Ref;");
-                break;
-
-              case Token.NUMBER:
-                {
-                    double num = node.getDouble();
-                    if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
-                        cfw.addPush(num);
-                    } else {
-                        codegen.pushNumberAsObject(cfw, num);
-                    }
-                }
-                break;
-
-              case Token.STRING:
-                cfw.addPush(node.getString());
-                break;
-
-              case Token.THIS:
-                cfw.addALoad(thisObjLocal);
-                break;
-
-              case Token.THISFN:
-                cfw.add(ByteCode.ALOAD_0);
-                break;
-
-              case Token.NULL:
-                cfw.add(ByteCode.ACONST_NULL);
-                break;
-
-              case Token.TRUE:
-                cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean",
-                        "TRUE", "Ljava/lang/Boolean;");
-                break;
-
-              case Token.FALSE:
-                cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean",
-                        "FALSE", "Ljava/lang/Boolean;");
-                break;
-
-              case Token.REGEXP:
-                {
-                    // Create a new wrapper around precompiled regexp
-                    cfw.addALoad(contextLocal);
-                    cfw.addALoad(variableObjectLocal);
-                    int i = node.getExistingIntProp(Node.REGEXP_PROP);
-                    cfw.add(ByteCode.GETSTATIC, codegen.mainClassName,
-                            codegen.getCompiledRegexpName(scriptOrFn, i),
-                            "Ljava/lang/Object;");
-                    cfw.addInvoke(ByteCode.INVOKESTATIC,
-                                  "org/mozilla/javascript/ScriptRuntime",
-                                  "wrapRegExp",
-                                  "(Lorg/mozilla/javascript/Context;"
-                                  +"Lorg/mozilla/javascript/Scriptable;"
-                                  +"Ljava/lang/Object;"
-                                  +")Lorg/mozilla/javascript/Scriptable;");
-                }
-                break;
-
-              case Token.COMMA: {
-                Node next = child.getNext();
-                while (next != null) {
-                    generateExpression(child, node);
-                    cfw.add(ByteCode.POP);
-                    child = next;
-                    next = next.getNext();
-                }
-                generateExpression(child, node);
-                break;
-              }
-
-              case Token.ENUM_NEXT:
-              case Token.ENUM_ID: {
-                int local = getLocalBlockRegister(node);
-                cfw.addALoad(local);
-                if (type == Token.ENUM_NEXT) {
-                    addScriptRuntimeInvoke(
-                        "enumNext", "(Ljava/lang/Object;)Ljava/lang/Boolean;");
-                } else {
-                    cfw.addALoad(contextLocal);
-                    addScriptRuntimeInvoke("enumId",
-                                           "(Ljava/lang/Object;"
-                                           +"Lorg/mozilla/javascript/Context;"
-                                           +")Ljava/lang/Object;");
-                }
-                break;
-              }
-
-              case Token.ARRAYLIT:
-                visitArrayLiteral(node, child, false);
-                break;
-
-              case Token.OBJECTLIT:
-                visitObjectLiteral(node, child, false);
-                break;
-
-              case Token.NOT: {
-                int trueTarget = cfw.acquireLabel();
-                int falseTarget = cfw.acquireLabel();
-                int beyond = cfw.acquireLabel();
-                generateIfJump(child, node, trueTarget, falseTarget);
-
-                cfw.markLabel(trueTarget);
-                cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean",
-                                        "FALSE", "Ljava/lang/Boolean;");
-                cfw.add(ByteCode.GOTO, beyond);
-                cfw.markLabel(falseTarget);
-                cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean",
-                                        "TRUE", "Ljava/lang/Boolean;");
-                cfw.markLabel(beyond);
-                cfw.adjustStackTop(-1);
-                break;
-              }
-
-              case Token.BITNOT:
-                generateExpression(child, node);
-                addScriptRuntimeInvoke("toInt32", "(Ljava/lang/Object;)I");
-                cfw.addPush(-1);         // implement ~a as (a ^ -1)
-                cfw.add(ByteCode.IXOR);
-                cfw.add(ByteCode.I2D);
-                addDoubleWrap();
-                break;
-
-              case Token.VOID:
-                generateExpression(child, node);
-                cfw.add(ByteCode.POP);
-                Codegen.pushUndefined(cfw);
-                break;
-
-              case Token.TYPEOF:
-                generateExpression(child, node);
-                addScriptRuntimeInvoke("typeof",
-                                       "(Ljava/lang/Object;"
-                                       +")Ljava/lang/String;");
-                break;
-
-              case Token.TYPEOFNAME:
-                visitTypeofname(node);
-                break;
-
-              case Token.INC:
-              case Token.DEC:
-                visitIncDec(node);
-                break;
-
-              case Token.OR:
-              case Token.AND: {
-                    generateExpression(child, node);
-                    cfw.add(ByteCode.DUP);
-                    addScriptRuntimeInvoke("toBoolean",
-                                           "(Ljava/lang/Object;)Z");
-                    int falseTarget = cfw.acquireLabel();
-                    if (type == Token.AND)
-                        cfw.add(ByteCode.IFEQ, falseTarget);
-                    else
-                        cfw.add(ByteCode.IFNE, falseTarget);
-                    cfw.add(ByteCode.POP);
-                    generateExpression(child.getNext(), node);
-                    cfw.markLabel(falseTarget);
-                }
-                break;
-
-              case Token.HOOK : {
-                    Node ifThen = child.getNext();
-                    Node ifElse = ifThen.getNext();
-                    generateExpression(child, node);
-                    addScriptRuntimeInvoke("toBoolean",
-                                           "(Ljava/lang/Object;)Z");
-                    int elseTarget = cfw.acquireLabel();
-                    cfw.add(ByteCode.IFEQ, elseTarget);
-                    short stack = cfw.getStackTop();
-                    generateExpression(ifThen, node);
-                    int afterHook = cfw.acquireLabel();
-                    cfw.add(ByteCode.GOTO, afterHook);
-                    cfw.markLabel(elseTarget, stack);
-                    generateExpression(ifElse, node);
-                    cfw.markLabel(afterHook);
-                }
-                break;
-
-              case Token.ADD: {
-                    generateExpression(child, node);
-                    generateExpression(child.getNext(), node);
-                    switch (node.getIntProp(Node.ISNUMBER_PROP, -1)) {
-                      case Node.BOTH:
-                        cfw.add(ByteCode.DADD);
-                        break;
-                      case Node.LEFT:
-                        addOptRuntimeInvoke("add",
-                            "(DLjava/lang/Object;)Ljava/lang/Object;");
-                        break;
-                      case Node.RIGHT:
-                        addOptRuntimeInvoke("add",
-                            "(Ljava/lang/Object;D)Ljava/lang/Object;");
-                        break;
-                      default:
-                        if (child.getType() == Token.STRING) {
-                            addScriptRuntimeInvoke("add",
-                                "(Ljava/lang/CharSequence;"
-                                +"Ljava/lang/Object;"
-                                +")Ljava/lang/CharSequence;");
-                        } else if (child.getNext().getType() == Token.STRING) {
-                            addScriptRuntimeInvoke("add",
-                                "(Ljava/lang/Object;"
-                                +"Ljava/lang/CharSequence;"
-                                +")Ljava/lang/CharSequence;");
-                        } else {
-                            cfw.addALoad(contextLocal);
-                            addScriptRuntimeInvoke("add",
-                                "(Ljava/lang/Object;"
-                                +"Ljava/lang/Object;"
-                                +"Lorg/mozilla/javascript/Context;"
-                                +")Ljava/lang/Object;");
-                        }
-                    }
-                }
-                break;
-
-              case Token.MUL:
-                visitArithmetic(node, ByteCode.DMUL, child, parent);
-                break;
-
-              case Token.SUB:
-                visitArithmetic(node, ByteCode.DSUB, child, parent);
-                break;
-
-              case Token.DIV:
-              case Token.MOD:
-                visitArithmetic(node, type == Token.DIV
-                                      ? ByteCode.DDIV
-                                      : ByteCode.DREM, child, parent);
-                break;
-
-              case Token.BITOR:
-              case Token.BITXOR:
-              case Token.BITAND:
-              case Token.LSH:
-              case Token.RSH:
-              case Token.URSH:
-                visitBitOp(node, type, child);
-                break;
-
-              case Token.POS:
-              case Token.NEG:
-                generateExpression(child, node);
-                addObjectToDouble();
-                if (type == Token.NEG) {
-                    cfw.add(ByteCode.DNEG);
-                }
-                addDoubleWrap();
-                break;
-
-              case Token.TO_DOUBLE:
-                // cnvt to double (not Double)
-                generateExpression(child, node);
-                addObjectToDouble();
-                break;
-
-              case Token.TO_OBJECT: {
-                // convert from double
-                int prop = -1;
-                if (child.getType() == Token.NUMBER) {
-                    prop = child.getIntProp(Node.ISNUMBER_PROP, -1);
-                }
-                if (prop != -1) {
-                    child.removeProp(Node.ISNUMBER_PROP);
-                    generateExpression(child, node);
-                    child.putIntProp(Node.ISNUMBER_PROP, prop);
-                } else {
-                    generateExpression(child, node);
-                    addDoubleWrap();
-                }
-                break;
-              }
-
-              case Token.IN:
-              case Token.INSTANCEOF:
-              case Token.LE:
-              case Token.LT:
-              case Token.GE:
-              case Token.GT: {
-                int trueGOTO = cfw.acquireLabel();
-                int falseGOTO = cfw.acquireLabel();
-                visitIfJumpRelOp(node, child, trueGOTO, falseGOTO);
-                addJumpedBooleanWrap(trueGOTO, falseGOTO);
-                break;
-              }
-
-              case Token.EQ:
-              case Token.NE:
-              case Token.SHEQ:
-              case Token.SHNE: {
-                int trueGOTO = cfw.acquireLabel();
-                int falseGOTO = cfw.acquireLabel();
-                visitIfJumpEqOp(node, child, trueGOTO, falseGOTO);
-                addJumpedBooleanWrap(trueGOTO, falseGOTO);
-                break;
-              }
-
-              case Token.GETPROP:
-              case Token.GETPROPNOWARN:
-                visitGetProp(node, child);
-                break;
-
-              case Token.GETELEM:
-                generateExpression(child, node); // object
-                generateExpression(child.getNext(), node);  // id
-                cfw.addALoad(contextLocal);
-                if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
-                    addScriptRuntimeInvoke(
-                        "getObjectIndex",
-                        "(Ljava/lang/Object;D"
-                        +"Lorg/mozilla/javascript/Context;"
-                        +")Ljava/lang/Object;");
-                }
-                else {
-                    cfw.addALoad(variableObjectLocal);
-                    addScriptRuntimeInvoke(
-                        "getObjectElem",
-                        "(Ljava/lang/Object;"
-                        +"Ljava/lang/Object;"
-                        +"Lorg/mozilla/javascript/Context;"
-                        +"Lorg/mozilla/javascript/Scriptable;"
-                        +")Ljava/lang/Object;");
-                }
-                break;
-
-              case Token.GET_REF:
-                generateExpression(child, node); // reference
-                cfw.addALoad(contextLocal);
-                addScriptRuntimeInvoke(
-                    "refGet",
-                    "(Lorg/mozilla/javascript/Ref;"
-                    +"Lorg/mozilla/javascript/Context;"
-                    +")Ljava/lang/Object;");
-                break;
-
-              case Token.GETVAR:
-                visitGetVar(node);
-                break;
-
-              case Token.SETVAR:
-                visitSetVar(node, child, true);
-                break;
-
-              case Token.SETNAME:
-                visitSetName(node, child);
-                break;
-
-              case Token.STRICT_SETNAME:
-                  visitStrictSetName(node, child);
-                  break;
-
-              case Token.SETCONST:
-                visitSetConst(node, child);
-                break;
-
-              case Token.SETCONSTVAR:
-                visitSetConstVar(node, child, true);
-                break;
-
-              case Token.SETPROP:
-              case Token.SETPROP_OP:
-                visitSetProp(type, node, child);
-                break;
-
-              case Token.SETELEM:
-              case Token.SETELEM_OP:
-                visitSetElem(type, node, child);
-                break;
-
-              case Token.SET_REF:
-              case Token.SET_REF_OP:
-                {
-                    generateExpression(child, node);
-                    child = child.getNext();
-                    if (type == Token.SET_REF_OP) {
-                        cfw.add(ByteCode.DUP);
-                        cfw.addALoad(contextLocal);
-                        addScriptRuntimeInvoke(
-                            "refGet",
-                            "(Lorg/mozilla/javascript/Ref;"
-                            +"Lorg/mozilla/javascript/Context;"
-                            +")Ljava/lang/Object;");
-                    }
-                    generateExpression(child, node);
-                    cfw.addALoad(contextLocal);
-                    cfw.addALoad(variableObjectLocal);
-                    addScriptRuntimeInvoke(
-                        "refSet",
-                        "(Lorg/mozilla/javascript/Ref;"
-                        +"Ljava/lang/Object;"
-                        +"Lorg/mozilla/javascript/Context;"
-                        +"Lorg/mozilla/javascript/Scriptable;"
-                        +")Ljava/lang/Object;");
-                }
-                break;
-
-              case Token.DEL_REF:
-                generateExpression(child, node);
-                cfw.addALoad(contextLocal);
-                addScriptRuntimeInvoke("refDel",
-                                       "(Lorg/mozilla/javascript/Ref;"
-                                       +"Lorg/mozilla/javascript/Context;"
-                                       +")Ljava/lang/Object;");
-                break;
-
-              case Token.DELPROP:
-                boolean isName = child.getType() == Token.BINDNAME;
-                generateExpression(child, node);
-                child = child.getNext();
-                generateExpression(child, node);
-                cfw.addALoad(contextLocal);
-                cfw.addPush(isName);
-                addScriptRuntimeInvoke("delete",
-                                       "(Ljava/lang/Object;"
-                                       +"Ljava/lang/Object;"
-                                       +"Lorg/mozilla/javascript/Context;"
-                                       +"Z)Ljava/lang/Object;");
-                break;
-
-              case Token.BINDNAME:
-                {
-                    while (child != null) {
-                        generateExpression(child, node);
-                        child = child.getNext();
-                    }
-                    // Generate code for "ScriptRuntime.bind(varObj, "s")"
-                    cfw.addALoad(contextLocal);
-                    cfw.addALoad(variableObjectLocal);
-                    cfw.addPush(node.getString());
-                    addScriptRuntimeInvoke(
-                        "bind",
-                        "(Lorg/mozilla/javascript/Context;"
-                        +"Lorg/mozilla/javascript/Scriptable;"
-                        +"Ljava/lang/String;"
-                        +")Lorg/mozilla/javascript/Scriptable;");
-                }
-                break;
-
-              case Token.LOCAL_LOAD:
-                cfw.addALoad(getLocalBlockRegister(node));
-                break;
-
-              case Token.REF_SPECIAL:
-                {
-                    String special = (String)node.getProp(Node.NAME_PROP);
-                    generateExpression(child, node);
-                    cfw.addPush(special);
-                    cfw.addALoad(contextLocal);
-                    cfw.addALoad(variableObjectLocal);
-                    addScriptRuntimeInvoke(
-                        "specialRef",
-                        "(Ljava/lang/Object;"
-                        +"Ljava/lang/String;"
-                        +"Lorg/mozilla/javascript/Context;"
-                        +"Lorg/mozilla/javascript/Scriptable;"
-                        +")Lorg/mozilla/javascript/Ref;");
-                }
-                break;
-
-              case Token.REF_MEMBER:
-              case Token.REF_NS_MEMBER:
-              case Token.REF_NAME:
-              case Token.REF_NS_NAME:
-                {
-                    int memberTypeFlags
-                        = node.getIntProp(Node.MEMBER_TYPE_PROP, 0);
-                    // generate possible target, possible namespace and member
-                    do {
-                        generateExpression(child, node);
-                        child = child.getNext();
-                    } while (child != null);
-                    cfw.addALoad(contextLocal);
-                    String methodName, signature;
-                    switch (type) {
-                      case Token.REF_MEMBER:
-                        methodName = "memberRef";
-                        signature = "(Ljava/lang/Object;"
-                                    +"Ljava/lang/Object;"
-                                    +"Lorg/mozilla/javascript/Context;"
-                                    +"I"
-                                    +")Lorg/mozilla/javascript/Ref;";
-                        break;
-                      case Token.REF_NS_MEMBER:
-                        methodName = "memberRef";
-                        signature = "(Ljava/lang/Object;"
-                                    +"Ljava/lang/Object;"
-                                    +"Ljava/lang/Object;"
-                                    +"Lorg/mozilla/javascript/Context;"
-                                    +"I"
-                                    +")Lorg/mozilla/javascript/Ref;";
-                        break;
-                      case Token.REF_NAME:
-                        methodName = "nameRef";
-                        signature = "(Ljava/lang/Object;"
-                                    +"Lorg/mozilla/javascript/Context;"
-                                    +"Lorg/mozilla/javascript/Scriptable;"
-                                    +"I"
-                                    +")Lorg/mozilla/javascript/Ref;";
-                        cfw.addALoad(variableObjectLocal);
-                        break;
-                      case Token.REF_NS_NAME:
-                        methodName = "nameRef";
-                        signature = "(Ljava/lang/Object;"
-                                    +"Ljava/lang/Object;"
-                                    +"Lorg/mozilla/javascript/Context;"
-                                    +"Lorg/mozilla/javascript/Scriptable;"
-                                    +"I"
-                                    +")Lorg/mozilla/javascript/Ref;";
-                        cfw.addALoad(variableObjectLocal);
-                        break;
-                      default:
-                        throw Kit.codeBug();
-                    }
-                    cfw.addPush(memberTypeFlags);
-                    addScriptRuntimeInvoke(methodName, signature);
-                }
-                break;
-
-              case Token.DOTQUERY:
-                visitDotQuery(node, child);
-                break;
-
-              case Token.ESCXMLATTR:
-                generateExpression(child, node);
-                cfw.addALoad(contextLocal);
-                addScriptRuntimeInvoke("escapeAttributeValue",
-                                       "(Ljava/lang/Object;"
-                                       +"Lorg/mozilla/javascript/Context;"
-                                       +")Ljava/lang/String;");
-                break;
-
-              case Token.ESCXMLTEXT:
-                generateExpression(child, node);
-                cfw.addALoad(contextLocal);
-                addScriptRuntimeInvoke("escapeTextValue",
-                                       "(Ljava/lang/Object;"
-                                       +"Lorg/mozilla/javascript/Context;"
-                                       +")Ljava/lang/String;");
-                break;
-
-              case Token.DEFAULTNAMESPACE:
-                generateExpression(child, node);
-                cfw.addALoad(contextLocal);
-                addScriptRuntimeInvoke("setDefaultNamespace",
-                                       "(Ljava/lang/Object;"
-                                       +"Lorg/mozilla/javascript/Context;"
-                                       +")Ljava/lang/Object;");
-                break;
-
-              case Token.YIELD:
-                generateYieldPoint(node, true);
-                break;
-
-              case Token.WITHEXPR: {
-                Node enterWith = child;
-                Node with = enterWith.getNext();
-                Node leaveWith = with.getNext();
-                generateStatement(enterWith);
-                generateExpression(with.getFirstChild(), with);
-                generateStatement(leaveWith);
-                break;
-              }
-
-              case Token.ARRAYCOMP: {
-                Node initStmt = child;
-                Node expr = child.getNext();
-                generateStatement(initStmt);
-                generateExpression(expr, node);
-                break;
-              }
-
-              default:
-                throw new RuntimeException("Unexpected node type "+type);
-        }
-
-    }
-
-    private void generateYieldPoint(Node node, boolean exprContext) {
-        // save stack state
-        int top = cfw.getStackTop();
-        maxStack = maxStack > top ? maxStack : top;
-        if (cfw.getStackTop() != 0) {
-            generateGetGeneratorStackState();
-            for (int i = 0; i < top; i++) {
-                cfw.add(ByteCode.DUP_X1);
-                cfw.add(ByteCode.SWAP);
-                cfw.addLoadConstant(i);
-                cfw.add(ByteCode.SWAP);
-                cfw.add(ByteCode.AASTORE);
-            }
-            // pop the array object
-            cfw.add(ByteCode.POP);
-        }
-
-        // generate the yield argument
-        Node child = node.getFirstChild();
-        if (child != null)
-            generateExpression(child, node);
-        else
-            Codegen.pushUndefined(cfw);
-
-        // change the resumption state
-        int nextState = getNextGeneratorState(node);
-        generateSetGeneratorResumptionPoint(nextState);
-
-        boolean hasLocals = generateSaveLocals(node);
-
-        cfw.add(ByteCode.ARETURN);
-
-        generateCheckForThrowOrClose(getTargetLabel(node),
-                hasLocals, nextState);
-
-        // reconstruct the stack
-        if (top != 0) {
-            generateGetGeneratorStackState();
-            for (int i = 0; i < top; i++) {
-                cfw.add(ByteCode.DUP);
-                cfw.addLoadConstant(top - i - 1);
-                cfw.add(ByteCode.AALOAD);
-                cfw.add(ByteCode.SWAP);
-            }
-            cfw.add(ByteCode.POP);
-        }
-
-        // load return value from yield
-        if (exprContext) {
-            cfw.addALoad(argsLocal);
-        }
-    }
-
-    private void generateCheckForThrowOrClose(int label,
-                                              boolean hasLocals,
-                                              int nextState) {
-        int throwLabel = cfw.acquireLabel();
-        int closeLabel = cfw.acquireLabel();
-
-        // throw the user provided object, if the operation is .throw()
-        cfw.markLabel(throwLabel);
-        cfw.addALoad(argsLocal);
-        generateThrowJavaScriptException();
-
-        // throw our special internal exception if the generator is being closed
-        cfw.markLabel(closeLabel);
-        cfw.addALoad(argsLocal);
-        cfw.add(ByteCode.CHECKCAST, "java/lang/Throwable");
-        cfw.add(ByteCode.ATHROW);
-
-        // mark the re-entry point
-        // jump here after initializing the locals
-        if (label != -1)
-            cfw.markLabel(label);
-        if (!hasLocals) {
-            // jump here directly if there are no locals
-            cfw.markTableSwitchCase(generatorSwitch, nextState);
-        }
-
-        // see if we need to dispatch for .close() or .throw()
-        cfw.addILoad(operationLocal);
-        cfw.addLoadConstant(NativeGenerator.GENERATOR_CLOSE);
-        cfw.add(ByteCode.IF_ICMPEQ, closeLabel);
-        cfw.addILoad(operationLocal);
-        cfw.addLoadConstant(NativeGenerator.GENERATOR_THROW);
-        cfw.add(ByteCode.IF_ICMPEQ, throwLabel);
-    }
-
-    private void generateIfJump(Node node, Node parent,
-                                int trueLabel, int falseLabel)
-    {
-        // System.out.println("gen code for " + node.toString());
-
-        int type = node.getType();
-        Node child = node.getFirstChild();
-
-        switch (type) {
-          case Token.NOT:
-            generateIfJump(child, node, falseLabel, trueLabel);
-            break;
-
-          case Token.OR:
-          case Token.AND: {
-            int interLabel = cfw.acquireLabel();
-            if (type == Token.AND) {
-                generateIfJump(child, node, interLabel, falseLabel);
-            }
-            else {
-                generateIfJump(child, node, trueLabel, interLabel);
-            }
-            cfw.markLabel(interLabel);
-            child = child.getNext();
-            generateIfJump(child, node, trueLabel, falseLabel);
-            break;
-          }
-
-          case Token.IN:
-          case Token.INSTANCEOF:
-          case Token.LE:
-          case Token.LT:
-          case Token.GE:
-          case Token.GT:
-            visitIfJumpRelOp(node, child, trueLabel, falseLabel);
-            break;
-
-          case Token.EQ:
-          case Token.NE:
-          case Token.SHEQ:
-          case Token.SHNE:
-            visitIfJumpEqOp(node, child, trueLabel, falseLabel);
-            break;
-
-          default:
-            // Generate generic code for non-optimized jump
-            generateExpression(node, parent);
-            addScriptRuntimeInvoke("toBoolean", "(Ljava/lang/Object;)Z");
-            cfw.add(ByteCode.IFNE, trueLabel);
-            cfw.add(ByteCode.GOTO, falseLabel);
-        }
-    }
-
-    private void visitFunction(OptFunctionNode ofn, int functionType)
-    {
-        int fnIndex = codegen.getIndex(ofn.fnode);
-        cfw.add(ByteCode.NEW, codegen.mainClassName);
-        // Call function constructor
-        cfw.add(ByteCode.DUP);
-        cfw.addALoad(variableObjectLocal);
-        cfw.addALoad(contextLocal);           // load 'cx'
-        cfw.addPush(fnIndex);
-        cfw.addInvoke(ByteCode.INVOKESPECIAL, codegen.mainClassName,
-                      "<init>", Codegen.FUNCTION_CONSTRUCTOR_SIGNATURE);
-
-        if (functionType == FunctionNode.ARROW_FUNCTION) {
-            cfw.addALoad(contextLocal);           // load 'cx'
-            cfw.addALoad(variableObjectLocal);
-            cfw.addALoad(thisObjLocal);
-            addOptRuntimeInvoke("bindThis",
-                                "(Lorg/mozilla/javascript/NativeFunction;"
-                                +"Lorg/mozilla/javascript/Context;"
-                                +"Lorg/mozilla/javascript/Scriptable;"
-                                +"Lorg/mozilla/javascript/Scriptable;"
-                                +")Lorg/mozilla/javascript/Function;");
-        }
-
-        if (functionType == FunctionNode.FUNCTION_EXPRESSION ||
-            functionType == FunctionNode.ARROW_FUNCTION) {
-            // Leave closure object on stack and do not pass it to
-            // initFunction which suppose to connect statements to scope
-            return;
-        }
-        cfw.addPush(functionType);
-        cfw.addALoad(variableObjectLocal);
-        cfw.addALoad(contextLocal);           // load 'cx'
-        addOptRuntimeInvoke("initFunction",
-                            "(Lorg/mozilla/javascript/NativeFunction;"
-                            +"I"
-                            +"Lorg/mozilla/javascript/Scriptable;"
-                            +"Lorg/mozilla/javascript/Context;"
-                            +")V");
-    }
-
-    private int getTargetLabel(Node target)
-    {
-        int labelId = target.labelId();
-        if (labelId == -1) {
-            labelId = cfw.acquireLabel();
-            target.labelId(labelId);
-        }
-        return labelId;
-    }
-
-    private void visitGoto(Jump node, int type, Node child)
-    {
-        Node target = node.target;
-        if (type == Token.IFEQ || type == Token.IFNE) {
-            if (child == null) throw Codegen.badTree();
-            int targetLabel = getTargetLabel(target);
-            int fallThruLabel = cfw.acquireLabel();
-            if (type == Token.IFEQ)
-                generateIfJump(child, node, targetLabel, fallThruLabel);
-            else
-                generateIfJump(child, node, fallThruLabel, targetLabel);
-            cfw.markLabel(fallThruLabel);
-        } else {
-            if (type == Token.JSR) {
-                if (isGenerator) {
-                    addGotoWithReturn(target);
-                } else {
-                    // This assumes that JSR is only ever used for finally
-                    inlineFinally(target);
-                }
-            } else {
-                addGoto(target, ByteCode.GOTO);
-            }
-        }
-    }
-
-    private void addGotoWithReturn(Node target) {
-        FinallyReturnPoint ret = finallys.get(target);
-        cfw.addLoadConstant(ret.jsrPoints.size());
-        addGoto(target, ByteCode.GOTO);
-        int retLabel = cfw.acquireLabel();
-        cfw.markLabel(retLabel);
-        ret.jsrPoints.add(Integer.valueOf(retLabel));
-    }
-
-    private void generateArrayLiteralFactory(Node node, int count) {
-        String methodName = codegen.getBodyMethodName(scriptOrFn) + "_literal" + count;
-        initBodyGeneration();
-        argsLocal = firstFreeLocal++;
-        localsMax = firstFreeLocal;
-        cfw.startMethod(methodName, "(Lorg/mozilla/javascript/Context;"
-                +"Lorg/mozilla/javascript/Scriptable;"
-                +"Lorg/mozilla/javascript/Scriptable;"
-                +"[Ljava/lang/Object;"
-                +")Lorg/mozilla/javascript/Scriptable;",
-                ACC_PRIVATE);
-        visitArrayLiteral(node, node.getFirstChild(), true);
-        cfw.add(ByteCode.ARETURN);
-        cfw.stopMethod((short)(localsMax + 1));
-    }
-
-    private void generateObjectLiteralFactory(Node node, int count) {
-        String methodName = codegen.getBodyMethodName(scriptOrFn) + "_literal" + count;
-        initBodyGeneration();
-        argsLocal = firstFreeLocal++;
-        localsMax = firstFreeLocal;
-        cfw.startMethod(methodName, "(Lorg/mozilla/javascript/Context;"
-                +"Lorg/mozilla/javascript/Scriptable;"
-                +"Lorg/mozilla/javascript/Scriptable;"
-                +"[Ljava/lang/Object;"
-                +")Lorg/mozilla/javascript/Scriptable;",
-                ACC_PRIVATE);
-        visitObjectLiteral(node, node.getFirstChild(), true);
-        cfw.add(ByteCode.ARETURN);
-        cfw.stopMethod((short)(localsMax + 1));
-    }
-
-
-    private void visitArrayLiteral(Node node, Node child, boolean topLevel)
-    {
-        int count = 0;
-        for (Node cursor = child; cursor != null; cursor = cursor.getNext()) {
-            ++count;
-        }
-
-        // If code budget is tight swap out literals into separate method
-        if (!topLevel && (count > 10 || cfw.getCurrentCodeOffset() > 30000)
-                && !hasVarsInRegs && !isGenerator && !inLocalBlock) {
-            if (literals == null) {
-                literals = new LinkedList<Node>();
-            }
-            literals.add(node);
-            String methodName = codegen.getBodyMethodName(scriptOrFn) + "_literal" + literals.size();
-            cfw.addALoad(funObjLocal);
-            cfw.addALoad(contextLocal);
-            cfw.addALoad(variableObjectLocal);
-            cfw.addALoad(thisObjLocal);
-            cfw.addALoad(argsLocal);
-            cfw.addInvoke(ByteCode.INVOKEVIRTUAL, codegen.mainClassName, methodName,
-                    "(Lorg/mozilla/javascript/Context;"
-                        +"Lorg/mozilla/javascript/Scriptable;"
-                        +"Lorg/mozilla/javascript/Scriptable;"
-                        +"[Ljava/lang/Object;"
-                        +")Lorg/mozilla/javascript/Scriptable;");
-            return;
-        }
-
-        // load array to store array literal objects
-        if (isGenerator) {
-            // TODO: this is actually only necessary if the yield operation is
-            // a child of this array or its children (bug 757410)
-            for (int i = 0; i != count; ++i) {
-                generateExpression(child, node);
-                child = child.getNext();
-            }
-            addNewObjectArray(count);
-            for (int i = 0; i != count; ++i) {
-                cfw.add(ByteCode.DUP_X1);
-                cfw.add(ByteCode.SWAP);
-                cfw.addPush(count - i - 1);
-                cfw.add(ByteCode.SWAP);
-                cfw.add(ByteCode.AASTORE);
-            }
-        } else {
-            addNewObjectArray(count);
-            for (int i = 0; i != count; ++i) {
-                cfw.add(ByteCode.DUP);
-                cfw.addPush(i);
-                generateExpression(child, node);
-                cfw.add(ByteCode.AASTORE);
-                child = child.getNext();
-            }
-        }
-        int[] skipIndexes = (int[])node.getProp(Node.SKIP_INDEXES_PROP);
-        if (skipIndexes == null) {
-            cfw.add(ByteCode.ACONST_NULL);
-            cfw.add(ByteCode.ICONST_0);
-        } else {
-            cfw.addPush(OptRuntime.encodeIntArray(skipIndexes));
-            cfw.addPush(skipIndexes.length);
-        }
-        cfw.addALoad(contextLocal);
-        cfw.addALoad(variableObjectLocal);
-        addOptRuntimeInvoke("newArrayLiteral",
-             "([Ljava/lang/Object;"
-             +"Ljava/lang/String;"
-             +"I"
-             +"Lorg/mozilla/javascript/Context;"
-             +"Lorg/mozilla/javascript/Scriptable;"
-             +")Lorg/mozilla/javascript/Scriptable;");
-    }
-
-    /** load array with property ids */
-    private void addLoadPropertyIds(Object[] properties, int count) {
-        addNewObjectArray(count);
-        for (int i = 0; i != count; ++i) {
-            cfw.add(ByteCode.DUP);
-            cfw.addPush(i);
-            Object id = properties[i];
-            if (id instanceof String) {
-                cfw.addPush((String)id);
-            } else {
-                cfw.addPush(((Integer)id).intValue());
-                addScriptRuntimeInvoke("wrapInt", "(I)Ljava/lang/Integer;");
-            }
-            cfw.add(ByteCode.AASTORE);
-        }
-    }
-
-    /** load array with property values */
-    private void addLoadPropertyValues(Node node, Node child, int count) {
-        if (isGenerator) {
-            // see bug 757410 for an explanation why we need to split this
-            for (int i = 0; i != count; ++i) {
-                int childType = child.getType();
-                if (childType == Token.GET || childType == Token.SET || childType == Token.METHOD) {
-                    generateExpression(child.getFirstChild(), node);
-                } else {
-                    generateExpression(child, node);
-                }
-                child = child.getNext();
-            }
-            addNewObjectArray(count);
-            for (int i = 0; i != count; ++i) {
-                cfw.add(ByteCode.DUP_X1);
-                cfw.add(ByteCode.SWAP);
-                cfw.addPush(count - i - 1);
-                cfw.add(ByteCode.SWAP);
-                cfw.add(ByteCode.AASTORE);
-            }
-        } else {
-            addNewObjectArray(count);
-            Node child2 = child;
-            for (int i = 0; i != count; ++i) {
-                cfw.add(ByteCode.DUP);
-                cfw.addPush(i);
-                int childType = child2.getType();
-                if (childType == Token.GET || childType == Token.SET || childType == Token.METHOD) {
-                    generateExpression(child2.getFirstChild(), node);
-                } else {
-                    generateExpression(child2, node);
-                }
-                cfw.add(ByteCode.AASTORE);
-                child2 = child2.getNext();
-            }
-        }
-    }
-
-    private void visitObjectLiteral(Node node, Node child, boolean topLevel)
-    {
-        Object[] properties = (Object[])node.getProp(Node.OBJECT_IDS_PROP);
-        int count = properties.length;
-
-        // If code budget is tight swap out literals into separate method
-        if (!topLevel && (count > 10 || cfw.getCurrentCodeOffset() > 30000)
-                && !hasVarsInRegs && !isGenerator && !inLocalBlock) {
-            if (literals == null) {
-                literals = new LinkedList<Node>();
-            }
-            literals.add(node);
-            String methodName = codegen.getBodyMethodName(scriptOrFn) + "_literal" + literals.size();
-            cfw.addALoad(funObjLocal);
-            cfw.addALoad(contextLocal);
-            cfw.addALoad(variableObjectLocal);
-            cfw.addALoad(thisObjLocal);
-            cfw.addALoad(argsLocal);
-            cfw.addInvoke(ByteCode.INVOKEVIRTUAL, codegen.mainClassName, methodName,
-                    "(Lorg/mozilla/javascript/Context;"
-                        +"Lorg/mozilla/javascript/Scriptable;"
-                        +"Lorg/mozilla/javascript/Scriptable;"
-                        +"[Ljava/lang/Object;"
-                        +")Lorg/mozilla/javascript/Scriptable;");
-            return;
-        }
-
-        if (isGenerator) {
-            // TODO: this is actually only necessary if the yield operation is
-            // a child of this object or its children (bug 757410)
-            addLoadPropertyValues(node, child, count);
-            addLoadPropertyIds(properties, count);
-            // swap property-values and property-ids arrays
-            cfw.add(ByteCode.SWAP);
-        } else {
-            addLoadPropertyIds(properties, count);
-            addLoadPropertyValues(node, child, count);
-        }
-
-        // check if object literal actually has any getters or setters
-        boolean hasGetterSetters = false;
-        Node child2 = child;
-        for (int i = 0; i != count; ++i) {
-            int childType = child2.getType();
-            if (childType == Token.GET || childType == Token.SET) {
-                hasGetterSetters = true;
-                break;
-            }
-            child2 = child2.getNext();
-        }
-        // create getter/setter flag array
-        if (hasGetterSetters) {
-            cfw.addPush(count);
-            cfw.add(ByteCode.NEWARRAY, ByteCode.T_INT);
-            child2 = child;
-            for (int i = 0; i != count; ++i) {
-                cfw.add(ByteCode.DUP);
-                cfw.addPush(i);
-                int childType = child2.getType();
-                if (childType == Token.GET) {
-                    cfw.add(ByteCode.ICONST_M1);
-                } else if (childType == Token.SET) {
-                    cfw.add(ByteCode.ICONST_1);
-                } else {
-                    cfw.add(ByteCode.ICONST_0);
-                }
-                cfw.add(ByteCode.IASTORE);
-                child2 = child2.getNext();
-            }
-        } else {
-            cfw.add(ByteCode.ACONST_NULL);
-        }
-
-        cfw.addALoad(contextLocal);
-        cfw.addALoad(variableObjectLocal);
-        addScriptRuntimeInvoke("newObjectLiteral",
-             "([Ljava/lang/Object;"
-             +"[Ljava/lang/Object;"
-             +"[I"
-             +"Lorg/mozilla/javascript/Context;"
-             +"Lorg/mozilla/javascript/Scriptable;"
-             +")Lorg/mozilla/javascript/Scriptable;");
-    }
-
-    private void visitSpecialCall(Node node, int type, int specialType,
-                                  Node child)
-    {
-        cfw.addALoad(contextLocal);
-
-        if (type == Token.NEW) {
-            generateExpression(child, node);
-            // stack: ... cx functionObj
-        } else {
-            generateFunctionAndThisObj(child, node);
-            // stack: ... cx functionObj thisObj
-        }
-        child = child.getNext();
-
-        generateCallArgArray(node, child, false);
-
-        String methodName;
-        String callSignature;
-
-        if (type == Token.NEW) {
-            methodName = "newObjectSpecial";
-            callSignature = "(Lorg/mozilla/javascript/Context;"
-                            +"Ljava/lang/Object;"
-                            +"[Ljava/lang/Object;"
-                            +"Lorg/mozilla/javascript/Scriptable;"
-                            +"Lorg/mozilla/javascript/Scriptable;"
-                            +"I" // call type
-                            +")Ljava/lang/Object;";
-            cfw.addALoad(variableObjectLocal);
-            cfw.addALoad(thisObjLocal);
-            cfw.addPush(specialType);
-        } else {
-            methodName = "callSpecial";
-            callSignature = "(Lorg/mozilla/javascript/Context;"
-                            +"Lorg/mozilla/javascript/Callable;"
-                            +"Lorg/mozilla/javascript/Scriptable;"
-                            +"[Ljava/lang/Object;"
-                            +"Lorg/mozilla/javascript/Scriptable;"
-                            +"Lorg/mozilla/javascript/Scriptable;"
-                            +"I" // call type
-                            +"Ljava/lang/String;I"  // filename, linenumber
-                            +")Ljava/lang/Object;";
-            cfw.addALoad(variableObjectLocal);
-            cfw.addALoad(thisObjLocal);
-            cfw.addPush(specialType);
-            String sourceName = scriptOrFn.getSourceName();
-            cfw.addPush(sourceName == null ? "" : sourceName);
-            cfw.addPush(itsLineNumber);
-        }
-
-        addOptRuntimeInvoke(methodName, callSignature);
-    }
-
-    private void visitStandardCall(Node node, Node child)
-    {
-        if (node.getType() != Token.CALL) throw Codegen.badTree();
-
-        Node firstArgChild = child.getNext();
-        int childType = child.getType();
-
-        String methodName;
-        String signature;
-
-        if (firstArgChild == null) {
-            if (childType == Token.NAME) {
-                // name() call
-                String name = child.getString();
-                cfw.addPush(name);
-                methodName = "callName0";
-                signature = "(Ljava/lang/String;"
-                            +"Lorg/mozilla/javascript/Context;"
-                            +"Lorg/mozilla/javascript/Scriptable;"
-                            +")Ljava/lang/Object;";
-            } else if (childType == Token.GETPROP) {
-                // x.name() call
-                Node propTarget = child.getFirstChild();
-                generateExpression(propTarget, node);
-                Node id = propTarget.getNext();
-                String property = id.getString();
-                cfw.addPush(property);
-                methodName = "callProp0";
-                signature = "(Ljava/lang/Object;"
-                            +"Ljava/lang/String;"
-                            +"Lorg/mozilla/javascript/Context;"
-                            +"Lorg/mozilla/javascript/Scriptable;"
-                            +")Ljava/lang/Object;";
-            } else if (childType == Token.GETPROPNOWARN) {
-                throw Kit.codeBug();
-            } else {
-                generateFunctionAndThisObj(child, node);
-                methodName = "call0";
-                signature = "(Lorg/mozilla/javascript/Callable;"
-                            +"Lorg/mozilla/javascript/Scriptable;"
-                            +"Lorg/mozilla/javascript/Context;"
-                            +"Lorg/mozilla/javascript/Scriptable;"
-                            +")Ljava/lang/Object;";
-            }
-
-        } else if (childType == Token.NAME) {
-            // XXX: this optimization is only possible if name
-            // resolution
-            // is not affected by arguments evaluation and currently
-            // there are no checks for it
-            String name = child.getString();
-            generateCallArgArray(node, firstArgChild, false);
-            cfw.addPush(name);
-            methodName = "callName";
-            signature = "([Ljava/lang/Object;"
-                        +"Ljava/lang/String;"
-                        +"Lorg/mozilla/javascript/Context;"
-                        +"Lorg/mozilla/javascript/Scriptable;"
-                        +")Ljava/lang/Object;";
-        } else {
-            int argCount = 0;
-            for (Node arg = firstArgChild; arg != null; arg = arg.getNext()) {
-                ++argCount;
-            }
-            generateFunctionAndThisObj(child, node);
-            // stack: ... functionObj thisObj
-            if (argCount == 1) {
-                generateExpression(firstArgChild, node);
-                methodName = "call1";
-                signature = "(Lorg/mozilla/javascript/Callable;"
-                            +"Lorg/mozilla/javascript/Scriptable;"
-                            +"Ljava/lang/Object;"
-                            +"Lorg/mozilla/javascript/Context;"
-                            +"Lorg/mozilla/javascript/Scriptable;"
-                            +")Ljava/lang/Object;";
-            } else if (argCount == 2) {
-                generateExpression(firstArgChild, node);
-                generateExpression(firstArgChild.getNext(), node);
-                methodName = "call2";
-                signature = "(Lorg/mozilla/javascript/Callable;"
-                            +"Lorg/mozilla/javascript/Scriptable;"
-                            +"Ljava/lang/Object;"
-                            +"Ljava/lang/Object;"
-                            +"Lorg/mozilla/javascript/Context;"
-                            +"Lorg/mozilla/javascript/Scriptable;"
-                            +")Ljava/lang/Object;";
-            } else {
-                generateCallArgArray(node, firstArgChild, false);
-                methodName = "callN";
-                signature = "(Lorg/mozilla/javascript/Callable;"
-                            +"Lorg/mozilla/javascript/Scriptable;"
-                            +"[Ljava/lang/Object;"
-                            +"Lorg/mozilla/javascript/Context;"
-                            +"Lorg/mozilla/javascript/Scriptable;"
-                            +")Ljava/lang/Object;";
-            }
-        }
-
-        cfw.addALoad(contextLocal);
-        cfw.addALoad(variableObjectLocal);
-        addOptRuntimeInvoke(methodName, signature);
-    }
-
-    private void visitStandardNew(Node node, Node child)
-    {
-        if (node.getType() != Token.NEW) throw Codegen.badTree();
-
-        Node firstArgChild = child.getNext();
-
-        generateExpression(child, node);
-        // stack: ... functionObj
-        cfw.addALoad(contextLocal);
-        cfw.addALoad(variableObjectLocal);
-        // stack: ... functionObj cx scope
-        generateCallArgArray(node, firstArgChild, false);
-        addScriptRuntimeInvoke(
-            "newObject",
-            "(Ljava/lang/Object;"
-            +"Lorg/mozilla/javascript/Context;"
-            +"Lorg/mozilla/javascript/Scriptable;"
-            +"[Ljava/lang/Object;"
-            +")Lorg/mozilla/javascript/Scriptable;");
-    }
-
-    private void visitOptimizedCall(Node node, OptFunctionNode target,
-                                    int type, Node child)
-    {
-        Node firstArgChild = child.getNext();
-        String className = codegen.mainClassName;
-
-        short thisObjLocal = 0;
-        if (type == Token.NEW) {
-            generateExpression(child, node);
-        } else {
-            generateFunctionAndThisObj(child, node);
-            thisObjLocal = getNewWordLocal();
-            cfw.addAStore(thisObjLocal);
-        }
-        // stack: ... functionObj
-
-        int beyond = cfw.acquireLabel();
-        int regularCall = cfw.acquireLabel();
-
-        cfw.add(ByteCode.DUP);
-        cfw.add(ByteCode.INSTANCEOF, className);
-        cfw.add(ByteCode.IFEQ, regularCall);
-        cfw.add(ByteCode.CHECKCAST, className);
-        cfw.add(ByteCode.DUP);
-        cfw.add(ByteCode.GETFIELD, className, Codegen.ID_FIELD_NAME, "I");
-        cfw.addPush(codegen.getIndex(target.fnode));
-        cfw.add(ByteCode.IF_ICMPNE, regularCall);
-
-        // stack: ... directFunct
-        cfw.addALoad(contextLocal);
-        cfw.addALoad(variableObjectLocal);
-        // stack: ... directFunc cx scope
-
-        if (type == Token.NEW) {
-            cfw.add(ByteCode.ACONST_NULL);
-        } else {
-            cfw.addALoad(thisObjLocal);
-        }
-        // stack: ... directFunc cx scope thisObj
-/*
-Remember that directCall parameters are paired in 1 aReg and 1 dReg
-If the argument is an incoming arg, just pass the orginal pair thru.
-Else, if the argument is known to be typed 'Number', pass Void.TYPE
-in the aReg and the number is the dReg
-Else pass the JS object in the aReg and 0.0 in the dReg.
-*/
-        Node argChild = firstArgChild;
-        while (argChild != null) {
-            int dcp_register = nodeIsDirectCallParameter(argChild);
-            if (dcp_register >= 0) {
-                cfw.addALoad(dcp_register);
-                cfw.addDLoad(dcp_register + 1);
-            } else if (argChild.getIntProp(Node.ISNUMBER_PROP, -1)
-                       == Node.BOTH)
-            {
-                cfw.add(ByteCode.GETSTATIC,
-                        "java/lang/Void",
-                        "TYPE",
-                        "Ljava/lang/Class;");
-                generateExpression(argChild, node);
-            } else {
-                generateExpression(argChild, node);
-                cfw.addPush(0.0);
-            }
-            argChild = argChild.getNext();
-        }
-
-        cfw.add(ByteCode.GETSTATIC,
-                "org/mozilla/javascript/ScriptRuntime",
-                "emptyArgs", "[Ljava/lang/Object;");
-        cfw.addInvoke(ByteCode.INVOKESTATIC,
-                      codegen.mainClassName,
-                      (type == Token.NEW)
-                          ? codegen.getDirectCtorName(target.fnode)
-                          : codegen.getBodyMethodName(target.fnode),
-                      codegen.getBodyMethodSignature(target.fnode));
-
-        cfw.add(ByteCode.GOTO, beyond);
-
-        cfw.markLabel(regularCall);
-        // stack: ... functionObj
-        cfw.addALoad(contextLocal);
-        cfw.addALoad(variableObjectLocal);
-        // stack: ... functionObj cx scope
-        if (type != Token.NEW) {
-            cfw.addALoad(thisObjLocal);
-            releaseWordLocal(thisObjLocal);
-            // stack: ... functionObj cx scope thisObj
-        }
-        // XXX: this will generate code for the child array the second time,
-        // so expression code generation better not to alter tree structure...
-        generateCallArgArray(node, firstArgChild, true);
-
-        if (type == Token.NEW) {
-            addScriptRuntimeInvoke(
-                "newObject",
-                "(Ljava/lang/Object;"
-                +"Lorg/mozilla/javascript/Context;"
-                +"Lorg/mozilla/javascript/Scriptable;"
-                +"[Ljava/lang/Object;"
-                +")Lorg/mozilla/javascript/Scriptable;");
-        } else {
-            cfw.addInvoke(ByteCode.INVOKEINTERFACE,
-                "org/mozilla/javascript/Callable",
-                "call",
-                "(Lorg/mozilla/javascript/Context;"
-                +"Lorg/mozilla/javascript/Scriptable;"
-                +"Lorg/mozilla/javascript/Scriptable;"
-                +"[Ljava/lang/Object;"
-                +")Ljava/lang/Object;");
-        }
-
-        cfw.markLabel(beyond);
-    }
-
-    private void generateCallArgArray(Node node, Node argChild, boolean directCall)
-    {
-        int argCount = 0;
-        for (Node child = argChild; child != null; child = child.getNext()) {
-            ++argCount;
-        }
-        // load array object to set arguments
-        if (argCount == 1 && itsOneArgArray >= 0) {
-            cfw.addALoad(itsOneArgArray);
-        } else {
-            addNewObjectArray(argCount);
-        }
-        // Copy arguments into it
-        for (int i = 0; i != argCount; ++i) {
-            // If we are compiling a generator an argument could be the result
-            // of a yield. In that case we will have an immediate on the stack
-            // which we need to avoid
-            if (!isGenerator) {
-                cfw.add(ByteCode.DUP);
-                cfw.addPush(i);
-            }
-
-            if (!directCall) {
-                generateExpression(argChild, node);
-            } else {
-                // If this has also been a directCall sequence, the Number
-                // flag will have remained set for any parameter so that
-                // the values could be copied directly into the outgoing
-                // args. Here we want to force it to be treated as not in
-                // a Number context, so we set the flag off.
-                int dcp_register = nodeIsDirectCallParameter(argChild);
-                if (dcp_register >= 0) {
-                    dcpLoadAsObject(dcp_register);
-                } else {
-                    generateExpression(argChild, node);
-                    int childNumberFlag
-                            = argChild.getIntProp(Node.ISNUMBER_PROP, -1);
-                    if (childNumberFlag == Node.BOTH) {
-                        addDoubleWrap();
-                    }
-                }
-            }
-
-            // When compiling generators, any argument to a method may be a
-            // yield expression. Hence we compile the argument first and then
-            // load the argument index and assign the value to the args array.
-            if (isGenerator) {
-                short tempLocal = getNewWordLocal();
-                cfw.addAStore(tempLocal);
-                cfw.add(ByteCode.CHECKCAST, "[Ljava/lang/Object;");
-                cfw.add(ByteCode.DUP);
-                cfw.addPush(i);
-                cfw.addALoad(tempLocal);
-                releaseWordLocal(tempLocal);
-            }
-
-            cfw.add(ByteCode.AASTORE);
-
-            argChild = argChild.getNext();
-        }
-    }
-
-    private void generateFunctionAndThisObj(Node node, Node parent)
-    {
-        // Place on stack (function object, function this) pair
-        int type = node.getType();
-        switch (node.getType()) {
-          case Token.GETPROPNOWARN:
-            throw Kit.codeBug();
-
-          case Token.GETPROP:
-          case Token.GETELEM: {
-            Node target = node.getFirstChild();
-            generateExpression(target, node);
-            Node id = target.getNext();
-            if (type == Token.GETPROP) {
-                String property = id.getString();
-                cfw.addPush(property);
-                cfw.addALoad(contextLocal);
-                cfw.addALoad(variableObjectLocal);
-                addScriptRuntimeInvoke(
-                    "getPropFunctionAndThis",
-                    "(Ljava/lang/Object;"
-                    +"Ljava/lang/String;"
-                    +"Lorg/mozilla/javascript/Context;"
-                    +"Lorg/mozilla/javascript/Scriptable;"
-                    +")Lorg/mozilla/javascript/Callable;");
-            } else {
-                generateExpression(id, node);  // id
-                if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1)
-                    addDoubleWrap();
-                cfw.addALoad(contextLocal);
-                cfw.addALoad(variableObjectLocal);
-                addScriptRuntimeInvoke(
-                    "getElemFunctionAndThis",
-                    "(Ljava/lang/Object;"
-                    +"Ljava/lang/Object;"
-                    +"Lorg/mozilla/javascript/Context;"
-                    +"Lorg/mozilla/javascript/Scriptable;"
-                    +")Lorg/mozilla/javascript/Callable;");
-            }
-            break;
-          }
-
-          case Token.NAME: {
-            String name = node.getString();
-            cfw.addPush(name);
-            cfw.addALoad(contextLocal);
-            cfw.addALoad(variableObjectLocal);
-            addScriptRuntimeInvoke(
-                "getNameFunctionAndThis",
-                "(Ljava/lang/String;"
-                +"Lorg/mozilla/javascript/Context;"
-                +"Lorg/mozilla/javascript/Scriptable;"
-                +")Lorg/mozilla/javascript/Callable;");
-            break;
-          }
-
-          default: // including GETVAR
-            generateExpression(node, parent);
-            cfw.addALoad(contextLocal);
-            addScriptRuntimeInvoke(
-                "getValueFunctionAndThis",
-                "(Ljava/lang/Object;"
-                +"Lorg/mozilla/javascript/Context;"
-                +")Lorg/mozilla/javascript/Callable;");
-            break;
-        }
-        // Get thisObj prepared by get(Name|Prop|Elem|Value)FunctionAndThis
-        cfw.addALoad(contextLocal);
-        addScriptRuntimeInvoke(
-            "lastStoredScriptable",
-            "(Lorg/mozilla/javascript/Context;"
-            +")Lorg/mozilla/javascript/Scriptable;");
-    }
-
-    private void updateLineNumber(Node node)
-    {
-        itsLineNumber = node.getLineno();
-        if (itsLineNumber == -1)
-            return;
-        cfw.addLineNumberEntry((short)itsLineNumber);
-    }
-
-    private void visitTryCatchFinally(Jump node, Node child)
-    {
-        /* Save the variable object, in case there are with statements
-         * enclosed by the try block and we catch some exception.
-         * We'll restore it for the catch block so that catch block
-         * statements get the right scope.
-         */
-
-        // OPT we only need to do this if there are enclosed WITH
-        // statements; could statically check and omit this if there aren't any.
-
-        // XXX OPT Maybe instead do syntactic transforms to associate
-        // each 'with' with a try/finally block that does the exitwith.
-
-        short savedVariableObject = getNewWordLocal();
-        cfw.addALoad(variableObjectLocal);
-        cfw.addAStore(savedVariableObject);
-
-        /*
-         * Generate the code for the tree; most of the work is done in IRFactory
-         * and NodeTransformer;  Codegen just adds the java handlers for the
-         * javascript catch and finally clauses.  */
-
-        int startLabel = cfw.acquireLabel();
-        cfw.markLabel(startLabel, (short)0);
-
-        Node catchTarget = node.target;
-        Node finallyTarget = node.getFinally();
-        int[] handlerLabels = new int[EXCEPTION_MAX];
-
-        exceptionManager.pushExceptionInfo(node);
-        if (catchTarget != null) {
-            handlerLabels[JAVASCRIPT_EXCEPTION] = cfw.acquireLabel();
-            handlerLabels[EVALUATOR_EXCEPTION] = cfw.acquireLabel();
-            handlerLabels[ECMAERROR_EXCEPTION] = cfw.acquireLabel();
-            Context cx = Context.getCurrentContext();
-            if (cx != null &&
-                cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)) {
-                handlerLabels[THROWABLE_EXCEPTION] = cfw.acquireLabel();
-            }
-        }
-        if (finallyTarget != null) {
-            handlerLabels[FINALLY_EXCEPTION] = cfw.acquireLabel();
-        }
-        exceptionManager.setHandlers(handlerLabels, startLabel);
-
-        // create a table for the equivalent of JSR returns
-        if (isGenerator && finallyTarget != null) {
-            FinallyReturnPoint ret = new FinallyReturnPoint();
-            if (finallys == null) {
-                finallys = new HashMap<Node,FinallyReturnPoint>();
-            }
-            // add the finally target to hashtable
-            finallys.put(finallyTarget, ret);
-            // add the finally node as well to the hash table
-            finallys.put(finallyTarget.getNext(), ret);
-        }
-
-        while (child != null) {
-            if (child == catchTarget) {
-                int catchLabel = getTargetLabel(catchTarget);
-                exceptionManager.removeHandler(JAVASCRIPT_EXCEPTION,
-                                               catchLabel);
-                exceptionManager.removeHandler(EVALUATOR_EXCEPTION,
-                                               catchLabel);
-                exceptionManager.removeHandler(ECMAERROR_EXCEPTION,
-                                               catchLabel);
-                exceptionManager.removeHandler(THROWABLE_EXCEPTION,
-                                               catchLabel);
-            }
-            generateStatement(child);
-            child = child.getNext();
-        }
-
-        // control flow skips the handlers
-        int realEnd = cfw.acquireLabel();
-        cfw.add(ByteCode.GOTO, realEnd);
-
-        int exceptionLocal = getLocalBlockRegister(node);
-        // javascript handler; unwrap exception and GOTO to javascript
-        // catch area.
-        if (catchTarget != null) {
-            // get the label to goto
-            int catchLabel = catchTarget.labelId();
-
-            // If the function is a generator, then handlerLabels will consist
-            // of zero labels. generateCatchBlock will create its own label
-            // in this case. The extra parameter for the label is added for
-            // the case of non-generator functions that inline finally blocks.
-
-            generateCatchBlock(JAVASCRIPT_EXCEPTION, savedVariableObject,
-                               catchLabel, exceptionLocal,
-                               handlerLabels[JAVASCRIPT_EXCEPTION]);
-            /*
-             * catch WrappedExceptions, see if they are wrapped
-             * JavaScriptExceptions. Otherwise, rethrow.
-             */
-            generateCatchBlock(EVALUATOR_EXCEPTION, savedVariableObject,
-                               catchLabel, exceptionLocal,
-                               handlerLabels[EVALUATOR_EXCEPTION]);
-
-            /*
-                we also need to catch EcmaErrors and feed the
-                associated error object to the handler
-            */
-            generateCatchBlock(ECMAERROR_EXCEPTION, savedVariableObject,
-                               catchLabel, exceptionLocal,
-                               handlerLabels[ECMAERROR_EXCEPTION]);
-
-            Context cx = Context.getCurrentContext();
-            if (cx != null &&
-                cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS))
-            {
-                generateCatchBlock(THROWABLE_EXCEPTION, savedVariableObject,
-                                   catchLabel, exceptionLocal,
-                                   handlerLabels[THROWABLE_EXCEPTION]);
-            }
-        }
-
-        // finally handler; catch all exceptions, store to a local; JSR to
-        // the finally, then re-throw.
-        if (finallyTarget != null) {
-            int finallyHandler = cfw.acquireLabel();
-            int finallyEnd = cfw.acquireLabel();
-            cfw.markHandler(finallyHandler);
-            if (!isGenerator) {
-                cfw.markLabel(handlerLabels[FINALLY_EXCEPTION]);
-            }
-            cfw.addAStore(exceptionLocal);
-
-            // reset the variable object local
-            cfw.addALoad(savedVariableObject);
-            cfw.addAStore(variableObjectLocal);
-
-            // get the label to JSR to
-            int finallyLabel = finallyTarget.labelId();
-            if (isGenerator)
-                addGotoWithReturn(finallyTarget);
-            else {
-                inlineFinally(finallyTarget, handlerLabels[FINALLY_EXCEPTION],
-                              finallyEnd);
-            }
-
-            // rethrow
-            cfw.addALoad(exceptionLocal);
-            if (isGenerator)
-                cfw.add(ByteCode.CHECKCAST, "java/lang/Throwable");
-            cfw.add(ByteCode.ATHROW);
-
-            cfw.markLabel(finallyEnd);
-            // mark the handler
-            if (isGenerator) {
-                cfw.addExceptionHandler(startLabel, finallyLabel,
-                                        finallyHandler, null); // catch any
-            }
-        }
-        releaseWordLocal(savedVariableObject);
-        cfw.markLabel(realEnd);
-
-        if (!isGenerator) {
-            exceptionManager.popExceptionInfo();
-        }
-    }
-
-    private static final int JAVASCRIPT_EXCEPTION  = 0;
-    private static final int EVALUATOR_EXCEPTION   = 1;
-    private static final int ECMAERROR_EXCEPTION   = 2;
-    private static final int THROWABLE_EXCEPTION   = 3;
-    // Finally catch-alls are technically Throwable, but we want a distinction
-    // for the exception manager and we want to use a null string instead of
-    // an explicit Throwable string.
-    private static final int FINALLY_EXCEPTION = 4;
-    private static final int EXCEPTION_MAX = 5;
-
-    private void generateCatchBlock(int exceptionType,
-                                    short savedVariableObject,
-                                    int catchLabel,
-                                    int exceptionLocal,
-                                    int handler)
-    {
-        if (handler == 0) {
-            handler = cfw.acquireLabel();
-        }
-        cfw.markHandler(handler);
-
-        // MS JVM gets cranky if the exception object is left on the stack
-        cfw.addAStore(exceptionLocal);
-
-        // reset the variable object local
-        cfw.addALoad(savedVariableObject);
-        cfw.addAStore(variableObjectLocal);
-
-        String exceptionName = exceptionTypeToName(exceptionType);
-
-        cfw.add(ByteCode.GOTO, catchLabel);
-    }
-
-    private String exceptionTypeToName(int exceptionType)
-    {
-        if (exceptionType == JAVASCRIPT_EXCEPTION) {
-            return "org/mozilla/javascript/JavaScriptException";
-        } else if (exceptionType == EVALUATOR_EXCEPTION) {
-            return "org/mozilla/javascript/EvaluatorException";
-        } else if (exceptionType == ECMAERROR_EXCEPTION) {
-            return "org/mozilla/javascript/EcmaError";
-        } else if (exceptionType == THROWABLE_EXCEPTION) {
-            return "java/lang/Throwable";
-        } else if (exceptionType == FINALLY_EXCEPTION) {
-            return null;
-        } else {
-            throw Kit.codeBug();
-        }
-    }
-
-    /**
-     * Manages placement of exception handlers for non-generator functions.
-     *
-     * For generator functions, there are mechanisms put into place to emulate
-     * jsr by using a goto with a return label. That is one mechanism for
-     * implementing finally blocks. The other, which is implemented by Sun,
-     * involves duplicating the finally block where jsr instructions would
-     * normally be. However, inlining finally blocks causes problems with
-     * translating exception handlers. Instead of having one big bytecode range
-     * for each exception, we now have to skip over the inlined finally blocks.
-     * This class is meant to help implement this.
-     *
-     * Every time a try block is encountered during translation, exception
-     * information should be pushed into the manager, which is treated as a
-     * stack. The addHandler() and setHandlers() methods may be used to register
-     * exceptionHandlers for the try block; removeHandler() is used to reverse
-     * the operation. At the end of the try/catch/finally, the exception state
-     * for it should be popped.
-     *
-     * The important function here is markInlineFinally. This finds which
-     * finally block on the exception state stack is being inlined and skips
-     * the proper exception handlers until the finally block is generated.
-     */
-    private class ExceptionManager
-    {
-        ExceptionManager()
-        {
-            exceptionInfo = new LinkedList<ExceptionInfo>();
-        }
-
-        /**
-         * Push a new try block onto the exception information stack.
-         *
-         * @param node an exception handling node (node.getType() ==
-         *             Token.TRY)
-         */
-        void pushExceptionInfo(Jump node)
-        {
-            Node fBlock = getFinallyAtTarget(node.getFinally());
-            ExceptionInfo ei = new ExceptionInfo(node, fBlock);
-            exceptionInfo.add(ei);
-        }
-
-        /**
-         * Register an exception handler for the try block at the top of the
-         * exception information stack.
-         *
-         * @param exceptionType one of the integer constants representing an
-         *                      exception type
-         * @param handlerLabel the label of the exception handler
-         * @param startLabel the label where the exception handling begins
-         */
-        void addHandler(int exceptionType, int handlerLabel, int startLabel)
-        {
-            ExceptionInfo top = getTop();
-            top.handlerLabels[exceptionType] = handlerLabel;
-            top.exceptionStarts[exceptionType] = startLabel;
-        }
-
-        /**
-         * Register multiple exception handlers for the top try block. If the
-         * exception type maps to a zero label, then it is ignored.
-         *
-         * @param handlerLabels a map from integer constants representing an
-         *                      exception type to the label of the exception
-         *                      handler
-         * @param startLabel the label where all of the exception handling
-         *                   begins
-         */
-        void setHandlers(int[] handlerLabels, int startLabel)
-        {
-            ExceptionInfo top = getTop();
-            for (int i = 0; i < handlerLabels.length; i++) {
-                if (handlerLabels[i] != 0) {
-                    addHandler(i, handlerLabels[i], startLabel);
-                }
-            }
-        }
-
-        /**
-         * Remove an exception handler for the top try block.
-         *
-         * @param exceptionType one of the integer constants representing an
-         *                      exception type
-         * @param endLabel a label representing the end of the last bytecode
-         *                 that should be handled by the exception
-         * @returns the label of the exception handler associated with the
-         *          exception type
-         */
-        int removeHandler(int exceptionType, int endLabel)
-        {
-            ExceptionInfo top = getTop();
-            if (top.handlerLabels[exceptionType] != 0) {
-                int handlerLabel = top.handlerLabels[exceptionType];
-                endCatch(top, exceptionType, endLabel);
-                top.handlerLabels[exceptionType] = 0;
-                return handlerLabel;
-            }
-            return 0;
-        }
-
-        /**
-         * Remove the top try block from the exception information stack.
-         */
-        void popExceptionInfo()
-        {
-            exceptionInfo.removeLast();
-        }
-
-        /**
-         * Mark the start of an inlined finally block.
-         *
-         * When a finally block is inlined, any exception handlers that are
-         * lexically inside of its try block should not cover the range of the
-         * exception block. We scan from the innermost try block outward until
-         * we find the try block that matches the finally block. For any block
-         * whose exception handlers that aren't currently stopped by a finally
-         * block, we stop the handlers at the beginning of the finally block
-         * and set it as the finally block that has stopped the handlers. This
-         * prevents other inlined finally blocks from prematurely ending skip
-         * ranges and creating bad exception handler ranges.
-         *
-         * @param finallyBlock the finally block that is being inlined
-         * @param finallyStart the label of the beginning of the inlined code
-         */
-        void markInlineFinallyStart(Node finallyBlock, int finallyStart)
-        {
-            // Traverse the stack in LIFO order until the try block
-            // corresponding to the finally block has been reached. We must
-            // traverse backwards because the earlier exception handlers in
-            // the exception handler table have priority when determining which
-            // handler to use. Therefore, we start with the most nested try
-            // block and move outward.
-            ListIterator<ExceptionInfo> iter =
-                    exceptionInfo.listIterator(exceptionInfo.size());
-            while (iter.hasPrevious()) {
-                ExceptionInfo ei = iter.previous();
-                for (int i = 0; i < EXCEPTION_MAX; i++) {
-                    if (ei.handlerLabels[i] != 0 && ei.currentFinally == null) {
-                        endCatch(ei, i, finallyStart);
-                        ei.exceptionStarts[i] = 0;
-                        ei.currentFinally = finallyBlock;
-                    }
-                }
-                if (ei.finallyBlock == finallyBlock) {
-                    break;
-                }
-            }
-        }
-
-        /**
-         * Mark the end of an inlined finally block.
-         *
-         * For any set of exception handlers that have been stopped by the
-         * inlined block, resume exception handling at the end of the finally
-         * block.
-         *
-         * @param finallyBlock the finally block that is being inlined
-         * @param finallyEnd the label of the end of the inlined code
-         */
-        void markInlineFinallyEnd(Node finallyBlock, int finallyEnd)
-        {
-            ListIterator<ExceptionInfo> iter =
-                    exceptionInfo.listIterator(exceptionInfo.size());
-            while (iter.hasPrevious()) {
-                ExceptionInfo ei = iter.previous();
-                for (int i = 0; i < EXCEPTION_MAX; i++) {
-                    if (ei.handlerLabels[i] != 0 &&
-                        ei.currentFinally == finallyBlock) {
-                        ei.exceptionStarts[i] = finallyEnd;
-                        ei.currentFinally = null;
-                    }
-                }
-                if (ei.finallyBlock == finallyBlock) {
-                    break;
-                }
-            }
-        }
-
-        /**
-         * Mark off the end of a bytecode chunk that should be handled by an
-         * exceptionHandler.
-         *
-         * The caller of this method must appropriately mark the start of the
-         * next bytecode chunk or remove the handler.
-         */
-        private void endCatch(ExceptionInfo ei, int exceptionType, int catchEnd)
-        {
-            if (ei.exceptionStarts[exceptionType] == 0) {
-                throw new IllegalStateException("bad exception start");
-            }
-
-            int currentStart = ei.exceptionStarts[exceptionType];
-            int currentStartPC = cfw.getLabelPC(currentStart);
-            int catchEndPC = cfw.getLabelPC(catchEnd);
-            if (currentStartPC != catchEndPC) {
-                cfw.addExceptionHandler(ei.exceptionStarts[exceptionType],
-                                        catchEnd,
-                                        ei.handlerLabels[exceptionType],
-                                        exceptionTypeToName(exceptionType));
-            }
-        }
-
-        private ExceptionInfo getTop()
-        {
-            return exceptionInfo.getLast();
-        }
-
-        private class ExceptionInfo
-        {
-            ExceptionInfo(Jump node, Node finallyBlock)
-            {
-                this.node = node;
-                this.finallyBlock = finallyBlock;
-                handlerLabels = new int[EXCEPTION_MAX];
-                exceptionStarts = new int[EXCEPTION_MAX];
-                currentFinally = null;
-            }
-
-            Jump node;
-            Node finallyBlock;
-            int[] handlerLabels;
-            int[] exceptionStarts;
-            // The current finally block that has temporarily ended the
-            // exception handler ranges
-            Node currentFinally;
-        }
-
-        // A stack of try/catch block information ordered by lexical scoping
-        private LinkedList<ExceptionInfo> exceptionInfo;
-    }
-
-    private ExceptionManager exceptionManager = new ExceptionManager();
-
-    /**
-     * Inline a FINALLY node into the method bytecode.
-     *
-     * This method takes a label that points to the real start of the finally
-     * block as implemented in the bytecode. This is because in some cases,
-     * the finally block really starts before any of the code in the Node. For
-     * example, the catch-all-rethrow finally block has a few instructions
-     * prior to the finally block made by the user.
-     *
-     * In addition, an end label that should be unmarked is given as a method
-     * parameter. It is the responsibility of any callers of this method to
-     * mark the label.
-     *
-     * The start and end labels of the finally block are used to exclude the
-     * inlined block from the proper exception handler. For example, an inlined
-     * finally block should not be handled by a catch-all-rethrow.
-     *
-     * @param finallyTarget a TARGET node directly preceding a FINALLY node or
-     *                      a FINALLY node itself
-     * @param finallyStart a pre-marked label that indicates the actual start
-     *                     of the finally block in the bytecode.
-     * @param finallyEnd an unmarked label that will indicate the actual end
-     *                   of the finally block in the bytecode.
-     */
-    private void inlineFinally(Node finallyTarget, int finallyStart,
-                               int finallyEnd) {
-        Node fBlock = getFinallyAtTarget(finallyTarget);
-        fBlock.resetTargets();
-        Node child = fBlock.getFirstChild();
-        exceptionManager.markInlineFinallyStart(fBlock, finallyStart);
-        while (child != null) {
-            generateStatement(child);
-            child = child.getNext();
-        }
-        exceptionManager.markInlineFinallyEnd(fBlock, finallyEnd);
-    }
-
-    private void inlineFinally(Node finallyTarget) {
-        int finallyStart = cfw.acquireLabel();
-        int finallyEnd = cfw.acquireLabel();
-        cfw.markLabel(finallyStart);
-        inlineFinally(finallyTarget, finallyStart, finallyEnd);
-        cfw.markLabel(finallyEnd);
-    }
-
-    /**
-     * Get a FINALLY node at a point in the IR.
-     *
-     * This is strongly dependent on the generated IR. If the node is a TARGET,
-     * it only check the next node to see if it is a FINALLY node.
-     */
-    private Node getFinallyAtTarget(Node node) {
-        if (node == null) {
-            return null;
-        } else if (node.getType() == Token.FINALLY) {
-            return node;
-        } else if (node != null && node.getType() == Token.TARGET) {
-            Node fBlock = node.getNext();
-            if (fBlock != null && fBlock.getType() == Token.FINALLY) {
-                return fBlock;
-            }
-        }
-        throw Kit.codeBug("bad finally target");
-    }
-
-    private boolean generateSaveLocals(Node node)
-    {
-        int count = 0;
-        for (int i = 0; i < firstFreeLocal; i++) {
-            if (locals[i] != 0)
-                count++;
-        }
-
-        if (count == 0) {
-            ((FunctionNode)scriptOrFn).addLiveLocals(node, null);
-            return false;
-        }
-
-        // calculate the max locals
-        maxLocals = maxLocals > count ? maxLocals : count;
-
-        // create a locals list
-        int[] ls = new int[count];
-        int s = 0;
-        for (int i = 0; i < firstFreeLocal; i++) {
-            if (locals[i] != 0) {
-                ls[s] = i;
-                s++;
-            }
-        }
-
-        // save the locals
-        ((FunctionNode)scriptOrFn).addLiveLocals(node, ls);
-
-        // save locals
-        generateGetGeneratorLocalsState();
-        for (int i = 0; i < count; i++) {
-            cfw.add(ByteCode.DUP);
-            cfw.addLoadConstant(i);
-            cfw.addALoad(ls[i]);
-            cfw.add(ByteCode.AASTORE);
-        }
-        // pop the array off the stack
-        cfw.add(ByteCode.POP);
-
-        return true;
-    }
-
-    private void visitSwitch(Jump switchNode, Node child)
-    {
-        // See comments in IRFactory.createSwitch() for description
-        // of SWITCH node
-
-        generateExpression(child, switchNode);
-        // save selector value
-        short selector = getNewWordLocal();
-        cfw.addAStore(selector);
-
-        for (Jump caseNode = (Jump)child.getNext();
-             caseNode != null;
-             caseNode = (Jump)caseNode.getNext())
-        {
-            if (caseNode.getType() != Token.CASE)
-                throw Codegen.badTree();
-            Node test = caseNode.getFirstChild();
-            generateExpression(test, caseNode);
-            cfw.addALoad(selector);
-            addScriptRuntimeInvoke("shallowEq",
-                                   "(Ljava/lang/Object;"
-                                   +"Ljava/lang/Object;"
-                                   +")Z");
-            addGoto(caseNode.target, ByteCode.IFNE);
-        }
-        releaseWordLocal(selector);
-    }
-
-    private void visitTypeofname(Node node)
-    {
-        if (hasVarsInRegs) {
-            int varIndex = fnCurrent.fnode.getIndexForNameNode(node);
-            if (varIndex >= 0) {
-                if (fnCurrent.isNumberVar(varIndex)) {
-                    cfw.addPush("number");
-                } else if (varIsDirectCallParameter(varIndex)) {
-                    int dcp_register = varRegisters[varIndex];
-                    cfw.addALoad(dcp_register);
-                    cfw.add(ByteCode.GETSTATIC, "java/lang/Void", "TYPE",
-                            "Ljava/lang/Class;");
-                    int isNumberLabel = cfw.acquireLabel();
-                    cfw.add(ByteCode.IF_ACMPEQ, isNumberLabel);
-                    short stack = cfw.getStackTop();
-                    cfw.addALoad(dcp_register);
-                    addScriptRuntimeInvoke("typeof",
-                                           "(Ljava/lang/Object;"
-                                           +")Ljava/lang/String;");
-                    int beyond = cfw.acquireLabel();
-                    cfw.add(ByteCode.GOTO, beyond);
-                    cfw.markLabel(isNumberLabel, stack);
-                    cfw.addPush("number");
-                    cfw.markLabel(beyond);
-                } else {
-                    cfw.addALoad(varRegisters[varIndex]);
-                    addScriptRuntimeInvoke("typeof",
-                                           "(Ljava/lang/Object;"
-                                           +")Ljava/lang/String;");
-                }
-                return;
-            }
-        }
-        cfw.addALoad(variableObjectLocal);
-        cfw.addPush(node.getString());
-        addScriptRuntimeInvoke("typeofName",
-                               "(Lorg/mozilla/javascript/Scriptable;"
-                               +"Ljava/lang/String;"
-                               +")Ljava/lang/String;");
-    }
-
-    /**
-     * Save the current code offset. This saved code offset is used to
-     * compute instruction counts in subsequent calls to
-     * {@link #addInstructionCount()}.
-     */
-    private void saveCurrentCodeOffset() {
-        savedCodeOffset = cfw.getCurrentCodeOffset();
-    }
-
-    /**
-     * Generate calls to ScriptRuntime.addInstructionCount to keep track of
-     * executed instructions and call <code>observeInstructionCount()</code>
-     * if a threshold is exceeded.<br>
-     * Calculates the count from getCurrentCodeOffset - savedCodeOffset
-     */
-    private void addInstructionCount() {
-        int count = cfw.getCurrentCodeOffset() - savedCodeOffset;
-        // TODO we used to return for count == 0 but that broke the following:
-        //    while(true) continue; (see bug 531600)
-        // To be safe, we now always count at least 1 instruction when invoked.
-        addInstructionCount(Math.max(count, 1));
-    }
-
-    /**
-     * Generate calls to ScriptRuntime.addInstructionCount to keep track of
-     * executed instructions and call <code>observeInstructionCount()</code>
-     * if a threshold is exceeded.<br>
-     * Takes the count as a parameter - used to add monitoring to loops and
-     * other blocks that don't have any ops - this allows
-     * for monitoring/killing of while(true) loops and such.
-     */
-    private void addInstructionCount(int count) {
-        cfw.addALoad(contextLocal);
-        cfw.addPush(count);
-        addScriptRuntimeInvoke("addInstructionCount",
-                "(Lorg/mozilla/javascript/Context;"
-                +"I)V");
-    }
-
-    private void visitIncDec(Node node)
-    {
-        int incrDecrMask = node.getExistingIntProp(Node.INCRDECR_PROP);
-        Node child = node.getFirstChild();
-        switch (child.getType()) {
-          case Token.GETVAR:
-            if (!hasVarsInRegs) Kit.codeBug();
-            boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
-            int varIndex = fnCurrent.getVarIndex(child);
-            short reg = varRegisters[varIndex];
-            boolean[] constDeclarations = fnCurrent.fnode.getParamAndVarConst();
-            if (constDeclarations[varIndex]) {
-                if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
-                    int offset = varIsDirectCallParameter(varIndex) ? 1 : 0;
-                    cfw.addDLoad(reg + offset);
-                    if (!post) {
-                        cfw.addPush(1.0);
-                        if ((incrDecrMask & Node.DECR_FLAG) == 0) {
-                            cfw.add(ByteCode.DADD);
-                        } else {
-                            cfw.add(ByteCode.DSUB);
-                        }
-                    }
-                } else {
-                    if (varIsDirectCallParameter(varIndex)) {
-                        dcpLoadAsObject(reg);
-                    } else {
-                        cfw.addALoad(reg);
-                    }
-                    if (post) {
-                        cfw.add(ByteCode.DUP);
-                        addObjectToDouble();
-                        cfw.add(ByteCode.POP2);
-                    } else {
-                        addObjectToDouble();
-                        cfw.addPush(1.0);
-                        if ((incrDecrMask & Node.DECR_FLAG) == 0) {
-                            cfw.add(ByteCode.DADD);
-                        } else {
-                            cfw.add(ByteCode.DSUB);
-                        }
-                        addDoubleWrap();
-                    }
-                }
-                break;
-            }
-            if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
-                int offset = varIsDirectCallParameter(varIndex) ? 1 : 0;
-                cfw.addDLoad(reg + offset);
-                if (post) {
-                    cfw.add(ByteCode.DUP2);
-                }
-                cfw.addPush(1.0);
-                if ((incrDecrMask & Node.DECR_FLAG) == 0) {
-                    cfw.add(ByteCode.DADD);
-                } else {
-                    cfw.add(ByteCode.DSUB);
-                }
-                if (!post) {
-                    cfw.add(ByteCode.DUP2);
-                }
-                cfw.addDStore(reg + offset);
-            } else {
-                if (varIsDirectCallParameter(varIndex)) {
-                    dcpLoadAsObject(reg);
-                } else {
-                    cfw.addALoad(reg);
-                }
-                addObjectToDouble();
-                if (post) {
-                    cfw.add(ByteCode.DUP2);
-                }
-                cfw.addPush(1.0);
-                if ((incrDecrMask & Node.DECR_FLAG) == 0) {
-                    cfw.add(ByteCode.DADD);
-                } else {
-                    cfw.add(ByteCode.DSUB);
-                }
-                addDoubleWrap();
-                if (!post) {
-                    cfw.add(ByteCode.DUP);
-                }
-                cfw.addAStore(reg);
-                if (post) {
-                    addDoubleWrap();
-                }
-            }
-            break;
-          case Token.NAME:
-            cfw.addALoad(variableObjectLocal);
-            cfw.addPush(child.getString());          // push name
-            cfw.addALoad(contextLocal);
-            cfw.addPush(incrDecrMask);
-            addScriptRuntimeInvoke("nameIncrDecr",
-                "(Lorg/mozilla/javascript/Scriptable;"
-                +"Ljava/lang/String;"
-                +"Lorg/mozilla/javascript/Context;"
-                +"I)Ljava/lang/Object;");
-            break;
-          case Token.GETPROPNOWARN:
-            throw Kit.codeBug();
-          case Token.GETPROP: {
-            Node getPropChild = child.getFirstChild();
-            generateExpression(getPropChild, node);
-            generateExpression(getPropChild.getNext(), node);
-            cfw.addALoad(contextLocal);
-            cfw.addALoad(variableObjectLocal);
-            cfw.addPush(incrDecrMask);
-            addScriptRuntimeInvoke("propIncrDecr",
-                                   "(Ljava/lang/Object;"
-                                   +"Ljava/lang/String;"
-                                   +"Lorg/mozilla/javascript/Context;"
-                                   +"Lorg/mozilla/javascript/Scriptable;"
-                                   +"I)Ljava/lang/Object;");
-            break;
-          }
-          case Token.GETELEM: {
-            Node elemChild = child.getFirstChild();
-            generateExpression(elemChild, node);
-            generateExpression(elemChild.getNext(), node);
-            cfw.addALoad(contextLocal);
-            cfw.addALoad(variableObjectLocal);
-            cfw.addPush(incrDecrMask);
-            if (elemChild.getNext().getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
-              addOptRuntimeInvoke("elemIncrDecr",
-                  "(Ljava/lang/Object;"
-                  +"D"
-                  +"Lorg/mozilla/javascript/Context;"
-                  +"Lorg/mozilla/javascript/Scriptable;"
-                  +"I"
-                  +")Ljava/lang/Object;");
-            } else {
-              addScriptRuntimeInvoke("elemIncrDecr",
-                  "(Ljava/lang/Object;"
-                  +"Ljava/lang/Object;"
-                  +"Lorg/mozilla/javascript/Context;"
-                  +"Lorg/mozilla/javascript/Scriptable;"
-                  +"I"
-                  +")Ljava/lang/Object;");
-            }
-            break;
-          }
-          case Token.GET_REF: {
-            Node refChild = child.getFirstChild();
-            generateExpression(refChild, node);
-            cfw.addALoad(contextLocal);
-            cfw.addALoad(variableObjectLocal);
-            cfw.addPush(incrDecrMask);
-            addScriptRuntimeInvoke(
-                "refIncrDecr",
-                "(Lorg/mozilla/javascript/Ref;"
-                +"Lorg/mozilla/javascript/Context;"
-                +"Lorg/mozilla/javascript/Scriptable;"
-                +"I)Ljava/lang/Object;");
-            break;
-          }
-          default:
-            Codegen.badTree();
-        }
-    }
-
-    private static boolean isArithmeticNode(Node node)
-    {
-        int type = node.getType();
-        return (type == Token.SUB)
-                  || (type == Token.MOD)
-                        || (type == Token.DIV)
-                              || (type == Token.MUL);
-    }
-
-    private void visitArithmetic(Node node, int opCode, Node child,
-                                 Node parent)
-    {
-        int childNumberFlag = node.getIntProp(Node.ISNUMBER_PROP, -1);
-        if (childNumberFlag != -1) {
-            generateExpression(child, node);
-            generateExpression(child.getNext(), node);
-            cfw.add(opCode);
-        }
-        else {
-            boolean childOfArithmetic = isArithmeticNode(parent);
-            generateExpression(child, node);
-            if (!isArithmeticNode(child))
-                addObjectToDouble();
-            generateExpression(child.getNext(), node);
-            if (!isArithmeticNode(child.getNext()))
-                  addObjectToDouble();
-            cfw.add(opCode);
-            if (!childOfArithmetic) {
-                addDoubleWrap();
-            }
-        }
-    }
-
-    private void visitBitOp(Node node, int type, Node child)
-    {
-        int childNumberFlag = node.getIntProp(Node.ISNUMBER_PROP, -1);
-        generateExpression(child, node);
-
-        // special-case URSH; work with the target arg as a long, so
-        // that we can return a 32-bit unsigned value, and call
-        // toUint32 instead of toInt32.
-        if (type == Token.URSH) {
-            addScriptRuntimeInvoke("toUint32", "(Ljava/lang/Object;)J");
-            generateExpression(child.getNext(), node);
-            addScriptRuntimeInvoke("toInt32", "(Ljava/lang/Object;)I");
-            // Looks like we need to explicitly mask the shift to 5 bits -
-            // LUSHR takes 6 bits.
-            cfw.addPush(31);
-            cfw.add(ByteCode.IAND);
-            cfw.add(ByteCode.LUSHR);
-            cfw.add(ByteCode.L2D);
-            addDoubleWrap();
-            return;
-        }
-        if (childNumberFlag == -1) {
-            addScriptRuntimeInvoke("toInt32", "(Ljava/lang/Object;)I");
-            generateExpression(child.getNext(), node);
-            addScriptRuntimeInvoke("toInt32", "(Ljava/lang/Object;)I");
-        }
-        else {
-            addScriptRuntimeInvoke("toInt32", "(D)I");
-            generateExpression(child.getNext(), node);
-            addScriptRuntimeInvoke("toInt32", "(D)I");
-        }
-        switch (type) {
-          case Token.BITOR:
-            cfw.add(ByteCode.IOR);
-            break;
-          case Token.BITXOR:
-            cfw.add(ByteCode.IXOR);
-            break;
-          case Token.BITAND:
-            cfw.add(ByteCode.IAND);
-            break;
-          case Token.RSH:
-            cfw.add(ByteCode.ISHR);
-            break;
-          case Token.LSH:
-            cfw.add(ByteCode.ISHL);
-            break;
-          default:
-            throw Codegen.badTree();
-        }
-        cfw.add(ByteCode.I2D);
-        if (childNumberFlag == -1) {
-            addDoubleWrap();
-        }
-    }
-
-    private int nodeIsDirectCallParameter(Node node)
-    {
-        if (node.getType() == Token.GETVAR
-            && inDirectCallFunction && !itsForcedObjectParameters)
-        {
-            int varIndex = fnCurrent.getVarIndex(node);
-            if (fnCurrent.isParameter(varIndex)) {
-                return varRegisters[varIndex];
-            }
-        }
-        return -1;
-    }
-
-    private boolean varIsDirectCallParameter(int varIndex)
-    {
-        return fnCurrent.isParameter(varIndex)
-            && inDirectCallFunction && !itsForcedObjectParameters;
-    }
-
-    private void genSimpleCompare(int type, int trueGOTO, int falseGOTO)
-    {
-        if (trueGOTO == -1) throw Codegen.badTree();
-        switch (type) {
-            case Token.LE :
-                cfw.add(ByteCode.DCMPG);
-                cfw.add(ByteCode.IFLE, trueGOTO);
-                break;
-            case Token.GE :
-                cfw.add(ByteCode.DCMPL);
-                cfw.add(ByteCode.IFGE, trueGOTO);
-                break;
-            case Token.LT :
-                cfw.add(ByteCode.DCMPG);
-                cfw.add(ByteCode.IFLT, trueGOTO);
-                break;
-            case Token.GT :
-                cfw.add(ByteCode.DCMPL);
-                cfw.add(ByteCode.IFGT, trueGOTO);
-                break;
-            default :
-                throw Codegen.badTree();
-
-        }
-        if (falseGOTO != -1)
-            cfw.add(ByteCode.GOTO, falseGOTO);
-    }
-
-    private void visitIfJumpRelOp(Node node, Node child,
-                                  int trueGOTO, int falseGOTO)
-    {
-        if (trueGOTO == -1 || falseGOTO == -1) throw Codegen.badTree();
-        int type = node.getType();
-        Node rChild = child.getNext();
-        if (type == Token.INSTANCEOF || type == Token.IN) {
-            generateExpression(child, node);
-            generateExpression(rChild, node);
-            cfw.addALoad(contextLocal);
-            addScriptRuntimeInvoke(
-                (type == Token.INSTANCEOF) ? "instanceOf" : "in",
-                "(Ljava/lang/Object;"
-                +"Ljava/lang/Object;"
-                +"Lorg/mozilla/javascript/Context;"
-                +")Z");
-            cfw.add(ByteCode.IFNE, trueGOTO);
-            cfw.add(ByteCode.GOTO, falseGOTO);
-            return;
-        }
-        int childNumberFlag = node.getIntProp(Node.ISNUMBER_PROP, -1);
-        int left_dcp_register = nodeIsDirectCallParameter(child);
-        int right_dcp_register = nodeIsDirectCallParameter(rChild);
-        if (childNumberFlag != -1) {
-            // Force numeric context on both parameters and optimize
-            // direct call case as Optimizer currently does not handle it
-
-            if (childNumberFlag != Node.RIGHT) {
-                // Left already has number content
-                generateExpression(child, node);
-            } else if (left_dcp_register != -1) {
-                dcpLoadAsNumber(left_dcp_register);
-            } else {
-                generateExpression(child, node);
-                addObjectToDouble();
-            }
-
-            if (childNumberFlag != Node.LEFT) {
-                // Right already has number content
-                generateExpression(rChild, node);
-            } else if (right_dcp_register != -1) {
-                dcpLoadAsNumber(right_dcp_register);
-            } else {
-                generateExpression(rChild, node);
-                addObjectToDouble();
-            }
-
-            genSimpleCompare(type, trueGOTO, falseGOTO);
-
-        } else {
-            if (left_dcp_register != -1 && right_dcp_register != -1) {
-                // Generate code to dynamically check for number content
-                // if both operands are dcp
-                short stack = cfw.getStackTop();
-                int leftIsNotNumber = cfw.acquireLabel();
-                cfw.addALoad(left_dcp_register);
-                cfw.add(ByteCode.GETSTATIC,
-                        "java/lang/Void",
-                        "TYPE",
-                        "Ljava/lang/Class;");
-                cfw.add(ByteCode.IF_ACMPNE, leftIsNotNumber);
-                cfw.addDLoad(left_dcp_register + 1);
-                dcpLoadAsNumber(right_dcp_register);
-                genSimpleCompare(type, trueGOTO, falseGOTO);
-                if (stack != cfw.getStackTop()) throw Codegen.badTree();
-
-                cfw.markLabel(leftIsNotNumber);
-                int rightIsNotNumber = cfw.acquireLabel();
-                cfw.addALoad(right_dcp_register);
-                cfw.add(ByteCode.GETSTATIC,
-                        "java/lang/Void",
-                        "TYPE",
-                        "Ljava/lang/Class;");
-                cfw.add(ByteCode.IF_ACMPNE, rightIsNotNumber);
-                cfw.addALoad(left_dcp_register);
-                addObjectToDouble();
-                cfw.addDLoad(right_dcp_register + 1);
-                genSimpleCompare(type, trueGOTO, falseGOTO);
-                if (stack != cfw.getStackTop()) throw Codegen.badTree();
-
-                cfw.markLabel(rightIsNotNumber);
-                // Load both register as objects to call generic cmp_*
-                cfw.addALoad(left_dcp_register);
-                cfw.addALoad(right_dcp_register);
-
-            } else {
-                generateExpression(child, node);
-                generateExpression(rChild, node);
-            }
-
-            if (type == Token.GE || type == Token.GT) {
-                cfw.add(ByteCode.SWAP);
-            }
-            String routine = ((type == Token.LT)
-                      || (type == Token.GT)) ? "cmp_LT" : "cmp_LE";
-            addScriptRuntimeInvoke(routine,
-                                   "(Ljava/lang/Object;"
-                                   +"Ljava/lang/Object;"
-                                   +")Z");
-            cfw.add(ByteCode.IFNE, trueGOTO);
-            cfw.add(ByteCode.GOTO, falseGOTO);
-        }
-    }
-
-    private void visitIfJumpEqOp(Node node, Node child,
-                                 int trueGOTO, int falseGOTO)
-    {
-        if (trueGOTO == -1 || falseGOTO == -1) throw Codegen.badTree();
-
-        short stackInitial = cfw.getStackTop();
-        int type = node.getType();
-        Node rChild = child.getNext();
-
-        // Optimize if one of operands is null
-        if (child.getType() == Token.NULL || rChild.getType() == Token.NULL) {
-            // eq is symmetric in this case
-            if (child.getType() == Token.NULL) {
-                child = rChild;
-            }
-            generateExpression(child, node);
-            if (type == Token.SHEQ || type == Token.SHNE) {
-                int testCode = (type == Token.SHEQ)
-                                ? ByteCode.IFNULL : ByteCode.IFNONNULL;
-                cfw.add(testCode, trueGOTO);
-            } else {
-                if (type != Token.EQ) {
-                    // swap false/true targets for !=
-                    if (type != Token.NE) throw Codegen.badTree();
-                    int tmp = trueGOTO;
-                    trueGOTO = falseGOTO;
-                    falseGOTO = tmp;
-                }
-                cfw.add(ByteCode.DUP);
-                int undefCheckLabel = cfw.acquireLabel();
-                cfw.add(ByteCode.IFNONNULL, undefCheckLabel);
-                short stack = cfw.getStackTop();
-                cfw.add(ByteCode.POP);
-                cfw.add(ByteCode.GOTO, trueGOTO);
-                cfw.markLabel(undefCheckLabel, stack);
-                Codegen.pushUndefined(cfw);
-                cfw.add(ByteCode.IF_ACMPEQ, trueGOTO);
-            }
-            cfw.add(ByteCode.GOTO, falseGOTO);
-        } else {
-            int child_dcp_register = nodeIsDirectCallParameter(child);
-            if (child_dcp_register != -1
-                && rChild.getType() == Token.TO_OBJECT)
-            {
-                Node convertChild = rChild.getFirstChild();
-                if (convertChild.getType() == Token.NUMBER) {
-                    cfw.addALoad(child_dcp_register);
-                    cfw.add(ByteCode.GETSTATIC,
-                            "java/lang/Void",
-                            "TYPE",
-                            "Ljava/lang/Class;");
-                    int notNumbersLabel = cfw.acquireLabel();
-                    cfw.add(ByteCode.IF_ACMPNE, notNumbersLabel);
-                    cfw.addDLoad(child_dcp_register + 1);
-                    cfw.addPush(convertChild.getDouble());
-                    cfw.add(ByteCode.DCMPL);
-                    if (type == Token.EQ)
-                        cfw.add(ByteCode.IFEQ, trueGOTO);
-                    else
-                        cfw.add(ByteCode.IFNE, trueGOTO);
-                    cfw.add(ByteCode.GOTO, falseGOTO);
-                    cfw.markLabel(notNumbersLabel);
-                    // fall thru into generic handling
-                }
-            }
-
-            generateExpression(child, node);
-            generateExpression(rChild, node);
-
-            String name;
-            int testCode;
-            switch (type) {
-              case Token.EQ:
-                name = "eq";
-                testCode = ByteCode.IFNE;
-                break;
-              case Token.NE:
-                name = "eq";
-                testCode = ByteCode.IFEQ;
-                break;
-              case Token.SHEQ:
-                name = "shallowEq";
-                testCode = ByteCode.IFNE;
-                break;
-              case Token.SHNE:
-                name = "shallowEq";
-                testCode = ByteCode.IFEQ;
-                break;
-              default:
-                throw Codegen.badTree();
-            }
-            addScriptRuntimeInvoke(name,
-                                   "(Ljava/lang/Object;"
-                                   +"Ljava/lang/Object;"
-                                   +")Z");
-            cfw.add(testCode, trueGOTO);
-            cfw.add(ByteCode.GOTO, falseGOTO);
-        }
-        if (stackInitial != cfw.getStackTop()) throw Codegen.badTree();
-    }
-
-    private void visitSetName(Node node, Node child)
-    {
-        String name = node.getFirstChild().getString();
-        while (child != null) {
-            generateExpression(child, node);
-            child = child.getNext();
-        }
-        cfw.addALoad(contextLocal);
-        cfw.addALoad(variableObjectLocal);
-        cfw.addPush(name);
-        addScriptRuntimeInvoke(
-            "setName",
-            "(Lorg/mozilla/javascript/Scriptable;"
-            +"Ljava/lang/Object;"
-            +"Lorg/mozilla/javascript/Context;"
-            +"Lorg/mozilla/javascript/Scriptable;"
-            +"Ljava/lang/String;"
-            +")Ljava/lang/Object;");
-    }
-
-    private void visitStrictSetName(Node node, Node child)
-    {
-        String name = node.getFirstChild().getString();
-        while (child != null) {
-            generateExpression(child, node);
-            child = child.getNext();
-        }
-        cfw.addALoad(contextLocal);
-        cfw.addALoad(variableObjectLocal);
-        cfw.addPush(name);
-        addScriptRuntimeInvoke(
-            "strictSetName",
-            "(Lorg/mozilla/javascript/Scriptable;"
-            +"Ljava/lang/Object;"
-            +"Lorg/mozilla/javascript/Context;"
-            +"Lorg/mozilla/javascript/Scriptable;"
-            +"Ljava/lang/String;"
-            +")Ljava/lang/Object;");
-    }
-
-    private void visitSetConst(Node node, Node child)
-    {
-        String name = node.getFirstChild().getString();
-        while (child != null) {
-            generateExpression(child, node);
-            child = child.getNext();
-        }
-        cfw.addALoad(contextLocal);
-        cfw.addPush(name);
-        addScriptRuntimeInvoke(
-            "setConst",
-            "(Lorg/mozilla/javascript/Scriptable;"
-            +"Ljava/lang/Object;"
-            +"Lorg/mozilla/javascript/Context;"
-            +"Ljava/lang/String;"
-            +")Ljava/lang/Object;");
-    }
-
-    private void visitGetVar(Node node)
-    {
-        if (!hasVarsInRegs) Kit.codeBug();
-        int varIndex = fnCurrent.getVarIndex(node);
-        short reg = varRegisters[varIndex];
-        if (varIsDirectCallParameter(varIndex)) {
-            // Remember that here the isNumber flag means that we
-            // want to use the incoming parameter in a Number
-            // context, so test the object type and convert the
-            //  value as necessary.
-            if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
-                dcpLoadAsNumber(reg);
-            } else {
-                dcpLoadAsObject(reg);
-            }
-        } else if (fnCurrent.isNumberVar(varIndex)) {
-            cfw.addDLoad(reg);
-        } else {
-            cfw.addALoad(reg);
-        }
-    }
-
-    private void visitSetVar(Node node, Node child, boolean needValue)
-    {
-        if (!hasVarsInRegs) Kit.codeBug();
-        int varIndex = fnCurrent.getVarIndex(node);
-        generateExpression(child.getNext(), node);
-        boolean isNumber = (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1);
-        short reg = varRegisters[varIndex];
-        boolean [] constDeclarations = fnCurrent.fnode.getParamAndVarConst();
-        if (constDeclarations[varIndex]) {
-            if (!needValue) {
-                if (isNumber)
-                    cfw.add(ByteCode.POP2);
-                else
-                    cfw.add(ByteCode.POP);
-            }
-        }
-        else if (varIsDirectCallParameter(varIndex)) {
-            if (isNumber) {
-                if (needValue) cfw.add(ByteCode.DUP2);
-                cfw.addALoad(reg);
-                cfw.add(ByteCode.GETSTATIC,
-                        "java/lang/Void",
-                        "TYPE",
-                        "Ljava/lang/Class;");
-                int isNumberLabel = cfw.acquireLabel();
-                int beyond = cfw.acquireLabel();
-                cfw.add(ByteCode.IF_ACMPEQ, isNumberLabel);
-                short stack = cfw.getStackTop();
-                addDoubleWrap();
-                cfw.addAStore(reg);
-                cfw.add(ByteCode.GOTO, beyond);
-                cfw.markLabel(isNumberLabel, stack);
-                cfw.addDStore(reg + 1);
-                cfw.markLabel(beyond);
-            }
-            else {
-                if (needValue) cfw.add(ByteCode.DUP);
-                cfw.addAStore(reg);
-            }
-        } else {
-            boolean isNumberVar = fnCurrent.isNumberVar(varIndex);
-            if (isNumber) {
-                if (isNumberVar) {
-                    cfw.addDStore(reg);
-                    if (needValue) cfw.addDLoad(reg);
-                } else {
-                    if (needValue) cfw.add(ByteCode.DUP2);
-                    // Cannot save number in variable since !isNumberVar,
-                    // so convert to object
-                    addDoubleWrap();
-                    cfw.addAStore(reg);
-                }
-            } else {
-                if (isNumberVar) Kit.codeBug();
-                cfw.addAStore(reg);
-                if (needValue) cfw.addALoad(reg);
-            }
-        }
-    }
-
-    private void visitSetConstVar(Node node, Node child, boolean needValue)
-    {
-        if (!hasVarsInRegs) Kit.codeBug();
-        int varIndex = fnCurrent.getVarIndex(node);
-        generateExpression(child.getNext(), node);
-        boolean isNumber = (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1);
-        short reg = varRegisters[varIndex];
-        int beyond = cfw.acquireLabel();
-        int noAssign = cfw.acquireLabel();
-        if (isNumber) {
-            cfw.addILoad(reg + 2);
-            cfw.add(ByteCode.IFNE, noAssign);
-            short stack = cfw.getStackTop();
-            cfw.addPush(1);
-            cfw.addIStore(reg + 2);
-            cfw.addDStore(reg);
-            if (needValue) {
-                cfw.addDLoad(reg);
-                cfw.markLabel(noAssign, stack);
-            } else {
-                cfw.add(ByteCode.GOTO, beyond);
-                cfw.markLabel(noAssign, stack);
-                cfw.add(ByteCode.POP2);
-            }
-        }
-        else {
-            cfw.addILoad(reg + 1);
-            cfw.add(ByteCode.IFNE, noAssign);
-            short stack = cfw.getStackTop();
-            cfw.addPush(1);
-            cfw.addIStore(reg + 1);
-            cfw.addAStore(reg);
-            if (needValue) {
-                cfw.addALoad(reg);
-                cfw.markLabel(noAssign, stack);
-            } else {
-                cfw.add(ByteCode.GOTO, beyond);
-                cfw.markLabel(noAssign, stack);
-                cfw.add(ByteCode.POP);
-            }
-        }
-        cfw.markLabel(beyond);
-    }
-
-    private void visitGetProp(Node node, Node child)
-    {
-        generateExpression(child, node); // object
-        Node nameChild = child.getNext();
-        generateExpression(nameChild, node);  // the name
-        if (node.getType() == Token.GETPROPNOWARN) {
-            cfw.addALoad(contextLocal);
-            cfw.addALoad(variableObjectLocal);
-            addScriptRuntimeInvoke(
-                "getObjectPropNoWarn",
-                "(Ljava/lang/Object;"
-                +"Ljava/lang/String;"
-                +"Lorg/mozilla/javascript/Context;"
-                +"Lorg/mozilla/javascript/Scriptable;"
-                +")Ljava/lang/Object;");
-            return;
-        }
-        /*
-            for 'this.foo' we call getObjectProp(Scriptable...) which can
-            skip some casting overhead.
-        */
-        int childType = child.getType();
-        if (childType == Token.THIS && nameChild.getType() == Token.STRING) {
-            cfw.addALoad(contextLocal);
-            addScriptRuntimeInvoke(
-                "getObjectProp",
-                "(Lorg/mozilla/javascript/Scriptable;"
-                +"Ljava/lang/String;"
-                +"Lorg/mozilla/javascript/Context;"
-                +")Ljava/lang/Object;");
-        } else {
-            cfw.addALoad(contextLocal);
-            cfw.addALoad(variableObjectLocal);
-            addScriptRuntimeInvoke(
-                "getObjectProp",
-                "(Ljava/lang/Object;"
-                +"Ljava/lang/String;"
-                +"Lorg/mozilla/javascript/Context;"
-                +"Lorg/mozilla/javascript/Scriptable;"
-                +")Ljava/lang/Object;");
-        }
-    }
-
-    private void visitSetProp(int type, Node node, Node child)
-    {
-        Node objectChild = child;
-        generateExpression(child, node);
-        child = child.getNext();
-        if (type == Token.SETPROP_OP) {
-            cfw.add(ByteCode.DUP);
-        }
-        Node nameChild = child;
-        generateExpression(child, node);
-        child = child.getNext();
-        if (type == Token.SETPROP_OP) {
-            // stack: ... object object name -> ... object name object name
-            cfw.add(ByteCode.DUP_X1);
-            //for 'this.foo += ...' we call thisGet which can skip some
-            //casting overhead.
-            if (objectChild.getType() == Token.THIS
-                && nameChild.getType() == Token.STRING)
-            {
-                cfw.addALoad(contextLocal);
-                addScriptRuntimeInvoke(
-                    "getObjectProp",
-                    "(Lorg/mozilla/javascript/Scriptable;"
-                    +"Ljava/lang/String;"
-                    +"Lorg/mozilla/javascript/Context;"
-                    +")Ljava/lang/Object;");
-            } else {
-                cfw.addALoad(contextLocal);
-                cfw.addALoad(variableObjectLocal);
-                addScriptRuntimeInvoke(
-                    "getObjectProp",
-                    "(Ljava/lang/Object;"
-                    +"Ljava/lang/String;"
-                    +"Lorg/mozilla/javascript/Context;"
-                    +"Lorg/mozilla/javascript/Scriptable;"
-                    +")Ljava/lang/Object;");
-            }
-        }
-        generateExpression(child, node);
-        cfw.addALoad(contextLocal);
-        cfw.addALoad(variableObjectLocal);
-        addScriptRuntimeInvoke(
-            "setObjectProp",
-            "(Ljava/lang/Object;"
-            +"Ljava/lang/String;"
-            +"Ljava/lang/Object;"
-            +"Lorg/mozilla/javascript/Context;"
-            +"Lorg/mozilla/javascript/Scriptable;"
-            +")Ljava/lang/Object;");
-    }
-
-    private void visitSetElem(int type, Node node, Node child)
-    {
-        generateExpression(child, node);
-        child = child.getNext();
-        if (type == Token.SETELEM_OP) {
-            cfw.add(ByteCode.DUP);
-        }
-        generateExpression(child, node);
-        child = child.getNext();
-        boolean indexIsNumber = (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1);
-        if (type == Token.SETELEM_OP) {
-            if (indexIsNumber) {
-                // stack: ... object object number
-                //        -> ... object number object number
-                cfw.add(ByteCode.DUP2_X1);
-                cfw.addALoad(contextLocal);
-                cfw.addALoad(variableObjectLocal);
-                addScriptRuntimeInvoke(
-                    "getObjectIndex",
-                    "(Ljava/lang/Object;D"
-                    +"Lorg/mozilla/javascript/Context;"
-                    +"Lorg/mozilla/javascript/Scriptable;"
-                    +")Ljava/lang/Object;");
-            } else {
-                // stack: ... object object indexObject
-                //        -> ... object indexObject object indexObject
-                cfw.add(ByteCode.DUP_X1);
-                cfw.addALoad(contextLocal);
-                cfw.addALoad(variableObjectLocal);
-                addScriptRuntimeInvoke(
-                    "getObjectElem",
-                    "(Ljava/lang/Object;"
-                    +"Ljava/lang/Object;"
-                    +"Lorg/mozilla/javascript/Context;"
-                    +"Lorg/mozilla/javascript/Scriptable;"
-                    +")Ljava/lang/Object;");
-            }
-        }
-        generateExpression(child, node);
-        cfw.addALoad(contextLocal);
-        cfw.addALoad(variableObjectLocal);
-        if (indexIsNumber) {
-            addScriptRuntimeInvoke(
-                "setObjectIndex",
-                "(Ljava/lang/Object;"
-                +"D"
-                +"Ljava/lang/Object;"
-                +"Lorg/mozilla/javascript/Context;"
-                +"Lorg/mozilla/javascript/Scriptable;"
-                +")Ljava/lang/Object;");
-        } else {
-            addScriptRuntimeInvoke(
-                "setObjectElem",
-                "(Ljava/lang/Object;"
-                +"Ljava/lang/Object;"
-                +"Ljava/lang/Object;"
-                +"Lorg/mozilla/javascript/Context;"
-                +"Lorg/mozilla/javascript/Scriptable;"
-                +")Ljava/lang/Object;");
-        }
-    }
-
-    private void visitDotQuery(Node node, Node child)
-    {
-        updateLineNumber(node);
-        generateExpression(child, node);
-        cfw.addALoad(variableObjectLocal);
-        addScriptRuntimeInvoke("enterDotQuery",
-                               "(Ljava/lang/Object;"
-                               +"Lorg/mozilla/javascript/Scriptable;"
-                               +")Lorg/mozilla/javascript/Scriptable;");
-        cfw.addAStore(variableObjectLocal);
-
-        // add push null/pop with label in between to simplify code for loop
-        // continue when it is necessary to pop the null result from
-        // updateDotQuery
-        cfw.add(ByteCode.ACONST_NULL);
-        int queryLoopStart = cfw.acquireLabel();
-        cfw.markLabel(queryLoopStart); // loop continue jumps here
-        cfw.add(ByteCode.POP);
-
-        generateExpression(child.getNext(), node);
-        addScriptRuntimeInvoke("toBoolean", "(Ljava/lang/Object;)Z");
-        cfw.addALoad(variableObjectLocal);
-        addScriptRuntimeInvoke("updateDotQuery",
-                               "(Z"
-                               +"Lorg/mozilla/javascript/Scriptable;"
-                               +")Ljava/lang/Object;");
-        cfw.add(ByteCode.DUP);
-        cfw.add(ByteCode.IFNULL, queryLoopStart);
-        // stack: ... non_null_result_of_updateDotQuery
-        cfw.addALoad(variableObjectLocal);
-        addScriptRuntimeInvoke("leaveDotQuery",
-                               "(Lorg/mozilla/javascript/Scriptable;"
-                               +")Lorg/mozilla/javascript/Scriptable;");
-        cfw.addAStore(variableObjectLocal);
-    }
-
-    private int getLocalBlockRegister(Node node)
-    {
-        Node localBlock = (Node)node.getProp(Node.LOCAL_BLOCK_PROP);
-        int localSlot = localBlock.getExistingIntProp(Node.LOCAL_PROP);
-        return localSlot;
-    }
-
-    private void dcpLoadAsNumber(int dcp_register)
-    {
-        cfw.addALoad(dcp_register);
-        cfw.add(ByteCode.GETSTATIC,
-                "java/lang/Void",
-                "TYPE",
-                "Ljava/lang/Class;");
-        int isNumberLabel = cfw.acquireLabel();
-        cfw.add(ByteCode.IF_ACMPEQ, isNumberLabel);
-        short stack = cfw.getStackTop();
-        cfw.addALoad(dcp_register);
-        addObjectToDouble();
-        int beyond = cfw.acquireLabel();
-        cfw.add(ByteCode.GOTO, beyond);
-        cfw.markLabel(isNumberLabel, stack);
-        cfw.addDLoad(dcp_register + 1);
-        cfw.markLabel(beyond);
-    }
-
-    private void dcpLoadAsObject(int dcp_register)
-    {
-        cfw.addALoad(dcp_register);
-        cfw.add(ByteCode.GETSTATIC,
-                "java/lang/Void",
-                "TYPE",
-                "Ljava/lang/Class;");
-        int isNumberLabel = cfw.acquireLabel();
-        cfw.add(ByteCode.IF_ACMPEQ, isNumberLabel);
-        short stack = cfw.getStackTop();
-        cfw.addALoad(dcp_register);
-        int beyond = cfw.acquireLabel();
-        cfw.add(ByteCode.GOTO, beyond);
-        cfw.markLabel(isNumberLabel, stack);
-        cfw.addDLoad(dcp_register + 1);
-        addDoubleWrap();
-        cfw.markLabel(beyond);
-    }
-
-    private void addGoto(Node target, int jumpcode)
-    {
-        int targetLabel = getTargetLabel(target);
-        cfw.add(jumpcode, targetLabel);
-    }
-
-    private void addObjectToDouble()
-    {
-        addScriptRuntimeInvoke("toNumber", "(Ljava/lang/Object;)D");
-    }
-
-    private void addNewObjectArray(int size)
-    {
-        if (size == 0) {
-            if (itsZeroArgArray >= 0) {
-                cfw.addALoad(itsZeroArgArray);
-            } else {
-                cfw.add(ByteCode.GETSTATIC,
-                        "org/mozilla/javascript/ScriptRuntime",
-                        "emptyArgs", "[Ljava/lang/Object;");
-            }
-        } else {
-            cfw.addPush(size);
-            cfw.add(ByteCode.ANEWARRAY, "java/lang/Object");
-        }
-    }
-
-    private void addScriptRuntimeInvoke(String methodName,
-                                        String methodSignature)
-    {
-        cfw.addInvoke(ByteCode.INVOKESTATIC,
-                      "org.mozilla.javascript.ScriptRuntime",
-                      methodName,
-                      methodSignature);
-    }
-
-    private void addOptRuntimeInvoke(String methodName,
-                                     String methodSignature)
-    {
-        cfw.addInvoke(ByteCode.INVOKESTATIC,
-                      "org/mozilla/javascript/optimizer/OptRuntime",
-                      methodName,
-                      methodSignature);
-    }
-
-    private void addJumpedBooleanWrap(int trueLabel, int falseLabel)
-    {
-        cfw.markLabel(falseLabel);
-        int skip = cfw.acquireLabel();
-        cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean",
-                                "FALSE", "Ljava/lang/Boolean;");
-        cfw.add(ByteCode.GOTO, skip);
-        cfw.markLabel(trueLabel);
-        cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean",
-                                "TRUE", "Ljava/lang/Boolean;");
-        cfw.markLabel(skip);
-        cfw.adjustStackTop(-1);   // only have 1 of true/false
-    }
-
-    private void addDoubleWrap()
-    {
-        addOptRuntimeInvoke("wrapDouble", "(D)Ljava/lang/Double;");
-    }
-
-    /**
-     * Const locals use an extra slot to hold the has-been-assigned-once flag at
-     * runtime.
-     * @param isConst true iff the variable is const
-     * @return the register for the word pair (double/long)
-     */
-    private short getNewWordPairLocal(boolean isConst)
-    {
-        return getNewWordIntern(isConst ? 3 : 2);
-    }
-
-    private short getNewWordLocal(boolean isConst)
-    {
-        return getNewWordIntern(isConst ? 2 : 1);
-    }
-
-    private short getNewWordLocal()
-    {
-        return getNewWordIntern(1);
-    }
-
-    private short getNewWordIntern(int count)
-    {
-        assert count >= 1 && count <= 3;
-
-        int[] locals = this.locals;
-        int result = -1;
-        if (count > 1) {
-            // we need 'count' consecutive free slots
-            OUTER: for (int i = firstFreeLocal; i + count <= MAX_LOCALS;) {
-                for (int j = 0; j < count; ++j) {
-                    if (locals[i + j] != 0) {
-                        i += j + 1;
-                        continue OUTER;
-                    }
-                }
-                result = i;
-                break;
-            }
-        } else {
-            result = firstFreeLocal;
-        }
-
-        if (result != -1) {
-            locals[result] = 1;
-            if (count > 1)
-                locals[result + 1] = 1;
-            if (count > 2)
-                locals[result + 2] = 1;
-
-            if (result == firstFreeLocal) {
-                for (int i = result + count; i < MAX_LOCALS; i++) {
-                    if (locals[i] == 0) {
-                        firstFreeLocal = (short) i;
-                        if (localsMax < firstFreeLocal)
-                            localsMax = firstFreeLocal;
-                        return (short) result;
-                    }
-                }
-            } else {
-                return (short) result;
-            }
-        }
-
-        throw Context.reportRuntimeError("Program too complex (out of locals)");
-    }
-
-    // This is a valid call only for a local that is allocated by default.
-    private void incReferenceWordLocal(short local)
-    {
-        locals[local]++;
-    }
-
-    // This is a valid call only for a local that is allocated by default.
-    private void decReferenceWordLocal(short local)
-    {
-        locals[local]--;
-    }
-
-    private void releaseWordLocal(short local)
-    {
-        if (local < firstFreeLocal)
-            firstFreeLocal = local;
-        locals[local] = 0;
-    }
-
-
-    static final int GENERATOR_TERMINATE = -1;
-    static final int GENERATOR_START = 0;
-    static final int GENERATOR_YIELD_START = 1;
-
-    ClassFileWriter cfw;
-    Codegen codegen;
-    CompilerEnvirons compilerEnv;
-    ScriptNode scriptOrFn;
-    public int scriptOrFnIndex;
-    private int savedCodeOffset;
-
-    private OptFunctionNode fnCurrent;
-
-    private static final int MAX_LOCALS = 1024;
-    private int[] locals;
-    private short firstFreeLocal;
-    private short localsMax;
-
-    private int itsLineNumber;
-
-    private boolean hasVarsInRegs;
-    private short[] varRegisters;
-    private boolean inDirectCallFunction;
-    private boolean itsForcedObjectParameters;
-    private int enterAreaStartLabel;
-    private int epilogueLabel;
-    private boolean inLocalBlock;
-
-    // special known locals. If you add a new local here, be sure
-    // to initialize it to -1 in initBodyGeneration
-    private short variableObjectLocal;
-    private short popvLocal;
-    private short contextLocal;
-    private short argsLocal;
-    private short operationLocal;
-    private short thisObjLocal;
-    private short funObjLocal;
-    private short itsZeroArgArray;
-    private short itsOneArgArray;
-    private short generatorStateLocal;
-
-    private boolean isGenerator;
-    private int generatorSwitch;
-    private int maxLocals = 0;
-    private int maxStack = 0;
-
-    private Map<Node,FinallyReturnPoint> finallys;
-    private List<Node> literals;
-
-    static class FinallyReturnPoint {
-        public List<Integer> jsrPoints  = new ArrayList<Integer>();
-        public int tableLabel = 0;
-    }
-}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/optimizer/OptFunctionNode.java rhino-1.7.14/src/org/mozilla/javascript/optimizer/OptFunctionNode.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/optimizer/OptFunctionNode.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/optimizer/OptFunctionNode.java	2022-01-06 22:57:21.000000000 +0100
@@ -5,7 +5,9 @@
 
 package org.mozilla.javascript.optimizer;
 
-import org.mozilla.javascript.*;
+import org.mozilla.javascript.Kit;
+import org.mozilla.javascript.Node;
+import org.mozilla.javascript.Token;
 import org.mozilla.javascript.ast.FunctionNode;
 import org.mozilla.javascript.ast.ScriptNode;
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/optimizer/Optimizer.java rhino-1.7.14/src/org/mozilla/javascript/optimizer/Optimizer.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/optimizer/Optimizer.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/optimizer/Optimizer.java	2022-01-06 22:57:21.000000000 +0100
@@ -2,15 +2,14 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-
-
 package org.mozilla.javascript.optimizer;
 
-import org.mozilla.javascript.*;
+import org.mozilla.javascript.Node;
+import org.mozilla.javascript.ObjArray;
+import org.mozilla.javascript.Token;
 import org.mozilla.javascript.ast.ScriptNode;
 
-class Optimizer
-{
+class Optimizer {
 
     static final int NoType = 0;
     static final int NumberType = 1;
@@ -18,8 +17,7 @@
 
     // It is assumed that (NumberType | AnyType) == AnyType
 
-    void optimize(ScriptNode scriptOrFn)
-    {
+    void optimize(ScriptNode scriptOrFn) {
         //  run on one function at a time for now
         int functionCount = scriptOrFn.getFunctionCount();
         for (int i = 0; i != functionCount; ++i) {
@@ -28,8 +26,7 @@
         }
     }
 
-    private void optimizeFunction(OptFunctionNode theFunction)
-    {
+    private void optimizeFunction(OptFunctionNode theFunction) {
         if (theFunction.fnode.requiresActivation()) return;
 
         inDirectCallFunction = theFunction.isTargetOfDirectCall();
@@ -56,43 +53,39 @@
             }
             theFunction.setParameterNumberContext(parameterUsedInNumberContext);
         }
-
     }
 
-
-/*
-        Each directCall parameter is passed as a pair of values - an object
-        and a double. The value passed depends on the type of value available at
-        the call site. If a double is available, the object in java/lang/Void.TYPE
-        is passed as the object value, and if an object value is available, then
-        0.0 is passed as the double value.
-
-        The receiving routine always tests the object value before proceeding.
-        If the parameter is being accessed in a 'Number Context' then the code
-        sequence is :
-        if ("parameter_objectValue" == java/lang/Void.TYPE)
-            ...fine..., use the parameter_doubleValue
-        else
-            toNumber(parameter_objectValue)
-
-        and if the parameter is being referenced in an Object context, the code is
-        if ("parameter_objectValue" == java/lang/Void.TYPE)
-            new Double(parameter_doubleValue)
-        else
-            ...fine..., use the parameter_objectValue
-
-        If the receiving code never uses the doubleValue, it is converted on
-        entry to a Double instead.
-*/
-
-
-/*
-        We're referencing a node in a Number context (i.e. we'd prefer it
-        was a double value). If the node is a parameter in a directCall
-        function, mark it as being referenced in this context.
-*/
-    private void markDCPNumberContext(Node n)
-    {
+    /*
+            Each directCall parameter is passed as a pair of values - an object
+            and a double. The value passed depends on the type of value available at
+            the call site. If a double is available, the object in java/lang/Void.TYPE
+            is passed as the object value, and if an object value is available, then
+            0.0 is passed as the double value.
+
+            The receiving routine always tests the object value before proceeding.
+            If the parameter is being accessed in a 'Number Context' then the code
+            sequence is :
+            if ("parameter_objectValue" == java/lang/Void.TYPE)
+                ...fine..., use the parameter_doubleValue
+            else
+                toNumber(parameter_objectValue)
+
+            and if the parameter is being referenced in an Object context, the code is
+            if ("parameter_objectValue" == java/lang/Void.TYPE)
+                Double.valueOf(parameter_doubleValue)
+            else
+                ...fine..., use the parameter_objectValue
+
+            If the receiving code never uses the doubleValue, it is converted on
+            entry to a Double instead.
+    */
+
+    /*
+            We're referencing a node in a Number context (i.e. we'd prefer it
+            was a double value). If the node is a parameter in a directCall
+            function, mark it as being referenced in this context.
+    */
+    private void markDCPNumberContext(Node n) {
         if (inDirectCallFunction && n.getType() == Token.GETVAR) {
             int varIndex = theFunction.getVarIndex(n);
             if (theFunction.isParameter(varIndex)) {
@@ -101,8 +94,7 @@
         }
     }
 
-    private boolean convertParameter(Node n)
-    {
+    private boolean convertParameter(Node n) {
         if (inDirectCallFunction && n.getType() == Token.GETVAR) {
             int varIndex = theFunction.getVarIndex(n);
             if (theFunction.isParameter(varIndex)) {
@@ -113,65 +105,60 @@
         return false;
     }
 
-    private int rewriteForNumberVariables(Node n, int desired)
-    {
+    private int rewriteForNumberVariables(Node n, int desired) {
         switch (n.getType()) {
-            case Token.EXPR_VOID : {
+            case Token.EXPR_VOID:
+                {
                     Node child = n.getFirstChild();
                     int type = rewriteForNumberVariables(child, NumberType);
-                    if (type == NumberType)
-                        n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
-                     return NoType;
+                    if (type == NumberType) n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
+                    return NoType;
                 }
-            case Token.NUMBER :
+            case Token.NUMBER:
                 n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
                 return NumberType;
 
-            case Token.GETVAR :
+            case Token.GETVAR:
                 {
                     int varIndex = theFunction.getVarIndex(n);
                     if (inDirectCallFunction
-                        && theFunction.isParameter(varIndex)
-                        && desired == NumberType)
-                    {
+                            && theFunction.isParameter(varIndex)
+                            && desired == NumberType) {
                         n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
                         return NumberType;
-                    }
-                    else if (theFunction.isNumberVar(varIndex)) {
+                    } else if (theFunction.isNumberVar(varIndex)) {
                         n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
                         return NumberType;
                     }
                     return NoType;
                 }
 
-            case Token.INC :
-            case Token.DEC : {
+            case Token.INC:
+            case Token.DEC:
+                {
                     Node child = n.getFirstChild();
                     int type = rewriteForNumberVariables(child, NumberType);
                     if (child.getType() == Token.GETVAR) {
-                        if (type == NumberType && !convertParameter(child))
-                        {
+                        if (type == NumberType && !convertParameter(child)) {
                             n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
                             markDCPNumberContext(child);
                             return NumberType;
                         }
                         return NoType;
-                    }
-                    else if (child.getType() == Token.GETELEM
+                    } else if (child.getType() == Token.GETELEM
                             || child.getType() == Token.GETPROP) {
                         return type;
                     }
                     return NoType;
                 }
-            case Token.SETVAR :
-            case Token.SETCONSTVAR : {
+            case Token.SETVAR:
+            case Token.SETCONSTVAR:
+                {
                     Node lChild = n.getFirstChild();
                     Node rChild = lChild.getNext();
                     int rType = rewriteForNumberVariables(rChild, NumberType);
                     int varIndex = theFunction.getVarIndex(n);
-                    if (inDirectCallFunction
-                        && theFunction.isParameter(varIndex))
-                    {
+                    if (inDirectCallFunction && theFunction.isParameter(varIndex)) {
                         if (rType == NumberType) {
                             if (!convertParameter(rChild)) {
                                 n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
@@ -180,34 +167,30 @@
                             markDCPNumberContext(rChild);
                             return NoType;
                         }
-                        else
-                            return rType;
-                    }
-                    else if (theFunction.isNumberVar(varIndex)) {
+                        return rType;
+                    } else if (theFunction.isNumberVar(varIndex)) {
                         if (rType != NumberType) {
                             n.removeChild(rChild);
-                            n.addChildToBack(
-                                new Node(Token.TO_DOUBLE, rChild));
+                            n.addChildToBack(new Node(Token.TO_DOUBLE, rChild));
                         }
                         n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
                         markDCPNumberContext(rChild);
                         return NumberType;
-                    }
-                    else {
+                    } else {
                         if (rType == NumberType) {
                             if (!convertParameter(rChild)) {
                                 n.removeChild(rChild);
-                                n.addChildToBack(
-                                    new Node(Token.TO_OBJECT, rChild));
+                                n.addChildToBack(new Node(Token.TO_OBJECT, rChild));
                             }
                         }
                         return NoType;
                     }
                 }
-            case Token.LE :
-            case Token.LT :
-            case Token.GE :
-            case Token.GT : {
+            case Token.LE:
+            case Token.LT:
+            case Token.GE:
+            case Token.GT:
+                {
                     Node lChild = n.getFirstChild();
                     Node rChild = lChild.getNext();
                     int lType = rewriteForNumberVariables(lChild, NumberType);
@@ -221,22 +204,18 @@
                         } else if (rType == NumberType) {
                             n.putIntProp(Node.ISNUMBER_PROP, Node.RIGHT);
                         }
-                    }
-                    else if (convertParameter(rChild)) {
+                    } else if (convertParameter(rChild)) {
                         if (lType == NumberType) {
                             n.putIntProp(Node.ISNUMBER_PROP, Node.LEFT);
                         }
-                    }
-                    else {
+                    } else {
                         if (lType == NumberType) {
                             if (rType == NumberType) {
                                 n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
-                            }
-                            else {
+                            } else {
                                 n.putIntProp(Node.ISNUMBER_PROP, Node.LEFT);
                             }
-                        }
-                        else {
+                        } else {
                             if (rType == NumberType) {
                                 n.putIntProp(Node.ISNUMBER_PROP, Node.RIGHT);
                             }
@@ -246,43 +225,35 @@
                     return NoType;
                 }
 
-            case Token.ADD : {
+            case Token.ADD:
+                {
                     Node lChild = n.getFirstChild();
                     Node rChild = lChild.getNext();
                     int lType = rewriteForNumberVariables(lChild, NumberType);
                     int rType = rewriteForNumberVariables(rChild, NumberType);
 
-
                     if (convertParameter(lChild)) {
                         if (convertParameter(rChild)) {
                             return NoType;
                         }
-                        else {
-                            if (rType == NumberType) {
-                                n.putIntProp(Node.ISNUMBER_PROP, Node.RIGHT);
-                            }
+                        if (rType == NumberType) {
+                            n.putIntProp(Node.ISNUMBER_PROP, Node.RIGHT);
                         }
-                    }
-                    else {
+                    } else {
                         if (convertParameter(rChild)) {
                             if (lType == NumberType) {
                                 n.putIntProp(Node.ISNUMBER_PROP, Node.LEFT);
                             }
-                        }
-                        else {
+                        } else {
                             if (lType == NumberType) {
                                 if (rType == NumberType) {
                                     n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
                                     return NumberType;
                                 }
-                                else {
-                                    n.putIntProp(Node.ISNUMBER_PROP, Node.LEFT);
-                                }
-                            }
-                            else {
+                                n.putIntProp(Node.ISNUMBER_PROP, Node.LEFT);
+                            } else {
                                 if (rType == NumberType) {
-                                    n.putIntProp(Node.ISNUMBER_PROP,
-                                                 Node.RIGHT);
+                                    n.putIntProp(Node.ISNUMBER_PROP, Node.RIGHT);
                                 }
                             }
                         }
@@ -290,15 +261,17 @@
                     return NoType;
                 }
 
-            case Token.BITXOR :
-            case Token.BITOR :
-            case Token.BITAND :
-            case Token.RSH :
-            case Token.LSH :
-            case Token.SUB :
-            case Token.MUL :
-            case Token.DIV :
-            case Token.MOD : {
+            case Token.BITXOR:
+            case Token.BITOR:
+            case Token.BITAND:
+            case Token.RSH:
+            case Token.LSH:
+            case Token.SUB:
+            case Token.MUL:
+            case Token.DIV:
+            case Token.MOD:
+            case Token.EXP:
+                {
                     Node lChild = n.getFirstChild();
                     Node rChild = lChild.getNext();
                     int lType = rewriteForNumberVariables(lChild, NumberType);
@@ -310,44 +283,42 @@
                             n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
                             return NumberType;
                         }
-                        else {
-                            if (!convertParameter(rChild)) {
-                                n.removeChild(rChild);
-                                n.addChildToBack(
-                                    new Node(Token.TO_DOUBLE, rChild));
-                                n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
-                            }
-                            return NumberType;
+                        if (!convertParameter(rChild)) {
+                            n.removeChild(rChild);
+                            n.addChildToBack(new Node(Token.TO_DOUBLE, rChild));
+                            n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
                         }
+                        return NumberType;
                     }
-                    else {
-                        if (rType == NumberType) {
-                            if (!convertParameter(lChild)) {
-                                n.removeChild(lChild);
-                                n.addChildToFront(
-                                    new Node(Token.TO_DOUBLE, lChild));
-                                n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
-                            }
-                            return NumberType;
-                        }
-                        else {
-                            if (!convertParameter(lChild)) {
-                                n.removeChild(lChild);
-                                n.addChildToFront(
-                                    new Node(Token.TO_DOUBLE, lChild));
-                            }
-                            if (!convertParameter(rChild)) {
-                                n.removeChild(rChild);
-                                n.addChildToBack(
-                                    new Node(Token.TO_DOUBLE, rChild));
-                            }
+                    if (rType == NumberType) {
+                        if (!convertParameter(lChild)) {
+                            n.removeChild(lChild);
+                            n.addChildToFront(new Node(Token.TO_DOUBLE, lChild));
                             n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
-                            return NumberType;
                         }
+                        return NumberType;
+                    }
+                    // Numeric Type (Number or BigInt)
+                    return AnyType;
+                }
+
+            case Token.BITNOT:
+            case Token.POS:
+            case Token.NEG:
+                {
+                    Node child = n.getFirstChild();
+                    int type = rewriteForNumberVariables(child, NumberType);
+                    if (type == NumberType && !convertParameter(child)) {
+                        n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
+                        markDCPNumberContext(child);
+                        return NumberType;
                     }
+                    return NoType;
                 }
-            case Token.SETELEM :
-            case Token.SETELEM_OP : {
+
+            case Token.SETELEM:
+            case Token.SETELEM_OP:
+                {
                     Node arrayBase = n.getFirstChild();
                     Node arrayIndex = arrayBase.getNext();
                     Node rValue = arrayIndex.getNext();
@@ -355,8 +326,7 @@
                     if (baseType == NumberType) {
                         if (!convertParameter(arrayBase)) {
                             n.removeChild(arrayBase);
-                            n.addChildToFront(
-                                new Node(Token.TO_OBJECT, arrayBase));
+                            n.addChildToFront(new Node(Token.TO_OBJECT, arrayBase));
                         }
                     }
                     int indexType = rewriteForNumberVariables(arrayIndex, NumberType);
@@ -372,21 +342,20 @@
                     if (rValueType == NumberType) {
                         if (!convertParameter(rValue)) {
                             n.removeChild(rValue);
-                            n.addChildToBack(
-                                new Node(Token.TO_OBJECT, rValue));
+                            n.addChildToBack(new Node(Token.TO_OBJECT, rValue));
                         }
                     }
                     return NoType;
                 }
-            case Token.GETELEM : {
+            case Token.GETELEM:
+                {
                     Node arrayBase = n.getFirstChild();
                     Node arrayIndex = arrayBase.getNext();
                     int baseType = rewriteForNumberVariables(arrayBase, NumberType);
                     if (baseType == NumberType) {
                         if (!convertParameter(arrayBase)) {
                             n.removeChild(arrayBase);
-                            n.addChildToFront(
-                                new Node(Token.TO_OBJECT, arrayBase));
+                            n.addChildToFront(new Node(Token.TO_OBJECT, arrayBase));
                         }
                     }
                     int indexType = rewriteForNumberVariables(arrayIndex, NumberType);
@@ -400,20 +369,19 @@
                     }
                     return NoType;
                 }
-            case Token.CALL :
+            case Token.CALL:
                 {
                     Node child = n.getFirstChild(); // the function node
                     // must be an object
                     rewriteAsObjectChildren(child, child.getFirstChild());
                     child = child.getNext(); // the first arg
 
-                    OptFunctionNode target
-                            = (OptFunctionNode)n.getProp(Node.DIRECTCALL_PROP);
+                    OptFunctionNode target = (OptFunctionNode) n.getProp(Node.DIRECTCALL_PROP);
                     if (target != null) {
-/*
-    we leave each child as a Number if it can be. The codegen will
-    handle moving the pairs of parameters.
-*/
+                        /*
+                            we leave each child as a Number if it can be. The codegen will
+                            handle moving the pairs of parameters.
+                        */
                         while (child != null) {
                             int type = rewriteForNumberVariables(child, NumberType);
                             if (type == NumberType) {
@@ -426,15 +394,15 @@
                     }
                     return NoType;
                 }
-            default : {
+            default:
+                {
                     rewriteAsObjectChildren(n, n.getFirstChild());
                     return NoType;
                 }
         }
     }
 
-    private void rewriteAsObjectChildren(Node n, Node child)
-    {
+    private void rewriteAsObjectChildren(Node n, Node child) {
         // Force optimized children to be objects
         while (child != null) {
             Node nextChild = child.getNext();
@@ -443,24 +411,20 @@
                 if (!convertParameter(child)) {
                     n.removeChild(child);
                     Node nuChild = new Node(Token.TO_OBJECT, child);
-                    if (nextChild == null)
-                        n.addChildToBack(nuChild);
-                    else
-                        n.addChildBefore(nuChild, nextChild);
+                    if (nextChild == null) n.addChildToBack(nuChild);
+                    else n.addChildBefore(nuChild, nextChild);
                 }
             }
             child = nextChild;
         }
     }
 
-    private static void buildStatementList_r(Node node, ObjArray statements)
-    {
+    private static void buildStatementList_r(Node node, ObjArray statements) {
         int type = node.getType();
         if (type == Token.BLOCK
-            || type == Token.LOCAL_BLOCK
-            || type == Token.LOOP
-            || type == Token.FUNCTION)
-        {
+                || type == Token.LOCAL_BLOCK
+                || type == Token.LOOP
+                || type == Token.FUNCTION) {
             Node child = node.getFirstChild();
             while (child != null) {
                 buildStatementList_r(child, statements);
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/optimizer/OptRuntime.java rhino-1.7.14/src/org/mozilla/javascript/optimizer/OptRuntime.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/optimizer/OptRuntime.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/optimizer/OptRuntime.java	2022-01-06 22:57:21.000000000 +0100
@@ -2,123 +2,106 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-
 package org.mozilla.javascript.optimizer;
 
-import org.mozilla.javascript.*;
-
-public final class OptRuntime extends ScriptRuntime
-{
+import org.mozilla.javascript.ArrowFunction;
+import org.mozilla.javascript.Callable;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.ContextFactory;
+import org.mozilla.javascript.ES6Generator;
+import org.mozilla.javascript.Function;
+import org.mozilla.javascript.JavaScriptException;
+import org.mozilla.javascript.NativeFunction;
+import org.mozilla.javascript.NativeGenerator;
+import org.mozilla.javascript.NativeIterator;
+import org.mozilla.javascript.Script;
+import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+import org.mozilla.javascript.Undefined;
+
+public final class OptRuntime extends ScriptRuntime {
+    public static final Double oneObj = Double.valueOf(1.0);
+    public static final Double minusOneObj = Double.valueOf(-1.0);
 
-    public static final Double zeroObj = new Double(0.0);
-    public static final Double oneObj = new Double(1.0);
-    public static final Double minusOneObj = new Double(-1.0);
-
-    /**
-     * Implement ....() call shrinking optimizer code.
-     */
-    public static Object call0(Callable fun, Scriptable thisObj,
-                               Context cx, Scriptable scope)
-    {
+    /** Implement ....() call shrinking optimizer code. */
+    public static Object call0(Callable fun, Scriptable thisObj, Context cx, Scriptable scope) {
         return fun.call(cx, scope, thisObj, ScriptRuntime.emptyArgs);
     }
 
-    /**
-     * Implement ....(arg) call shrinking optimizer code.
-     */
-    public static Object call1(Callable fun, Scriptable thisObj, Object arg0,
-                               Context cx, Scriptable scope)
-    {
-        return fun.call(cx, scope, thisObj, new Object[] { arg0 } );
-    }
-
-    /**
-     * Implement ....(arg0, arg1) call shrinking optimizer code.
-     */
-    public static Object call2(Callable fun, Scriptable thisObj,
-                               Object arg0, Object arg1,
-                               Context cx, Scriptable scope)
-    {
-        return fun.call(cx, scope, thisObj, new Object[] { arg0, arg1 });
-    }
-
-    /**
-     * Implement ....(arg0, arg1, ...) call shrinking optimizer code.
-     */
-    public static Object callN(Callable fun, Scriptable thisObj,
-                               Object[] args,
-                               Context cx, Scriptable scope)
-    {
+    /** Implement ....(arg) call shrinking optimizer code. */
+    public static Object call1(
+            Callable fun, Scriptable thisObj, Object arg0, Context cx, Scriptable scope) {
+        return fun.call(cx, scope, thisObj, new Object[] {arg0});
+    }
+
+    /** Implement ....(arg0, arg1) call shrinking optimizer code. */
+    public static Object call2(
+            Callable fun,
+            Scriptable thisObj,
+            Object arg0,
+            Object arg1,
+            Context cx,
+            Scriptable scope) {
+        return fun.call(cx, scope, thisObj, new Object[] {arg0, arg1});
+    }
+
+    /** Implement ....(arg0, arg1, ...) call shrinking optimizer code. */
+    public static Object callN(
+            Callable fun, Scriptable thisObj, Object[] args, Context cx, Scriptable scope) {
         return fun.call(cx, scope, thisObj, args);
     }
 
-    /**
-     * Implement name(args) call shrinking optimizer code.
-     */
-    public static Object callName(Object[] args, String name,
-                                  Context cx, Scriptable scope)
-    {
+    /** Implement name(args) call shrinking optimizer code. */
+    public static Object callName(Object[] args, String name, Context cx, Scriptable scope) {
         Callable f = getNameFunctionAndThis(name, cx, scope);
         Scriptable thisObj = lastStoredScriptable(cx);
         return f.call(cx, scope, thisObj, args);
     }
 
-    /**
-     * Implement name() call shrinking optimizer code.
-     */
-    public static Object callName0(String name,
-                                   Context cx, Scriptable scope)
-    {
+    /** Implement name() call shrinking optimizer code. */
+    public static Object callName0(String name, Context cx, Scriptable scope) {
         Callable f = getNameFunctionAndThis(name, cx, scope);
         Scriptable thisObj = lastStoredScriptable(cx);
         return f.call(cx, scope, thisObj, ScriptRuntime.emptyArgs);
     }
 
-    /**
-     * Implement x.property() call shrinking optimizer code.
-     */
-    public static Object callProp0(Object value, String property,
-                                   Context cx, Scriptable scope)
-    {
+    /** Implement x.property() call shrinking optimizer code. */
+    public static Object callProp0(Object value, String property, Context cx, Scriptable scope) {
         Callable f = getPropFunctionAndThis(value, property, cx, scope);
         Scriptable thisObj = lastStoredScriptable(cx);
         return f.call(cx, scope, thisObj, ScriptRuntime.emptyArgs);
     }
 
-    public static Object add(Object val1, double val2)
-    {
-        if (val1 instanceof Scriptable)
-            val1 = ((Scriptable) val1).getDefaultValue(null);
-        if (!(val1 instanceof CharSequence))
-            return wrapDouble(toNumber(val1) + val2);
-        return new ConsString((CharSequence)val1, toString(val2));
-    }
-
-    public static Object add(double val1, Object val2)
-    {
-        if (val2 instanceof Scriptable)
-            val2 = ((Scriptable) val2).getDefaultValue(null);
-        if (!(val2 instanceof CharSequence))
-            return wrapDouble(toNumber(val2) + val1);
-        return new ConsString(toString(val1), (CharSequence)val2);
-    }
-
-    /**
-     * @deprecated Use {@link #elemIncrDecr(Object, double, Context, Scriptable, int)} instead
-     */
+    public static Object add(Object val1, double val2, Context cx) {
+        if (val1 instanceof Double) {
+            return ((Double) val1) + val2;
+        }
+        if (val1 instanceof Integer) {
+            return ((Integer) val1) + val2;
+        }
+        return ScriptRuntime.add(val1, val2, cx);
+    }
+
+    public static Object add(double val1, Object val2, Context cx) {
+        if (val2 instanceof Double) {
+            return val1 + ((Double) val2);
+        }
+        if (val2 instanceof Integer) {
+            return val1 + ((Integer) val2);
+        }
+        return ScriptRuntime.add(val1, val2, cx);
+    }
+
+    /** @deprecated Use {@link #elemIncrDecr(Object, double, Context, Scriptable, int)} instead */
     @Deprecated
-    public static Object elemIncrDecr(Object obj, double index,
-                                      Context cx, int incrDecrMask)
-    {
+    public static Object elemIncrDecr(Object obj, double index, Context cx, int incrDecrMask) {
         return elemIncrDecr(obj, index, cx, getTopCallScope(cx), incrDecrMask);
     }
 
-    public static Object elemIncrDecr(Object obj, double index,
-                                      Context cx, Scriptable scope,
-                                      int incrDecrMask)
-    {
-        return ScriptRuntime.elemIncrDecr(obj, new Double(index), cx, scope,
-                                          incrDecrMask);
+    public static Object elemIncrDecr(
+            Object obj, double index, Context cx, Scriptable scope, int incrDecrMask) {
+        return ScriptRuntime.elemIncrDecr(obj, Double.valueOf(index), cx, scope, incrDecrMask);
     }
 
     public static Object[] padStart(Object[] currentArgs, int count) {
@@ -127,37 +110,41 @@
         return result;
     }
 
-    public static void initFunction(NativeFunction fn, int functionType,
-                                    Scriptable scope, Context cx)
-    {
+    public static void initFunction(
+            NativeFunction fn, int functionType, Scriptable scope, Context cx) {
         ScriptRuntime.initFunction(cx, scope, fn, functionType, false);
     }
 
-    public static Function bindThis(NativeFunction fn, Context cx, Scriptable scope, Scriptable thisObj)
-    {
+    public static Function bindThis(
+            NativeFunction fn, Context cx, Scriptable scope, Scriptable thisObj) {
         return new ArrowFunction(cx, scope, fn, thisObj);
     }
 
-    public static Object callSpecial(Context cx, Callable fun,
-                                     Scriptable thisObj, Object[] args,
-                                     Scriptable scope,
-                                     Scriptable callerThis, int callType,
-                                     String fileName, int lineNumber)
-    {
-        return ScriptRuntime.callSpecial(cx, fun, thisObj, args, scope,
-                                         callerThis, callType,
-                                         fileName, lineNumber);
-    }
-
-    public static Object newObjectSpecial(Context cx, Object fun,
-                                          Object[] args, Scriptable scope,
-                                          Scriptable callerThis, int callType)
-    {
+    public static Object callSpecial(
+            Context cx,
+            Callable fun,
+            Scriptable thisObj,
+            Object[] args,
+            Scriptable scope,
+            Scriptable callerThis,
+            int callType,
+            String fileName,
+            int lineNumber) {
+        return ScriptRuntime.callSpecial(
+                cx, fun, thisObj, args, scope, callerThis, callType, fileName, lineNumber);
+    }
+
+    public static Object newObjectSpecial(
+            Context cx,
+            Object fun,
+            Object[] args,
+            Scriptable scope,
+            Scriptable callerThis,
+            int callType) {
         return ScriptRuntime.newSpecial(cx, fun, args, scope, callType);
     }
 
-    public static Double wrapDouble(double num)
-    {
+    public static Double wrapDouble(double num) {
         if (num == 0.0) {
             if (1 / num > 0) {
                 // +0.0
@@ -167,30 +154,30 @@
             return oneObj;
         } else if (num == -1.0) {
             return minusOneObj;
-        } else if (num != num) {
+        } else if (Double.isNaN(num)) {
             return NaNobj;
         }
-        return new Double(num);
+        return Double.valueOf(num);
     }
 
-    static String encodeIntArray(int[] array)
-    {
+    static String encodeIntArray(int[] array) {
         // XXX: this extremely inefficient for small integers
-        if (array == null) { return null; }
+        if (array == null) {
+            return null;
+        }
         int n = array.length;
         char[] buffer = new char[1 + n * 2];
         buffer[0] = 1;
         for (int i = 0; i != n; ++i) {
             int value = array[i];
             int shift = 1 + i * 2;
-            buffer[shift] = (char)(value >>> 16);
-            buffer[shift + 1] = (char)value;
+            buffer[shift] = (char) (value >>> 16);
+            buffer[shift + 1] = (char) value;
         }
         return new String(buffer);
     }
 
-    private static int[] decodeIntArray(String str, int arraySize)
-    {
+    private static int[] decodeIntArray(String str, int arraySize) {
         // XXX: this extremely inefficient for small integers
         if (arraySize == 0) {
             if (str != null) throw new IllegalArgumentException();
@@ -207,82 +194,95 @@
         return array;
     }
 
-    public static Scriptable newArrayLiteral(Object[] objects,
-                                             String encodedInts,
-                                             int skipCount,
-                                             Context cx,
-                                             Scriptable scope)
-    {
+    public static Scriptable newArrayLiteral(
+            Object[] objects, String encodedInts, int skipCount, Context cx, Scriptable scope) {
         int[] skipIndexces = decodeIntArray(encodedInts, skipCount);
         return newArrayLiteral(objects, skipIndexces, cx, scope);
     }
 
-    public static void main(final Script script, final String[] args)
-    {
-        ContextFactory.getGlobal().call(new ContextAction() {
-            public Object run(Context cx)
-            {
-                ScriptableObject global = getGlobal(cx);
-
-                // get the command line arguments and define "arguments"
-                // array in the top-level object
-                Object[] argsCopy = new Object[args.length];
-                System.arraycopy(args, 0, argsCopy, 0, args.length);
-                Scriptable argsObj = cx.newArray(global, argsCopy);
-                global.defineProperty("arguments", argsObj,
-                                      ScriptableObject.DONTENUM);
-                script.exec(cx, global);
-                return null;
-            }
-        });
-    }
-
-    public static void throwStopIteration(Object obj) {
-        throw new JavaScriptException(
-            NativeIterator.getStopIterationObject((Scriptable)obj), "", 0);
-    }
-
-    public static Scriptable createNativeGenerator(NativeFunction funObj,
-                                                   Scriptable scope,
-                                                   Scriptable thisObj,
-                                                   int maxLocals,
-                                                   int maxStack)
-    {
-        return new NativeGenerator(scope, funObj,
-                new GeneratorState(thisObj, maxLocals, maxStack));
+    public static void main(final Script script, final String[] args) {
+        ContextFactory.getGlobal()
+                .call(
+                        cx -> {
+                            ScriptableObject global = getGlobal(cx);
+
+                            // get the command line arguments and define "arguments"
+                            // array in the top-level object
+                            Object[] argsCopy = new Object[args.length];
+                            System.arraycopy(args, 0, argsCopy, 0, args.length);
+                            Scriptable argsObj = cx.newArray(global, argsCopy);
+                            global.defineProperty("arguments", argsObj, ScriptableObject.DONTENUM);
+                            script.exec(cx, global);
+                            return null;
+                        });
+    }
+
+    public static void throwStopIteration(Object scope, Object genState) {
+        Object value = getGeneratorReturnValue(genState);
+        Object si =
+                (value == Undefined.instance)
+                        ? NativeIterator.getStopIterationObject((Scriptable) scope)
+                        : new NativeIterator.StopIteration(value);
+        throw new JavaScriptException(si, "", 0);
+    }
+
+    public static Scriptable createNativeGenerator(
+            NativeFunction funObj,
+            Scriptable scope,
+            Scriptable thisObj,
+            int maxLocals,
+            int maxStack) {
+        GeneratorState gs = new GeneratorState(thisObj, maxLocals, maxStack);
+        if (Context.getCurrentContext().getLanguageVersion() >= Context.VERSION_ES6) {
+            return new ES6Generator(scope, funObj, gs);
+        } else {
+            return new NativeGenerator(scope, funObj, gs);
+        }
     }
 
     public static Object[] getGeneratorStackState(Object obj) {
         GeneratorState rgs = (GeneratorState) obj;
-        if (rgs.stackState == null)
-            rgs.stackState = new Object[rgs.maxStack];
+        if (rgs.stackState == null) rgs.stackState = new Object[rgs.maxStack];
         return rgs.stackState;
     }
 
     public static Object[] getGeneratorLocalsState(Object obj) {
         GeneratorState rgs = (GeneratorState) obj;
-        if (rgs.localsState == null)
-            rgs.localsState = new Object[rgs.maxLocals];
+        if (rgs.localsState == null) rgs.localsState = new Object[rgs.maxLocals];
         return rgs.localsState;
     }
 
+    public static void setGeneratorReturnValue(Object obj, Object val) {
+        GeneratorState rgs = (GeneratorState) obj;
+        rgs.returnValue = val;
+    }
+
+    public static Object getGeneratorReturnValue(Object obj) {
+        GeneratorState rgs = (GeneratorState) obj;
+        return (rgs.returnValue == null ? Undefined.instance : rgs.returnValue);
+    }
+
     public static class GeneratorState {
         static final String CLASS_NAME =
-            "org/mozilla/javascript/optimizer/OptRuntime$GeneratorState";
+                "org/mozilla/javascript/optimizer/OptRuntime$GeneratorState";
 
+        @SuppressWarnings("unused")
         public int resumptionPoint;
+
         static final String resumptionPoint_NAME = "resumptionPoint";
         static final String resumptionPoint_TYPE = "I";
 
+        @SuppressWarnings("unused")
         public Scriptable thisObj;
+
         static final String thisObj_NAME = "thisObj";
-        static final String thisObj_TYPE =
-            "Lorg/mozilla/javascript/Scriptable;";
+        static final String thisObj_TYPE = "Lorg/mozilla/javascript/Scriptable;";
 
         Object[] stackState;
         Object[] localsState;
         int maxLocals;
         int maxStack;
+        Object returnValue;
 
         GeneratorState(Scriptable thisObj, int maxLocals, int maxStack) {
             this.thisObj = thisObj;
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/optimizer/OptTransformer.java rhino-1.7.14/src/org/mozilla/javascript/optimizer/OptTransformer.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/optimizer/OptTransformer.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/optimizer/OptTransformer.java	2022-01-06 22:57:21.000000000 +0100
@@ -2,12 +2,15 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-
 package org.mozilla.javascript.optimizer;
 
-import org.mozilla.javascript.*;
-import org.mozilla.javascript.ast.ScriptNode;
 import java.util.Map;
+import org.mozilla.javascript.Kit;
+import org.mozilla.javascript.Node;
+import org.mozilla.javascript.NodeTransformer;
+import org.mozilla.javascript.ObjArray;
+import org.mozilla.javascript.Token;
+import org.mozilla.javascript.ast.ScriptNode;
 
 /**
  * This class performs node transforms to prepare for optimization.
@@ -15,11 +18,9 @@
  * @see NodeTransformer
  * @author Norris Boyd
  */
-
 class OptTransformer extends NodeTransformer {
 
-    OptTransformer(Map<String,OptFunctionNode> possibleDirectCalls, ObjArray directCallTargets)
-    {
+    OptTransformer(Map<String, OptFunctionNode> possibleDirectCalls, ObjArray directCallTargets) {
         this.possibleDirectCalls = possibleDirectCalls;
         this.directCallTargets = directCallTargets;
     }
@@ -36,17 +37,18 @@
         super.visitCall(node, tree);
     }
 
-    private void detectDirectCall(Node node, ScriptNode tree)
-    {
+    private void detectDirectCall(Node node, ScriptNode tree) {
         if (tree.getType() == Token.FUNCTION) {
             Node left = node.getFirstChild();
 
             // count the arguments
             int argCount = 0;
-            Node arg = left.getNext();
-            while (arg != null) {
-                arg = arg.getNext();
-                argCount++;
+            if (left != null) {
+                Node arg = left.getNext();
+                while (arg != null) {
+                    arg = arg.getNext();
+                    argCount++;
+                }
             }
 
             if (argCount == 0) {
@@ -78,9 +80,8 @@
                     OptFunctionNode ofn;
                     ofn = possibleDirectCalls.get(targetName);
                     if (ofn != null
-                        && argCount == ofn.fnode.getParamCount()
-                        && !ofn.fnode.requiresActivation())
-                    {
+                            && argCount == ofn.fnode.getParamCount()
+                            && !ofn.fnode.requiresActivation()) {
                         // Refuse to directCall any function with more
                         // than 32 parameters - prevent code explosion
                         // for wacky test cases
@@ -98,6 +99,6 @@
         }
     }
 
-    private Map<String,OptFunctionNode> possibleDirectCalls;
+    private Map<String, OptFunctionNode> possibleDirectCalls;
     private ObjArray directCallTargets;
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/Parser.java rhino-1.7.14/src/org/mozilla/javascript/Parser.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/Parser.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/Parser.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,12 +6,21 @@
 
 package org.mozilla.javascript;
 
+import java.io.IOException;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import org.mozilla.javascript.ast.ArrayComprehension;
 import org.mozilla.javascript.ast.ArrayComprehensionLoop;
 import org.mozilla.javascript.ast.ArrayLiteral;
 import org.mozilla.javascript.ast.Assignment;
 import org.mozilla.javascript.ast.AstNode;
 import org.mozilla.javascript.ast.AstRoot;
+import org.mozilla.javascript.ast.BigIntLiteral;
 import org.mozilla.javascript.ast.Block;
 import org.mozilla.javascript.ast.BreakStatement;
 import org.mozilla.javascript.ast.CatchClause;
@@ -55,9 +64,13 @@
 import org.mozilla.javascript.ast.SwitchCase;
 import org.mozilla.javascript.ast.SwitchStatement;
 import org.mozilla.javascript.ast.Symbol;
+import org.mozilla.javascript.ast.TaggedTemplateLiteral;
+import org.mozilla.javascript.ast.TemplateCharacters;
+import org.mozilla.javascript.ast.TemplateLiteral;
 import org.mozilla.javascript.ast.ThrowStatement;
 import org.mozilla.javascript.ast.TryStatement;
 import org.mozilla.javascript.ast.UnaryExpression;
+import org.mozilla.javascript.ast.UpdateExpression;
 import org.mozilla.javascript.ast.VariableDeclaration;
 import org.mozilla.javascript.ast.VariableInitializer;
 import org.mozilla.javascript.ast.WhileLoop;
@@ -72,50 +85,33 @@
 import org.mozilla.javascript.ast.XmlString;
 import org.mozilla.javascript.ast.Yield;
 
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.Reader;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.HashSet;
-
 /**
- * This class implements the JavaScript parser.<p>
+ * This class implements the JavaScript parser.
  *
- * It is based on the SpiderMonkey C source files jsparse.c and jsparse.h in the
- * jsref package.<p>
+ * <p>It is based on the SpiderMonkey C source files jsparse.c and jsparse.h in the jsref package.
  *
- * The parser generates an {@link AstRoot} parse tree representing the source
- * code.  No tree rewriting is permitted at this stage, so that the parse tree
- * is a faithful representation of the source for frontend processing tools and
- * IDEs.<p>
+ * <p>The parser generates an {@link AstRoot} parse tree representing the source code. No tree
+ * rewriting is permitted at this stage, so that the parse tree is a faithful representation of the
+ * source for frontend processing tools and IDEs.
  *
- * This parser implementation is not intended to be reused after a parse
- * finishes, and will throw an IllegalStateException() if invoked again.<p>
+ * <p>This parser implementation is not intended to be reused after a parse finishes, and will throw
+ * an IllegalStateException() if invoked again.
  *
- * @see TokenStream
+ * <p>
  *
+ * @see TokenStream
  * @author Mike McCabe
  * @author Brendan Eich
  */
-public class Parser
-{
-    /**
-     * Maximum number of allowed function or constructor arguments,
-     * to follow SpiderMonkey.
-     */
+public class Parser {
+    /** Maximum number of allowed function or constructor arguments, to follow SpiderMonkey. */
     public static final int ARGC_LIMIT = 1 << 16;
 
     // TokenInformation flags : currentFlaggedToken stores them together
     // with token type
-    final static int
-        CLEAR_TI_MASK    = 0xFFFF,  // mask to clear token information bits
-        TI_AFTER_EOL     = 1 << 16, // first token of the source line
-        TI_CHECK_LABEL   = 1 << 17; // indicates to check for label
+    static final int CLEAR_TI_MASK = 0xFFFF, // mask to clear token information bits
+            TI_AFTER_EOL = 1 << 16, // first token of the source line
+            TI_CHECK_LABEL = 1 << 17; // indicates to check for label
 
     CompilerEnvirons compilerEnv;
     private ErrorReporter errorReporter;
@@ -123,8 +119,8 @@
     private String sourceURI;
     private char[] sourceChars;
 
-    boolean calledByCompileFunction;  // ugly - set directly by Context
-    private boolean parseFinished;  // set when finished to prevent reuse
+    boolean calledByCompileFunction; // ugly - set directly by Context
+    private boolean parseFinished; // set when finished to prevent reuse
 
     private TokenStream ts;
     private int currentFlaggedToken = Token.EOF;
@@ -144,8 +140,8 @@
     ScriptNode currentScriptOrFn;
     Scope currentScope;
     private int endFlags;
-    private boolean inForInit;  // bound temporarily during forStatement()
-    private Map<String,LabeledStatement> labelSet;
+    private boolean inForInit; // bound temporarily during forStatement()
+    private Map<String, LabeledStatement> labelSet;
     private List<Loop> loopSet;
     private List<Jump> loopAndSwitchSet;
     // end of per function variables
@@ -160,9 +156,8 @@
     private boolean defaultUseStrictDirective;
 
     // Exception to unwind
-    private static class ParserException extends RuntimeException
-    {
-        static final long serialVersionUID = 5882582646773765630L;
+    private static class ParserException extends RuntimeException {
+        private static final long serialVersionUID = 5882582646773765630L;
     }
 
     public Parser() {
@@ -177,7 +172,7 @@
         this.compilerEnv = compilerEnv;
         this.errorReporter = errorReporter;
         if (errorReporter instanceof IdeErrorReporter) {
-            errorCollector = (IdeErrorReporter)errorReporter;
+            errorCollector = (IdeErrorReporter) errorReporter;
         }
     }
 
@@ -191,10 +186,8 @@
         addStrictWarning(messageId, messageArg, beg, end);
     }
 
-    void addStrictWarning(String messageId, String messageArg,
-                          int position, int length) {
-        if (compilerEnv.isStrictMode())
-            addWarning(messageId, messageArg, position, length);
+    void addStrictWarning(String messageId, String messageArg, int position, int length) {
+        if (compilerEnv.isStrictMode()) addWarning(messageId, messageArg, position, length);
     }
 
     void addWarning(String messageId, String messageArg) {
@@ -210,22 +203,25 @@
         addWarning(messageId, null, position, length);
     }
 
-    void addWarning(String messageId, String messageArg,
-                    int position, int length)
-    {
+    void addWarning(String messageId, String messageArg, int position, int length) {
         String message = lookupMessage(messageId, messageArg);
         if (compilerEnv.reportWarningAsError()) {
             addError(messageId, messageArg, position, length);
         } else if (errorCollector != null) {
             errorCollector.warning(message, sourceURI, position, length);
+        } else if (ts != null) {
+            errorReporter.warning(message, sourceURI, ts.getLineno(), ts.getLine(), ts.getOffset());
         } else {
-            errorReporter.warning(message, sourceURI, ts.getLineno(),
-                                  ts.getLine(), ts.getOffset());
+            errorReporter.warning(message, sourceURI, 1, "", 1);
         }
     }
 
     void addError(String messageId) {
-        addError(messageId, ts.tokenBeg, ts.tokenEnd - ts.tokenBeg);
+        if (ts == null) {
+            addError(messageId, 0, 0);
+        } else {
+            addError(messageId, ts.tokenBeg, ts.tokenEnd - ts.tokenBeg);
+        }
     }
 
     void addError(String messageId, int position, int length) {
@@ -233,12 +229,19 @@
     }
 
     void addError(String messageId, String messageArg) {
-        addError(messageId, messageArg, ts.tokenBeg,
-                 ts.tokenEnd - ts.tokenBeg);
+        if (ts == null) {
+            addError(messageId, messageArg, 0, 0);
+        } else {
+            addError(messageId, messageArg, ts.tokenBeg, ts.tokenEnd - ts.tokenBeg);
+        }
+    }
+
+    void addError(String messageId, int c) {
+        String messageArg = Character.toString((char) c);
+        addError(messageId, messageArg);
     }
 
-    void addError(String messageId, String messageArg, int position, int length)
-    {
+    void addError(String messageId, String messageArg, int position, int length) {
         ++syntaxErrorCount;
         String message = lookupMessage(messageId, messageArg);
         if (errorCollector != null) {
@@ -246,7 +249,7 @@
         } else {
             int lineno = 1, offset = 1;
             String line = "";
-            if (ts != null) {  // happens in some regression tests
+            if (ts != null) { // happens in some regression tests
                 lineno = ts.getLineno();
                 line = ts.getLine();
                 offset = ts.getOffset();
@@ -255,17 +258,27 @@
         }
     }
 
-    private void addStrictWarning(String messageId, String messageArg,
-                                  int position, int length,
-                                  int line, String lineSource, int lineOffset) {
+    private void addStrictWarning(
+            String messageId,
+            String messageArg,
+            int position,
+            int length,
+            int line,
+            String lineSource,
+            int lineOffset) {
         if (compilerEnv.isStrictMode()) {
             addWarning(messageId, messageArg, position, length, line, lineSource, lineOffset);
         }
     }
 
-    private void addWarning(String messageId, String messageArg,
-                            int position, int length,
-                            int line, String lineSource, int lineOffset) {
+    private void addWarning(
+            String messageId,
+            String messageArg,
+            int position,
+            int length,
+            int line,
+            String lineSource,
+            int lineOffset) {
         String message = lookupMessage(messageId, messageArg);
         if (compilerEnv.reportWarningAsError()) {
             addError(messageId, messageArg, position, length, line, lineSource, lineOffset);
@@ -276,9 +289,14 @@
         }
     }
 
-    private void addError(String messageId, String messageArg,
-                          int position, int length,
-                          int line, String lineSource, int lineOffset) {
+    private void addError(
+            String messageId,
+            String messageArg,
+            int position,
+            int length,
+            int line,
+            String lineSource,
+            int lineOffset) {
         ++syntaxErrorCount;
         String message = lookupMessage(messageId, messageArg);
         if (errorCollector != null) {
@@ -294,8 +312,8 @@
 
     String lookupMessage(String messageId, String messageArg) {
         return messageArg == null
-            ? ScriptRuntime.getMessage0(messageId)
-            : ScriptRuntime.getMessage1(messageId, messageArg);
+                ? ScriptRuntime.getMessageById(messageId)
+                : ScriptRuntime.getMessageById(messageId, messageArg);
     }
 
     void reportError(String messageId) {
@@ -303,22 +321,18 @@
     }
 
     void reportError(String messageId, String messageArg) {
-        if (ts == null) {  // happens in some regression tests
+        if (ts == null) { // happens in some regression tests
             reportError(messageId, messageArg, 1, 1);
         } else {
-            reportError(messageId, messageArg, ts.tokenBeg,
-                        ts.tokenEnd - ts.tokenBeg);
+            reportError(messageId, messageArg, ts.tokenBeg, ts.tokenEnd - ts.tokenBeg);
         }
     }
 
-    void reportError(String messageId, int position, int length)
-    {
+    void reportError(String messageId, int position, int length) {
         reportError(messageId, null, position, length);
     }
 
-    void reportError(String messageId, String messageArg, int position,
-                     int length)
-    {
+    void reportError(String messageId, String messageArg, int position, int length) {
         addError(messageId, messageArg, position, length);
 
         if (!compilerEnv.recoverFromErrors()) {
@@ -329,7 +343,7 @@
     // Computes the absolute end offset of node N.
     // Use with caution!  Assumes n.getPosition() is -absolute-, which
     // is only true before the node is added to its parent.
-    private int getNodeEnd(AstNode n) {
+    private static int getNodeEnd(AstNode n) {
         return n.getPosition() + n.getLength();
     }
 
@@ -337,13 +351,14 @@
         if (scannedComments == null) {
             scannedComments = new ArrayList<Comment>();
         }
-        Comment commentNode = new Comment(ts.tokenBeg,
-                                          ts.getTokenLength(),
-                                          ts.commentType,
-                                          comment);
-        if (ts.commentType == Token.CommentType.JSDOC &&
-            compilerEnv.isRecordingLocalJsDocComments()) {
-            currentJsDocComment = commentNode;
+        Comment commentNode =
+                new Comment(ts.tokenBeg, ts.getTokenLength(), ts.commentType, comment);
+        if (ts.commentType == Token.CommentType.JSDOC
+                && compilerEnv.isRecordingLocalJsDocComments()) {
+            Comment jsDocCommentNode =
+                    new Comment(ts.tokenBeg, ts.getTokenLength(), ts.commentType, comment);
+            currentJsDocComment = jsDocCommentNode;
+            currentJsDocComment.setLineno(lineno);
         }
         commentNode.setLineno(lineno);
         scannedComments.add(commentNode);
@@ -355,18 +370,6 @@
         return saved;
     }
 
-
-    private int getNumberOfEols(String comment) {
-      int lines = 0;
-      for (int i = comment.length()-1; i >= 0; i--) {
-        if (comment.charAt(i) == '\n') {
-          lines++;
-        }
-      }
-      return lines;
-    }
-
-
     // Returns the next token without consuming it.
     // If previous token was consumed, calls scanner to get new token.
     // If previous token was -not- consumed, returns it (idempotent).
@@ -382,9 +385,7 @@
     //
     // Note that this function always returned the un-flagged token!
     // The flags, if any, are saved in currentFlaggedToken.
-    private int peekToken()
-        throws IOException
-    {
+    private int peekToken() throws IOException {
         // By far the most common case:  last token hasn't been consumed,
         // so return already-peeked token.
         if (currentFlaggedToken != Token.EOF) {
@@ -400,26 +401,23 @@
             if (tt == Token.EOL) {
                 lineno++;
                 sawEOL = true;
+                tt = ts.getToken();
             } else {
                 if (compilerEnv.isRecordingComments()) {
                     String comment = ts.getAndResetCurrentComment();
                     recordComment(lineno, comment);
-                    // Comments may contain multiple lines, get the number
-                    // of EoLs and increase the lineno
-                    lineno += getNumberOfEols(comment);
+                    break;
                 }
+                tt = ts.getToken();
             }
-            tt = ts.getToken();
         }
 
         currentToken = tt;
         currentFlaggedToken = tt | (sawEOL ? TI_AFTER_EOL : 0);
-        return currentToken;  // return unflagged token
+        return currentToken; // return unflagged token
     }
 
-    private int peekFlaggedToken()
-        throws IOException
-    {
+    private int peekFlaggedToken() throws IOException {
         peekToken();
         return currentFlaggedToken;
     }
@@ -428,27 +426,19 @@
         currentFlaggedToken = Token.EOF;
     }
 
-    private int nextToken()
-        throws IOException
-    {
+    private int nextToken() throws IOException {
         int tt = peekToken();
         consumeToken();
         return tt;
     }
 
-    private int nextFlaggedToken()
-        throws IOException
-    {
-        peekToken();
-        int ttFlagged = currentFlaggedToken;
-        consumeToken();
-        return ttFlagged;
-    }
-
-    private boolean matchToken(int toMatch)
-        throws IOException
-    {
-        if (peekToken() != toMatch) {
+    private boolean matchToken(int toMatch, boolean ignoreComment) throws IOException {
+        int tt = peekToken();
+        while (tt == Token.COMMENT && ignoreComment) {
+            consumeToken();
+            tt = peekToken();
+        }
+        if (tt != toMatch) {
             return false;
         }
         consumeToken();
@@ -460,9 +450,7 @@
     // token types valid if they are preceded by a newline.  One example is the
     // postfix ++ or -- operator, which has to be on the same line as its
     // operand.
-    private int peekTokenOrEOL()
-        throws IOException
-    {
+    private int peekTokenOrEOL() throws IOException {
         int tt = peekToken();
         // Check for last peeked token flags
         if ((currentFlaggedToken & TI_AFTER_EOL) != 0) {
@@ -471,17 +459,15 @@
         return tt;
     }
 
-    private boolean mustMatchToken(int toMatch, String messageId)
-        throws IOException
-    {
-        return mustMatchToken(toMatch, messageId, ts.tokenBeg,
-                              ts.tokenEnd - ts.tokenBeg);
+    private boolean mustMatchToken(int toMatch, String messageId, boolean ignoreComment)
+            throws IOException {
+        return mustMatchToken(
+                toMatch, messageId, ts.tokenBeg, ts.tokenEnd - ts.tokenBeg, ignoreComment);
     }
 
-    private boolean mustMatchToken(int toMatch, String msgId, int pos, int len)
-        throws IOException
-    {
-        if (matchToken(toMatch)) {
+    private boolean mustMatchToken(
+            int toMatch, String msgId, int pos, int len, boolean ignoreComment) throws IOException {
+        if (matchToken(toMatch, ignoreComment)) {
             return true;
         }
         reportError(msgId, pos, len);
@@ -507,8 +493,7 @@
         // During codegen, parent scope chain may already be initialized,
         // in which case we just need to set currentScope variable.
         if (parent != null) {
-            if (parent != currentScope)
-                codeBug();
+            if (parent != currentScope) codeBug();
         } else {
             currentScope.addChildScope(scope);
         }
@@ -520,11 +505,9 @@
     }
 
     private void enterLoop(Loop loop) {
-        if (loopSet == null)
-            loopSet = new ArrayList<Loop>();
+        if (loopSet == null) loopSet = new ArrayList<Loop>();
         loopSet.add(loop);
-        if (loopAndSwitchSet == null)
-            loopAndSwitchSet = new ArrayList<Jump>();
+        if (loopAndSwitchSet == null) loopAndSwitchSet = new ArrayList<Jump>();
         loopAndSwitchSet.add(loop);
         pushScope(loop);
         if (currentLabel != null) {
@@ -533,23 +516,27 @@
             // This is the only time during parsing that we set a node's parent
             // before parsing the children.  In order for the child node offsets
             // to be correct, we adjust the loop's reported position back to an
-            // absolute source offset, and restore it when we call exitLoop().
+            // absolute source offset, and restore it when we call
+            // restoreRelativeLoopPosition() (invoked just before setBody() is
+            // called on the loop).
             loop.setRelative(-currentLabel.getPosition());
         }
     }
 
     private void exitLoop() {
-        Loop loop = loopSet.remove(loopSet.size() - 1);
+        loopSet.remove(loopSet.size() - 1);
         loopAndSwitchSet.remove(loopAndSwitchSet.size() - 1);
-        if (loop.getParent() != null) {  // see comment in enterLoop
+        popScope();
+    }
+
+    private void restoreRelativeLoopPosition(Loop loop) {
+        if (loop.getParent() != null) { // see comment in enterLoop
             loop.setRelative(loop.getParent().getPosition());
         }
-        popScope();
     }
 
     private void enterSwitch(SwitchStatement node) {
-        if (loopAndSwitchSet == null)
-            loopAndSwitchSet = new ArrayList<Jump>();
+        if (loopAndSwitchSet == null) loopAndSwitchSet = new ArrayList<Jump>();
         loopAndSwitchSet.add(node);
     }
 
@@ -560,13 +547,11 @@
     /**
      * Builds a parse tree from the given source string.
      *
-     * @return an {@link AstRoot} object representing the parsed program.  If
-     * the parse fails, {@code null} will be returned.  (The parse failure will
-     * result in a call to the {@link ErrorReporter} from
-     * {@link CompilerEnvirons}.)
+     * @return an {@link AstRoot} object representing the parsed program. If the parse fails, {@code
+     *     null} will be returned. (The parse failure will result in a call to the {@link
+     *     ErrorReporter} from {@link CompilerEnvirons}.)
      */
-    public AstRoot parse(String sourceString, String sourceURI, int lineno)
-    {
+    public AstRoot parse(String sourceString, String sourceURI, int lineno) {
         if (parseFinished) throw new IllegalStateException("parser reused");
         this.sourceURI = sourceURI;
         if (compilerEnv.isIdeMode()) {
@@ -585,15 +570,16 @@
 
     /**
      * Builds a parse tree from the given sourcereader.
+     *
      * @see #parse(String,String,int)
      * @throws IOException if the {@link Reader} encounters an error
+     * @deprecated use parse(String, String, int) instead
      */
-    public AstRoot parse(Reader sourceReader, String sourceURI, int lineno)
-        throws IOException
-    {
+    @Deprecated
+    public AstRoot parse(Reader sourceReader, String sourceURI, int lineno) throws IOException {
         if (parseFinished) throw new IllegalStateException("parser reused");
         if (compilerEnv.isIdeMode()) {
-            return parse(readFully(sourceReader), sourceURI, lineno);
+            return parse(Kit.readReader(sourceReader), sourceURI, lineno);
         }
         try {
             this.sourceURI = sourceURI;
@@ -604,14 +590,13 @@
         }
     }
 
-    private AstRoot parse() throws IOException
-    {
+    private AstRoot parse() throws IOException {
         int pos = 0;
         AstRoot root = new AstRoot(pos);
         currentScope = currentScriptOrFn = root;
 
-        int baseLineno = ts.lineno;  // line number where source starts
-        int end = pos;  // in case source is empty
+        int baseLineno = ts.lineno; // line number where source starts
+        int end = pos; // in case source is empty
 
         boolean inDirectivePrologue = true;
         boolean savedStrictMode = inUseStrictDirective;
@@ -622,7 +607,7 @@
         }
 
         try {
-            for (;;) {
+            for (; ; ) {
                 int tt = peekToken();
                 if (tt <= Token.EOF) {
                     break;
@@ -632,12 +617,17 @@
                 if (tt == Token.FUNCTION) {
                     consumeToken();
                     try {
-                        n = function(calledByCompileFunction
-                                     ? FunctionNode.FUNCTION_EXPRESSION
-                                     : FunctionNode.FUNCTION_STATEMENT);
+                        n =
+                                function(
+                                        calledByCompileFunction
+                                                ? FunctionNode.FUNCTION_EXPRESSION
+                                                : FunctionNode.FUNCTION_STATEMENT);
                     } catch (ParserException e) {
                         break;
                     }
+                } else if (tt == Token.COMMENT) {
+                    n = scannedComments.get(scannedComments.size() - 1);
+                    consumeToken();
                 } else {
                     n = statement();
                     if (inDirectivePrologue) {
@@ -649,7 +639,6 @@
                             root.setInStrictMode(true);
                         }
                     }
-
                 }
                 end = getNodeEnd(n);
                 root.addChildToBack(n);
@@ -658,8 +647,7 @@
         } catch (StackOverflowError ex) {
             String msg = lookupMessage("msg.too.deep.parser.recursion");
             if (!compilerEnv.isIdeMode())
-                throw Context.reportRuntimeError(msg, sourceURI,
-                                                 ts.lineno, null, 0);
+                throw Context.reportRuntimeError(msg, sourceURI, ts.lineno, null, 0);
         } finally {
             inUseStrictDirective = savedStrictMode;
         }
@@ -668,8 +656,7 @@
             String msg = String.valueOf(this.syntaxErrorCount);
             msg = lookupMessage("msg.got.syntax.errors", msg);
             if (!compilerEnv.isIdeMode())
-                throw errorReporter.runtimeError(msg, sourceURI, baseLineno,
-                                                 null, 0);
+                throw errorReporter.runtimeError(msg, sourceURI, baseLineno, null, 0);
         }
 
         // add comments to root in lexical order
@@ -690,12 +677,11 @@
         return root;
     }
 
-    private AstNode parseFunctionBody(int type, FunctionNode fnNode)
-        throws IOException
-    {
+    private AstNode parseFunctionBody(int type, FunctionNode fnNode) throws IOException {
         boolean isExpressionClosure = false;
-        if (!matchToken(Token.LC)) {
-            if (compilerEnv.getLanguageVersion() < Context.VERSION_1_8 && type != FunctionNode.ARROW_FUNCTION) {
+        if (!matchToken(Token.LC, true)) {
+            if (compilerEnv.getLanguageVersion() < Context.VERSION_1_8
+                    && type != FunctionNode.ARROW_FUNCTION) {
                 reportError("msg.no.brace.body");
             } else {
                 isExpressionClosure = true;
@@ -704,17 +690,23 @@
         boolean isArrow = type == FunctionNode.ARROW_FUNCTION;
         ++nestingOfFunction;
         int pos = ts.tokenBeg;
-        Block pn = new Block(pos);  // starts at LC position
+        Block pn = new Block(pos); // starts at LC position
 
+        // Function code that is supplied as the arguments to the built-in
+        // Function, Generator, and AsyncFunction constructors is strict mode code
+        // if the last argument is a String that when processed is a FunctionBody
+        // that begins with a Directive Prologue that contains a Use Strict Directive.
         boolean inDirectivePrologue = true;
         boolean savedStrictMode = inUseStrictDirective;
-        // Don't set 'inUseStrictDirective' to false: inherit strict mode.
+        inUseStrictDirective = false;
 
         pn.setLineno(ts.lineno);
         try {
             if (isExpressionClosure) {
-                ReturnStatement n = new ReturnStatement(ts.lineno);
-                n.setReturnValue(assignExpr());
+                AstNode returnValue = assignExpr();
+                ReturnStatement n =
+                        new ReturnStatement(
+                                returnValue.getPosition(), returnValue.getLength(), returnValue);
                 // expression closure flag is required on both nodes
                 n.putProp(Node.EXPRESSION_CLOSURE_PROP, Boolean.TRUE);
                 pn.putProp(Node.EXPRESSION_CLOSURE_PROP, Boolean.TRUE);
@@ -723,7 +715,8 @@
                 }
                 pn.addStatement(n);
             } else {
-                bodyLoop: for (;;) {
+                bodyLoop:
+                for (; ; ) {
                     AstNode n;
                     int tt = peekToken();
                     switch (tt) {
@@ -731,7 +724,10 @@
                         case Token.EOF:
                         case Token.RC:
                             break bodyLoop;
-
+                        case Token.COMMENT:
+                            consumeToken();
+                            n = scannedComments.get(scannedComments.size() - 1);
+                            break;
                         case Token.FUNCTION:
                             consumeToken();
                             n = function(FunctionNode.FUNCTION_STATEMENT);
@@ -764,13 +760,13 @@
 
         int end = ts.tokenEnd;
         getAndResetJsDoc();
-        if (!isExpressionClosure && mustMatchToken(Token.RC, "msg.no.brace.after.body"))
+        if (!isExpressionClosure && mustMatchToken(Token.RC, "msg.no.brace.after.body", true))
             end = ts.tokenEnd;
         pn.setLength(end - pos);
         return pn;
     }
 
-    private String getDirective(AstNode n) {
+    private static String getDirective(AstNode n) {
         if (n instanceof ExpressionStatement) {
             AstNode e = ((ExpressionStatement) n).getExpression();
             if (e instanceof StringLiteral) {
@@ -780,10 +776,8 @@
         return null;
     }
 
-    private void  parseFunctionParams(FunctionNode fnNode)
-        throws IOException
-    {
-        if (matchToken(Token.RP)) {
+    private void parseFunctionParams(FunctionNode fnNode) throws IOException {
+        if (matchToken(Token.RP, true)) {
             fnNode.setRp(ts.tokenBeg - fnNode.getPosition());
             return;
         }
@@ -807,14 +801,17 @@
                 defineSymbol(Token.LP, pname, false);
                 destructuring.put(pname, expr);
             } else {
-                if (mustMatchToken(Token.NAME, "msg.no.parm")) {
-                    fnNode.addParam(createNameNode());
+                if (mustMatchToken(Token.NAME, "msg.no.parm", true)) {
+                    Name paramNameNode = createNameNode();
+                    Comment jsdocNodeForName = getAndResetJsDoc();
+                    if (jsdocNodeForName != null) {
+                        paramNameNode.setJsDocNode(jsdocNodeForName);
+                    }
+                    fnNode.addParam(paramNameNode);
                     String paramName = ts.getString();
                     defineSymbol(Token.LP, paramName);
                     if (this.inUseStrictDirective) {
-                        if ("eval".equals(paramName) ||
-                            "arguments".equals(paramName))
-                        {
+                        if ("eval".equals(paramName) || "arguments".equals(paramName)) {
                             reportError("msg.bad.id.strict", paramName);
                         }
                         if (paramNames.contains(paramName))
@@ -825,52 +822,58 @@
                     fnNode.addParam(makeErrorNode());
                 }
             }
-        } while (matchToken(Token.COMMA));
+        } while (matchToken(Token.COMMA, true));
 
         if (destructuring != null) {
             Node destructuringNode = new Node(Token.COMMA);
             // Add assignment helper for each destructuring parameter
-            for (Map.Entry<String, Node> param: destructuring.entrySet()) {
-                Node assign = createDestructuringAssignment(Token.VAR,
-                        param.getValue(), createName(param.getKey()));
+            for (Map.Entry<String, Node> param : destructuring.entrySet()) {
+                Node assign =
+                        createDestructuringAssignment(
+                                Token.VAR, param.getValue(), createName(param.getKey()));
                 destructuringNode.addChildToBack(assign);
-
             }
             fnNode.putProp(Node.DESTRUCTURING_PARAMS, destructuringNode);
         }
 
-        if (mustMatchToken(Token.RP, "msg.no.paren.after.parms")) {
+        if (mustMatchToken(Token.RP, "msg.no.paren.after.parms", true)) {
             fnNode.setRp(ts.tokenBeg - fnNode.getPosition());
         }
     }
 
-    private FunctionNode function(int type)
-        throws IOException
-    {
+    private FunctionNode function(int type) throws IOException {
+        return function(type, false);
+    }
+
+    private FunctionNode function(int type, boolean isGenerator) throws IOException {
         int syntheticType = type;
-        int baseLineno = ts.lineno;  // line number where source starts
-        int functionSourceStart = ts.tokenBeg;  // start of "function" kwd
+        int baseLineno = ts.lineno; // line number where source starts
+        int functionSourceStart = ts.tokenBeg; // start of "function" kwd
         Name name = null;
         AstNode memberExprNode = null;
 
-        if (matchToken(Token.NAME)) {
+        if (matchToken(Token.NAME, true)) {
             name = createNameNode(true, Token.NAME);
             if (inUseStrictDirective) {
                 String id = name.getIdentifier();
-                if ("eval".equals(id)|| "arguments".equals(id)) {
+                if ("eval".equals(id) || "arguments".equals(id)) {
                     reportError("msg.bad.id.strict", id);
                 }
             }
-            if (!matchToken(Token.LP)) {
+            if (!matchToken(Token.LP, true)) {
                 if (compilerEnv.isAllowMemberExprAsFunctionName()) {
                     AstNode memberExprHead = name;
                     name = null;
                     memberExprNode = memberExprTail(false, memberExprHead);
                 }
-                mustMatchToken(Token.LP, "msg.no.paren.parms");
+                mustMatchToken(Token.LP, "msg.no.paren.parms", true);
             }
-        } else if (matchToken(Token.LP)) {
+        } else if (matchToken(Token.LP, true)) {
             // Anonymous function:  leave name as null
+        } else if (matchToken(Token.MUL, true)
+                && (compilerEnv.getLanguageVersion() >= Context.VERSION_ES6)) {
+            // ES6 generator function
+            return function(type, true);
         } else {
             if (compilerEnv.isAllowMemberExprAsFunctionName()) {
                 // Note that memberExpr can not start with '(' like
@@ -878,7 +881,7 @@
                 // processed as anonymous function
                 memberExprNode = memberExpr(false);
             }
-            mustMatchToken(Token.LP, "msg.no.paren.parms");
+            mustMatchToken(Token.LP, "msg.no.paren.parms", true);
         }
         int lpPos = currentToken == Token.LP ? ts.tokenBeg : -1;
 
@@ -887,15 +890,18 @@
         }
 
         if (syntheticType != FunctionNode.FUNCTION_EXPRESSION
-            && name != null && name.length() > 0) {
+                && name != null
+                && name.length() > 0) {
             // Function statements define a symbol in the enclosing scope
             defineSymbol(Token.FUNCTION, name.getIdentifier());
         }
 
         FunctionNode fnNode = new FunctionNode(functionSourceStart, name);
         fnNode.setFunctionType(type);
-        if (lpPos != -1)
-            fnNode.setLp(lpPos - functionSourceStart);
+        if (isGenerator) {
+            fnNode.setIsES6Generator();
+        }
+        if (lpPos != -1) fnNode.setLp(lpPos - functionSourceStart);
 
         fnNode.setJsDocNode(getAndResetJsDoc());
 
@@ -906,11 +912,11 @@
             fnNode.setEncodedSourceBounds(functionSourceStart, ts.tokenEnd);
             fnNode.setLength(ts.tokenEnd - functionSourceStart);
 
-            if (compilerEnv.isStrictMode()
-                && !fnNode.getBody().hasConsistentReturnUsage()) {
-                String msg = (name != null && name.length() > 0)
-                           ? "msg.no.return.value"
-                           : "msg.anon.no.return.value";
+            if (compilerEnv.isStrictMode() && !fnNode.getBody().hasConsistentReturnUsage()) {
+                String msg =
+                        (name != null && name.length() > 0)
+                                ? "msg.no.return.value"
+                                : "msg.anon.no.return.value";
                 addStrictWarning(msg, name == null ? "" : name.getIdentifier());
             }
         } finally {
@@ -920,7 +926,7 @@
         if (memberExprNode != null) {
             // TODO(stevey): fix missing functionality
             Kit.codeBug();
-            fnNode.setMemberExprNode(memberExprNode);  // rewrite later
+            fnNode.setMemberExprNode(memberExprNode); // rewrite later
             /* old code:
             if (memberExprNode != null) {
                 pn = nf.createAssignment(Token.ASSIGN, memberExprNode, pn);
@@ -947,8 +953,9 @@
     }
 
     private AstNode arrowFunction(AstNode params) throws IOException {
-        int baseLineno = ts.lineno;  // line number where source starts
-        int functionSourceStart = params != null ? params.getPosition() : -1;  // start of "function" kwd
+        int baseLineno = ts.lineno; // line number where source starts
+        int functionSourceStart =
+                params != null ? params.getPosition() : -1; // start of "function" kwd
 
         FunctionNode fnNode = new FunctionNode(functionSourceStart);
         fnNode.setFunctionType(FunctionNode.ARROW_FUNCTION);
@@ -963,7 +970,7 @@
         try {
             if (params instanceof ParenthesizedExpression) {
                 fnNode.setParens(0, params.getLength());
-                AstNode p = ((ParenthesizedExpression)params).getExpression();
+                AstNode p = ((ParenthesizedExpression) params).getExpression();
                 if (!(p instanceof EmptyExpression)) {
                     arrowFunctionParams(fnNode, p, destructuring, paramNames);
                 }
@@ -974,15 +981,15 @@
             if (!destructuring.isEmpty()) {
                 Node destructuringNode = new Node(Token.COMMA);
                 // Add assignment helper for each destructuring parameter
-                for (Map.Entry<String, Node> param: destructuring.entrySet()) {
-                    Node assign = createDestructuringAssignment(Token.VAR,
-                                                                param.getValue(), createName(param.getKey()));
+                for (Map.Entry<String, Node> param : destructuring.entrySet()) {
+                    Node assign =
+                            createDestructuringAssignment(
+                                    Token.VAR, param.getValue(), createName(param.getKey()));
                     destructuringNode.addChildToBack(assign);
-
                 }
                 fnNode.putProp(Node.DESTRUCTURING_PARAMS, destructuringNode);
             }
-                
+
             fnNode.setBody(parseFunctionBody(FunctionNode.ARROW_FUNCTION, fnNode));
             fnNode.setEncodedSourceBounds(functionSourceStart, ts.tokenEnd);
             fnNode.setLength(ts.tokenEnd - functionSourceStart);
@@ -1002,7 +1009,11 @@
         return fnNode;
     }
 
-    private void arrowFunctionParams(FunctionNode fnNode, AstNode params, Map<String, Node> destructuring, Set<String> paramNames) {
+    private void arrowFunctionParams(
+            FunctionNode fnNode,
+            AstNode params,
+            Map<String, Node> destructuring,
+            Set<String> paramNames) {
         if (params instanceof ArrayLiteral || params instanceof ObjectLiteral) {
             markDestructuring(params);
             fnNode.addParam(params);
@@ -1010,21 +1021,20 @@
             defineSymbol(Token.LP, pname, false);
             destructuring.put(pname, params);
         } else if (params instanceof InfixExpression && params.getType() == Token.COMMA) {
-            arrowFunctionParams(fnNode, ((InfixExpression)params).getLeft(), destructuring, paramNames);
-            arrowFunctionParams(fnNode, ((InfixExpression)params).getRight(), destructuring, paramNames);
+            arrowFunctionParams(
+                    fnNode, ((InfixExpression) params).getLeft(), destructuring, paramNames);
+            arrowFunctionParams(
+                    fnNode, ((InfixExpression) params).getRight(), destructuring, paramNames);
         } else if (params instanceof Name) {
             fnNode.addParam(params);
-            String paramName = ((Name)params).getIdentifier();
+            String paramName = ((Name) params).getIdentifier();
             defineSymbol(Token.LP, paramName);
 
             if (this.inUseStrictDirective) {
-                if ("eval".equals(paramName) ||
-                    "arguments".equals(paramName))
-                    {
-                        reportError("msg.bad.id.strict", paramName);
-                    }
-                if (paramNames.contains(paramName))
-                    addError("msg.dup.param.strict", paramName);
+                if ("eval".equals(paramName) || "arguments".equals(paramName)) {
+                    reportError("msg.bad.id.strict", paramName);
+                }
+                if (paramNames.contains(paramName)) addError("msg.dup.param.strict", paramName);
                 paramNames.add(paramName);
             }
         } else {
@@ -1042,8 +1052,8 @@
     // node are given relative start positions and correct lengths.
 
     private AstNode statements(AstNode parent) throws IOException {
-        if (currentToken != Token.LC  // assertion can be invalid in bad code
-            && !compilerEnv.isIdeMode()) codeBug();
+        if (currentToken != Token.LC // assertion can be invalid in bad code
+                && !compilerEnv.isIdeMode()) codeBug();
         int pos = ts.tokenBeg;
         AstNode block = parent != null ? parent : new Block(pos);
         block.setLineno(ts.lineno);
@@ -1067,32 +1077,28 @@
     }
 
     // parse and return a parenthesized expression
-    private ConditionData condition()
-        throws IOException
-    {
+    private ConditionData condition() throws IOException {
         ConditionData data = new ConditionData();
 
-        if (mustMatchToken(Token.LP, "msg.no.paren.cond"))
-            data.lp = ts.tokenBeg;
+        if (mustMatchToken(Token.LP, "msg.no.paren.cond", true)) data.lp = ts.tokenBeg;
 
         data.condition = expr();
 
-        if (mustMatchToken(Token.RP, "msg.no.paren.after.cond"))
-            data.rp = ts.tokenBeg;
+        if (mustMatchToken(Token.RP, "msg.no.paren.after.cond", true)) data.rp = ts.tokenBeg;
 
         // Report strict warning on code like "if (a = 7) ...". Suppress the
         // warning if the condition is parenthesized, like "if ((a = 7)) ...".
         if (data.condition instanceof Assignment) {
-            addStrictWarning("msg.equal.as.assign", "",
-                             data.condition.getPosition(),
-                             data.condition.getLength());
+            addStrictWarning(
+                    "msg.equal.as.assign",
+                    "",
+                    data.condition.getPosition(),
+                    data.condition.getLength());
         }
         return data;
     }
 
-    private AstNode statement()
-        throws IOException
-    {
+    private AstNode statement() throws IOException {
         int pos = ts.tokenBeg;
         try {
             AstNode pn = statementHelper();
@@ -1100,10 +1106,20 @@
                 if (compilerEnv.isStrictMode() && !pn.hasSideEffects()) {
                     int beg = pn.getPosition();
                     beg = Math.max(beg, lineBeginningFor(beg));
-                    addStrictWarning(pn instanceof EmptyStatement
-                                     ? "msg.extra.trailing.semi"
-                                     : "msg.no.side.effects",
-                                     "", beg, nodeEnd(pn) - beg);
+                    addStrictWarning(
+                            pn instanceof EmptyStatement
+                                    ? "msg.extra.trailing.semi"
+                                    : "msg.no.side.effects",
+                            "",
+                            beg,
+                            nodeEnd(pn) - beg);
+                }
+                int ntt = peekToken();
+                if (ntt == Token.COMMENT
+                        && pn.getLineno()
+                                == scannedComments.get(scannedComments.size() - 1).getLineno()) {
+                    pn.setInlineComment(scannedComments.get(scannedComments.size() - 1));
+                    consumeToken();
                 }
                 return pn;
             }
@@ -1112,15 +1128,16 @@
         }
 
         // error:  skip ahead to a probable statement boundary
-        guessingStatementEnd: for (;;) {
+        guessingStatementEnd:
+        for (; ; ) {
             int tt = peekTokenOrEOL();
             consumeToken();
             switch (tt) {
-              case Token.ERROR:
-              case Token.EOF:
-              case Token.EOL:
-              case Token.SEMI:
-                break guessingStatementEnd;
+                case Token.ERROR:
+                case Token.EOF:
+                case Token.EOL:
+                case Token.SEMI:
+                    break guessingStatementEnd;
             }
         }
         // We don't make error nodes explicitly part of the tree;
@@ -1129,113 +1146,109 @@
         return new EmptyStatement(pos, ts.tokenBeg - pos);
     }
 
-    private AstNode statementHelper()
-        throws IOException
-    {
+    private AstNode statementHelper() throws IOException {
         // If the statement is set, then it's been told its label by now.
-        if (currentLabel != null && currentLabel.getStatement() != null)
-            currentLabel = null;
+        if (currentLabel != null && currentLabel.getStatement() != null) currentLabel = null;
 
         AstNode pn = null;
         int tt = peekToken(), pos = ts.tokenBeg;
 
         switch (tt) {
-          case Token.IF:
-              return ifStatement();
+            case Token.IF:
+                return ifStatement();
+
+            case Token.SWITCH:
+                return switchStatement();
+
+            case Token.WHILE:
+                return whileLoop();
+
+            case Token.DO:
+                return doLoop();
+
+            case Token.FOR:
+                return forLoop();
+
+            case Token.TRY:
+                return tryStatement();
+
+            case Token.THROW:
+                pn = throwStatement();
+                break;
+
+            case Token.BREAK:
+                pn = breakStatement();
+                break;
+
+            case Token.CONTINUE:
+                pn = continueStatement();
+                break;
+
+            case Token.WITH:
+                if (this.inUseStrictDirective) {
+                    reportError("msg.no.with.strict");
+                }
+                return withStatement();
+
+            case Token.CONST:
+            case Token.VAR:
+                consumeToken();
+                int lineno = ts.lineno;
+                pn = variables(currentToken, ts.tokenBeg, true);
+                pn.setLineno(lineno);
+                break;
+
+            case Token.LET:
+                pn = letStatement();
+                if (pn instanceof VariableDeclaration && peekToken() == Token.SEMI) break;
+                return pn;
+
+            case Token.RETURN:
+            case Token.YIELD:
+                pn = returnOrYield(tt, false);
+                break;
 
-          case Token.SWITCH:
-              return switchStatement();
+            case Token.DEBUGGER:
+                consumeToken();
+                pn = new KeywordLiteral(ts.tokenBeg, ts.tokenEnd - ts.tokenBeg, tt);
+                pn.setLineno(ts.lineno);
+                break;
+
+            case Token.LC:
+                return block();
+
+            case Token.ERROR:
+                consumeToken();
+                return makeErrorNode();
+
+            case Token.SEMI:
+                consumeToken();
+                pos = ts.tokenBeg;
+                pn = new EmptyStatement(pos, ts.tokenEnd - pos);
+                pn.setLineno(ts.lineno);
+                return pn;
+
+            case Token.FUNCTION:
+                consumeToken();
+                return function(FunctionNode.FUNCTION_EXPRESSION_STATEMENT);
 
-          case Token.WHILE:
-              return whileLoop();
+            case Token.DEFAULT:
+                pn = defaultXmlNamespace();
+                break;
 
-          case Token.DO:
-              return doLoop();
-
-          case Token.FOR:
-              return forLoop();
-
-          case Token.TRY:
-              return tryStatement();
-
-          case Token.THROW:
-              pn = throwStatement();
-              break;
-
-          case Token.BREAK:
-              pn = breakStatement();
-              break;
-
-          case Token.CONTINUE:
-              pn = continueStatement();
-              break;
-
-          case Token.WITH:
-              if (this.inUseStrictDirective) {
-                  reportError("msg.no.with.strict");
-              }
-              return withStatement();
-
-          case Token.CONST:
-          case Token.VAR:
-              consumeToken();
-              int lineno = ts.lineno;
-              pn = variables(currentToken, ts.tokenBeg, true);
-              pn.setLineno(lineno);
-              break;
-
-          case Token.LET:
-              pn = letStatement();
-              if (pn instanceof VariableDeclaration
-                  && peekToken() == Token.SEMI)
-                  break;
-              return pn;
-
-          case Token.RETURN:
-          case Token.YIELD:
-              pn = returnOrYield(tt, false);
-              break;
-
-          case Token.DEBUGGER:
-              consumeToken();
-              pn = new KeywordLiteral(ts.tokenBeg,
-                                      ts.tokenEnd - ts.tokenBeg, tt);
-              pn.setLineno(ts.lineno);
-              break;
-
-          case Token.LC:
-              return block();
-
-          case Token.ERROR:
-              consumeToken();
-              return makeErrorNode();
-
-          case Token.SEMI:
-              consumeToken();
-              pos = ts.tokenBeg;
-              pn = new EmptyStatement(pos, ts.tokenEnd - pos);
-              pn.setLineno(ts.lineno);
-              return pn;
-
-          case Token.FUNCTION:
-              consumeToken();
-              return function(FunctionNode.FUNCTION_EXPRESSION_STATEMENT);
-
-          case Token.DEFAULT :
-              pn = defaultXmlNamespace();
-              break;
-
-          case Token.NAME:
-              pn = nameOrLabel();
-              if (pn instanceof ExpressionStatement)
-                  break;
-              return pn;  // LabeledStatement
-
-          default:
-              lineno = ts.lineno;
-              pn = new ExpressionStatement(expr(), !insideFunction());
-              pn.setLineno(lineno);
-              break;
+            case Token.NAME:
+                pn = nameOrLabel();
+                if (pn instanceof ExpressionStatement) break;
+                return pn; // LabeledStatement
+            case Token.COMMENT:
+                // Do not consume token here
+                pn = scannedComments.get(scannedComments.size() - 1);
+                return pn;
+            default:
+                lineno = ts.lineno;
+                pn = new ExpressionStatement(expr(), !insideFunction());
+                pn.setLineno(lineno);
+                break;
         }
 
         autoInsertSemicolon(pn);
@@ -1246,43 +1259,49 @@
         int ttFlagged = peekFlaggedToken();
         int pos = pn.getPosition();
         switch (ttFlagged & CLEAR_TI_MASK) {
-          case Token.SEMI:
-              // Consume ';' as a part of expression
-              consumeToken();
-              // extend the node bounds to include the semicolon.
-              pn.setLength(ts.tokenEnd - pos);
-              break;
-          case Token.ERROR:
-          case Token.EOF:
-          case Token.RC:
-              // Autoinsert ;
-              warnMissingSemi(pos, nodeEnd(pn));
-              break;
-          default:
-              if ((ttFlagged & TI_AFTER_EOL) == 0) {
-                  // Report error if no EOL or autoinsert ; otherwise
-                  reportError("msg.no.semi.stmt");
-              } else {
-                  warnMissingSemi(pos, nodeEnd(pn));
-              }
-              break;
+            case Token.SEMI:
+                // Consume ';' as a part of expression
+                consumeToken();
+                // extend the node bounds to include the semicolon.
+                pn.setLength(ts.tokenEnd - pos);
+                break;
+            case Token.ERROR:
+            case Token.EOF:
+            case Token.RC:
+                // Autoinsert ;
+                // Token.EOF can have negative length and negative nodeEnd(pn).
+                // So, make the end position at least pos+1.
+                warnMissingSemi(pos, Math.max(pos + 1, nodeEnd(pn)));
+                break;
+            default:
+                if ((ttFlagged & TI_AFTER_EOL) == 0) {
+                    // Report error if no EOL or autoinsert ; otherwise
+                    reportError("msg.no.semi.stmt");
+                } else {
+                    warnMissingSemi(pos, nodeEnd(pn));
+                }
+                break;
         }
     }
 
-    private IfStatement ifStatement()
-        throws IOException
-    {
+    private IfStatement ifStatement() throws IOException {
         if (currentToken != Token.IF) codeBug();
         consumeToken();
         int pos = ts.tokenBeg, lineno = ts.lineno, elsePos = -1;
+        IfStatement pn = new IfStatement(pos);
         ConditionData data = condition();
-        AstNode ifTrue = statement(), ifFalse = null;
-        if (matchToken(Token.ELSE)) {
+        AstNode ifTrue = getNextStatementAfterInlineComments(pn), ifFalse = null;
+        if (matchToken(Token.ELSE, true)) {
+            int tt = peekToken();
+            if (tt == Token.COMMENT) {
+                pn.setElseKeyWordInlineComment(scannedComments.get(scannedComments.size() - 1));
+                consumeToken();
+            }
             elsePos = ts.tokenBeg - pos;
             ifFalse = statement();
         }
         int end = getNodeEnd(ifFalse != null ? ifFalse : ifTrue);
-        IfStatement pn = new IfStatement(pos, end - pos);
+        pn.setLength(end - pos);
         pn.setCondition(data.condition);
         pn.setParens(data.lp - pos, data.rp - pos);
         pn.setThenPart(ifTrue);
@@ -1292,16 +1311,13 @@
         return pn;
     }
 
-    private SwitchStatement switchStatement()
-        throws IOException
-    {
+    private SwitchStatement switchStatement() throws IOException {
         if (currentToken != Token.SWITCH) codeBug();
         consumeToken();
         int pos = ts.tokenBeg;
 
         SwitchStatement pn = new SwitchStatement(pos);
-        if (mustMatchToken(Token.LP, "msg.no.paren.switch"))
-            pn.setLp(ts.tokenBeg - pos);
+        if (mustMatchToken(Token.LP, "msg.no.paren.switch", true)) pn.setLp(ts.tokenBeg - pos);
         pn.setLineno(ts.lineno);
 
         AstNode discriminant = expr();
@@ -1309,14 +1325,15 @@
         enterSwitch(pn);
 
         try {
-            if (mustMatchToken(Token.RP, "msg.no.paren.after.switch"))
+            if (mustMatchToken(Token.RP, "msg.no.paren.after.switch", true))
                 pn.setRp(ts.tokenBeg - pos);
 
-            mustMatchToken(Token.LC, "msg.no.brace.switch");
+            mustMatchToken(Token.LC, "msg.no.brace.switch", true);
 
             boolean hasDefault = false;
             int tt;
-            switchLoop: for (;;) {
+            switchLoop:
+            for (; ; ) {
                 tt = nextToken();
                 int casePos = ts.tokenBeg;
                 int caseLineno = ts.lineno;
@@ -1328,7 +1345,7 @@
 
                     case Token.CASE:
                         caseExpression = expr();
-                        mustMatchToken(Token.COLON, "msg.no.colon.case");
+                        mustMatchToken(Token.COLON, "msg.no.colon.case", true);
                         break;
 
                     case Token.DEFAULT:
@@ -1336,10 +1353,12 @@
                             reportError("msg.double.switch.default");
                         }
                         hasDefault = true;
-                        caseExpression = null;
-                        mustMatchToken(Token.COLON, "msg.no.colon.case");
+                        mustMatchToken(Token.COLON, "msg.no.colon.case", true);
                         break;
-
+                    case Token.COMMENT:
+                        AstNode n = scannedComments.get(scannedComments.size() - 1);
+                        pn.addChild(n);
+                        continue switchLoop;
                     default:
                         reportError("msg.bad.switch");
                         break switchLoop;
@@ -1347,15 +1366,26 @@
 
                 SwitchCase caseNode = new SwitchCase(casePos);
                 caseNode.setExpression(caseExpression);
-                caseNode.setLength(ts.tokenEnd - pos);  // include colon
+                caseNode.setLength(ts.tokenEnd - pos); // include colon
                 caseNode.setLineno(caseLineno);
 
                 while ((tt = peekToken()) != Token.RC
-                       && tt != Token.CASE
-                       && tt != Token.DEFAULT
-                       && tt != Token.EOF)
-                {
-                    caseNode.addStatement(statement());  // updates length
+                        && tt != Token.CASE
+                        && tt != Token.DEFAULT
+                        && tt != Token.EOF) {
+                    if (tt == Token.COMMENT) {
+                        Comment inlineComment = scannedComments.get(scannedComments.size() - 1);
+                        if (caseNode.getInlineComment() == null
+                                && inlineComment.getLineno() == caseNode.getLineno()) {
+                            caseNode.setInlineComment(inlineComment);
+                        } else {
+                            caseNode.addStatement(inlineComment);
+                        }
+                        consumeToken();
+                        continue;
+                    }
+                    AstNode nextStmt = statement();
+                    caseNode.addStatement(nextStmt); // updates length
                 }
                 pn.addCase(caseNode);
             }
@@ -1365,9 +1395,7 @@
         return pn;
     }
 
-    private WhileLoop whileLoop()
-        throws IOException
-    {
+    private WhileLoop whileLoop() throws IOException {
         if (currentToken != Token.WHILE) codeBug();
         consumeToken();
         int pos = ts.tokenBeg;
@@ -1378,8 +1406,9 @@
             ConditionData data = condition();
             pn.setCondition(data.condition);
             pn.setParens(data.lp - pos, data.rp - pos);
-            AstNode body = statement();
+            AstNode body = getNextStatementAfterInlineComments(pn);
             pn.setLength(getNodeEnd(body) - pos);
+            restoreRelativeLoopPosition(pn);
             pn.setBody(body);
         } finally {
             exitLoop();
@@ -1387,9 +1416,7 @@
         return pn;
     }
 
-    private DoLoop doLoop()
-        throws IOException
-    {
+    private DoLoop doLoop() throws IOException {
         if (currentToken != Token.DO) codeBug();
         consumeToken();
         int pos = ts.tokenBeg, end;
@@ -1397,13 +1424,14 @@
         pn.setLineno(ts.lineno);
         enterLoop(pn);
         try {
-            AstNode body = statement();
-            mustMatchToken(Token.WHILE, "msg.no.while.do");
+            AstNode body = getNextStatementAfterInlineComments(pn);
+            mustMatchToken(Token.WHILE, "msg.no.while.do", true);
             pn.setWhilePosition(ts.tokenBeg - pos);
             ConditionData data = condition();
             pn.setCondition(data.condition);
             pn.setParens(data.lp - pos, data.rp - pos);
             end = getNodeEnd(body);
+            restoreRelativeLoopPosition(pn);
             pn.setBody(body);
         } finally {
             exitLoop();
@@ -1411,31 +1439,51 @@
         // Always auto-insert semicolon to follow SpiderMonkey:
         // It is required by ECMAScript but is ignored by the rest of
         // world, see bug 238945
-        if (matchToken(Token.SEMI)) {
+        if (matchToken(Token.SEMI, true)) {
             end = ts.tokenEnd;
         }
         pn.setLength(end - pos);
         return pn;
     }
 
-    private Loop forLoop()
-        throws IOException
-    {
+    private int peekUntilNonComment(int tt) throws IOException {
+        while (tt == Token.COMMENT) {
+            consumeToken();
+            tt = peekToken();
+        }
+        return tt;
+    }
+
+    private AstNode getNextStatementAfterInlineComments(AstNode pn) throws IOException {
+        AstNode body = statement();
+        if (Token.COMMENT == body.getType()) {
+            AstNode commentNode = body;
+            body = statement();
+            if (pn != null) {
+                pn.setInlineComment(commentNode);
+            } else {
+                body.setInlineComment(commentNode);
+            }
+        }
+        return body;
+    }
+
+    private Loop forLoop() throws IOException {
         if (currentToken != Token.FOR) codeBug();
         consumeToken();
         int forPos = ts.tokenBeg, lineno = ts.lineno;
         boolean isForEach = false, isForIn = false, isForOf = false;
         int eachPos = -1, inPos = -1, lp = -1, rp = -1;
-        AstNode init = null;  // init is also foo in 'foo in object'
-        AstNode cond = null;  // cond is also object in 'foo in object'
+        AstNode init = null; // init is also foo in 'foo in object'
+        AstNode cond = null; // cond is also object in 'foo in object'
         AstNode incr = null;
         Loop pn = null;
 
         Scope tempScope = new Scope();
-        pushScope(tempScope);  // decide below what AST class to use
+        pushScope(tempScope); // decide below what AST class to use
         try {
             // See if this is a for each () instead of just a for ()
-            if (matchToken(Token.NAME)) {
+            if (matchToken(Token.NAME, true)) {
                 if ("each".equals(ts.getString())) {
                     isForEach = true;
                     eachPos = ts.tokenBeg - forPos;
@@ -1444,22 +1492,24 @@
                 }
             }
 
-            if (mustMatchToken(Token.LP, "msg.no.paren.for"))
-                lp = ts.tokenBeg - forPos;
+            if (mustMatchToken(Token.LP, "msg.no.paren.for", true)) lp = ts.tokenBeg - forPos;
             int tt = peekToken();
 
             init = forLoopInit(tt);
-            if (matchToken(Token.IN)) {
+            if (matchToken(Token.IN, true)) {
                 isForIn = true;
                 inPos = ts.tokenBeg - forPos;
-                cond = expr();  // object over which we're iterating
-            } else if (compilerEnv.getLanguageVersion() >= Context.VERSION_ES6 &&
-                       matchToken(Token.NAME) && "of".equals(ts.getString())) {
+                markDestructuring(init);
+                cond = expr(); // object over which we're iterating
+            } else if (compilerEnv.getLanguageVersion() >= Context.VERSION_ES6
+                    && matchToken(Token.NAME, true)
+                    && "of".equals(ts.getString())) {
                 isForOf = true;
                 inPos = ts.tokenBeg - forPos;
-                cond = expr();  // object over which we're iterating
-            } else {  // ordinary for-loop
-                mustMatchToken(Token.SEMI, "msg.no.semi.for");
+                markDestructuring(init);
+                cond = expr(); // object over which we're iterating
+            } else { // ordinary for-loop
+                mustMatchToken(Token.SEMI, "msg.no.semi.for", true);
                 if (peekToken() == Token.SEMI) {
                     // no loop condition
                     cond = new EmptyExpression(ts.tokenBeg, 1);
@@ -1468,7 +1518,7 @@
                     cond = expr();
                 }
 
-                mustMatchToken(Token.SEMI, "msg.no.semi.for.cond");
+                mustMatchToken(Token.SEMI, "msg.no.semi.for.cond", true);
                 int tmpPos = ts.tokenEnd;
                 if (peekToken() == Token.RP) {
                     incr = new EmptyExpression(tmpPos, 1);
@@ -1478,14 +1528,13 @@
                 }
             }
 
-            if (mustMatchToken(Token.RP, "msg.no.paren.for.ctrl"))
-                rp = ts.tokenBeg - forPos;
+            if (mustMatchToken(Token.RP, "msg.no.paren.for.ctrl", true)) rp = ts.tokenBeg - forPos;
 
             if (isForIn || isForOf) {
                 ForInLoop fis = new ForInLoop(forPos);
                 if (init instanceof VariableDeclaration) {
                     // check that there was only one variable given
-                    if (((VariableDeclaration)init).getVariables().size() > 1) {
+                    if (((VariableDeclaration) init).getVariables().size() > 1) {
                         reportError("msg.mult.index");
                     }
                 }
@@ -1516,8 +1565,9 @@
             // break/continue statements to find the enclosing loop.
             enterLoop(pn);
             try {
-                AstNode body = statement();
+                AstNode body = getNextStatementAfterInlineComments(pn);
                 pn.setLength(getNodeEnd(body) - forPos);
+                restoreRelativeLoopPosition(pn);
                 pn.setBody(body);
             } finally {
                 exitLoop();
@@ -1535,7 +1585,7 @@
 
     private AstNode forLoopInit(int tt) throws IOException {
         try {
-            inForInit = true;  // checked by variables() and relExpr()
+            inForInit = true; // checked by variables() and relExpr()
             AstNode init = null;
             if (tt == Token.SEMI) {
                 init = new EmptyExpression(ts.tokenBeg, 1);
@@ -1545,7 +1595,6 @@
                 init = variables(tt, ts.tokenBeg, false);
             } else {
                 init = expr();
-                markDestructuring(init);
             }
             return init;
         } finally {
@@ -1553,9 +1602,7 @@
         }
     }
 
-    private TryStatement tryStatement()
-        throws IOException
-    {
+    private TryStatement tryStatement() throws IOException {
         if (currentToken != Token.TRY) codeBug();
         consumeToken();
 
@@ -1563,50 +1610,67 @@
         Comment jsdocNode = getAndResetJsDoc();
 
         int tryPos = ts.tokenBeg, lineno = ts.lineno, finallyPos = -1;
-        if (peekToken() != Token.LC) {
+
+        TryStatement pn = new TryStatement(tryPos);
+        // Hnadled comment here because there should not be try without LC
+        int lctt = peekToken();
+        while (lctt == Token.COMMENT) {
+            Comment commentNode = scannedComments.get(scannedComments.size() - 1);
+            pn.setInlineComment(commentNode);
+            consumeToken();
+            lctt = peekToken();
+        }
+        if (lctt != Token.LC) {
             reportError("msg.no.brace.try");
         }
-        AstNode tryBlock = statement();
+        AstNode tryBlock = getNextStatementAfterInlineComments(pn);
         int tryEnd = getNodeEnd(tryBlock);
 
         List<CatchClause> clauses = null;
 
         boolean sawDefaultCatch = false;
         int peek = peekToken();
+        while (peek == Token.COMMENT) {
+            Comment commentNode = scannedComments.get(scannedComments.size() - 1);
+            pn.setInlineComment(commentNode);
+            consumeToken();
+            peek = peekToken();
+        }
         if (peek == Token.CATCH) {
-            while (matchToken(Token.CATCH)) {
+            while (matchToken(Token.CATCH, true)) {
                 int catchLineNum = ts.lineno;
                 if (sawDefaultCatch) {
                     reportError("msg.catch.unreachable");
                 }
                 int catchPos = ts.tokenBeg, lp = -1, rp = -1, guardPos = -1;
-                if (mustMatchToken(Token.LP, "msg.no.paren.catch"))
-                    lp = ts.tokenBeg;
+                if (mustMatchToken(Token.LP, "msg.no.paren.catch", true)) lp = ts.tokenBeg;
+
+                mustMatchToken(Token.NAME, "msg.bad.catchcond", true);
 
-                mustMatchToken(Token.NAME, "msg.bad.catchcond");
                 Name varName = createNameNode();
+                Comment jsdocNodeForName = getAndResetJsDoc();
+                if (jsdocNodeForName != null) {
+                    varName.setJsDocNode(jsdocNodeForName);
+                }
                 String varNameString = varName.getIdentifier();
                 if (inUseStrictDirective) {
-                    if ("eval".equals(varNameString) ||
-                        "arguments".equals(varNameString))
-                    {
+                    if ("eval".equals(varNameString) || "arguments".equals(varNameString)) {
                         reportError("msg.bad.id.strict", varNameString);
                     }
                 }
 
                 AstNode catchCond = null;
-                if (matchToken(Token.IF)) {
+                if (matchToken(Token.IF, true)) {
                     guardPos = ts.tokenBeg;
                     catchCond = expr();
                 } else {
                     sawDefaultCatch = true;
                 }
 
-                if (mustMatchToken(Token.RP, "msg.bad.catchcond"))
-                    rp = ts.tokenBeg;
-                mustMatchToken(Token.LC, "msg.no.brace.catchblock");
+                if (mustMatchToken(Token.RP, "msg.bad.catchcond", true)) rp = ts.tokenBeg;
+                mustMatchToken(Token.LC, "msg.no.brace.catchblock", true);
 
-                Block catchBlock = (Block)statements();
+                Block catchBlock = (Block) statements();
                 tryEnd = getNodeEnd(catchBlock);
                 CatchClause catchNode = new CatchClause(catchPos);
                 catchNode.setVarName(varName);
@@ -1618,25 +1682,23 @@
                 catchNode.setParens(lp, rp);
                 catchNode.setLineno(catchLineNum);
 
-                if (mustMatchToken(Token.RC, "msg.no.brace.after.body"))
-                    tryEnd = ts.tokenEnd;
+                if (mustMatchToken(Token.RC, "msg.no.brace.after.body", true)) tryEnd = ts.tokenEnd;
                 catchNode.setLength(tryEnd - catchPos);
-                if (clauses == null)
-                    clauses = new ArrayList<CatchClause>();
+                if (clauses == null) clauses = new ArrayList<CatchClause>();
                 clauses.add(catchNode);
             }
         } else if (peek != Token.FINALLY) {
-            mustMatchToken(Token.FINALLY, "msg.try.no.catchfinally");
+            mustMatchToken(Token.FINALLY, "msg.try.no.catchfinally", true);
         }
 
         AstNode finallyBlock = null;
-        if (matchToken(Token.FINALLY)) {
+        if (matchToken(Token.FINALLY, true)) {
             finallyPos = ts.tokenBeg;
             finallyBlock = statement();
             tryEnd = getNodeEnd(finallyBlock);
         }
 
-        TryStatement pn = new TryStatement(tryPos, tryEnd - tryPos);
+        pn.setLength(tryEnd - tryPos);
         pn.setTryBlock(tryBlock);
         pn.setCatchClauses(clauses);
         pn.setFinallyBlock(finallyBlock);
@@ -1652,9 +1714,7 @@
         return pn;
     }
 
-    private ThrowStatement throwStatement()
-        throws IOException
-    {
+    private ThrowStatement throwStatement() throws IOException {
         if (currentToken != Token.THROW) codeBug();
         consumeToken();
         int pos = ts.tokenBeg, lineno = ts.lineno;
@@ -1664,7 +1724,7 @@
             reportError("msg.bad.throw.eol");
         }
         AstNode expr = expr();
-        ThrowStatement pn = new ThrowStatement(pos, getNodeEnd(expr), expr);
+        ThrowStatement pn = new ThrowStatement(pos, expr);
         pn.setLineno(lineno);
         return pn;
     }
@@ -1675,9 +1735,7 @@
     // the peeked token was not a name.  Side effect:  sets scanner token
     // information for the label identifier (tokenBeg, tokenEnd, etc.)
 
-    private LabeledStatement matchJumpLabelName()
-        throws IOException
-    {
+    private LabeledStatement matchJumpLabelName() throws IOException {
         LabeledStatement label = null;
 
         if (peekTokenOrEOL() == Token.NAME) {
@@ -1693,9 +1751,7 @@
         return label;
     }
 
-    private BreakStatement breakStatement()
-        throws IOException
-    {
+    private BreakStatement breakStatement() throws IOException {
         if (currentToken != Token.BREAK) codeBug();
         consumeToken();
         int lineno = ts.lineno, pos = ts.tokenBeg, end = ts.tokenEnd;
@@ -1712,9 +1768,7 @@
 
         if (breakTarget == null && breakLabel == null) {
             if (loopAndSwitchSet == null || loopAndSwitchSet.size() == 0) {
-                if (breakLabel == null) {
-                    reportError("msg.bad.break", pos, end - pos);
-                }
+                reportError("msg.bad.break", pos, end - pos);
             } else {
                 breakTarget = loopAndSwitchSet.get(loopAndSwitchSet.size() - 1);
             }
@@ -1723,15 +1777,12 @@
         BreakStatement pn = new BreakStatement(pos, end - pos);
         pn.setBreakLabel(breakLabel);
         // can be null if it's a bad break in error-recovery mode
-        if (breakTarget != null)
-            pn.setBreakTarget(breakTarget);
+        if (breakTarget != null) pn.setBreakTarget(breakTarget);
         pn.setLineno(lineno);
         return pn;
     }
 
-    private ContinueStatement continueStatement()
-        throws IOException
-    {
+    private ContinueStatement continueStatement() throws IOException {
         if (currentToken != Token.CONTINUE) codeBug();
         consumeToken();
         int lineno = ts.lineno, pos = ts.tokenBeg, end = ts.tokenEnd;
@@ -1754,37 +1805,33 @@
             if (labels == null || !(labels.getStatement() instanceof Loop)) {
                 reportError("msg.continue.nonloop", pos, end - pos);
             }
-            target = labels == null ? null : (Loop)labels.getStatement();
+            target = labels == null ? null : (Loop) labels.getStatement();
         }
 
         ContinueStatement pn = new ContinueStatement(pos, end - pos);
-        if (target != null)  // can be null in error-recovery mode
-            pn.setTarget(target);
+        if (target != null) // can be null in error-recovery mode
+        pn.setTarget(target);
         pn.setLabel(label);
         pn.setLineno(lineno);
         return pn;
     }
 
-    private WithStatement withStatement()
-        throws IOException
-    {
+    private WithStatement withStatement() throws IOException {
         if (currentToken != Token.WITH) codeBug();
         consumeToken();
 
         Comment withComment = getAndResetJsDoc();
 
         int lineno = ts.lineno, pos = ts.tokenBeg, lp = -1, rp = -1;
-        if (mustMatchToken(Token.LP, "msg.no.paren.with"))
-            lp = ts.tokenBeg;
+        if (mustMatchToken(Token.LP, "msg.no.paren.with", true)) lp = ts.tokenBeg;
 
         AstNode obj = expr();
 
-        if (mustMatchToken(Token.RP, "msg.no.paren.after.with"))
-            rp = ts.tokenBeg;
+        if (mustMatchToken(Token.RP, "msg.no.paren.after.with", true)) rp = ts.tokenBeg;
 
-        AstNode body = statement();
-
-        WithStatement pn = new WithStatement(pos, getNodeEnd(body) - pos);
+        WithStatement pn = new WithStatement(pos);
+        AstNode body = getNextStatementAfterInlineComments(pn);
+        pn.setLength(getNodeEnd(body) - pos);
         pn.setJsDocNode(withComment);
         pn.setExpression(obj);
         pn.setStatement(body);
@@ -1793,9 +1840,7 @@
         return pn;
     }
 
-    private AstNode letStatement()
-        throws IOException
-    {
+    private AstNode letStatement() throws IOException {
         if (currentToken != Token.LET) codeBug();
         consumeToken();
         int lineno = ts.lineno, pos = ts.tokenBeg;
@@ -1803,7 +1848,7 @@
         if (peekToken() == Token.LP) {
             pn = let(true, pos);
         } else {
-            pn = variables(Token.LET, pos, true);  // else, e.g.: let x=6, y=7;
+            pn = variables(Token.LET, pos, true); // else, e.g.: let x=6, y=7;
         }
         pn.setLineno(lineno);
         return pn;
@@ -1811,35 +1856,51 @@
 
     /**
      * Returns whether or not the bits in the mask have changed to all set.
+     *
      * @param before bits before change
      * @param after bits after change
      * @param mask mask for bits
-     * @return {@code true} if all the bits in the mask are set in "after"
-     *          but not in "before"
+     * @return {@code true} if all the bits in the mask are set in "after" but not in "before"
      */
     private static final boolean nowAllSet(int before, int after, int mask) {
         return ((before & mask) != mask) && ((after & mask) == mask);
     }
 
-    private AstNode returnOrYield(int tt, boolean exprContext)
-        throws IOException
-    {
+    private AstNode returnOrYield(int tt, boolean exprContext) throws IOException {
         if (!insideFunction()) {
-            reportError(tt == Token.RETURN ? "msg.bad.return"
-                                           : "msg.bad.yield");
+            reportError(tt == Token.RETURN ? "msg.bad.return" : "msg.bad.yield");
         }
         consumeToken();
         int lineno = ts.lineno, pos = ts.tokenBeg, end = ts.tokenEnd;
 
+        boolean yieldStar = false;
+        if ((tt == Token.YIELD)
+                && (compilerEnv.getLanguageVersion() >= Context.VERSION_ES6)
+                && (peekToken() == Token.MUL)) {
+            yieldStar = true;
+            consumeToken();
+        }
+
         AstNode e = null;
         // This is ugly, but we don't want to require a semicolon.
         switch (peekTokenOrEOL()) {
-          case Token.SEMI: case Token.RC:  case Token.RB:    case Token.RP:
-          case Token.EOF:  case Token.EOL: case Token.ERROR: case Token.YIELD:
-            break;
-          default:
-            e = expr();
-            end = getNodeEnd(e);
+            case Token.SEMI:
+            case Token.RC:
+            case Token.RB:
+            case Token.RP:
+            case Token.EOF:
+            case Token.EOL:
+            case Token.ERROR:
+                break;
+            case Token.YIELD:
+                if (compilerEnv.getLanguageVersion() < Context.VERSION_ES6) {
+                    // Take extra care to preserve language compatibility
+                    break;
+                }
+                // fallthrough
+            default:
+                e = expr();
+                end = getNodeEnd(e);
         }
 
         int before = endFlags;
@@ -1850,14 +1911,12 @@
             ret = new ReturnStatement(pos, end - pos, e);
 
             // see if we need a strict mode warning
-            if (nowAllSet(before, endFlags,
-                    Node.END_RETURNS|Node.END_RETURNS_VALUE))
+            if (nowAllSet(before, endFlags, Node.END_RETURNS | Node.END_RETURNS_VALUE))
                 addStrictWarning("msg.return.inconsistent", "", pos, end - pos);
         } else {
-            if (!insideFunction())
-                reportError("msg.bad.yield");
+            if (!insideFunction()) reportError("msg.bad.yield");
             endFlags |= Node.END_YIELDS;
-            ret = new Yield(pos, end - pos, e);
+            ret = new Yield(pos, end - pos, e, yieldStar);
             setRequiresActivation();
             setIsGenerator();
             if (!exprContext) {
@@ -1867,22 +1926,23 @@
 
         // see if we are mixing yields and value returns.
         if (insideFunction()
-            && nowAllSet(before, endFlags,
-                    Node.END_YIELDS|Node.END_RETURNS_VALUE)) {
-            Name name = ((FunctionNode)currentScriptOrFn).getFunctionName();
-            if (name == null || name.length() == 0)
-                addError("msg.anon.generator.returns", "");
-            else
-                addError("msg.generator.returns", name.getIdentifier());
+                && nowAllSet(before, endFlags, Node.END_YIELDS | Node.END_RETURNS_VALUE)) {
+            FunctionNode fn = (FunctionNode) currentScriptOrFn;
+            if (!fn.isES6Generator()) {
+                Name name = ((FunctionNode) currentScriptOrFn).getFunctionName();
+                if (name == null || name.length() == 0) {
+                    addError("msg.anon.generator.returns", "");
+                } else {
+                    addError("msg.generator.returns", name.getIdentifier());
+                }
+            }
         }
 
         ret.setLineno(lineno);
         return ret;
     }
 
-    private AstNode block()
-        throws IOException
-    {
+    private AstNode block() throws IOException {
         if (currentToken != Token.LC) codeBug();
         consumeToken();
         int pos = ts.tokenBeg;
@@ -1891,7 +1951,7 @@
         pushScope(block);
         try {
             statements(block);
-            mustMatchToken(Token.RC, "msg.no.brace.block");
+            mustMatchToken(Token.RC, "msg.no.brace.block", true);
             block.setLength(ts.tokenEnd - pos);
             return block;
         } finally {
@@ -1899,22 +1959,20 @@
         }
     }
 
-    private AstNode defaultXmlNamespace()
-        throws IOException
-    {
+    private AstNode defaultXmlNamespace() throws IOException {
         if (currentToken != Token.DEFAULT) codeBug();
         consumeToken();
         mustHaveXML();
         setRequiresActivation();
         int lineno = ts.lineno, pos = ts.tokenBeg;
 
-        if (!(matchToken(Token.NAME) && "xml".equals(ts.getString()))) {
+        if (!(matchToken(Token.NAME, true) && "xml".equals(ts.getString()))) {
             reportError("msg.bad.namespace");
         }
-        if (!(matchToken(Token.NAME) && "namespace".equals(ts.getString()))) {
+        if (!(matchToken(Token.NAME, true) && "namespace".equals(ts.getString()))) {
             reportError("msg.bad.namespace");
         }
-        if (!matchToken(Token.ASSIGN)) {
+        if (!matchToken(Token.ASSIGN, true)) {
             reportError("msg.bad.namespace");
         }
 
@@ -1928,25 +1986,21 @@
         return es;
     }
 
-    private void recordLabel(Label label, LabeledStatement bundle)
-        throws IOException
-    {
+    private void recordLabel(Label label, LabeledStatement bundle) throws IOException {
         // current token should be colon that primaryExpr left untouched
         if (peekToken() != Token.COLON) codeBug();
         consumeToken();
         String name = label.getName();
         if (labelSet == null) {
-            labelSet = new HashMap<String,LabeledStatement>();
+            labelSet = new HashMap<String, LabeledStatement>();
         } else {
             LabeledStatement ls = labelSet.get(name);
             if (ls != null) {
                 if (compilerEnv.isIdeMode()) {
                     Label dup = ls.getLabelByName(name);
-                    reportError("msg.dup.label",
-                                dup.getAbsolutePosition(), dup.getLength());
+                    reportError("msg.dup.label", dup.getAbsolutePosition(), dup.getLength());
                 }
-                reportError("msg.dup.label",
-                            label.getPosition(), label.getLength());
+                reportError("msg.dup.label", label.getPosition(), label.getLength());
             }
         }
         bundle.addLabel(label);
@@ -1954,14 +2008,11 @@
     }
 
     /**
-     * Found a name in a statement context.  If it's a label, we gather
-     * up any following labels and the next non-label statement into a
-     * {@link LabeledStatement} "bundle" and return that.  Otherwise we parse
-     * an expression and return it wrapped in an {@link ExpressionStatement}.
+     * Found a name in a statement context. If it's a label, we gather up any following labels and
+     * the next non-label statement into a {@link LabeledStatement} "bundle" and return that.
+     * Otherwise we parse an expression and return it wrapped in an {@link ExpressionStatement}.
      */
-    private AstNode nameOrLabel()
-        throws IOException
-    {
+    private AstNode nameOrLabel() throws IOException {
         if (currentToken != Token.NAME) throw codeBug();
         int pos = ts.tokenBeg;
 
@@ -1976,7 +2027,7 @@
         }
 
         LabeledStatement bundle = new LabeledStatement(pos);
-        recordLabel((Label)expr, bundle);
+        recordLabel((Label) expr, bundle);
         bundle.setLineno(ts.lineno);
         // look for more labels
         AstNode stmt = null;
@@ -1988,7 +2039,7 @@
                 autoInsertSemicolon(stmt);
                 break;
             }
-            recordLabel((Label)expr, bundle);
+            recordLabel((Label) expr, bundle);
         }
 
         // no more labels; now parse the labeled statement
@@ -1996,6 +2047,13 @@
             currentLabel = bundle;
             if (stmt == null) {
                 stmt = statementHelper();
+                int ntt = peekToken();
+                if (ntt == Token.COMMENT
+                        && stmt.getLineno()
+                                == scannedComments.get(scannedComments.size() - 1).getLineno()) {
+                    stmt.setInlineComment(scannedComments.get(scannedComments.size() - 1));
+                    consumeToken();
+                }
             }
         } finally {
             currentLabel = null;
@@ -2007,26 +2065,22 @@
 
         // If stmt has parent assigned its position already is relative
         // (See bug #710225)
-        bundle.setLength(stmt.getParent() == null
-                     ? getNodeEnd(stmt) - pos
-                     : getNodeEnd(stmt));
+        bundle.setLength(stmt.getParent() == null ? getNodeEnd(stmt) - pos : getNodeEnd(stmt));
         bundle.setStatement(stmt);
         return bundle;
     }
 
     /**
-     * Parse a 'var' or 'const' statement, or a 'var' init list in a for
-     * statement.
-     * @param declType A token value: either VAR, CONST, or LET depending on
-     * context.
-     * @param pos the position where the node should start.  It's sometimes
-     * the var/const/let keyword, and other times the beginning of the first
-     * token in the first variable declaration.
+     * Parse a 'var' or 'const' statement, or a 'var' init list in a for statement.
+     *
+     * @param declType A token value: either VAR, CONST, or LET depending on context.
+     * @param pos the position where the node should start. It's sometimes the var/const/let
+     *     keyword, and other times the beginning of the first token in the first variable
+     *     declaration.
      * @return the parsed variable list
      */
     private VariableDeclaration variables(int declType, int pos, boolean isStatement)
-        throws IOException
-    {
+            throws IOException {
         int end;
         VariableDeclaration pn = new VariableDeclaration(pos);
         pn.setType(declType);
@@ -2038,7 +2092,7 @@
         // Example:
         // var foo = {a: 1, b: 2}, bar = [3, 4];
         // var {b: s2, a: s1} = foo, x = 6, y, [s3, s4] = bar;
-        for (;;) {
+        for (; ; ) {
             AstNode destructuring = null;
             Name name = null;
             int tt = peekToken(), kidPos = ts.tokenBeg;
@@ -2053,13 +2107,12 @@
                 markDestructuring(destructuring);
             } else {
                 // Simple variable name
-                mustMatchToken(Token.NAME, "msg.bad.var");
+                mustMatchToken(Token.NAME, "msg.bad.var", true);
                 name = createNameNode();
                 name.setLineno(ts.getLineno());
                 if (inUseStrictDirective) {
                     String id = ts.getString();
-                    if ("eval".equals(id) || "arguments".equals(ts.getString()))
-                    {
+                    if ("eval".equals(id) || "arguments".equals(ts.getString())) {
                         reportError("msg.bad.id.strict", id);
                     }
                 }
@@ -2071,7 +2124,7 @@
             Comment jsdocNode = getAndResetJsDoc();
 
             AstNode init = null;
-            if (matchToken(Token.ASSIGN)) {
+            if (matchToken(Token.ASSIGN, true)) {
                 init = assignExpr();
                 end = getNodeEnd(init);
             }
@@ -2091,8 +2144,7 @@
             vi.setLineno(lineno);
             pn.addVariable(vi);
 
-            if (!matchToken(Token.COMMA))
-                break;
+            if (!matchToken(Token.COMMA, true)) break;
         }
         pn.setLength(end - pos);
         pn.setIsStatement(isStatement);
@@ -2100,26 +2152,23 @@
     }
 
     // have to pass in 'let' kwd position to compute kid offsets properly
-    private AstNode let(boolean isStatement, int pos)
-        throws IOException
-    {
+    private AstNode let(boolean isStatement, int pos) throws IOException {
         LetNode pn = new LetNode(pos);
         pn.setLineno(ts.lineno);
-        if (mustMatchToken(Token.LP, "msg.no.paren.after.let"))
-            pn.setLp(ts.tokenBeg - pos);
+        if (mustMatchToken(Token.LP, "msg.no.paren.after.let", true)) pn.setLp(ts.tokenBeg - pos);
         pushScope(pn);
         try {
             VariableDeclaration vars = variables(Token.LET, ts.tokenBeg, isStatement);
             pn.setVariables(vars);
-            if (mustMatchToken(Token.RP, "msg.no.paren.let")) {
+            if (mustMatchToken(Token.RP, "msg.no.paren.let", true)) {
                 pn.setRp(ts.tokenBeg - pos);
             }
             if (isStatement && peekToken() == Token.LC) {
                 // let statement
                 consumeToken();
-                int beg = ts.tokenBeg;  // position stmt at LC
+                int beg = ts.tokenBeg; // position stmt at LC
                 AstNode stmt = statements();
-                mustMatchToken(Token.RC, "msg.no.curly.let");
+                mustMatchToken(Token.RC, "msg.no.curly.let", true);
                 stmt.setLength(ts.tokenEnd - beg);
                 pn.setLength(ts.tokenEnd - pos);
                 pn.setBody(stmt);
@@ -2131,8 +2180,7 @@
                 pn.setBody(expr);
                 if (isStatement) {
                     // let expression in statement context
-                    ExpressionStatement es =
-                            new ExpressionStatement(pn, !insideFunction());
+                    ExpressionStatement es = new ExpressionStatement(pn, !insideFunction());
                     es.setLineno(pn.getLineno());
                     return es;
                 }
@@ -2149,88 +2197,82 @@
 
     void defineSymbol(int declType, String name, boolean ignoreNotInBlock) {
         if (name == null) {
-            if (compilerEnv.isIdeMode()) {  // be robust in IDE-mode
+            if (compilerEnv.isIdeMode()) { // be robust in IDE-mode
                 return;
-            } else {
-                codeBug();
             }
+            codeBug();
         }
         Scope definingScope = currentScope.getDefiningScope(name);
-        Symbol symbol = definingScope != null
-                        ? definingScope.getSymbol(name)
-                        : null;
+        Symbol symbol = definingScope != null ? definingScope.getSymbol(name) : null;
         int symDeclType = symbol != null ? symbol.getDeclType() : -1;
         if (symbol != null
-            && (symDeclType == Token.CONST
-                || declType == Token.CONST
-                || (definingScope == currentScope && symDeclType == Token.LET)))
-        {
-            addError(symDeclType == Token.CONST ? "msg.const.redecl" :
-                     symDeclType == Token.LET ? "msg.let.redecl" :
-                     symDeclType == Token.VAR ? "msg.var.redecl" :
-                     symDeclType == Token.FUNCTION ? "msg.fn.redecl" :
-                     "msg.parm.redecl", name);
+                && (symDeclType == Token.CONST
+                        || declType == Token.CONST
+                        || (definingScope == currentScope && symDeclType == Token.LET))) {
+            addError(
+                    symDeclType == Token.CONST
+                            ? "msg.const.redecl"
+                            : symDeclType == Token.LET
+                                    ? "msg.let.redecl"
+                                    : symDeclType == Token.VAR
+                                            ? "msg.var.redecl"
+                                            : symDeclType == Token.FUNCTION
+                                                    ? "msg.fn.redecl"
+                                                    : "msg.parm.redecl",
+                    name);
             return;
         }
         switch (declType) {
-          case Token.LET:
-              if (!ignoreNotInBlock &&
-                  ((currentScope.getType() == Token.IF) ||
-                   currentScope instanceof Loop)) {
-                  addError("msg.let.decl.not.in.block");
-                  return;
-              }
-              currentScope.putSymbol(new Symbol(declType, name));
-              return;
-
-          case Token.VAR:
-          case Token.CONST:
-          case Token.FUNCTION:
-              if (symbol != null) {
-                  if (symDeclType == Token.VAR)
-                      addStrictWarning("msg.var.redecl", name);
-                  else if (symDeclType == Token.LP) {
-                      addStrictWarning("msg.var.hides.arg", name);
-                  }
-              } else {
-                  currentScriptOrFn.putSymbol(new Symbol(declType, name));
-              }
-              return;
-
-          case Token.LP:
-              if (symbol != null) {
-                  // must be duplicate parameter. Second parameter hides the
-                  // first, so go ahead and add the second parameter
-                  addWarning("msg.dup.parms", name);
-              }
-              currentScriptOrFn.putSymbol(new Symbol(declType, name));
-              return;
-
-          default:
-              throw codeBug();
+            case Token.LET:
+                if (!ignoreNotInBlock
+                        && ((currentScope.getType() == Token.IF) || currentScope instanceof Loop)) {
+                    addError("msg.let.decl.not.in.block");
+                    return;
+                }
+                currentScope.putSymbol(new Symbol(declType, name));
+                return;
+
+            case Token.VAR:
+            case Token.CONST:
+            case Token.FUNCTION:
+                if (symbol != null) {
+                    if (symDeclType == Token.VAR) addStrictWarning("msg.var.redecl", name);
+                    else if (symDeclType == Token.LP) {
+                        addStrictWarning("msg.var.hides.arg", name);
+                    }
+                } else {
+                    currentScriptOrFn.putSymbol(new Symbol(declType, name));
+                }
+                return;
+
+            case Token.LP:
+                if (symbol != null) {
+                    // must be duplicate parameter. Second parameter hides the
+                    // first, so go ahead and add the second parameter
+                    addWarning("msg.dup.parms", name);
+                }
+                currentScriptOrFn.putSymbol(new Symbol(declType, name));
+                return;
+
+            default:
+                throw codeBug();
         }
     }
 
-    private AstNode expr()
-        throws IOException
-    {
+    private AstNode expr() throws IOException {
         AstNode pn = assignExpr();
         int pos = pn.getPosition();
-        while (matchToken(Token.COMMA)) {
+        while (matchToken(Token.COMMA, true)) {
             int opPos = ts.tokenBeg;
             if (compilerEnv.isStrictMode() && !pn.hasSideEffects())
-                addStrictWarning("msg.no.side.effects", "",
-                                 pos, nodeEnd(pn) - pos);
-            if (peekToken() == Token.YIELD)
-                reportError("msg.yield.parenthesized");
+                addStrictWarning("msg.no.side.effects", "", pos, nodeEnd(pn) - pos);
+            if (peekToken() == Token.YIELD) reportError("msg.yield.parenthesized");
             pn = new InfixExpression(Token.COMMA, pn, assignExpr(), opPos);
         }
         return pn;
     }
 
-    private AstNode assignExpr()
-        throws IOException
-    {
+    private AstNode assignExpr() throws IOException {
         int tt = peekToken();
         if (tt == Token.YIELD) {
             return returnOrYield(tt, true);
@@ -2243,6 +2285,13 @@
             tt = peekToken();
         }
         if (Token.FIRST_ASSIGN <= tt && tt <= Token.LAST_ASSIGN) {
+            if (inDestructuringAssignment) {
+                // default values inside destructuring assignments,
+                // like 'var [a = 10] = b' or 'var {a: b = 10} = c',
+                // are not supported
+                reportError("msg.destruct.default.vals");
+            }
+
             consumeToken();
 
             // Pull out JSDoc info and reset it before recursing.
@@ -2269,11 +2318,9 @@
         return pn;
     }
 
-    private AstNode condExpr()
-        throws IOException
-    {
+    private AstNode condExpr() throws IOException {
         AstNode pn = orExpr();
-        if (matchToken(Token.HOOK)) {
+        if (matchToken(Token.HOOK, true)) {
             int line = ts.lineno;
             int qmarkPos = ts.tokenBeg, colonPos = -1;
             /*
@@ -2289,8 +2336,7 @@
             } finally {
                 inForInit = wasInForInit;
             }
-            if (mustMatchToken(Token.COLON, "msg.no.colon.cond"))
-                colonPos = ts.tokenBeg;
+            if (mustMatchToken(Token.COLON, "msg.no.colon.cond", true)) colonPos = ts.tokenBeg;
             AstNode ifFalse = assignExpr();
             int beg = pn.getPosition(), len = getNodeEnd(ifFalse) - beg;
             ConditionalExpression ce = new ConditionalExpression(beg, len);
@@ -2305,138 +2351,117 @@
         return pn;
     }
 
-    private AstNode orExpr()
-        throws IOException
-    {
+    private AstNode orExpr() throws IOException {
         AstNode pn = andExpr();
-        if (matchToken(Token.OR)) {
+        if (matchToken(Token.OR, true)) {
             int opPos = ts.tokenBeg;
             pn = new InfixExpression(Token.OR, pn, orExpr(), opPos);
         }
         return pn;
     }
 
-    private AstNode andExpr()
-        throws IOException
-    {
+    private AstNode andExpr() throws IOException {
         AstNode pn = bitOrExpr();
-        if (matchToken(Token.AND)) {
+        if (matchToken(Token.AND, true)) {
             int opPos = ts.tokenBeg;
             pn = new InfixExpression(Token.AND, pn, andExpr(), opPos);
         }
         return pn;
     }
 
-    private AstNode bitOrExpr()
-        throws IOException
-    {
+    private AstNode bitOrExpr() throws IOException {
         AstNode pn = bitXorExpr();
-        while (matchToken(Token.BITOR)) {
+        while (matchToken(Token.BITOR, true)) {
             int opPos = ts.tokenBeg;
             pn = new InfixExpression(Token.BITOR, pn, bitXorExpr(), opPos);
         }
         return pn;
     }
 
-    private AstNode bitXorExpr()
-        throws IOException
-    {
+    private AstNode bitXorExpr() throws IOException {
         AstNode pn = bitAndExpr();
-        while (matchToken(Token.BITXOR)) {
+        while (matchToken(Token.BITXOR, true)) {
             int opPos = ts.tokenBeg;
             pn = new InfixExpression(Token.BITXOR, pn, bitAndExpr(), opPos);
         }
         return pn;
     }
 
-    private AstNode bitAndExpr()
-        throws IOException
-    {
+    private AstNode bitAndExpr() throws IOException {
         AstNode pn = eqExpr();
-        while (matchToken(Token.BITAND)) {
+        while (matchToken(Token.BITAND, true)) {
             int opPos = ts.tokenBeg;
             pn = new InfixExpression(Token.BITAND, pn, eqExpr(), opPos);
         }
         return pn;
     }
 
-    private AstNode eqExpr()
-        throws IOException
-    {
+    private AstNode eqExpr() throws IOException {
         AstNode pn = relExpr();
-        for (;;) {
+        for (; ; ) {
             int tt = peekToken(), opPos = ts.tokenBeg;
             switch (tt) {
-              case Token.EQ:
-              case Token.NE:
-              case Token.SHEQ:
-              case Token.SHNE:
-                consumeToken();
-                int parseToken = tt;
-                if (compilerEnv.getLanguageVersion() == Context.VERSION_1_2) {
-                    // JavaScript 1.2 uses shallow equality for == and != .
-                    if (tt == Token.EQ)
-                        parseToken = Token.SHEQ;
-                    else if (tt == Token.NE)
-                        parseToken = Token.SHNE;
-                }
-                pn = new InfixExpression(parseToken, pn, relExpr(), opPos);
-                continue;
+                case Token.EQ:
+                case Token.NE:
+                case Token.SHEQ:
+                case Token.SHNE:
+                    consumeToken();
+                    int parseToken = tt;
+                    if (compilerEnv.getLanguageVersion() == Context.VERSION_1_2) {
+                        // JavaScript 1.2 uses shallow equality for == and != .
+                        if (tt == Token.EQ) parseToken = Token.SHEQ;
+                        else if (tt == Token.NE) parseToken = Token.SHNE;
+                    }
+                    pn = new InfixExpression(parseToken, pn, relExpr(), opPos);
+                    continue;
             }
             break;
         }
         return pn;
     }
 
-    private AstNode relExpr()
-        throws IOException
-    {
+    private AstNode relExpr() throws IOException {
         AstNode pn = shiftExpr();
-        for (;;) {
+        for (; ; ) {
             int tt = peekToken(), opPos = ts.tokenBeg;
             switch (tt) {
-              case Token.IN:
-                if (inForInit)
-                    break;
-                // fall through
-              case Token.INSTANCEOF:
-              case Token.LE:
-              case Token.LT:
-              case Token.GE:
-              case Token.GT:
-                consumeToken();
-                pn = new InfixExpression(tt, pn, shiftExpr(), opPos);
-                continue;
+                case Token.IN:
+                    if (inForInit) break;
+                    // fall through
+                case Token.INSTANCEOF:
+                case Token.LE:
+                case Token.LT:
+                case Token.GE:
+                case Token.GT:
+                    consumeToken();
+                    pn = new InfixExpression(tt, pn, shiftExpr(), opPos);
+                    continue;
             }
             break;
         }
         return pn;
     }
 
-    private AstNode shiftExpr()
-        throws IOException
-    {
+    private AstNode shiftExpr() throws IOException {
         AstNode pn = addExpr();
-        for (;;) {
+        for (; ; ) {
             int tt = peekToken(), opPos = ts.tokenBeg;
             switch (tt) {
-              case Token.LSH:
-              case Token.URSH:
-              case Token.RSH:
-                consumeToken();
-                pn = new InfixExpression(tt, pn, addExpr(), opPos);
-                continue;
+                case Token.LSH:
+                case Token.URSH:
+                case Token.RSH:
+                    consumeToken();
+                    pn = new InfixExpression(tt, pn, addExpr(), opPos);
+                    continue;
             }
             break;
         }
         return pn;
     }
 
-    private AstNode addExpr()
-        throws IOException
-    {
+    private AstNode addExpr() throws IOException {
         AstNode pn = mulExpr();
-        for (;;) {
+        for (; ; ) {
             int tt = peekToken(), opPos = ts.tokenBeg;
             if (tt == Token.ADD || tt == Token.SUB) {
                 consumeToken();
@@ -2448,102 +2473,119 @@
         return pn;
     }
 
-    private AstNode mulExpr()
-        throws IOException
-    {
+    private AstNode mulExpr() throws IOException {
+        AstNode pn = expExpr();
+        for (; ; ) {
+            int tt = peekToken(), opPos = ts.tokenBeg;
+            switch (tt) {
+                case Token.MUL:
+                case Token.DIV:
+                case Token.MOD:
+                    consumeToken();
+                    pn = new InfixExpression(tt, pn, expExpr(), opPos);
+                    continue;
+            }
+            break;
+        }
+        return pn;
+    }
+
+    private AstNode expExpr() throws IOException {
         AstNode pn = unaryExpr();
-        for (;;) {
+        for (; ; ) {
             int tt = peekToken(), opPos = ts.tokenBeg;
             switch (tt) {
-              case Token.MUL:
-              case Token.DIV:
-              case Token.MOD:
-                consumeToken();
-                pn = new InfixExpression(tt, pn, unaryExpr(), opPos);
-                continue;
+                case Token.EXP:
+                    if (pn instanceof UnaryExpression) {
+                        reportError(
+                                "msg.no.unary.expr.on.left.exp",
+                                AstNode.operatorToString(pn.getType()));
+                        return makeErrorNode();
+                    }
+                    consumeToken();
+                    pn = new InfixExpression(tt, pn, expExpr(), opPos);
+                    continue;
             }
             break;
         }
         return pn;
     }
 
-    private AstNode unaryExpr()
-        throws IOException
-    {
+    private AstNode unaryExpr() throws IOException {
         AstNode node;
         int tt = peekToken();
+        if (tt == Token.COMMENT) {
+            consumeToken();
+            tt = peekUntilNonComment(tt);
+        }
         int line = ts.lineno;
 
-        switch(tt) {
-          case Token.VOID:
-          case Token.NOT:
-          case Token.BITNOT:
-          case Token.TYPEOF:
-              consumeToken();
-              node = new UnaryExpression(tt, ts.tokenBeg, unaryExpr());
-              node.setLineno(line);
-              return node;
-
-          case Token.ADD:
-              consumeToken();
-              // Convert to special POS token in parse tree
-              node = new UnaryExpression(Token.POS, ts.tokenBeg, unaryExpr());
-              node.setLineno(line);
-              return node;
-
-          case Token.SUB:
-              consumeToken();
-              // Convert to special NEG token in parse tree
-              node = new UnaryExpression(Token.NEG, ts.tokenBeg, unaryExpr());
-              node.setLineno(line);
-              return node;
-
-          case Token.INC:
-          case Token.DEC:
-              consumeToken();
-              UnaryExpression expr = new UnaryExpression(tt, ts.tokenBeg,
-                                                         memberExpr(true));
-              expr.setLineno(line);
-              checkBadIncDec(expr);
-              return expr;
-
-          case Token.DELPROP:
-              consumeToken();
-              node = new UnaryExpression(tt, ts.tokenBeg, unaryExpr());
-              node.setLineno(line);
-              return node;
-
-          case Token.ERROR:
-              consumeToken();
-              return makeErrorNode();
-
-          case Token.LT:
-              // XML stream encountered in expression.
-              if (compilerEnv.isXmlAvailable()) {
-                  consumeToken();
-                  return memberExprTail(true, xmlInitializer());
-              }
-              // Fall thru to the default handling of RELOP
-
-          default:
-              AstNode pn = memberExpr(true);
-              // Don't look across a newline boundary for a postfix incop.
-              tt = peekTokenOrEOL();
-              if (!(tt == Token.INC || tt == Token.DEC)) {
-                  return pn;
-              }
-              consumeToken();
-              UnaryExpression uexpr =
-                      new UnaryExpression(tt, ts.tokenBeg, pn, true);
-              uexpr.setLineno(line);
-              checkBadIncDec(uexpr);
-              return uexpr;
-        }
-    }
-
-    private AstNode xmlInitializer()
-        throws IOException
-    {
+        switch (tt) {
+            case Token.VOID:
+            case Token.NOT:
+            case Token.BITNOT:
+            case Token.TYPEOF:
+                consumeToken();
+                node = new UnaryExpression(tt, ts.tokenBeg, unaryExpr());
+                node.setLineno(line);
+                return node;
+
+            case Token.ADD:
+                consumeToken();
+                // Convert to special POS token in parse tree
+                node = new UnaryExpression(Token.POS, ts.tokenBeg, unaryExpr());
+                node.setLineno(line);
+                return node;
+
+            case Token.SUB:
+                consumeToken();
+                // Convert to special NEG token in parse tree
+                node = new UnaryExpression(Token.NEG, ts.tokenBeg, unaryExpr());
+                node.setLineno(line);
+                return node;
+
+            case Token.INC:
+            case Token.DEC:
+                consumeToken();
+                UpdateExpression expr = new UpdateExpression(tt, ts.tokenBeg, memberExpr(true));
+                expr.setLineno(line);
+                checkBadIncDec(expr);
+                return expr;
+
+            case Token.DELPROP:
+                consumeToken();
+                node = new UnaryExpression(tt, ts.tokenBeg, unaryExpr());
+                node.setLineno(line);
+                return node;
+
+            case Token.ERROR:
+                consumeToken();
+                return makeErrorNode();
+            case Token.LT:
+                // XML stream encountered in expression.
+                if (compilerEnv.isXmlAvailable()) {
+                    consumeToken();
+                    return memberExprTail(true, xmlInitializer());
+                }
+                // Fall thru to the default handling of RELOP
+                // fall through
+
+            default:
+                AstNode pn = memberExpr(true);
+                // Don't look across a newline boundary for a postfix incop.
+                tt = peekTokenOrEOL();
+                if (!(tt == Token.INC || tt == Token.DEC)) {
+                    return pn;
+                }
+                consumeToken();
+                UpdateExpression uexpr = new UpdateExpression(tt, ts.tokenBeg, pn, true);
+                uexpr.setLineno(line);
+                checkBadIncDec(uexpr);
+                return uexpr;
+        }
+    }
+
+    private AstNode xmlInitializer() throws IOException {
         if (currentToken != Token.LT) codeBug();
         int pos = ts.tokenBeg, tt = ts.getFirstXMLToken();
         if (tt != Token.XML && tt != Token.XMLEND) {
@@ -2554,44 +2596,46 @@
         XmlLiteral pn = new XmlLiteral(pos);
         pn.setLineno(ts.lineno);
 
-        for (;;tt = ts.getNextXMLToken()) {
+        for (; ; tt = ts.getNextXMLToken()) {
             switch (tt) {
-              case Token.XML:
-                  pn.addFragment(new XmlString(ts.tokenBeg, ts.getString()));
-                  mustMatchToken(Token.LC, "msg.syntax");
-                  int beg = ts.tokenBeg;
-                  AstNode expr = (peekToken() == Token.RC)
-                                 ? new EmptyExpression(beg, ts.tokenEnd - beg)
-                                 : expr();
-                  mustMatchToken(Token.RC, "msg.syntax");
-                  XmlExpression xexpr = new XmlExpression(beg, expr);
-                  xexpr.setIsXmlAttribute(ts.isXMLAttribute());
-                  xexpr.setLength(ts.tokenEnd - beg);
-                  pn.addFragment(xexpr);
-                  break;
-
-              case Token.XMLEND:
-                  pn.addFragment(new XmlString(ts.tokenBeg, ts.getString()));
-                  return pn;
-
-              default:
-                  reportError("msg.syntax");
-                  return makeErrorNode();
+                case Token.XML:
+                    pn.addFragment(new XmlString(ts.tokenBeg, ts.getString()));
+                    mustMatchToken(Token.LC, "msg.syntax", true);
+                    int beg = ts.tokenBeg;
+                    AstNode expr =
+                            (peekToken() == Token.RC)
+                                    ? new EmptyExpression(beg, ts.tokenEnd - beg)
+                                    : expr();
+                    mustMatchToken(Token.RC, "msg.syntax", true);
+                    XmlExpression xexpr = new XmlExpression(beg, expr);
+                    xexpr.setIsXmlAttribute(ts.isXMLAttribute());
+                    xexpr.setLength(ts.tokenEnd - beg);
+                    pn.addFragment(xexpr);
+                    break;
+
+                case Token.XMLEND:
+                    pn.addFragment(new XmlString(ts.tokenBeg, ts.getString()));
+                    return pn;
+
+                default:
+                    reportError("msg.syntax");
+                    return makeErrorNode();
             }
         }
     }
 
-    private List<AstNode> argumentList()
-        throws IOException
-    {
-        if (matchToken(Token.RP))
-            return null;
+    private List<AstNode> argumentList() throws IOException {
+        if (matchToken(Token.RP, true)) return null;
 
         List<AstNode> result = new ArrayList<AstNode>();
         boolean wasInForInit = inForInit;
         inForInit = false;
         try {
             do {
+                if (peekToken() == Token.RP) {
+                    // Quick fix to handle scenario like f1(a,); but not f1(a,b
+                    break;
+                }
                 if (peekToken() == Token.YIELD) {
                     reportError("msg.yield.parenthesized");
                 }
@@ -2599,31 +2643,27 @@
                 if (peekToken() == Token.FOR) {
                     try {
                         result.add(generatorExpression(en, 0, true));
-                    }
-                    catch(IOException ex) {
+                    } catch (IOException ex) {
                         // #TODO
                     }
-                }
-                else {                           
+                } else {
                     result.add(en);
                 }
-            } while (matchToken(Token.COMMA));
+            } while (matchToken(Token.COMMA, true));
         } finally {
             inForInit = wasInForInit;
         }
 
-        mustMatchToken(Token.RP, "msg.no.paren.arg");
+        mustMatchToken(Token.RP, "msg.no.paren.arg", true);
         return result;
     }
 
     /**
-     * Parse a new-expression, or if next token isn't {@link Token#NEW},
-     * a primary expression.
+     * Parse a new-expression, or if next token isn't {@link Token#NEW}, a primary expression.
+     *
      * @param allowCallSyntax passed down to {@link #memberExprTail}
      */
-    private AstNode memberExpr(boolean allowCallSyntax)
-        throws IOException
-    {
+    private AstNode memberExpr(boolean allowCallSyntax) throws IOException {
         int tt = peekToken(), lineno = ts.lineno;
         AstNode pn;
 
@@ -2639,15 +2679,14 @@
             nx.setTarget(target);
 
             int lp = -1;
-            if (matchToken(Token.LP)) {
+            if (matchToken(Token.LP, true)) {
                 lp = ts.tokenBeg;
                 List<AstNode> args = argumentList();
                 if (args != null && args.size() > ARGC_LIMIT)
                     reportError("msg.too.many.constructor.args");
                 int rp = ts.tokenBeg;
                 end = ts.tokenEnd;
-                if (args != null)
-                    nx.setArguments(args);
+                if (args != null) nx.setArguments(args);
                 nx.setParens(lp - pos, rp - pos);
             }
 
@@ -2655,7 +2694,7 @@
             // expression, which will mean a kind of anonymous class built with
             // the JavaAdapter.  the object literal will be passed as an
             // additional argument to the constructor.
-            if (matchToken(Token.LC)) {
+            if (matchToken(Token.LC, true)) {
                 ObjectLiteral initializer = objectLiteral();
                 end = getNodeEnd(initializer);
                 nx.setInitializer(initializer);
@@ -2669,106 +2708,125 @@
     }
 
     /**
-     * Parse any number of "(expr)", "[expr]" ".expr", "..expr",
-     * or ".(expr)" constructs trailing the passed expression.
+     * Parse any number of "(expr)", "[expr]" ".expr", "..expr", or ".(expr)" constructs trailing
+     * the passed expression.
+     *
      * @param pn the non-null parent node
-     * @return the outermost (lexically last occurring) expression,
-     * which will have the passed parent node as a descendant
+     * @return the outermost (lexically last occurring) expression, which will have the passed
+     *     parent node as a descendant
      */
-    private AstNode memberExprTail(boolean allowCallSyntax, AstNode pn)
-        throws IOException
-    {
+    private AstNode memberExprTail(boolean allowCallSyntax, AstNode pn) throws IOException {
         // we no longer return null for errors, so this won't be null
         if (pn == null) codeBug();
         int pos = pn.getPosition();
         int lineno;
-      tailLoop:
-        for (;;) {
+        tailLoop:
+        for (; ; ) {
             int tt = peekToken();
             switch (tt) {
-              case Token.DOT:
-              case Token.DOTDOT:
-                  lineno = ts.lineno;
-                  pn = propertyAccess(tt, pn);
-                  pn.setLineno(lineno);
-                  break;
-
-              case Token.DOTQUERY:
-                  consumeToken();
-                  int opPos = ts.tokenBeg, rp = -1;
-                  lineno = ts.lineno;
-                  mustHaveXML();
-                  setRequiresActivation();
-                  AstNode filter = expr();
-                  int end = getNodeEnd(filter);
-                  if (mustMatchToken(Token.RP, "msg.no.paren")) {
-                      rp = ts.tokenBeg;
-                      end = ts.tokenEnd;
-                  }
-                  XmlDotQuery q = new XmlDotQuery(pos, end - pos);
-                  q.setLeft(pn);
-                  q.setRight(filter);
-                  q.setOperatorPosition(opPos);
-                  q.setRp(rp - pos);
-                  q.setLineno(lineno);
-                  pn = q;
-                  break;
-
-              case Token.LB:
-                  consumeToken();
-                  int lb = ts.tokenBeg, rb = -1;
-                  lineno = ts.lineno;
-                  AstNode expr = expr();
-                  end = getNodeEnd(expr);
-                  if (mustMatchToken(Token.RB, "msg.no.bracket.index")) {
-                      rb = ts.tokenBeg;
-                      end = ts.tokenEnd;
-                  }
-                  ElementGet g = new ElementGet(pos, end - pos);
-                  g.setTarget(pn);
-                  g.setElement(expr);
-                  g.setParens(lb, rb);
-                  g.setLineno(lineno);
-                  pn = g;
-                  break;
-
-              case Token.LP:
-                  if (!allowCallSyntax) {
-                      break tailLoop;
-                  }
-                  lineno = ts.lineno;
-                  consumeToken();
-                  checkCallRequiresActivation(pn);
-                  FunctionCall f = new FunctionCall(pos);
-                  f.setTarget(pn);
-                  // Assign the line number for the function call to where
-                  // the paren appeared, not where the name expression started.
-                  f.setLineno(lineno);
-                  f.setLp(ts.tokenBeg - pos);
-                  List<AstNode> args = argumentList();
-                  if (args != null && args.size() > ARGC_LIMIT)
-                      reportError("msg.too.many.function.args");
-                  f.setArguments(args);
-                  f.setRp(ts.tokenBeg - pos);
-                  f.setLength(ts.tokenEnd - pos);
-                  pn = f;
-                  break;
+                case Token.DOT:
+                case Token.DOTDOT:
+                    lineno = ts.lineno;
+                    pn = propertyAccess(tt, pn);
+                    pn.setLineno(lineno);
+                    break;
 
-              default:
-                  break tailLoop;
+                case Token.DOTQUERY:
+                    consumeToken();
+                    int opPos = ts.tokenBeg, rp = -1;
+                    lineno = ts.lineno;
+                    mustHaveXML();
+                    setRequiresActivation();
+                    AstNode filter = expr();
+                    int end = getNodeEnd(filter);
+                    if (mustMatchToken(Token.RP, "msg.no.paren", true)) {
+                        rp = ts.tokenBeg;
+                        end = ts.tokenEnd;
+                    }
+                    XmlDotQuery q = new XmlDotQuery(pos, end - pos);
+                    q.setLeft(pn);
+                    q.setRight(filter);
+                    q.setOperatorPosition(opPos);
+                    q.setRp(rp - pos);
+                    q.setLineno(lineno);
+                    pn = q;
+                    break;
+
+                case Token.LB:
+                    consumeToken();
+                    int lb = ts.tokenBeg, rb = -1;
+                    lineno = ts.lineno;
+                    AstNode expr = expr();
+                    end = getNodeEnd(expr);
+                    if (mustMatchToken(Token.RB, "msg.no.bracket.index", true)) {
+                        rb = ts.tokenBeg;
+                        end = ts.tokenEnd;
+                    }
+                    ElementGet g = new ElementGet(pos, end - pos);
+                    g.setTarget(pn);
+                    g.setElement(expr);
+                    g.setParens(lb, rb);
+                    g.setLineno(lineno);
+                    pn = g;
+                    break;
+
+                case Token.LP:
+                    if (!allowCallSyntax) {
+                        break tailLoop;
+                    }
+                    lineno = ts.lineno;
+                    consumeToken();
+                    checkCallRequiresActivation(pn);
+                    FunctionCall f = new FunctionCall(pos);
+                    f.setTarget(pn);
+                    // Assign the line number for the function call to where
+                    // the paren appeared, not where the name expression started.
+                    f.setLineno(lineno);
+                    f.setLp(ts.tokenBeg - pos);
+                    List<AstNode> args = argumentList();
+                    if (args != null && args.size() > ARGC_LIMIT)
+                        reportError("msg.too.many.function.args");
+                    f.setArguments(args);
+                    f.setRp(ts.tokenBeg - pos);
+                    f.setLength(ts.tokenEnd - pos);
+                    pn = f;
+                    break;
+                case Token.COMMENT:
+                    // Ignoring all the comments, because previous statement may not be terminated
+                    // properly.
+                    int currentFlagTOken = currentFlaggedToken;
+                    peekUntilNonComment(tt);
+                    currentFlaggedToken =
+                            (currentFlaggedToken & TI_AFTER_EOL) != 0
+                                    ? currentFlaggedToken
+                                    : currentFlagTOken;
+                    break;
+                case Token.TEMPLATE_LITERAL:
+                    consumeToken();
+                    pn = taggedTemplateLiteral(pn);
+                    break;
+                default:
+                    break tailLoop;
             }
         }
         return pn;
     }
 
+    private AstNode taggedTemplateLiteral(AstNode pn) throws IOException {
+        AstNode templateLiteral = templateLiteral(true);
+        TaggedTemplateLiteral tagged = new TaggedTemplateLiteral();
+        tagged.setTarget(pn);
+        tagged.setTemplateLiteral(templateLiteral);
+        return tagged;
+    }
+
     /**
      * Handles any construct following a "." or ".." operator.
-     * @param pn the left-hand side (target) of the operator.  Never null.
+     *
+     * @param pn the left-hand side (target) of the operator. Never null.
      * @return a PropertyGet, XmlMemberGet, or ErrorNode
      */
-    private AstNode propertyAccess(int tt, AstNode pn)
-            throws IOException
-    {
+    private AstNode propertyAccess(int tt, AstNode pn) throws IOException {
         if (pn == null) codeBug();
         int memberTypeFlags = 0, lineno = ts.lineno, dotPos = ts.tokenBeg;
         consumeToken();
@@ -2782,8 +2840,11 @@
             int maybeName = nextToken();
             if (maybeName != Token.NAME
                     && !(compilerEnv.isReservedKeywordAsIdentifier()
-                    && TokenStream.isKeyword(ts.getString(), compilerEnv.getLanguageVersion(), inUseStrictDirective))) {
-              reportError("msg.no.name.after.dot");
+                            && TokenStream.isKeyword(
+                                    ts.getString(),
+                                    compilerEnv.getLanguageVersion(),
+                                    inUseStrictDirective))) {
+                reportError("msg.no.name.after.dot");
             }
 
             Name name = createNameNode(true, Token.GETPROP);
@@ -2792,97 +2853,96 @@
             return pg;
         }
 
-        AstNode ref = null;  // right side of . or .. operator
+        AstNode ref = null; // right side of . or .. operator
 
         int token = nextToken();
         switch (token) {
-          case Token.THROW:
-              // needed for generator.throw();
-              saveNameTokenData(ts.tokenBeg, "throw", ts.lineno);
-              ref = propertyName(-1, "throw", memberTypeFlags);
-              break;
-
-          case Token.NAME:
-              // handles: name, ns::name, ns::*, ns::[expr]
-              ref = propertyName(-1, ts.getString(), memberTypeFlags);
-              break;
-
-          case Token.MUL:
-              // handles: *, *::name, *::*, *::[expr]
-              saveNameTokenData(ts.tokenBeg, "*", ts.lineno);
-              ref = propertyName(-1, "*", memberTypeFlags);
-              break;
-
-          case Token.XMLATTR:
-              // handles: '@attr', '@ns::attr', '@ns::*', '@ns::*',
-              //          '@::attr', '@::*', '@*', '@*::attr', '@*::*'
-              ref = attributeAccess();
-              break;
-
-          case Token.RESERVED: {
-              String name = ts.getString();
-              saveNameTokenData(ts.tokenBeg, name, ts.lineno);
-              ref = propertyName(-1, name, memberTypeFlags);
-              break;
-          }
-
-          default:
-              if (compilerEnv.isReservedKeywordAsIdentifier()) {
-                  // allow keywords as property names, e.g. ({if: 1})
-                  String name = Token.keywordToName(token);
-                  if (name != null) {
-                      saveNameTokenData(ts.tokenBeg, name, ts.lineno);
-                      ref = propertyName(-1, name, memberTypeFlags);
-                      break;
-                  }
-              }
-              reportError("msg.no.name.after.dot");
-              return makeErrorNode();
+            case Token.THROW:
+                // needed for generator.throw();
+                saveNameTokenData(ts.tokenBeg, "throw", ts.lineno);
+                ref = propertyName(-1, memberTypeFlags);
+                break;
+
+            case Token.NAME:
+                // handles: name, ns::name, ns::*, ns::[expr]
+                ref = propertyName(-1, memberTypeFlags);
+                break;
+
+            case Token.MUL:
+                // handles: *, *::name, *::*, *::[expr]
+                saveNameTokenData(ts.tokenBeg, "*", ts.lineno);
+                ref = propertyName(-1, memberTypeFlags);
+                break;
+
+            case Token.XMLATTR:
+                // handles: '@attr', '@ns::attr', '@ns::*', '@ns::*',
+                //          '@::attr', '@::*', '@*', '@*::attr', '@*::*'
+                ref = attributeAccess();
+                break;
+
+            case Token.RESERVED:
+                {
+                    String name = ts.getString();
+                    saveNameTokenData(ts.tokenBeg, name, ts.lineno);
+                    ref = propertyName(-1, memberTypeFlags);
+                    break;
+                }
+
+            default:
+                if (compilerEnv.isReservedKeywordAsIdentifier()) {
+                    // allow keywords as property names, e.g. ({if: 1})
+                    String name = Token.keywordToName(token);
+                    if (name != null) {
+                        saveNameTokenData(ts.tokenBeg, name, ts.lineno);
+                        ref = propertyName(-1, memberTypeFlags);
+                        break;
+                    }
+                }
+                reportError("msg.no.name.after.dot");
+                return makeErrorNode();
         }
 
         boolean xml = ref instanceof XmlRef;
         InfixExpression result = xml ? new XmlMemberGet() : new PropertyGet();
-        if (xml && tt == Token.DOT)
-            result.setType(Token.DOT);
+        if (xml && tt == Token.DOT) result.setType(Token.DOT);
         int pos = pn.getPosition();
         result.setPosition(pos);
         result.setLength(getNodeEnd(ref) - pos);
         result.setOperatorPosition(dotPos - pos);
         result.setLineno(pn.getLineno());
-        result.setLeft(pn);  // do this after setting position
+        result.setLeft(pn); // do this after setting position
         result.setRight(ref);
         return result;
     }
 
     /**
-     * Xml attribute expression:<p>
-     *   {@code @attr}, {@code @ns::attr}, {@code @ns::*}, {@code @ns::*},
-     *   {@code @*}, {@code @*::attr}, {@code @*::*}, {@code @ns::[expr]},
-     *   {@code @*::[expr]}, {@code @[expr]} <p>
-     * Called if we peeked an '@' token.
+     * Xml attribute expression:
+     *
+     * <p>{@code @attr}, {@code @ns::attr}, {@code @ns::*}, {@code @ns::*}, {@code @*},
+     * {@code @*::attr}, {@code @*::*}, {@code @ns::[expr]}, {@code @*::[expr]}, {@code @[expr]}
+     *
+     * <p>Called if we peeked an '@' token.
      */
-    private AstNode attributeAccess()
-        throws IOException
-    {
+    private AstNode attributeAccess() throws IOException {
         int tt = nextToken(), atPos = ts.tokenBeg;
 
         switch (tt) {
-          // handles: @name, @ns::name, @ns::*, @ns::[expr]
-          case Token.NAME:
-              return propertyName(atPos, ts.getString(), 0);
-
-          // handles: @*, @*::name, @*::*, @*::[expr]
-          case Token.MUL:
-              saveNameTokenData(ts.tokenBeg, "*", ts.lineno);
-              return propertyName(atPos, "*", 0);
-
-          // handles @[expr]
-          case Token.LB:
-              return xmlElemRef(atPos, null, -1);
-
-          default:
-              reportError("msg.no.name.after.xmlAttr");
-              return makeErrorNode();
+                // handles: @name, @ns::name, @ns::*, @ns::[expr]
+            case Token.NAME:
+                return propertyName(atPos, 0);
+
+                // handles: @*, @*::name, @*::*, @*::[expr]
+            case Token.MUL:
+                saveNameTokenData(ts.tokenBeg, "*", ts.lineno);
+                return propertyName(atPos, 0);
+
+                // handles @[expr]
+            case Token.LB:
+                return xmlElemRef(atPos, null, -1);
+
+            default:
+                reportError("msg.no.name.after.xmlAttr");
+                return makeErrorNode();
         }
     }
 
@@ -2890,48 +2950,41 @@
      * Check if :: follows name in which case it becomes a qualified name.
      *
      * @param atPos a natural number if we just read an '@' token, else -1
-     *
-     * @param s the name or string that was matched (an identifier, "throw" or
-     * "*").
-     *
+     * @param s the name or string that was matched (an identifier, "throw" or "*").
      * @param memberTypeFlags flags tracking whether we're a '.' or '..' child
-     *
-     * @return an XmlRef node if it's an attribute access, a child of a
-     * '..' operator, or the name is followed by ::.  For a plain name,
-     * returns a Name node.  Returns an ErrorNode for malformed XML
-     * expressions.  (For now - might change to return a partial XmlRef.)
+     * @return an XmlRef node if it's an attribute access, a child of a '..' operator, or the name
+     *     is followed by ::. For a plain name, returns a Name node. Returns an ErrorNode for
+     *     malformed XML expressions. (For now - might change to return a partial XmlRef.)
      */
-    private AstNode propertyName(int atPos, String s, int memberTypeFlags)
-        throws IOException
-    {
+    private AstNode propertyName(int atPos, int memberTypeFlags) throws IOException {
         int pos = atPos != -1 ? atPos : ts.tokenBeg, lineno = ts.lineno;
         int colonPos = -1;
         Name name = createNameNode(true, currentToken);
         Name ns = null;
 
-        if (matchToken(Token.COLONCOLON)) {
+        if (matchToken(Token.COLONCOLON, true)) {
             ns = name;
             colonPos = ts.tokenBeg;
 
             switch (nextToken()) {
-              // handles name::name
-              case Token.NAME:
-                  name = createNameNode();
-                  break;
-
-              // handles name::*
-              case Token.MUL:
-                  saveNameTokenData(ts.tokenBeg, "*", ts.lineno);
-                  name = createNameNode(false, -1);
-                  break;
-
-              // handles name::[expr] or *::[expr]
-              case Token.LB:
-                  return xmlElemRef(atPos, ns, colonPos);
-
-              default:
-                  reportError("msg.no.name.after.coloncolon");
-                  return makeErrorNode();
+                    // handles name::name
+                case Token.NAME:
+                    name = createNameNode();
+                    break;
+
+                    // handles name::*
+                case Token.MUL:
+                    saveNameTokenData(ts.tokenBeg, "*", ts.lineno);
+                    name = createNameNode(false, -1);
+                    break;
+
+                    // handles name::[expr] or *::[expr]
+                case Token.LB:
+                    return xmlElemRef(atPos, ns, colonPos);
+
+                default:
+                    reportError("msg.no.name.after.coloncolon");
+                    return makeErrorNode();
             }
         }
 
@@ -2949,16 +3002,14 @@
     }
 
     /**
-     * Parse the [expr] portion of an xml element reference, e.g.
-     * @[expr], @*::[expr], or ns::[expr].
+     * Parse the [expr] portion of an xml element reference, e.g. @[expr], @*::[expr], or
+     * ns::[expr].
      */
-    private XmlElemRef xmlElemRef(int atPos, Name namespace, int colonPos)
-        throws IOException
-    {
+    private XmlElemRef xmlElemRef(int atPos, Name namespace, int colonPos) throws IOException {
         int lb = ts.tokenBeg, rb = -1, pos = atPos != -1 ? atPos : lb;
         AstNode expr = expr();
         int end = getNodeEnd(expr);
-        if (mustMatchToken(Token.RB, "msg.no.bracket.index")) {
+        if (mustMatchToken(Token.RB, "msg.no.bracket.index", true)) {
             rb = ts.tokenBeg;
             end = ts.tokenEnd;
         }
@@ -2971,9 +3022,7 @@
         return ref;
     }
 
-    private AstNode destructuringPrimaryExpr()
-        throws IOException, ParserException
-    {
+    private AstNode destructuringPrimaryExpr() throws IOException, ParserException {
         try {
             inDestructuringAssignment = true;
             return primaryExpr();
@@ -2982,110 +3031,94 @@
         }
     }
 
-    private AstNode primaryExpr()
-        throws IOException
-    {
+    private AstNode primaryExpr() throws IOException {
         int ttFlagged = peekFlaggedToken();
         int tt = ttFlagged & CLEAR_TI_MASK;
 
-        switch(tt) {
-          case Token.FUNCTION:
-              consumeToken();
-              return function(FunctionNode.FUNCTION_EXPRESSION);
-
-          case Token.LB:
-              consumeToken();
-              return arrayLiteral();
-
-          case Token.LC:
-              consumeToken();
-              return objectLiteral();
-
-          case Token.LET:
-              consumeToken();
-              return let(false, ts.tokenBeg);
-
-          case Token.LP:
-              consumeToken();
-              return parenExpr();
-
-          case Token.XMLATTR:
-              consumeToken();
-              mustHaveXML();
-              return attributeAccess();
-
-          case Token.NAME:
-              consumeToken();
-              return name(ttFlagged, tt);
-
-          case Token.NUMBER: {
-              consumeToken();
-              String s = ts.getString();
-              if (this.inUseStrictDirective && ts.isNumberOldOctal()) {
-                  reportError("msg.no.old.octal.strict");
-              }
-              if (ts.isNumberBinary()) {
-                  s = "0b"+s;
-              }
-              if (ts.isNumberOldOctal()) {
-                  s = "0"+s;
-              }
-              if (ts.isNumberOctal()) {
-                  s = "0o"+s;
-              }
-              if (ts.isNumberHex()) {
-                  s = "0x"+s;
-              }
-              return new NumberLiteral(ts.tokenBeg,
-                                       s,
-                                       ts.getNumber());
-          }
-
-          case Token.STRING:
-              consumeToken();
-              return createStringLiteral();
-
-          case Token.DIV:
-          case Token.ASSIGN_DIV:
-              consumeToken();
-              // Got / or /= which in this context means a regexp
-              ts.readRegExp(tt);
-              int pos = ts.tokenBeg, end = ts.tokenEnd;
-              RegExpLiteral re = new RegExpLiteral(pos, end - pos);
-              re.setValue(ts.getString());
-              re.setFlags(ts.readAndClearRegExpFlags());
-              return re;
-
-          case Token.NULL:
-          case Token.THIS:
-          case Token.FALSE:
-          case Token.TRUE:
-              consumeToken();
-              pos = ts.tokenBeg; end = ts.tokenEnd;
-              return new KeywordLiteral(pos, end - pos, tt);
-
-          case Token.RP:
-              return new EmptyExpression();
-
-          case Token.RESERVED:
-              consumeToken();
-              reportError("msg.reserved.id");
-              break;
-
-          case Token.ERROR:
-              consumeToken();
-              // the scanner or one of its subroutines reported the error.
-              break;
-
-          case Token.EOF:
-              consumeToken();
-              reportError("msg.unexpected.eof");
-              break;
-
-          default:
-              consumeToken();
-              reportError("msg.syntax");
-              break;
+        switch (tt) {
+            case Token.FUNCTION:
+                consumeToken();
+                return function(FunctionNode.FUNCTION_EXPRESSION);
+
+            case Token.LB:
+                consumeToken();
+                return arrayLiteral();
+
+            case Token.LC:
+                consumeToken();
+                return objectLiteral();
+
+            case Token.LET:
+                consumeToken();
+                return let(false, ts.tokenBeg);
+
+            case Token.LP:
+                consumeToken();
+                return parenExpr();
+
+            case Token.XMLATTR:
+                consumeToken();
+                mustHaveXML();
+                return attributeAccess();
+
+            case Token.NAME:
+                consumeToken();
+                return name(ttFlagged, tt);
+
+            case Token.NUMBER:
+            case Token.BIGINT:
+                {
+                    consumeToken();
+                    return createNumericLiteral(tt, false);
+                }
+
+            case Token.STRING:
+                consumeToken();
+                return createStringLiteral();
+
+            case Token.DIV:
+            case Token.ASSIGN_DIV:
+                consumeToken();
+                // Got / or /= which in this context means a regexp
+                ts.readRegExp(tt);
+                int pos = ts.tokenBeg, end = ts.tokenEnd;
+                RegExpLiteral re = new RegExpLiteral(pos, end - pos);
+                re.setValue(ts.getString());
+                re.setFlags(ts.readAndClearRegExpFlags());
+                return re;
+
+            case Token.NULL:
+            case Token.THIS:
+            case Token.FALSE:
+            case Token.TRUE:
+                consumeToken();
+                pos = ts.tokenBeg;
+                end = ts.tokenEnd;
+                return new KeywordLiteral(pos, end - pos, tt);
+
+            case Token.TEMPLATE_LITERAL:
+                consumeToken();
+                return templateLiteral(false);
+
+            case Token.RESERVED:
+                consumeToken();
+                reportError("msg.reserved.id", ts.getString());
+                break;
+
+            case Token.ERROR:
+                consumeToken();
+                // the scanner or one of its subroutines reported the error.
+                break;
+
+            case Token.EOF:
+                consumeToken();
+                reportError("msg.unexpected.eof");
+                break;
+
+            default:
+                consumeToken();
+                reportError("msg.syntax");
+                break;
         }
         // should only be reachable in IDE/error-recovery mode
         consumeToken();
@@ -3099,24 +3132,24 @@
             Comment jsdocNode = getAndResetJsDoc();
             int lineno = ts.lineno;
             int begin = ts.tokenBeg;
-            AstNode e = expr();
+            AstNode e = (peekToken() == Token.RP ? new EmptyExpression(begin) : expr());
             if (peekToken() == Token.FOR) {
                 return generatorExpression(e, begin);
             }
-            ParenthesizedExpression pn = new ParenthesizedExpression(e);
+            mustMatchToken(Token.RP, "msg.no.paren", true);
+            if (e.getType() == Token.EMPTY && peekToken() != Token.ARROW) {
+                reportError("msg.syntax");
+                return makeErrorNode();
+            }
+            int length = ts.tokenEnd - begin;
+            ParenthesizedExpression pn = new ParenthesizedExpression(begin, length, e);
+            pn.setLineno(lineno);
             if (jsdocNode == null) {
                 jsdocNode = getAndResetJsDoc();
             }
             if (jsdocNode != null) {
                 pn.setJsDocNode(jsdocNode);
             }
-            mustMatchToken(Token.RP, "msg.no.paren");
-            if (e.getType() == Token.EMPTY && peekToken() != Token.ARROW) {
-              reportError("msg.syntax");
-              return makeErrorNode();
-            }
-            pn.setLength(ts.tokenEnd - pn.getPosition());
-            pn.setLineno(lineno);
             return pn;
         } finally {
             inForInit = wasInForInit;
@@ -3140,18 +3173,13 @@
         saveNameTokenData(namePos, nameString, nameLineno);
 
         if (compilerEnv.isXmlAvailable()) {
-            return propertyName(-1, nameString, 0);
-        } else {
-            return createNameNode(true, Token.NAME);
+            return propertyName(-1, 0);
         }
+        return createNameNode(true, Token.NAME);
     }
 
-    /**
-     * May return an {@link ArrayLiteral} or {@link ArrayComprehension}.
-     */
-    private AstNode arrayLiteral()
-        throws IOException
-    {
+    /** May return an {@link ArrayLiteral} or {@link ArrayComprehension}. */
+    private AstNode arrayLiteral() throws IOException {
         if (currentToken != Token.LB) codeBug();
         int pos = ts.tokenBeg, end = ts.tokenEnd;
         List<AstNode> elements = new ArrayList<AstNode>();
@@ -3159,7 +3187,7 @@
         boolean after_lb_or_comma = true;
         int afterComma = -1;
         int skipCount = 0;
-        for (;;) {
+        for (; ; ) {
             int tt = peekToken();
             if (tt == Token.COMMA) {
                 consumeToken();
@@ -3170,6 +3198,8 @@
                     elements.add(new EmptyExpression(ts.tokenBeg, 1));
                     skipCount++;
                 }
+            } else if (tt == Token.COMMENT) {
+                consumeToken();
             } else if (tt == Token.RB) {
                 consumeToken();
                 // for ([a,] in obj) is legal, but for ([a] in obj) is
@@ -3178,14 +3208,11 @@
                 // array literal contexts. So we calculate a special
                 // length value just for destructuring assignment.
                 end = ts.tokenEnd;
-                pn.setDestructuringLength(elements.size() +
-                                          (after_lb_or_comma ? 1 : 0));
+                pn.setDestructuringLength(elements.size() + (after_lb_or_comma ? 1 : 0));
                 pn.setSkipCount(skipCount);
-                if (afterComma != -1)
-                    warnTrailingComma(pos, elements, afterComma);
+                if (afterComma != -1) warnTrailingComma(pos, elements, afterComma);
                 break;
-            } else if (tt == Token.FOR && !after_lb_or_comma
-                       && elements.size() == 1) {
+            } else if (tt == Token.FOR && !after_lb_or_comma && elements.size() == 1) {
                 return arrayComprehension(elements.get(0), pos);
             } else if (tt == Token.EOF) {
                 reportError("msg.no.bracket.arg");
@@ -3208,15 +3235,13 @@
 
     /**
      * Parse a JavaScript 1.7 Array comprehension.
+     *
      * @param result the first expression after the opening left-bracket
      * @param pos start of LB token that begins the array comprehension
      * @return the array comprehension or an error node
      */
-    private AstNode arrayComprehension(AstNode result, int pos)
-        throws IOException
-    {
-        List<ArrayComprehensionLoop> loops =
-                new ArrayList<ArrayComprehensionLoop>();
+    private AstNode arrayComprehension(AstNode result, int pos) throws IOException {
+        List<ArrayComprehensionLoop> loops = new ArrayList<ArrayComprehensionLoop>();
         while (peekToken() == Token.FOR) {
             loops.add(arrayComprehensionLoop());
         }
@@ -3227,7 +3252,7 @@
             ifPos = ts.tokenBeg - pos;
             data = condition();
         }
-        mustMatchToken(Token.RB, "msg.no.bracket.arg");
+        mustMatchToken(Token.RB, "msg.no.bracket.arg", true);
         ArrayComprehension pn = new ArrayComprehension(pos, ts.tokenEnd - pos);
         pn.setResult(result);
         pn.setLoops(loops);
@@ -3240,42 +3265,40 @@
         return pn;
     }
 
-    private ArrayComprehensionLoop arrayComprehensionLoop()
-        throws IOException
-    {
+    private ArrayComprehensionLoop arrayComprehensionLoop() throws IOException {
         if (nextToken() != Token.FOR) codeBug();
         int pos = ts.tokenBeg;
         int eachPos = -1, lp = -1, rp = -1, inPos = -1;
-        boolean isForIn = false, isForOf = false;
+        boolean isForOf = false;
         ArrayComprehensionLoop pn = new ArrayComprehensionLoop(pos);
 
         pushScope(pn);
         try {
-            if (matchToken(Token.NAME)) {
+            if (matchToken(Token.NAME, true)) {
                 if (ts.getString().equals("each")) {
                     eachPos = ts.tokenBeg - pos;
                 } else {
                     reportError("msg.no.paren.for");
                 }
             }
-            if (mustMatchToken(Token.LP, "msg.no.paren.for")) {
+            if (mustMatchToken(Token.LP, "msg.no.paren.for", true)) {
                 lp = ts.tokenBeg - pos;
             }
 
             AstNode iter = null;
             switch (peekToken()) {
-              case Token.LB:
-              case Token.LC:
-                  // handle destructuring assignment
-                  iter = destructuringPrimaryExpr();
-                  markDestructuring(iter);
-                  break;
-              case Token.NAME:
-                  consumeToken();
-                  iter = createNameNode();
-                  break;
-              default:
-                  reportError("msg.bad.var");
+                case Token.LB:
+                case Token.LC:
+                    // handle destructuring assignment
+                    iter = destructuringPrimaryExpr();
+                    markDestructuring(iter);
+                    break;
+                case Token.NAME:
+                    consumeToken();
+                    iter = createNameNode();
+                    break;
+                default:
+                    reportError("msg.bad.var");
             }
 
             // Define as a let since we want the scope of the variable to
@@ -3285,25 +3308,24 @@
             }
 
             switch (nextToken()) {
-            case Token.IN:
-                inPos = ts.tokenBeg - pos;
-                isForIn = true;
-                break;
-            case Token.NAME:
-                if ("of".equals(ts.getString())) {
-                    if (eachPos != -1) {
-                        reportError("msg.invalid.for.each");
-                    }
+                case Token.IN:
                     inPos = ts.tokenBeg - pos;
-                    isForOf = true;
                     break;
-                }
-            default:
-                reportError("msg.in.after.for.name");
+                case Token.NAME:
+                    if ("of".equals(ts.getString())) {
+                        if (eachPos != -1) {
+                            reportError("msg.invalid.for.each");
+                        }
+                        inPos = ts.tokenBeg - pos;
+                        isForOf = true;
+                        break;
+                    }
+                    // fall through
+                default:
+                    reportError("msg.in.after.for.name");
             }
             AstNode obj = expr();
-            if (mustMatchToken(Token.RP, "msg.no.paren.for.ctrl"))
-                rp = ts.tokenBeg - pos;
+            if (mustMatchToken(Token.RP, "msg.no.paren.for.ctrl", true)) rp = ts.tokenBeg - pos;
 
             pn.setLength(ts.tokenEnd - pos);
             pn.setIterator(iter);
@@ -3319,18 +3341,14 @@
         }
     }
 
-    private AstNode generatorExpression(AstNode result, int pos)
-        throws IOException
-    {
+    private AstNode generatorExpression(AstNode result, int pos) throws IOException {
         return generatorExpression(result, pos, false);
     }
-    
+
     private AstNode generatorExpression(AstNode result, int pos, boolean inFunctionParams)
-        throws IOException
-    {
-        
-        List<GeneratorExpressionLoop> loops =
-                new ArrayList<GeneratorExpressionLoop>();
+            throws IOException {
+
+        List<GeneratorExpressionLoop> loops = new ArrayList<GeneratorExpressionLoop>();
         while (peekToken() == Token.FOR) {
             loops.add(generatorExpressionLoop());
         }
@@ -3341,8 +3359,8 @@
             ifPos = ts.tokenBeg - pos;
             data = condition();
         }
-        if(!inFunctionParams) {
-            mustMatchToken(Token.RP, "msg.no.paren.let");
+        if (!inFunctionParams) {
+            mustMatchToken(Token.RP, "msg.no.paren.let", true);
         }
         GeneratorExpression pn = new GeneratorExpression(pos, ts.tokenEnd - pos);
         pn.setResult(result);
@@ -3355,10 +3373,8 @@
         }
         return pn;
     }
-        
-    private GeneratorExpressionLoop generatorExpressionLoop()
-        throws IOException
-    {
+
+    private GeneratorExpressionLoop generatorExpressionLoop() throws IOException {
         if (nextToken() != Token.FOR) codeBug();
         int pos = ts.tokenBeg;
         int lp = -1, rp = -1, inPos = -1;
@@ -3366,24 +3382,24 @@
 
         pushScope(pn);
         try {
-            if (mustMatchToken(Token.LP, "msg.no.paren.for")) {
+            if (mustMatchToken(Token.LP, "msg.no.paren.for", true)) {
                 lp = ts.tokenBeg - pos;
             }
 
             AstNode iter = null;
             switch (peekToken()) {
-              case Token.LB:
-              case Token.LC:
-                  // handle destructuring assignment
-                  iter = destructuringPrimaryExpr();
-                  markDestructuring(iter);
-                  break;
-              case Token.NAME:
-                  consumeToken();
-                  iter = createNameNode();
-                  break;
-              default:
-                  reportError("msg.bad.var");
+                case Token.LB:
+                case Token.LC:
+                    // handle destructuring assignment
+                    iter = destructuringPrimaryExpr();
+                    markDestructuring(iter);
+                    break;
+                case Token.NAME:
+                    consumeToken();
+                    iter = createNameNode();
+                    break;
+                default:
+                    reportError("msg.bad.var");
             }
 
             // Define as a let since we want the scope of the variable to
@@ -3392,11 +3408,9 @@
                 defineSymbol(Token.LET, ts.getString(), true);
             }
 
-            if (mustMatchToken(Token.IN, "msg.in.after.for.name"))
-                inPos = ts.tokenBeg - pos;
+            if (mustMatchToken(Token.IN, "msg.in.after.for.name", true)) inPos = ts.tokenBeg - pos;
             AstNode obj = expr();
-            if (mustMatchToken(Token.RP, "msg.no.paren.for.ctrl"))
-                rp = ts.tokenBeg - pos;
+            if (mustMatchToken(Token.RP, "msg.no.paren.for.ctrl", true)) rp = ts.tokenBeg - pos;
 
             pn.setLength(ts.tokenEnd - pos);
             pn.setIterator(iter);
@@ -3409,14 +3423,12 @@
         }
     }
 
-    private static final int PROP_ENTRY   = 1;
-    private static final int GET_ENTRY    = 2;
-    private static final int SET_ENTRY    = 4;
+    private static final int PROP_ENTRY = 1;
+    private static final int GET_ENTRY = 2;
+    private static final int SET_ENTRY = 4;
     private static final int METHOD_ENTRY = 8;
 
-    private ObjectLiteral objectLiteral()
-        throws IOException
-    {
+    private ObjectLiteral objectLiteral() throws IOException {
         int pos = ts.tokenBeg, lineno = ts.lineno;
         int afterComma = -1;
         List<ObjectProperty> elems = new ArrayList<ObjectProperty>();
@@ -3428,109 +3440,105 @@
         }
         Comment objJsdocNode = getAndResetJsDoc();
 
-      commaLoop:
-        for (;;) {
+        commaLoop:
+        for (; ; ) {
             String propertyName = null;
             int entryKind = PROP_ENTRY;
             int tt = peekToken();
             Comment jsdocNode = getAndResetJsDoc();
-
+            if (tt == Token.COMMENT) {
+                consumeToken();
+                tt = peekUntilNonComment(tt);
+            }
             if (tt == Token.RC) {
-                if (afterComma != -1)
-                    warnTrailingComma(pos, elems, afterComma);
+                if (afterComma != -1) warnTrailingComma(pos, elems, afterComma);
                 break commaLoop;
+            }
+            AstNode pname = objliteralProperty();
+            if (pname == null) {
+                reportError("msg.bad.prop");
             } else {
-                AstNode pname = objliteralProperty();
-                if (pname == null) {
-                    propertyName = null;
-                    reportError("msg.bad.prop");
-                } else {
-                    propertyName = ts.getString();
-                    int ppos = ts.tokenBeg;
-                    consumeToken();
+                propertyName = ts.getString();
+                int ppos = ts.tokenBeg;
+                consumeToken();
 
-                    // This code path needs to handle both destructuring object
-                    // literals like:
-                    // var {get, b} = {get: 1, b: 2};
-                    // and getters like:
-                    // var x = {get 1() { return 2; };
-                    // So we check a whitelist of tokens to check if we're at the
-                    // first case. (Because of keywords, the second case may be
-                    // many tokens.)
-                    int peeked = peekToken();
-                    if (peeked != Token.COMMA
-                            && peeked != Token.COLON
-                            && peeked != Token.RC)
-                    {
-                        if (peeked == Token.LP) {
-                            entryKind = METHOD_ENTRY;
-                        } else if (pname.getType() == Token.NAME) {
-                            if ("get".equals(propertyName)) {
-                                entryKind = GET_ENTRY;
-                            } else if ("set".equals(propertyName)) {
-                                entryKind = SET_ENTRY;
-                            }
-                        }
-                        if (entryKind == GET_ENTRY || entryKind == SET_ENTRY) {
-                            pname = objliteralProperty();
-                            if (pname == null) {
-                                reportError("msg.bad.prop");
-                            }
-                            consumeToken();
+                // This code path needs to handle both destructuring object
+                // literals like:
+                // var {get, b} = {get: 1, b: 2};
+                // and getters like:
+                // var x = {get 1() { return 2; };
+                // So we check a whitelist of tokens to check if we're at the
+                // first case. (Because of keywords, the second case may be
+                // many tokens.)
+                int peeked = peekToken();
+                if (peeked != Token.COMMA && peeked != Token.COLON && peeked != Token.RC) {
+                    if (peeked == Token.LP) {
+                        entryKind = METHOD_ENTRY;
+                    } else if (pname.getType() == Token.NAME) {
+                        if ("get".equals(propertyName)) {
+                            entryKind = GET_ENTRY;
+                        } else if ("set".equals(propertyName)) {
+                            entryKind = SET_ENTRY;
                         }
+                    }
+                    if (entryKind == GET_ENTRY || entryKind == SET_ENTRY) {
+                        pname = objliteralProperty();
                         if (pname == null) {
-                            propertyName = null;
-                        } else {
-                            propertyName = ts.getString();
-                            ObjectProperty objectProp = methodDefinition(
-                                    ppos, pname, entryKind);
-                            pname.setJsDocNode(jsdocNode);
-                            elems.add(objectProp);
+                            reportError("msg.bad.prop");
                         }
+                        consumeToken();
+                    }
+                    if (pname == null) {
+                        propertyName = null;
                     } else {
+                        propertyName = ts.getString();
+                        ObjectProperty objectProp = methodDefinition(ppos, pname, entryKind);
                         pname.setJsDocNode(jsdocNode);
-                        elems.add(plainProperty(pname, tt));
+                        elems.add(objectProp);
                     }
+                } else {
+                    pname.setJsDocNode(jsdocNode);
+                    elems.add(plainProperty(pname, tt));
                 }
             }
 
             if (this.inUseStrictDirective && propertyName != null) {
                 switch (entryKind) {
-                case PROP_ENTRY:
-                case METHOD_ENTRY:
-                    if (getterNames.contains(propertyName)
-                            || setterNames.contains(propertyName)) {
-                        addError("msg.dup.obj.lit.prop.strict", propertyName);
-                    }
-                    getterNames.add(propertyName);
-                    setterNames.add(propertyName);
-                    break;
-                case GET_ENTRY:
-                    if (getterNames.contains(propertyName)) {
-                        addError("msg.dup.obj.lit.prop.strict", propertyName);
-                    }
-                    getterNames.add(propertyName);
-                    break;
-                case SET_ENTRY:
-                    if (setterNames.contains(propertyName)) {
-                        addError("msg.dup.obj.lit.prop.strict", propertyName);
-                    }
-                    setterNames.add(propertyName);
-                    break;
+                    case PROP_ENTRY:
+                    case METHOD_ENTRY:
+                        if (getterNames.contains(propertyName)
+                                || setterNames.contains(propertyName)) {
+                            addError("msg.dup.obj.lit.prop.strict", propertyName);
+                        }
+                        getterNames.add(propertyName);
+                        setterNames.add(propertyName);
+                        break;
+                    case GET_ENTRY:
+                        if (getterNames.contains(propertyName)) {
+                            addError("msg.dup.obj.lit.prop.strict", propertyName);
+                        }
+                        getterNames.add(propertyName);
+                        break;
+                    case SET_ENTRY:
+                        if (setterNames.contains(propertyName)) {
+                            addError("msg.dup.obj.lit.prop.strict", propertyName);
+                        }
+                        setterNames.add(propertyName);
+                        break;
                 }
             }
 
             // Eat any dangling jsdoc in the property.
             getAndResetJsDoc();
 
-            if (matchToken(Token.COMMA)) {
+            if (matchToken(Token.COMMA, true)) {
                 afterComma = ts.tokenEnd;
             } else {
                 break commaLoop;
             }
         }
 
-        mustMatchToken(Token.RC, "msg.no.brace.prop");
+        mustMatchToken(Token.RC, "msg.no.brace.prop", true);
         ObjectLiteral pn = new ObjectLiteral(pos, ts.tokenEnd - pos);
         if (objJsdocNode != null) {
             pn.setJsDocNode(objJsdocNode);
@@ -3543,51 +3551,54 @@
     private AstNode objliteralProperty() throws IOException {
         AstNode pname;
         int tt = peekToken();
-        switch(tt) {
-          case Token.NAME:
-              pname = createNameNode();
-              break;
-
-          case Token.STRING:
-              pname = createStringLiteral();
-              break;
-
-          case Token.NUMBER:
-              pname = new NumberLiteral(
-                      ts.tokenBeg, ts.getString(), ts.getNumber());
-              break;
-
-          default:
-              if (compilerEnv.isReservedKeywordAsIdentifier()
-                      && TokenStream.isKeyword(ts.getString(), compilerEnv.getLanguageVersion(), inUseStrictDirective)) {
-                  // convert keyword to property name, e.g. ({if: 1})
-                  pname = createNameNode();
-                  break;
-              }
-              return null;
+        switch (tt) {
+            case Token.NAME:
+                pname = createNameNode();
+                break;
+
+            case Token.STRING:
+                pname = createStringLiteral();
+                break;
+
+            case Token.NUMBER:
+            case Token.BIGINT:
+                pname = createNumericLiteral(tt, true);
+                break;
+
+            default:
+                if (compilerEnv.isReservedKeywordAsIdentifier()
+                        && TokenStream.isKeyword(
+                                ts.getString(),
+                                compilerEnv.getLanguageVersion(),
+                                inUseStrictDirective)) {
+                    // convert keyword to property name, e.g. ({if: 1})
+                    pname = createNameNode();
+                    break;
+                }
+                return null;
         }
 
         return pname;
     }
 
-    private ObjectProperty plainProperty(AstNode property, int ptt)
-        throws IOException
-    {
+    private ObjectProperty plainProperty(AstNode property, int ptt) throws IOException {
         // Support, e.g., |var {x, y} = o| as destructuring shorthand
         // for |var {x: x, y: y} = o|, as implemented in spidermonkey JS 1.8.
         int tt = peekToken();
-        if ((tt == Token.COMMA || tt == Token.RC) && ptt == Token.NAME
+        if ((tt == Token.COMMA || tt == Token.RC)
+                && ptt == Token.NAME
                 && compilerEnv.getLanguageVersion() >= Context.VERSION_1_8) {
-            if (!inDestructuringAssignment) {
+            if (!inDestructuringAssignment
+                    && compilerEnv.getLanguageVersion() < Context.VERSION_ES6) {
                 reportError("msg.bad.object.init");
             }
             AstNode nn = new Name(property.getPosition(), property.getString());
             ObjectProperty pn = new ObjectProperty();
-            pn.putProp(Node.DESTRUCTURING_SHORTHAND, Boolean.TRUE);
+            pn.putProp(Node.SHORTHAND_PROPERTY_NAME, Boolean.TRUE);
             pn.setLeftAndRight(property, nn);
             return pn;
         }
-        mustMatchToken(Token.COLON, "msg.no.colon.prop");
+        mustMatchToken(Token.COLON, "msg.no.colon.prop", true);
         ObjectProperty pn = new ObjectProperty();
         pn.setOperatorPosition(ts.tokenBeg);
         pn.setLeftAndRight(property, assignExpr());
@@ -3595,8 +3606,7 @@
     }
 
     private ObjectProperty methodDefinition(int pos, AstNode propName, int entryKind)
-        throws IOException
-    {
+            throws IOException {
         FunctionNode fn = function(FunctionNode.FUNCTION_EXPRESSION);
         // We've already parsed the function name, so fn should be anonymous.
         Name name = fn.getFunctionName();
@@ -3605,18 +3615,18 @@
         }
         ObjectProperty pn = new ObjectProperty(pos);
         switch (entryKind) {
-        case GET_ENTRY:
-            pn.setIsGetterMethod();
-            fn.setFunctionIsGetterMethod();
-            break;
-        case SET_ENTRY:
-            pn.setIsSetterMethod();
-            fn.setFunctionIsSetterMethod();
-            break;
-        case METHOD_ENTRY:
-            pn.setIsNormalMethod();
-            fn.setFunctionIsNormalMethod();
-            break;
+            case GET_ENTRY:
+                pn.setIsGetterMethod();
+                fn.setFunctionIsGetterMethod();
+                break;
+            case SET_ENTRY:
+                pn.setIsSetterMethod();
+                fn.setFunctionIsSetterMethod();
+                break;
+            case METHOD_ENTRY:
+                pn.setIsNormalMethod();
+                fn.setFunctionIsNormalMethod();
+                break;
         }
         int end = getNodeEnd(fn);
         pn.setLeft(propName);
@@ -3630,11 +3640,10 @@
     }
 
     /**
-     * Create a {@code Name} node using the token info from the
-     * last scanned name.  In some cases we need to either synthesize
-     * a name node, or we lost the name token information by peeking.
-     * If the {@code token} parameter is not {@link Token#NAME}, then
-     * we use token info saved in instance vars.
+     * Create a {@code Name} node using the token info from the last scanned name. In some cases we
+     * need to either synthesize a name node, or we lost the name token information by peeking. If
+     * the {@code token} parameter is not {@link Token#NAME}, then we use token info saved in
+     * instance vars.
      */
     private Name createNameNode(boolean checkActivation, int token) {
         int beg = ts.tokenBeg;
@@ -3672,22 +3681,85 @@
         return s;
     }
 
+    private AstNode templateLiteral(boolean isTaggedLiteral) throws IOException {
+        if (currentToken != Token.TEMPLATE_LITERAL) codeBug();
+        int pos = ts.tokenBeg, end = ts.tokenEnd;
+        List<AstNode> elements = new ArrayList<AstNode>();
+        TemplateLiteral pn = new TemplateLiteral(pos);
+
+        int posChars = ts.tokenBeg + 1;
+        int tt = ts.readTemplateLiteral(isTaggedLiteral);
+        while (tt == Token.TEMPLATE_LITERAL_SUBST) {
+            elements.add(createTemplateLiteralCharacters(posChars));
+            elements.add(expr());
+            mustMatchToken(Token.RC, "msg.syntax", true);
+            posChars = ts.tokenBeg + 1;
+            tt = ts.readTemplateLiteral(isTaggedLiteral);
+        }
+        if (tt == Token.ERROR) {
+            return makeErrorNode();
+        }
+        assert tt == Token.TEMPLATE_LITERAL;
+        elements.add(createTemplateLiteralCharacters(posChars));
+        end = ts.tokenEnd;
+        pn.setElements(elements);
+        pn.setLength(end - pos);
+
+        return pn;
+    }
+
+    private TemplateCharacters createTemplateLiteralCharacters(int pos) {
+        TemplateCharacters chars = new TemplateCharacters(pos, ts.tokenEnd - pos - 1);
+        chars.setValue(ts.getString());
+        chars.setRawValue(ts.getRawString());
+        return chars;
+    }
+
+    private AstNode createNumericLiteral(int tt, boolean isProperty) {
+        String s = ts.getString();
+        if (this.inUseStrictDirective && ts.isNumericOldOctal()) {
+            if (compilerEnv.getLanguageVersion() >= Context.VERSION_ES6 || !isProperty) {
+                if (tt == Token.BIGINT) {
+                    reportError("msg.no.old.octal.bigint");
+                } else {
+                    reportError("msg.no.old.octal.strict");
+                }
+            }
+        }
+        if (compilerEnv.getLanguageVersion() >= Context.VERSION_ES6 || !isProperty) {
+            if (ts.isNumericBinary()) {
+                s = "0b" + s;
+            } else if (ts.isNumericOldOctal()) {
+                s = "0" + s;
+            } else if (ts.isNumericOctal()) {
+                s = "0o" + s;
+            } else if (ts.isNumericHex()) {
+                s = "0x" + s;
+            }
+        }
+        if (tt == Token.BIGINT) {
+            return new BigIntLiteral(ts.tokenBeg, s + "n", ts.getBigInt());
+        } else {
+            return new NumberLiteral(ts.tokenBeg, s, ts.getNumber());
+        }
+    }
+
     protected void checkActivationName(String name, int token) {
         if (!insideFunction()) {
             return;
         }
         boolean activation = false;
-        if ("arguments".equals(name) &&
-            // An arrow function not generate arguments. So it not need activation.
-            ((FunctionNode)currentScriptOrFn).getFunctionType() != FunctionNode.ARROW_FUNCTION) {
+        if ("arguments".equals(name)
+                &&
+                // An arrow function not generate arguments. So it not need activation.
+                ((FunctionNode) currentScriptOrFn).getFunctionType()
+                        != FunctionNode.ARROW_FUNCTION) {
             activation = true;
         } else if (compilerEnv.getActivationNames() != null
                 && compilerEnv.getActivationNames().contains(name)) {
             activation = true;
         } else if ("length".equals(name)) {
-            if (token == Token.GETPROP
-                && compilerEnv.getLanguageVersion() == Context.VERSION_1_2)
-            {
+            if (token == Token.GETPROP && compilerEnv.getLanguageVersion() == Context.VERSION_1_2) {
                 // Use of "length" in 1.2 requires an activation object.
                 activation = true;
             }
@@ -3699,35 +3771,32 @@
 
     protected void setRequiresActivation() {
         if (insideFunction()) {
-            ((FunctionNode)currentScriptOrFn).setRequiresActivation();
+            ((FunctionNode) currentScriptOrFn).setRequiresActivation();
         }
     }
 
     private void checkCallRequiresActivation(AstNode pn) {
-        if ((pn.getType() == Token.NAME
-             && "eval".equals(((Name)pn).getIdentifier()))
-            || (pn.getType() == Token.GETPROP &&
-                "eval".equals(((PropertyGet)pn).getProperty().getIdentifier())))
+        if ((pn.getType() == Token.NAME && "eval".equals(((Name) pn).getIdentifier()))
+                || (pn.getType() == Token.GETPROP
+                        && "eval".equals(((PropertyGet) pn).getProperty().getIdentifier())))
             setRequiresActivation();
     }
 
     protected void setIsGenerator() {
         if (insideFunction()) {
-            ((FunctionNode)currentScriptOrFn).setIsGenerator();
+            ((FunctionNode) currentScriptOrFn).setIsGenerator();
         }
     }
 
-    private void checkBadIncDec(UnaryExpression expr) {
+    private void checkBadIncDec(UpdateExpression expr) {
         AstNode op = removeParens(expr.getOperand());
         int tt = op.getType();
         if (!(tt == Token.NAME
-              || tt == Token.GETPROP
-              || tt == Token.GETELEM
-              || tt == Token.GET_REF
-              || tt == Token.CALL))
-            reportError(expr.getType() == Token.INC
-                        ? "msg.bad.incr"
-                        : "msg.bad.decr");
+                || tt == Token.GETPROP
+                || tt == Token.GETELEM
+                || tt == Token.GET_REF
+                || tt == Token.CALL))
+            reportError(expr.getType() == Token.INC ? "msg.bad.incr" : "msg.bad.decr");
     }
 
     private ErrorNode makeErrorNode() {
@@ -3737,7 +3806,7 @@
     }
 
     // Return end of node.  Assumes node does NOT have a parent yet.
-    private int nodeEnd(AstNode node) {
+    private static int nodeEnd(AstNode node) {
         return node.getPosition() + node.getLength();
     }
 
@@ -3748,17 +3817,14 @@
     }
 
     /**
-     * Return the file offset of the beginning of the input source line
-     * containing the passed position.
+     * Return the file offset of the beginning of the input source line containing the passed
+     * position.
      *
-     * @param pos an offset into the input source stream.  If the offset
-     * is negative, it's converted to 0, and if it's beyond the end of
-     * the source buffer, the last source position is used.
-     *
-     * @return the offset of the beginning of the line containing pos
-     * (i.e. 1+ the offset of the first preceding newline).  Returns -1
-     * if the {@link CompilerEnvirons} is not set to ide-mode,
-     * and {@link #parse(java.io.Reader,String,int)} was used.
+     * @param pos an offset into the input source stream. If the offset is negative, it's converted
+     *     to 0, and if it's beyond the end of the source buffer, the last source position is used.
+     * @return the offset of the beginning of the line containing pos (i.e. 1+ the offset of the
+     *     first preceding newline). Returns -1 if the {@link CompilerEnvirons} is not set to
+     *     ide-mode, and {@link #parse(java.io.Reader,String,int)} was used.
      */
     private int lineBeginningFor(int pos) {
         if (sourceChars == null) {
@@ -3790,12 +3856,9 @@
             // this code originally called lineBeginningFor() and in order to
             // preserve its different line-offset handling, we need to special
             // case ide-mode here
-            int beg = compilerEnv.isIdeMode()
-                      ? Math.max(pos, end - linep[1])
-                      : pos;
+            int beg = compilerEnv.isIdeMode() ? Math.max(pos, end - linep[1]) : pos;
             if (line != null) {
-                addStrictWarning("msg.missing.semi", "", beg, end - beg,
-                                 linep[0], line, linep[1]);
+                addStrictWarning("msg.missing.semi", "", beg, end - beg, linep[0], line, linep[1]);
             } else {
                 // no line information available, report warning at current line
                 addStrictWarning("msg.missing.semi", "", beg, end - beg);
@@ -3807,37 +3870,20 @@
         if (compilerEnv.getWarnTrailingComma()) {
             // back up from comma to beginning of line or array/objlit
             if (!elems.isEmpty()) {
-                pos = ((AstNode)elems.get(0)).getPosition();
+                pos = ((AstNode) elems.get(0)).getPosition();
             }
             pos = Math.max(pos, lineBeginningFor(commaPos));
             addWarning("msg.extra.trailing.comma", pos, commaPos - pos);
         }
     }
 
-
-    private String readFully(Reader reader) throws IOException {
-        BufferedReader in = new BufferedReader(reader);
-        try {
-            char[] cbuf = new char[1024];
-            StringBuilder sb = new StringBuilder(1024);
-            int bytes_read;
-            while ((bytes_read = in.read(cbuf, 0, 1024)) != -1) {
-                sb.append(cbuf, 0, bytes_read);
-            }
-            return sb.toString();
-        } finally {
-            in.close();
-        }
-    }
-
     // helps reduce clutter in the already-large function() method
-    protected class PerFunctionVariables
-    {
+    protected class PerFunctionVariables {
         private ScriptNode savedCurrentScriptOrFn;
         private Scope savedCurrentScope;
         private int savedEndFlags;
         private boolean savedInForInit;
-        private Map<String,LabeledStatement> savedLabelSet;
+        private Map<String, LabeledStatement> savedLabelSet;
         private List<Loop> savedLoopSet;
         private List<Jump> savedLoopAndSwitchSet;
 
@@ -3876,33 +3922,26 @@
     }
 
     /**
-     * Given a destructuring assignment with a left hand side parsed
-     * as an array or object literal and a right hand side expression,
-     * rewrite as a series of assignments to the variables defined in
-     * left from property accesses to the expression on the right.
+     * Given a destructuring assignment with a left hand side parsed as an array or object literal
+     * and a right hand side expression, rewrite as a series of assignments to the variables defined
+     * in left from property accesses to the expression on the right.
+     *
      * @param type declaration type: Token.VAR or Token.LET or -1
-     * @param left array or object literal containing NAME nodes for
-     *        variables to assign
+     * @param left array or object literal containing NAME nodes for variables to assign
      * @param right expression to assign from
-     * @return expression that performs a series of assignments to
-     *         the variables defined in left
+     * @return expression that performs a series of assignments to the variables defined in left
      */
-    Node createDestructuringAssignment(int type, Node left, Node right)
-    {
+    Node createDestructuringAssignment(int type, Node left, Node right) {
         String tempName = currentScriptOrFn.getNextTempName();
-        Node result = destructuringAssignmentHelper(type, left, right,
-            tempName);
+        Node result = destructuringAssignmentHelper(type, left, right, tempName);
         Node comma = result.getLastChild();
         comma.addChildToBack(createName(tempName));
         return result;
     }
 
-    Node destructuringAssignmentHelper(int variableType, Node left,
-                                       Node right, String tempName)
-    {
+    Node destructuringAssignmentHelper(int variableType, Node left, Node right, String tempName) {
         Scope result = createScopeNode(Token.LETEXPR, left.getLineno());
-        result.addChildToFront(new Node(Token.LET,
-            createName(Token.NAME, tempName, right)));
+        result.addChildToFront(new Node(Token.LET, createName(Token.NAME, tempName, right)));
         try {
             pushScope(result);
             defineSymbol(Token.LET, tempName, true);
@@ -3914,28 +3953,36 @@
         List<String> destructuringNames = new ArrayList<String>();
         boolean empty = true;
         switch (left.getType()) {
-          case Token.ARRAYLIT:
-              empty = destructuringArray((ArrayLiteral)left,
-                                         variableType, tempName, comma,
-                                         destructuringNames);
-              break;
-          case Token.OBJECTLIT:
-              empty = destructuringObject((ObjectLiteral)left,
-                                          variableType, tempName, comma,
-                                          destructuringNames);
-              break;
-          case Token.GETPROP:
-          case Token.GETELEM:
-              switch (variableType) {
-                  case Token.CONST:
-                  case Token.LET:
-                  case Token.VAR:
-                      reportError("msg.bad.assign.left");
-              }
-              comma.addChildToBack(simpleAssignment(left, createName(tempName)));
-              break;
-          default:
-              reportError("msg.bad.assign.left");
+            case Token.ARRAYLIT:
+                empty =
+                        destructuringArray(
+                                (ArrayLiteral) left,
+                                variableType,
+                                tempName,
+                                comma,
+                                destructuringNames);
+                break;
+            case Token.OBJECTLIT:
+                empty =
+                        destructuringObject(
+                                (ObjectLiteral) left,
+                                variableType,
+                                tempName,
+                                comma,
+                                destructuringNames);
+                break;
+            case Token.GETPROP:
+            case Token.GETELEM:
+                switch (variableType) {
+                    case Token.CONST:
+                    case Token.LET:
+                    case Token.VAR:
+                        reportError("msg.bad.assign.left");
+                }
+                comma.addChildToBack(simpleAssignment(left, createName(tempName)));
+                break;
+            default:
+                reportError("msg.bad.assign.left");
         }
         if (empty) {
             // Don't want a COMMA node with no children. Just add a zero.
@@ -3945,40 +3992,33 @@
         return result;
     }
 
-    boolean destructuringArray(ArrayLiteral array,
-                               int variableType,
-                               String tempName,
-                               Node parent,
-                               List<String> destructuringNames)
-    {
+    boolean destructuringArray(
+            ArrayLiteral array,
+            int variableType,
+            String tempName,
+            Node parent,
+            List<String> destructuringNames) {
         boolean empty = true;
-        int setOp = variableType == Token.CONST
-            ? Token.SETCONST : Token.SETNAME;
+        int setOp = variableType == Token.CONST ? Token.SETCONST : Token.SETNAME;
         int index = 0;
         for (AstNode n : array.getElements()) {
             if (n.getType() == Token.EMPTY) {
                 index++;
                 continue;
             }
-            Node rightElem = new Node(Token.GETELEM,
-                                      createName(tempName),
-                                      createNumber(index));
+            Node rightElem = new Node(Token.GETELEM, createName(tempName), createNumber(index));
             if (n.getType() == Token.NAME) {
                 String name = n.getString();
-                parent.addChildToBack(new Node(setOp,
-                                              createName(Token.BINDNAME,
-                                                         name, null),
-                                              rightElem));
+                parent.addChildToBack(
+                        new Node(setOp, createName(Token.BINDNAME, name, null), rightElem));
                 if (variableType != -1) {
                     defineSymbol(variableType, name, true);
                     destructuringNames.add(name);
                 }
             } else {
-                parent.addChildToBack
-                    (destructuringAssignmentHelper
-                     (variableType, n,
-                      rightElem,
-                      currentScriptOrFn.getNextTempName()));
+                parent.addChildToBack(
+                        destructuringAssignmentHelper(
+                                variableType, n, rightElem, currentScriptOrFn.getNextTempName()));
             }
             index++;
             empty = false;
@@ -3986,15 +4026,14 @@
         return empty;
     }
 
-    boolean destructuringObject(ObjectLiteral node,
-                                int variableType,
-                                String tempName,
-                                Node parent,
-                                List<String> destructuringNames)
-    {
+    boolean destructuringObject(
+            ObjectLiteral node,
+            int variableType,
+            String tempName,
+            Node parent,
+            List<String> destructuringNames) {
         boolean empty = true;
-        int setOp = variableType == Token.CONST
-            ? Token.SETCONST : Token.SETNAME;
+        int setOp = variableType == Token.CONST ? Token.SETCONST : Token.SETNAME;
 
         for (ObjectProperty prop : node.getElements()) {
             int lineno = 0;
@@ -4002,18 +4041,18 @@
             // when executing regression tests, and in those cases the
             // tokenStream isn't set.  Deal with it.
             if (ts != null) {
-              lineno = ts.lineno;
+                lineno = ts.lineno;
             }
             AstNode id = prop.getLeft();
             Node rightElem = null;
             if (id instanceof Name) {
-                Node s = Node.newString(((Name)id).getIdentifier());
+                Node s = Node.newString(((Name) id).getIdentifier());
                 rightElem = new Node(Token.GETPROP, createName(tempName), s);
             } else if (id instanceof StringLiteral) {
-                Node s = Node.newString(((StringLiteral)id).getValue());
+                Node s = Node.newString(((StringLiteral) id).getValue());
                 rightElem = new Node(Token.GETPROP, createName(tempName), s);
             } else if (id instanceof NumberLiteral) {
-                Node s = createNumber((int)((NumberLiteral)id).getNumber());
+                Node s = createNumber((int) ((NumberLiteral) id).getNumber());
                 rightElem = new Node(Token.GETELEM, createName(tempName), s);
             } else {
                 throw codeBug();
@@ -4021,20 +4060,20 @@
             rightElem.setLineno(lineno);
             AstNode value = prop.getRight();
             if (value.getType() == Token.NAME) {
-                String name = ((Name)value).getIdentifier();
-                parent.addChildToBack(new Node(setOp,
-                                              createName(Token.BINDNAME,
-                                                         name, null),
-                                              rightElem));
+                String name = ((Name) value).getIdentifier();
+                parent.addChildToBack(
+                        new Node(setOp, createName(Token.BINDNAME, name, null), rightElem));
                 if (variableType != -1) {
                     defineSymbol(variableType, name, true);
                     destructuringNames.add(name);
                 }
             } else {
-                parent.addChildToBack
-                    (destructuringAssignmentHelper
-                     (variableType, value, rightElem,
-                      currentScriptOrFn.getNextTempName()));
+                parent.addChildToBack(
+                        destructuringAssignmentHelper(
+                                variableType,
+                                value,
+                                rightElem,
+                                currentScriptOrFn.getNextTempName()));
             }
             empty = false;
         }
@@ -4049,8 +4088,7 @@
     protected Node createName(int type, String name, Node child) {
         Node result = createName(name);
         result.setType(type);
-        if (child != null)
-            result.addChildToBack(child);
+        if (child != null) result.addChildToBack(child);
         return result;
     }
 
@@ -4059,15 +4097,15 @@
     }
 
     /**
-     * Create a node that can be used to hold lexically scoped variable
-     * definitions (via let declarations).
+     * Create a node that can be used to hold lexically scoped variable definitions (via let
+     * declarations).
      *
      * @param token the token of the node to create
      * @param lineno line number of source
      * @return the created node
      */
     protected Scope createScopeNode(int token, int lineno) {
-        Scope scope =new Scope();
+        Scope scope = new Scope();
         scope.setType(token);
         scope.setLineno(lineno);
         return scope;
@@ -4098,53 +4136,55 @@
     protected Node simpleAssignment(Node left, Node right) {
         int nodeType = left.getType();
         switch (nodeType) {
-          case Token.NAME:
-              String name = ((Name) left).getIdentifier();
-              if (inUseStrictDirective &&
-                  ("eval".equals(name) || "arguments".equals(name)))
-              {
-                  reportError("msg.bad.id.strict", name);
-              }
-              left.setType(Token.BINDNAME);
-              return new Node(Token.SETNAME, left, right);
-
-          case Token.GETPROP:
-          case Token.GETELEM: {
-              Node obj, id;
-              // If it's a PropertyGet or ElementGet, we're in the parse pass.
-              // We could alternately have PropertyGet and ElementGet
-              // override getFirstChild/getLastChild and return the appropriate
-              // field, but that seems just as ugly as this casting.
-              if (left instanceof PropertyGet) {
-                  obj = ((PropertyGet)left).getTarget();
-                  id = ((PropertyGet)left).getProperty();
-              } else if (left instanceof ElementGet) {
-                  obj = ((ElementGet)left).getTarget();
-                  id = ((ElementGet)left).getElement();
-              } else {
-                  // This branch is called during IRFactory transform pass.
-                  obj = left.getFirstChild();
-                  id = left.getLastChild();
-              }
-              int type;
-              if (nodeType == Token.GETPROP) {
-                  type = Token.SETPROP;
-                  // TODO(stevey) - see https://bugzilla.mozilla.org/show_bug.cgi?id=492036
-                  // The new AST code generates NAME tokens for GETPROP ids where the old parser
-                  // generated STRING nodes. If we don't set the type to STRING below, this will
-                  // cause java.lang.VerifyError in codegen for code like
-                  // "var obj={p:3};[obj.p]=[9];"
-                  id.setType(Token.STRING);
-              } else {
-                  type = Token.SETELEM;
-              }
-              return new Node(type, obj, id, right);
-          }
-          case Token.GET_REF: {
-              Node ref = left.getFirstChild();
-              checkMutableReference(ref);
-              return new Node(Token.SET_REF, ref, right);
-          }
+            case Token.NAME:
+                String name = ((Name) left).getIdentifier();
+                if (inUseStrictDirective && ("eval".equals(name) || "arguments".equals(name))) {
+                    reportError("msg.bad.id.strict", name);
+                }
+                left.setType(Token.BINDNAME);
+                return new Node(Token.SETNAME, left, right);
+
+            case Token.GETPROP:
+            case Token.GETELEM:
+                {
+                    Node obj, id;
+                    // If it's a PropertyGet or ElementGet, we're in the parse pass.
+                    // We could alternately have PropertyGet and ElementGet
+                    // override getFirstChild/getLastChild and return the appropriate
+                    // field, but that seems just as ugly as this casting.
+                    if (left instanceof PropertyGet) {
+                        obj = ((PropertyGet) left).getTarget();
+                        id = ((PropertyGet) left).getProperty();
+                    } else if (left instanceof ElementGet) {
+                        obj = ((ElementGet) left).getTarget();
+                        id = ((ElementGet) left).getElement();
+                    } else {
+                        // This branch is called during IRFactory transform pass.
+                        obj = left.getFirstChild();
+                        id = left.getLastChild();
+                    }
+                    int type;
+                    if (nodeType == Token.GETPROP) {
+                        type = Token.SETPROP;
+                        // TODO(stevey) - see https://bugzilla.mozilla.org/show_bug.cgi?id=492036
+                        // The new AST code generates NAME tokens for GETPROP ids where the old
+                        // parser
+                        // generated STRING nodes. If we don't set the type to STRING below, this
+                        // will
+                        // cause java.lang.VerifyError in codegen for code like
+                        // "var obj={p:3};[obj.p]=[9];"
+                        id.setType(Token.STRING);
+                    } else {
+                        type = Token.SETELEM;
+                    }
+                    return new Node(type, obj, id, right);
+                }
+            case Token.GET_REF:
+                {
+                    Node ref = left.getFirstChild();
+                    checkMutableReference(ref);
+                    return new Node(Token.SET_REF, ref, right);
+                }
         }
 
         throw codeBug();
@@ -4160,26 +4200,28 @@
     // remove any ParenthesizedExpression wrappers
     protected AstNode removeParens(AstNode node) {
         while (node instanceof ParenthesizedExpression) {
-            node = ((ParenthesizedExpression)node).getExpression();
+            node = ((ParenthesizedExpression) node).getExpression();
         }
         return node;
     }
 
     void markDestructuring(AstNode node) {
         if (node instanceof DestructuringForm) {
-            ((DestructuringForm)node).setIsDestructuring(true);
+            ((DestructuringForm) node).setIsDestructuring(true);
         } else if (node instanceof ParenthesizedExpression) {
-            markDestructuring(((ParenthesizedExpression)node).getExpression());
+            markDestructuring(((ParenthesizedExpression) node).getExpression());
         }
     }
 
     // throw a failed-assertion with some helpful debugging info
-    private RuntimeException codeBug()
-        throws RuntimeException
-    {
-        throw Kit.codeBug("ts.cursor=" + ts.cursor
-                          + ", ts.tokenBeg=" + ts.tokenBeg
-                          + ", currentToken=" + currentToken);
+    private RuntimeException codeBug() throws RuntimeException {
+        throw Kit.codeBug(
+                "ts.cursor="
+                        + ts.cursor
+                        + ", ts.tokenBeg="
+                        + ts.tokenBeg
+                        + ", currentToken="
+                        + currentToken);
     }
 
     public void setDefaultUseStrictDirective(boolean useStrict) {
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/PolicySecurityController.java rhino-1.7.14/src/org/mozilla/javascript/PolicySecurityController.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/PolicySecurityController.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/PolicySecurityController.java	2022-01-06 22:57:21.000000000 +0100
@@ -58,11 +58,13 @@
             this.codeSource = codeSource;
         }
 
+        @Override
         public Class<?> defineClass(String name, byte[] data)
         {
             return defineClass(name, data, 0, data.length, codeSource);
         }
 
+        @Override
         public void linkClass(Class<?> cl)
         {
             resolveClass(cl);
@@ -76,6 +78,7 @@
         return (Loader)AccessController.doPrivileged(
             new PrivilegedAction<Object>()
             {
+                @Override
                 public Object run()
                 {
                     return new Loader(parent, (CodeSource)securityDomain);
@@ -100,6 +103,7 @@
         // runtime permission
         final ClassLoader classLoader = (ClassLoader)AccessController.doPrivileged(
             new PrivilegedAction<Object>() {
+                @Override
                 public Object run() {
                     return cx.getApplicationClassLoader();
                 }
@@ -130,6 +134,7 @@
                     caller = (SecureCaller)AccessController.doPrivileged(
                             new PrivilegedExceptionAction<Object>()
                     {
+                        @Override
                         public Object run() throws Exception
                         {
                             Loader loader = new Loader(classLoader,
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/Ref.java rhino-1.7.14/src/org/mozilla/javascript/Ref.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/Ref.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/Ref.java	2022-01-06 22:57:21.000000000 +0100
@@ -14,9 +14,9 @@
  */
 public abstract class Ref implements Serializable
 {
-    
-    static final long serialVersionUID = 4044540354730911424L;
-    
+
+    private static final long serialVersionUID = 4044540354730911424L;
+
     public boolean has(Context cx)
     {
         return true;
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/regexp/NativeRegExpCallable.java rhino-1.7.14/src/org/mozilla/javascript/regexp/NativeRegExpCallable.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/regexp/NativeRegExpCallable.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/regexp/NativeRegExpCallable.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,29 @@
+package org.mozilla.javascript.regexp;
+
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Function;
+import org.mozilla.javascript.Scriptable;
+
+/**
+ * Legacy implementation of RegExp was callable, this class exists to preserve this functionality
+ */
+class NativeRegExpCallable extends NativeRegExp implements Function {
+
+    NativeRegExpCallable(Scriptable scope, RECompiled compiled) {
+        super(scope, compiled);
+    }
+
+    NativeRegExpCallable() {
+        super();
+    }
+
+    @Override
+    public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        return execSub(cx, scope, args, MATCH);
+    }
+
+    @Override
+    public Scriptable construct(Context cx, Scriptable scope, Object[] args) {
+        return (Scriptable) execSub(cx, scope, args, MATCH);
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/regexp/NativeRegExpCtor.java rhino-1.7.14/src/org/mozilla/javascript/regexp/NativeRegExpCtor.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/regexp/NativeRegExpCtor.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/regexp/NativeRegExpCtor.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,32 +6,30 @@
 
 package org.mozilla.javascript.regexp;
 
-import org.mozilla.javascript.*;
+import org.mozilla.javascript.BaseFunction;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.TopLevel;
+import org.mozilla.javascript.Undefined;
 
 /**
  * This class implements the RegExp constructor native object.
  *
- * Revision History:
- * Implementation in C by Brendan Eich
- * Initial port to Java by Norris Boyd from jsregexp.c version 1.36
- * Merged up to version 1.38, which included Unicode support.
- * Merged bug fixes in version 1.39.
- * Merged JSFUN13_BRANCH changes up to 1.32.2.11
+ * <p>Revision History: Implementation in C by Brendan Eich Initial port to Java by Norris Boyd from
+ * jsregexp.c version 1.36 Merged up to version 1.38, which included Unicode support. Merged bug
+ * fixes in version 1.39. Merged JSFUN13_BRANCH changes up to 1.32.2.11
  *
  * @author Brendan Eich
  * @author Norris Boyd
  */
-class NativeRegExpCtor extends BaseFunction
-{
-    static final long serialVersionUID = -5733330028285400526L;
+class NativeRegExpCtor extends BaseFunction {
+    private static final long serialVersionUID = -5733330028285400526L;
 
-    NativeRegExpCtor()
-    {
-    }
+    NativeRegExpCtor() {}
 
     @Override
-    public String getFunctionName()
-    {
+    public String getFunctionName() {
         return "RegExp";
     }
 
@@ -46,212 +44,239 @@
     }
 
     @Override
-    public Object call(Context cx, Scriptable scope, Scriptable thisObj,
-                       Object[] args)
-    {
-        if (args.length > 0 && args[0] instanceof NativeRegExp &&
-            (args.length == 1 || args[1] == Undefined.instance))
-        {
+    public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+        if (args.length > 0
+                && args[0] instanceof NativeRegExp
+                && (args.length == 1 || args[1] == Undefined.instance)) {
             return args[0];
         }
         return construct(cx, scope, args);
     }
 
     @Override
-    public Scriptable construct(Context cx, Scriptable scope, Object[] args)
-    {
-        NativeRegExp re = new NativeRegExp();
+    public Scriptable construct(Context cx, Scriptable scope, Object[] args) {
+        NativeRegExp re = NativeRegExpInstantiator.withLanguageVersion(cx.getLanguageVersion());
         re.compile(cx, scope, args);
         ScriptRuntime.setBuiltinProtoAndParent(re, scope, TopLevel.Builtins.RegExp);
         return re;
     }
 
-    private static RegExpImpl getImpl()
-    {
+    private static RegExpImpl getImpl() {
         Context cx = Context.getCurrentContext();
         return (RegExpImpl) ScriptRuntime.getRegExpProxy(cx);
     }
 
-// #string_id_map#
-
-    private static final int
-        Id_multiline     = 1,
-        Id_STAR          = 2,  // #string=$*#
-
-        Id_input         = 3,
-        Id_UNDERSCORE    = 4,  // #string=$_#
-
-        Id_lastMatch     = 5,
-        Id_AMPERSAND     = 6,  // #string=$&#
-
-        Id_lastParen     = 7,
-        Id_PLUS          = 8,  // #string=$+#
-
-        Id_leftContext   = 9,
-        Id_BACK_QUOTE    = 10, // #string=$`#
-
-        Id_rightContext  = 11,
-        Id_QUOTE         = 12, // #string=$'#
-
-        DOLLAR_ID_BASE   = 12;
-
-    private static final int
-        Id_DOLLAR_1 = DOLLAR_ID_BASE + 1, // #string=$1#
-        Id_DOLLAR_2 = DOLLAR_ID_BASE + 2, // #string=$2#
-        Id_DOLLAR_3 = DOLLAR_ID_BASE + 3, // #string=$3#
-        Id_DOLLAR_4 = DOLLAR_ID_BASE + 4, // #string=$4#
-        Id_DOLLAR_5 = DOLLAR_ID_BASE + 5, // #string=$5#
-        Id_DOLLAR_6 = DOLLAR_ID_BASE + 6, // #string=$6#
-        Id_DOLLAR_7 = DOLLAR_ID_BASE + 7, // #string=$7#
-        Id_DOLLAR_8 = DOLLAR_ID_BASE + 8, // #string=$8#
-        Id_DOLLAR_9 = DOLLAR_ID_BASE + 9, // #string=$9#
-
-        MAX_INSTANCE_ID = DOLLAR_ID_BASE + 9;
+    private static final int Id_multiline = 1,
+            Id_STAR = 2,
+            Id_input = 3,
+            Id_UNDERSCORE = 4,
+            Id_lastMatch = 5,
+            Id_AMPERSAND = 6,
+            Id_lastParen = 7,
+            Id_PLUS = 8,
+            Id_leftContext = 9,
+            Id_BACK_QUOTE = 10,
+            Id_rightContext = 11,
+            Id_QUOTE = 12,
+            DOLLAR_ID_BASE = 12;
+    private static final int Id_DOLLAR_1 = DOLLAR_ID_BASE + 1,
+            Id_DOLLAR_2 = DOLLAR_ID_BASE + 2,
+            Id_DOLLAR_3 = DOLLAR_ID_BASE + 3,
+            Id_DOLLAR_4 = DOLLAR_ID_BASE + 4,
+            Id_DOLLAR_5 = DOLLAR_ID_BASE + 5,
+            Id_DOLLAR_6 = DOLLAR_ID_BASE + 6,
+            Id_DOLLAR_7 = DOLLAR_ID_BASE + 7,
+            Id_DOLLAR_8 = DOLLAR_ID_BASE + 8,
+            Id_DOLLAR_9 = DOLLAR_ID_BASE + 9,
+            MAX_INSTANCE_ID = DOLLAR_ID_BASE + 9;
 
     @Override
-    protected int getMaxInstanceId()
-    {
+    protected int getMaxInstanceId() {
         return super.getMaxInstanceId() + MAX_INSTANCE_ID;
     }
 
     @Override
     protected int findInstanceIdInfo(String s) {
         int id;
-// #generated# Last update: 2001-05-24 16:09:31 GMT+02:00
-        L0: { id = 0; String X = null; int c;
-            L: switch (s.length()) {
-            case 2: switch (s.charAt(1)) {
-                case '&': if (s.charAt(0)=='$') {id=Id_AMPERSAND; break L0;} break L;
-                case '\'': if (s.charAt(0)=='$') {id=Id_QUOTE; break L0;} break L;
-                case '*': if (s.charAt(0)=='$') {id=Id_STAR; break L0;} break L;
-                case '+': if (s.charAt(0)=='$') {id=Id_PLUS; break L0;} break L;
-                case '1': if (s.charAt(0)=='$') {id=Id_DOLLAR_1; break L0;} break L;
-                case '2': if (s.charAt(0)=='$') {id=Id_DOLLAR_2; break L0;} break L;
-                case '3': if (s.charAt(0)=='$') {id=Id_DOLLAR_3; break L0;} break L;
-                case '4': if (s.charAt(0)=='$') {id=Id_DOLLAR_4; break L0;} break L;
-                case '5': if (s.charAt(0)=='$') {id=Id_DOLLAR_5; break L0;} break L;
-                case '6': if (s.charAt(0)=='$') {id=Id_DOLLAR_6; break L0;} break L;
-                case '7': if (s.charAt(0)=='$') {id=Id_DOLLAR_7; break L0;} break L;
-                case '8': if (s.charAt(0)=='$') {id=Id_DOLLAR_8; break L0;} break L;
-                case '9': if (s.charAt(0)=='$') {id=Id_DOLLAR_9; break L0;} break L;
-                case '_': if (s.charAt(0)=='$') {id=Id_UNDERSCORE; break L0;} break L;
-                case '`': if (s.charAt(0)=='$') {id=Id_BACK_QUOTE; break L0;} break L;
-                } break L;
-            case 5: X="input";id=Id_input; break L;
-            case 9: c=s.charAt(4);
-                if (c=='M') { X="lastMatch";id=Id_lastMatch; }
-                else if (c=='P') { X="lastParen";id=Id_lastParen; }
-                else if (c=='i') { X="multiline";id=Id_multiline; }
-                break L;
-            case 11: X="leftContext";id=Id_leftContext; break L;
-            case 12: X="rightContext";id=Id_rightContext; break L;
-            }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
+        switch (s) {
+            case "multiline":
+                id = Id_multiline;
+                break;
+            case "$*":
+                id = Id_STAR;
+                break;
+            case "input":
+                id = Id_input;
+                break;
+            case "$_":
+                id = Id_UNDERSCORE;
+                break;
+            case "lastMatch":
+                id = Id_lastMatch;
+                break;
+            case "$&":
+                id = Id_AMPERSAND;
+                break;
+            case "lastParen":
+                id = Id_lastParen;
+                break;
+            case "$+":
+                id = Id_PLUS;
+                break;
+            case "leftContext":
+                id = Id_leftContext;
+                break;
+            case "$`":
+                id = Id_BACK_QUOTE;
+                break;
+            case "rightContext":
+                id = Id_rightContext;
+                break;
+            case "$'":
+                id = Id_QUOTE;
+                break;
+            case "$1":
+                id = Id_DOLLAR_1;
+                break;
+            case "$2":
+                id = Id_DOLLAR_2;
+                break;
+            case "$3":
+                id = Id_DOLLAR_3;
+                break;
+            case "$4":
+                id = Id_DOLLAR_4;
+                break;
+            case "$5":
+                id = Id_DOLLAR_5;
+                break;
+            case "$6":
+                id = Id_DOLLAR_6;
+                break;
+            case "$7":
+                id = Id_DOLLAR_7;
+                break;
+            case "$8":
+                id = Id_DOLLAR_8;
+                break;
+            case "$9":
+                id = Id_DOLLAR_9;
+                break;
+            default:
+                id = 0;
+                break;
         }
-// #/generated#
 
         if (id == 0) return super.findInstanceIdInfo(s);
 
         int attr;
         switch (id) {
-          case Id_multiline:
-            attr = multilineAttr;
-            break;
-          case Id_STAR:
-            attr = starAttr;
-            break;
-          case Id_input:
-            attr = inputAttr;
-            break;
-          case Id_UNDERSCORE:
-            attr = underscoreAttr;
-            break;
-          default:
-            attr = PERMANENT | READONLY;
-            break;
+            case Id_multiline:
+                attr = multilineAttr;
+                break;
+            case Id_STAR:
+                attr = starAttr;
+                break;
+            case Id_input:
+                attr = inputAttr;
+                break;
+            case Id_UNDERSCORE:
+                attr = underscoreAttr;
+                break;
+            default:
+                attr = PERMANENT | READONLY;
+                break;
         }
 
         return instanceIdInfo(attr, super.getMaxInstanceId() + id);
     }
 
-// #/string_id_map#
-
     @Override
-    protected String getInstanceIdName(int id)
-    {
+    protected String getInstanceIdName(int id) {
         int shifted = id - super.getMaxInstanceId();
         if (1 <= shifted && shifted <= MAX_INSTANCE_ID) {
             switch (shifted) {
-                case Id_multiline:    return "multiline";
-                case Id_STAR:         return "$*";
-
-                case Id_input:        return "input";
-                case Id_UNDERSCORE:   return "$_";
-
-                case Id_lastMatch:    return "lastMatch";
-                case Id_AMPERSAND:    return "$&";
-
-                case Id_lastParen:    return "lastParen";
-                case Id_PLUS:         return "$+";
-
-                case Id_leftContext:  return "leftContext";
-                case Id_BACK_QUOTE:   return "$`";
-
-                case Id_rightContext: return "rightContext";
-                case Id_QUOTE:        return "$'";
+                case Id_multiline:
+                    return "multiline";
+                case Id_STAR:
+                    return "$*";
+
+                case Id_input:
+                    return "input";
+                case Id_UNDERSCORE:
+                    return "$_";
+
+                case Id_lastMatch:
+                    return "lastMatch";
+                case Id_AMPERSAND:
+                    return "$&";
+
+                case Id_lastParen:
+                    return "lastParen";
+                case Id_PLUS:
+                    return "$+";
+
+                case Id_leftContext:
+                    return "leftContext";
+                case Id_BACK_QUOTE:
+                    return "$`";
+
+                case Id_rightContext:
+                    return "rightContext";
+                case Id_QUOTE:
+                    return "$'";
             }
             // Must be one of $1..$9, convert to 0..8
             int substring_number = shifted - DOLLAR_ID_BASE - 1;
-            char[] buf = { '$', (char)('1' + substring_number) };
+            char[] buf = {'$', (char) ('1' + substring_number)};
             return new String(buf);
         }
         return super.getInstanceIdName(id);
     }
 
     @Override
-    protected Object getInstanceIdValue(int id)
-    {
+    protected Object getInstanceIdValue(int id) {
         int shifted = id - super.getMaxInstanceId();
         if (1 <= shifted && shifted <= MAX_INSTANCE_ID) {
             RegExpImpl impl = getImpl();
             Object stringResult;
             switch (shifted) {
-              case Id_multiline:
-              case Id_STAR:
-                return ScriptRuntime.wrapBoolean(impl.multiline);
-
-              case Id_input:
-              case Id_UNDERSCORE:
-                stringResult = impl.input;
-                break;
-
-              case Id_lastMatch:
-              case Id_AMPERSAND:
-                stringResult = impl.lastMatch;
-                break;
+                case Id_multiline:
+                case Id_STAR:
+                    return ScriptRuntime.wrapBoolean(impl.multiline);
+
+                case Id_input:
+                case Id_UNDERSCORE:
+                    stringResult = impl.input;
+                    break;
 
-              case Id_lastParen:
-              case Id_PLUS:
-                stringResult = impl.lastParen;
-                break;
+                case Id_lastMatch:
+                case Id_AMPERSAND:
+                    stringResult = impl.lastMatch;
+                    break;
 
-              case Id_leftContext:
-              case Id_BACK_QUOTE:
-                stringResult = impl.leftContext;
-                break;
+                case Id_lastParen:
+                case Id_PLUS:
+                    stringResult = impl.lastParen;
+                    break;
 
-              case Id_rightContext:
-              case Id_QUOTE:
-                stringResult = impl.rightContext;
-                break;
+                case Id_leftContext:
+                case Id_BACK_QUOTE:
+                    stringResult = impl.leftContext;
+                    break;
 
-              default:
-                {
-                    // Must be one of $1..$9, convert to 0..8
-                    int substring_number = shifted - DOLLAR_ID_BASE - 1;
-                    stringResult = impl.getParenSubString(substring_number);
+                case Id_rightContext:
+                case Id_QUOTE:
+                    stringResult = impl.rightContext;
                     break;
-                }
+
+                default:
+                    {
+                        // Must be one of $1..$9, convert to 0..8
+                        int substring_number = shifted - DOLLAR_ID_BASE - 1;
+                        stringResult = impl.getParenSubString(substring_number);
+                        break;
+                    }
             }
             return (stringResult == null) ? "" : stringResult.toString();
         }
@@ -259,8 +284,7 @@
     }
 
     @Override
-    protected void setInstanceIdValue(int id, Object value)
-    {
+    protected void setInstanceIdValue(int id, Object value) {
         int shifted = id - super.getMaxInstanceId();
         switch (shifted) {
             case Id_multiline:
@@ -285,7 +309,7 @@
             default:
                 int substring_number = shifted - DOLLAR_ID_BASE - 1;
                 if (0 <= substring_number && substring_number <= 8) {
-                  return;
+                    return;
                 }
         }
         super.setInstanceIdValue(id, value);
@@ -321,8 +345,8 @@
             default:
                 int substring_number = shifted - DOLLAR_ID_BASE - 1;
                 if (0 <= substring_number && substring_number <= 8) {
-                  // non-configurable + non-writable
-                  return;
+                    // non-configurable + non-writable
+                    return;
                 }
         }
         super.setInstanceIdAttributes(id, attr);
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/regexp/NativeRegExpInstantiator.java rhino-1.7.14/src/org/mozilla/javascript/regexp/NativeRegExpInstantiator.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/regexp/NativeRegExpInstantiator.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/regexp/NativeRegExpInstantiator.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,25 @@
+package org.mozilla.javascript.regexp;
+
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Scriptable;
+
+public class NativeRegExpInstantiator {
+
+    private NativeRegExpInstantiator() {}
+
+    static NativeRegExp withLanguageVersion(int languageVersion) {
+        if (languageVersion < Context.VERSION_ES6) {
+            return new NativeRegExpCallable();
+        } else {
+            return new NativeRegExp();
+        }
+    }
+
+    static NativeRegExp withLanguageVersionScopeCompiled(int languageVersion, Scriptable scope, RECompiled compiled) {
+        if (languageVersion < Context.VERSION_ES6) {
+            return new NativeRegExpCallable(scope, compiled);
+        } else {
+            return new NativeRegExp(scope, compiled);
+        }
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/regexp/NativeRegExp.java rhino-1.7.14/src/org/mozilla/javascript/regexp/NativeRegExp.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/regexp/NativeRegExp.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/regexp/NativeRegExp.java	2022-01-06 22:57:21.000000000 +0100
@@ -7,113 +7,110 @@
 package org.mozilla.javascript.regexp;
 
 import java.io.Serializable;
-
 import org.mozilla.javascript.Context;
-import org.mozilla.javascript.Function;
 import org.mozilla.javascript.IdFunctionObject;
 import org.mozilla.javascript.IdScriptableObject;
 import org.mozilla.javascript.Kit;
 import org.mozilla.javascript.ScriptRuntime;
 import org.mozilla.javascript.Scriptable;
 import org.mozilla.javascript.ScriptableObject;
+import org.mozilla.javascript.Symbol;
+import org.mozilla.javascript.SymbolKey;
 import org.mozilla.javascript.TopLevel;
 import org.mozilla.javascript.Undefined;
 
 /**
  * This class implements the RegExp native object.
  *
- * Revision History:
- * Implementation in C by Brendan Eich
- * Initial port to Java by Norris Boyd from jsregexp.c version 1.36
- * Merged up to version 1.38, which included Unicode support.
- * Merged bug fixes in version 1.39.
- * Merged JSFUN13_BRANCH changes up to 1.32.2.13
+ * <p>Revision History: Implementation in C by Brendan Eich Initial port to Java by Norris Boyd from
+ * jsregexp.c version 1.36 Merged up to version 1.38, which included Unicode support. Merged bug
+ * fixes in version 1.39. Merged JSFUN13_BRANCH changes up to 1.32.2.13
  *
  * @author Brendan Eich
  * @author Norris Boyd
  */
-
-
-
-public class NativeRegExp extends IdScriptableObject implements Function
-{
-    static final long serialVersionUID = 4965263491464903264L;
+public class NativeRegExp extends IdScriptableObject {
+    private static final long serialVersionUID = 4965263491464903264L;
 
     private static final Object REGEXP_TAG = new Object();
 
-    public static final int JSREG_GLOB = 0x1;       // 'g' flag: global
-    public static final int JSREG_FOLD = 0x2;       // 'i' flag: fold
-    public static final int JSREG_MULTILINE = 0x4;  // 'm' flag: multiline
+    public static final int JSREG_GLOB = 0x1; // 'g' flag: global
+    public static final int JSREG_FOLD = 0x2; // 'i' flag: fold
+    public static final int JSREG_MULTILINE = 0x4; // 'm' flag: multiline
 
-    //type of match to perform
+    // type of match to perform
     public static final int TEST = 0;
     public static final int MATCH = 1;
     public static final int PREFIX = 2;
 
     private static final boolean debug = false;
 
-    private static final byte REOP_SIMPLE_START  = 1;  /* start of 'simple opcodes' */
-    private static final byte REOP_EMPTY         = 1;  /* match rest of input against rest of r.e. */
-    private static final byte REOP_BOL           = 2;  /* beginning of input (or line if multiline) */
-    private static final byte REOP_EOL           = 3;  /* end of input (or line if multiline) */
-    private static final byte REOP_WBDRY         = 4;  /* match "" at word boundary */
-    private static final byte REOP_WNONBDRY      = 5;  /* match "" at word non-boundary */
-    private static final byte REOP_DOT           = 6;  /* stands for any character */
-    private static final byte REOP_DIGIT         = 7;  /* match a digit char: [0-9] */
-    private static final byte REOP_NONDIGIT      = 8;  /* match a non-digit char: [^0-9] */
-    private static final byte REOP_ALNUM         = 9;  /* match an alphanumeric char: [0-9a-z_A-Z] */
-    private static final byte REOP_NONALNUM      = 10; /* match a non-alphanumeric char: [^0-9a-z_A-Z] */
-    private static final byte REOP_SPACE         = 11; /* match a whitespace char */
-    private static final byte REOP_NONSPACE      = 12; /* match a non-whitespace char */
-    private static final byte REOP_BACKREF       = 13; /* back-reference (e.g., \1) to a parenthetical */
-    private static final byte REOP_FLAT          = 14; /* match a flat string */
-    private static final byte REOP_FLAT1         = 15; /* match a single char */
-    private static final byte REOP_FLATi         = 16; /* case-independent REOP_FLAT */
-    private static final byte REOP_FLAT1i        = 17; /* case-independent REOP_FLAT1 */
-    private static final byte REOP_UCFLAT1       = 18; /* single Unicode char */
-    private static final byte REOP_UCFLAT1i      = 19; /* case-independent REOP_UCFLAT1 */
-//    private static final byte REOP_UCFLAT        = 20; /* flat Unicode string; len immediate counts chars */
-//    private static final byte REOP_UCFLATi       = 21; /* case-independent REOP_UCFLAT */
-    private static final byte REOP_CLASS         = 22; /* character class with index */
-    private static final byte REOP_NCLASS        = 23; /* negated character class with index */
-    private static final byte REOP_SIMPLE_END    = 23; /* end of 'simple opcodes' */
-    private static final byte REOP_QUANT         = 25; /* quantified atom: atom{1,2} */
-    private static final byte REOP_STAR          = 26; /* zero or more occurrences of kid */
-    private static final byte REOP_PLUS          = 27; /* one or more occurrences of kid */
-    private static final byte REOP_OPT           = 28; /* optional subexpression in kid */
-    private static final byte REOP_LPAREN        = 29; /* left paren bytecode: kid is u.num'th sub-regexp */
-    private static final byte REOP_RPAREN        = 30; /* right paren bytecode */
-    private static final byte REOP_ALT           = 31; /* alternative subexpressions in kid and next */
-    private static final byte REOP_JUMP          = 32; /* for deoptimized closure loops */
-//    private static final byte REOP_DOTSTAR       = 33; /* optimize .* to use a single opcode */
-//    private static final byte REOP_ANCHOR        = 34; /* like .* but skips left context to unanchored r.e. */
-//    private static final byte REOP_EOLONLY       = 35; /* $ not preceded by any pattern */
-//    private static final byte REOP_BACKREFi      = 37; /* case-independent REOP_BACKREF */
-//    private static final byte REOP_LPARENNON     = 40; /* non-capturing version of REOP_LPAREN */
-    private static final byte REOP_ASSERT        = 41; /* zero width positive lookahead assertion */
-    private static final byte REOP_ASSERT_NOT    = 42; /* zero width negative lookahead assertion */
-    private static final byte REOP_ASSERTTEST    = 43; /* sentinel at end of assertion child */
+    private static final byte REOP_SIMPLE_START = 1; /* start of 'simple opcodes' */
+    private static final byte REOP_EMPTY = 1; /* match rest of input against rest of r.e. */
+    private static final byte REOP_BOL = 2; /* beginning of input (or line if multiline) */
+    private static final byte REOP_EOL = 3; /* end of input (or line if multiline) */
+    private static final byte REOP_WBDRY = 4; /* match "" at word boundary */
+    private static final byte REOP_WNONBDRY = 5; /* match "" at word non-boundary */
+    private static final byte REOP_DOT = 6; /* stands for any character */
+    private static final byte REOP_DIGIT = 7; /* match a digit char: [0-9] */
+    private static final byte REOP_NONDIGIT = 8; /* match a non-digit char: [^0-9] */
+    private static final byte REOP_ALNUM = 9; /* match an alphanumeric char: [0-9a-z_A-Z] */
+    private static final byte REOP_NONALNUM = 10; /* match a non-alphanumeric char: [^0-9a-z_A-Z] */
+    private static final byte REOP_SPACE = 11; /* match a whitespace char */
+    private static final byte REOP_NONSPACE = 12; /* match a non-whitespace char */
+    private static final byte REOP_BACKREF = 13; /* back-reference (e.g., \1) to a parenthetical */
+    private static final byte REOP_FLAT = 14; /* match a flat string */
+    private static final byte REOP_FLAT1 = 15; /* match a single char */
+    private static final byte REOP_FLATi = 16; /* case-independent REOP_FLAT */
+    private static final byte REOP_FLAT1i = 17; /* case-independent REOP_FLAT1 */
+    private static final byte REOP_UCFLAT1 = 18; /* single Unicode char */
+    private static final byte REOP_UCFLAT1i = 19; /* case-independent REOP_UCFLAT1 */
+    //    private static final byte REOP_UCFLAT        = 20; /* flat Unicode string; len immediate
+    // counts chars */
+    //    private static final byte REOP_UCFLATi       = 21; /* case-independent REOP_UCFLAT */
+    private static final byte REOP_CLASS = 22; /* character class with index */
+    private static final byte REOP_NCLASS = 23; /* negated character class with index */
+    private static final byte REOP_SIMPLE_END = 23; /* end of 'simple opcodes' */
+    private static final byte REOP_QUANT = 25; /* quantified atom: atom{1,2} */
+    private static final byte REOP_STAR = 26; /* zero or more occurrences of kid */
+    private static final byte REOP_PLUS = 27; /* one or more occurrences of kid */
+    private static final byte REOP_OPT = 28; /* optional subexpression in kid */
+    private static final byte REOP_LPAREN =
+            29; /* left paren bytecode: kid is u.num'th sub-regexp */
+    private static final byte REOP_RPAREN = 30; /* right paren bytecode */
+    private static final byte REOP_ALT = 31; /* alternative subexpressions in kid and next */
+    private static final byte REOP_JUMP = 32; /* for deoptimized closure loops */
+    //    private static final byte REOP_DOTSTAR       = 33; /* optimize .* to use a single opcode
+    // */
+    //    private static final byte REOP_ANCHOR        = 34; /* like .* but skips left context to
+    // unanchored r.e. */
+    //    private static final byte REOP_EOLONLY       = 35; /* $ not preceded by any pattern */
+    //    private static final byte REOP_BACKREFi      = 37; /* case-independent REOP_BACKREF */
+    //    private static final byte REOP_LPARENNON     = 40; /* non-capturing version of REOP_LPAREN
+    // */
+    private static final byte REOP_ASSERT = 41; /* zero width positive lookahead assertion */
+    private static final byte REOP_ASSERT_NOT = 42; /* zero width negative lookahead assertion */
+    private static final byte REOP_ASSERTTEST = 43; /* sentinel at end of assertion child */
     private static final byte REOP_ASSERTNOTTEST = 44; /* sentinel at end of !assertion child */
-    private static final byte REOP_MINIMALSTAR   = 45; /* non-greedy version of * */
-    private static final byte REOP_MINIMALPLUS   = 46; /* non-greedy version of + */
-    private static final byte REOP_MINIMALOPT    = 47; /* non-greedy version of ? */
-    private static final byte REOP_MINIMALQUANT  = 48; /* non-greedy version of {} */
-    private static final byte REOP_ENDCHILD      = 49; /* sentinel at end of quantifier child */
-    private static final byte REOP_REPEAT        = 51; /* directs execution of greedy quantifier */
-    private static final byte REOP_MINIMALREPEAT = 52; /* directs execution of non-greedy quantifier */
-    private static final byte REOP_ALTPREREQ     = 53; /* prerequisite for ALT, either of two chars */
-    private static final byte REOP_ALTPREREQi    = 54; /* case-independent REOP_ALTPREREQ */
-    private static final byte REOP_ALTPREREQ2    = 55; /* prerequisite for ALT, a char or a class */
-//    private static final byte REOP_ENDALT        = 56; /* end of final alternate */
-    private static final byte REOP_END           = 57;
+    private static final byte REOP_MINIMALSTAR = 45; /* non-greedy version of * */
+    private static final byte REOP_MINIMALPLUS = 46; /* non-greedy version of + */
+    private static final byte REOP_MINIMALOPT = 47; /* non-greedy version of ? */
+    private static final byte REOP_MINIMALQUANT = 48; /* non-greedy version of {} */
+    private static final byte REOP_ENDCHILD = 49; /* sentinel at end of quantifier child */
+    private static final byte REOP_REPEAT = 51; /* directs execution of greedy quantifier */
+    private static final byte REOP_MINIMALREPEAT =
+            52; /* directs execution of non-greedy quantifier */
+    private static final byte REOP_ALTPREREQ = 53; /* prerequisite for ALT, either of two chars */
+    private static final byte REOP_ALTPREREQi = 54; /* case-independent REOP_ALTPREREQ */
+    private static final byte REOP_ALTPREREQ2 = 55; /* prerequisite for ALT, a char or a class */
+    //    private static final byte REOP_ENDALT        = 56; /* end of final alternate */
+    private static final byte REOP_END = 57;
 
     private static final int ANCHOR_BOL = -2;
 
+    public static void init(Context cx, Scriptable scope, boolean sealed) {
 
-    public static void init(Context cx, Scriptable scope, boolean sealed)
-    {
-
-        NativeRegExp proto = new NativeRegExp();
+        NativeRegExp proto = NativeRegExpInstantiator.withLanguageVersion(cx.getLanguageVersion());
         proto.re = compileRE(cx, "", null, false);
         proto.activatePrototypeMap(MAX_PROTOTYPE_ID);
         proto.setParentScope(scope);
@@ -136,65 +133,51 @@
         defineProperty(scope, "RegExp", ctor, ScriptableObject.DONTENUM);
     }
 
-    NativeRegExp(Scriptable scope, RECompiled regexpCompiled)
-    {
+    NativeRegExp(Scriptable scope, RECompiled regexpCompiled) {
         this.re = regexpCompiled;
-        this.lastIndex = 0d;
+        setLastIndex(ScriptRuntime.zeroObj);
         ScriptRuntime.setBuiltinProtoAndParent(this, scope, TopLevel.Builtins.RegExp);
     }
 
     @Override
-    public String getClassName()
-    {
+    public String getClassName() {
         return "RegExp";
     }
 
     /**
      * Gets the value to be returned by the typeof operator called on this object.
+     *
      * @see org.mozilla.javascript.ScriptableObject#getTypeOf()
      * @return "object"
      */
     @Override
-    public String getTypeOf()
-    {
+    public String getTypeOf() {
         return "object";
     }
 
-    public Object call(Context cx, Scriptable scope, Scriptable thisObj,
-                       Object[] args)
-    {
-        return execSub(cx, scope, args, MATCH);
-    }
-
-    public Scriptable construct(Context cx, Scriptable scope, Object[] args)
-    {
-        return (Scriptable)execSub(cx, scope, args, MATCH);
-    }
-
-    Scriptable compile(Context cx, Scriptable scope, Object[] args)
-    {
+    Scriptable compile(Context cx, Scriptable scope, Object[] args) {
         if (args.length > 0 && args[0] instanceof NativeRegExp) {
             if (args.length > 1 && args[1] != Undefined.instance) {
                 // report error
-                throw ScriptRuntime.typeError0("msg.bad.regexp.compile");
+                throw ScriptRuntime.typeErrorById("msg.bad.regexp.compile");
             }
             NativeRegExp thatObj = (NativeRegExp) args[0];
             this.re = thatObj.re;
-            this.lastIndex = thatObj.lastIndex;
+            setLastIndex(thatObj.lastIndex);
             return this;
         }
         String s = args.length == 0 || args[0] instanceof Undefined ? "" : escapeRegExp(args[0]);
-        String global = args.length > 1 && args[1] != Undefined.instance
-            ? ScriptRuntime.toString(args[1])
-            : null;
+        String global =
+                args.length > 1 && args[1] != Undefined.instance
+                        ? ScriptRuntime.toString(args[1])
+                        : null;
         this.re = compileRE(cx, s, global, false);
-        this.lastIndex = 0d;
+        setLastIndex(ScriptRuntime.zeroObj);
         return this;
     }
 
     @Override
-    public String toString()
-    {
+    public String toString() {
         StringBuilder buf = new StringBuilder();
         buf.append('/');
         if (re.source.length != 0) {
@@ -204,19 +187,15 @@
             buf.append("(?:)");
         }
         buf.append('/');
-        if ((re.flags & JSREG_GLOB) != 0)
-            buf.append('g');
-        if ((re.flags & JSREG_FOLD) != 0)
-            buf.append('i');
-        if ((re.flags & JSREG_MULTILINE) != 0)
-            buf.append('m');
+        if ((re.flags & JSREG_GLOB) != 0) buf.append('g');
+        if ((re.flags & JSREG_FOLD) != 0) buf.append('i');
+        if ((re.flags & JSREG_MULTILINE) != 0) buf.append('m');
         return buf.toString();
     }
 
-    NativeRegExp() {  }
+    NativeRegExp() {}
 
-    private static RegExpImpl getImpl(Context cx)
-    {
+    private static RegExpImpl getImpl(Context cx) {
         return (RegExpImpl) ScriptRuntime.getRegExpProxy(cx);
     }
 
@@ -244,9 +223,7 @@
         return s;
     }
 
-    private Object execSub(Context cx, Scriptable scopeObj,
-                           Object[] args, int matchType)
-    {
+    Object execSub(Context cx, Scriptable scopeObj, Object[] args, int matchType) {
         RegExpImpl reImpl = getImpl(cx);
         String str;
         if (args.length == 0) {
@@ -264,22 +241,23 @@
 
         Object rval;
         if (d < 0 || str.length() < d) {
-            lastIndex = 0d;
+            setLastIndex(ScriptRuntime.zeroObj);
             rval = null;
-        }
-        else {
-            int indexp[] = { (int)d };
+        } else {
+            int indexp[] = {(int) d};
             rval = executeRegExp(cx, scopeObj, reImpl, str, indexp, matchType);
             if ((re.flags & JSREG_GLOB) != 0) {
-                lastIndex = (rval == null || rval == Undefined.instance)
-                            ? 0d : (double)indexp[0];
+                if (rval == null || rval == Undefined.instance) {
+                    setLastIndex(ScriptRuntime.zeroObj);
+                } else {
+                    setLastIndex(Double.valueOf(indexp[0]));
+                }
             }
         }
         return rval;
     }
 
-    static RECompiled compileRE(Context cx, String str, String global, boolean flat)
-    {
+    static RECompiled compileRE(Context cx, String str, String global, boolean flat) {
         RECompiled regexp = new RECompiled(str);
         int length = str.length();
         int flags = 0;
@@ -315,16 +293,14 @@
             state.result.flatIndex = 0;
             state.progLength += 5;
         } else {
-            if (!parseDisjunction(state))
-                return null;
+            if (!parseDisjunction(state)) return null;
             // Need to reparse if pattern contains invalid backreferences:
             // "Note: if the number of left parentheses is less than the number
             // specified in \#, the \# is taken as an octal escape"
             if (state.maxBackReference > state.parenCount) {
                 state = new CompilerState(cx, regexp.source, length, flags);
                 state.backReferenceLimit = state.parenCount;
-                if (!parseDisjunction(state))
-                    return null;
+                if (!parseDisjunction(state)) return null;
             }
         }
 
@@ -350,11 +326,11 @@
         switch (regexp.program[0]) {
             case REOP_UCFLAT1:
             case REOP_UCFLAT1i:
-                regexp.anchorCh = (char)getIndex(regexp.program, 1);
+                regexp.anchorCh = (char) getIndex(regexp.program, 1);
                 break;
             case REOP_FLAT1:
             case REOP_FLAT1i:
-                regexp.anchorCh = (char)(regexp.program[1] & 0xFF);
+                regexp.anchorCh = (char) (regexp.program[1] & 0xFF);
                 break;
             case REOP_FLAT:
             case REOP_FLATi:
@@ -374,34 +350,29 @@
 
         if (debug) {
             if (regexp.anchorCh >= 0) {
-                System.out.println("Anchor ch = '" + (char)regexp.anchorCh + "'");
+                System.out.println("Anchor ch = '" + (char) regexp.anchorCh + "'");
             }
         }
         return regexp;
     }
 
-    static boolean isDigit(char c)
-    {
+    static boolean isDigit(char c) {
         return '0' <= c && c <= '9';
     }
 
-    private static boolean isWord(char c)
-    {
+    private static boolean isWord(char c) {
         return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || isDigit(c) || c == '_';
     }
 
-    private static boolean isControlLetter(char c)
-    {
+    private static boolean isControlLetter(char c) {
         return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
     }
 
-    private static boolean isLineTerm(char c)
-    {
+    private static boolean isLineTerm(char c) {
         return ScriptRuntime.isJSLineTerminator(c);
     }
 
-    private static boolean isREWhiteSpace(int c)
-    {
+    private static boolean isREWhiteSpace(int c) {
         return ScriptRuntime.isJSWhitespaceOrLineTerminator(c);
     }
 
@@ -416,11 +387,10 @@
      *    code point value is less than decimal 128, then return ch.
      * 6. Return cu.
      */
-    private static char upcase(char ch)
-    {
+    private static char upcase(char ch) {
         if (ch < 128) {
             if ('a' <= ch && ch <= 'z') {
-                return (char)(ch + ('A' - 'a'));
+                return (char) (ch + ('A' - 'a'));
             }
             return ch;
         }
@@ -428,26 +398,22 @@
         return (cu < 128) ? ch : cu;
     }
 
-    private static char downcase(char ch)
-    {
+    private static char downcase(char ch) {
         if (ch < 128) {
             if ('A' <= ch && ch <= 'Z') {
-                return (char)(ch + ('a' - 'A'));
+                return (char) (ch + ('a' - 'A'));
             }
             return ch;
         }
         char cl = Character.toLowerCase(ch);
         return (cl < 128) ? ch : cl;
-
     }
 
-/*
- * Validates and converts hex ascii value.
- */
-    private static int toASCIIHexDigit(int c)
-    {
-        if (c < '0')
-            return -1;
+    /*
+     * Validates and converts hex ascii value.
+     */
+    private static int toASCIIHexDigit(int c) {
+        if (c < '0') return -1;
         if (c <= '9') {
             return c - '0';
         }
@@ -458,16 +424,14 @@
         return -1;
     }
 
-/*
- * Top-down regular expression grammar, based closely on Perl4.
- *
- *  regexp:     altern                  A regular expression is one or more
- *              altern '|' regexp       alternatives separated by vertical bar.
- */
-    private static boolean parseDisjunction(CompilerState state)
-    {
-        if (!parseAlternative(state))
-            return false;
+    /*
+     * Top-down regular expression grammar, based closely on Perl4.
+     *
+     *  regexp:     altern                  A regular expression is one or more
+     *              altern '|' regexp       alternatives separated by vertical bar.
+     */
+    private static boolean parseDisjunction(CompilerState state) {
+        if (!parseAlternative(state)) return false;
         char[] source = state.cpbegin;
         int index = state.cp;
         if (index != source.length && source[index] == '|') {
@@ -475,8 +439,7 @@
             ++state.cp;
             result = new RENode(REOP_ALT);
             result.kid = state.result;
-            if (!parseDisjunction(state))
-                return false;
+            if (!parseDisjunction(state)) return false;
             result.kid2 = state.result;
             state.result = result;
             /*
@@ -484,28 +447,31 @@
              * the start of each. If so, use a prerequisite match.
              */
             if (result.kid.op == REOP_FLAT && result.kid2.op == REOP_FLAT) {
-                result.op = (state.flags & JSREG_FOLD) == 0 ?
-                        REOP_ALTPREREQ : REOP_ALTPREREQi;
+                result.op = (state.flags & JSREG_FOLD) == 0 ? REOP_ALTPREREQ : REOP_ALTPREREQi;
                 result.chr = result.kid.chr;
                 result.index = result.kid2.chr;
                 /* ALTPREREQ, uch1, uch2, <next>, ...,
-                                            JUMP, <end> ... JUMP, <end> */
+                JUMP, <end> ... JUMP, <end> */
                 state.progLength += 13;
-            } else if (result.kid.op == REOP_CLASS && result.kid.index < 256
-                    && result.kid2.op == REOP_FLAT && (state.flags & JSREG_FOLD) == 0) {
+            } else if (result.kid.op == REOP_CLASS
+                    && result.kid.index < 256
+                    && result.kid2.op == REOP_FLAT
+                    && (state.flags & JSREG_FOLD) == 0) {
                 result.op = REOP_ALTPREREQ2;
                 result.chr = result.kid2.chr;
                 result.index = result.kid.index;
                 /* ALTPREREQ2, uch1, uch2, <next>, ...,
-                                            JUMP, <end> ... JUMP, <end> */
+                JUMP, <end> ... JUMP, <end> */
                 state.progLength += 13;
-            } else if (result.kid.op == REOP_FLAT && result.kid2.op == REOP_CLASS
-                    && result.kid2.index < 256 && (state.flags & JSREG_FOLD) == 0) {
+            } else if (result.kid.op == REOP_FLAT
+                    && result.kid2.op == REOP_CLASS
+                    && result.kid2.index < 256
+                    && (state.flags & JSREG_FOLD) == 0) {
                 result.op = REOP_ALTPREREQ2;
                 result.chr = result.kid.chr;
                 result.index = result.kid2.index;
                 /* ALTPREREQ2, uch1, uch2, <next>, ...,
-                                            JUMP, <end> ... JUMP, <end> */
+                JUMP, <end> ... JUMP, <end> */
                 state.progLength += 13;
             } else {
                 /* ALT, <next>, ..., JUMP, <end> ... JUMP, <end> */
@@ -515,43 +481,35 @@
         return true;
     }
 
-/*
- *  altern:     item                    An alternative is one or more items,
- *              item altern             concatenated together.
- */
-    private static boolean parseAlternative(CompilerState state)
-    {
+    /*
+     *  altern:     item                    An alternative is one or more items,
+     *              item altern             concatenated together.
+     */
+    private static boolean parseAlternative(CompilerState state) {
         RENode headTerm = null;
         RENode tailTerm = null;
         char[] source = state.cpbegin;
         while (true) {
-            if (state.cp == state.cpend || source[state.cp] == '|'
-                || (state.parenNesting != 0 && source[state.cp] == ')'))
-            {
+            if (state.cp == state.cpend
+                    || source[state.cp] == '|'
+                    || (state.parenNesting != 0 && source[state.cp] == ')')) {
                 if (headTerm == null) {
                     state.result = new RENode(REOP_EMPTY);
-                }
-                else
-                    state.result = headTerm;
+                } else state.result = headTerm;
                 return true;
             }
-            if (!parseTerm(state))
-                return false;
+            if (!parseTerm(state)) return false;
             if (headTerm == null) {
                 headTerm = state.result;
                 tailTerm = headTerm;
-            }
-            else
-                tailTerm.next = state.result;
+            } else tailTerm.next = state.result;
             while (tailTerm.next != null) tailTerm = tailTerm.next;
         }
     }
 
     /* calculate the total size of the bitmap required for a class expression */
-    private static boolean
-    calculateBitmapSize(CompilerState state, RENode target, char[] src,
-                        int index, int end)
-    {
+    private static boolean calculateBitmapSize(
+            CompilerState state, RENode target, char[] src, int index, int end) {
         char rangeStart = 0;
         char c;
         int n;
@@ -563,8 +521,7 @@
         target.bmsize = 0;
         target.sense = true;
 
-        if (index == end)
-            return true;
+        if (index == end) return true;
 
         if (src[index] == '^') {
             ++index;
@@ -575,111 +532,105 @@
             int localMax = 0;
             nDigits = 2;
             switch (src[index]) {
-            case '\\':
-                ++index;
-                c = src[index++];
-                switch (c) {
-                case 'b':
-                    localMax = 0x8;
-                    break;
-                case 'f':
-                    localMax = 0xC;
-                    break;
-                case 'n':
-                    localMax = 0xA;
-                    break;
-                case 'r':
-                    localMax = 0xD;
-                    break;
-                case 't':
-                    localMax = 0x9;
-                    break;
-                case 'v':
-                    localMax = 0xB;
-                    break;
-                case 'c':
-                    if ((index < end) && isControlLetter(src[index]))
-                        localMax = (char)(src[index++] & 0x1F);
-                    else
-                        --index;
-                        localMax = '\\';
-                    break;
-                case 'u':
-                    nDigits += 2;
-                    // fallthru
-                case 'x':
-                    n = 0;
-                    for (i = 0; (i < nDigits) && (index < end); i++) {
-                        c = src[index++];
-                        n = Kit.xDigitToInt(c, n);
-                        if (n < 0) {
-                            // Back off to accepting the original
-                            // '\' as a literal
-                            index -= (i + 1);
-                            n = '\\';
+                case '\\':
+                    ++index;
+                    c = src[index++];
+                    switch (c) {
+                        case 'b':
+                            localMax = 0x8;
+                            break;
+                        case 'f':
+                            localMax = 0xC;
+                            break;
+                        case 'n':
+                            localMax = 0xA;
+                            break;
+                        case 'r':
+                            localMax = 0xD;
+                            break;
+                        case 't':
+                            localMax = 0x9;
+                            break;
+                        case 'v':
+                            localMax = 0xB;
+                            break;
+                        case 'c':
+                            if ((index < end) && isControlLetter(src[index]))
+                                localMax = (char) (src[index++] & 0x1F);
+                            else --index;
+                            localMax = '\\';
+                            break;
+                        case 'u':
+                            nDigits += 2;
+                            // fall through
+                        case 'x':
+                            n = 0;
+                            for (i = 0; (i < nDigits) && (index < end); i++) {
+                                c = src[index++];
+                                n = Kit.xDigitToInt(c, n);
+                                if (n < 0) {
+                                    // Back off to accepting the original
+                                    // '\' as a literal
+                                    index -= (i + 1);
+                                    n = '\\';
+                                    break;
+                                }
+                            }
+                            localMax = n;
+                            break;
+                        case 'd':
+                            if (inRange) {
+                                target.bmsize = 65536;
+                                return true;
+                            }
+                            localMax = '9';
+                            break;
+                        case 'D':
+                        case 'w':
+                        case 'W':
+                        case 'S':
+                        case 's':
+                            target.bmsize = 65536;
+                            return true;
+
+                        case '0':
+                        case '1':
+                        case '2':
+                        case '3':
+                        case '4':
+                        case '5':
+                        case '6':
+                        case '7':
+                            /*
+                             *  This is a non-ECMA extension - decimal escapes (in this
+                             *  case, octal!) are supposed to be an error inside class
+                             *  ranges, but supported here for backwards compatibility.
+                             *
+                             */
+                            n = (c - '0');
+                            c = src[index];
+                            if ('0' <= c && c <= '7') {
+                                index++;
+                                n = 8 * n + (c - '0');
+                                c = src[index];
+                                if ('0' <= c && c <= '7') {
+                                    index++;
+                                    i = 8 * n + (c - '0');
+                                    if (i <= 0377) n = i;
+                                    else index--;
+                                }
+                            }
+                            localMax = n;
+                            break;
+
+                        default:
+                            localMax = c;
                             break;
-                        }
-                    }
-                    localMax = n;
-                    break;
-                case 'd':
-                    if (inRange) {
-                        reportError("msg.bad.range", "");
-                        return false;
-                    }
-                    localMax = '9';
-                    break;
-                case 'D':
-                case 's':
-                case 'S':
-                case 'w':
-                case 'W':
-                    if (inRange) {
-                        reportError("msg.bad.range", "");
-                        return false;
-                    }
-                    target.bmsize = 65536;
-                    return true;
-                case '0':
-                case '1':
-                case '2':
-                case '3':
-                case '4':
-                case '5':
-                case '6':
-                case '7':
-                    /*
-                     *  This is a non-ECMA extension - decimal escapes (in this
-                     *  case, octal!) are supposed to be an error inside class
-                     *  ranges, but supported here for backwards compatibility.
-                     *
-                     */
-                    n = (c - '0');
-                    c = src[index];
-                    if ('0' <= c && c <= '7') {
-                        index++;
-                        n = 8 * n + (c - '0');
-                        c = src[index];
-                        if ('0' <= c && c <= '7') {
-                            index++;
-                            i = 8 * n + (c - '0');
-                            if (i <= 0377)
-                                n = i;
-                            else
-                                index--;
-                        }
                     }
-                    localMax = n;
                     break;
-
                 default:
-                    localMax = c;
+                    localMax = src[index++];
                     break;
-                }
-                break;
-            default:
-                localMax = src[index++];
-                break;
             }
             if (inRange) {
                 if (rangeStart > localMax) {
@@ -687,24 +638,22 @@
                     return false;
                 }
                 inRange = false;
-            }
-            else {
+            } else {
                 if (index < (end - 1)) {
                     if (src[index] == '-') {
                         ++index;
                         inRange = true;
-                        rangeStart = (char)localMax;
+                        rangeStart = (char) localMax;
                         continue;
                     }
                 }
             }
-            if ((state.flags & JSREG_FOLD) != 0){
-                char cu = upcase((char)localMax);
-                char cd = downcase((char)localMax);
+            if ((state.flags & JSREG_FOLD) != 0) {
+                char cu = upcase((char) localMax);
+                char cd = downcase((char) localMax);
                 localMax = (cu >= cd) ? cu : cd;
             }
-            if (localMax > max)
-                max = localMax;
+            if (localMax > max) max = localMax;
         }
         target.bmsize = max + 1;
         return true;
@@ -764,8 +713,7 @@
      *                                      atom right-hand sides.
      */
 
-    private static void doFlat(CompilerState state, char c)
-    {
+    private static void doFlat(CompilerState state, char c) {
         state.result = new RENode(REOP_FLAT);
         state.result.chr = c;
         state.result.length = 1;
@@ -773,10 +721,8 @@
         state.progLength += 3;
     }
 
-    private static int
-    getDecimalValue(char c, CompilerState state, int maxValue,
-                    String overflowMessageId)
-    {
+    private static int getDecimalValue(
+            char c, CompilerState state, int maxValue, String overflowMessageId) {
         boolean overflow = false;
         int start = state.cp;
         char[] src = state.cpbegin;
@@ -797,15 +743,12 @@
             }
         }
         if (overflow) {
-            reportError(overflowMessageId,
-                        String.valueOf(src, start, state.cp - start));
+            reportError(overflowMessageId, String.valueOf(src, start, state.cp - start));
         }
         return value;
     }
 
-    private static boolean
-    parseTerm(CompilerState state)
-    {
+    private static boolean parseTerm(CompilerState state) {
         char[] src = state.cpbegin;
         char c = src[state.cp++];
         int nDigits = 2;
@@ -815,285 +758,275 @@
         int termStart;
 
         switch (c) {
-        /* assertions and atoms */
-        case '^':
-            state.result = new RENode(REOP_BOL);
-            state.progLength++;
-            return true;
-        case '$':
-            state.result = new RENode(REOP_EOL);
-            state.progLength++;
-            return true;
-        case '\\':
-            if (state.cp < state.cpend) {
-                c = src[state.cp++];
-                switch (c) {
-                /* assertion escapes */
-                case 'b' :
-                    state.result = new RENode(REOP_WBDRY);
-                    state.progLength++;
-                    return true;
-                case 'B':
-                    state.result = new RENode(REOP_WNONBDRY);
-                    state.progLength++;
-                    return true;
-                /* Decimal escape */
-                case '0':
-/*
- * We're deliberately violating the ECMA 5.1 specification and allow octal
- * escapes to follow spidermonkey and general 'web reality':
- * http://wiki.ecmascript.org/doku.php?id=harmony:regexp_match_web_reality
- * http://wiki.ecmascript.org/doku.php?id=strawman:match_web_reality_spec
- */
-                    reportWarning(state.cx, "msg.bad.backref", "");
-                    /* octal escape */
-                    num = 0;
-                    // follow spidermonkey and allow multiple leading zeros,
-                    // e.g. let /\0000/ match the string "\0"
-                    while (num < 040 && state.cp < state.cpend) {
-                        c = src[state.cp];
-                        if ((c >= '0') && (c <= '7')) {
-                            state.cp++;
-                            num = 8 * num + (c - '0');
-                        }
-                        else
-                            break;
-                    }
-                    c = (char)(num);
-                    doFlat(state, c);
-                    break;
-                case '1':
-                case '2':
-                case '3':
-                case '4':
-                case '5':
-                case '6':
-                case '7':
-                case '8':
-                case '9':
-                    termStart = state.cp - 1;
-                    num = getDecimalValue(c, state, 0xFFFF,
-                                          "msg.overlarge.backref");
-                    if (num > state.backReferenceLimit)
-                        reportWarning(state.cx, "msg.bad.backref", "");
-                    /*
-                     * n > count of parentheses, then treat as octal instead.
-                     * Also see note above concerning 'web reality'
-                     */
-                    if (num > state.backReferenceLimit) {
-                        state.cp = termStart;
-                        if (c >= '8') {
-                            // invalid octal escape, follow spidermonkey and
-                            // treat as \\8 resp. \\9
-                            c = '\\';
+                /* assertions and atoms */
+            case '^':
+                state.result = new RENode(REOP_BOL);
+                state.progLength++;
+                return true;
+            case '$':
+                state.result = new RENode(REOP_EOL);
+                state.progLength++;
+                return true;
+            case '\\':
+                if (state.cp < state.cpend) {
+                    c = src[state.cp++];
+                    switch (c) {
+                            /* assertion escapes */
+                        case 'b':
+                            state.result = new RENode(REOP_WBDRY);
+                            state.progLength++;
+                            return true;
+                        case 'B':
+                            state.result = new RENode(REOP_WNONBDRY);
+                            state.progLength++;
+                            return true;
+                            /* Decimal escape */
+                        case '0':
+                            /*
+                             * We're deliberately violating the ECMA 5.1 specification and allow octal
+                             * escapes to follow spidermonkey and general 'web reality':
+                             * http://wiki.ecmascript.org/doku.php?id=harmony:regexp_match_web_reality
+                             * http://wiki.ecmascript.org/doku.php?id=strawman:match_web_reality_spec
+                             */
+                            reportWarning(state.cx, "msg.bad.backref", "");
+                            /* octal escape */
+                            num = 0;
+                            // follow spidermonkey and allow multiple leading zeros,
+                            // e.g. let /\0000/ match the string "\0"
+                            while (num < 040 && state.cp < state.cpend) {
+                                c = src[state.cp];
+                                if ((c >= '0') && (c <= '7')) {
+                                    state.cp++;
+                                    num = 8 * num + (c - '0');
+                                } else break;
+                            }
+                            c = (char) (num);
                             doFlat(state, c);
                             break;
-                        }
-                        state.cp++;
-                        num = c - '0';
-                        while (num < 040 && state.cp < state.cpend) {
-                            c = src[state.cp];
-                            if ((c >= '0') && (c <= '7')) {
+                        case '1':
+                        case '2':
+                        case '3':
+                        case '4':
+                        case '5':
+                        case '6':
+                        case '7':
+                        case '8':
+                        case '9':
+                            termStart = state.cp - 1;
+                            num = getDecimalValue(c, state, 0xFFFF, "msg.overlarge.backref");
+                            if (num > state.backReferenceLimit)
+                                reportWarning(state.cx, "msg.bad.backref", "");
+                            /*
+                             * n > count of parentheses, then treat as octal instead.
+                             * Also see note above concerning 'web reality'
+                             */
+                            if (num > state.backReferenceLimit) {
+                                state.cp = termStart;
+                                if (c >= '8') {
+                                    // invalid octal escape, follow spidermonkey and
+                                    // treat as \\8 resp. \\9
+                                    c = '\\';
+                                    doFlat(state, c);
+                                    break;
+                                }
                                 state.cp++;
-                                num = 8 * num + (c - '0');
-                            }
-                            else
-                                break;
-                        }
-                        c = (char)(num);
-                        doFlat(state, c);
-                        break;
-                    }
-                    /* otherwise, it's a back-reference */
-                    state.result = new RENode(REOP_BACKREF);
-                    state.result.parenIndex = num - 1;
-                    state.progLength += 3;
-                    if (state.maxBackReference < num) {
-                        state.maxBackReference = num;
-                    }
-                    break;
-                /* Control escape */
-                case 'f':
-                    c = 0xC;
-                    doFlat(state, c);
-                    break;
-                case 'n':
-                    c = 0xA;
-                    doFlat(state, c);
-                    break;
-                case 'r':
-                    c = 0xD;
-                    doFlat(state, c);
-                    break;
-                case 't':
-                    c = 0x9;
-                    doFlat(state, c);
-                    break;
-                case 'v':
-                    c = 0xB;
-                    doFlat(state, c);
-                    break;
-                /* Control letter */
-                case 'c':
-                    if ((state.cp < state.cpend) &&
-                                        isControlLetter(src[state.cp]))
-                        c = (char)(src[state.cp++] & 0x1F);
-                    else {
-                        /* back off to accepting the original '\' as a literal */
-                        --state.cp;
-                        c = '\\';
-                    }
-                    doFlat(state, c);
-                    break;
-                /* UnicodeEscapeSequence */
-                case 'u':
-                    nDigits += 2;
-                /* fallthru */ case 'x':  /* HexEscapeSequence */
-                    {
-                        int n = 0;
-                        int i;
-                        for (i = 0; (i < nDigits)
-                                && (state.cp < state.cpend); i++) {
-                            c = src[state.cp++];
-                            n = Kit.xDigitToInt(c, n);
-                            if (n < 0) {
-                                // Back off to accepting the original
-                                // 'u' or 'x' as a literal
-                                state.cp -= (i + 2);
-                                n = src[state.cp++];
+                                num = c - '0';
+                                while (num < 040 && state.cp < state.cpend) {
+                                    c = src[state.cp];
+                                    if ((c >= '0') && (c <= '7')) {
+                                        state.cp++;
+                                        num = 8 * num + (c - '0');
+                                    } else break;
+                                }
+                                c = (char) (num);
+                                doFlat(state, c);
                                 break;
                             }
-                        }
-                        c = (char)(n);
+                            /* otherwise, it's a back-reference */
+                            state.result = new RENode(REOP_BACKREF);
+                            state.result.parenIndex = num - 1;
+                            state.progLength += 3;
+                            if (state.maxBackReference < num) {
+                                state.maxBackReference = num;
+                            }
+                            break;
+                            /* Control escape */
+                        case 'f':
+                            c = 0xC;
+                            doFlat(state, c);
+                            break;
+                        case 'n':
+                            c = 0xA;
+                            doFlat(state, c);
+                            break;
+                        case 'r':
+                            c = 0xD;
+                            doFlat(state, c);
+                            break;
+                        case 't':
+                            c = 0x9;
+                            doFlat(state, c);
+                            break;
+                        case 'v':
+                            c = 0xB;
+                            doFlat(state, c);
+                            break;
+                            /* Control letter */
+                        case 'c':
+                            if ((state.cp < state.cpend) && isControlLetter(src[state.cp]))
+                                c = (char) (src[state.cp++] & 0x1F);
+                            else {
+                                /* back off to accepting the original '\' as a literal */
+                                --state.cp;
+                                c = '\\';
+                            }
+                            doFlat(state, c);
+                            break;
+                            /* UnicodeEscapeSequence */
+                        case 'u':
+                            nDigits += 2;
+                            /* fall through */ case 'x': /* HexEscapeSequence */
+                            {
+                                int n = 0;
+                                int i;
+                                for (i = 0; (i < nDigits) && (state.cp < state.cpend); i++) {
+                                    c = src[state.cp++];
+                                    n = Kit.xDigitToInt(c, n);
+                                    if (n < 0) {
+                                        // Back off to accepting the original
+                                        // 'u' or 'x' as a literal
+                                        state.cp -= (i + 2);
+                                        n = src[state.cp++];
+                                        break;
+                                    }
+                                }
+                                c = (char) (n);
+                            }
+                            doFlat(state, c);
+                            break;
+                            /* Character class escapes */
+                        case 'd':
+                            state.result = new RENode(REOP_DIGIT);
+                            state.progLength++;
+                            break;
+                        case 'D':
+                            state.result = new RENode(REOP_NONDIGIT);
+                            state.progLength++;
+                            break;
+                        case 's':
+                            state.result = new RENode(REOP_SPACE);
+                            state.progLength++;
+                            break;
+                        case 'S':
+                            state.result = new RENode(REOP_NONSPACE);
+                            state.progLength++;
+                            break;
+                        case 'w':
+                            state.result = new RENode(REOP_ALNUM);
+                            state.progLength++;
+                            break;
+                        case 'W':
+                            state.result = new RENode(REOP_NONALNUM);
+                            state.progLength++;
+                            break;
+                            /* IdentityEscape */
+                        default:
+                            state.result = new RENode(REOP_FLAT);
+                            state.result.chr = c;
+                            state.result.length = 1;
+                            state.result.flatIndex = state.cp - 1;
+                            state.progLength += 3;
+                            break;
                     }
-                    doFlat(state, c);
-                    break;
-                /* Character class escapes */
-                case 'd':
-                    state.result = new RENode(REOP_DIGIT);
-                    state.progLength++;
-                    break;
-                case 'D':
-                    state.result = new RENode(REOP_NONDIGIT);
-                    state.progLength++;
-                    break;
-                case 's':
-                    state.result = new RENode(REOP_SPACE);
-                    state.progLength++;
-                    break;
-                case 'S':
-                    state.result = new RENode(REOP_NONSPACE);
-                    state.progLength++;
-                    break;
-                case 'w':
-                    state.result = new RENode(REOP_ALNUM);
-                    state.progLength++;
-                    break;
-                case 'W':
-                    state.result = new RENode(REOP_NONALNUM);
-                    state.progLength++;
-                    break;
-                /* IdentityEscape */
-                default:
-                    state.result = new RENode(REOP_FLAT);
-                    state.result.chr = c;
-                    state.result.length = 1;
-                    state.result.flatIndex = state.cp - 1;
-                    state.progLength += 3;
                     break;
                 }
-                break;
-            }
-            else {
                 /* a trailing '\' is an error */
                 reportError("msg.trail.backslash", "");
                 return false;
-            }
-        case '(': {
-            RENode result = null;
-            termStart = state.cp;
-            if (state.cp + 1 < state.cpend && src[state.cp] == '?'
-                && ((c = src[state.cp + 1]) == '=' || c == '!' || c == ':'))
-            {
-                state.cp += 2;
-                if (c == '=') {
-                    result = new RENode(REOP_ASSERT);
-                    /* ASSERT, <next>, ... ASSERTTEST */
-                    state.progLength += 4;
-                } else if (c == '!') {
-                    result = new RENode(REOP_ASSERT_NOT);
-                    /* ASSERTNOT, <next>, ... ASSERTNOTTEST */
-                    state.progLength += 4;
+            case '(':
+                {
+                    RENode result = null;
+                    termStart = state.cp;
+                    if (state.cp + 1 < state.cpend
+                            && src[state.cp] == '?'
+                            && ((c = src[state.cp + 1]) == '=' || c == '!' || c == ':')) {
+                        state.cp += 2;
+                        if (c == '=') {
+                            result = new RENode(REOP_ASSERT);
+                            /* ASSERT, <next>, ... ASSERTTEST */
+                            state.progLength += 4;
+                        } else if (c == '!') {
+                            result = new RENode(REOP_ASSERT_NOT);
+                            /* ASSERTNOT, <next>, ... ASSERTNOTTEST */
+                            state.progLength += 4;
+                        }
+                    } else {
+                        result = new RENode(REOP_LPAREN);
+                        /* LPAREN, <index>, ... RPAREN, <index> */
+                        state.progLength += 6;
+                        result.parenIndex = state.parenCount++;
+                    }
+                    ++state.parenNesting;
+                    if (!parseDisjunction(state)) return false;
+                    if (state.cp == state.cpend || src[state.cp] != ')') {
+                        reportError("msg.unterm.paren", "");
+                        return false;
+                    }
+                    ++state.cp;
+                    --state.parenNesting;
+                    if (result != null) {
+                        result.kid = state.result;
+                        state.result = result;
+                    }
+                    break;
                 }
-            } else {
-                result = new RENode(REOP_LPAREN);
-                /* LPAREN, <index>, ... RPAREN, <index> */
-                state.progLength += 6;
-                result.parenIndex = state.parenCount++;
-            }
-            ++state.parenNesting;
-            if (!parseDisjunction(state))
-                return false;
-            if (state.cp == state.cpend || src[state.cp] != ')') {
-                reportError("msg.unterm.paren", "");
+            case ')':
+                reportError("msg.re.unmatched.right.paren", "");
                 return false;
-            }
-            ++state.cp;
-            --state.parenNesting;
-            if (result != null) {
-                result.kid = state.result;
-                state.result = result;
-            }
-            break;
-        }
-        case ')':
-          reportError("msg.re.unmatched.right.paren", "");
-          return false;
-        case '[':
-            state.result = new RENode(REOP_CLASS);
-            termStart = state.cp;
-            state.result.startIndex = termStart;
-            while (true) {
-                if (state.cp == state.cpend) {
-                    reportError("msg.unterm.class", "");
-                    return false;
-                }
-                if (src[state.cp] == '\\')
-                    state.cp++;
-                else {
-                    if (src[state.cp] == ']') {
-                        state.result.kidlen = state.cp - termStart;
-                        break;
+            case '[':
+                state.result = new RENode(REOP_CLASS);
+                termStart = state.cp;
+                state.result.startIndex = termStart;
+                while (true) {
+                    if (state.cp == state.cpend) {
+                        reportError("msg.unterm.class", "");
+                        return false;
+                    }
+                    if (src[state.cp] == '\\') state.cp++;
+                    else {
+                        if (src[state.cp] == ']') {
+                            state.result.kidlen = state.cp - termStart;
+                            break;
+                        }
                     }
+                    state.cp++;
                 }
-                state.cp++;
-            }
-            state.result.index = state.classCount++;
-            /*
-             * Call calculateBitmapSize now as we want any errors it finds
-             * to be reported during the parse phase, not at execution.
-             */
-            if (!calculateBitmapSize(state, state.result, src, termStart, state.cp++))
-                return false;
-            state.progLength += 3; /* CLASS, <index> */
-            break;
+                state.result.index = state.classCount++;
+                /*
+                 * Call calculateBitmapSize now as we want any errors it finds
+                 * to be reported during the parse phase, not at execution.
+                 */
+                if (!calculateBitmapSize(state, state.result, src, termStart, state.cp++))
+                    return false;
+                state.progLength += 3; /* CLASS, <index> */
+                break;
 
-        case '.':
-            state.result = new RENode(REOP_DOT);
-            state.progLength++;
-            break;
-        case '*':
-        case '+':
-        case '?':
-            reportError("msg.bad.quant", String.valueOf(src[state.cp - 1]));
-            return false;
-        default:
-            state.result = new RENode(REOP_FLAT);
-            state.result.chr = c;
-            state.result.length = 1;
-            state.result.flatIndex = state.cp - 1;
-            state.progLength += 3;
-            break;
+            case '.':
+                state.result = new RENode(REOP_DOT);
+                state.progLength++;
+                break;
+            case '*':
+            case '+':
+            case '?':
+                reportError("msg.bad.quant", String.valueOf(src[state.cp - 1]));
+                return false;
+            default:
+                state.result = new RENode(REOP_FLAT);
+                state.result.chr = c;
+                state.result.length = 1;
+                state.result.flatIndex = state.cp - 1;
+                state.progLength += 3;
+                break;
         }
 
         term = state.result;
@@ -1126,58 +1059,56 @@
                 state.progLength += 8;
                 hasQ = true;
                 break;
-            case '{':  /* balance '}' */
-            {
-                int min = 0;
-                int max = -1;
-                int leftCurl = state.cp;
-
-               /* For Perl etc. compatibility, if quntifier does not match
-                * \{\d+(,\d*)?\} exactly back off from it
-                * being a quantifier, and chew it up as a literal
-                * atom next time instead.
-                */
+            case '{': /* balance '}' */
+                {
+                    int min = 0;
+                    int max = -1;
+                    int leftCurl = state.cp;
+
+                    /* For Perl etc. compatibility, if quntifier does not match
+                     * \{\d+(,\d*)?\} exactly back off from it
+                     * being a quantifier, and chew it up as a literal
+                     * atom next time instead.
+                     */
 
-                if (++state.cp < src.length && isDigit(c = src[state.cp])) {
-                    ++state.cp;
-                    min = getDecimalValue(c, state, 0xFFFF,
-                                          "msg.overlarge.min");
-                    c = src[state.cp];
-                    if (c == ',') {
-                        c = src[++state.cp];
-                        if (isDigit(c)) {
-                            ++state.cp;
-                            max = getDecimalValue(c, state, 0xFFFF,
-                                                  "msg.overlarge.max");
+                    if (++state.cp < src.length && isDigit(c = src[state.cp])) {
+                        ++state.cp;
+                        min = getDecimalValue(c, state, 0xFFFF, "msg.overlarge.min");
+                        if (state.cp < src.length) {
                             c = src[state.cp];
-                            if (min > max) {
-                                reportError("msg.max.lt.min",
-                                            String.valueOf(src[state.cp]));
-                                return false;
+                            if (c == ',' && ++state.cp < src.length) {
+                                c = src[state.cp];
+                                if (isDigit(c) && ++state.cp < src.length) {
+                                    max = getDecimalValue(c, state, 0xFFFF, "msg.overlarge.max");
+                                    c = src[state.cp];
+                                    if (min > max) {
+                                        reportError(
+                                                "msg.max.lt.min", String.valueOf(src[state.cp]));
+                                        return false;
+                                    }
+                                }
+                            } else {
+                                max = min;
+                            }
+                            /* balance '{' */
+                            if (c == '}') {
+                                state.result = new RENode(REOP_QUANT);
+                                state.result.min = min;
+                                state.result.max = max;
+                                // QUANT, <min>, <max>, <parencount>,
+                                // <parenindex>, <next> ... <ENDCHILD>
+                                state.progLength += 12;
+                                hasQ = true;
                             }
                         }
-                    } else {
-                        max = min;
                     }
-                    /* balance '{' */
-                    if (c == '}') {
-                        state.result = new RENode(REOP_QUANT);
-                        state.result.min = min;
-                        state.result.max = max;
-                        // QUANT, <min>, <max>, <parencount>,
-                        // <parenindex>, <next> ... <ENDCHILD>
-                        state.progLength += 12;
-                        hasQ = true;
+                    if (!hasQ) {
+                        state.cp = leftCurl;
                     }
+                    break;
                 }
-                if (!hasQ) {
-                    state.cp = leftCurl;
-                }
-                break;
-            }
         }
-        if (!hasQ)
-            return true;
+        if (!hasQ) return true;
 
         ++state.cp;
         state.result.kid = term;
@@ -1186,43 +1117,34 @@
         if ((state.cp < state.cpend) && (src[state.cp] == '?')) {
             ++state.cp;
             state.result.greedy = false;
-        }
-        else
-            state.result.greedy = true;
+        } else state.result.greedy = true;
         return true;
     }
 
-    private static void resolveForwardJump(byte[] array, int from, int pc)
-    {
+    private static void resolveForwardJump(byte[] array, int from, int pc) {
         if (from > pc) throw Kit.codeBug();
         addIndex(array, from, pc - from);
     }
 
-    private static int getOffset(byte[] array, int pc)
-    {
+    private static int getOffset(byte[] array, int pc) {
         return getIndex(array, pc);
     }
 
-    private static int addIndex(byte[] array, int pc, int index)
-    {
+    private static int addIndex(byte[] array, int pc, int index) {
         if (index < 0) throw Kit.codeBug();
-        if (index > 0xFFFF)
-            throw Context.reportRuntimeError("Too complex regexp");
-        array[pc] = (byte)(index >> 8);
-        array[pc + 1] = (byte)(index);
+        if (index > 0xFFFF) throw Context.reportRuntimeError("Too complex regexp");
+        array[pc] = (byte) (index >> 8);
+        array[pc + 1] = (byte) (index);
         return pc + 2;
     }
 
-    private static int getIndex(byte[] array, int pc)
-    {
+    private static int getIndex(byte[] array, int pc) {
         return ((array[pc] & 0xFF) << 8) | (array[pc + 1] & 0xFF);
     }
 
-    private static final int INDEX_LEN  = 2;
+    private static final int INDEX_LEN = 2;
 
-    private static int
-    emitREBytecode(CompilerState state, RECompiled re, int pc, RENode t)
-    {
+    private static int emitREBytecode(CompilerState state, RECompiled re, int pc, RENode t) {
         RENode nextAlt;
         int nextAltFixup, nextTermFixup;
         byte[] program = re.program;
@@ -1230,177 +1152,169 @@
         while (t != null) {
             program[pc++] = t.op;
             switch (t.op) {
-            case REOP_EMPTY:
-                --pc;
-                break;
-            case REOP_ALTPREREQ:
-            case REOP_ALTPREREQi:
-            case REOP_ALTPREREQ2:
-                boolean ignoreCase = t.op == REOP_ALTPREREQi;
-                addIndex(program, pc, ignoreCase ? upcase(t.chr) : t.chr);
-                pc += INDEX_LEN;
-                addIndex(program, pc, ignoreCase ? upcase((char)t.index) : t.index);
-                pc += INDEX_LEN;
-                // fall through to REOP_ALT
-            case REOP_ALT:
-                nextAlt = t.kid2;
-                nextAltFixup = pc;    /* address of next alternate */
-                pc += INDEX_LEN;
-                pc = emitREBytecode(state, re, pc, t.kid);
-                program[pc++] = REOP_JUMP;
-                nextTermFixup = pc;    /* address of following term */
-                pc += INDEX_LEN;
-                resolveForwardJump(program, nextAltFixup, pc);
-                pc = emitREBytecode(state, re, pc, nextAlt);
-
-                program[pc++] = REOP_JUMP;
-                nextAltFixup = pc;
-                pc += INDEX_LEN;
+                case REOP_EMPTY:
+                    --pc;
+                    break;
+                case REOP_ALTPREREQ:
+                case REOP_ALTPREREQi:
+                case REOP_ALTPREREQ2:
+                    boolean ignoreCase = t.op == REOP_ALTPREREQi;
+                    addIndex(program, pc, ignoreCase ? upcase(t.chr) : t.chr);
+                    pc += INDEX_LEN;
+                    addIndex(program, pc, ignoreCase ? upcase((char) t.index) : t.index);
+                    pc += INDEX_LEN;
+                    // fall through to REOP_ALT
+                case REOP_ALT:
+                    nextAlt = t.kid2;
+                    nextAltFixup = pc; /* address of next alternate */
+                    pc += INDEX_LEN;
+                    pc = emitREBytecode(state, re, pc, t.kid);
+                    program[pc++] = REOP_JUMP;
+                    nextTermFixup = pc; /* address of following term */
+                    pc += INDEX_LEN;
+                    resolveForwardJump(program, nextAltFixup, pc);
+                    pc = emitREBytecode(state, re, pc, nextAlt);
+
+                    program[pc++] = REOP_JUMP;
+                    nextAltFixup = pc;
+                    pc += INDEX_LEN;
 
-                resolveForwardJump(program, nextTermFixup, pc);
-                resolveForwardJump(program, nextAltFixup, pc);
-                break;
-            case REOP_FLAT:
-                /*
-                 * Consecutize FLAT's if possible.
-                 */
-                if (t.flatIndex != -1) {
-                    while ((t.next != null) && (t.next.op == REOP_FLAT)
-                            && ((t.flatIndex + t.length)
-                                            == t.next.flatIndex)) {
-                        t.length += t.next.length;
-                        t.next = t.next.next;
-                    }
-                }
-                if ((t.flatIndex != -1) && (t.length > 1)) {
-                    if ((state.flags & JSREG_FOLD) != 0)
-                        program[pc - 1] = REOP_FLATi;
-                    else
-                        program[pc - 1] = REOP_FLAT;
-                    pc = addIndex(program, pc, t.flatIndex);
-                    pc = addIndex(program, pc, t.length);
-                }
-                else {
-                    if (t.chr < 256) {
-                        if ((state.flags & JSREG_FOLD) != 0)
-                            program[pc - 1] = REOP_FLAT1i;
-                        else
-                            program[pc - 1] = REOP_FLAT1;
-                        program[pc++] = (byte)(t.chr);
+                    resolveForwardJump(program, nextTermFixup, pc);
+                    resolveForwardJump(program, nextAltFixup, pc);
+                    break;
+                case REOP_FLAT:
+                    /*
+                     * Consecutize FLAT's if possible.
+                     */
+                    if (t.flatIndex != -1) {
+                        while ((t.next != null)
+                                && (t.next.op == REOP_FLAT)
+                                && ((t.flatIndex + t.length) == t.next.flatIndex)) {
+                            t.length += t.next.length;
+                            t.next = t.next.next;
+                        }
                     }
-                    else {
-                        if ((state.flags & JSREG_FOLD) != 0)
-                            program[pc - 1] = REOP_UCFLAT1i;
-                        else
-                            program[pc - 1] = REOP_UCFLAT1;
-                        pc = addIndex(program, pc, t.chr);
+                    if ((t.flatIndex != -1) && (t.length > 1)) {
+                        if ((state.flags & JSREG_FOLD) != 0) program[pc - 1] = REOP_FLATi;
+                        else program[pc - 1] = REOP_FLAT;
+                        pc = addIndex(program, pc, t.flatIndex);
+                        pc = addIndex(program, pc, t.length);
+                    } else {
+                        if (t.chr < 256) {
+                            if ((state.flags & JSREG_FOLD) != 0) program[pc - 1] = REOP_FLAT1i;
+                            else program[pc - 1] = REOP_FLAT1;
+                            program[pc++] = (byte) (t.chr);
+                        } else {
+                            if ((state.flags & JSREG_FOLD) != 0) program[pc - 1] = REOP_UCFLAT1i;
+                            else program[pc - 1] = REOP_UCFLAT1;
+                            pc = addIndex(program, pc, t.chr);
+                        }
                     }
-                }
-                break;
-            case REOP_LPAREN:
-                pc = addIndex(program, pc, t.parenIndex);
-                pc = emitREBytecode(state, re, pc, t.kid);
-                program[pc++] = REOP_RPAREN;
-                pc = addIndex(program, pc, t.parenIndex);
-                break;
-            case REOP_BACKREF:
-                pc = addIndex(program, pc, t.parenIndex);
-                break;
-            case REOP_ASSERT:
-                nextTermFixup = pc;
-                pc += INDEX_LEN;
-                pc = emitREBytecode(state, re, pc, t.kid);
-                program[pc++] = REOP_ASSERTTEST;
-                resolveForwardJump(program, nextTermFixup, pc);
-                break;
-            case REOP_ASSERT_NOT:
-                nextTermFixup = pc;
-                pc += INDEX_LEN;
-                pc = emitREBytecode(state, re, pc, t.kid);
-                program[pc++] = REOP_ASSERTNOTTEST;
-                resolveForwardJump(program, nextTermFixup, pc);
-                break;
-            case REOP_QUANT:
-                if ((t.min == 0) && (t.max == -1))
-                    program[pc - 1] = (t.greedy) ? REOP_STAR : REOP_MINIMALSTAR;
-                else
-                if ((t.min == 0) && (t.max == 1))
-                    program[pc - 1] = (t.greedy) ? REOP_OPT : REOP_MINIMALOPT;
-                else
-                if ((t.min == 1) && (t.max == -1))
-                    program[pc - 1] = (t.greedy) ? REOP_PLUS : REOP_MINIMALPLUS;
-                else {
-                    if (!t.greedy) program[pc - 1] = REOP_MINIMALQUANT;
-                    pc = addIndex(program, pc, t.min);
-                    // max can be -1 which addIndex does not accept
-                    pc = addIndex(program, pc, t.max + 1);
-                }
-                pc = addIndex(program, pc, t.parenCount);
-                pc = addIndex(program, pc, t.parenIndex);
-                nextTermFixup = pc;
-                pc += INDEX_LEN;
-                pc = emitREBytecode(state, re, pc, t.kid);
-                program[pc++] = REOP_ENDCHILD;
-                resolveForwardJump(program, nextTermFixup, pc);
-                break;
-            case REOP_CLASS:
-                if (!t.sense)
-                    program[pc - 1] = REOP_NCLASS;
-                pc = addIndex(program, pc, t.index);
-                re.classList[t.index] = new RECharSet(t.bmsize, t.startIndex,
-                                                      t.kidlen, t.sense);
-                break;
-            default:
-                break;
+                    break;
+                case REOP_LPAREN:
+                    pc = addIndex(program, pc, t.parenIndex);
+                    pc = emitREBytecode(state, re, pc, t.kid);
+                    program[pc++] = REOP_RPAREN;
+                    pc = addIndex(program, pc, t.parenIndex);
+                    break;
+                case REOP_BACKREF:
+                    pc = addIndex(program, pc, t.parenIndex);
+                    break;
+                case REOP_ASSERT:
+                    nextTermFixup = pc;
+                    pc += INDEX_LEN;
+                    pc = emitREBytecode(state, re, pc, t.kid);
+                    program[pc++] = REOP_ASSERTTEST;
+                    resolveForwardJump(program, nextTermFixup, pc);
+                    break;
+                case REOP_ASSERT_NOT:
+                    nextTermFixup = pc;
+                    pc += INDEX_LEN;
+                    pc = emitREBytecode(state, re, pc, t.kid);
+                    program[pc++] = REOP_ASSERTNOTTEST;
+                    resolveForwardJump(program, nextTermFixup, pc);
+                    break;
+                case REOP_QUANT:
+                    if ((t.min == 0) && (t.max == -1))
+                        program[pc - 1] = (t.greedy) ? REOP_STAR : REOP_MINIMALSTAR;
+                    else if ((t.min == 0) && (t.max == 1))
+                        program[pc - 1] = (t.greedy) ? REOP_OPT : REOP_MINIMALOPT;
+                    else if ((t.min == 1) && (t.max == -1))
+                        program[pc - 1] = (t.greedy) ? REOP_PLUS : REOP_MINIMALPLUS;
+                    else {
+                        if (!t.greedy) program[pc - 1] = REOP_MINIMALQUANT;
+                        pc = addIndex(program, pc, t.min);
+                        // max can be -1 which addIndex does not accept
+                        pc = addIndex(program, pc, t.max + 1);
+                    }
+                    pc = addIndex(program, pc, t.parenCount);
+                    pc = addIndex(program, pc, t.parenIndex);
+                    nextTermFixup = pc;
+                    pc += INDEX_LEN;
+                    pc = emitREBytecode(state, re, pc, t.kid);
+                    program[pc++] = REOP_ENDCHILD;
+                    resolveForwardJump(program, nextTermFixup, pc);
+                    break;
+                case REOP_CLASS:
+                    if (!t.sense) program[pc - 1] = REOP_NCLASS;
+                    pc = addIndex(program, pc, t.index);
+                    re.classList[t.index] =
+                            new RECharSet(
+                                    t.bmsize, t.startIndex,
+                                    t.kidlen, t.sense);
+                    break;
+                default:
+                    break;
             }
             t = t.next;
         }
         return pc;
     }
 
-    private static void
-    pushProgState(REGlobalData gData, int min, int max, int cp,
-                  REBackTrackData backTrackLastToSave,
-                  int continuationOp, int continuationPc)
-    {
-        gData.stateStackTop = new REProgState(gData.stateStackTop, min, max,
-                                              cp, backTrackLastToSave,
-                                              continuationOp, continuationPc);
+    private static void pushProgState(
+            REGlobalData gData,
+            int min,
+            int max,
+            int cp,
+            REBackTrackData backTrackLastToSave,
+            int continuationOp,
+            int continuationPc) {
+        gData.stateStackTop =
+                new REProgState(
+                        gData.stateStackTop,
+                        min,
+                        max,
+                        cp,
+                        backTrackLastToSave,
+                        continuationOp,
+                        continuationPc);
     }
 
-    private static REProgState
-    popProgState(REGlobalData gData)
-    {
+    private static REProgState popProgState(REGlobalData gData) {
         REProgState state = gData.stateStackTop;
         gData.stateStackTop = state.previous;
         return state;
     }
 
-    private static void
-    pushBackTrackState(REGlobalData gData, byte op, int pc)
-    {
+    private static void pushBackTrackState(REGlobalData gData, byte op, int pc) {
         REProgState state = gData.stateStackTop;
-        gData.backTrackStackTop = new REBackTrackData(gData, op, pc,
-                gData.cp, state.continuationOp, state.continuationPc);
+        gData.backTrackStackTop =
+                new REBackTrackData(
+                        gData, op, pc, gData.cp, state.continuationOp, state.continuationPc);
     }
 
-    private static void
-    pushBackTrackState(REGlobalData gData, byte op, int pc,
-                       int cp, int continuationOp, int continuationPc)
-    {
-        gData.backTrackStackTop = new REBackTrackData(gData, op, pc,
-                cp, continuationOp, continuationPc);
+    private static void pushBackTrackState(
+            REGlobalData gData, byte op, int pc, int cp, int continuationOp, int continuationPc) {
+        gData.backTrackStackTop =
+                new REBackTrackData(gData, op, pc, cp, continuationOp, continuationPc);
     }
 
     /*
      *   Consecutive literal characters.
      */
-    private static boolean
-    flatNMatcher(REGlobalData gData, int matchChars,
-                 int length, String input, int end)
-    {
-        if ((gData.cp + length) > end)
-            return false;
+    private static boolean flatNMatcher(
+            REGlobalData gData, int matchChars, int length, String input, int end) {
+        if ((gData.cp + length) > end) return false;
         for (int i = 0; i < length; i++) {
             if (gData.regexp.source[matchChars + i] != input.charAt(gData.cp + i)) {
                 return false;
@@ -1410,12 +1324,9 @@
         return true;
     }
 
-    private static boolean
-    flatNIMatcher(REGlobalData gData, int matchChars,
-                  int length, String input, int end)
-    {
-        if ((gData.cp + length) > end)
-            return false;
+    private static boolean flatNIMatcher(
+            REGlobalData gData, int matchChars, int length, String input, int end) {
+        if ((gData.cp + length) > end) return false;
         char[] source = gData.regexp.source;
         for (int i = 0; i < length; i++) {
             char c1 = source[matchChars + i];
@@ -1451,63 +1362,48 @@
         9. Let y be the State (f, cap).
         10. Call c(y) and return its result.
     */
-    private static boolean
-    backrefMatcher(REGlobalData gData, int parenIndex,
-                   String input, int end)
-    {
+    private static boolean backrefMatcher(
+            REGlobalData gData, int parenIndex, String input, int end) {
         int len;
         int i;
-        if (gData.parens == null || parenIndex >= gData.parens.length)
-            return false;
+        if (gData.parens == null || parenIndex >= gData.parens.length) return false;
         int parenContent = gData.parensIndex(parenIndex);
-        if (parenContent == -1)
-            return true;
+        if (parenContent == -1) return true;
 
         len = gData.parensLength(parenIndex);
-        if ((gData.cp + len) > end)
-            return false;
+        if ((gData.cp + len) > end) return false;
 
         if ((gData.regexp.flags & JSREG_FOLD) != 0) {
             for (i = 0; i < len; i++) {
                 char c1 = input.charAt(parenContent + i);
                 char c2 = input.charAt(gData.cp + i);
-                if (c1 != c2 && upcase(c1) != upcase(c2))
-                    return false;
+                if (c1 != c2 && upcase(c1) != upcase(c2)) return false;
             }
-        }
-        else if (!input.regionMatches(parenContent, input, gData.cp, len)) {
+        } else if (!input.regionMatches(parenContent, input, gData.cp, len)) {
             return false;
         }
         gData.cp += len;
         return true;
     }
 
-
     /* Add a single character to the RECharSet */
-    private static void
-    addCharacterToCharSet(RECharSet cs, char c)
-    {
+    private static void addCharacterToCharSet(RECharSet cs, char c) {
         int byteIndex = (c / 8);
         if (c >= cs.length) {
-            throw ScriptRuntime.constructError("SyntaxError",
-                    "invalid range in character class");
+            throw ScriptRuntime.constructError("SyntaxError", "invalid range in character class");
         }
         cs.bits[byteIndex] |= 1 << (c & 0x7);
     }
 
-
     /* Add a character range, c1 to c2 (inclusive) to the RECharSet */
-    private static void
-    addCharacterRangeToCharSet(RECharSet cs, char c1, char c2)
-    {
+    private static void addCharacterRangeToCharSet(RECharSet cs, char c1, char c2) {
         int i;
 
         int byteIndex1 = (c1 / 8);
         int byteIndex2 = (c2 / 8);
 
         if ((c2 >= cs.length) || (c1 > c2)) {
-            throw ScriptRuntime.constructError("SyntaxError",
-                    "invalid range in character class");
+            throw ScriptRuntime.constructError("SyntaxError", "invalid range in character class");
         }
 
         c1 &= 0x7;
@@ -1515,19 +1411,15 @@
 
         if (byteIndex1 == byteIndex2) {
             cs.bits[byteIndex1] |= ((0xFF) >> (7 - (c2 - c1))) << c1;
-        }
-        else {
+        } else {
             cs.bits[byteIndex1] |= 0xFF << c1;
-            for (i = byteIndex1 + 1; i < byteIndex2; i++)
-                cs.bits[i] = (byte)0xFF;
+            for (i = byteIndex1 + 1; i < byteIndex2; i++) cs.bits[i] = (byte) 0xFF;
             cs.bits[byteIndex2] |= (0xFF) >> (7 - c2);
         }
     }
 
     /* Compile the source of the class into a RECharSet */
-    private static void
-    processCharSet(REGlobalData gData, RECharSet charSet)
-    {
+    private static void processCharSet(REGlobalData gData, RECharSet charSet) {
         synchronized (charSet) {
             if (!charSet.converted) {
                 processCharSetImpl(gData, charSet);
@@ -1536,10 +1428,7 @@
         }
     }
 
-
-    private static void
-    processCharSetImpl(REGlobalData gData, RECharSet charSet)
-    {
+    private static void processCharSetImpl(REGlobalData gData, RECharSet charSet) {
         int src = charSet.startIndex;
         int end = src + charSet.strlength;
 
@@ -1554,8 +1443,7 @@
         byteLength = (charSet.length + 7) / 8;
         charSet.bits = new byte[byteLength];
 
-        if (src == end)
-            return;
+        if (src == end) return;
 
         if (gData.regexp.source[src] == '^') {
             assert (!charSet.sense);
@@ -1567,148 +1455,160 @@
         while (src != end) {
             nDigits = 2;
             switch (gData.regexp.source[src]) {
-            case '\\':
-                ++src;
-                c = gData.regexp.source[src++];
-                switch (c) {
-                case 'b':
-                    thisCh = 0x8;
-                    break;
-                case 'f':
-                    thisCh = 0xC;
-                    break;
-                case 'n':
-                    thisCh = 0xA;
-                    break;
-                case 'r':
-                    thisCh = 0xD;
-                    break;
-                case 't':
-                    thisCh = 0x9;
-                    break;
-                case 'v':
-                    thisCh = 0xB;
-                    break;
-                case 'c':
-                    if ((src < end) && isControlLetter(gData.regexp.source[src]))
-                        thisCh = (char)(gData.regexp.source[src++] & 0x1F);
-                    else {
-                        --src;
-                        thisCh = '\\';
-                    }
-                    break;
-                case 'u':
-                    nDigits += 2;
-                    // fallthru
-                case 'x':
-                    n = 0;
-                    for (i = 0; (i < nDigits) && (src < end); i++) {
-                        c = gData.regexp.source[src++];
-                        int digit = toASCIIHexDigit(c);
-                        if (digit < 0) {
-                            /* back off to accepting the original '\'
-                             * as a literal
+                case '\\':
+                    ++src;
+                    c = gData.regexp.source[src++];
+                    switch (c) {
+                        case 'b':
+                            thisCh = 0x8;
+                            break;
+                        case 'f':
+                            thisCh = 0xC;
+                            break;
+                        case 'n':
+                            thisCh = 0xA;
+                            break;
+                        case 'r':
+                            thisCh = 0xD;
+                            break;
+                        case 't':
+                            thisCh = 0x9;
+                            break;
+                        case 'v':
+                            thisCh = 0xB;
+                            break;
+                        case 'c':
+                            if ((src < end) && isControlLetter(gData.regexp.source[src]))
+                                thisCh = (char) (gData.regexp.source[src++] & 0x1F);
+                            else {
+                                --src;
+                                thisCh = '\\';
+                            }
+                            break;
+                        case 'u':
+                            nDigits += 2;
+                            // fall through
+                        case 'x':
+                            n = 0;
+                            for (i = 0; (i < nDigits) && (src < end); i++) {
+                                c = gData.regexp.source[src++];
+                                int digit = toASCIIHexDigit(c);
+                                if (digit < 0) {
+                                    /* back off to accepting the original '\'
+                                     * as a literal
+                                     */
+                                    src -= (i + 1);
+                                    n = '\\';
+                                    break;
+                                }
+                                n = (n << 4) | digit;
+                            }
+                            thisCh = (char) (n);
+                            break;
+                        case '0':
+                        case '1':
+                        case '2':
+                        case '3':
+                        case '4':
+                        case '5':
+                        case '6':
+                        case '7':
+                            /*
+                             *  This is a non-ECMA extension - decimal escapes (in this
+                             *  case, octal!) are supposed to be an error inside class
+                             *  ranges, but supported here for backwards compatibility.
+                             *
                              */
-                            src -= (i + 1);
-                            n = '\\';
+                            n = (c - '0');
+                            c = gData.regexp.source[src];
+                            if ('0' <= c && c <= '7') {
+                                src++;
+                                n = 8 * n + (c - '0');
+                                c = gData.regexp.source[src];
+                                if ('0' <= c && c <= '7') {
+                                    src++;
+                                    i = 8 * n + (c - '0');
+                                    if (i <= 0377) n = i;
+                                    else src--;
+                                }
+                            }
+                            thisCh = (char) (n);
+                            break;
+
+                        case 'd':
+                            if (inRange) {
+                                addCharacterToCharSet(charSet, '-');
+                                inRange = false;
+                            }
+                            addCharacterRangeToCharSet(charSet, '0', '9');
+                            continue; /* don't need range processing */
+                        case 'D':
+                            if (inRange) {
+                                addCharacterToCharSet(charSet, '-');
+                                inRange = false;
+                            }
+                            addCharacterRangeToCharSet(charSet, (char) 0, (char) ('0' - 1));
+                            addCharacterRangeToCharSet(
+                                    charSet, (char) ('9' + 1), (char) (charSet.length - 1));
+                            continue;
+                        case 's':
+                            if (inRange) {
+                                addCharacterToCharSet(charSet, '-');
+                                inRange = false;
+                            }
+                            for (i = (charSet.length - 1); i >= 0; i--)
+                                if (isREWhiteSpace(i)) addCharacterToCharSet(charSet, (char) (i));
+                            continue;
+                        case 'S':
+                            if (inRange) {
+                                addCharacterToCharSet(charSet, '-');
+                                inRange = false;
+                            }
+                            for (i = (charSet.length - 1); i >= 0; i--)
+                                if (!isREWhiteSpace(i)) addCharacterToCharSet(charSet, (char) (i));
+                            continue;
+                        case 'w':
+                            if (inRange) {
+                                addCharacterToCharSet(charSet, '-');
+                                inRange = false;
+                            }
+                            for (i = (charSet.length - 1); i >= 0; i--)
+                                if (isWord((char) i)) addCharacterToCharSet(charSet, (char) (i));
+                            continue;
+                        case 'W':
+                            if (inRange) {
+                                addCharacterToCharSet(charSet, '-');
+                                inRange = false;
+                            }
+                            for (i = (charSet.length - 1); i >= 0; i--)
+                                if (!isWord((char) i)) addCharacterToCharSet(charSet, (char) (i));
+                            continue;
+                        default:
+                            thisCh = c;
                             break;
-                        }
-                        n = (n << 4) | digit;
-                    }
-                    thisCh = (char)(n);
-                    break;
-                case '0':
-                case '1':
-                case '2':
-                case '3':
-                case '4':
-                case '5':
-                case '6':
-                case '7':
-                    /*
-                     *  This is a non-ECMA extension - decimal escapes (in this
-                     *  case, octal!) are supposed to be an error inside class
-                     *  ranges, but supported here for backwards compatibility.
-                     *
-                     */
-                    n = (c - '0');
-                    c = gData.regexp.source[src];
-                    if ('0' <= c && c <= '7') {
-                        src++;
-                        n = 8 * n + (c - '0');
-                        c = gData.regexp.source[src];
-                        if ('0' <= c && c <= '7') {
-                            src++;
-                            i = 8 * n + (c - '0');
-                            if (i <= 0377)
-                                n = i;
-                            else
-                                src--;
-                        }
                     }
-                    thisCh = (char)(n);
                     break;
 
-                case 'd':
-                    addCharacterRangeToCharSet(charSet, '0', '9');
-                    continue;   /* don't need range processing */
-                case 'D':
-                    addCharacterRangeToCharSet(charSet, (char)0, (char)('0' - 1));
-                    addCharacterRangeToCharSet(charSet, (char)('9' + 1),
-                                                (char)(charSet.length - 1));
-                    continue;
-                case 's':
-                    for (i = (charSet.length - 1); i >= 0; i--)
-                        if (isREWhiteSpace(i))
-                            addCharacterToCharSet(charSet, (char)(i));
-                    continue;
-                case 'S':
-                    for (i = (charSet.length - 1); i >= 0; i--)
-                        if (!isREWhiteSpace(i))
-                            addCharacterToCharSet(charSet, (char)(i));
-                    continue;
-                case 'w':
-                    for (i = (charSet.length - 1); i >= 0; i--)
-                        if (isWord((char)i))
-                            addCharacterToCharSet(charSet, (char)(i));
-                    continue;
-                case 'W':
-                    for (i = (charSet.length - 1); i >= 0; i--)
-                        if (!isWord((char)i))
-                            addCharacterToCharSet(charSet, (char)(i));
-                    continue;
                 default:
-                    thisCh = c;
+                    thisCh = gData.regexp.source[src++];
                     break;
-
-                }
-                break;
-
-            default:
-                thisCh = gData.regexp.source[src++];
-                break;
-
             }
             if (inRange) {
                 if ((gData.regexp.flags & JSREG_FOLD) != 0) {
-                    assert(rangeStart <= thisCh);
-                    for (c = rangeStart; c <= thisCh;) {
+                    assert (rangeStart <= thisCh);
+                    for (c = rangeStart; c <= thisCh; ) {
                         addCharacterToCharSet(charSet, c);
                         char uch = upcase(c);
                         char dch = downcase(c);
-                        if (c != uch)
-                            addCharacterToCharSet(charSet, uch);
-                        if (c != dch)
-                            addCharacterToCharSet(charSet, dch);
-                        if (++c == 0)
-                            break; // overflow
+                        if (c != uch) addCharacterToCharSet(charSet, uch);
+                        if (c != dch) addCharacterToCharSet(charSet, dch);
+                        if (++c == 0) break; // overflow
                     }
                 } else {
                     addCharacterRangeToCharSet(charSet, rangeStart, thisCh);
                 }
                 inRange = false;
-            }
-            else {
+            } else {
                 if ((gData.regexp.flags & JSREG_FOLD) != 0) {
                     addCharacterToCharSet(charSet, upcase(thisCh));
                     addCharacterToCharSet(charSet, downcase(thisCh));
@@ -1726,22 +1626,20 @@
         }
     }
 
-
     /*
      *   Initialize the character set if it this is the first call.
      *   Test the bit - if the ^ flag was specified, non-inclusion is a success
      */
-    private static boolean
-    classMatcher(REGlobalData gData, RECharSet charSet, char ch)
-    {
+    private static boolean classMatcher(REGlobalData gData, RECharSet charSet, char ch) {
         if (!charSet.converted) {
             processCharSet(gData, charSet);
         }
 
         int byteIndex = ch >> 3;
-        return (charSet.length == 0 ||
-                ch >= charSet.length ||
-                (charSet.bits[byteIndex] & (1 << (ch & 0x7))) == 0) ^ charSet.sense;
+        return (charSet.length == 0
+                        || ch >= charSet.length
+                        || (charSet.bits[byteIndex] & (1 << (ch & 0x7))) == 0)
+                ^ charSet.sense;
     }
 
     private static boolean reopIsSimple(int op) {
@@ -1754,9 +1652,14 @@
      *   get a match, true if we do and update the state of the
      *   input and pc if the update flag is true.
      */
-    private static int simpleMatch(REGlobalData gData, String input, int op,
-                                   byte[] program, int pc, int end, boolean updatecp)
-    {
+    private static int simpleMatch(
+            REGlobalData gData,
+            String input,
+            int op,
+            byte[] program,
+            int pc,
+            int end,
+            boolean updatecp) {
         boolean result = false;
         char matchCh;
         int parenIndex;
@@ -1784,12 +1687,14 @@
                 result = true;
                 break;
             case REOP_WBDRY:
-                result = ((gData.cp == 0 || !isWord(input.charAt(gData.cp - 1)))
-                        ^ !((gData.cp < end) && isWord(input.charAt(gData.cp))));
+                result =
+                        ((gData.cp == 0 || !isWord(input.charAt(gData.cp - 1)))
+                                ^ !((gData.cp < end) && isWord(input.charAt(gData.cp))));
                 break;
             case REOP_WNONBDRY:
-                result = ((gData.cp == 0 || !isWord(input.charAt(gData.cp - 1)))
-                        ^ ((gData.cp < end) && isWord(input.charAt(gData.cp))));
+                result =
+                        ((gData.cp == 0 || !isWord(input.charAt(gData.cp - 1)))
+                                ^ ((gData.cp < end) && isWord(input.charAt(gData.cp))));
                 break;
             case REOP_DOT:
                 if (gData.cp != end && !isLineTerm(input.charAt(gData.cp))) {
@@ -1834,108 +1739,103 @@
                 }
                 break;
             case REOP_BACKREF:
-            {
-                parenIndex = getIndex(program, pc);
-                pc += INDEX_LEN;
-                result = backrefMatcher(gData, parenIndex, input, end);
-            }
-            break;
+                {
+                    parenIndex = getIndex(program, pc);
+                    pc += INDEX_LEN;
+                    result = backrefMatcher(gData, parenIndex, input, end);
+                }
+                break;
             case REOP_FLAT:
-            {
-                offset = getIndex(program, pc);
-                pc += INDEX_LEN;
-                length = getIndex(program, pc);
-                pc += INDEX_LEN;
-                result = flatNMatcher(gData, offset, length, input, end);
-            }
-            break;
+                {
+                    offset = getIndex(program, pc);
+                    pc += INDEX_LEN;
+                    length = getIndex(program, pc);
+                    pc += INDEX_LEN;
+                    result = flatNMatcher(gData, offset, length, input, end);
+                }
+                break;
             case REOP_FLAT1:
-            {
-                matchCh = (char)(program[pc++] & 0xFF);
-                if (gData.cp != end && input.charAt(gData.cp) == matchCh) {
-                    result = true;
-                    gData.cp++;
+                {
+                    matchCh = (char) (program[pc++] & 0xFF);
+                    if (gData.cp != end && input.charAt(gData.cp) == matchCh) {
+                        result = true;
+                        gData.cp++;
+                    }
                 }
-            }
-            break;
+                break;
             case REOP_FLATi:
-            {
-                offset = getIndex(program, pc);
-                pc += INDEX_LEN;
-                length = getIndex(program, pc);
-                pc += INDEX_LEN;
-                result = flatNIMatcher(gData, offset, length, input, end);
-            }
-            break;
+                {
+                    offset = getIndex(program, pc);
+                    pc += INDEX_LEN;
+                    length = getIndex(program, pc);
+                    pc += INDEX_LEN;
+                    result = flatNIMatcher(gData, offset, length, input, end);
+                }
+                break;
             case REOP_FLAT1i:
-            {
-                matchCh = (char)(program[pc++] & 0xFF);
-                if (gData.cp != end) {
-                    char c = input.charAt(gData.cp);
-                    if (matchCh == c || upcase(matchCh) == upcase(c)) {
-                        result = true;
-                        gData.cp++;
+                {
+                    matchCh = (char) (program[pc++] & 0xFF);
+                    if (gData.cp != end) {
+                        char c = input.charAt(gData.cp);
+                        if (matchCh == c || upcase(matchCh) == upcase(c)) {
+                            result = true;
+                            gData.cp++;
+                        }
                     }
                 }
-            }
-            break;
+                break;
             case REOP_UCFLAT1:
-            {
-                matchCh = (char)getIndex(program, pc);
-                pc += INDEX_LEN;
-                if (gData.cp != end && input.charAt(gData.cp) == matchCh) {
-                    result = true;
-                    gData.cp++;
-                }
-            }
-            break;
-            case REOP_UCFLAT1i:
-            {
-                matchCh = (char)getIndex(program, pc);
-                pc += INDEX_LEN;
-                if (gData.cp != end) {
-                    char c = input.charAt(gData.cp);
-                    if (matchCh == c || upcase(matchCh) == upcase(c)) {
+                {
+                    matchCh = (char) getIndex(program, pc);
+                    pc += INDEX_LEN;
+                    if (gData.cp != end && input.charAt(gData.cp) == matchCh) {
                         result = true;
                         gData.cp++;
                     }
                 }
-            }
-            break;
+                break;
+            case REOP_UCFLAT1i:
+                {
+                    matchCh = (char) getIndex(program, pc);
+                    pc += INDEX_LEN;
+                    if (gData.cp != end) {
+                        char c = input.charAt(gData.cp);
+                        if (matchCh == c || upcase(matchCh) == upcase(c)) {
+                            result = true;
+                            gData.cp++;
+                        }
+                    }
+                }
+                break;
 
             case REOP_CLASS:
             case REOP_NCLASS:
-            {
-                index = getIndex(program, pc);
-                pc += INDEX_LEN;
-                if (gData.cp != end) {
-                    if (classMatcher(gData, gData.regexp.classList[index],
-                            input.charAt(gData.cp)))
-                    {
-                        gData.cp++;
-                        result = true;
-                        break;
+                {
+                    index = getIndex(program, pc);
+                    pc += INDEX_LEN;
+                    if (gData.cp != end) {
+                        if (classMatcher(
+                                gData, gData.regexp.classList[index], input.charAt(gData.cp))) {
+                            gData.cp++;
+                            result = true;
+                            break;
+                        }
                     }
                 }
-            }
-            break;
+                break;
 
             default:
                 throw Kit.codeBug();
         }
         if (result) {
-            if (!updatecp)
-                gData.cp = startcp;
+            if (!updatecp) gData.cp = startcp;
             return pc;
         }
         gData.cp = startcp;
         return -1;
     }
 
-
-    private static boolean
-    executeREBytecode(REGlobalData gData, String input, int end)
-    {
+    private static boolean executeREBytecode(REGlobalData gData, String input, int end) {
         int pc = 0;
         byte program[] = gData.regexp.program;
         int continuationOp = REOP_END;
@@ -1954,153 +1854,165 @@
                 int match = simpleMatch(gData, input, op, program, pc, end, true);
                 if (match >= 0) {
                     anchor = true;
-                    pc = match;    /* accept skip to next opcode */
+                    pc = match; /* accept skip to next opcode */
                     op = program[pc++];
                     break;
                 }
                 gData.skipped++;
                 gData.cp++;
             }
-            if (!anchor)
-                return false;
+            if (!anchor) return false;
         }
 
-        for (;;) {
+        for (; ; ) {
 
             if (reopIsSimple(op)) {
                 int match = simpleMatch(gData, input, op, program, pc, end, true);
                 result = match >= 0;
-                if (result)
-                    pc = match;    /* accept skip to next opcode */
+                if (result) pc = match; /* accept skip to next opcode */
             } else {
                 switchStatement:
                 switch (op) {
                     case REOP_ALTPREREQ:
                     case REOP_ALTPREREQi:
                     case REOP_ALTPREREQ2:
-                    {
-                        char matchCh1 = (char)getIndex(program, pc);
-                        pc += INDEX_LEN;
-                        char matchCh2 = (char)getIndex(program, pc);
-                        pc += INDEX_LEN;
+                        {
+                            char matchCh1 = (char) getIndex(program, pc);
+                            pc += INDEX_LEN;
+                            char matchCh2 = (char) getIndex(program, pc);
+                            pc += INDEX_LEN;
 
-                        if (gData.cp == end) {
-                            result = false;
-                            break;
-                        }
-                        char c = input.charAt(gData.cp);
-                        if (op == REOP_ALTPREREQ2) {
-                            if (c != matchCh1 &&
-                                !classMatcher(gData, gData.regexp.classList[matchCh2], c)) {
+                            if (gData.cp == end) {
                                 result = false;
                                 break;
                             }
-                        } else {
-                            if (op == REOP_ALTPREREQi)
-                                c = upcase(c);
-                            if (c != matchCh1 && c != matchCh2) {
-                                result = false;
-                                break;
+                            char c = input.charAt(gData.cp);
+                            if (op == REOP_ALTPREREQ2) {
+                                if (c != matchCh1
+                                        && !classMatcher(
+                                                gData, gData.regexp.classList[matchCh2], c)) {
+                                    result = false;
+                                    break;
+                                }
+                            } else {
+                                if (op == REOP_ALTPREREQi) c = upcase(c);
+                                if (c != matchCh1 && c != matchCh2) {
+                                    result = false;
+                                    break;
+                                }
                             }
                         }
-                    }
-                    /* else false thru... */
+                        /* else false thru... */
+                        // fall through
                     case REOP_ALT:
-                    {
-                        int nextpc = pc + getOffset(program, pc);
-                        pc += INDEX_LEN;
-                        op = program[pc++];
-                        int startcp = gData.cp;
-                        if (reopIsSimple(op)) {
-                            int match = simpleMatch(gData, input, op, program, pc, end, true);
-                            if (match < 0) {
-                                op = program[nextpc++];
-                                pc = nextpc;
-                                continue;
-                            }
-                            result = true;
-                            pc = match;
+                        {
+                            int nextpc = pc + getOffset(program, pc);
+                            pc += INDEX_LEN;
                             op = program[pc++];
+                            int startcp = gData.cp;
+                            if (reopIsSimple(op)) {
+                                int match = simpleMatch(gData, input, op, program, pc, end, true);
+                                if (match < 0) {
+                                    op = program[nextpc++];
+                                    pc = nextpc;
+                                    continue;
+                                }
+                                result = true;
+                                pc = match;
+                                op = program[pc++];
+                            }
+                            byte nextop = program[nextpc++];
+                            pushBackTrackState(
+                                    gData, nextop, nextpc, startcp, continuationOp, continuationPc);
                         }
-                        byte nextop = program[nextpc++];
-                        pushBackTrackState(gData, nextop, nextpc, startcp,
-                                continuationOp, continuationPc);
-                    }
-                    continue;
+                        continue;
 
                     case REOP_JUMP:
-                    {
-                        int offset = getOffset(program, pc);
-                        pc += offset;
-                        op = program[pc++];
-                    }
-                    continue;
-
+                        {
+                            int offset = getOffset(program, pc);
+                            pc += offset;
+                            op = program[pc++];
+                        }
+                        continue;
 
                     case REOP_LPAREN:
-                    {
-                        int parenIndex = getIndex(program, pc);
-                        pc += INDEX_LEN;
-                        gData.setParens(parenIndex, gData.cp, 0);
-                        op = program[pc++];
-                    }
-                    continue;
+                        {
+                            int parenIndex = getIndex(program, pc);
+                            pc += INDEX_LEN;
+                            gData.setParens(parenIndex, gData.cp, 0);
+                            op = program[pc++];
+                        }
+                        continue;
                     case REOP_RPAREN:
-                    {
-                        int parenIndex = getIndex(program, pc);
-                        pc += INDEX_LEN;
-                        int cap_index = gData.parensIndex(parenIndex);
-                        gData.setParens(parenIndex, cap_index,
-                                gData.cp - cap_index);
-                        op = program[pc++];
-                    }
-                    continue;
+                        {
+                            int parenIndex = getIndex(program, pc);
+                            pc += INDEX_LEN;
+                            int cap_index = gData.parensIndex(parenIndex);
+                            gData.setParens(parenIndex, cap_index, gData.cp - cap_index);
+                            op = program[pc++];
+                        }
+                        continue;
 
                     case REOP_ASSERT:
-                    {
-                        int nextpc = pc + getIndex(program, pc); /* start of term after ASSERT */
-                        pc += INDEX_LEN;                         /* start of ASSERT child */
-                        op = program[pc++];
-                        if (reopIsSimple(op) && simpleMatch(gData, input, op, program, pc, end, false) < 0) {
-                            result = false;
-                            break;
-                        }
-                        pushProgState(gData, 0, 0, gData.cp, gData.backTrackStackTop,
-                                continuationOp, continuationPc);
-                        pushBackTrackState(gData, REOP_ASSERTTEST, nextpc);
-                    }
-                    continue;
-                    case REOP_ASSERT_NOT:
-                    {
-                        int nextpc = pc + getIndex(program, pc); /* start of term after ASSERT */
-                        pc += INDEX_LEN;                         /* start of ASSERT child */
-                        op = program[pc++];
-                        if (reopIsSimple(op)) {
-                            int match = simpleMatch(gData, input, op, program, pc, end, false);
-                            if (match >= 0 && program[match] == REOP_ASSERTNOTTEST) {
+                        {
+                            int nextpc =
+                                    pc + getIndex(program, pc); /* start of term after ASSERT */
+                            pc += INDEX_LEN; /* start of ASSERT child */
+                            op = program[pc++];
+                            if (reopIsSimple(op)
+                                    && simpleMatch(gData, input, op, program, pc, end, false) < 0) {
                                 result = false;
                                 break;
                             }
+                            pushProgState(
+                                    gData,
+                                    0,
+                                    0,
+                                    gData.cp,
+                                    gData.backTrackStackTop,
+                                    continuationOp,
+                                    continuationPc);
+                            pushBackTrackState(gData, REOP_ASSERTTEST, nextpc);
                         }
-                        pushProgState(gData, 0, 0, gData.cp, gData.backTrackStackTop,
-                                continuationOp, continuationPc);
-                        pushBackTrackState(gData, REOP_ASSERTNOTTEST, nextpc);
-                    }
-                    continue;
+                        continue;
+                    case REOP_ASSERT_NOT:
+                        {
+                            int nextpc =
+                                    pc + getIndex(program, pc); /* start of term after ASSERT */
+                            pc += INDEX_LEN; /* start of ASSERT child */
+                            op = program[pc++];
+                            if (reopIsSimple(op)) {
+                                int match = simpleMatch(gData, input, op, program, pc, end, false);
+                                if (match >= 0 && program[match] == REOP_ASSERTNOTTEST) {
+                                    result = false;
+                                    break;
+                                }
+                            }
+                            pushProgState(
+                                    gData,
+                                    0,
+                                    0,
+                                    gData.cp,
+                                    gData.backTrackStackTop,
+                                    continuationOp,
+                                    continuationPc);
+                            pushBackTrackState(gData, REOP_ASSERTNOTTEST, nextpc);
+                        }
+                        continue;
 
                     case REOP_ASSERTTEST:
                     case REOP_ASSERTNOTTEST:
-                    {
-                        REProgState state = popProgState(gData);
-                        gData.cp = state.index;
-                        gData.backTrackStackTop = state.backTrack;
-                        continuationPc = state.continuationPc;
-                        continuationOp = state.continuationOp;
-                        if (op == REOP_ASSERTNOTTEST) {
-                            result = !result;
+                        {
+                            REProgState state = popProgState(gData);
+                            gData.cp = state.index;
+                            gData.backTrackStackTop = state.backTrack;
+                            continuationPc = state.continuationPc;
+                            continuationOp = state.continuationOp;
+                            if (op == REOP_ASSERTNOTTEST) {
+                                result = !result;
+                            }
                         }
-                    }
-                    break;
+                        break;
 
                     case REOP_STAR:
                     case REOP_PLUS:
@@ -2110,70 +2022,76 @@
                     case REOP_MINIMALPLUS:
                     case REOP_MINIMALOPT:
                     case REOP_MINIMALQUANT:
-                    {
-                        int min, max;
-                        boolean greedy = false;
-                        switch (op) {
-                            case REOP_STAR:
-                                greedy = true;
-                                // fallthrough
-                            case REOP_MINIMALSTAR:
-                                min = 0;
-                                max = -1;
-                                break;
-                            case REOP_PLUS:
-                                greedy = true;
-                                // fallthrough
-                            case REOP_MINIMALPLUS:
-                                min = 1;
-                                max = -1;
-                                break;
-                            case REOP_OPT:
-                                greedy = true;
-                                // fallthrough
-                            case REOP_MINIMALOPT:
-                                min = 0;
-                                max = 1;
-                                break;
-                            case REOP_QUANT:
-                                greedy = true;
-                                // fallthrough
-                            case REOP_MINIMALQUANT:
-                                min = getOffset(program, pc);
-                                pc += INDEX_LEN;
-                                // See comments in emitREBytecode for " - 1" reason
-                                max = getOffset(program, pc) - 1;
-                                pc += INDEX_LEN;
-                                break;
-                            default:
-                                throw Kit.codeBug();
-                        }
-                        pushProgState(gData, min, max, gData.cp, null,
-                                continuationOp, continuationPc);
-                        if (greedy) {
-                            pushBackTrackState(gData, REOP_REPEAT, pc);
-                            continuationOp = REOP_REPEAT;
-                            continuationPc = pc;
-                            /* Step over <parencount>, <parenindex> & <next> */
-                            pc += 3 * INDEX_LEN;
-                            op = program[pc++];
-                        } else {
-                            if (min != 0) {
-                                continuationOp = REOP_MINIMALREPEAT;
+                        {
+                            int min, max;
+                            boolean greedy = false;
+                            switch (op) {
+                                case REOP_STAR:
+                                    greedy = true;
+                                    // fallthrough
+                                case REOP_MINIMALSTAR:
+                                    min = 0;
+                                    max = -1;
+                                    break;
+                                case REOP_PLUS:
+                                    greedy = true;
+                                    // fallthrough
+                                case REOP_MINIMALPLUS:
+                                    min = 1;
+                                    max = -1;
+                                    break;
+                                case REOP_OPT:
+                                    greedy = true;
+                                    // fallthrough
+                                case REOP_MINIMALOPT:
+                                    min = 0;
+                                    max = 1;
+                                    break;
+                                case REOP_QUANT:
+                                    greedy = true;
+                                    // fallthrough
+                                case REOP_MINIMALQUANT:
+                                    min = getOffset(program, pc);
+                                    pc += INDEX_LEN;
+                                    // See comments in emitREBytecode for " - 1" reason
+                                    max = getOffset(program, pc) - 1;
+                                    pc += INDEX_LEN;
+                                    break;
+                                default:
+                                    throw Kit.codeBug();
+                            }
+                            pushProgState(
+                                    gData,
+                                    min,
+                                    max,
+                                    gData.cp,
+                                    null,
+                                    continuationOp,
+                                    continuationPc);
+                            if (greedy) {
+                                pushBackTrackState(gData, REOP_REPEAT, pc);
+                                continuationOp = REOP_REPEAT;
                                 continuationPc = pc;
-                                /* <parencount> <parenindex> & <next> */
+                                /* Step over <parencount>, <parenindex> & <next> */
                                 pc += 3 * INDEX_LEN;
                                 op = program[pc++];
                             } else {
-                                pushBackTrackState(gData, REOP_MINIMALREPEAT, pc);
-                                popProgState(gData);
-                                pc += 2 * INDEX_LEN;  // <parencount> & <parenindex>
-                                pc = pc + getOffset(program, pc);
-                                op = program[pc++];
+                                if (min != 0) {
+                                    continuationOp = REOP_MINIMALREPEAT;
+                                    continuationPc = pc;
+                                    /* <parencount> <parenindex> & <next> */
+                                    pc += 3 * INDEX_LEN;
+                                    op = program[pc++];
+                                } else {
+                                    pushBackTrackState(gData, REOP_MINIMALREPEAT, pc);
+                                    popProgState(gData);
+                                    pc += 2 * INDEX_LEN; // <parencount> & <parenindex>
+                                    pc = pc + getOffset(program, pc);
+                                    op = program[pc++];
+                                }
                             }
                         }
-                    }
-                    continue;
+                        continue;
 
                     case REOP_ENDCHILD: /* marks the end of a quantifier child */
                         // If we have not gotten a result here, it is because of an
@@ -2185,105 +2103,123 @@
                         continue;
 
                     case REOP_REPEAT:
-                    {
-                        int nextpc, nextop;
-                        do {
-                            REProgState state = popProgState(gData);
-                            if (!result) {
-                                // Failed, see if we have enough children.
-                                if (state.min == 0)
+                        {
+                            int nextpc, nextop;
+                            do {
+                                REProgState state = popProgState(gData);
+                                if (!result) {
+                                    // Failed, see if we have enough children.
+                                    if (state.min == 0) result = true;
+                                    continuationPc = state.continuationPc;
+                                    continuationOp = state.continuationOp;
+                                    pc += 2 * INDEX_LEN; /* <parencount> & <parenindex> */
+                                    pc += getOffset(program, pc);
+                                    break switchStatement;
+                                }
+                                if (state.min == 0 && gData.cp == state.index) {
+                                    // matched an empty string, that'll get us nowhere
+                                    result = false;
+                                    continuationPc = state.continuationPc;
+                                    continuationOp = state.continuationOp;
+                                    pc += 2 * INDEX_LEN;
+                                    pc += getOffset(program, pc);
+                                    break switchStatement;
+                                }
+                                int new_min = state.min, new_max = state.max;
+                                if (new_min != 0) new_min--;
+                                if (new_max != -1) new_max--;
+                                if (new_max == 0) {
                                     result = true;
-                                continuationPc = state.continuationPc;
-                                continuationOp = state.continuationOp;
-                                pc += 2 * INDEX_LEN;  /* <parencount> & <parenindex> */
-                                pc += getOffset(program, pc);
-                                break switchStatement;
-                            }
-                            if (state.min == 0 && gData.cp == state.index) {
-                                // matched an empty string, that'll get us nowhere
-                                result = false;
-                                continuationPc = state.continuationPc;
-                                continuationOp = state.continuationOp;
-                                pc += 2 * INDEX_LEN;
-                                pc += getOffset(program, pc);
-                                break switchStatement;
-                            }
-                            int new_min = state.min, new_max = state.max;
-                            if (new_min != 0) new_min--;
-                            if (new_max != -1) new_max--;
-                            if (new_max == 0) {
-                                result = true;
-                                continuationPc = state.continuationPc;
-                                continuationOp = state.continuationOp;
-                                pc += 2 * INDEX_LEN;
-                                pc += getOffset(program, pc);
-                                break switchStatement;
-                            }
-                            nextpc = pc + 3 * INDEX_LEN;
-                            nextop = program[nextpc];
-                            int startcp = gData.cp;
-                            if (reopIsSimple(nextop)) {
-                                nextpc++;
-                                int match = simpleMatch(gData, input, nextop, program, nextpc, end, true);
-                                if (match < 0) {
-                                    result = (new_min == 0);
                                     continuationPc = state.continuationPc;
                                     continuationOp = state.continuationOp;
-                                    pc += 2 * INDEX_LEN;  /* <parencount> & <parenindex> */
+                                    pc += 2 * INDEX_LEN;
                                     pc += getOffset(program, pc);
                                     break switchStatement;
                                 }
-                                result = true;
-                                nextpc = match;
-                            }
-                            continuationOp = REOP_REPEAT;
-                            continuationPc = pc;
-                            pushProgState(gData, new_min, new_max, startcp, null,
-                                    state.continuationOp, state.continuationPc);
-                            if (new_min == 0) {
-                                pushBackTrackState(gData, REOP_REPEAT, pc, startcp,
-                                        state.continuationOp, state.continuationPc);
-                                int parenCount = getIndex(program, pc);
-                                int parenIndex = getIndex(program, pc + INDEX_LEN);
-                                for (int k = 0; k < parenCount; k++) {
-                                    gData.setParens(parenIndex + k, -1, 0);
+                                nextpc = pc + 3 * INDEX_LEN;
+                                nextop = program[nextpc];
+                                int startcp = gData.cp;
+                                if (reopIsSimple(nextop)) {
+                                    nextpc++;
+                                    int match =
+                                            simpleMatch(
+                                                    gData, input, nextop, program, nextpc, end,
+                                                    true);
+                                    if (match < 0) {
+                                        result = (new_min == 0);
+                                        continuationPc = state.continuationPc;
+                                        continuationOp = state.continuationOp;
+                                        pc += 2 * INDEX_LEN; /* <parencount> & <parenindex> */
+                                        pc += getOffset(program, pc);
+                                        break switchStatement;
+                                    }
+                                    result = true;
+                                    nextpc = match;
                                 }
-                            }
-                        } while (program[nextpc] == REOP_ENDCHILD);
+                                continuationOp = REOP_REPEAT;
+                                continuationPc = pc;
+                                pushProgState(
+                                        gData,
+                                        new_min,
+                                        new_max,
+                                        startcp,
+                                        null,
+                                        state.continuationOp,
+                                        state.continuationPc);
+                                if (new_min == 0) {
+                                    pushBackTrackState(
+                                            gData,
+                                            REOP_REPEAT,
+                                            pc,
+                                            startcp,
+                                            state.continuationOp,
+                                            state.continuationPc);
+                                    int parenCount = getIndex(program, pc);
+                                    int parenIndex = getIndex(program, pc + INDEX_LEN);
+                                    for (int k = 0; k < parenCount; k++) {
+                                        gData.setParens(parenIndex + k, -1, 0);
+                                    }
+                                }
+                            } while (program[nextpc] == REOP_ENDCHILD);
 
-                        pc = nextpc;
-                        op = program[pc++];
-                    }
-                    continue;
+                            pc = nextpc;
+                            op = program[pc++];
+                        }
+                        continue;
 
                     case REOP_MINIMALREPEAT:
-                    {
-                        REProgState state = popProgState(gData);
-                        if (!result) {
-                            //
-                            // Non-greedy failure - try to consume another child.
-                            //
-                            if (state.max == -1 || state.max > 0) {
-                                pushProgState(gData, state.min, state.max, gData.cp, null,
-                                        state.continuationOp, state.continuationPc);
-                                continuationOp = REOP_MINIMALREPEAT;
-                                continuationPc = pc;
-                                int parenCount = getIndex(program, pc);
-                                pc += INDEX_LEN;
-                                int parenIndex = getIndex(program, pc);
-                                pc += 2 * INDEX_LEN;
-                                for (int k = 0; k < parenCount; k++) {
-                                    gData.setParens(parenIndex + k, -1, 0);
+                        {
+                            REProgState state = popProgState(gData);
+                            if (!result) {
+                                //
+                                // Non-greedy failure - try to consume another child.
+                                //
+                                if (state.max == -1 || state.max > 0) {
+                                    pushProgState(
+                                            gData,
+                                            state.min,
+                                            state.max,
+                                            gData.cp,
+                                            null,
+                                            state.continuationOp,
+                                            state.continuationPc);
+                                    continuationOp = REOP_MINIMALREPEAT;
+                                    continuationPc = pc;
+                                    int parenCount = getIndex(program, pc);
+                                    pc += INDEX_LEN;
+                                    int parenIndex = getIndex(program, pc);
+                                    pc += 2 * INDEX_LEN;
+                                    for (int k = 0; k < parenCount; k++) {
+                                        gData.setParens(parenIndex + k, -1, 0);
+                                    }
+                                    op = program[pc++];
+                                    continue;
                                 }
-                                op = program[pc++];
-                                continue;
-                            } else {
                                 // Don't need to adjust pc since we're going to pop.
                                 continuationPc = state.continuationPc;
                                 continuationOp = state.continuationOp;
                                 break;
                             }
-                        } else {
                             if (state.min == 0 && gData.cp == state.index) {
                                 // Matched an empty string, that'll get us nowhere.
                                 result = false;
@@ -2294,8 +2230,14 @@
                             int new_min = state.min, new_max = state.max;
                             if (new_min != 0) new_min--;
                             if (new_max != -1) new_max--;
-                            pushProgState(gData, new_min, new_max, gData.cp, null,
-                                    state.continuationOp, state.continuationPc);
+                            pushProgState(
+                                    gData,
+                                    new_min,
+                                    new_max,
+                                    gData.cp,
+                                    null,
+                                    state.continuationOp,
+                                    state.continuationPc);
                             if (new_min != 0) {
                                 continuationOp = REOP_MINIMALREPEAT;
                                 continuationPc = pc;
@@ -2318,14 +2260,12 @@
                             }
                             continue;
                         }
-                    }
 
                     case REOP_END:
                         return true;
 
                     default:
                         throw Kit.codeBug("invalid bytecode");
-
                 }
             }
             /*
@@ -2345,19 +2285,20 @@
                     op = backTrackData.op;
                     continue;
                 }
-                else
-                    return false;
+                return false;
             }
 
             op = program[pc++];
         }
-
     }
 
-    private static boolean
-    matchRegExp(REGlobalData gData, RECompiled re,
-                String input, int start, int end, boolean multiline)
-    {
+    private static boolean matchRegExp(
+            REGlobalData gData,
+            RECompiled re,
+            String input,
+            int start,
+            int end,
+            boolean multiline) {
         if (re.parenCount != 0) {
             gData.parens = new long[re.parenCount];
         } else {
@@ -2382,15 +2323,14 @@
             // found at all.
             //
             if (anchorCh >= 0) {
-                for (;;) {
+                for (; ; ) {
                     if (i == end) {
                         return false;
                     }
                     char matchCh = input.charAt(i);
-                    if (matchCh == anchorCh ||
-                            ((gData.regexp.flags & JSREG_FOLD) != 0
-                             && upcase(matchCh) == upcase((char)anchorCh)))
-                    {
+                    if (matchCh == anchorCh
+                            || ((gData.regexp.flags & JSREG_FOLD) != 0
+                                    && upcase(matchCh) == upcase((char) anchorCh))) {
                         break;
                     }
                     ++i;
@@ -2420,20 +2360,17 @@
     /*
      * indexp is assumed to be an array of length 1
      */
-    Object executeRegExp(Context cx, Scriptable scope, RegExpImpl res,
-                         String str, int indexp[], int matchType)
-    {
+    Object executeRegExp(
+            Context cx, Scriptable scope, RegExpImpl res, String str, int indexp[], int matchType) {
         REGlobalData gData = new REGlobalData();
 
         int start = indexp[0];
         int end = str.length();
-        if (start > end)
-            start = end;
+        if (start > end) start = end;
         //
         // Call the recursive matcher to do the real work.
         //
-        boolean matches = matchRegExp(gData, re, str, start, end,
-                                      res.multiline);
+        boolean matches = matchRegExp(gData, re, str, start, end, res.multiline);
         if (!matches) {
             if (matchType != PREFIX) return null;
             return Undefined.instance;
@@ -2452,8 +2389,7 @@
              */
             result = Boolean.TRUE;
             obj = null;
-        }
-        else {
+        } else {
             /*
              * The array returned on match has element 0 bound to the matched
              * string, elements 1 through re.parenCount bound to the paren
@@ -2469,30 +2405,26 @@
 
         if (re.parenCount == 0) {
             res.parens = null;
-            res.lastParen = SubString.emptySubString;
+            res.lastParen = new SubString();
         } else {
             SubString parsub = null;
             int num;
             res.parens = new SubString[re.parenCount];
             for (num = 0; num < re.parenCount; num++) {
                 int cap_index = gData.parensIndex(num);
-                String parstr;
                 if (cap_index != -1) {
                     int cap_length = gData.parensLength(num);
                     parsub = new SubString(str, cap_index, cap_length);
                     res.parens[num] = parsub;
-                    if (matchType != TEST)
-                        obj.put(num+1, obj, parsub.toString());
-                }
-                else {
-                    if (matchType != TEST)
-                        obj.put(num+1, obj, Undefined.instance);
+                    if (matchType != TEST) obj.put(num + 1, obj, parsub.toString());
+                } else {
+                    if (matchType != TEST) obj.put(num + 1, obj, Undefined.instance);
                 }
             }
             res.lastParen = parsub;
         }
 
-        if (! (matchType == TEST)) {
+        if (!(matchType == TEST)) {
             /*
              * Define the index and input properties last for better for/in loop
              * order (so they come after the elements).
@@ -2544,128 +2476,129 @@
         return result;
     }
 
-    int getFlags()
-    {
+    int getFlags() {
         return re.flags;
     }
 
-    private static void reportWarning(Context cx, String messageId, String arg)
-    {
+    private static void reportWarning(Context cx, String messageId, String arg) {
         if (cx.hasFeature(Context.FEATURE_STRICT_MODE)) {
-            String msg = ScriptRuntime.getMessage1(messageId, arg);
+            String msg = ScriptRuntime.getMessageById(messageId, arg);
             Context.reportWarning(msg);
         }
     }
 
-    private static void reportError(String messageId, String arg)
-    {
-        String msg = ScriptRuntime.getMessage1(messageId, arg);
+    private static void reportError(String messageId, String arg) {
+        String msg = ScriptRuntime.getMessageById(messageId, arg);
         throw ScriptRuntime.constructError("SyntaxError", msg);
     }
 
-// #string_id_map#
-
-    private static final int
-        Id_lastIndex    = 1,
-        Id_source       = 2,
-        Id_global       = 3,
-        Id_ignoreCase   = 4,
-        Id_multiline    = 5,
-
-        MAX_INSTANCE_ID = 5;
+    private static final int Id_lastIndex = 1,
+            Id_source = 2,
+            Id_global = 3,
+            Id_ignoreCase = 4,
+            Id_multiline = 5,
+            MAX_INSTANCE_ID = 5;
 
     @Override
-    protected int getMaxInstanceId()
-    {
+    protected int getMaxInstanceId() {
         return MAX_INSTANCE_ID;
     }
 
     @Override
-    protected int findInstanceIdInfo(String s)
-    {
+    protected int findInstanceIdInfo(String s) {
         int id;
-// #generated# Last update: 2007-05-09 08:16:24 EDT
-        L0: { id = 0; String X = null; int c;
-            int s_length = s.length();
-            if (s_length==6) {
-                c=s.charAt(0);
-                if (c=='g') { X="global";id=Id_global; }
-                else if (c=='s') { X="source";id=Id_source; }
-            }
-            else if (s_length==9) {
-                c=s.charAt(0);
-                if (c=='l') { X="lastIndex";id=Id_lastIndex; }
-                else if (c=='m') { X="multiline";id=Id_multiline; }
-            }
-            else if (s_length==10) { X="ignoreCase";id=Id_ignoreCase; }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-            break L0;
+        switch (s) {
+            case "lastIndex":
+                id = Id_lastIndex;
+                break;
+            case "source":
+                id = Id_source;
+                break;
+            case "global":
+                id = Id_global;
+                break;
+            case "ignoreCase":
+                id = Id_ignoreCase;
+                break;
+            case "multiline":
+                id = Id_multiline;
+                break;
+            default:
+                id = 0;
+                break;
         }
-// #/generated#
-// #/string_id_map#
 
         if (id == 0) return super.findInstanceIdInfo(s);
 
         int attr;
         switch (id) {
-          case Id_lastIndex:
-            attr = lastIndexAttr;
-            break;
-          case Id_source:
-          case Id_global:
-          case Id_ignoreCase:
-          case Id_multiline:
-            attr = PERMANENT | READONLY | DONTENUM;
-            break;
-          default:
-            throw new IllegalStateException();
+            case Id_lastIndex:
+                attr = lastIndexAttr;
+                break;
+            case Id_source:
+            case Id_global:
+            case Id_ignoreCase:
+            case Id_multiline:
+                attr = PERMANENT | READONLY | DONTENUM;
+                break;
+            default:
+                throw new IllegalStateException();
         }
         return instanceIdInfo(attr, id);
     }
 
     @Override
-    protected String getInstanceIdName(int id)
-    {
+    protected String getInstanceIdName(int id) {
         switch (id) {
-            case Id_lastIndex:  return "lastIndex";
-            case Id_source:     return "source";
-            case Id_global:     return "global";
-            case Id_ignoreCase: return "ignoreCase";
-            case Id_multiline:  return "multiline";
+            case Id_lastIndex:
+                return "lastIndex";
+            case Id_source:
+                return "source";
+            case Id_global:
+                return "global";
+            case Id_ignoreCase:
+                return "ignoreCase";
+            case Id_multiline:
+                return "multiline";
         }
         return super.getInstanceIdName(id);
     }
 
     @Override
-    protected Object getInstanceIdValue(int id)
-    {
+    protected Object getInstanceIdValue(int id) {
         switch (id) {
-          case Id_lastIndex:
-            return lastIndex;
-          case Id_source:
-            return new String(re.source);
-          case Id_global:
-            return ScriptRuntime.wrapBoolean((re.flags & JSREG_GLOB) != 0);
-          case Id_ignoreCase:
-            return ScriptRuntime.wrapBoolean((re.flags & JSREG_FOLD) != 0);
-          case Id_multiline:
-            return ScriptRuntime.wrapBoolean((re.flags & JSREG_MULTILINE) != 0);
+            case Id_lastIndex:
+                return lastIndex;
+            case Id_source:
+                return new String(re.source);
+            case Id_global:
+                return ScriptRuntime.wrapBoolean((re.flags & JSREG_GLOB) != 0);
+            case Id_ignoreCase:
+                return ScriptRuntime.wrapBoolean((re.flags & JSREG_FOLD) != 0);
+            case Id_multiline:
+                return ScriptRuntime.wrapBoolean((re.flags & JSREG_MULTILINE) != 0);
         }
         return super.getInstanceIdValue(id);
     }
 
+    private void setLastIndex(Object value) {
+        if ((lastIndexAttr & READONLY) != 0) {
+            throw ScriptRuntime.typeErrorById("msg.modify.readonly", "lastIndex");
+        }
+        lastIndex = value;
+    }
+
     @Override
-    protected void setInstanceIdValue(int id, Object value)
-    {
+    protected void setInstanceIdValue(int id, Object value) {
         switch (id) {
-          case Id_lastIndex:
-            lastIndex = value;
-            return;
-          case Id_source:
-          case Id_global:
-          case Id_ignoreCase:
-          case Id_multiline:
-            return;
+            case Id_lastIndex:
+                setLastIndex(value);
+                return;
+            case Id_source:
+            case Id_global:
+            case Id_ignoreCase:
+            case Id_multiline:
+                return;
         }
         super.setInstanceIdValue(id, value);
     }
@@ -2673,122 +2606,164 @@
     @Override
     protected void setInstanceIdAttributes(int id, int attr) {
         switch (id) {
-          case Id_lastIndex:
-            lastIndexAttr = attr;
-            return;
+            case Id_lastIndex:
+                lastIndexAttr = attr;
+                return;
         }
         super.setInstanceIdAttributes(id, attr);
     }
 
     @Override
-    protected void initPrototypeId(int id)
-    {
+    protected void initPrototypeId(int id) {
+        if (id == SymbolId_match) {
+            initPrototypeMethod(REGEXP_TAG, id, SymbolKey.MATCH, "[Symbol.match]", 1);
+            return;
+        }
+        if (id == SymbolId_search) {
+            initPrototypeMethod(REGEXP_TAG, id, SymbolKey.SEARCH, "[Symbol.search]", 1);
+            return;
+        }
+
         String s;
         int arity;
         switch (id) {
-          case Id_compile:  arity=2; s="compile";  break;
-          case Id_toString: arity=0; s="toString"; break;
-          case Id_toSource: arity=0; s="toSource"; break;
-          case Id_exec:     arity=1; s="exec";     break;
-          case Id_test:     arity=1; s="test";     break;
-          case Id_prefix:   arity=1; s="prefix";   break;
-          default: throw new IllegalArgumentException(String.valueOf(id));
+            case Id_compile:
+                arity = 2;
+                s = "compile";
+                break;
+            case Id_toString:
+                arity = 0;
+                s = "toString";
+                break;
+            case Id_toSource:
+                arity = 0;
+                s = "toSource";
+                break;
+            case Id_exec:
+                arity = 1;
+                s = "exec";
+                break;
+            case Id_test:
+                arity = 1;
+                s = "test";
+                break;
+            case Id_prefix:
+                arity = 1;
+                s = "prefix";
+                break;
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
         }
         initPrototypeMethod(REGEXP_TAG, id, s, arity);
     }
 
     @Override
-    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
-                             Scriptable thisObj, Object[] args)
-    {
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         if (!f.hasTag(REGEXP_TAG)) {
             return super.execIdCall(f, cx, scope, thisObj, args);
         }
         int id = f.methodId();
         switch (id) {
-          case Id_compile:
-            return realThis(thisObj, f).compile(cx, scope, args);
+            case Id_compile:
+                return realThis(thisObj, f).compile(cx, scope, args);
+
+            case Id_toString:
+            case Id_toSource:
+                return realThis(thisObj, f).toString();
+
+            case Id_exec:
+                return realThis(thisObj, f).execSub(cx, scope, args, MATCH);
+
+            case Id_test:
+                {
+                    Object x = realThis(thisObj, f).execSub(cx, scope, args, TEST);
+                    return Boolean.TRUE.equals(x) ? Boolean.TRUE : Boolean.FALSE;
+                }
+
+            case Id_prefix:
+                return realThis(thisObj, f).execSub(cx, scope, args, PREFIX);
 
-          case Id_toString:
-          case Id_toSource:
-            return realThis(thisObj, f).toString();
-
-          case Id_exec:
-            return realThis(thisObj, f).execSub(cx, scope, args, MATCH);
-
-          case Id_test: {
-            Object x = realThis(thisObj, f).execSub(cx, scope, args, TEST);
-            return Boolean.TRUE.equals(x) ? Boolean.TRUE : Boolean.FALSE;
-          }
+            case SymbolId_match:
+                return realThis(thisObj, f).execSub(cx, scope, args, MATCH);
 
-          case Id_prefix:
-            return realThis(thisObj, f).execSub(cx, scope, args, PREFIX);
+            case SymbolId_search:
+                Scriptable scriptable =
+                        (Scriptable) realThis(thisObj, f).execSub(cx, scope, args, MATCH);
+                return scriptable == null ? -1 : scriptable.get("index", scriptable);
         }
         throw new IllegalArgumentException(String.valueOf(id));
     }
 
-    private static NativeRegExp realThis(Scriptable thisObj, IdFunctionObject f)
-    {
-        if (!(thisObj instanceof NativeRegExp))
-            throw incompatibleCallError(f);
-        return (NativeRegExp)thisObj;
+    private static NativeRegExp realThis(Scriptable thisObj, IdFunctionObject f) {
+        return ensureType(thisObj, NativeRegExp.class, f);
     }
 
-// #string_id_map#
     @Override
-    protected int findPrototypeId(String s)
-    {
+    protected int findPrototypeId(Symbol k) {
+        if (SymbolKey.MATCH.equals(k)) {
+            return SymbolId_match;
+        }
+        if (SymbolKey.SEARCH.equals(k)) {
+            return SymbolId_search;
+        }
+        return 0;
+    }
+
+    @Override
+    protected int findPrototypeId(String s) {
         int id;
-// #generated# Last update: 2007-05-09 08:16:24 EDT
-        L0: { id = 0; String X = null; int c;
-            L: switch (s.length()) {
-            case 4: c=s.charAt(0);
-                if (c=='e') { X="exec";id=Id_exec; }
-                else if (c=='t') { X="test";id=Id_test; }
-                break L;
-            case 6: X="prefix";id=Id_prefix; break L;
-            case 7: X="compile";id=Id_compile; break L;
-            case 8: c=s.charAt(3);
-                if (c=='o') { X="toSource";id=Id_toSource; }
-                else if (c=='t') { X="toString";id=Id_toString; }
-                break L;
-            }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-            break L0;
+        switch (s) {
+            case "compile":
+                id = Id_compile;
+                break;
+            case "toString":
+                id = Id_toString;
+                break;
+            case "toSource":
+                id = Id_toSource;
+                break;
+            case "exec":
+                id = Id_exec;
+                break;
+            case "test":
+                id = Id_test;
+                break;
+            case "prefix":
+                id = Id_prefix;
+                break;
+            default:
+                id = 0;
+                break;
         }
-// #/generated#
         return id;
     }
 
-    private static final int
-        Id_compile       = 1,
-        Id_toString      = 2,
-        Id_toSource      = 3,
-        Id_exec          = 4,
-        Id_test          = 5,
-        Id_prefix        = 6,
-
-        MAX_PROTOTYPE_ID = 6;
-
-// #/string_id_map#
+    private static final int Id_compile = 1,
+            Id_toString = 2,
+            Id_toSource = 3,
+            Id_exec = 4,
+            Id_test = 5,
+            Id_prefix = 6,
+            SymbolId_match = 7,
+            SymbolId_search = 8,
+            MAX_PROTOTYPE_ID = SymbolId_search;
 
     private RECompiled re;
-    Object lastIndex = 0d;     /* index after last match, for //g iterator */
+    Object lastIndex = ScriptRuntime.zeroObj; /* index after last match, for //g iterator */
     private int lastIndexAttr = DONTENUM | PERMANENT;
+} // class NativeRegExp
 
-}       // class NativeRegExp
+class RECompiled implements Serializable {
+    private static final long serialVersionUID = -6144956577595844213L;
 
-class RECompiled implements Serializable
-{
-    static final long serialVersionUID = -6144956577595844213L;
-
-    final char[] source;    /* locked source string, sans // */
-    int parenCount;         /* number of parenthesized submatches */
-    int flags;              /* flags  */
-    byte[] program;         /* regular expression bytecode */
-    int classCount;         /* count [...] bitmaps */
-    RECharSet[] classList;  /* list of [...] bitmaps */
-    int anchorCh = -1;      /* if >= 0, then re starts with this literal char */
+    final char[] source; /* locked source string, sans // */
+    int parenCount; /* number of parenthesized submatches */
+    int flags; /* flags  */
+    byte[] program; /* regular expression bytecode */
+    int classCount; /* count [...] bitmaps */
+    RECharSet[] classList; /* list of [...] bitmaps */
+    int anchorCh = -1; /* if >= 0, then re starts with this literal char */
 
     RECompiled(String str) {
         this.source = str.toCharArray();
@@ -2797,42 +2772,39 @@
 
 class RENode {
 
-    RENode(byte op)
-    {
+    RENode(byte op) {
         this.op = op;
     }
 
-    byte            op;         /* r.e. op bytecode */
-    RENode          next;       /* next in concatenation order */
-    RENode          kid;        /* first operand */
-
-    RENode          kid2;       /* second operand */
-    int             parenIndex; /* or a parenthesis index */
-
-                                /* or a range */
-    int             min;
-    int             max;
-    int             parenCount;
-    boolean         greedy;
-
-                                /* or a character class */
-    int             startIndex;
-    int             kidlen;     /* length of string at kid, in chars */
-    int             bmsize;     /* bitmap size, based on max char code */
-    int             index;      /* index into class list */
-    boolean         sense;
-
-                                /* or a literal sequence */
-    char            chr;        /* of one character */
-    int             length;     /* or many (via the index) */
-    int             flatIndex;  /* which is -1 if not sourced */
-
+    byte op; /* r.e. op bytecode */
+    RENode next; /* next in concatenation order */
+    RENode kid; /* first operand */
+
+    RENode kid2; /* second operand */
+    int parenIndex; /* or a parenthesis index */
+
+    /* or a range */
+    int min;
+    int max;
+    int parenCount;
+    boolean greedy;
+
+    /* or a character class */
+    int startIndex;
+    int kidlen; /* length of string at kid, in chars */
+    int bmsize; /* bitmap size, based on max char code */
+    int index; /* index into class list */
+    boolean sense;
+
+    /* or a literal sequence */
+    char chr; /* of one character */
+    int length; /* or many (via the index) */
+    int flatIndex; /* which is -1 if not sourced */
 }
 
 class CompilerState {
 
-    CompilerState(Context cx, char[] source, int length, int flags)
-    {
+    CompilerState(Context cx, char[] source, int length, int flags) {
         this.cx = cx;
         this.cpbegin = source;
         this.cp = 0;
@@ -2845,26 +2817,29 @@
         this.progLength = 0;
     }
 
-    Context     cx;
-    char        cpbegin[];
-    int         cpend;
-    int         cp;
-    int         flags;
-    int         backReferenceLimit;
-    int         maxBackReference;
-    int         parenCount;
-    int         parenNesting;
-    int         classCount;   /* number of [] encountered */
-    int         progLength;   /* estimated bytecode length */
-    RENode      result;
+    Context cx;
+    char cpbegin[];
+    int cpend;
+    int cp;
+    int flags;
+    int backReferenceLimit;
+    int maxBackReference;
+    int parenCount;
+    int parenNesting;
+    int classCount; /* number of [] encountered */
+    int progLength; /* estimated bytecode length */
+    RENode result;
 }
 
-class REProgState
-{
-    REProgState(REProgState previous, int min, int max, int index,
-                REBackTrackData backTrack,
-                int continuationOp, int continuationPc)
-    {
+class REProgState {
+    REProgState(
+            REProgState previous,
+            int min,
+            int max,
+            int index,
+            REBackTrackData backTrack,
+            int continuationOp,
+            int continuationPc) {
         this.previous = previous;
         this.min = min;
         this.max = max;
@@ -2876,9 +2851,9 @@
 
     final REProgState previous; // previous state in stack
 
-    final int min;                      /* current quantifier min */
-    final int max;                      /* current quantifier max */
-    final int index;                    /* progress in text */
+    final int min; /* current quantifier min */
+    final int max; /* current quantifier max */
+    final int index; /* progress in text */
     final int continuationOp;
     final int continuationPc;
     final REBackTrackData backTrack; // used by ASSERT_  to recover state
@@ -2886,9 +2861,8 @@
 
 class REBackTrackData {
 
-    REBackTrackData(REGlobalData gData, int op, int pc, int cp,
-                    int continuationOp, int continuationPc)
-    {
+    REBackTrackData(
+            REGlobalData gData, int op, int pc, int cp, int continuationOp, int continuationPc) {
         previous = gData.backTrackStackTop;
         this.op = op;
         this.pc = pc;
@@ -2901,53 +2875,44 @@
 
     final REBackTrackData previous;
 
-    final int op;                             /* operator */
-    final int pc;                             /* bytecode pointer */
-    final int cp;                             /* char buffer index */
-    final int continuationOp;                 /* continuation op */
-    final int continuationPc;                 /* continuation pc */
-    final long[] parens;                      /* parenthesis captures */
-    final REProgState stateStackTop;          /* state of op that backtracked */
+    final int op; /* operator */
+    final int pc; /* bytecode pointer */
+    final int cp; /* char buffer index */
+    final int continuationOp; /* continuation op */
+    final int continuationPc; /* continuation pc */
+    final long[] parens; /* parenthesis captures */
+    final REProgState stateStackTop; /* state of op that backtracked */
 }
 
 class REGlobalData {
     boolean multiline;
-    RECompiled regexp;              /* the RE in execution */
-    int skipped;                    /* chars skipped anchoring this r.e. */
+    RECompiled regexp; /* the RE in execution */
+    int skipped; /* chars skipped anchoring this r.e. */
 
-    int cp;                         /* char buffer index */
-    long[] parens;                  /* parens captures */
+    int cp; /* char buffer index */
+    long[] parens; /* parens captures */
 
-    REProgState stateStackTop;       /* stack of state of current ancestors */
+    REProgState stateStackTop; /* stack of state of current ancestors */
 
-    REBackTrackData backTrackStackTop;  /* last matched-so-far position */
+    REBackTrackData backTrackStackTop; /* last matched-so-far position */
 
-
-    /**
-     * Get start of parenthesis capture contents, -1 for empty.
-     */
-    int parensIndex(int i)
-    {
-        return (int)(parens[i]);
+    /** Get start of parenthesis capture contents, -1 for empty. */
+    int parensIndex(int i) {
+        return (int) (parens[i]);
     }
 
-    /**
-     * Get length of parenthesis capture contents.
-     */
-    int parensLength(int i)
-    {
-        return (int)(parens[i] >>> 32);
+    /** Get length of parenthesis capture contents. */
+    int parensLength(int i) {
+        return (int) (parens[i] >>> 32);
     }
 
-    void setParens(int i, int index, int length)
-    {
+    void setParens(int i, int index, int length) {
         // clone parens array if it is shared with backtrack state
         if (backTrackStackTop != null && backTrackStackTop.parens == parens) {
             parens = parens.clone();
         }
-        parens[i] = (index & 0xffffffffL) | ((long)length << 32);
+        parens[i] = (index & 0xffffffffL) | ((long) length << 32);
     }
-
 }
 
 /*
@@ -2958,12 +2923,10 @@
  * use of the class converts the source representation into a bitmap.
  *
  */
-final class RECharSet implements Serializable
-{
-    static final long serialVersionUID = 7931787979395898394L;
+final class RECharSet implements Serializable {
+    private static final long serialVersionUID = 7931787979395898394L;
 
-    RECharSet(int length, int startIndex, int strlength, boolean sense)
-    {
+    RECharSet(int length, int startIndex, int strlength, boolean sense) {
         this.length = length;
         this.startIndex = startIndex;
         this.strlength = strlength;
@@ -2975,8 +2938,6 @@
     final int strlength;
     final boolean sense;
 
-    volatile transient boolean converted;
-    volatile transient byte[] bits;
+    transient volatile boolean converted;
+    transient volatile byte[] bits;
 }
-
-
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/regexp/RegExpImpl.java rhino-1.7.14/src/org/mozilla/javascript/regexp/RegExpImpl.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/regexp/RegExpImpl.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/regexp/RegExpImpl.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,28 +6,40 @@
 
 package org.mozilla.javascript.regexp;
 
-import org.mozilla.javascript.*;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Function;
+import org.mozilla.javascript.Kit;
+import org.mozilla.javascript.RegExpProxy;
+import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+import org.mozilla.javascript.Undefined;
 
 /**
  *
  */
 public class RegExpImpl implements RegExpProxy {
 
+    @Override
     public boolean isRegExp(Scriptable obj) {
         return obj instanceof NativeRegExp;
     }
 
+    @Override
     public Object compileRegExp(Context cx, String source, String flags)
     {
         return NativeRegExp.compileRE(cx, source, flags, false);
     }
 
+    @Override
     public Scriptable wrapRegExp(Context cx, Scriptable scope,
                                  Object compiled)
     {
-        return new NativeRegExp(scope, (RECompiled) compiled);
+        return NativeRegExpInstantiator
+                .withLanguageVersionScopeCompiled(cx.getLanguageVersion(), scope, (RECompiled) compiled);
     }
 
+    @Override
     public Object action(Context cx, Scriptable scope,
                          Scriptable thisObj, Object[] args,
                          int actionType)
@@ -39,23 +51,36 @@
         switch (actionType) {
           case RA_MATCH:
             {
-                NativeRegExp re = createRegExp(cx, scope, args, 1, false);
-                Object rval = matchOrReplace(cx, scope, thisObj, args,
-                                             this, data, re);
+                int optarg = Integer.MAX_VALUE;
+                if (cx.getLanguageVersion() < Context.VERSION_1_6) {
+                    optarg = 1;
+                }
+
+                NativeRegExp re = createRegExp(cx, scope, args, optarg, false);
+                Object rval = matchOrReplace(cx, scope, thisObj, args, this, data, re);
                 return data.arrayobj == null ? rval : data.arrayobj;
             }
 
           case RA_SEARCH:
             {
-                NativeRegExp re = createRegExp(cx, scope, args, 1, false);
-                return matchOrReplace(cx, scope, thisObj, args,
-                                      this, data, re);
+                int optarg = Integer.MAX_VALUE;
+                if (cx.getLanguageVersion() < Context.VERSION_1_6) {
+                    optarg = 1;
+                }
+
+                NativeRegExp re = createRegExp(cx, scope, args, optarg, false);
+                return matchOrReplace(cx, scope, thisObj, args, this, data, re);
             }
 
           case RA_REPLACE:
             {
-                boolean useRE = (args.length > 0 && args[0] instanceof NativeRegExp)
-                                || args.length > 2;
+                boolean useRE = args.length > 0 && args[0] instanceof NativeRegExp;
+
+                // ignore other parameters
+                if (cx.getLanguageVersion() < Context.VERSION_1_6) {
+                    useRE |= args.length > 2;
+                }
+
                 NativeRegExp re = null;
                 String search = null;
                 if (useRE) {
@@ -68,7 +93,9 @@
                 Object arg1 = args.length < 2 ? Undefined.instance : args[1];
                 String repstr = null;
                 Function lambda = null;
-                if (arg1 instanceof Function) {
+                if (arg1 instanceof Function
+                        && (cx.getLanguageVersion() < Context.VERSION_ES6
+                                || !(arg1 instanceof NativeRegExp))) {
                     lambda = (Function) arg1;
                 } else {
                     repstr = ScriptRuntime.toString(arg1);
@@ -89,6 +116,7 @@
                     int index = str.indexOf(search);
                     if (index >= 0) {
                         int slen = search.length();
+                        this.parens = null;
                         this.lastParen = null;
                         this.leftContext = new SubString(str, 0, index);
                         this.lastMatch = new SubString(str, index, slen);
@@ -127,7 +155,7 @@
         Scriptable topScope = ScriptableObject.getTopLevelScope(scope);
         if (args.length == 0 || args[0] == Undefined.instance) {
             RECompiled compiled = NativeRegExp.compileRE(cx, "", "", false);
-            re = new NativeRegExp(topScope, compiled);
+            re = NativeRegExpInstantiator.withLanguageVersionScopeCompiled(cx.getLanguageVersion(), topScope, compiled);
         } else if (args[0] instanceof NativeRegExp) {
             re = (NativeRegExp) args[0];
         } else {
@@ -140,7 +168,7 @@
                 opt = null;
             }
             RECompiled compiled = NativeRegExp.compileRE(cx, src, opt, forceFlat);
-            re = new NativeRegExp(topScope, compiled);
+            re = NativeRegExpInstantiator.withLanguageVersionScopeCompiled(cx.getLanguageVersion(), topScope, compiled);
         }
         return re;
     }
@@ -165,7 +193,7 @@
             else
                 result = Integer.valueOf(-1);
         } else if (data.global) {
-            re.lastIndex = 0d;
+            re.lastIndex = ScriptRuntime.zeroObj;
             for (int count = 0; indexp[0] <= str.length(); count++) {
                 result = re.executeRegExp(cx, scope, reImpl,
                                           str, indexp, NativeRegExp.TEST);
@@ -199,6 +227,7 @@
 
 
 
+    @Override
     public int find_split(Context cx, Scriptable scope, String target,
                           String separator, Scriptable reObj,
                           int[] ip, int[] matchlen,
@@ -217,7 +246,7 @@
             ip[0] = i;
             Object ret = re.executeRegExp(cx, scope, this, target, ip,
                                           NativeRegExp.TEST);
-            if (ret != Boolean.TRUE) {
+            if (!Boolean.TRUE.equals(ret)) {
                 // Mismatch: ensure our caller advances i past end of string.
                 ip[0] = ipsave;
                 matchlen[0] = 1;
@@ -282,7 +311,7 @@
                 return parsub;
             }
         }
-        return SubString.emptySubString;
+        return new SubString();
     }
 
     /*
@@ -512,6 +541,7 @@
      * a limit argument and accepts a regular expression as the split
      * argument.
      */
+    @Override
     public Object js_split(Context cx, Scriptable scope,
                                    String target, Object[] args)
     {
@@ -524,8 +554,12 @@
         if (limited) {
             /* Clamp limit between 0 and 1 + string length. */
             limit = ScriptRuntime.toUint32(args[1]);
-            if (limit > target.length())
+            if (limit == 0) {
+                return result;
+            }
+            if (limit > target.length()) {
                 limit = 1 + target.length();
+            }
         }
 
         // return an array consisting of the target if no separator given
@@ -726,8 +760,7 @@
 
     protected String          input;         /* input string to match (perl $_, GC root) */
     protected boolean         multiline;     /* whether input contains newlines (perl $*) */
-    protected SubString[]     parens;        /* Vector of SubString; last set of parens
-                                      matched (perl $1, $2) */
+    protected SubString[]     parens;        /* Vector of SubString; last set of parens matched (perl $1, $2) */
     protected SubString       lastMatch;     /* last string matched (perl $&) */
     protected SubString       lastParen;     /* last paren matched (perl $+) */
     protected SubString       leftContext;   /* input to left of last match (perl $`) */
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/regexp/SubString.java rhino-1.7.14/src/org/mozilla/javascript/regexp/SubString.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/regexp/SubString.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/regexp/SubString.java	2022-01-06 22:57:21.000000000 +0100
@@ -36,8 +36,6 @@
                : str.substring(index, index + length);
     }
 
-    public static final SubString emptySubString = new SubString();
-
     String str;
     int    index;
     int    length;
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/resources/Messages_en.properties rhino-1.7.14/src/org/mozilla/javascript/resources/Messages_en.properties
--- rhino-1.7.7.2/src/org/mozilla/javascript/resources/Messages_en.properties	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/resources/Messages_en.properties	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,10 @@
+#
+# English JavaScript messages file.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# The base messages file is used for all messages, but this file is
+# here so that English is selectable as a language when the default
+# Locale also has a translation.
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/resources/Messages_fr.properties rhino-1.7.14/src/org/mozilla/javascript/resources/Messages_fr.properties
--- rhino-1.7.7.2/src/org/mozilla/javascript/resources/Messages_fr.properties	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/resources/Messages_fr.properties	2022-01-06 22:57:21.000000000 +0100
@@ -97,7 +97,7 @@
     c''est la valeur en question qui est renvoy\u00E9e. \u00E9tait-ce votre intention?
 
 msg.eval.nonstring.strict =\
-    La fonction eval() ne peut \u00eatre appel\u00e9e avec une valeur autre qu'une cha\u00EEne primitive \
+    La fonction eval() ne peut \u00eatre appel\u00e9e avec une valeur autre qu''une cha\u00EEne primitive \
     en mode strict
 
 msg.bad.destruct.op =\
@@ -152,8 +152,8 @@
     Impossible de convertir la fonction en interface {0} sans aucune m\u00e9thode
 
 msg.no.function.interface.conversion =\
-    Impossible de convertir la fonction {0} en interface puisqu'elle contient des m\u00e9thodes avec \
-    des noms diff\u00e9rentes
+    Impossible de convertir la fonction {0} en interface puisqu''elle contient des m\u00e9thodes avec \
+    des noms diff\u00e9rents
 
 msg.undefined.function.interface =\
     La propri\u00E9t\u00E9 "{0}" n''est pas d\u00E9finie dans l''adaptateur d''interface
@@ -200,7 +200,7 @@
     \\ \u00e0 la fin d''une expression rationnelle
 
 msg.re.unmatched.right.paren =\
-    Parenth\u00e8se fermante orpheline dans l'expression rationnelle
+    Parenth\u00e8se fermante orpheline dans l''expression rationnelle
 
 msg.no.regexp =\
     Les expressions rationnelles ne sont pas disponibles
@@ -245,7 +245,7 @@
     Erreur de syntaxe: la d\u00e9claration ''let'' n''est pas directement dans un block
 
 msg.bad.object.init =\
-    Erreur de syntaxe: initialiseur d'objet incorrect
+    Erreur de syntaxe: initialiseur d''objet incorrect
 
 # NodeTransformer
 msg.dup.label =\
@@ -352,7 +352,7 @@
     il manque '')'' apr\u00E8s un objet with-statement
 
 msg.no.with.strict =\
-    L'instruction with n''est pas autoris\u00e9e en mode strict
+    L''instruction with n''est pas autoris\u00e9e en mode strict
 
 msg.no.paren.after.let =\
     il manque ''('' apr\u00e8s let
@@ -361,7 +361,7 @@
     il manque '')'' apr\u00e8s la liste de variables
 
 msg.no.curly.let =\
-    il manque ''}'' apr\u00e8s l'instruction let
+    il manque ''}'' apr\u00e8s l''instruction let
 
 msg.bad.return =\
     la valeur renvoy\u00E9e est incorrecte
@@ -427,7 +427,7 @@
     la fonction anonyme ne renvoie pas toujours de valeur
 
 msg.return.inconsistent =\
-    l'instruction return est incoh\u00e9rente avec les usages pr\u00e9c\u00e9dents
+    l''instruction return est incoh\u00e9rente avec les usages pr\u00e9c\u00e9dents
 
 msg.generator.returns =\
     Erreur de type: la fonction g\u00e9n\u00e9ratrice {0} renvoie une valeur
@@ -548,22 +548,22 @@
     Impossible de supprimer la propri\u00e9t\u00e9 "{1}" de {0}
 
 msg.undef.method.call =\
-    Impossible d'appeler la m\u00e9thode "{1}" de {0}
+    Impossible d''appeler la m\u00e9thode "{1}" de {0}
 
 msg.undef.with =\
-    Impossible d'appliquer "with" \u00e0 {0}
+    Impossible d''appliquer "with" \u00e0 {0}
 
 msg.isnt.function =\
     {0} n''est pas une fonction, c''est un {1}
 
 msg.isnt.function.in =\
-    Impossible d'appeler la propri\u00e9t\u00e9 {0} de l'objet {1}. Ce n''est pas une fonction, c''est un "{2}".
+    Impossible d''appeler la propri\u00e9t\u00e9 {0} de l''objet {1}. Ce n''est pas une fonction, c''est un "{2}".
 
 msg.function.not.found =\
     Impossible de trouver la fonction {0}.
 
 msg.function.not.found.in =\
-    Impossible de trouver la fonction {0} dans l'objet {1}.
+    Impossible de trouver la fonction {0} dans l''objet {1}.
 
 msg.isnt.xml.object =\
     {0} n''est pas un objet xml.
@@ -576,7 +576,7 @@
 
 msg.no.ref.from.function =\
     La fonction {0} ne peut pas \u00eatre utilis\u00e9e comme la partie gauche de l''assignation \
-    ou comme l'op\u00e9rande de l''op\u00e9rateur ++ ou --.
+    ou comme l''op\u00e9rande de l''op\u00e9rateur ++ ou --.
 
 msg.bad.default.value =\
     La m\u00E9thode getDefaultValue() de l''objet a renvoy\u00E9 un objet
@@ -781,7 +781,7 @@
 
 #JavaAdapter
 msg.only.one.super = \
-    Un JavaAdapter ne peut \u00e9tendre qu'une seule classe. Les classes {0} et {1} ont \u00e9t\u00e9 donn\u00e9es.
+    Un JavaAdapter ne peut \u00e9tendre qu''une seule classe. Les classes {0} et {1} ont \u00e9t\u00e9 donn\u00e9es.
 
 
 # Arrays
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/resources/Messages.properties rhino-1.7.14/src/org/mozilla/javascript/resources/Messages.properties
--- rhino-1.7.7.2/src/org/mozilla/javascript/resources/Messages.properties	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/resources/Messages.properties	2022-01-06 22:57:21.000000000 +0100
@@ -68,6 +68,9 @@
 msg.incompat.call =\
     Method "{0}" called on incompatible object.
 
+msg.incompat.call.details =\
+    Method "{0}" called on incompatible object ({1} is not an instance of {2}).
+
 msg.bad.parms =\
     Unsupported parameter type "{0}" in method "{1}".
 
@@ -83,6 +86,9 @@
 msg.method.not.found =\
     Method "{0}" not found in "{1}".
 
+msg.method.missing.parameter =\
+    {0}: At least {1} arguments required, but only {2} passed
+
 # IRFactory
 
 msg.bad.for.in.lhs =\
@@ -131,7 +137,7 @@
 
 # NativeCall
 msg.only.from.new =\
-    "{0}" may only be invoked from a "new" expression.
+    "Constructor {0}" may only be invoked from a "new" expression.
 
 msg.deprec.ctor =\
     The "{0}" constructor is deprecated.
@@ -248,6 +254,10 @@
 msg.toisostring.must.return.primitive =\
     toISOString must return a primitive value, but instead returned "{0}"
 
+# NativeJSON
+msg.json.cant.serialize =\
+    Do not know how to serialize a {0}
+
 # Parser
 msg.got.syntax.errors = \
     Compilation produced {0} syntax errors.
@@ -262,7 +272,7 @@
     TypeError: redeclaration of variable {0}.
 
 msg.parm.redecl =\
-	TypeError: redeclaration of formal parameter {0}.
+    TypeError: redeclaration of formal parameter {0}.
 
 msg.fn.redecl =\
     TypeError: redeclaration of function {0}.
@@ -426,7 +436,7 @@
     missing ) in parenthetical
 
 msg.reserved.id =\
-    identifier is a reserved word
+    identifier is a reserved word: {0}
 
 msg.no.paren.catch =\
     missing ( before catch-block condition
@@ -503,9 +513,15 @@
 msg.destruct.assign.no.init =\
     Missing = in destructuring declaration
 
+msg.destruct.default.vals =\
+    Default values in destructuring declarations are not supported
+
 msg.no.old.octal.strict =\
     Old octal numbers prohibited in strict mode.
 
+msg.no.old.octal.bigint =\
+    Old octal numbers prohibited in BigInt.
+
 msg.dup.obj.lit.prop.strict =\
     Property "{0}" already defined in this object literal.
 
@@ -515,6 +531,9 @@
 msg.bad.id.strict =\
     "{0}" is not a valid identifier for this use in strict mode.
 
+msg.no.unary.expr.on.left.exp =\
+    "{0}" is not allowed on the left-hand side of "**".
+
 # ScriptRuntime
 
 # is there a better message for this? 
@@ -547,7 +566,7 @@
     Property {0} not found.
 
 msg.set.prop.no.setter =\
-    Cannot set property {0} that has only a getter.
+    Cannot set property {0} that has only a getter to value ''{1}''.
 
 msg.invalid.type =\
     Invalid JavaScript value of type {0}
@@ -564,6 +583,15 @@
 msg.undef.to.object =\
     Cannot convert undefined to an object.
 
+msg.cant.convert.to.bigint =\
+    Cannot convert {0} to an BigInt.
+
+msg.cant.convert.to.bigint.isnt.integer =\
+    Cannot convert {0} to an BigInt. It isn\'t an integer.
+
+msg.bigint.bad.form =\
+    illegally formed BigInt syntax
+
 msg.cyclic.value =\
     Cyclic {0} value not allowed.
 
@@ -625,6 +653,15 @@
 msg.bad.radix = \
     illegal radix {0}.
 
+msg.division.zero = \
+    Division by zero.
+
+msg.bigint.negative.exponent = \
+    BigInt negative exponent.
+
+msg.bigint.out.of.range.arithmetic = \
+    BigInt is too large.
+
 # ScriptableObject
 msg.default.value =\
     Cannot find default value for object.
@@ -738,7 +775,7 @@
     no input for {0}
 
 msg.illegal.character =\
-    illegal character
+    illegal character: {0}
 
 msg.invalid.escape =\
     invalid Unicode escape sequence
@@ -848,6 +885,9 @@
 msg.StopIteration.invalid =\
     StopIteration may not be changed to an arbitrary object.
 
+msg.generator.executing =\
+    The generator is still executing from a previous invocation.
+
 # Interpreter
 msg.yield.closing =\
   Yield from closing generator
@@ -865,6 +905,9 @@
 msg.arguments.not.access.strict =\
   Cannot access "{0}" property of the arguments object in strict mode.
 
+msg.object.cyclic.prototype =\
+  Cyclic prototype "{0}" value not allowed.
+
 # Symbol support
 msg.object.not.symbolscriptable =\
   Object {0} does not support Symbol keys
@@ -878,8 +921,38 @@
 msg.not.a.number =\
   The object is not a number
 
+msg.cant.convert.to.number =\
+  Cannot convert {0} to a number
+
 msg.no.symbol.new =\
   Symbol objects may not be constructed using \"new\"
 
 msg.compare.symbol =\
-  Symbol objects may not be compared
\ No newline at end of file
+  Symbol objects may not be compared
+
+msg.no.new =\
+  {0} objects may not be constructed using \"new\"
+
+msg.map.function.not =\
+  Map function is not actually a function
+
+msg.constructor.no.function \=
+  The constructor for {0} may not be invoked as a function
+
+msg.this.not.instance \= \"this\" is not an instance of the class
+
+# Promises
+msg.function.expected =\
+  Expecting the first argument to be a function
+
+msg.constructor.expected =\
+  Expecting the first argument to be a constructor
+
+msg.promise.self.resolution =\
+  Promise is being self-resolved
+
+msg.promise.capability.state \=
+Invalid promise capability state
+
+msg.promise.all.toobig \=
+  Too many inputs to Promise.all
\ No newline at end of file
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/RhinoException.java rhino-1.7.14/src/org/mozilla/javascript/RhinoException.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/RhinoException.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/RhinoException.java	2022-01-06 22:57:21.000000000 +0100
@@ -4,7 +4,6 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-
 package org.mozilla.javascript;
 
 import java.io.CharArrayWriter;
@@ -16,40 +15,30 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-/**
- * The class of exceptions thrown by the JavaScript engine.
- */
-public abstract class RhinoException extends RuntimeException
-{
+/** The class of exceptions thrown by the JavaScript engine. */
+public abstract class RhinoException extends RuntimeException {
     private static final Pattern JAVA_STACK_PATTERN = Pattern.compile("_c_(.*)_\\d+");
 
-    RhinoException()
-    {
+    RhinoException() {
         Evaluator e = Context.createInterpreter();
-        if (e != null)
-            e.captureStackInfo(this);
+        if (e != null) e.captureStackInfo(this);
     }
 
-    RhinoException(String details)
-    {
+    RhinoException(String details) {
         super(details);
         Evaluator e = Context.createInterpreter();
-        if (e != null)
-            e.captureStackInfo(this);
+        if (e != null) e.captureStackInfo(this);
     }
 
     @Override
-    public final String getMessage()
-    {
+    public final String getMessage() {
         String details = details();
         if (sourceName == null || lineNumber <= 0) {
             return details;
         }
         StringBuilder buf = new StringBuilder(details);
         buf.append(" (");
-        if (sourceName != null) {
-            buf.append(sourceName);
-        }
+        buf.append(sourceName);
         if (lineNumber > 0) {
             buf.append('#');
             buf.append(lineNumber);
@@ -58,108 +47,85 @@
         return buf.toString();
     }
 
-    public String details()
-    {
+    public String details() {
         return super.getMessage();
     }
 
     /**
-     * Get the uri of the script source containing the error, or null
-     * if that information is not available.
+     * Get the uri of the script source containing the error, or null if that information is not
+     * available.
      */
-    public final String sourceName()
-    {
+    public final String sourceName() {
         return sourceName;
     }
 
     /**
      * Initialize the uri of the script source containing the error.
      *
-     * @param sourceName the uri of the script source responsible for the error.
-     *                   It should not be <tt>null</tt>.
-     *
+     * @param sourceName the uri of the script source responsible for the error. It should not be
+     *     <code>null</code>.
      * @throws IllegalStateException if the method is called more then once.
      */
-    public final void initSourceName(String sourceName)
-    {
+    public final void initSourceName(String sourceName) {
         if (sourceName == null) throw new IllegalArgumentException();
         if (this.sourceName != null) throw new IllegalStateException();
         this.sourceName = sourceName;
     }
 
-    /**
-     * Returns the line number of the statement causing the error,
-     * or zero if not available.
-     */
-    public final int lineNumber()
-    {
+    /** Returns the line number of the statement causing the error, or zero if not available. */
+    public final int lineNumber() {
         return lineNumber;
     }
 
     /**
      * Initialize the line number of the script statement causing the error.
      *
-     * @param lineNumber the line number in the script source.
-     *                   It should be positive number.
-     *
+     * @param lineNumber the line number in the script source. It should be positive number.
      * @throws IllegalStateException if the method is called more then once.
      */
-    public final void initLineNumber(int lineNumber)
-    {
+    public final void initLineNumber(int lineNumber) {
         if (lineNumber <= 0) throw new IllegalArgumentException(String.valueOf(lineNumber));
         if (this.lineNumber > 0) throw new IllegalStateException();
         this.lineNumber = lineNumber;
     }
 
-    /**
-     * The column number of the location of the error, or zero if unknown.
-     */
-    public final int columnNumber()
-    {
+    /** The column number of the location of the error, or zero if unknown. */
+    public final int columnNumber() {
         return columnNumber;
     }
 
     /**
      * Initialize the column number of the script statement causing the error.
      *
-     * @param columnNumber the column number in the script source.
-     *                     It should be positive number.
-     *
+     * @param columnNumber the column number in the script source. It should be positive number.
      * @throws IllegalStateException if the method is called more then once.
      */
-    public final void initColumnNumber(int columnNumber)
-    {
+    public final void initColumnNumber(int columnNumber) {
         if (columnNumber <= 0) throw new IllegalArgumentException(String.valueOf(columnNumber));
         if (this.columnNumber > 0) throw new IllegalStateException();
         this.columnNumber = columnNumber;
     }
 
-    /**
-     * The source text of the line causing the error, or null if unknown.
-     */
-    public final String lineSource()
-    {
+    /** The source text of the line causing the error, or null if unknown. */
+    public final String lineSource() {
         return lineSource;
     }
 
     /**
      * Initialize the text of the source line containing the error.
      *
-     * @param lineSource the text of the source line responsible for the error.
-     *                   It should not be <tt>null</tt>.
-     *
+     * @param lineSource the text of the source line responsible for the error. It should not be
+     *     <code>null</code>.
      * @throws IllegalStateException if the method is called more then once.
      */
-    public final void initLineSource(String lineSource)
-    {
+    public final void initLineSource(String lineSource) {
         if (lineSource == null) throw new IllegalArgumentException();
         if (this.lineSource != null) throw new IllegalStateException();
         this.lineSource = lineSource;
     }
 
-    final void recordErrorOrigin(String sourceName, int lineNumber,
-                                 String lineSource, int columnNumber)
-    {
+    final void recordErrorOrigin(
+            String sourceName, int lineNumber, String lineSource, int columnNumber) {
         // XXX: for compatibility allow for now -1 to mean 0
         if (lineNumber == -1) {
             lineNumber = 0;
@@ -179,38 +145,33 @@
         }
     }
 
-    private String generateStackTrace()
-    {
+    private String generateStackTrace() {
         // Get stable reference to work properly with concurrent access
         CharArrayWriter writer = new CharArrayWriter();
         super.printStackTrace(new PrintWriter(writer));
         String origStackTrace = writer.toString();
         Evaluator e = Context.createInterpreter();
-        if (e != null)
-            return e.getPatchedStack(this, origStackTrace);
+        if (e != null) return e.getPatchedStack(this, origStackTrace);
         return null;
     }
 
     /**
-     * Get a string representing the script stack of this exception.
-     * If optimization is enabled, this includes java stack elements
-     * whose source and method names suggest they have been generated
-     * by the Rhino script compiler.
+     * Get a string representing the script stack of this exception. If optimization is enabled,
+     * this includes java stack elements whose source and method names suggest they have been
+     * generated by the Rhino script compiler.
+     *
      * @return a script stack dump
      * @since 1.6R6
      */
-    public String getScriptStackTrace()
-    {
+    public String getScriptStackTrace() {
         return getScriptStackTrace(NativeError.DEFAULT_STACK_LIMIT, null);
     }
 
     /**
-     * Get a string representing the script stack of this exception.
-     * If optimization is enabled, this includes java stack elements
-     * whose source and method names suggest they have been generated
-     * by the Rhino script compiler.
-     * The optional "limit" parameter limits the number of stack frames returned.
-     * The "functionName" parameter will exclude any stack frames "below" the
+     * Get a string representing the script stack of this exception. If optimization is enabled,
+     * this includes java stack elements whose source and method names suggest they have been
+     * generated by the Rhino script compiler. The optional "limit" parameter limits the number of
+     * stack frames returned. The "functionName" parameter will exclude any stack frames "below" the
      * specified function on the stack.
      *
      * @param limit the number of stack frames returned
@@ -218,14 +179,12 @@
      * @return a script stack dump
      * @since 1.8.0
      */
-    public String getScriptStackTrace(int limit, String functionName)
-    {
+    public String getScriptStackTrace(int limit, String functionName) {
         ScriptStackElement[] stack = getScriptStack(limit, functionName);
         return formatStackTrace(stack, details());
     }
 
-    static String formatStackTrace(ScriptStackElement[] stack, String message)
-    {
+    static String formatStackTrace(ScriptStackElement[] stack, String message) {
         StringBuilder buffer = new StringBuilder();
         String lineSeparator = SecurityUtilities.getSystemProperty("line.separator");
 
@@ -237,42 +196,46 @@
 
         for (ScriptStackElement elem : stack) {
             switch (stackStyle) {
-            case MOZILLA:
-                elem.renderMozillaStyle(buffer);
-                break;
-            case V8:
-                elem.renderV8Style(buffer);
-                break;
-            case RHINO:
-                elem.renderJavaStyle(buffer);
-                break;
+                case MOZILLA:
+                    elem.renderMozillaStyle(buffer);
+                    buffer.append(lineSeparator);
+                    break;
+                case MOZILLA_LF:
+                    elem.renderMozillaStyle(buffer);
+                    buffer.append('\n');
+                    break;
+                case V8:
+                    elem.renderV8Style(buffer);
+                    buffer.append(lineSeparator);
+                    break;
+                case RHINO:
+                    elem.renderJavaStyle(buffer);
+                    buffer.append(lineSeparator);
+                    break;
             }
-            buffer.append(lineSeparator);
         }
         return buffer.toString();
     }
 
     /**
      * Get a string representing the script stack of this exception.
-     * @deprecated the filter argument is ignored as we are able to
-     * recognize script stack elements by our own. Use
-     * #getScriptStackTrace() instead.
+     *
+     * @deprecated the filter argument is ignored as we are able to recognize script stack elements
+     *     by our own. Use #getScriptStackTrace() instead.
      * @param filter ignored
      * @return a script stack dump
      * @since 1.6R6
      */
     @Deprecated
-    public String getScriptStackTrace(FilenameFilter filter)
-    {
+    public String getScriptStackTrace(FilenameFilter filter) {
         return getScriptStackTrace();
     }
 
     /**
-     * Get the script stack of this exception as an array of
-     * {@link ScriptStackElement}s.
-     * If optimization is enabled, this includes java stack elements
-     * whose source and method names suggest they have been generated
-     * by the Rhino script compiler.
+     * Get the script stack of this exception as an array of {@link ScriptStackElement}s. If
+     * optimization is enabled, this includes java stack elements whose source and method names
+     * suggest they have been generated by the Rhino script compiler.
+     *
      * @return the script stack for this exception
      * @since 1.7R3
      */
@@ -281,14 +244,13 @@
     }
 
     /**
-     * Get the script stack of this exception as an array of
-     * {@link ScriptStackElement}s.
-     * If optimization is enabled, this includes java stack elements
-     * whose source and method names suggest they have been generated
-     * by the Rhino script compiler.
+     * Get the script stack of this exception as an array of {@link ScriptStackElement}s. If
+     * optimization is enabled, this includes java stack elements whose source and method names
+     * suggest they have been generated by the Rhino script compiler.
      *
      * @param limit the number of stack frames returned, or -1 for unlimited
-     * @param hideFunction the name of a function on the stack -- frames below it will be ignored, or null
+     * @param hideFunction the name of a function on the stack -- frames below it will be ignored,
+     *     or null
      * @return the script stack for this exception
      * @since 1.8.0
      */
@@ -313,19 +275,19 @@
             String fileName = e.getFileName();
             if (e.getMethodName().startsWith("_c_")
                     && e.getLineNumber() > -1
-                    && fileName != null
-                    && !fileName.endsWith(".java")) {
+                    && (fileName == null || !fileName.endsWith(".java"))) {
                 String methodName = e.getMethodName();
                 Matcher match = JAVA_STACK_PATTERN.matcher(methodName);
                 // the method representing the main script is always "_c_script_0" -
                 // at least we hope so
-                methodName = !"_c_script_0".equals(methodName) && match.find() ?
-                        match.group(1) : null;
+                methodName =
+                        !"_c_script_0".equals(methodName) && match.find() ? match.group(1) : null;
 
                 if (!printStarted && hideFunction.equals(methodName)) {
                     printStarted = true;
                 } else if (printStarted && ((limit < 0) || (count < limit))) {
-                    list.add(new ScriptStackElement(fileName, methodName, e.getLineNumber()));
+                    String fn = fileName == null ? "(unknown)" : fileName;
+                    list.add(new ScriptStackElement(fn, methodName, e.getLineNumber()));
                     count++;
                 }
 
@@ -344,13 +306,11 @@
                 }
             }
         }
-        return list.toArray(new ScriptStackElement[list.size()]);
+        return list.toArray(new ScriptStackElement[0]);
     }
 
-
     @Override
-    public void printStackTrace(PrintWriter s)
-    {
+    public void printStackTrace(PrintWriter s) {
         if (interpreterStackInfo == null) {
             super.printStackTrace(s);
         } else {
@@ -359,8 +319,7 @@
     }
 
     @Override
-    public void printStackTrace(PrintStream s)
-    {
+    public void printStackTrace(PrintStream s) {
         if (interpreterStackInfo == null) {
             super.printStackTrace(s);
         } else {
@@ -369,11 +328,10 @@
     }
 
     /**
-     * Returns true if subclasses of <code>RhinoException</code>
-     * use the Mozilla/Firefox style of rendering script stacks
-     * (<code>functionName()@fileName:lineNumber</code>)
-     * instead of Rhino's own Java-inspired format
-     * (<code>    at fileName:lineNumber (functionName)</code>).
+     * Returns true if subclasses of <code>RhinoException</code> use the Mozilla/Firefox style of
+     * rendering script stacks (<code>functionName()@fileName:lineNumber</code>) instead of Rhino's
+     * own Java-inspired format (<code>    at fileName:lineNumber (functionName)</code>).
+     *
      * @return true if stack is rendered in Mozilla/Firefox style
      * @see ScriptStackElement
      * @since 1.7R3
@@ -383,12 +341,11 @@
     }
 
     /**
-     * Tell subclasses of <code>RhinoException</code> whether to
-     * use the Mozilla/Firefox style of rendering script stacks
-     * (<code>functionName()@fileName:lineNumber</code>)
-     * instead of Rhino's own Java-inspired format
-     * (<code>    at fileName:lineNumber (functionName)</code>).
-     * Use "setStackStyle" to select between more than just the "Mozilla" and "Rhino" formats.
+     * Tell subclasses of <code>RhinoException</code> whether to use the Mozilla/Firefox style of
+     * rendering script stacks (<code>functionName()@fileName:lineNumber</code>) instead of Rhino's
+     * own Java-inspired format (<code>    at fileName:lineNumber (functionName)</code>). Use
+     * "setStackStyle" to select between more than just the "Mozilla" and "Rhino" formats.
+     *
      * @param flag whether to render stacks in Mozilla/Firefox style
      * @see ScriptStackElement
      * @since 1.7R3
@@ -400,6 +357,7 @@
     /**
      * Specify the stack style to use from between three different formats: "Rhino" (the default),
      * "Mozilla", and "V8." See StackStyle for information about each.
+     *
      * @param style the style to select -- an instance of the StackStyle class
      * @see StackStyle
      * @since 1.8.0
@@ -408,15 +366,12 @@
         stackStyle = style;
     }
 
-    /**
-     * Return the current stack style in use.
-     * Return the current stack style in use.
-     */
+    /** Return the current stack style in use. Return the current stack style in use. */
     public static StackStyle getStackStyle() {
         return stackStyle;
     }
 
-    static final long serialVersionUID = 1883500631321581169L;
+    private static final long serialVersionUID = 1883500631321581169L;
 
     // Just for testing!
     private static StackStyle stackStyle = StackStyle.RHINO;
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/Scriptable.java rhino-1.7.14/src/org/mozilla/javascript/Scriptable.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/Scriptable.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/Scriptable.java	2022-01-06 22:57:21.000000000 +0100
@@ -148,7 +148,7 @@
      * object's responsibility to modify <i>o</i>. <p>
      * This design allows properties to be defined in prototypes and implemented
      * in terms of getters and setters of Java values without consuming slots
-     * in each instance.<p>
+     * in each instance.
      * <p>
      * The values that may be set are limited to the following:
      * <UL>
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ScriptableObject.java rhino-1.7.14/src/org/mozilla/javascript/ScriptableObject.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ScriptableObject.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ScriptableObject.java	2022-01-06 22:57:21.000000000 +0100
@@ -24,67 +24,62 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
-
-import org.mozilla.javascript.debug.DebuggableObject;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import org.mozilla.javascript.ScriptRuntime.StringIdOrIndex;
 import org.mozilla.javascript.annotations.JSConstructor;
 import org.mozilla.javascript.annotations.JSFunction;
 import org.mozilla.javascript.annotations.JSGetter;
 import org.mozilla.javascript.annotations.JSSetter;
 import org.mozilla.javascript.annotations.JSStaticFunction;
+import org.mozilla.javascript.debug.DebuggableObject;
 
 /**
- * This is the default implementation of the Scriptable interface. This
- * class provides convenient default behavior that makes it easier to
- * define host objects.
- * <p>
- * Various properties and methods of JavaScript objects can be conveniently
- * defined using methods of ScriptableObject.
- * <p>
- * Classes extending ScriptableObject must define the getClassName method.
+ * This is the default implementation of the Scriptable interface. This class provides convenient
+ * default behavior that makes it easier to define host objects.
+ *
+ * <p>Various properties and methods of JavaScript objects can be conveniently defined using methods
+ * of ScriptableObject.
+ *
+ * <p>Classes extending ScriptableObject must define the getClassName method.
  *
  * @see org.mozilla.javascript.Scriptable
  * @author Norris Boyd
  */
+public abstract class ScriptableObject
+        implements Scriptable, SymbolScriptable, Serializable, DebuggableObject, ConstProperties {
 
-public abstract class ScriptableObject implements Scriptable,
-                                                  SymbolScriptable,
-                                                  Serializable,
-                                                  DebuggableObject,
-                                                  ConstProperties
-{
+    private static final long serialVersionUID = 2829861078851942586L;
 
-    static final long serialVersionUID = 2829861078851942586L;
-    
     /**
      * The empty property attribute.
      *
-     * Used by getAttributes() and setAttributes().
+     * <p>Used by getAttributes() and setAttributes().
      *
      * @see org.mozilla.javascript.ScriptableObject#getAttributes(String)
      * @see org.mozilla.javascript.ScriptableObject#setAttributes(String, int)
      */
-    public static final int EMPTY =     0x00;
+    public static final int EMPTY = 0x00;
 
     /**
      * Property attribute indicating assignment to this property is ignored.
      *
-     * @see org.mozilla.javascript.ScriptableObject
-     *      #put(String, Scriptable, Object)
+     * @see org.mozilla.javascript.ScriptableObject #put(String, Scriptable, Object)
      * @see org.mozilla.javascript.ScriptableObject#getAttributes(String)
      * @see org.mozilla.javascript.ScriptableObject#setAttributes(String, int)
      */
-    public static final int READONLY =  0x01;
+    public static final int READONLY = 0x01;
 
     /**
      * Property attribute indicating property is not enumerated.
      *
-     * Only enumerated properties will be returned by getIds().
+     * <p>Only enumerated properties will be returned by getIds().
      *
      * @see org.mozilla.javascript.ScriptableObject#getIds()
      * @see org.mozilla.javascript.ScriptableObject#getAttributes(String)
      * @see org.mozilla.javascript.ScriptableObject#setAttributes(String, int)
      */
-    public static final int DONTENUM =  0x02;
+    public static final int DONTENUM = 0x02;
 
     /**
      * Property attribute indicating property cannot be deleted.
@@ -96,48 +91,31 @@
     public static final int PERMANENT = 0x04;
 
     /**
-     * Property attribute indicating that this is a const property that has not
-     * been assigned yet.  The first 'const' assignment to the property will
-     * clear this bit.
+     * Property attribute indicating that this is a const property that has not been assigned yet.
+     * The first 'const' assignment to the property will clear this bit.
      */
     public static final int UNINITIALIZED_CONST = 0x08;
 
-    public static final int CONST = PERMANENT|READONLY|UNINITIALIZED_CONST;
-    /**
-     * The prototype of this object.
-     */
+    public static final int CONST = PERMANENT | READONLY | UNINITIALIZED_CONST;
+    /** The prototype of this object. */
     private Scriptable prototypeObject;
 
-    /**
-     * The parent scope of this object.
-     */
+    /** The parent scope of this object. */
     private Scriptable parentScopeObject;
 
-    private transient Slot[] slots;
-    // If count >= 0, it gives number of keys or if count < 0,
-    // it indicates sealed object where ~count gives number of keys
-    private int count;
+    /**
+     * This holds all the slots. It may or may not be thread-safe, and may expand itself to a
+     * different data structure depending on the size of the object.
+     */
+    private transient SlotMapContainer slotMap;
 
     // Where external array data is stored.
     private transient ExternalArrayData externalData;
 
-    // gateways into the definition-order linked list of slots
-    private transient Slot firstAdded;
-    private transient Slot lastAdded;
-
-
-    private volatile Map<Object,Object> associatedValues;
-
-    private static final int SLOT_QUERY = 1;
-    private static final int SLOT_MODIFY = 2;
-    private static final int SLOT_MODIFY_CONST = 3;
-    private static final int SLOT_MODIFY_GETTER_SETTER = 4;
-    private static final int SLOT_CONVERT_ACCESSOR_TO_DATA = 5;
-
-    // initial slot array size, must be a power of 2
-    private static final int INITIAL_SLOT_SIZE = 4;
+    private volatile Map<Object, Object> associatedValues;
 
     private boolean isExtensible = true;
+    private boolean isSealed = false;
 
     private static final Method GET_ARRAY_LENGTH;
 
@@ -149,282 +127,55 @@
         }
     }
 
-    private static class Slot implements Serializable
-    {
-        private static final long serialVersionUID = -6090581677123995491L;
-        Object name; // This can change due to caching
-        int indexOrHash;
-        private volatile short attributes;
-        transient volatile boolean wasDeleted;
-        volatile Object value;
-        transient Slot next; // next in hash table bucket
-        transient volatile Slot orderedNext; // next in linked list
-
-        Slot(Object name, int indexOrHash, int attributes)
-        {
-            this.name = name;
-            this.indexOrHash = indexOrHash;
-            this.attributes = (short)attributes;
-        }
-
-        private void readObject(ObjectInputStream in)
-            throws IOException, ClassNotFoundException
-        {
-            in.defaultReadObject();
-            if (name != null) {
-                indexOrHash = name.hashCode();
-            }
-        }
-
-        boolean setValue(Object value, Scriptable owner, Scriptable start) {
-            if ((attributes & READONLY) != 0) {
-                Context cx = Context.getContext();
-                if (cx.isStrictMode()) {
-                    throw ScriptRuntime.typeError1("msg.modify.readonly", name);
-                }
-                return true;
-            }
-            if (owner == start) {
-                this.value = value;
-                return true;
-            } else {
-                return false;
-            }
-        }
-
-        Object getValue(Scriptable start) {
-            return value;
-        }
-
-        int getAttributes()
-        {
-            return attributes;
-        }
-
-        synchronized void setAttributes(int value)
-        {
-            checkValidAttributes(value);
-            attributes = (short)value;
-        }
-
-        void markDeleted() {
-            wasDeleted = true;
-            value = null;
-            name = null;
-        }
-
-        ScriptableObject getPropertyDescriptor(Context cx, Scriptable scope) {
-            return buildDataDescriptor(scope, value, attributes);
-        }
-
-    }
-
-    protected static ScriptableObject buildDataDescriptor(Scriptable scope,
-                                                          Object value,
-                                                          int attributes) {
+    protected static ScriptableObject buildDataDescriptor(
+            Scriptable scope, Object value, int attributes) {
         ScriptableObject desc = new NativeObject();
         ScriptRuntime.setBuiltinProtoAndParent(desc, scope, TopLevel.Builtins.Object);
         desc.defineProperty("value", value, EMPTY);
-        desc.defineProperty("writable", (attributes & READONLY) == 0, EMPTY);
-        desc.defineProperty("enumerable", (attributes & DONTENUM) == 0, EMPTY);
-        desc.defineProperty("configurable", (attributes & PERMANENT) == 0, EMPTY);
+        desc.setCommonDescriptorProperties(attributes, true);
         return desc;
     }
 
-    private static final class GetterSlot extends Slot
-    {
-        static final long serialVersionUID = -4900574849788797588L;
-
-        Object getter;
-        Object setter;
-
-        GetterSlot(Object name, int indexOrHash, int attributes)
-        {
-            super(name, indexOrHash, attributes);
-        }
-
-        @Override
-        ScriptableObject getPropertyDescriptor(Context cx, Scriptable scope) {
-            int attr = getAttributes();
-            ScriptableObject desc = new NativeObject();
-            ScriptRuntime.setBuiltinProtoAndParent(desc, scope, TopLevel.Builtins.Object);
-            desc.defineProperty("enumerable", (attr & DONTENUM) == 0, EMPTY);
-            desc.defineProperty("configurable", (attr & PERMANENT) == 0, EMPTY);
-            if (getter == null && setter == null) {
-                desc.defineProperty("writable", (attr & READONLY) == 0, EMPTY);
-            }
-            if (getter != null) desc.defineProperty("get", getter, EMPTY);
-            if (setter != null) desc.defineProperty("set", setter, EMPTY);
-            return desc;
-        }
-
-        @Override
-        boolean setValue(Object value, Scriptable owner, Scriptable start) {
-            if (setter == null) {
-                if (getter != null) {
-                    Context cx = Context.getContext();
-                    if (cx.isStrictMode() ||
-                        // Based on TC39 ES3.1 Draft of 9-Feb-2009, 8.12.4, step 2,
-                        // we should throw a TypeError in this case.
-                        cx.hasFeature(Context.FEATURE_STRICT_MODE)) {
-                        throw ScriptRuntime.typeError1("msg.set.prop.no.setter", name);
-                    }
-                    // Assignment to a property with only a getter defined. The
-                    // assignment is ignored. See bug 478047.
-                    return true;
-                }
-            } else {
-                Context cx = Context.getContext();
-                if (setter instanceof MemberBox) {
-                    MemberBox nativeSetter = (MemberBox)setter;
-                    Class<?> pTypes[] = nativeSetter.argTypes;
-                    // XXX: cache tag since it is already calculated in
-                    // defineProperty ?
-                    Class<?> valueType = pTypes[pTypes.length - 1];
-                    int tag = FunctionObject.getTypeTag(valueType);
-                    Object actualArg = FunctionObject.convertArg(cx, start,
-                                                                 value, tag);
-                    Object setterThis;
-                    Object[] args;
-                    if (nativeSetter.delegateTo == null) {
-                        setterThis = start;
-                        args = new Object[] { actualArg };
-                    } else {
-                        setterThis = nativeSetter.delegateTo;
-                        args = new Object[] { start, actualArg };
-                    }
-                    nativeSetter.invoke(setterThis, args);
-                } else if (setter instanceof Function) {
-                    Function f = (Function)setter;
-                    f.call(cx, f.getParentScope(), start,
-                           new Object[] { value });
-                }
-                return true;
-            }
-            return super.setValue(value, owner, start);
-        }
-
-        @Override
-        Object getValue(Scriptable start) {
-            if (getter != null) {
-                if (getter instanceof MemberBox) {
-                    MemberBox nativeGetter = (MemberBox)getter;
-                    Object getterThis;
-                    Object[] args;
-                    if (nativeGetter.delegateTo == null) {
-                        getterThis = start;
-                        args = ScriptRuntime.emptyArgs;
-                    } else {
-                        getterThis = nativeGetter.delegateTo;
-                        args = new Object[] { start };
-                    }
-                    return nativeGetter.invoke(getterThis, args);
-                } else if (getter instanceof Function) {
-                    Function f = (Function)getter;
-                    Context cx = Context.getContext();
-                    return f.call(cx, f.getParentScope(), start,
-                                  ScriptRuntime.emptyArgs);
-                }
-            }
-            Object val = this.value;
-            if (val instanceof LazilyLoadedCtor) {
-                LazilyLoadedCtor initializer = (LazilyLoadedCtor)val;
-                try {
-                    initializer.init();
-                } finally {
-                    this.value = val = initializer.getValue();
-                }
-            }
-            return val;
-        }
-
-        @Override
-        void markDeleted() {
-            super.markDeleted();
-            getter = null;
-            setter = null;
+    protected void setCommonDescriptorProperties(int attributes, boolean defineWritable) {
+        if (defineWritable) {
+            defineProperty("writable", (attributes & READONLY) == 0, EMPTY);
         }
+        defineProperty("enumerable", (attributes & DONTENUM) == 0, EMPTY);
+        defineProperty("configurable", (attributes & PERMANENT) == 0, EMPTY);
     }
 
-    /**
-     * A wrapper around a slot that allows the slot to be used in a new slot
-     * table while keeping it functioning in its old slot table/linked list
-     * context. This is used when linked slots are copied to a new slot table.
-     * In a multi-threaded environment, these slots may still be accessed
-     * through their old slot table. See bug 688458.
-     */
-    private static class RelinkedSlot extends Slot {
-
-        final Slot slot;
-
-        RelinkedSlot(Slot slot) {
-            super(slot.name, slot.indexOrHash, slot.attributes);
-            // Make sure we always wrap the actual slot, not another relinked one
-            this.slot = unwrapSlot(slot);
-        }
-
-        @Override
-        boolean setValue(Object value, Scriptable owner, Scriptable start) {
-            return slot.setValue(value, owner, start);
-        }
-
-        @Override
-        Object getValue(Scriptable start) {
-            return slot.getValue(start);
-        }
-
-        @Override
-        ScriptableObject getPropertyDescriptor(Context cx, Scriptable scope) {
-            return slot.getPropertyDescriptor(cx, scope);
-        }
-
-        @Override
-        int getAttributes() {
-            return slot.getAttributes();
-        }
-
-        @Override
-        void setAttributes(int value) {
-            slot.setAttributes(value);
-        }
-
-        @Override
-        void markDeleted() {
-            super.markDeleted();
-            slot.markDeleted();
-        }
-
-        private void writeObject(ObjectOutputStream out) throws IOException {
-            out.writeObject(slot);  // just serialize the wrapped slot
-        }
-
-    }
-
-    static void checkValidAttributes(int attributes)
-    {
+    static void checkValidAttributes(int attributes) {
         final int mask = READONLY | DONTENUM | PERMANENT | UNINITIALIZED_CONST;
         if ((attributes & ~mask) != 0) {
             throw new IllegalArgumentException(String.valueOf(attributes));
         }
     }
 
-    public ScriptableObject()
-    {
+    private static SlotMapContainer createSlotMap(int initialSize) {
+        Context cx = Context.getCurrentContext();
+        if ((cx != null) && cx.hasFeature(Context.FEATURE_THREAD_SAFE_OBJECTS)) {
+            return new ThreadSafeSlotMapContainer(initialSize);
+        }
+        return new SlotMapContainer(initialSize);
+    }
+
+    public ScriptableObject() {
+        slotMap = createSlotMap(0);
     }
 
-    public ScriptableObject(Scriptable scope, Scriptable prototype)
-    {
-        if (scope == null)
-            throw new IllegalArgumentException();
+    public ScriptableObject(Scriptable scope, Scriptable prototype) {
+        if (scope == null) throw new IllegalArgumentException();
 
         parentScopeObject = scope;
         prototypeObject = prototype;
+        slotMap = createSlotMap(0);
     }
 
     /**
      * Gets the value that will be returned by calling the typeof operator on this object.
-     * @return default is "object" unless {@link #avoidObjectDetection()} is <code>true</code> in which
-     * case it returns "undefined"
+     *
+     * @return default is "object" unless {@link #avoidObjectDetection()} is <code>true</code> in
+     *     which case it returns "undefined"
      */
     public String getTypeOf() {
         return avoidObjectDetection() ? "undefined" : "object";
@@ -433,10 +184,10 @@
     /**
      * Return the name of the class.
      *
-     * This is typically the same name as the constructor.
-     * Classes extending ScriptableObject must implement this abstract
-     * method.
+     * <p>This is typically the same name as the constructor. Classes extending ScriptableObject
+     * must implement this abstract method.
      */
+    @Override
     public abstract String getClassName();
 
     /**
@@ -446,9 +197,9 @@
      * @param start the object in which the lookup began
      * @return true if and only if the property was found in the object
      */
-    public boolean has(String name, Scriptable start)
-    {
-        return null != getSlot(name, 0, SLOT_QUERY);
+    @Override
+    public boolean has(String name, Scriptable start) {
+        return null != slotMap.query(name, 0);
     }
 
     /**
@@ -458,35 +209,32 @@
      * @param start the object in which the lookup began
      * @return true if and only if the property was found in the object
      */
-    public boolean has(int index, Scriptable start)
-    {
+    @Override
+    public boolean has(int index, Scriptable start) {
         if (externalData != null) {
             return (index < externalData.getArrayLength());
         }
-        return null != getSlot(null, index, SLOT_QUERY);
+        return null != slotMap.query(null, index);
     }
 
-    /**
-     * A version of "has" that supports symbols.
-     */
-    public boolean has(Symbol key, Scriptable start)
-    {
-        return null != getSlot(key, 0, SLOT_QUERY);
+    /** A version of "has" that supports symbols. */
+    @Override
+    public boolean has(Symbol key, Scriptable start) {
+        return null != slotMap.query(key, 0);
     }
 
     /**
      * Returns the value of the named property or NOT_FOUND.
      *
-     * If the property was created using defineProperty, the
-     * appropriate getter method is called.
+     * <p>If the property was created using defineProperty, the appropriate getter method is called.
      *
      * @param name the name of the property
      * @param start the object in which the lookup began
      * @return the value of the property (may be null), or NOT_FOUND
      */
-    public Object get(String name, Scriptable start)
-    {
-        Slot slot = getSlot(name, 0, SLOT_QUERY);
+    @Override
+    public Object get(String name, Scriptable start) {
+        Slot slot = slotMap.query(name, 0);
         if (slot == null) {
             return Scriptable.NOT_FOUND;
         }
@@ -500,8 +248,8 @@
      * @param start the object in which the lookup began
      * @return the value of the property (may be null), or NOT_FOUND
      */
-    public Object get(int index, Scriptable start)
-    {
+    @Override
+    public Object get(int index, Scriptable start) {
         if (externalData != null) {
             if (index < externalData.getArrayLength()) {
                 return externalData.getArrayElement(index);
@@ -509,19 +257,17 @@
             return Scriptable.NOT_FOUND;
         }
 
-        Slot slot = getSlot(null, index, SLOT_QUERY);
+        Slot slot = slotMap.query(null, index);
         if (slot == null) {
             return Scriptable.NOT_FOUND;
         }
         return slot.getValue(start);
     }
 
-    /**
-     * Another version of Get that supports Symbol keyed properties.
-     */
-    public Object get(Symbol key, Scriptable start)
-    {
-        Slot slot = getSlot(key, 0, SLOT_QUERY);
+    /** Another version of Get that supports Symbol keyed properties. */
+    @Override
+    public Object get(Symbol key, Scriptable start) {
+        Slot slot = slotMap.query(key, 0);
         if (slot == null) {
             return Scriptable.NOT_FOUND;
         }
@@ -531,22 +277,18 @@
     /**
      * Sets the value of the named property, creating it if need be.
      *
-     * If the property was created using defineProperty, the
-     * appropriate setter method is called. <p>
+     * <p>If the property was created using defineProperty, the appropriate setter method is called.
      *
-     * If the property's attributes include READONLY, no action is
-     * taken.
-     * This method will actually set the property in the start
-     * object.
+     * <p>If the property's attributes include READONLY, no action is taken. This method will
+     * actually set the property in the start object.
      *
      * @param name the name of the property
      * @param start the object whose property is being set
      * @param value value to set the property to
      */
-    public void put(String name, Scriptable start, Object value)
-    {
-        if (putImpl(name, 0, start, value))
-            return;
+    @Override
+    public void put(String name, Scriptable start, Object value) {
+        if (putImpl(name, 0, start, value)) return;
 
         if (start == this) throw Kit.codeBug();
         start.put(name, start, value);
@@ -559,35 +301,34 @@
      * @param start the object whose property is being set
      * @param value value to set the property to
      */
-    public void put(int index, Scriptable start, Object value)
-    {
+    @Override
+    public void put(int index, Scriptable start, Object value) {
         if (externalData != null) {
             if (index < externalData.getArrayLength()) {
                 externalData.setArrayElement(index, value);
             } else {
                 throw new JavaScriptException(
-                    ScriptRuntime.newNativeError(Context.getCurrentContext(), this,
-                                                 TopLevel.NativeErrors.RangeError,
-                                                 new Object[] { "External array index out of bounds " }),
-                    null, 0);
+                        ScriptRuntime.newNativeError(
+                                Context.getCurrentContext(),
+                                this,
+                                TopLevel.NativeErrors.RangeError,
+                                new Object[] {"External array index out of bounds "}),
+                        null,
+                        0);
             }
             return;
         }
 
-        if (putImpl(null, index, start, value))
-            return;
+        if (putImpl(null, index, start, value)) return;
 
         if (start == this) throw Kit.codeBug();
         start.put(index, start, value);
     }
 
-    /**
-     * Implementation of put required by SymbolScriptable objects.
-     */
-    public void put(Symbol key, Scriptable start, Object value)
-    {
-        if (putImpl(key, 0, start, value))
-            return;
+    /** Implementation of put required by SymbolScriptable objects. */
+    @Override
+    public void put(Symbol key, Scriptable start, Object value) {
+        if (putImpl(key, 0, start, value)) return;
 
         if (start == this) throw Kit.codeBug();
         ensureSymbolScriptable(start).put(key, start, value);
@@ -596,141 +337,123 @@
     /**
      * Removes a named property from the object.
      *
-     * If the property is not found, or it has the PERMANENT attribute,
-     * no action is taken.
+     * <p>If the property is not found, or it has the PERMANENT attribute, no action is taken.
      *
      * @param name the name of the property
      */
-    public void delete(String name)
-    {
+    @Override
+    public void delete(String name) {
         checkNotSealed(name, 0);
-        removeSlot(name, 0);
+        slotMap.remove(name, 0);
     }
 
     /**
      * Removes the indexed property from the object.
      *
-     * If the property is not found, or it has the PERMANENT attribute,
-     * no action is taken.
+     * <p>If the property is not found, or it has the PERMANENT attribute, no action is taken.
      *
      * @param index the numeric index for the property
      */
-    public void delete(int index)
-    {
+    @Override
+    public void delete(int index) {
         checkNotSealed(null, index);
-        removeSlot(null, index);
+        slotMap.remove(null, index);
     }
 
-    /**
-     * Removes an object like the others, but using a Symbol as the key.
-     */
-    public void delete(Symbol key)
-    {
+    /** Removes an object like the others, but using a Symbol as the key. */
+    @Override
+    public void delete(Symbol key) {
         checkNotSealed(key, 0);
-        removeSlot(key, 0);
+        slotMap.remove(key, 0);
     }
 
     /**
      * Sets the value of the named const property, creating it if need be.
      *
-     * If the property was created using defineProperty, the
-     * appropriate setter method is called. <p>
+     * <p>If the property was created using defineProperty, the appropriate setter method is called.
      *
-     * If the property's attributes include READONLY, no action is
-     * taken.
-     * This method will actually set the property in the start
-     * object.
+     * <p>If the property's attributes include READONLY, no action is taken. This method will
+     * actually set the property in the start object.
      *
      * @param name the name of the property
      * @param start the object whose property is being set
      * @param value value to set the property to
      */
-    public void putConst(String name, Scriptable start, Object value)
-    {
-        if (putConstImpl(name, 0, start, value, READONLY))
-            return;
+    @Override
+    public void putConst(String name, Scriptable start, Object value) {
+        if (putConstImpl(name, 0, start, value, READONLY)) return;
 
         if (start == this) throw Kit.codeBug();
         if (start instanceof ConstProperties)
-            ((ConstProperties)start).putConst(name, start, value);
-        else
-            start.put(name, start, value);
+            ((ConstProperties) start).putConst(name, start, value);
+        else start.put(name, start, value);
     }
 
-    public void defineConst(String name, Scriptable start)
-    {
-        if (putConstImpl(name, 0, start, Undefined.instance, UNINITIALIZED_CONST))
-            return;
+    @Override
+    public void defineConst(String name, Scriptable start) {
+        if (putConstImpl(name, 0, start, Undefined.instance, UNINITIALIZED_CONST)) return;
 
         if (start == this) throw Kit.codeBug();
-        if (start instanceof ConstProperties)
-            ((ConstProperties)start).defineConst(name, start);
+        if (start instanceof ConstProperties) ((ConstProperties) start).defineConst(name, start);
     }
 
     /**
      * Returns true if the named property is defined as a const on this object.
+     *
      * @param name
-     * @return true if the named property is defined as a const, false
-     * otherwise.
+     * @return true if the named property is defined as a const, false otherwise.
      */
-    public boolean isConst(String name)
-    {
-        Slot slot = getSlot(name, 0, SLOT_QUERY);
+    @Override
+    public boolean isConst(String name) {
+        Slot slot = slotMap.query(name, 0);
         if (slot == null) {
             return false;
         }
-        return (slot.getAttributes() & (PERMANENT|READONLY)) ==
-                                       (PERMANENT|READONLY);
-
+        return (slot.getAttributes() & (PERMANENT | READONLY)) == (PERMANENT | READONLY);
     }
 
     /**
-     * @deprecated Use {@link #getAttributes(String name)}. The engine always
-     * ignored the start argument.
+     * @deprecated Use {@link #getAttributes(String name)}. The engine always ignored the start
+     *     argument.
      */
     @Deprecated
-    public final int getAttributes(String name, Scriptable start)
-    {
+    public final int getAttributes(String name, Scriptable start) {
         return getAttributes(name);
     }
 
     /**
-     * @deprecated Use {@link #getAttributes(int index)}. The engine always
-     * ignored the start argument.
+     * @deprecated Use {@link #getAttributes(int index)}. The engine always ignored the start
+     *     argument.
      */
     @Deprecated
-    public final int getAttributes(int index, Scriptable start)
-    {
+    public final int getAttributes(int index, Scriptable start) {
         return getAttributes(index);
     }
 
     /**
-     * @deprecated Use {@link #setAttributes(String name, int attributes)}.
-     * The engine always ignored the start argument.
+     * @deprecated Use {@link #setAttributes(String name, int attributes)}. The engine always
+     *     ignored the start argument.
      */
     @Deprecated
-    public final void setAttributes(String name, Scriptable start,
-                                    int attributes)
-    {
+    public final void setAttributes(String name, Scriptable start, int attributes) {
         setAttributes(name, attributes);
     }
 
     /**
-     * @deprecated Use {@link #setAttributes(int index, int attributes)}.
-     * The engine always ignored the start argument.
+     * @deprecated Use {@link #setAttributes(int index, int attributes)}. The engine always ignored
+     *     the start argument.
      */
     @Deprecated
-    public void setAttributes(int index, Scriptable start,
-                              int attributes)
-    {
+    public void setAttributes(int index, Scriptable start, int attributes) {
         setAttributes(index, attributes);
     }
 
     /**
      * Get the attributes of a named property.
      *
-     * The property is specified by <code>name</code>
-     * as defined for <code>has</code>.<p>
+     * <p>The property is specified by <code>name</code> as defined for <code>has</code>.
+     *
+     * <p>
      *
      * @param name the identifier for the property
      * @return the bitset of attributes
@@ -741,17 +464,15 @@
      * @see org.mozilla.javascript.ScriptableObject#PERMANENT
      * @see org.mozilla.javascript.ScriptableObject#EMPTY
      */
-    public int getAttributes(String name)
-    {
-        return findAttributeSlot(name, 0, SLOT_QUERY).getAttributes();
+    public int getAttributes(String name) {
+        return getAttributeSlot(name, 0).getAttributes();
     }
 
     /**
      * Get the attributes of an indexed property.
      *
      * @param index the numeric index for the property
-     * @exception EvaluatorException if the named property is not found
-     *            is not found
+     * @exception EvaluatorException if the named property is not found is not found
      * @return the bitset of attributes
      * @see org.mozilla.javascript.ScriptableObject#has(String, Scriptable)
      * @see org.mozilla.javascript.ScriptableObject#READONLY
@@ -759,28 +480,22 @@
      * @see org.mozilla.javascript.ScriptableObject#PERMANENT
      * @see org.mozilla.javascript.ScriptableObject#EMPTY
      */
-    public int getAttributes(int index)
-    {
-        return findAttributeSlot(null, index, SLOT_QUERY).getAttributes();
+    public int getAttributes(int index) {
+        return getAttributeSlot(null, index).getAttributes();
     }
 
-    public int getAttributes(Symbol sym)
-    {
-        return findAttributeSlot(sym, SLOT_QUERY).getAttributes();
+    public int getAttributes(Symbol sym) {
+        return getAttributeSlot(sym).getAttributes();
     }
 
-
     /**
      * Set the attributes of a named property.
      *
-     * The property is specified by <code>name</code>
-     * as defined for <code>has</code>.<p>
+     * <p>The property is specified by <code>name</code> as defined for <code>has</code>.
      *
-     * The possible attributes are READONLY, DONTENUM,
-     * and PERMANENT. Combinations of attributes
-     * are expressed by the bitwise OR of attributes.
-     * EMPTY is the state of no attributes set. Any unused
-     * bits are reserved for future use.
+     * <p>The possible attributes are READONLY, DONTENUM, and PERMANENT. Combinations of attributes
+     * are expressed by the bitwise OR of attributes. EMPTY is the state of no attributes set. Any
+     * unused bits are reserved for future use.
      *
      * @param name the name of the property
      * @param attributes the bitset of attributes
@@ -791,10 +506,10 @@
      * @see org.mozilla.javascript.ScriptableObject#PERMANENT
      * @see org.mozilla.javascript.ScriptableObject#EMPTY
      */
-    public void setAttributes(String name, int attributes)
-    {
+    public void setAttributes(String name, int attributes) {
         checkNotSealed(name, 0);
-        findAttributeSlot(name, 0, SLOT_MODIFY).setAttributes(attributes);
+        Slot attrSlot = slotMap.modify(name, 0, 0);
+        attrSlot.setAttributes(attributes);
     }
 
     /**
@@ -809,142 +524,156 @@
      * @see org.mozilla.javascript.ScriptableObject#PERMANENT
      * @see org.mozilla.javascript.ScriptableObject#EMPTY
      */
-    public void setAttributes(int index, int attributes)
-    {
+    public void setAttributes(int index, int attributes) {
         checkNotSealed(null, index);
-        findAttributeSlot(null, index, SLOT_MODIFY).setAttributes(attributes);
+        Slot attrSlot = slotMap.modify(null, index, 0);
+        attrSlot.setAttributes(attributes);
     }
 
-    /**
-     * Set attributes of a Symbol-keyed property.
-     */
-    public void setAttributes(Symbol key, int attributes)
-    {
+    /** Set attributes of a Symbol-keyed property. */
+    public void setAttributes(Symbol key, int attributes) {
         checkNotSealed(key, 0);
-        findAttributeSlot(key, SLOT_MODIFY).setAttributes(attributes);
-    }
-
-    /**
-     * XXX: write docs.
-     */
-    public void setGetterOrSetter(String name, int index,
-                                  Callable getterOrSetter, boolean isSetter)
-    {
-        setGetterOrSetter(name, index, getterOrSetter, isSetter, false);
+        Slot attrSlot = slotMap.modify(key, 0, 0);
+        attrSlot.setAttributes(attributes);
     }
 
-    private void setGetterOrSetter(String name, int index, Callable getterOrSetter,
-                                   boolean isSetter, boolean force)
-    {
-        if (name != null && index != 0)
-            throw new IllegalArgumentException(name);
-
-        if (!force) {
-            checkNotSealed(name, index);
-        }
+    /** Implement the legacy "__defineGetter__" and "__defineSetter__" methods. */
+    public void setGetterOrSetter(
+            String name, int index, Callable getterOrSetter, boolean isSetter) {
+        if (name != null && index != 0) throw new IllegalArgumentException(name);
+        checkNotSealed(name, index);
 
-        final GetterSlot gslot;
+        AccessorSlot aSlot;
         if (isExtensible()) {
-            gslot = (GetterSlot)getSlot(name, index, SLOT_MODIFY_GETTER_SETTER);
+            Slot slot = slotMap.modify(name, index, 0);
+            if (slot instanceof AccessorSlot) {
+                aSlot = (AccessorSlot) slot;
+            } else {
+                aSlot = new AccessorSlot(slot);
+                slotMap.replace(slot, aSlot);
+            }
         } else {
-            Slot slot = unwrapSlot(getSlot(name, index, SLOT_QUERY));
-            if (!(slot instanceof GetterSlot))
+            Slot slot = slotMap.query(name, index);
+            if (slot instanceof AccessorSlot) {
+                aSlot = (AccessorSlot) slot;
+            } else {
                 return;
-            gslot = (GetterSlot) slot;
+            }
         }
 
-        if (!force) {
-            int attributes = gslot.getAttributes();
-            if ((attributes & READONLY) != 0) {
-                throw Context.reportRuntimeError1("msg.modify.readonly", name);
-            }
+        int attributes = aSlot.getAttributes();
+        if ((attributes & READONLY) != 0) {
+            throw Context.reportRuntimeErrorById("msg.modify.readonly", name);
         }
+
+        // Emulate old behavior where nothing happened if it wasn't actually a Function
         if (isSetter) {
-            gslot.setter = getterOrSetter;
+            if (getterOrSetter instanceof Function) {
+                aSlot.setter = new AccessorSlot.FunctionSetter(getterOrSetter);
+            } else {
+                aSlot.setter = null;
+            }
         } else {
-            gslot.getter = getterOrSetter;
+            if (getterOrSetter instanceof Function) {
+                aSlot.getter = new AccessorSlot.FunctionGetter(getterOrSetter);
+            } else {
+                aSlot.getter = null;
+            }
         }
-        gslot.value = Undefined.instance;
+
+        aSlot.value = Undefined.instance;
     }
 
     /**
-     * Get the getter or setter for a given property. Used by __lookupGetter__
-     * and __lookupSetter__.
+     * Get the getter or setter for a given property. Used by __lookupGetter__ and __lookupSetter__.
      *
      * @param name Name of the object. If nonnull, index must be 0.
      * @param index Index of the object. If nonzero, name must be null.
+     * @param scope the current scope.
      * @param isSetter If true, return the setter, otherwise return the getter.
-     * @exception IllegalArgumentException if both name and index are nonnull
-     *            and nonzero respectively.
-     * @return Null if the property does not exist. Otherwise returns either
-     *         the getter or the setter for the property, depending on
-     *         the value of isSetter (may be undefined if unset).
-     */
-    public Object getGetterOrSetter(String name, int index, boolean isSetter)
-    {
-        if (name != null && index != 0)
-            throw new IllegalArgumentException(name);
-        Slot slot = unwrapSlot(getSlot(name, index, SLOT_QUERY));
-        if (slot == null)
-            return null;
-        if (slot instanceof GetterSlot) {
-            GetterSlot gslot = (GetterSlot)slot;
-            Object result = isSetter ? gslot.setter : gslot.getter;
-            return result != null ? result : Undefined.instance;
-        } else
-            return Undefined.instance;
+     * @exception IllegalArgumentException if both name and index are nonnull and nonzero
+     *     respectively.
+     * @return Undefined.instance if the property does not exist. Otherwise returns either the
+     *     getter or the setter for the property, depending on the value of isSetter (may be
+     *     undefined if unset).
+     */
+    public Object getGetterOrSetter(String name, int index, Scriptable scope, boolean isSetter) {
+        if (name != null && index != 0) throw new IllegalArgumentException(name);
+        Slot slot = slotMap.query(name, index);
+        if (slot == null) return null;
+        Function getterOrSetter =
+                isSetter
+                        ? slot.getSetterFunction(name, scope)
+                        : slot.getGetterFunction(name, scope);
+        return (getterOrSetter == null) ? Undefined.instance : getterOrSetter;
+    }
+
+    /**
+     * Get the getter or setter for a given property. Used by __lookupGetter__ and __lookupSetter__.
+     *
+     * @deprecated since 1.7.14. Use the form with the scope to ensure consistent function prototype
+     *     handling in all cases.
+     * @param name Name of the object. If nonnull, index must be 0.
+     * @param index Index of the object. If nonzero, name must be null.
+     * @param isSetter If true, return the setter, otherwise return the getter.
+     * @exception IllegalArgumentException if both name and index are nonnull and nonzero
+     *     respectively.
+     * @return Null if the property does not exist. Otherwise returns either the getter or the
+     *     setter for the property, depending on the value of isSetter (may be undefined if unset).
+     */
+    @Deprecated
+    public Object getGetterOrSetter(String name, int index, boolean isSetter) {
+        return getGetterOrSetter(name, index, this, isSetter);
     }
 
     /**
      * Returns whether a property is a getter or a setter
+     *
      * @param name property name
      * @param index property index
      * @param setter true to check for a setter, false for a getter
      * @return whether the property is a getter or a setter
      */
     protected boolean isGetterOrSetter(String name, int index, boolean setter) {
-        Slot slot = unwrapSlot(getSlot(name, index, SLOT_QUERY));
-        if (slot instanceof GetterSlot) {
-            if (setter && ((GetterSlot)slot).setter != null) return true;
-            if (!setter && ((GetterSlot)slot).getter != null) return true;
-        }
-        return false;
+        Slot slot = slotMap.query(name, index);
+        return (slot != null && slot.isSetterSlot());
     }
 
-    void addLazilyInitializedValue(String name, int index,
-                                   LazilyLoadedCtor init, int attributes)
-    {
-        if (name != null && index != 0)
-            throw new IllegalArgumentException(name);
+    void addLazilyInitializedValue(String name, int index, LazilyLoadedCtor init, int attributes) {
+        if (name != null && index != 0) throw new IllegalArgumentException(name);
         checkNotSealed(name, index);
-        GetterSlot gslot = (GetterSlot)getSlot(name, index,
-                                               SLOT_MODIFY_GETTER_SETTER);
-        gslot.setAttributes(attributes);
-        gslot.getter = null;
-        gslot.setter = null;
-        gslot.value = init;
+        Slot slot = slotMap.modify(name, index, 0);
+        LazyLoadSlot lslot;
+        if (slot instanceof LazyLoadSlot) {
+            lslot = (LazyLoadSlot) slot;
+        } else {
+            lslot = new LazyLoadSlot(slot);
+            slotMap.replace(slot, lslot);
+        }
+
+        lslot.setAttributes(attributes);
+        lslot.value = init;
     }
 
     /**
-     * Attach the specified object to this object, and delegate all indexed property lookups to it. In other words,
-     * if the object has 3 elements, then an attempt to look up or modify "[0]", "[1]", or "[2]" will be delegated
-     * to this object. Additional indexed properties outside the range specified, and additional non-indexed
-     * properties, may still be added. The object specified must implement the ExternalArrayData interface.
+     * Attach the specified object to this object, and delegate all indexed property lookups to it.
+     * In other words, if the object has 3 elements, then an attempt to look up or modify "[0]",
+     * "[1]", or "[2]" will be delegated to this object. Additional indexed properties outside the
+     * range specified, and additional non-indexed properties, may still be added. The object
+     * specified must implement the ExternalArrayData interface.
      *
-     * @param array the List to use for delegated property access. Set this to null to revert back to regular
-     *              property access.
+     * @param array the List to use for delegated property access. Set this to null to revert back
+     *     to regular property access.
      * @since 1.7.6
      */
-    public void setExternalArrayData(ExternalArrayData array)
-    {
+    public void setExternalArrayData(ExternalArrayData array) {
         externalData = array;
 
         if (array == null) {
             delete("length");
         } else {
             // Define "length" to return whatever length the List gives us.
-            defineProperty("length", null,
-                           GET_ARRAY_LENGTH, null, READONLY | DONTENUM);
+            defineProperty("length", null, GET_ARRAY_LENGTH, null, READONLY | DONTENUM);
         }
     }
 
@@ -954,77 +683,68 @@
      * @return the array, or null if it was never set
      * @since 1.7.6
      */
-    public ExternalArrayData getExternalArrayData()
-    {
+    public ExternalArrayData getExternalArrayData() {
         return externalData;
     }
 
     /**
-     * This is a function used by setExternalArrayData to dynamically get the "length" property value.
+     * This is a function used by setExternalArrayData to dynamically get the "length" property
+     * value.
      */
-    public Object getExternalArrayLength()
-    {
-        return (externalData == null ? 0 : externalData.getArrayLength());
+    public Object getExternalArrayLength() {
+        return Integer.valueOf(externalData == null ? 0 : externalData.getArrayLength());
     }
 
-    /**
-     * Returns the prototype of the object.
-     */
-    public Scriptable getPrototype()
-    {
+    /** Returns the prototype of the object. */
+    @Override
+    public Scriptable getPrototype() {
         return prototypeObject;
     }
 
-    /**
-     * Sets the prototype of the object.
-     */
-    public void setPrototype(Scriptable m)
-    {
+    /** Sets the prototype of the object. */
+    @Override
+    public void setPrototype(Scriptable m) {
         prototypeObject = m;
     }
 
-    /**
-     * Returns the parent (enclosing) scope of the object.
-     */
-    public Scriptable getParentScope()
-    {
+    /** Returns the parent (enclosing) scope of the object. */
+    @Override
+    public Scriptable getParentScope() {
         return parentScopeObject;
     }
 
-    /**
-     * Sets the parent (enclosing) scope of the object.
-     */
-    public void setParentScope(Scriptable m)
-    {
+    /** Sets the parent (enclosing) scope of the object. */
+    @Override
+    public void setParentScope(Scriptable m) {
         parentScopeObject = m;
     }
 
     /**
      * Returns an array of ids for the properties of the object.
      *
-     * <p>Any properties with the attribute DONTENUM are not listed. <p>
+     * <p>Any properties with the attribute DONTENUM are not listed.
      *
-     * @return an array of java.lang.Objects with an entry for every
-     * listed property. Properties accessed via an integer index will
-     * have a corresponding
-     * Integer entry in the returned array. Properties accessed by
-     * a String will have a String entry in the returned array.
+     * <p>
+     *
+     * @return an array of java.lang.Objects with an entry for every listed property. Properties
+     *     accessed via an integer index will have a corresponding Integer entry in the returned
+     *     array. Properties accessed by a String will have a String entry in the returned array.
      */
+    @Override
     public Object[] getIds() {
         return getIds(false, false);
     }
 
     /**
-     * Returns an array of ids for the properties of the object.
+     * Returns an array of ids of all non-Symbol properties of the object.
      *
-     * <p>All properties, even those with attribute DONTENUM, are listed. <p>
+     * <p>All non-Symbol properties, even those with attribute DONTENUM, are listed.
      *
-     * @return an array of java.lang.Objects with an entry for every
-     * listed property. Properties accessed via an integer index will
-     * have a corresponding
-     * Integer entry in the returned array. Properties accessed by
-     * a String will have a String entry in the returned array.
+     * @return an array of java.lang.Objects with an entry for every non-Symbol property. Properties
+     *     accessed via an integer index will have a corresponding Integer entry in the returned
+     *     array. Properties accessed by a String will have a String entry in the returned array.
      */
+    @Override
     public Object[] getAllIds() {
         return getIds(true, false);
     }
@@ -1032,26 +752,23 @@
     /**
      * Implements the [[DefaultValue]] internal method.
      *
-     * <p>Note that the toPrimitive conversion is a no-op for
-     * every type other than Object, for which [[DefaultValue]]
-     * is called. See ECMA 9.1.<p>
+     * <p>Note that the toPrimitive conversion is a no-op for every type other than Object, for
+     * which [[DefaultValue]] is called. See ECMA 9.1.
      *
-     * A <code>hint</code> of null means "no hint".
+     * <p>A <code>hint</code> of null means "no hint".
      *
      * @param typeHint the type hint
      * @return the default value for the object
-     *
-     * See ECMA 8.6.2.6.
+     *     <p>See ECMA 8.6.2.6.
      */
-    public Object getDefaultValue(Class<?> typeHint)
-    {
+    @Override
+    public Object getDefaultValue(Class<?> typeHint) {
         return getDefaultValue(this, typeHint);
     }
 
-    public static Object getDefaultValue(Scriptable object, Class<?> typeHint)
-    {
+    public static Object getDefaultValue(Scriptable object, Class<?> typeHint) {
         Context cx = null;
-        for (int i=0; i < 2; i++) {
+        for (int i = 0; i < 2; i++) {
             boolean tryToString;
             if (typeHint == ScriptRuntime.StringClass) {
                 tryToString = (i == 0);
@@ -1060,73 +777,37 @@
             }
 
             String methodName;
-            Object[] args;
             if (tryToString) {
                 methodName = "toString";
-                args = ScriptRuntime.emptyArgs;
             } else {
                 methodName = "valueOf";
-                args = new Object[1];
-                String hint;
-                if (typeHint == null) {
-                    hint = "undefined";
-                } else if (typeHint == ScriptRuntime.StringClass) {
-                    hint = "string";
-                } else if (typeHint == ScriptRuntime.ScriptableClass) {
-                    hint = "object";
-                } else if (typeHint == ScriptRuntime.FunctionClass) {
-                    hint = "function";
-                } else if (typeHint == ScriptRuntime.BooleanClass
-                           || typeHint == Boolean.TYPE)
-                {
-                    hint = "boolean";
-                } else if (typeHint == ScriptRuntime.NumberClass ||
-                         typeHint == ScriptRuntime.ByteClass ||
-                         typeHint == Byte.TYPE ||
-                         typeHint == ScriptRuntime.ShortClass ||
-                         typeHint == Short.TYPE ||
-                         typeHint == ScriptRuntime.IntegerClass ||
-                         typeHint == Integer.TYPE ||
-                         typeHint == ScriptRuntime.FloatClass ||
-                         typeHint == Float.TYPE ||
-                         typeHint == ScriptRuntime.DoubleClass ||
-                         typeHint == Double.TYPE)
-                {
-                    hint = "number";
-                } else {
-                    throw Context.reportRuntimeError1(
-                        "msg.invalid.type", typeHint.toString());
-                }
-                args[0] = hint;
             }
             Object v = getProperty(object, methodName);
-            if (!(v instanceof Function))
-                continue;
+            if (!(v instanceof Function)) continue;
             Function fun = (Function) v;
-            if (cx == null)
+            if (cx == null) {
                 cx = Context.getContext();
-            v = fun.call(cx, fun.getParentScope(), object, args);
+            }
+            v = fun.call(cx, fun.getParentScope(), object, ScriptRuntime.emptyArgs);
             if (v != null) {
                 if (!(v instanceof Scriptable)) {
                     return v;
                 }
                 if (typeHint == ScriptRuntime.ScriptableClass
-                    || typeHint == ScriptRuntime.FunctionClass)
-                {
+                        || typeHint == ScriptRuntime.FunctionClass) {
                     return v;
                 }
                 if (tryToString && v instanceof Wrapper) {
                     // Let a wrapped java.lang.String pass for a primitive
                     // string.
-                    Object u = ((Wrapper)v).unwrap();
-                    if (u instanceof String)
-                        return u;
+                    Object u = ((Wrapper) v).unwrap();
+                    if (u instanceof String) return u;
                 }
             }
         }
         // fall through to error
         String arg = (typeHint == null) ? "undefined" : typeHint.getName();
-        throw ScriptRuntime.typeError1("msg.default.value", arg);
+        throw ScriptRuntime.typeErrorById("msg.default.value", arg);
     }
 
     /**
@@ -1134,11 +815,10 @@
      *
      * <p>This operator has been proposed to ECMA.
      *
-     * @param instance The value that appeared on the LHS of the instanceof
-     *              operator
+     * @param instance The value that appeared on the LHS of the instanceof operator
      * @return true if "this" appears in value's prototype chain
-     *
      */
+    @Override
     public boolean hasInstance(Scriptable instance) {
         // Default for JS objects (other than Function) is to do prototype
         // chasing.  This will be overridden in NativeFunction and non-JS
@@ -1148,13 +828,12 @@
     }
 
     /**
-     * Emulate the SpiderMonkey (and Firefox) feature of allowing
-     * custom objects to avoid detection by normal "object detection"
-     * code patterns. This is used to implement document.all.
-     * See https://bugzilla.mozilla.org/show_bug.cgi?id=412247.
-     * This is an analog to JOF_DETECTING from SpiderMonkey; see
-     * https://bugzilla.mozilla.org/show_bug.cgi?id=248549.
-     * Other than this special case, embeddings should return false.
+     * Emulate the SpiderMonkey (and Firefox) feature of allowing custom objects to avoid detection
+     * by normal "object detection" code patterns. This is used to implement document.all. See
+     * https://bugzilla.mozilla.org/show_bug.cgi?id=412247. This is an analog to JOF_DETECTING from
+     * SpiderMonkey; see https://bugzilla.mozilla.org/show_bug.cgi?id=248549. Other than this
+     * special case, embeddings should return false.
+     *
      * @return true if this object should avoid object detection
      * @since 1.7R1
      */
@@ -1163,230 +842,190 @@
     }
 
     /**
-     * Custom <tt>==</tt> operator.
-     * Must return {@link Scriptable#NOT_FOUND} if this object does not
-     * have custom equality operator for the given value,
-     * <tt>Boolean.TRUE</tt> if this object is equivalent to <tt>value</tt>,
-     * <tt>Boolean.FALSE</tt> if this object is not equivalent to
-     * <tt>value</tt>.
-     * <p>
-     * The default implementation returns Boolean.TRUE
-     * if <tt>this == value</tt> or {@link Scriptable#NOT_FOUND} otherwise.
-     * It indicates that by default custom equality is available only if
-     * <tt>value</tt> is <tt>this</tt> in which case true is returned.
+     * Custom <code>==</code> operator. Must return {@link Scriptable#NOT_FOUND} if this object does
+     * not have custom equality operator for the given value, <code>Boolean.TRUE</code> if this
+     * object is equivalent to <code>value</code>, <code>Boolean.FALSE</code> if this object is not
+     * equivalent to <code>value</code>.
+     *
+     * <p>The default implementation returns Boolean.TRUE if <code>this == value</code> or {@link
+     * Scriptable#NOT_FOUND} otherwise. It indicates that by default custom equality is available
+     * only if <code>value</code> is <code>this</code> in which case true is returned.
      */
-    protected Object equivalentValues(Object value)
-    {
+    protected Object equivalentValues(Object value) {
         return (this == value) ? Boolean.TRUE : Scriptable.NOT_FOUND;
     }
 
     /**
      * Defines JavaScript objects from a Java class that implements Scriptable.
      *
-     * If the given class has a method
+     * <p>If the given class has a method
+     *
      * <pre>
      * static void init(Context cx, Scriptable scope, boolean sealed);</pre>
      *
      * or its compatibility form
+     *
      * <pre>
      * static void init(Scriptable scope);</pre>
      *
-     * then it is invoked and no further initialization is done.<p>
+     * then it is invoked and no further initialization is done.
+     *
+     * <p>However, if no such a method is found, then the class's constructors and methods are used
+     * to initialize a class in the following manner.
+     *
+     * <p>First, the zero-parameter constructor of the class is called to create the prototype. If
+     * no such constructor exists, a {@link EvaluatorException} is thrown.
      *
-     * However, if no such a method is found, then the class's constructors and
-     * methods are used to initialize a class in the following manner.<p>
+     * <p>Next, all methods are scanned for special prefixes that indicate that they have special
+     * meaning for defining JavaScript objects. These special prefixes are
      *
-     * First, the zero-parameter constructor of the class is called to
-     * create the prototype. If no such constructor exists,
-     * a {@link EvaluatorException} is thrown. <p>
-     *
-     * Next, all methods are scanned for special prefixes that indicate that they
-     * have special meaning for defining JavaScript objects.
-     * These special prefixes are
      * <ul>
-     * <li><code>jsFunction_</code> for a JavaScript function
-     * <li><code>jsStaticFunction_</code> for a JavaScript function that
-     *           is a property of the constructor
-     * <li><code>jsGet_</code> for a getter of a JavaScript property
-     * <li><code>jsSet_</code> for a setter of a JavaScript property
-     * <li><code>jsConstructor</code> for a JavaScript function that
-     *           is the constructor
-     * </ul><p>
-     *
-     * If the method's name begins with "jsFunction_", a JavaScript function
-     * is created with a name formed from the rest of the Java method name
-     * following "jsFunction_". So a Java method named "jsFunction_foo" will
-     * define a JavaScript method "foo". Calling this JavaScript function
-     * will cause the Java method to be called. The parameters of the method
-     * must be of number and types as defined by the FunctionObject class.
-     * The JavaScript function is then added as a property
-     * of the prototype. <p>
-     *
-     * If the method's name begins with "jsStaticFunction_", it is handled
-     * similarly except that the resulting JavaScript function is added as a
-     * property of the constructor object. The Java method must be static.
-     *
-     * If the method's name begins with "jsGet_" or "jsSet_", the method is
-     * considered to define a property. Accesses to the defined property
-     * will result in calls to these getter and setter methods. If no
-     * setter is defined, the property is defined as READONLY.<p>
-     *
-     * If the method's name is "jsConstructor", the method is
-     * considered to define the body of the constructor. Only one
-     * method of this name may be defined. You may use the varargs forms
-     * for constructors documented in {@link FunctionObject#FunctionObject(String, Member, Scriptable)}
-     *
-     * If no method is found that can serve as constructor, a Java
-     * constructor will be selected to serve as the JavaScript
-     * constructor in the following manner. If the class has only one
-     * Java constructor, that constructor is used to define
-     * the JavaScript constructor. If the the class has two constructors,
-     * one must be the zero-argument constructor (otherwise an
-     * {@link EvaluatorException} would have already been thrown
-     * when the prototype was to be created). In this case
-     * the Java constructor with one or more parameters will be used
-     * to define the JavaScript constructor. If the class has three
-     * or more constructors, an {@link EvaluatorException}
-     * will be thrown.<p>
+     *   <li><code>jsFunction_</code> for a JavaScript function
+     *   <li><code>jsStaticFunction_</code> for a JavaScript function that is a property of the
+     *       constructor
+     *   <li><code>jsGet_</code> for a getter of a JavaScript property
+     *   <li><code>jsSet_</code> for a setter of a JavaScript property
+     *   <li><code>jsConstructor</code> for a JavaScript function that is the constructor
+     * </ul>
+     *
+     * <p>If the method's name begins with "jsFunction_", a JavaScript function is created with a
+     * name formed from the rest of the Java method name following "jsFunction_". So a Java method
+     * named "jsFunction_foo" will define a JavaScript method "foo". Calling this JavaScript
+     * function will cause the Java method to be called. The parameters of the method must be of
+     * number and types as defined by the FunctionObject class. The JavaScript function is then
+     * added as a property of the prototype.
+     *
+     * <p>If the method's name begins with "jsStaticFunction_", it is handled similarly except that
+     * the resulting JavaScript function is added as a property of the constructor object. The Java
+     * method must be static.
+     *
+     * <p>If the method's name begins with "jsGet_" or "jsSet_", the method is considered to define
+     * a property. Accesses to the defined property will result in calls to these getter and setter
+     * methods. If no setter is defined, the property is defined as READONLY.
+     *
+     * <p>If the method's name is "jsConstructor", the method is considered to define the body of
+     * the constructor. Only one method of this name may be defined. You may use the varargs forms
+     * for constructors documented in {@link FunctionObject#FunctionObject(String, Member,
+     * Scriptable)}
+     *
+     * <p>If no method is found that can serve as constructor, a Java constructor will be selected
+     * to serve as the JavaScript constructor in the following manner. If the class has only one
+     * Java constructor, that constructor is used to define the JavaScript constructor. If the the
+     * class has two constructors, one must be the zero-argument constructor (otherwise an {@link
+     * EvaluatorException} would have already been thrown when the prototype was to be created). In
+     * this case the Java constructor with one or more parameters will be used to define the
+     * JavaScript constructor. If the class has three or more constructors, an {@link
+     * EvaluatorException} will be thrown.
+     *
+     * <p>Finally, if there is a method
      *
-     * Finally, if there is a method
      * <pre>
      * static void finishInit(Scriptable scope, FunctionObject constructor,
      *                        Scriptable prototype)</pre>
      *
-     * it will be called to finish any initialization. The <code>scope</code>
-     * argument will be passed, along with the newly created constructor and
-     * the newly created prototype.<p>
+     * it will be called to finish any initialization. The <code>scope</code> argument will be
+     * passed, along with the newly created constructor and the newly created prototype.
+     *
+     * <p>
      *
      * @param scope The scope in which to define the constructor.
-     * @param clazz The Java class to use to define the JavaScript objects
-     *              and properties.
-     * @exception IllegalAccessException if access is not available
-     *            to a reflected class member
-     * @exception InstantiationException if unable to instantiate
-     *            the named class
-     * @exception InvocationTargetException if an exception is thrown
-     *            during execution of methods of the named class
+     * @param clazz The Java class to use to define the JavaScript objects and properties.
+     * @exception IllegalAccessException if access is not available to a reflected class member
+     * @exception InstantiationException if unable to instantiate the named class
+     * @exception InvocationTargetException if an exception is thrown during execution of methods of
+     *     the named class
      * @see org.mozilla.javascript.Function
      * @see org.mozilla.javascript.FunctionObject
      * @see org.mozilla.javascript.ScriptableObject#READONLY
-     * @see org.mozilla.javascript.ScriptableObject
-     *      #defineProperty(String, Class, int)
+     * @see org.mozilla.javascript.ScriptableObject #defineProperty(String, Class, int)
      */
-    public static <T extends Scriptable> void defineClass(
-            Scriptable scope, Class<T> clazz)
-        throws IllegalAccessException, InstantiationException,
-               InvocationTargetException
-    {
+    public static <T extends Scriptable> void defineClass(Scriptable scope, Class<T> clazz)
+            throws IllegalAccessException, InstantiationException, InvocationTargetException {
         defineClass(scope, clazz, false, false);
     }
 
     /**
-     * Defines JavaScript objects from a Java class, optionally
-     * allowing sealing.
+     * Defines JavaScript objects from a Java class, optionally allowing sealing.
      *
-     * Similar to <code>defineClass(Scriptable scope, Class clazz)</code>
-     * except that sealing is allowed. An object that is sealed cannot have
-     * properties added or removed. Note that sealing is not allowed in
-     * the current ECMA/ISO language specification, but is likely for
-     * the next version.
+     * <p>Similar to <code>defineClass(Scriptable scope, Class clazz)</code> except that sealing is
+     * allowed. An object that is sealed cannot have properties added or removed. Note that sealing
+     * is not allowed in the current ECMA/ISO language specification, but is likely for the next
+     * version.
      *
      * @param scope The scope in which to define the constructor.
-     * @param clazz The Java class to use to define the JavaScript objects
-     *              and properties. The class must implement Scriptable.
-     * @param sealed Whether or not to create sealed standard objects that
-     *               cannot be modified.
-     * @exception IllegalAccessException if access is not available
-     *            to a reflected class member
-     * @exception InstantiationException if unable to instantiate
-     *            the named class
-     * @exception InvocationTargetException if an exception is thrown
-     *            during execution of methods of the named class
+     * @param clazz The Java class to use to define the JavaScript objects and properties. The class
+     *     must implement Scriptable.
+     * @param sealed Whether or not to create sealed standard objects that cannot be modified.
+     * @exception IllegalAccessException if access is not available to a reflected class member
+     * @exception InstantiationException if unable to instantiate the named class
+     * @exception InvocationTargetException if an exception is thrown during execution of methods of
+     *     the named class
      * @since 1.4R3
      */
     public static <T extends Scriptable> void defineClass(
             Scriptable scope, Class<T> clazz, boolean sealed)
-        throws IllegalAccessException, InstantiationException,
-               InvocationTargetException
-    {
+            throws IllegalAccessException, InstantiationException, InvocationTargetException {
         defineClass(scope, clazz, sealed, false);
     }
 
     /**
-     * Defines JavaScript objects from a Java class, optionally
-     * allowing sealing and mapping of Java inheritance to JavaScript
-     * prototype-based inheritance.
-     *
-     * Similar to <code>defineClass(Scriptable scope, Class clazz)</code>
-     * except that sealing and inheritance mapping are allowed. An object
-     * that is sealed cannot have properties added or removed. Note that
-     * sealing is not allowed in the current ECMA/ISO language specification,
-     * but is likely for the next version.
+     * Defines JavaScript objects from a Java class, optionally allowing sealing and mapping of Java
+     * inheritance to JavaScript prototype-based inheritance.
+     *
+     * <p>Similar to <code>defineClass(Scriptable scope, Class clazz)</code> except that sealing and
+     * inheritance mapping are allowed. An object that is sealed cannot have properties added or
+     * removed. Note that sealing is not allowed in the current ECMA/ISO language specification, but
+     * is likely for the next version.
      *
      * @param scope The scope in which to define the constructor.
-     * @param clazz The Java class to use to define the JavaScript objects
-     *              and properties. The class must implement Scriptable.
-     * @param sealed Whether or not to create sealed standard objects that
-     *               cannot be modified.
-     * @param mapInheritance Whether or not to map Java inheritance to
-     *                       JavaScript prototype-based inheritance.
+     * @param clazz The Java class to use to define the JavaScript objects and properties. The class
+     *     must implement Scriptable.
+     * @param sealed Whether or not to create sealed standard objects that cannot be modified.
+     * @param mapInheritance Whether or not to map Java inheritance to JavaScript prototype-based
+     *     inheritance.
      * @return the class name for the prototype of the specified class
-     * @exception IllegalAccessException if access is not available
-     *            to a reflected class member
-     * @exception InstantiationException if unable to instantiate
-     *            the named class
-     * @exception InvocationTargetException if an exception is thrown
-     *            during execution of methods of the named class
+     * @exception IllegalAccessException if access is not available to a reflected class member
+     * @exception InstantiationException if unable to instantiate the named class
+     * @exception InvocationTargetException if an exception is thrown during execution of methods of
+     *     the named class
      * @since 1.6R2
      */
     public static <T extends Scriptable> String defineClass(
-            Scriptable scope, Class<T> clazz, boolean sealed,
-            boolean mapInheritance)
-        throws IllegalAccessException, InstantiationException,
-               InvocationTargetException
-    {
-        BaseFunction ctor = buildClassCtor(scope, clazz, sealed,
-                                           mapInheritance);
-        if (ctor == null)
-            return null;
+            Scriptable scope, Class<T> clazz, boolean sealed, boolean mapInheritance)
+            throws IllegalAccessException, InstantiationException, InvocationTargetException {
+        BaseFunction ctor = buildClassCtor(scope, clazz, sealed, mapInheritance);
+        if (ctor == null) return null;
         String name = ctor.getClassPrototype().getClassName();
         defineProperty(scope, name, ctor, ScriptableObject.DONTENUM);
         return name;
     }
 
     static <T extends Scriptable> BaseFunction buildClassCtor(
-            Scriptable scope, Class<T> clazz,
-            boolean sealed,
-            boolean mapInheritance)
-        throws IllegalAccessException, InstantiationException,
-               InvocationTargetException
-    {
+            Scriptable scope, Class<T> clazz, boolean sealed, boolean mapInheritance)
+            throws IllegalAccessException, InstantiationException, InvocationTargetException {
         Method[] methods = FunctionObject.getMethodList(clazz);
-        for (int i=0; i < methods.length; i++) {
+        for (int i = 0; i < methods.length; i++) {
             Method method = methods[i];
-            if (!method.getName().equals("init"))
-                continue;
+            if (!method.getName().equals("init")) continue;
             Class<?>[] parmTypes = method.getParameterTypes();
-            if (parmTypes.length == 3 &&
-                parmTypes[0] == ScriptRuntime.ContextClass &&
-                parmTypes[1] == ScriptRuntime.ScriptableClass &&
-                parmTypes[2] == Boolean.TYPE &&
-                Modifier.isStatic(method.getModifiers()))
-            {
-                Object args[] = { Context.getContext(), scope,
-                                  sealed ? Boolean.TRUE : Boolean.FALSE };
+            if (parmTypes.length == 3
+                    && parmTypes[0] == ScriptRuntime.ContextClass
+                    && parmTypes[1] == ScriptRuntime.ScriptableClass
+                    && parmTypes[2] == Boolean.TYPE
+                    && Modifier.isStatic(method.getModifiers())) {
+                Object args[] = {
+                    Context.getContext(), scope, sealed ? Boolean.TRUE : Boolean.FALSE
+                };
                 method.invoke(null, args);
                 return null;
             }
-            if (parmTypes.length == 1 &&
-                parmTypes[0] == ScriptRuntime.ScriptableClass &&
-                Modifier.isStatic(method.getModifiers()))
-            {
-                Object args[] = { scope };
+            if (parmTypes.length == 1
+                    && parmTypes[0] == ScriptRuntime.ScriptableClass
+                    && Modifier.isStatic(method.getModifiers())) {
+                Object args[] = {scope};
                 method.invoke(null, args);
                 return null;
             }
-
         }
 
         // If we got here, there isn't an "init" method with the right
@@ -1394,15 +1033,14 @@
 
         Constructor<?>[] ctors = clazz.getConstructors();
         Constructor<?> protoCtor = null;
-        for (int i=0; i < ctors.length; i++) {
+        for (int i = 0; i < ctors.length; i++) {
             if (ctors[i].getParameterTypes().length == 0) {
                 protoCtor = ctors[i];
                 break;
             }
         }
         if (protoCtor == null) {
-            throw Context.reportRuntimeError1(
-                      "msg.zero.arg.ctor", clazz.getName());
+            throw Context.reportRuntimeErrorById("msg.zero.arg.ctor", clazz.getName());
         }
 
         Scriptable proto = (Scriptable) protoCtor.newInstance(ScriptRuntime.emptyArgs);
@@ -1411,9 +1049,9 @@
         // check for possible redefinition
         Object existing = getProperty(getTopLevelScope(scope), className);
         if (existing instanceof BaseFunction) {
-            Object existingProto = ((BaseFunction)existing).getPrototypeProperty();
+            Object existingProto = ((BaseFunction) existing).getPrototypeProperty();
             if (existingProto != null && clazz.equals(existingProto.getClass())) {
-                return (BaseFunction)existing;
+                return (BaseFunction) existing;
             }
         }
 
@@ -1422,13 +1060,12 @@
         Scriptable superProto = null;
         if (mapInheritance) {
             Class<? super T> superClass = clazz.getSuperclass();
-            if (ScriptRuntime.ScriptableClass.isAssignableFrom(superClass) &&
-                !Modifier.isAbstract(superClass.getModifiers()))
-            {
-                Class<? extends Scriptable> superScriptable =
-                    extendsScriptable(superClass);
-                String name = ScriptableObject.defineClass(scope,
-                        superScriptable, sealed, mapInheritance);
+            if (ScriptRuntime.ScriptableClass.isAssignableFrom(superClass)
+                    && !Modifier.isAbstract(superClass.getModifiers())) {
+                Class<? extends Scriptable> superScriptable = extendsScriptable(superClass);
+                String name =
+                        ScriptableObject.defineClass(
+                                scope, superScriptable, sealed, mapInheritance);
                 if (name != null) {
                     superProto = ScriptableObject.getClassPrototype(scope, name);
                 }
@@ -1459,27 +1096,22 @@
             if (ctors.length == 1) {
                 ctorMember = ctors[0];
             } else if (ctors.length == 2) {
-                if (ctors[0].getParameterTypes().length == 0)
-                    ctorMember = ctors[1];
-                else if (ctors[1].getParameterTypes().length == 0)
-                    ctorMember = ctors[0];
+                if (ctors[0].getParameterTypes().length == 0) ctorMember = ctors[1];
+                else if (ctors[1].getParameterTypes().length == 0) ctorMember = ctors[0];
             }
             if (ctorMember == null) {
-                throw Context.reportRuntimeError1(
-                          "msg.ctor.multiple.parms", clazz.getName());
+                throw Context.reportRuntimeErrorById("msg.ctor.multiple.parms", clazz.getName());
             }
         }
 
         FunctionObject ctor = new FunctionObject(className, ctorMember, scope);
         if (ctor.isVarArgsMethod()) {
-            throw Context.reportRuntimeError1
-                ("msg.varargs.ctor", ctorMember.getName());
+            throw Context.reportRuntimeErrorById("msg.varargs.ctor", ctorMember.getName());
         }
         ctor.initAsConstructor(scope, proto);
 
         Method finishInit = null;
-        HashSet<String> staticNames = new HashSet<String>(),
-                        instanceNames = new HashSet<String>();
+        HashSet<String> staticNames = new HashSet<String>(), instanceNames = new HashSet<String>();
         for (Method method : methods) {
             if (method == ctorMember) {
                 continue;
@@ -1487,21 +1119,18 @@
             String name = method.getName();
             if (name.equals("finishInit")) {
                 Class<?>[] parmTypes = method.getParameterTypes();
-                if (parmTypes.length == 3 &&
-                    parmTypes[0] == ScriptRuntime.ScriptableClass &&
-                    parmTypes[1] == FunctionObject.class &&
-                    parmTypes[2] == ScriptRuntime.ScriptableClass &&
-                    Modifier.isStatic(method.getModifiers()))
-                {
+                if (parmTypes.length == 3
+                        && parmTypes[0] == ScriptRuntime.ScriptableClass
+                        && parmTypes[1] == FunctionObject.class
+                        && parmTypes[2] == ScriptRuntime.ScriptableClass
+                        && Modifier.isStatic(method.getModifiers())) {
                     finishInit = method;
                     continue;
                 }
             }
             // ignore any compiler generated methods.
-            if (name.indexOf('$') != -1)
-                continue;
-            if (name.equals(ctorName))
-                continue;
+            if (name.indexOf('$') != -1) continue;
+            if (name.equals(ctorName)) continue;
 
             Annotation annotation = null;
             String prefix = null;
@@ -1522,38 +1151,34 @@
                     prefix = staticFunctionPrefix;
                 } else if (name.startsWith(getterPrefix)) {
                     prefix = getterPrefix;
-                } else if (annotation == null) {
+                } else {
                     // note that setterPrefix is among the unhandled names here -
                     // we deal with that when we see the getter
                     continue;
                 }
             }
 
-            boolean isStatic = annotation instanceof JSStaticFunction
-                    || prefix == staticFunctionPrefix;
+            boolean isStatic =
+                    annotation instanceof JSStaticFunction || prefix == staticFunctionPrefix;
             HashSet<String> names = isStatic ? staticNames : instanceNames;
             String propName = getPropertyName(name, prefix, annotation);
             if (names.contains(propName)) {
-                throw Context.reportRuntimeError2("duplicate.defineClass.name",
-                        name, propName);
+                throw Context.reportRuntimeErrorById("duplicate.defineClass.name", name, propName);
             }
             names.add(propName);
             name = propName;
 
             if (annotation instanceof JSGetter || prefix == getterPrefix) {
                 if (!(proto instanceof ScriptableObject)) {
-                    throw Context.reportRuntimeError2(
-                        "msg.extend.scriptable",
-                        proto.getClass().toString(), name);
+                    throw Context.reportRuntimeErrorById(
+                            "msg.extend.scriptable", proto.getClass().toString(), name);
                 }
                 Method setter = findSetterMethod(methods, name, setterPrefix);
-                int attr = ScriptableObject.PERMANENT |
-                           ScriptableObject.DONTENUM  |
-                           (setter != null ? 0
-                                           : ScriptableObject.READONLY);
-                ((ScriptableObject) proto).defineProperty(name, null,
-                                                          method, setter,
-                                                          attr);
+                int attr =
+                        ScriptableObject.PERMANENT
+                                | ScriptableObject.DONTENUM
+                                | (setter != null ? 0 : ScriptableObject.READONLY);
+                ((ScriptableObject) proto).defineProperty(name, null, method, setter, attr);
                 continue;
             }
 
@@ -1564,8 +1189,7 @@
 
             FunctionObject f = new FunctionObject(name, method, proto);
             if (f.isVarArgsConstructor()) {
-                throw Context.reportRuntimeError1
-                    ("msg.varargs.fun", ctorMember.getName());
+                throw Context.reportRuntimeErrorById("msg.varargs.fun", ctorMember.getName());
             }
             defineProperty(isStatic ? ctor : proto, name, f, DONTENUM);
             if (sealed) {
@@ -1575,7 +1199,7 @@
 
         // Call user code to complete initialization if necessary.
         if (finishInit != null) {
-            Object[] finishArgs = { scope, ctor, proto };
+            Object[] finishArgs = {scope, ctor, proto};
             finishInit.invoke(null, finishArgs);
         }
 
@@ -1590,8 +1214,8 @@
         return ctor;
     }
 
-    private static Member findAnnotatedMember(AccessibleObject[] members,
-                                              Class<? extends Annotation> annotation) {
+    private static Member findAnnotatedMember(
+            AccessibleObject[] members, Class<? extends Annotation> annotation) {
         for (AccessibleObject member : members) {
             if (member.isAnnotationPresent(annotation)) {
                 return (Member) member;
@@ -1600,17 +1224,14 @@
         return null;
     }
 
-    private static Method findSetterMethod(Method[] methods,
-                                           String name,
-                                           String prefix) {
-        String newStyleName = "set"
-                + Character.toUpperCase(name.charAt(0))
-                + name.substring(1);
+    private static Method findSetterMethod(Method[] methods, String name, String prefix) {
+        String newStyleName = "set" + Character.toUpperCase(name.charAt(0)) + name.substring(1);
         for (Method method : methods) {
             JSSetter annotation = method.getAnnotation(JSSetter.class);
             if (annotation != null) {
-                if (name.equals(annotation.value()) ||
-                        ("".equals(annotation.value()) && newStyleName.equals(method.getName()))) {
+                if (name.equals(annotation.value())
+                        || ("".equals(annotation.value())
+                                && newStyleName.equals(method.getName()))) {
                     return method;
                 }
             }
@@ -1624,9 +1245,7 @@
         return null;
     }
 
-    private static String getPropertyName(String methodName,
-                                          String prefix,
-                                          Annotation annotation) {
+    private static String getPropertyName(String methodName, String prefix, Annotation annotation) {
         if (prefix != null) {
             return methodName.substring(prefix.length());
         }
@@ -1639,9 +1258,10 @@
                     if (Character.isUpperCase(propName.charAt(0))) {
                         if (propName.length() == 1) {
                             propName = propName.toLowerCase();
-                        } else if (!Character.isUpperCase(propName.charAt(1))){
-                            propName = Character.toLowerCase(propName.charAt(0))
-                                    + propName.substring(1);
+                        } else if (!Character.isUpperCase(propName.charAt(1))) {
+                            propName =
+                                    Character.toLowerCase(propName.charAt(0))
+                                            + propName.substring(1);
                         }
                     }
                 }
@@ -1658,26 +1278,22 @@
     }
 
     @SuppressWarnings({"unchecked"})
-    private static <T extends Scriptable> Class<T> extendsScriptable(Class<?> c)
-    {
-        if (ScriptRuntime.ScriptableClass.isAssignableFrom(c))
-            return (Class<T>) c;
+    private static <T extends Scriptable> Class<T> extendsScriptable(Class<?> c) {
+        if (ScriptRuntime.ScriptableClass.isAssignableFrom(c)) return (Class<T>) c;
         return null;
     }
 
     /**
      * Define a JavaScript property.
      *
-     * Creates the property with an initial value and sets its attributes.
+     * <p>Creates the property with an initial value and sets its attributes.
      *
      * @param propertyName the name of the property to define.
      * @param value the initial value of the property
      * @param attributes the attributes of the JavaScript property
      * @see org.mozilla.javascript.Scriptable#put(String, Scriptable, Object)
      */
-    public void defineProperty(String propertyName, Object value,
-                               int attributes)
-    {
+    public void defineProperty(String propertyName, Object value, int attributes) {
         checkNotSealed(propertyName, 0);
         put(propertyName, this, value);
         setAttributes(propertyName, attributes);
@@ -1685,70 +1301,70 @@
 
     /**
      * A version of defineProperty that uses a Symbol key.
+     *
+     * @param key symbol of the property to define.
+     * @param value the initial value of the property
+     * @param attributes the attributes of the JavaScript property
      */
-    public void defineProperty(Symbol key, Object value,
-                               int attributes)
-    {
+    public void defineProperty(Symbol key, Object value, int attributes) {
         checkNotSealed(key, 0);
         put(key, this, value);
         setAttributes(key, attributes);
     }
 
     /**
-     * Utility method to add properties to arbitrary Scriptable object.
-     * If destination is instance of ScriptableObject, calls
-     * defineProperty there, otherwise calls put in destination
-     * ignoring attributes
-     */
-    public static void defineProperty(Scriptable destination,
-                                      String propertyName, Object value,
-                                      int attributes)
-    {
+     * Utility method to add properties to arbitrary Scriptable object. If destination is instance
+     * of ScriptableObject, calls defineProperty there, otherwise calls put in destination ignoring
+     * attributes
+     *
+     * @param destination ScriptableObject to define the property on
+     * @param propertyName the name of the property to define.
+     * @param value the initial value of the property
+     * @param attributes the attributes of the JavaScript property
+     */
+    public static void defineProperty(
+            Scriptable destination, String propertyName, Object value, int attributes) {
         if (!(destination instanceof ScriptableObject)) {
             destination.put(propertyName, destination, value);
             return;
         }
-        ScriptableObject so = (ScriptableObject)destination;
+        ScriptableObject so = (ScriptableObject) destination;
         so.defineProperty(propertyName, value, attributes);
     }
 
     /**
-     * Utility method to add properties to arbitrary Scriptable object.
-     * If destination is instance of ScriptableObject, calls
-     * defineProperty there, otherwise calls put in destination
-     * ignoring attributes
-     */
-    public static void defineConstProperty(Scriptable destination,
-                                           String propertyName)
-    {
+     * Utility method to add properties to arbitrary Scriptable object. If destination is instance
+     * of ScriptableObject, calls defineProperty there, otherwise calls put in destination ignoring
+     * attributes
+     *
+     * @param destination ScriptableObject to define the property on
+     * @param propertyName the name of the property to define.
+     */
+    public static void defineConstProperty(Scriptable destination, String propertyName) {
         if (destination instanceof ConstProperties) {
-            ConstProperties cp = (ConstProperties)destination;
+            ConstProperties cp = (ConstProperties) destination;
             cp.defineConst(propertyName, destination);
-        } else
-            defineProperty(destination, propertyName, Undefined.instance, CONST);
+        } else defineProperty(destination, propertyName, Undefined.instance, CONST);
     }
 
     /**
      * Define a JavaScript property with getter and setter side effects.
      *
-     * If the setter is not found, the attribute READONLY is added to
-     * the given attributes. <p>
+     * <p>If the setter is not found, the attribute READONLY is added to the given attributes.
+     *
+     * <p>The getter must be a method with zero parameters, and the setter, if found, must be a
+     * method with one parameter.
      *
-     * The getter must be a method with zero parameters, and the setter, if
-     * found, must be a method with one parameter.<p>
+     * <p>
      *
-     * @param propertyName the name of the property to define. This name
-     *                    also affects the name of the setter and getter
-     *                    to search for. If the propertyId is "foo", then
-     *                    <code>clazz</code> will be searched for "getFoo"
-     *                    and "setFoo" methods.
+     * @param propertyName the name of the property to define. This name also affects the name of
+     *     the setter and getter to search for. If the propertyId is "foo", then <code>clazz</code>
+     *     will be searched for "getFoo" and "setFoo" methods.
      * @param clazz the Java class to search for the getter and setter
      * @param attributes the attributes of the JavaScript property
      * @see org.mozilla.javascript.Scriptable#put(String, Scriptable, Object)
      */
-    public void defineProperty(String propertyName, Class<?> clazz,
-                               int attributes)
-    {
+    public void defineProperty(String propertyName, Class<?> clazz, int attributes) {
         int length = propertyName.length();
         if (length == 0) throw new IllegalArgumentException();
         char[] buf = new char[3 + length];
@@ -1764,56 +1380,53 @@
         Method[] methods = FunctionObject.getMethodList(clazz);
         Method getter = FunctionObject.findSingleMethod(methods, getterName);
         Method setter = FunctionObject.findSingleMethod(methods, setterName);
-        if (setter == null)
-            attributes |= ScriptableObject.READONLY;
-        defineProperty(propertyName, null, getter,
-                       setter == null ? null : setter, attributes);
+        if (setter == null) attributes |= ScriptableObject.READONLY;
+        defineProperty(propertyName, null, getter, setter == null ? null : setter, attributes);
     }
 
     /**
      * Define a JavaScript property.
      *
-     * Use this method only if you wish to define getters and setters for
-     * a given property in a ScriptableObject. To create a property without
-     * special getter or setter side effects, use
+     * <p>Use this method only if you wish to define getters and setters for a given property in a
+     * ScriptableObject. To create a property without special getter or setter side effects, use
      * <code>defineProperty(String,int)</code>.
      *
-     * If <code>setter</code> is null, the attribute READONLY is added to
-     * the given attributes.<p>
+     * <p>If <code>setter</code> is null, the attribute READONLY is added to the given attributes.
+     *
+     * <p>Several forms of getters or setters are allowed. In all cases the type of the value
+     * parameter can be any one of the following types: Object, String, boolean, Scriptable, byte,
+     * short, int, long, float, or double. The runtime will perform appropriate conversions based
+     * upon the type of the parameter (see description in FunctionObject). The first forms are
+     * nonstatic methods of the class referred to by 'this':
      *
-     * Several forms of getters or setters are allowed. In all cases the
-     * type of the value parameter can be any one of the following types:
-     * Object, String, boolean, Scriptable, byte, short, int, long, float,
-     * or double. The runtime will perform appropriate conversions based
-     * upon the type of the parameter (see description in FunctionObject).
-     * The first forms are nonstatic methods of the class referred to
-     * by 'this':
      * <pre>
      * Object getFoo();
      * void setFoo(SomeType value);</pre>
-     * Next are static methods that may be of any class; the object whose
-     * property is being accessed is passed in as an extra argument:
+     *
+     * Next are static methods that may be of any class; the object whose property is being accessed
+     * is passed in as an extra argument:
+     *
      * <pre>
      * static Object getFoo(Scriptable obj);
      * static void setFoo(Scriptable obj, SomeType value);</pre>
-     * Finally, it is possible to delegate to another object entirely using
-     * the <code>delegateTo</code> parameter. In this case the methods are
-     * nonstatic methods of the class delegated to, and the object whose
-     * property is being accessed is passed in as an extra argument:
+     *
+     * Finally, it is possible to delegate to another object entirely using the <code>delegateTo
+     * </code> parameter. In this case the methods are nonstatic methods of the class delegated to,
+     * and the object whose property is being accessed is passed in as an extra argument:
+     *
      * <pre>
      * Object getFoo(Scriptable obj);
      * void setFoo(Scriptable obj, SomeType value);</pre>
      *
      * @param propertyName the name of the property to define.
-     * @param delegateTo an object to call the getter and setter methods on,
-     *                   or null, depending on the form used above.
+     * @param delegateTo an object to call the getter and setter methods on, or null, depending on
+     *     the form used above.
      * @param getter the method to invoke to get the value of the property
      * @param setter the method to invoke to set the value of the property
      * @param attributes the attributes of the JavaScript property
      */
-    public void defineProperty(String propertyName, Object delegateTo,
-                               Method getter, Method setter, int attributes)
-    {
+    public void defineProperty(
+            String propertyName, Object delegateTo, Method getter, Method setter, int attributes) {
         MemberBox getterBox = null;
         if (getter != null) {
             getterBox = new MemberBox(getter);
@@ -1838,9 +1451,8 @@
             } else if (parmTypes.length == 1) {
                 Object argType = parmTypes[0];
                 // Allow ScriptableObject for compatibility
-                if (!(argType == ScriptRuntime.ScriptableClass ||
-                      argType == ScriptRuntime.ScriptableObjectClass))
-                {
+                if (!(argType == ScriptRuntime.ScriptableClass
+                        || argType == ScriptRuntime.ScriptableObjectClass)) {
                     errorId = "msg.bad.getter.parms";
                 } else if (!delegatedForm) {
                     errorId = "msg.bad.getter.parms";
@@ -1849,15 +1461,14 @@
                 errorId = "msg.bad.getter.parms";
             }
             if (errorId != null) {
-                throw Context.reportRuntimeError1(errorId, getter.toString());
+                throw Context.reportRuntimeErrorById(errorId, getter.toString());
             }
         }
 
         MemberBox setterBox = null;
         if (setter != null) {
             if (setter.getReturnType() != Void.TYPE)
-                throw Context.reportRuntimeError1("msg.setter.return",
-                                                  setter.toString());
+                throw Context.reportRuntimeErrorById("msg.setter.return", setter.toString());
 
             setterBox = new MemberBox(setter);
 
@@ -1881,9 +1492,8 @@
             } else if (parmTypes.length == 2) {
                 Object argType = parmTypes[0];
                 // Allow ScriptableObject for compatibility
-                if (!(argType == ScriptRuntime.ScriptableClass ||
-                      argType == ScriptRuntime.ScriptableObjectClass))
-                {
+                if (!(argType == ScriptRuntime.ScriptableClass
+                        || argType == ScriptRuntime.ScriptableObjectClass)) {
                     errorId = "msg.setter2.parms";
                 } else if (!delegatedForm) {
                     errorId = "msg.setter1.parms";
@@ -1892,15 +1502,26 @@
                 errorId = "msg.setter.parms";
             }
             if (errorId != null) {
-                throw Context.reportRuntimeError1(errorId, setter.toString());
+                throw Context.reportRuntimeErrorById(errorId, setter.toString());
             }
         }
 
-        GetterSlot gslot = (GetterSlot)getSlot(propertyName, 0,
-                                               SLOT_MODIFY_GETTER_SETTER);
-        gslot.setAttributes(attributes);
-        gslot.getter = getterBox;
-        gslot.setter = setterBox;
+        Slot slot = slotMap.modify(propertyName, 0, 0);
+        AccessorSlot aSlot;
+        if (slot instanceof AccessorSlot) {
+            aSlot = (AccessorSlot) slot;
+        } else {
+            aSlot = new AccessorSlot(slot);
+            slotMap.replace(slot, aSlot);
+        }
+
+        aSlot.setAttributes(attributes);
+        if (getterBox != null) {
+            aSlot.getter = new AccessorSlot.MemberBoxGetter(getterBox);
+        }
+        if (setterBox != null) {
+            aSlot.setter = new AccessorSlot.MemberBoxSetter(setterBox);
+        }
     }
 
     /**
@@ -1938,60 +1559,74 @@
     /**
      * Defines a property on an object.
      *
-     * Based on [[DefineOwnProperty]] from 8.12.10 of the spec.
+     * <p>Based on [[DefineOwnProperty]] from 8.12.10 of the spec.
      *
      * @param cx the current Context
      * @param id the name/index of the property
      * @param desc the new property descriptor, as described in 8.6.1
      * @param checkValid whether to perform validity checks
      */
-    protected void defineOwnProperty(Context cx, Object id, ScriptableObject desc,
-                                     boolean checkValid) {
+    protected void defineOwnProperty(
+            Context cx, Object id, ScriptableObject desc, boolean checkValid) {
 
-        Slot slot = getSlot(cx, id, SLOT_QUERY);
+        Object key = null;
+        int index = 0;
+        if (id instanceof Symbol) {
+            key = id;
+        } else {
+            StringIdOrIndex s = ScriptRuntime.toStringIdOrIndex(cx, id);
+            if (s.stringId == null) {
+                index = s.index;
+            } else {
+                key = s.stringId;
+            }
+        }
+
+        Slot slot = slotMap.query(key, index);
         boolean isNew = slot == null;
 
         if (checkValid) {
-            ScriptableObject current = slot == null ?
-                    null : slot.getPropertyDescriptor(cx, this);
+            ScriptableObject current = slot == null ? null : slot.getPropertyDescriptor(cx, this);
             checkPropertyChange(id, current, desc);
         }
 
         boolean isAccessor = isAccessorDescriptor(desc);
         final int attributes;
 
-        if (slot == null) { // new slot
-            slot = getSlot(cx, id, isAccessor ? SLOT_MODIFY_GETTER_SETTER : SLOT_MODIFY);
-            attributes = applyDescriptorToAttributeBitset(DONTENUM|READONLY|PERMANENT, desc);
+        if (slot == null) {
+            slot = slotMap.modify(key, index, 0);
+            attributes = applyDescriptorToAttributeBitset(DONTENUM | READONLY | PERMANENT, desc);
         } else {
             attributes = applyDescriptorToAttributeBitset(slot.getAttributes(), desc);
         }
 
-        slot = unwrapSlot(slot);
-
         if (isAccessor) {
-            if ( !(slot instanceof GetterSlot) ) {
-                slot = getSlot(cx, id, SLOT_MODIFY_GETTER_SETTER);
-            }
+            AccessorSlot fslot;
 
-            GetterSlot gslot = (GetterSlot) slot;
+            if (slot instanceof AccessorSlot) {
+                fslot = (AccessorSlot) slot;
+            } else {
+                fslot = new AccessorSlot(slot);
+                slotMap.replace(slot, fslot);
+            }
 
             Object getter = getProperty(desc, "get");
             if (getter != NOT_FOUND) {
-                gslot.getter = getter;
+                fslot.getter = new AccessorSlot.FunctionGetter(getter);
             }
             Object setter = getProperty(desc, "set");
             if (setter != NOT_FOUND) {
-                gslot.setter = setter;
+                fslot.setter = new AccessorSlot.FunctionSetter(setter);
             }
 
-            gslot.value = Undefined.instance;
-            gslot.setAttributes(attributes);
+            fslot.value = Undefined.instance;
+            fslot.setAttributes(attributes);
         } else {
-            if (slot instanceof GetterSlot && isDataDescriptor(desc)) {
-                slot = getSlot(cx, id, SLOT_CONVERT_ACCESSOR_TO_DATA);
+            if (!slot.isValueSlot() && isDataDescriptor(desc)) {
+                Slot newSlot = new Slot(slot);
+                slotMap.replace(slot, newSlot);
+                slot = newSlot;
             }
-
             Object value = getProperty(desc, "value");
             if (value != NOT_FOUND) {
                 slot.value = value;
@@ -2002,34 +1637,62 @@
         }
     }
 
+    /**
+     * Define a property on this object that is implemented using lambda functions. If a property
+     * with the same name already exists, then it will be replaced. This property will appear to the
+     * JavaScript user exactly like any other property -- unlike Function properties and those based
+     * on reflection, the property descriptor will only reflect the value as defined by this
+     * function.
+     *
+     * @param name the name of the function
+     * @param getter a function that returns the value of the property. If null, then the previous
+     *     version of the value will be returned.
+     * @param setter a function that sets the value of the property. If null, then the value will be
+     *     set directly and may not be retrieved by the getter.
+     * @param attributes the attributes to set on the property
+     */
+    public void defineProperty(
+            String name, Supplier<Object> getter, Consumer<Object> setter, int attributes) {
+        Slot slot = slotMap.modify(name, 0, attributes);
+
+        LambdaSlot lSlot;
+        if (slot instanceof LambdaSlot) {
+            lSlot = (LambdaSlot) slot;
+        } else {
+            lSlot = new LambdaSlot(slot);
+            slotMap.replace(slot, lSlot);
+        }
+
+        lSlot.getter = getter;
+        lSlot.setter = setter;
+        setAttributes(name, attributes);
+    }
+
     protected void checkPropertyDefinition(ScriptableObject desc) {
         Object getter = getProperty(desc, "get");
-        if (getter != NOT_FOUND && getter != Undefined.instance
-                && !(getter instanceof Callable)) {
+        if (getter != NOT_FOUND && getter != Undefined.instance && !(getter instanceof Callable)) {
             throw ScriptRuntime.notFunctionError(getter);
         }
         Object setter = getProperty(desc, "set");
-        if (setter != NOT_FOUND && setter != Undefined.instance
-                && !(setter instanceof Callable)) {
+        if (setter != NOT_FOUND && setter != Undefined.instance && !(setter instanceof Callable)) {
             throw ScriptRuntime.notFunctionError(setter);
         }
         if (isDataDescriptor(desc) && isAccessorDescriptor(desc)) {
-            throw ScriptRuntime.typeError0("msg.both.data.and.accessor.desc");
+            throw ScriptRuntime.typeErrorById("msg.both.data.and.accessor.desc");
         }
     }
 
-    protected void checkPropertyChange(Object id, ScriptableObject current,
-                                       ScriptableObject desc) {
+    protected void checkPropertyChange(Object id, ScriptableObject current, ScriptableObject desc) {
         if (current == null) { // new property
-            if (!isExtensible()) throw ScriptRuntime.typeError0("msg.not.extensible");
+            if (!isExtensible()) throw ScriptRuntime.typeErrorById("msg.not.extensible");
         } else {
             if (isFalse(current.get("configurable", current))) {
                 if (isTrue(getProperty(desc, "configurable")))
-                    throw ScriptRuntime.typeError1(
-                        "msg.change.configurable.false.to.true", id);
-                if (isTrue(current.get("enumerable", current)) != isTrue(getProperty(desc, "enumerable")))
-                    throw ScriptRuntime.typeError1(
-                        "msg.change.enumerable.with.configurable.false", id);
+                    throw ScriptRuntime.typeErrorById("msg.change.configurable.false.to.true", id);
+                if (isTrue(current.get("enumerable", current))
+                        != isTrue(getProperty(desc, "enumerable")))
+                    throw ScriptRuntime.typeErrorById(
+                            "msg.change.enumerable.with.configurable.false", id);
                 boolean isData = isDataDescriptor(desc);
                 boolean isAccessor = isAccessorDescriptor(desc);
                 if (!isData && !isAccessor) {
@@ -2037,27 +1700,27 @@
                 } else if (isData && isDataDescriptor(current)) {
                     if (isFalse(current.get("writable", current))) {
                         if (isTrue(getProperty(desc, "writable")))
-                            throw ScriptRuntime.typeError1(
-                                "msg.change.writable.false.to.true.with.configurable.false", id);
+                            throw ScriptRuntime.typeErrorById(
+                                    "msg.change.writable.false.to.true.with.configurable.false",
+                                    id);
 
                         if (!sameValue(getProperty(desc, "value"), current.get("value", current)))
-                            throw ScriptRuntime.typeError1(
-                                "msg.change.value.with.writable.false", id);
+                            throw ScriptRuntime.typeErrorById(
+                                    "msg.change.value.with.writable.false", id);
                     }
                 } else if (isAccessor && isAccessorDescriptor(current)) {
                     if (!sameValue(getProperty(desc, "set"), current.get("set", current)))
-                        throw ScriptRuntime.typeError1(
-                            "msg.change.setter.with.configurable.false", id);
+                        throw ScriptRuntime.typeErrorById(
+                                "msg.change.setter.with.configurable.false", id);
 
                     if (!sameValue(getProperty(desc, "get"), current.get("get", current)))
-                        throw ScriptRuntime.typeError1(
-                            "msg.change.getter.with.configurable.false", id);
+                        throw ScriptRuntime.typeErrorById(
+                                "msg.change.getter.with.configurable.false", id);
                 } else {
                     if (isDataDescriptor(current))
-                        throw ScriptRuntime.typeError1(
-                            "msg.change.property.data.to.accessor.with.configurable.false", id);
-                    else
-                        throw ScriptRuntime.typeError1(
+                        throw ScriptRuntime.typeErrorById(
+                                "msg.change.property.data.to.accessor.with.configurable.false", id);
+                    throw ScriptRuntime.typeErrorById(
                             "msg.change.property.accessor.to.data.with.configurable.false", id);
                 }
             }
@@ -2073,8 +1736,8 @@
     }
 
     /**
-     * Implements SameValue as described in ES5 9.12, additionally checking
-     * if new value is defined.
+     * Implements SameValue as described in ES5 9.12, additionally checking if new value is defined.
+     *
      * @param newValue the new value
      * @param currentValue the current value
      * @return true if values are the same as defined by ES5 9.12
@@ -2089,8 +1752,8 @@
         // Special rules for numbers: NaN is considered the same value,
         // while zeroes with different signs are considered different.
         if (currentValue instanceof Number && newValue instanceof Number) {
-            double d1 = ((Number)currentValue).doubleValue();
-            double d2 = ((Number)newValue).doubleValue();
+            double d1 = ((Number) currentValue).doubleValue();
+            double d2 = ((Number) newValue).doubleValue();
             if (Double.isNaN(d1) && Double.isNaN(d2)) {
                 return true;
             }
@@ -2101,25 +1764,29 @@
         return ScriptRuntime.shallowEq(currentValue, newValue);
     }
 
-    protected int applyDescriptorToAttributeBitset(int attributes,
-                                                   ScriptableObject desc)
-    {
+    protected int applyDescriptorToAttributeBitset(int attributes, ScriptableObject desc) {
         Object enumerable = getProperty(desc, "enumerable");
         if (enumerable != NOT_FOUND) {
-            attributes = ScriptRuntime.toBoolean(enumerable)
-                    ? attributes & ~DONTENUM : attributes | DONTENUM;
+            attributes =
+                    ScriptRuntime.toBoolean(enumerable)
+                            ? attributes & ~DONTENUM
+                            : attributes | DONTENUM;
         }
 
         Object writable = getProperty(desc, "writable");
         if (writable != NOT_FOUND) {
-            attributes = ScriptRuntime.toBoolean(writable)
-                    ? attributes & ~READONLY : attributes | READONLY;
+            attributes =
+                    ScriptRuntime.toBoolean(writable)
+                            ? attributes & ~READONLY
+                            : attributes | READONLY;
         }
 
         Object configurable = getProperty(desc, "configurable");
         if (configurable != NOT_FOUND) {
-            attributes = ScriptRuntime.toBoolean(configurable)
-                    ? attributes & ~PERMANENT : attributes | PERMANENT;
+            attributes =
+                    ScriptRuntime.toBoolean(configurable)
+                            ? attributes & ~PERMANENT
+                            : attributes | PERMANENT;
         }
 
         return attributes;
@@ -2127,6 +1794,7 @@
 
     /**
      * Implements IsDataDescriptor as described in ES5 8.10.2
+     *
      * @param desc a property descriptor
      * @return true if this is a data descriptor.
      */
@@ -2136,6 +1804,7 @@
 
     /**
      * Implements IsAccessorDescriptor as described in ES5 8.10.1
+     *
      * @param desc a property descriptor
      * @return true if this is an accessor descriptor.
      */
@@ -2145,6 +1814,7 @@
 
     /**
      * Implements IsGenericDescriptor as described in ES5 8.10.3
+     *
      * @param desc a property descriptor
      * @return true if this is a generic descriptor.
      */
@@ -2153,46 +1823,47 @@
     }
 
     protected static Scriptable ensureScriptable(Object arg) {
-        if ( !(arg instanceof Scriptable) )
-            throw ScriptRuntime.typeError1("msg.arg.not.object", ScriptRuntime.typeof(arg));
+        if (!(arg instanceof Scriptable))
+            throw ScriptRuntime.typeErrorById("msg.arg.not.object", ScriptRuntime.typeof(arg));
         return (Scriptable) arg;
     }
 
     protected static SymbolScriptable ensureSymbolScriptable(Object arg) {
-        if ( !(arg instanceof SymbolScriptable) )
-            throw ScriptRuntime.typeError1("msg.object.not.symbolscriptable", ScriptRuntime.typeof(arg));
+        if (!(arg instanceof SymbolScriptable))
+            throw ScriptRuntime.typeErrorById(
+                    "msg.object.not.symbolscriptable", ScriptRuntime.typeof(arg));
         return (SymbolScriptable) arg;
     }
 
     protected static ScriptableObject ensureScriptableObject(Object arg) {
-        if ( !(arg instanceof ScriptableObject) )
-            throw ScriptRuntime.typeError1("msg.arg.not.object", ScriptRuntime.typeof(arg));
-        return (ScriptableObject) arg;
+        if (arg instanceof ScriptableObject) {
+            return (ScriptableObject) arg;
+        }
+        if (arg instanceof Delegator) {
+            return (ScriptableObject) ((Delegator) arg).getDelegee();
+        }
+        throw ScriptRuntime.typeErrorById("msg.arg.not.object", ScriptRuntime.typeof(arg));
     }
 
     /**
-     * Search for names in a class, adding the resulting methods
-     * as properties.
+     * Search for names in a class, adding the resulting methods as properties.
      *
-     * <p> Uses reflection to find the methods of the given names. Then
-     * FunctionObjects are constructed from the methods found, and
-     * are added to this object as properties with the given names.
+     * <p>Uses reflection to find the methods of the given names. Then FunctionObjects are
+     * constructed from the methods found, and are added to this object as properties with the given
+     * names.
      *
      * @param names the names of the Methods to add as function properties
      * @param clazz the class to search for the Methods
      * @param attributes the attributes of the new properties
      * @see org.mozilla.javascript.FunctionObject
      */
-    public void defineFunctionProperties(String[] names, Class<?> clazz,
-                                         int attributes)
-    {
+    public void defineFunctionProperties(String[] names, Class<?> clazz, int attributes) {
         Method[] methods = FunctionObject.getMethodList(clazz);
-        for (int i=0; i < names.length; i++) {
+        for (int i = 0; i < names.length; i++) {
             String name = names[i];
             Method m = FunctionObject.findSingleMethod(methods, name);
             if (m == null) {
-                throw Context.reportRuntimeError2(
-                    "msg.method.not.found", name, clazz.getName());
+                throw Context.reportRuntimeErrorById("msg.method.not.found", name, clazz.getName());
             }
             FunctionObject f = new FunctionObject(name, m, this);
             defineProperty(name, f, attributes);
@@ -2200,59 +1871,58 @@
     }
 
     /**
-     * Get the Object.prototype property.
-     * See ECMA 15.2.4.
+     * Get the Object.prototype property. See ECMA 15.2.4.
+     *
+     * @param scope an object in the scope chain
      */
     public static Scriptable getObjectPrototype(Scriptable scope) {
-        return TopLevel.getBuiltinPrototype(getTopLevelScope(scope),
-                TopLevel.Builtins.Object);
+        return TopLevel.getBuiltinPrototype(getTopLevelScope(scope), TopLevel.Builtins.Object);
     }
 
     /**
-     * Get the Function.prototype property.
-     * See ECMA 15.3.4.
+     * Get the Function.prototype property. See ECMA 15.3.4.
+     *
+     * @param scope an object in the scope chain
      */
     public static Scriptable getFunctionPrototype(Scriptable scope) {
-        return TopLevel.getBuiltinPrototype(getTopLevelScope(scope),
-                TopLevel.Builtins.Function);
+        return TopLevel.getBuiltinPrototype(getTopLevelScope(scope), TopLevel.Builtins.Function);
+    }
+
+    public static Scriptable getGeneratorFunctionPrototype(Scriptable scope) {
+        return TopLevel.getBuiltinPrototype(
+                getTopLevelScope(scope), TopLevel.Builtins.GeneratorFunction);
     }
 
     public static Scriptable getArrayPrototype(Scriptable scope) {
-        return TopLevel.getBuiltinPrototype(getTopLevelScope(scope),
-                TopLevel.Builtins.Array);
+        return TopLevel.getBuiltinPrototype(getTopLevelScope(scope), TopLevel.Builtins.Array);
     }
 
     /**
      * Get the prototype for the named class.
      *
-     * For example, <code>getClassPrototype(s, "Date")</code> will first
-     * walk up the parent chain to find the outermost scope, then will
-     * search that scope for the Date constructor, and then will
-     * return Date.prototype. If any of the lookups fail, or
-     * the prototype is not a JavaScript object, then null will
-     * be returned.
+     * <p>For example, <code>getClassPrototype(s, "Date")</code> will first walk up the parent chain
+     * to find the outermost scope, then will search that scope for the Date constructor, and then
+     * will return Date.prototype. If any of the lookups fail, or the prototype is not a JavaScript
+     * object, then null will be returned.
      *
      * @param scope an object in the scope chain
      * @param className the name of the constructor
-     * @return the prototype for the named class, or null if it
-     *         cannot be found.
+     * @return the prototype for the named class, or null if it cannot be found.
      */
-    public static Scriptable getClassPrototype(Scriptable scope,
-                                               String className)
-    {
+    public static Scriptable getClassPrototype(Scriptable scope, String className) {
         scope = getTopLevelScope(scope);
         Object ctor = getProperty(scope, className);
         Object proto;
         if (ctor instanceof BaseFunction) {
-            proto = ((BaseFunction)ctor).getPrototypeProperty();
+            proto = ((BaseFunction) ctor).getPrototypeProperty();
         } else if (ctor instanceof Scriptable) {
-            Scriptable ctorObj = (Scriptable)ctor;
+            Scriptable ctorObj = (Scriptable) ctor;
             proto = ctorObj.get("prototype", ctorObj);
         } else {
             return null;
         }
         if (proto instanceof Scriptable) {
-            return (Scriptable)proto;
+            return (Scriptable) proto;
         }
         return null;
     }
@@ -2260,15 +1930,14 @@
     /**
      * Get the global scope.
      *
-     * <p>Walks the parent scope chain to find an object with a null
-     * parent scope (the global object).
+     * <p>Walks the parent scope chain to find an object with a null parent scope (the global
+     * object).
      *
      * @param obj a JavaScript object
      * @return the corresponding global scope
      */
-    public static Scriptable getTopLevelScope(Scriptable obj)
-    {
-        for (;;) {
+    public static Scriptable getTopLevelScope(Scriptable obj) {
+        for (; ; ) {
             Scriptable parent = obj.getParentScope();
             if (parent == null) {
                 return obj;
@@ -2278,39 +1947,41 @@
     }
 
     public boolean isExtensible() {
-      return isExtensible;
+        return isExtensible;
     }
 
     public void preventExtensions() {
-      isExtensible = false;
+        isExtensible = false;
     }
 
     /**
      * Seal this object.
      *
-     * It is an error to add properties to or delete properties from
-     * a sealed object. It is possible to change the value of an
-     * existing property. Once an object is sealed it may not be unsealed.
+     * <p>It is an error to add properties to or delete properties from a sealed object. It is
+     * possible to change the value of an existing property. Once an object is sealed it may not be
+     * unsealed.
      *
      * @since 1.4R3
      */
-    public synchronized void sealObject() {
-        if (count >= 0) {
-            // Make sure all LazilyLoadedCtors are initialized before sealing.
-            Slot slot = firstAdded;
-            while (slot != null) {
-                Object value = slot.value;
-                if (value instanceof LazilyLoadedCtor) {
-                    LazilyLoadedCtor initializer = (LazilyLoadedCtor) value;
-                    try {
-                        initializer.init();
-                    } finally {
-                        slot.value = initializer.getValue();
+    public void sealObject() {
+        if (!isSealed) {
+            final long stamp = slotMap.readLock();
+            try {
+                for (Slot slot : slotMap) {
+                    Object value = slot.value;
+                    if (value instanceof LazilyLoadedCtor) {
+                        LazilyLoadedCtor initializer = (LazilyLoadedCtor) value;
+                        try {
+                            initializer.init();
+                        } finally {
+                            slot.value = initializer.getValue();
+                        }
                     }
                 }
-                slot = slot.orderedNext;
+                isSealed = true;
+            } finally {
+                slotMap.unlockRead(stamp);
             }
-            count = ~count;
         }
     }
 
@@ -2322,81 +1993,73 @@
      * @see #sealObject()
      */
     public final boolean isSealed() {
-        return count < 0;
+        return isSealed;
     }
 
-    private void checkNotSealed(Object key, int index)
-    {
-        if (!isSealed())
-            return;
+    private void checkNotSealed(Object key, int index) {
+        if (!isSealed()) return;
 
         String str = (key != null) ? key.toString() : Integer.toString(index);
-        throw Context.reportRuntimeError1("msg.modify.sealed", str);
+        throw Context.reportRuntimeErrorById("msg.modify.sealed", str);
     }
 
     /**
      * Gets a named property from an object or any object in its prototype chain.
+     *
+     * <p>Searches the prototype chain for a property named <code>name</code>.
+     *
      * <p>
-     * Searches the prototype chain for a property named <code>name</code>.
-     * <p>
+     *
      * @param obj a JavaScript object
      * @param name a property name
-     * @return the value of a property with name <code>name</code> found in
-     *         <code>obj</code> or any object in its prototype chain, or
-     *         <code>Scriptable.NOT_FOUND</code> if not found
+     * @return the value of a property with name <code>name</code> found in <code>obj</code> or any
+     *     object in its prototype chain, or <code>Scriptable.NOT_FOUND</code> if not found
      * @since 1.5R2
      */
-    public static Object getProperty(Scriptable obj, String name)
-    {
+    public static Object getProperty(Scriptable obj, String name) {
         Scriptable start = obj;
         Object result;
         do {
             result = obj.get(name, start);
-            if (result != Scriptable.NOT_FOUND)
-                break;
+            if (result != Scriptable.NOT_FOUND) break;
             obj = obj.getPrototype();
         } while (obj != null);
         return result;
     }
 
-    /**
-     * This is a version of getProperty that works with Symbols.
-     */
-    public static Object getProperty(Scriptable obj, Symbol key)
-    {
+    /** This is a version of getProperty that works with Symbols. */
+    public static Object getProperty(Scriptable obj, Symbol key) {
         Scriptable start = obj;
         Object result;
         do {
             result = ensureSymbolScriptable(obj).get(key, start);
-            if (result != Scriptable.NOT_FOUND)
-                break;
+            if (result != Scriptable.NOT_FOUND) break;
             obj = obj.getPrototype();
         } while (obj != null);
         return result;
     }
 
     /**
-     * Gets an indexed property from an object or any object in its prototype
-     * chain and coerces it to the requested Java type.
-     * <p>
-     * Searches the prototype chain for a property with integral index
-     * <code>index</code>. Note that if you wish to look for properties with numerical
-     * but non-integral indicies, you should use getProperty(Scriptable,String) with
-     * the string value of the index.
+     * Gets an indexed property from an object or any object in its prototype chain and coerces it
+     * to the requested Java type.
+     *
+     * <p>Searches the prototype chain for a property with integral index <code>index</code>. Note
+     * that if you wish to look for properties with numerical but non-integral indicies, you should
+     * use getProperty(Scriptable,String) with the string value of the index.
+     *
      * <p>
+     *
      * @param s a JavaScript object
      * @param index an integral index
      * @param type the required Java type of the result
-     * @return the value of a property with name <code>name</code> found in
-     *         <code>obj</code> or any object in its prototype chain, or
-     *         null if not found. Note that it does not return
-     *         {@link Scriptable#NOT_FOUND} as it can ordinarily not be
-     *         converted to most of the types.
+     * @return the value of a property with name <code>name</code> found in <code>obj</code> or any
+     *     object in its prototype chain, or null if not found. Note that it does not return {@link
+     *     Scriptable#NOT_FOUND} as it can ordinarily not be converted to most of the types.
      * @since 1.7R3
      */
     public static <T> T getTypedProperty(Scriptable s, int index, Class<T> type) {
         Object val = getProperty(s, index);
-        if(val == Scriptable.NOT_FOUND) {
+        if (val == Scriptable.NOT_FOUND) {
             val = null;
         }
         return type.cast(Context.jsToJava(val, type));
@@ -2404,258 +2067,235 @@
 
     /**
      * Gets an indexed property from an object or any object in its prototype chain.
+     *
+     * <p>Searches the prototype chain for a property with integral index <code>index</code>. Note
+     * that if you wish to look for properties with numerical but non-integral indicies, you should
+     * use getProperty(Scriptable,String) with the string value of the index.
+     *
      * <p>
-     * Searches the prototype chain for a property with integral index
-     * <code>index</code>. Note that if you wish to look for properties with numerical
-     * but non-integral indicies, you should use getProperty(Scriptable,String) with
-     * the string value of the index.
-     * <p>
+     *
      * @param obj a JavaScript object
      * @param index an integral index
-     * @return the value of a property with index <code>index</code> found in
-     *         <code>obj</code> or any object in its prototype chain, or
-     *         <code>Scriptable.NOT_FOUND</code> if not found
+     * @return the value of a property with index <code>index</code> found in <code>obj</code> or
+     *     any object in its prototype chain, or <code>Scriptable.NOT_FOUND</code> if not found
      * @since 1.5R2
      */
-    public static Object getProperty(Scriptable obj, int index)
-    {
+    public static Object getProperty(Scriptable obj, int index) {
         Scriptable start = obj;
         Object result;
         do {
             result = obj.get(index, start);
-            if (result != Scriptable.NOT_FOUND)
-                break;
+            if (result != Scriptable.NOT_FOUND) break;
             obj = obj.getPrototype();
         } while (obj != null);
         return result;
     }
 
     /**
-     * Gets a named property from an object or any object in its prototype chain
-     * and coerces it to the requested Java type.
-     * <p>
-     * Searches the prototype chain for a property named <code>name</code>.
+     * Gets a named property from an object or any object in its prototype chain and coerces it to
+     * the requested Java type.
+     *
+     * <p>Searches the prototype chain for a property named <code>name</code>.
+     *
      * <p>
+     *
      * @param s a JavaScript object
      * @param name a property name
      * @param type the required Java type of the result
-     * @return the value of a property with name <code>name</code> found in
-     *         <code>obj</code> or any object in its prototype chain, or
-     *         null if not found. Note that it does not return
-     *         {@link Scriptable#NOT_FOUND} as it can ordinarily not be
-     *         converted to most of the types.
+     * @return the value of a property with name <code>name</code> found in <code>obj</code> or any
+     *     object in its prototype chain, or null if not found. Note that it does not return {@link
+     *     Scriptable#NOT_FOUND} as it can ordinarily not be converted to most of the types.
      * @since 1.7R3
      */
     public static <T> T getTypedProperty(Scriptable s, String name, Class<T> type) {
         Object val = getProperty(s, name);
-        if(val == Scriptable.NOT_FOUND) {
+        if (val == Scriptable.NOT_FOUND) {
             val = null;
         }
         return type.cast(Context.jsToJava(val, type));
     }
 
     /**
-     * Returns whether a named property is defined in an object or any object
-     * in its prototype chain.
-     * <p>
-     * Searches the prototype chain for a property named <code>name</code>.
+     * Returns whether a named property is defined in an object or any object in its prototype
+     * chain.
+     *
+     * <p>Searches the prototype chain for a property named <code>name</code>.
+     *
      * <p>
+     *
      * @param obj a JavaScript object
      * @param name a property name
      * @return the true if property was found
      * @since 1.5R2
      */
-    public static boolean hasProperty(Scriptable obj, String name)
-    {
+    public static boolean hasProperty(Scriptable obj, String name) {
         return null != getBase(obj, name);
     }
 
     /**
-     * If hasProperty(obj, name) would return true, then if the property that
-     * was found is compatible with the new property, this method just returns.
-     * If the property is not compatible, then an exception is thrown.
-     *
-     * A property redefinition is incompatible if the first definition was a
-     * const declaration or if this one is.  They are compatible only if neither
-     * was const.
-     */
-    public static void redefineProperty(Scriptable obj, String name,
-                                        boolean isConst)
-    {
+     * If hasProperty(obj, name) would return true, then if the property that was found is
+     * compatible with the new property, this method just returns. If the property is not
+     * compatible, then an exception is thrown.
+     *
+     * <p>A property redefinition is incompatible if the first definition was a const declaration or
+     * if this one is. They are compatible only if neither was const.
+     */
+    public static void redefineProperty(Scriptable obj, String name, boolean isConst) {
         Scriptable base = getBase(obj, name);
-        if (base == null)
-            return;
+        if (base == null) return;
         if (base instanceof ConstProperties) {
-            ConstProperties cp = (ConstProperties)base;
+            ConstProperties cp = (ConstProperties) base;
 
-            if (cp.isConst(name))
-                throw ScriptRuntime.typeError1("msg.const.redecl", name);
+            if (cp.isConst(name)) throw ScriptRuntime.typeErrorById("msg.const.redecl", name);
         }
-        if (isConst)
-            throw ScriptRuntime.typeError1("msg.var.redecl", name);
+        if (isConst) throw ScriptRuntime.typeErrorById("msg.var.redecl", name);
     }
+
     /**
-     * Returns whether an indexed property is defined in an object or any object
-     * in its prototype chain.
-     * <p>
-     * Searches the prototype chain for a property with index <code>index</code>.
+     * Returns whether an indexed property is defined in an object or any object in its prototype
+     * chain.
+     *
+     * <p>Searches the prototype chain for a property with index <code>index</code>.
+     *
      * <p>
+     *
      * @param obj a JavaScript object
      * @param index a property index
      * @return the true if property was found
      * @since 1.5R2
      */
-    public static boolean hasProperty(Scriptable obj, int index)
-    {
+    public static boolean hasProperty(Scriptable obj, int index) {
         return null != getBase(obj, index);
     }
 
-    /**
-     * A version of hasProperty for properties with Symbol keys.
-     */
-    public static boolean hasProperty(Scriptable obj, Symbol key)
-    {
+    /** A version of hasProperty for properties with Symbol keys. */
+    public static boolean hasProperty(Scriptable obj, Symbol key) {
         return null != getBase(obj, key);
     }
 
     /**
      * Puts a named property in an object or in an object in its prototype chain.
-     * <p>
-     * Searches for the named property in the prototype chain. If it is found,
-     * the value of the property in <code>obj</code> is changed through a call
-     * to {@link Scriptable#put(String, Scriptable, Object)} on the
-     * prototype passing <code>obj</code> as the <code>start</code> argument.
-     * This allows the prototype to veto the property setting in case the
-     * prototype defines the property with [[ReadOnly]] attribute. If the
-     * property is not found, it is added in <code>obj</code>.
+     *
+     * <p>Searches for the named property in the prototype chain. If it is found, the value of the
+     * property in <code>obj</code> is changed through a call to {@link Scriptable#put(String,
+     * Scriptable, Object)} on the prototype passing <code>obj</code> as the <code>start</code>
+     * argument. This allows the prototype to veto the property setting in case the prototype
+     * defines the property with [[ReadOnly]] attribute. If the property is not found, it is added
+     * in <code>obj</code>.
+     *
      * @param obj a JavaScript object
      * @param name a property name
      * @param value any JavaScript value accepted by Scriptable.put
      * @since 1.5R2
      */
-    public static void putProperty(Scriptable obj, String name, Object value)
-    {
+    public static void putProperty(Scriptable obj, String name, Object value) {
         Scriptable base = getBase(obj, name);
-        if (base == null)
-            base = obj;
+        if (base == null) base = obj;
         base.put(name, obj, value);
     }
 
-    /**
-     * This is a version of putProperty for Symbol keys.
-     */
-    public static void putProperty(Scriptable obj, Symbol key, Object value)
-    {
+    /** This is a version of putProperty for Symbol keys. */
+    public static void putProperty(Scriptable obj, Symbol key, Object value) {
         Scriptable base = getBase(obj, key);
-        if (base == null)
-            base = obj;
+        if (base == null) base = obj;
         ensureSymbolScriptable(base).put(key, obj, value);
     }
 
     /**
      * Puts a named property in an object or in an object in its prototype chain.
-     * <p>
-     * Searches for the named property in the prototype chain. If it is found,
-     * the value of the property in <code>obj</code> is changed through a call
-     * to {@link Scriptable#put(String, Scriptable, Object)} on the
-     * prototype passing <code>obj</code> as the <code>start</code> argument.
-     * This allows the prototype to veto the property setting in case the
-     * prototype defines the property with [[ReadOnly]] attribute. If the
-     * property is not found, it is added in <code>obj</code>.
+     *
+     * <p>Searches for the named property in the prototype chain. If it is found, the value of the
+     * property in <code>obj</code> is changed through a call to {@link Scriptable#put(String,
+     * Scriptable, Object)} on the prototype passing <code>obj</code> as the <code>start</code>
+     * argument. This allows the prototype to veto the property setting in case the prototype
+     * defines the property with [[ReadOnly]] attribute. If the property is not found, it is added
+     * in <code>obj</code>.
+     *
      * @param obj a JavaScript object
      * @param name a property name
      * @param value any JavaScript value accepted by Scriptable.put
      * @since 1.5R2
      */
-    public static void putConstProperty(Scriptable obj, String name, Object value)
-    {
+    public static void putConstProperty(Scriptable obj, String name, Object value) {
         Scriptable base = getBase(obj, name);
-        if (base == null)
-            base = obj;
-        if (base instanceof ConstProperties)
-            ((ConstProperties)base).putConst(name, obj, value);
+        if (base == null) base = obj;
+        if (base instanceof ConstProperties) ((ConstProperties) base).putConst(name, obj, value);
     }
 
     /**
      * Puts an indexed property in an object or in an object in its prototype chain.
-     * <p>
-     * Searches for the indexed property in the prototype chain. If it is found,
-     * the value of the property in <code>obj</code> is changed through a call
-     * to {@link Scriptable#put(int, Scriptable, Object)} on the prototype
-     * passing <code>obj</code> as the <code>start</code> argument. This allows
-     * the prototype to veto the property setting in case the prototype defines
-     * the property with [[ReadOnly]] attribute. If the property is not found,
-     * it is added in <code>obj</code>.
+     *
+     * <p>Searches for the indexed property in the prototype chain. If it is found, the value of the
+     * property in <code>obj</code> is changed through a call to {@link Scriptable#put(int,
+     * Scriptable, Object)} on the prototype passing <code>obj</code> as the <code>start</code>
+     * argument. This allows the prototype to veto the property setting in case the prototype
+     * defines the property with [[ReadOnly]] attribute. If the property is not found, it is added
+     * in <code>obj</code>.
+     *
      * @param obj a JavaScript object
      * @param index a property index
      * @param value any JavaScript value accepted by Scriptable.put
      * @since 1.5R2
      */
-    public static void putProperty(Scriptable obj, int index, Object value)
-    {
+    public static void putProperty(Scriptable obj, int index, Object value) {
         Scriptable base = getBase(obj, index);
-        if (base == null)
-            base = obj;
+        if (base == null) base = obj;
         base.put(index, obj, value);
     }
 
     /**
      * Removes the property from an object or its prototype chain.
-     * <p>
-     * Searches for a property with <code>name</code> in obj or
-     * its prototype chain. If it is found, the object's delete
-     * method is called.
+     *
+     * <p>Searches for a property with <code>name</code> in obj or its prototype chain. If it is
+     * found, the object's delete method is called.
+     *
      * @param obj a JavaScript object
      * @param name a property name
      * @return true if the property doesn't exist or was successfully removed
      * @since 1.5R2
      */
-    public static boolean deleteProperty(Scriptable obj, String name)
-    {
+    public static boolean deleteProperty(Scriptable obj, String name) {
         Scriptable base = getBase(obj, name);
-        if (base == null)
-            return true;
+        if (base == null) return true;
         base.delete(name);
         return !base.has(name, obj);
     }
 
     /**
      * Removes the property from an object or its prototype chain.
-     * <p>
-     * Searches for a property with <code>index</code> in obj or
-     * its prototype chain. If it is found, the object's delete
-     * method is called.
+     *
+     * <p>Searches for a property with <code>index</code> in obj or its prototype chain. If it is
+     * found, the object's delete method is called.
+     *
      * @param obj a JavaScript object
      * @param index a property index
      * @return true if the property doesn't exist or was successfully removed
      * @since 1.5R2
      */
-    public static boolean deleteProperty(Scriptable obj, int index)
-    {
+    public static boolean deleteProperty(Scriptable obj, int index) {
         Scriptable base = getBase(obj, index);
-        if (base == null)
-            return true;
+        if (base == null) return true;
         base.delete(index);
         return !base.has(index, obj);
     }
 
     /**
      * Returns an array of all ids from an object and its prototypes.
+     *
      * <p>
+     *
      * @param obj a JavaScript object
-     * @return an array of all ids from all object in the prototype chain.
-     *         If a given id occurs multiple times in the prototype chain,
-     *         it will occur only once in this list.
+     * @return an array of all ids from all object in the prototype chain. If a given id occurs
+     *     multiple times in the prototype chain, it will occur only once in this list.
      * @since 1.5R2
      */
-    public static Object[] getPropertyIds(Scriptable obj)
-    {
+    public static Object[] getPropertyIds(Scriptable obj) {
         if (obj == null) {
             return ScriptRuntime.emptyArgs;
         }
         Object[] result = obj.getIds();
         ObjToIntMap map = null;
-        for (;;) {
+        for (; ; ) {
             obj = obj.getPrototype();
             if (obj == null) {
                 break;
@@ -2687,34 +2327,30 @@
 
     /**
      * Call a method of an object.
+     *
      * @param obj the JavaScript object
      * @param methodName the name of the function property
      * @param args the arguments for the call
-     *
      * @see Context#getCurrentContext()
      */
-    public static Object callMethod(Scriptable obj, String methodName,
-                                    Object[] args)
-    {
+    public static Object callMethod(Scriptable obj, String methodName, Object[] args) {
         return callMethod(null, obj, methodName, args);
     }
 
     /**
      * Call a method of an object.
+     *
      * @param cx the Context object associated with the current thread.
      * @param obj the JavaScript object
      * @param methodName the name of the function property
      * @param args the arguments for the call
      */
-    public static Object callMethod(Context cx, Scriptable obj,
-                                    String methodName,
-                                    Object[] args)
-    {
+    public static Object callMethod(Context cx, Scriptable obj, String methodName, Object[] args) {
         Object funObj = getProperty(obj, methodName);
         if (!(funObj instanceof Function)) {
             throw ScriptRuntime.notFunctionError(obj, methodName);
         }
-        Function fun = (Function)funObj;
+        Function fun = (Function) funObj;
         // XXX: What should be the scope when calling funObj?
         // The following favor scope stored in the object on the assumption
         // that is more useful especially under dynamic scope setup.
@@ -2725,71 +2361,61 @@
         Scriptable scope = ScriptableObject.getTopLevelScope(obj);
         if (cx != null) {
             return fun.call(cx, scope, obj, args);
-        } else {
-            return Context.call(null, fun, scope, obj, args);
         }
+        return Context.call(null, fun, scope, obj, args);
     }
 
-    private static Scriptable getBase(Scriptable obj, String name)
-    {
+    private static Scriptable getBase(Scriptable obj, String name) {
         do {
-            if (obj.has(name, obj))
-                break;
+            if (obj.has(name, obj)) break;
             obj = obj.getPrototype();
-        } while(obj != null);
+        } while (obj != null);
         return obj;
     }
 
-    private static Scriptable getBase(Scriptable obj, int index)
-    {
+    private static Scriptable getBase(Scriptable obj, int index) {
         do {
-            if (obj.has(index, obj))
-                break;
+            if (obj.has(index, obj)) break;
             obj = obj.getPrototype();
-        } while(obj != null);
+        } while (obj != null);
         return obj;
     }
 
-    private static Scriptable getBase(Scriptable obj, Symbol key)
-    {
+    private static Scriptable getBase(Scriptable obj, Symbol key) {
         do {
-            if (ensureSymbolScriptable(obj).has(key, obj))
-                break;
+            if (ensureSymbolScriptable(obj).has(key, obj)) break;
             obj = obj.getPrototype();
-        } while(obj != null);
+        } while (obj != null);
         return obj;
     }
 
     /**
      * Get arbitrary application-specific value associated with this object.
+     *
      * @param key key object to select particular value.
      * @see #associateValue(Object key, Object value)
      */
-    public final Object getAssociatedValue(Object key)
-    {
-        Map<Object,Object> h = associatedValues;
-        if (h == null)
-            return null;
+    public final Object getAssociatedValue(Object key) {
+        Map<Object, Object> h = associatedValues;
+        if (h == null) return null;
         return h.get(key);
     }
 
     /**
-     * Get arbitrary application-specific value associated with the top scope
-     * of the given scope.
-     * The method first calls {@link #getTopLevelScope(Scriptable scope)}
-     * and then searches the prototype chain of the top scope for the first
-     * object containing the associated value with the given key.
+     * Get arbitrary application-specific value associated with the top scope of the given scope.
+     * The method first calls {@link #getTopLevelScope(Scriptable scope)} and then searches the
+     * prototype chain of the top scope for the first object containing the associated value with
+     * the given key.
      *
      * @param scope the starting scope.
      * @param key key object to select particular value.
      * @see #getAssociatedValue(Object key)
      */
-    public static Object getTopScopeValue(Scriptable scope, Object key)
-    {
+    public static Object getTopScopeValue(Scriptable scope, Object key) {
         scope = ScriptableObject.getTopLevelScope(scope);
-        for (;;) {
+        for (; ; ) {
             if (scope instanceof ScriptableObject) {
-                ScriptableObject so = (ScriptableObject)scope;
+                ScriptableObject so = (ScriptableObject) scope;
                 Object value = so.getAssociatedValue(key);
                 if (value != null) {
                     return value;
@@ -2803,106 +2429,109 @@
     }
 
     /**
-     * Associate arbitrary application-specific value with this object.
-     * Value can only be associated with the given object and key only once.
-     * The method ignores any subsequent attempts to change the already
-     * associated value.
-     * <p> The associated values are not serialized.
+     * Associate arbitrary application-specific value with this object. Value can only be associated
+     * with the given object and key only once. The method ignores any subsequent attempts to change
+     * the already associated value.
+     *
+     * <p>The associated values are not serialized.
+     *
      * @param key key object to select particular value.
      * @param value the value to associate
-     * @return the passed value if the method is called first time for the
-     * given key or old value for any subsequent calls.
+     * @return the passed value if the method is called first time for the given key or old value
+     *     for any subsequent calls.
      * @see #getAssociatedValue(Object key)
      */
-    public synchronized final Object associateValue(Object key, Object value)
-    {
+    public final synchronized Object associateValue(Object key, Object value) {
         if (value == null) throw new IllegalArgumentException();
-        Map<Object,Object> h = associatedValues;
+        Map<Object, Object> h = associatedValues;
         if (h == null) {
-            h = new HashMap<Object,Object>();
+            h = new HashMap<Object, Object>();
             associatedValues = h;
         }
         return Kit.initHash(h, key, value);
     }
 
     /**
-     *
      * @param key
      * @param index
      * @param start
      * @param value
-     * @return false if this != start and no slot was found.  true if this == start
-     * or this != start and a READONLY slot was found.
+     * @return false if this != start and no slot was found. true if this == start or this != start
+     *     and a READONLY slot was found.
      */
-    private boolean putImpl(Object key, int index, Scriptable start,
-                            Object value)
-    {
+    private boolean putImpl(Object key, int index, Scriptable start, Object value) {
         // This method is very hot (basically called on each assignment)
         // so we inline the extensible/sealed checks below.
-        if (!isExtensible) {
-            Context cx = Context.getContext();
-            if (cx.isStrictMode()) {
-                throw ScriptRuntime.typeError0("msg.not.extensible");
-            }
-        }
         Slot slot;
         if (this != start) {
-            slot = getSlot(key, index, SLOT_QUERY);
+            slot = slotMap.query(key, index);
+            if (!isExtensible
+                    && (slot == null
+                            || (!(slot instanceof AccessorSlot)
+                                    && (slot.getAttributes() & READONLY) != 0))
+                    && Context.isCurrentContextStrict()) {
+                throw ScriptRuntime.typeErrorById("msg.not.extensible");
+            }
             if (slot == null) {
                 return false;
             }
         } else if (!isExtensible) {
-            slot = getSlot(key, index, SLOT_QUERY);
+            slot = slotMap.query(key, index);
+            if ((slot == null
+                            || (!(slot instanceof AccessorSlot)
+                                    && (slot.getAttributes() & READONLY) != 0))
+                    && Context.isCurrentContextStrict()) {
+                throw ScriptRuntime.typeErrorById("msg.not.extensible");
+            }
             if (slot == null) {
                 return true;
             }
         } else {
-            if (count < 0) checkNotSealed(key, index);
-            slot = getSlot(key, index, SLOT_MODIFY);
+            if (isSealed) {
+                checkNotSealed(key, index);
+            }
+            slot = slotMap.modify(key, index, 0);
         }
         return slot.setValue(value, this, start);
     }
 
-
     /**
-     *
      * @param name
      * @param index
      * @param start
      * @param value
-     * @param constFlag EMPTY means normal put.  UNINITIALIZED_CONST means
-     * defineConstProperty.  READONLY means const initialization expression.
-     * @return false if this != start and no slot was found.  true if this == start
-     * or this != start and a READONLY slot was found.
-     */
-    private boolean putConstImpl(String name, int index, Scriptable start,
-                                 Object value, int constFlag)
-    {
+     * @param constFlag EMPTY means normal put. UNINITIALIZED_CONST means defineConstProperty.
+     *     READONLY means const initialization expression.
+     * @return false if this != start and no slot was found. true if this == start or this != start
+     *     and a READONLY slot was found.
+     */
+    private boolean putConstImpl(
+            String name, int index, Scriptable start, Object value, int constFlag) {
         assert (constFlag != EMPTY);
         if (!isExtensible) {
             Context cx = Context.getContext();
             if (cx.isStrictMode()) {
-                throw ScriptRuntime.typeError0("msg.not.extensible");
+                throw ScriptRuntime.typeErrorById("msg.not.extensible");
             }
         }
         Slot slot;
         if (this != start) {
-            slot = getSlot(name, index, SLOT_QUERY);
+            slot = slotMap.query(name, index);
             if (slot == null) {
                 return false;
             }
         } else if (!isExtensible()) {
-            slot = getSlot(name, index, SLOT_QUERY);
+            slot = slotMap.query(name, index);
             if (slot == null) {
                 return true;
             }
         } else {
             checkNotSealed(name, index);
             // either const hoisted declaration or initialization
-            slot = unwrapSlot(getSlot(name, index, SLOT_MODIFY_CONST));
+            slot = slotMap.modify(name, index, CONST);
             int attr = slot.getAttributes();
             if ((attr & READONLY) == 0)
-                throw Context.reportRuntimeError1("msg.var.redecl", name);
+                throw Context.reportRuntimeErrorById("msg.var.redecl", name);
             if ((attr & UNINITIALIZED_CONST) != 0) {
                 slot.value = value;
                 // clear the bit on const initialization
@@ -2914,292 +2543,24 @@
         return slot.setValue(value, this, start);
     }
 
-    private Slot findAttributeSlot(String name, int index, int accessType)
-    {
-        Slot slot = getSlot(name, index, accessType);
+    private Slot getAttributeSlot(String name, int index) {
+        Slot slot = slotMap.query(name, index);
         if (slot == null) {
             String str = (name != null ? name : Integer.toString(index));
-            throw Context.reportRuntimeError1("msg.prop.not.found", str);
+            throw Context.reportRuntimeErrorById("msg.prop.not.found", str);
         }
         return slot;
     }
 
-    private Slot findAttributeSlot(Symbol key, int accessType)
-    {
-        Slot slot = getSlot(key, 0, accessType);
+    private Slot getAttributeSlot(Symbol key) {
+        Slot slot = slotMap.query(key, 0);
         if (slot == null) {
-            throw Context.reportRuntimeError1("msg.prop.not.found", key);
+            throw Context.reportRuntimeErrorById("msg.prop.not.found", key);
         }
         return slot;
     }
 
-    private static Slot unwrapSlot(Slot slot) {
-        return (slot instanceof RelinkedSlot) ? ((RelinkedSlot)slot).slot : slot;
-    }
-
-    /**
-     * Locate the slot with given name or index. Depending on the accessType
-     * parameter and the current slot status, a new slot may be allocated.
-     *
-     * @param key either a String or a Symbol object that identifies the property
-     * @param index index or 0 if slot holds property name.
-     */
-    private Slot getSlot(Object key, int index, int accessType)
-    {
-        // Check the hashtable without using synchronization
-        Slot[] slotsLocalRef = slots; // Get stable local reference
-        if (slotsLocalRef == null && accessType == SLOT_QUERY) {
-            return null;
-        }
-
-        int indexOrHash = (key != null ? key.hashCode() : index);
-        if (slotsLocalRef != null) {
-            Slot slot;
-            int slotIndex = getSlotIndex(slotsLocalRef.length, indexOrHash);
-            for (slot = slotsLocalRef[slotIndex];
-                 slot != null;
-                 slot = slot.next) {
-                Object skey = slot.name;
-                if (indexOrHash == slot.indexOrHash &&
-                        (skey == key ||
-                                (key != null && key.equals(skey)))) {
-                    break;
-                }
-            }
-            switch (accessType) {
-                case SLOT_QUERY:
-                    return slot;
-                case SLOT_MODIFY:
-                case SLOT_MODIFY_CONST:
-                    if (slot != null)
-                        return slot;
-                    break;
-                case SLOT_MODIFY_GETTER_SETTER:
-                    slot = unwrapSlot(slot);
-                    if (slot instanceof GetterSlot)
-                        return slot;
-                    break;
-                case SLOT_CONVERT_ACCESSOR_TO_DATA:
-                    slot = unwrapSlot(slot);
-                    if ( !(slot instanceof GetterSlot) )
-                        return slot;
-                    break;
-            }
-        }
-
-        // A new slot has to be inserted or the old has to be replaced
-        // by GetterSlot. Time to synchronize.
-        return createSlot(key, indexOrHash, accessType);
-    }
-
-    private synchronized Slot createSlot(Object key, int indexOrHash, int accessType) {
-        Slot[] slotsLocalRef = slots;
-        int insertPos;
-        if (count == 0) {
-            // Always throw away old slots if any on empty insert.
-            slotsLocalRef = new Slot[INITIAL_SLOT_SIZE];
-            slots = slotsLocalRef;
-            insertPos = getSlotIndex(slotsLocalRef.length, indexOrHash);
-        } else {
-            int tableSize = slotsLocalRef.length;
-            insertPos = getSlotIndex(tableSize, indexOrHash);
-            Slot prev = slotsLocalRef[insertPos];
-            Slot slot = prev;
-            while (slot != null) {
-                if (slot.indexOrHash == indexOrHash &&
-                        (slot.name == key ||
-                                (key != null && key.equals(slot.name))))
-                {
-                    break;
-                }
-                prev = slot;
-                slot = slot.next;
-            }
-
-            if (slot != null) {
-                // A slot with same name/index already exists. This means that
-                // a slot is being redefined from a value to a getter slot or
-                // vice versa, or it could be a race in application code.
-                // Check if we need to replace the slot depending on the
-                // accessType flag and return the appropriate slot instance.
-
-                Slot inner = unwrapSlot(slot);
-                Slot newSlot;
-
-                if (accessType == SLOT_MODIFY_GETTER_SETTER
-                        && !(inner instanceof GetterSlot)) {
-                    newSlot = new GetterSlot(key, indexOrHash, inner.getAttributes());
-                } else if (accessType == SLOT_CONVERT_ACCESSOR_TO_DATA
-                        && (inner instanceof GetterSlot)) {
-                    newSlot = new Slot(key, indexOrHash, inner.getAttributes());
-                } else if (accessType == SLOT_MODIFY_CONST) {
-                    return null;
-                } else {
-                    return inner;
-                }
-
-                newSlot.value = inner.value;
-                newSlot.next = slot.next;
-                // add new slot to linked list
-                if (lastAdded != null) {
-                    lastAdded.orderedNext = newSlot;
-                }
-                if (firstAdded == null) {
-                    firstAdded = newSlot;
-                }
-                lastAdded = newSlot;
-                // add new slot to hash table
-                if (prev == slot) {
-                    slotsLocalRef[insertPos] = newSlot;
-                } else {
-                    prev.next = newSlot;
-                }
-                // other housekeeping
-                slot.markDeleted();
-                return newSlot;
-            } else {
-                // Check if the table is not too full before inserting.
-                if (4 * (count + 1) > 3 * slotsLocalRef.length) {
-                    // table size must be a power of 2, always grow by x2
-                    slotsLocalRef = new Slot[slotsLocalRef.length * 2];
-                    copyTable(slots, slotsLocalRef, count);
-                    slots = slotsLocalRef;
-                    insertPos = getSlotIndex(slotsLocalRef.length,
-                            indexOrHash);
-                }
-            }
-        }
-        Slot newSlot = (accessType == SLOT_MODIFY_GETTER_SETTER
-                ? new GetterSlot(key, indexOrHash, 0)
-                : new Slot(key, indexOrHash, 0));
-        if (accessType == SLOT_MODIFY_CONST)
-            newSlot.setAttributes(CONST);
-        ++count;
-        // add new slot to linked list
-        if (lastAdded != null)
-            lastAdded.orderedNext = newSlot;
-        if (firstAdded == null)
-            firstAdded = newSlot;
-        lastAdded = newSlot;
-        // add new slot to hash table, return it
-        addKnownAbsentSlot(slotsLocalRef, newSlot, insertPos);
-        return newSlot;
-    }
-
-    private synchronized void removeSlot(Object key, int index) {
-        int indexOrHash = (key != null ? key.hashCode() : index);
-
-        Slot[] slotsLocalRef = slots;
-        if (count != 0) {
-            int tableSize = slotsLocalRef.length;
-            int slotIndex = getSlotIndex(tableSize, indexOrHash);
-            Slot prev = slotsLocalRef[slotIndex];
-            Slot slot = prev;
-            while (slot != null) {
-                if (slot.indexOrHash == indexOrHash &&
-                        (slot.name == key ||
-                                (key != null && key.equals(slot.name))))
-                {
-                    break;
-                }
-                prev = slot;
-                slot = slot.next;
-            }
-            if (slot != null) {
-                // non-configurable
-                if ((slot.getAttributes() & PERMANENT) != 0) {
-                    Context cx = Context.getContext();
-                    if (cx.isStrictMode()) {
-                        throw ScriptRuntime.typeError1("msg.delete.property.with.configurable.false", key);
-                    }
-                    return;
-                }
-                count--;
-                // remove slot from hash table
-                if (prev == slot) {
-                    slotsLocalRef[slotIndex] = slot.next;
-                } else {
-                    prev.next = slot.next;
-                }
-
-                // remove from ordered list. Previously this was done lazily in
-                // getIds() but delete is an infrequent operation so O(n)
-                // should be ok
-
-                // ordered list always uses the actual slot
-                Slot deleted = unwrapSlot(slot);
-                if (deleted == firstAdded) {
-                    prev = null;
-                    firstAdded = deleted.orderedNext;
-                } else {
-                    prev = firstAdded;
-                    while (prev.orderedNext != deleted) {
-                        prev = prev.orderedNext;
-                    }
-                    prev.orderedNext = deleted.orderedNext;
-                }
-                if (deleted == lastAdded) {
-                    lastAdded = prev;
-                }
-
-                // Mark the slot as removed.
-                slot.markDeleted();
-            }
-        }
-    }
-
-    private static int getSlotIndex(int tableSize, int indexOrHash)
-    {
-        // tableSize is a power of 2
-        return indexOrHash & (tableSize - 1);
-    }
-
-    // Must be inside synchronized (this)
-    private static void copyTable(Slot[] oldSlots, Slot[] newSlots, int count)
-    {
-        if (count == 0) throw Kit.codeBug();
-
-        int tableSize = newSlots.length;
-        int i = oldSlots.length;
-        for (;;) {
-            --i;
-            Slot slot = oldSlots[i];
-            while (slot != null) {
-                int insertPos = getSlotIndex(tableSize, slot.indexOrHash);
-                // If slot has next chain in old table use a new
-                // RelinkedSlot wrapper to keep old table valid
-                Slot insSlot = slot.next == null ? slot : new RelinkedSlot(slot);
-                addKnownAbsentSlot(newSlots, insSlot, insertPos);
-                slot = slot.next;
-                if (--count == 0)
-                    return;
-            }
-        }
-    }
-
-    /**
-     * Add slot with keys that are known to absent from the table.
-     * This is an optimization to use when inserting into empty table,
-     * after table growth or during deserialization.
-     */
-    private static void addKnownAbsentSlot(Slot[] slots, Slot slot,
-                                           int insertPos)
-    {
-        if (slots[insertPos] == null) {
-            slots[insertPos] = slot;
-        } else {
-            Slot prev = slots[insertPos];
-            Slot next = prev.next;
-            while (next != null) {
-                prev = next;
-                next = prev.next;
-            }
-            prev.next = slot;
-        }
-    }
-
     Object[] getIds(boolean getNonEnumerable, boolean getSymbols) {
-        Slot[] s = slots;
         Object[] a;
         int externalLen = (externalData == null ? 0 : externalData.getArrayLength());
 
@@ -3211,45 +2572,38 @@
                 a[i] = Integer.valueOf(i);
             }
         }
-        if (s == null) {
+        if (slotMap.isEmpty()) {
             return a;
         }
 
         int c = externalLen;
-        Slot slot = firstAdded;
-        while (slot != null && slot.wasDeleted) {
-            // we used to removed deleted slots from the linked list here
-            // but this is now done in removeSlot(). There may still be deleted
-            // slots (e.g. from slot conversion) but we don't want to mess
-            // with the list in unsynchronized code.
-            slot = slot.orderedNext;
-        }
-        while (slot != null) {
-            if ((getNonEnumerable || (slot.getAttributes() & DONTENUM) == 0) &&
-                    (getSymbols || !(slot.name instanceof Symbol))) {
-                if (c == externalLen) {
-                    // Special handling to combine external array with additional properties
-                    Object[] oldA = a;
-                    a = new Object[s.length + externalLen];
-                    if (oldA != null) {
-                        System.arraycopy(oldA, 0, a, 0, externalLen);
+        final long stamp = slotMap.readLock();
+        try {
+            for (Slot slot : slotMap) {
+                if ((getNonEnumerable || (slot.getAttributes() & DONTENUM) == 0)
+                        && (getSymbols || !(slot.name instanceof Symbol))) {
+                    if (c == externalLen) {
+                        // Special handling to combine external array with additional properties
+                        Object[] oldA = a;
+                        a = new Object[slotMap.dirtySize() + externalLen];
+                        if (oldA != null) {
+                            System.arraycopy(oldA, 0, a, 0, externalLen);
+                        }
                     }
+                    a[c++] = slot.name != null ? slot.name : Integer.valueOf(slot.indexOrHash);
                 }
-                a[c++] = slot.name != null
-                        ? slot.name
-                        : Integer.valueOf(slot.indexOrHash);
-            }
-            slot = slot.orderedNext;
-            while (slot != null && slot.wasDeleted) {
-                // skip deleted slots, see comment above
-                slot = slot.orderedNext;
             }
+        } finally {
+            slotMap.unlockRead(stamp);
         }
+
+        Object[] result;
         if (c == (a.length + externalLen)) {
-            return a;
+            result = a;
+        } else {
+            result = new Object[c];
+            System.arraycopy(a, 0, result, 0, c);
         }
-        Object[] result = new Object[c];
-        System.arraycopy(a, 0, result, 0, c);
 
         Context cx = Context.getCurrentContext();
         if ((cx != null) && cx.hasFeature(Context.FEATURE_ENUMERATE_IDS_FIRST)) {
@@ -3260,108 +2614,64 @@
         return result;
     }
 
-    private synchronized void writeObject(ObjectOutputStream out)
-        throws IOException
-    {
+    private void writeObject(ObjectOutputStream out) throws IOException {
         out.defaultWriteObject();
-        int objectsCount = count;
-        if (objectsCount < 0) {
-            // "this" was sealed
-            objectsCount = ~objectsCount;
-        }
-        if (objectsCount == 0) {
-            out.writeInt(0);
-        } else {
-            out.writeInt(slots.length);
-            Slot slot = firstAdded;
-            while (slot != null && slot.wasDeleted) {
-                // as long as we're traversing the order-added linked list,
-                // remove deleted slots
-                slot = slot.orderedNext;
-            }
-            firstAdded = slot;
-            while (slot != null) {
-                out.writeObject(slot);
-                Slot next = slot.orderedNext;
-                while (next != null && next.wasDeleted) {
-                    // remove deleted slots
-                    next = next.orderedNext;
-                }
-                slot.orderedNext = next;
-                slot = next;
+        final long stamp = slotMap.readLock();
+        try {
+            int objectsCount = slotMap.dirtySize();
+            if (objectsCount == 0) {
+                out.writeInt(0);
+            } else {
+                out.writeInt(objectsCount);
+                for (Slot slot : slotMap) {
+                    out.writeObject(slot);
+                }
             }
+        } finally {
+            slotMap.unlockRead(stamp);
         }
     }
 
-    private void readObject(ObjectInputStream in)
-        throws IOException, ClassNotFoundException
-    {
+    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
         in.defaultReadObject();
 
         int tableSize = in.readInt();
-        if (tableSize != 0) {
-            // If tableSize is not a power of 2 find the closest
-            // power of 2 >= the original size.
-            if ((tableSize & (tableSize - 1)) != 0) {
-                if (tableSize > 1 << 30)
-                    throw new RuntimeException("Property table overflow");
-                int newSize = INITIAL_SLOT_SIZE;
-                while (newSize < tableSize)
-                    newSize <<= 1;
-                tableSize = newSize;
-            }
-            slots = new Slot[tableSize];
-            int objectsCount = count;
-            if (objectsCount < 0) {
-                // "this" was sealed
-                objectsCount = ~objectsCount;
-            }
-            Slot prev = null;
-            for (int i=0; i != objectsCount; ++i) {
-                lastAdded = (Slot)in.readObject();
-                if (i==0) {
-                    firstAdded = lastAdded;
-                } else {
-                    prev.orderedNext = lastAdded;
-                }
-                int slotIndex = getSlotIndex(tableSize, lastAdded.indexOrHash);
-                addKnownAbsentSlot(slots, lastAdded, slotIndex);
-                prev = lastAdded;
-            }
+        slotMap = createSlotMap(tableSize);
+        for (int i = 0; i < tableSize; i++) {
+            Slot slot = (Slot) in.readObject();
+            slotMap.add(slot);
         }
     }
 
     protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) {
-        Slot slot = getSlot(cx, id, SLOT_QUERY);
+        Slot slot = querySlot(cx, id);
         if (slot == null) return null;
         Scriptable scope = getParentScope();
         return slot.getPropertyDescriptor(cx, (scope == null ? this : scope));
     }
 
-    protected Slot getSlot(Context cx, Object id, int accessType) {
+    protected Slot querySlot(Context cx, Object id) {
         if (id instanceof Symbol) {
-            return getSlot(id, 0, accessType);
+            return slotMap.query(id, 0);
         }
-        String name = ScriptRuntime.toStringIdOrIndex(cx, id);
-        if (name == null) {
-            return getSlot(null, ScriptRuntime.lastIndexResult(cx), accessType);
-        } else {
-            return getSlot(name, 0, accessType);
+        StringIdOrIndex s = ScriptRuntime.toStringIdOrIndex(cx, id);
+        if (s.stringId == null) {
+            return slotMap.query(null, s.index);
         }
+        return slotMap.query(s.stringId, 0);
     }
 
     // Partial implementation of java.util.Map. See NativeObject for
     // a subclass that implements java.util.Map.
 
     public int size() {
-        return count < 0 ? ~count : count;
+        return slotMap.size();
     }
 
     public boolean isEmpty() {
-        return count == 0 || count == -1;
+        return slotMap.isEmpty();
     }
 
-
     public Object get(Object key) {
         Object value = null;
         if (key instanceof String) {
@@ -3385,27 +2695,18 @@
     /**
      * This comparator sorts property fields in spec-compliant order. Numeric ids first, in numeric
      * order, followed by string ids, in insertion order. Since this class already keeps string keys
-     * in insertion-time order, we treat all as equal. The "Arrays.sort" method will then not
-     * change their order, but simply move all the numeric properties to the front, since this
-     * method is defined to be stable.
-     */
-    public static final class KeyComparator
-        implements Comparator<Object>
-    {
+     * in insertion-time order, we treat all as equal. The "Arrays.sort" method will then not change
+     * their order, but simply move all the numeric properties to the front, since this method is
+     * defined to be stable.
+     */
+    public static final class KeyComparator implements Comparator<Object>, Serializable {
+        private static final long serialVersionUID = 6411335891523988149L;
+
         @Override
-        public int compare(Object o1, Object o2)
-        {
+        public int compare(Object o1, Object o2) {
             if (o1 instanceof Integer) {
                 if (o2 instanceof Integer) {
-                    int i1 = (Integer) o1;
-                    int i2 = (Integer) o2;
-                    if (i1 < i2) {
-                        return -1;
-                    }
-                    if (i1 > i2) {
-                        return 1;
-                    }
-                    return 0;
+                    return ((Integer) o1).compareTo((Integer) o2);
                 }
                 return -1;
             }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ScriptRuntimeES6.java rhino-1.7.14/src/org/mozilla/javascript/ScriptRuntimeES6.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ScriptRuntimeES6.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ScriptRuntimeES6.java	2022-01-06 22:57:21.000000000 +0100
@@ -8,9 +8,9 @@
 
 public class ScriptRuntimeES6 {
 
-    public static Scriptable requireObjectCoercible(Context cx, Scriptable val, IdFunctionObject idFuncObj) {
+    public static Object requireObjectCoercible(Context cx, Object val, IdFunctionObject idFuncObj) {
         if (val == null || Undefined.isUndefined(val)) {
-            throw ScriptRuntime.typeError2("msg.called.null.or.undefined", idFuncObj.getTag(), idFuncObj.getFunctionName());
+            throw ScriptRuntime.typeErrorById("msg.called.null.or.undefined", idFuncObj.getTag(), idFuncObj.getFunctionName());
         }
         return val;
     }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ScriptRuntime.java rhino-1.7.14/src/org/mozilla/javascript/ScriptRuntime.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ScriptRuntime.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ScriptRuntime.java	2022-01-06 22:57:21.000000000 +0100
@@ -8,73 +8,70 @@
 
 import java.io.Serializable;
 import java.lang.reflect.Constructor;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.math.MathContext;
 import java.text.MessageFormat;
+import java.util.Arrays;
 import java.util.Locale;
 import java.util.ResourceBundle;
-
+import java.util.function.BiConsumer;
 import org.mozilla.javascript.ast.FunctionNode;
 import org.mozilla.javascript.v8dtoa.DoubleConversion;
 import org.mozilla.javascript.v8dtoa.FastDtoa;
-import org.mozilla.javascript.xml.XMLObject;
 import org.mozilla.javascript.xml.XMLLib;
+import org.mozilla.javascript.xml.XMLObject;
 
 /**
  * This is the class that implements the runtime.
  *
  * @author Norris Boyd
  */
-
 public class ScriptRuntime {
 
-    /**
-     * No instances should be created.
-     */
-    protected ScriptRuntime() {
-    }
-
+    /** No instances should be created. */
+    protected ScriptRuntime() {}
 
     /**
-     * Returns representation of the [[ThrowTypeError]] object.
-     * See ECMA 5 spec, 13.2.3
+     * Returns representation of the [[ThrowTypeError]] object. See ECMA 5 spec, 13.2.3
      *
      * @deprecated {@link #typeErrorThrower(Context)}
      */
     @Deprecated
     public static BaseFunction typeErrorThrower() {
-      return typeErrorThrower(Context.getCurrentContext());
+        return typeErrorThrower(Context.getCurrentContext());
     }
 
-    /**
-     * Returns representation of the [[ThrowTypeError]] object.
-     * See ECMA 5 spec, 13.2.3
-     */
+    /** Returns representation of the [[ThrowTypeError]] object. See ECMA 5 spec, 13.2.3 */
     public static BaseFunction typeErrorThrower(Context cx) {
-      if (cx.typeErrorThrower == null) {
-        BaseFunction thrower = new BaseFunction() {
-          static final long serialVersionUID = -5891740962154902286L;
-
-          @Override
-          public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
-            throw typeError0("msg.op.not.allowed");
-          }
-          @Override
-          public int getLength() {
-            return 0;
-          }
-        };
-        ScriptRuntime.setFunctionProtoAndParent(thrower, cx.topCallScope);
-        thrower.preventExtensions();
-        cx.typeErrorThrower = thrower;
-      }
-      return cx.typeErrorThrower;
+        if (cx.typeErrorThrower == null) {
+            BaseFunction thrower =
+                    new BaseFunction() {
+                        private static final long serialVersionUID = -5891740962154902286L;
+
+                        @Override
+                        public Object call(
+                                Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
+                            throw typeErrorById("msg.op.not.allowed");
+                        }
+
+                        @Override
+                        public int getLength() {
+                            return 0;
+                        }
+                    };
+            ScriptRuntime.setFunctionProtoAndParent(thrower, cx.topCallScope);
+            thrower.preventExtensions();
+            cx.typeErrorThrower = thrower;
+        }
+        return cx.typeErrorThrower;
     }
 
     static class NoSuchMethodShim implements Callable {
         String methodName;
         Callable noSuchMethodMethod;
 
-        NoSuchMethodShim(Callable noSuchMethodMethod, String methodName)
-        {
+        NoSuchMethodShim(Callable noSuchMethodMethod, String methodName) {
             this.noSuchMethodMethod = noSuchMethodMethod;
             this.methodName = methodName;
         }
@@ -87,16 +84,14 @@
          * @param args the array of arguments
          * @return the result of the call
          */
-        public Object call(Context cx, Scriptable scope, Scriptable thisObj,
-                           Object[] args)
-        {
+        @Override
+        public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
             Object[] nestedArgs = new Object[2];
 
             nestedArgs[0] = methodName;
             nestedArgs[1] = newArrayLiteral(args, null, cx, scope);
             return noSuchMethodMethod.call(cx, scope, thisObj, nestedArgs);
         }
-
     }
     /*
      * There's such a huge space (and some time) waste for the Foo.class
@@ -108,56 +103,46 @@
      * that they won't cause problems by being loaded early.
      */
 
-    public final static Class<?>
-        BooleanClass      = Kit.classOrNull("java.lang.Boolean"),
-        ByteClass         = Kit.classOrNull("java.lang.Byte"),
-        CharacterClass    = Kit.classOrNull("java.lang.Character"),
-        ClassClass        = Kit.classOrNull("java.lang.Class"),
-        DoubleClass       = Kit.classOrNull("java.lang.Double"),
-        FloatClass        = Kit.classOrNull("java.lang.Float"),
-        IntegerClass      = Kit.classOrNull("java.lang.Integer"),
-        LongClass         = Kit.classOrNull("java.lang.Long"),
-        NumberClass       = Kit.classOrNull("java.lang.Number"),
-        ObjectClass       = Kit.classOrNull("java.lang.Object"),
-        ShortClass        = Kit.classOrNull("java.lang.Short"),
-        StringClass       = Kit.classOrNull("java.lang.String"),
-        DateClass         = Kit.classOrNull("java.util.Date");
-
-    public final static Class<?>
-        ContextClass
-            = Kit.classOrNull("org.mozilla.javascript.Context"),
-        ContextFactoryClass
-            = Kit.classOrNull("org.mozilla.javascript.ContextFactory"),
-        FunctionClass
-            = Kit.classOrNull("org.mozilla.javascript.Function"),
-        ScriptableObjectClass
-            = Kit.classOrNull("org.mozilla.javascript.ScriptableObject");
-    public static final Class<Scriptable> ScriptableClass =
-        Scriptable.class;
-
-    // Locale object used to request locale-neutral operations.
-    public static Locale ROOT_LOCALE = new Locale("");
+    public static final Class<?> BooleanClass = Kit.classOrNull("java.lang.Boolean"),
+            ByteClass = Kit.classOrNull("java.lang.Byte"),
+            CharacterClass = Kit.classOrNull("java.lang.Character"),
+            ClassClass = Kit.classOrNull("java.lang.Class"),
+            DoubleClass = Kit.classOrNull("java.lang.Double"),
+            FloatClass = Kit.classOrNull("java.lang.Float"),
+            IntegerClass = Kit.classOrNull("java.lang.Integer"),
+            LongClass = Kit.classOrNull("java.lang.Long"),
+            NumberClass = Kit.classOrNull("java.lang.Number"),
+            ObjectClass = Kit.classOrNull("java.lang.Object"),
+            ShortClass = Kit.classOrNull("java.lang.Short"),
+            StringClass = Kit.classOrNull("java.lang.String"),
+            DateClass = Kit.classOrNull("java.util.Date"),
+            BigIntegerClass = Kit.classOrNull("java.math.BigInteger");
+    public static final Class<?> ContextClass = Kit.classOrNull("org.mozilla.javascript.Context"),
+            ContextFactoryClass = Kit.classOrNull("org.mozilla.javascript.ContextFactory"),
+            FunctionClass = Kit.classOrNull("org.mozilla.javascript.Function"),
+            ScriptableObjectClass = Kit.classOrNull("org.mozilla.javascript.ScriptableObject");
+    public static final Class<Scriptable> ScriptableClass = Scriptable.class;
 
     private static final Object LIBRARY_SCOPE_KEY = "LIBRARY_SCOPE";
 
-    public static boolean isRhinoRuntimeType(Class<?> cl)
-    {
+    public static boolean isRhinoRuntimeType(Class<?> cl) {
         if (cl.isPrimitive()) {
             return (cl != Character.TYPE);
-        } else {
-            return (cl == StringClass || cl == BooleanClass
-                    || NumberClass.isAssignableFrom(cl)
-                    || ScriptableClass.isAssignableFrom(cl));
         }
+        return (cl == StringClass
+                || cl == BooleanClass
+                || NumberClass.isAssignableFrom(cl)
+                || ScriptableClass.isAssignableFrom(cl));
     }
 
-    public static ScriptableObject initSafeStandardObjects(Context cx,
-                                                           ScriptableObject scope,
-                                                           boolean sealed)
-    {
+    public static ScriptableObject initSafeStandardObjects(
+            Context cx, ScriptableObject scope, boolean sealed) {
         if (scope == null) {
             scope = new NativeObject();
+        } else if (scope instanceof TopLevel) {
+            ((TopLevel) scope).clearCache();
         }
+
         scope.associateValue(LIBRARY_SCOPE_KEY, scope);
         (new ClassCache()).associate(scope);
 
@@ -171,8 +156,7 @@
         functionProto.setPrototype(objectProto);
 
         // Set the prototype of the object passed in if need be
-        if (scope.getPrototype() == null)
-            scope.setPrototype(objectProto);
+        if (scope.getPrototype() == null) scope.setPrototype(objectProto);
 
         // must precede NativeGlobal since it's needed therein
         NativeError.init(scope, sealed);
@@ -196,19 +180,22 @@
         NativeCall.init(scope, sealed);
         NativeScript.init(scope, sealed);
 
-        NativeIterator.init(scope, sealed); // Also initializes NativeGenerator
+        NativeIterator.init(cx, scope, sealed); // Also initializes NativeGenerator & ES6Generator
 
         NativeArrayIterator.init(scope, sealed);
         NativeStringIterator.init(scope, sealed);
 
-        boolean withXml = cx.hasFeature(Context.FEATURE_E4X) &&
-                          cx.getE4xImplementationFactory() != null;
+        NativeJavaObject.init(scope, sealed);
+        NativeJavaMap.init(scope, sealed);
+
+        boolean withXml =
+                cx.hasFeature(Context.FEATURE_E4X) && cx.getE4xImplementationFactory() != null;
 
         // define lazy-loaded properties using their class name
-        new LazilyLoadedCtor(scope, "RegExp",
-                "org.mozilla.javascript.regexp.NativeRegExp", sealed, true);
-        new LazilyLoadedCtor(scope, "Continuation",
-                "org.mozilla.javascript.NativeContinuation", sealed, true);
+        new LazilyLoadedCtor(
+                scope, "RegExp", "org.mozilla.javascript.regexp.NativeRegExp", sealed, true);
+        new LazilyLoadedCtor(
+                scope, "Continuation", "org.mozilla.javascript.NativeContinuation", sealed, true);
 
         if (withXml) {
             String xmlImpl = cx.getE4xImplementationFactory().getImplementationClassName();
@@ -218,74 +205,111 @@
             new LazilyLoadedCtor(scope, "QName", xmlImpl, sealed, true);
         }
 
-        if (((cx.getLanguageVersion() >= Context.VERSION_1_8) &&
-             cx.hasFeature(Context.FEATURE_V8_EXTENSIONS)) ||
-            (cx.getLanguageVersion() >= Context.VERSION_ES6))
-        {
-            new LazilyLoadedCtor(scope, "ArrayBuffer",
-                                 "org.mozilla.javascript.typedarrays.NativeArrayBuffer",
-                                 sealed, true);
-            new LazilyLoadedCtor(scope, "Int8Array",
-                                 "org.mozilla.javascript.typedarrays.NativeInt8Array",
-                                 sealed, true);
-            new LazilyLoadedCtor(scope, "Uint8Array",
-                                 "org.mozilla.javascript.typedarrays.NativeUint8Array",
-                                 sealed, true);
-            new LazilyLoadedCtor(scope, "Uint8ClampedArray",
-                                 "org.mozilla.javascript.typedarrays.NativeUint8ClampedArray",
-                                 sealed, true);
-            new LazilyLoadedCtor(scope, "Int16Array",
-                                 "org.mozilla.javascript.typedarrays.NativeInt16Array",
-                                 sealed, true);
-            new LazilyLoadedCtor(scope, "Uint16Array",
-                                 "org.mozilla.javascript.typedarrays.NativeUint16Array",
-                                 sealed, true);
-            new LazilyLoadedCtor(scope, "Int32Array",
-                                 "org.mozilla.javascript.typedarrays.NativeInt32Array",
-                                 sealed, true);
-            new LazilyLoadedCtor(scope, "Uint32Array",
-                                 "org.mozilla.javascript.typedarrays.NativeUint32Array",
-                                 sealed, true);
-            new LazilyLoadedCtor(scope, "Float32Array",
-                                 "org.mozilla.javascript.typedarrays.NativeFloat32Array",
-                                 sealed, true);
-            new LazilyLoadedCtor(scope, "Float64Array",
-                                 "org.mozilla.javascript.typedarrays.NativeFloat64Array",
-                                 sealed, true);
-            new LazilyLoadedCtor(scope, "DataView",
-                                 "org.mozilla.javascript.typedarrays.NativeDataView",
-                                 sealed, true);
+        if (((cx.getLanguageVersion() >= Context.VERSION_1_8)
+                        && cx.hasFeature(Context.FEATURE_V8_EXTENSIONS))
+                || (cx.getLanguageVersion() >= Context.VERSION_ES6)) {
+            new LazilyLoadedCtor(
+                    scope,
+                    "ArrayBuffer",
+                    "org.mozilla.javascript.typedarrays.NativeArrayBuffer",
+                    sealed,
+                    true);
+            new LazilyLoadedCtor(
+                    scope,
+                    "Int8Array",
+                    "org.mozilla.javascript.typedarrays.NativeInt8Array",
+                    sealed,
+                    true);
+            new LazilyLoadedCtor(
+                    scope,
+                    "Uint8Array",
+                    "org.mozilla.javascript.typedarrays.NativeUint8Array",
+                    sealed,
+                    true);
+            new LazilyLoadedCtor(
+                    scope,
+                    "Uint8ClampedArray",
+                    "org.mozilla.javascript.typedarrays.NativeUint8ClampedArray",
+                    sealed,
+                    true);
+            new LazilyLoadedCtor(
+                    scope,
+                    "Int16Array",
+                    "org.mozilla.javascript.typedarrays.NativeInt16Array",
+                    sealed,
+                    true);
+            new LazilyLoadedCtor(
+                    scope,
+                    "Uint16Array",
+                    "org.mozilla.javascript.typedarrays.NativeUint16Array",
+                    sealed,
+                    true);
+            new LazilyLoadedCtor(
+                    scope,
+                    "Int32Array",
+                    "org.mozilla.javascript.typedarrays.NativeInt32Array",
+                    sealed,
+                    true);
+            new LazilyLoadedCtor(
+                    scope,
+                    "Uint32Array",
+                    "org.mozilla.javascript.typedarrays.NativeUint32Array",
+                    sealed,
+                    true);
+            new LazilyLoadedCtor(
+                    scope,
+                    "Float32Array",
+                    "org.mozilla.javascript.typedarrays.NativeFloat32Array",
+                    sealed,
+                    true);
+            new LazilyLoadedCtor(
+                    scope,
+                    "Float64Array",
+                    "org.mozilla.javascript.typedarrays.NativeFloat64Array",
+                    sealed,
+                    true);
+            new LazilyLoadedCtor(
+                    scope,
+                    "DataView",
+                    "org.mozilla.javascript.typedarrays.NativeDataView",
+                    sealed,
+                    true);
         }
 
         if (cx.getLanguageVersion() >= Context.VERSION_ES6) {
             NativeSymbol.init(cx, scope, sealed);
+            NativeCollectionIterator.init(scope, NativeSet.ITERATOR_TAG, sealed);
+            NativeCollectionIterator.init(scope, NativeMap.ITERATOR_TAG, sealed);
+            NativeMap.init(cx, scope, sealed);
+            NativePromise.init(cx, scope, sealed);
+            NativeSet.init(cx, scope, sealed);
+            NativeWeakMap.init(scope, sealed);
+            NativeWeakSet.init(scope, sealed);
+            NativeBigInt.init(scope, sealed);
         }
 
         if (scope instanceof TopLevel) {
-            ((TopLevel)scope).cacheBuiltins();
+            ((TopLevel) scope).cacheBuiltins(scope, sealed);
         }
 
         return scope;
     }
 
-    public static ScriptableObject initStandardObjects(Context cx,
-                                                       ScriptableObject scope,
-                                                       boolean sealed)
-    {
+    public static ScriptableObject initStandardObjects(
+            Context cx, ScriptableObject scope, boolean sealed) {
         ScriptableObject s = initSafeStandardObjects(cx, scope, sealed);
 
-        new LazilyLoadedCtor(s, "Packages",
-                "org.mozilla.javascript.NativeJavaTopPackage", sealed, true);
-        new LazilyLoadedCtor(s, "getClass",
-                "org.mozilla.javascript.NativeJavaTopPackage", sealed, true);
-        new LazilyLoadedCtor(s, "JavaAdapter",
-                "org.mozilla.javascript.JavaAdapter", sealed, true);
-        new LazilyLoadedCtor(s, "JavaImporter",
-                "org.mozilla.javascript.ImporterTopLevel", sealed, true);
+        new LazilyLoadedCtor(
+                s, "Packages", "org.mozilla.javascript.NativeJavaTopPackage", sealed, true);
+        new LazilyLoadedCtor(
+                s, "getClass", "org.mozilla.javascript.NativeJavaTopPackage", sealed, true);
+        new LazilyLoadedCtor(s, "JavaAdapter", "org.mozilla.javascript.JavaAdapter", sealed, true);
+        new LazilyLoadedCtor(
+                s, "JavaImporter", "org.mozilla.javascript.ImporterTopLevel", sealed, true);
 
         for (String packageName : getTopPackageNames()) {
-            new LazilyLoadedCtor(s, packageName,
-                    "org.mozilla.javascript.NativeJavaTopPackage", sealed, true);
+            new LazilyLoadedCtor(
+                    s, packageName, "org.mozilla.javascript.NativeJavaTopPackage", sealed, true);
         }
 
         return s;
@@ -293,22 +317,19 @@
 
     static String[] getTopPackageNames() {
         // Include "android" top package if running on Android
-        return "Dalvik".equals(System.getProperty("java.vm.name")) ?
-            new String[] { "java", "javax", "org", "com", "edu", "net", "android" } :
-            new String[] { "java", "javax", "org", "com", "edu", "net" };
+        return "Dalvik".equals(System.getProperty("java.vm.name"))
+                ? new String[] {"java", "javax", "org", "com", "edu", "net", "android"}
+                : new String[] {"java", "javax", "org", "com", "edu", "net"};
     }
 
-    public static ScriptableObject getLibraryScopeOrNull(Scriptable scope)
-    {
+    public static ScriptableObject getLibraryScopeOrNull(Scriptable scope) {
         ScriptableObject libScope;
-        libScope = (ScriptableObject)ScriptableObject.
-                       getTopScopeValue(scope, LIBRARY_SCOPE_KEY);
+        libScope = (ScriptableObject) ScriptableObject.getTopScopeValue(scope, LIBRARY_SCOPE_KEY);
         return libScope;
     }
 
     // It is public so NativeRegExp can access it.
-    public static boolean isJSLineTerminator(int c)
-    {
+    public static boolean isJSLineTerminator(int c) {
         // Optimization for faster check for eol character:
         // they do not have 0xDFD0 bits set
         if ((c & 0xDFD0) != 0) {
@@ -318,26 +339,14 @@
     }
 
     public static boolean isJSWhitespaceOrLineTerminator(int c) {
-      return (isStrWhiteSpaceChar(c) || isJSLineTerminator(c));
+        return (isStrWhiteSpaceChar(c) || isJSLineTerminator(c));
     }
 
     /**
-     * Indicates if the character is a Str whitespace char according to ECMA spec:
-     * StrWhiteSpaceChar :::
-      <TAB>
-      <SP>
-      <NBSP>
-      <FF>
-      <VT>
-      <CR>
-      <LF>
-      <LS>
-      <PS>
-      <USP>
-      <BOM>
+     * Indicates if the character is a Str whitespace char according to ECMA spec: StrWhiteSpaceChar
+     * ::: <TAB> <SP> <NBSP> <FF> <VT> <CR> <LF> <LS> <PS> <USP> <BOM>
      */
-    static boolean isStrWhiteSpaceChar(int c)
-    {
+    static boolean isStrWhiteSpaceChar(int c) {
         switch (c) {
             case ' ': // <SP>
             case '\n': // <LF>
@@ -355,46 +364,41 @@
         }
     }
 
-    public static Boolean wrapBoolean(boolean b)
-    {
-        return b ? Boolean.TRUE : Boolean.FALSE;
+    public static Boolean wrapBoolean(boolean b) {
+        return Boolean.valueOf(b);
     }
 
-    public static Integer wrapInt(int i)
-    {
+    public static Integer wrapInt(int i) {
         return Integer.valueOf(i);
     }
 
-    public static Number wrapNumber(double x)
-    {
-        if (x != x) {
+    public static Number wrapNumber(double x) {
+        if (Double.isNaN(x)) {
             return ScriptRuntime.NaNobj;
         }
-        return new Double(x);
+        return Double.valueOf(x);
     }
 
     /**
      * Convert the value to a boolean.
      *
-     * See ECMA 9.2.
+     * <p>See ECMA 9.2.
      */
-    public static boolean toBoolean(Object val)
-    {
-        for (;;) {
-            if (val instanceof Boolean)
-                return ((Boolean) val).booleanValue();
-            if (val == null || val == Undefined.instance)
-                return false;
-            if (val instanceof CharSequence)
-                return ((CharSequence) val).length() != 0;
+    public static boolean toBoolean(Object val) {
+        for (; ; ) {
+            if (val instanceof Boolean) return ((Boolean) val).booleanValue();
+            if (val == null || Undefined.isUndefined(val)) return false;
+            if (val instanceof CharSequence) return ((CharSequence) val).length() != 0;
+            if (val instanceof BigInteger) {
+                return !((BigInteger) val).equals(BigInteger.ZERO);
+            }
             if (val instanceof Number) {
                 double d = ((Number) val).doubleValue();
-                return (d == d && d != 0.0);
+                return (!Double.isNaN(d) && d != 0.0);
             }
             if (val instanceof Scriptable) {
-                if (val instanceof ScriptableObject &&
-                    ((ScriptableObject) val).avoidObjectDetection())
-                {
+                if (val instanceof ScriptableObject
+                        && ((ScriptableObject) val).avoidObjectDetection()) {
                     return false;
                 }
                 if (Context.getContext().isVersionECMA1()) {
@@ -415,25 +419,20 @@
     /**
      * Convert the value to a number.
      *
-     * See ECMA 9.3.
+     * <p>See ECMA 9.3.
      */
-    public static double toNumber(Object val)
-    {
-        for (;;) {
-            if (val instanceof Number)
-                return ((Number) val).doubleValue();
-            if (val == null)
-                return +0.0;
-            if (val == Undefined.instance)
-                return NaN;
-            if (val instanceof String)
-                return toNumber((String) val);
-            if (val instanceof CharSequence)
-                return toNumber(val.toString());
-            if (val instanceof Boolean)
-                return ((Boolean) val).booleanValue() ? 1 : +0.0;
-            if (val instanceof Symbol)
-                throw typeError0("msg.not.a.number");
+    public static double toNumber(Object val) {
+        for (; ; ) {
+            if (val instanceof BigInteger) {
+                throw typeErrorById("msg.cant.convert.to.number", "BigInt");
+            }
+            if (val instanceof Number) return ((Number) val).doubleValue();
+            if (val == null) return +0.0;
+            if (Undefined.isUndefined(val)) return NaN;
+            if (val instanceof String) return toNumber((String) val);
+            if (val instanceof CharSequence) return toNumber(val.toString());
+            if (val instanceof Boolean) return ((Boolean) val).booleanValue() ? 1 : +0.0;
+            if (val instanceof Symbol) throw typeErrorById("msg.not.a.number");
             if (val instanceof Scriptable) {
                 val = ((Scriptable) val).getDefaultValue(NumberClass);
                 if ((val instanceof Scriptable) && !isSymbol(val))
@@ -449,27 +448,31 @@
         return (index < args.length) ? toNumber(args[index]) : NaN;
     }
 
-    // Can not use Double.NaN defined as 0.0d / 0.0 as under the Microsoft VM,
-    // versions 2.01 and 3.0P1, that causes some uses (returns at least) of
-    // Double.NaN to be converted to 1.0.
-    // So we use ScriptRuntime.NaN instead of Double.NaN.
-    public static final double
-        NaN = Double.longBitsToDouble(0x7ff8000000000000L);
-
-    // A similar problem exists for negative zero.
-    public static final double
-        negativeZero = Double.longBitsToDouble(0x8000000000000000L);
+    public static final double NaN = Double.NaN;
+    public static final Double NaNobj = Double.valueOf(NaN);
+
+    // Preserve backward-compatibility with historical value of this.
+    public static final double negativeZero = Double.longBitsToDouble(0x8000000000000000L);
+
+    public static final Double zeroObj = Double.valueOf(0.0);
+    public static final Double negativeZeroObj = Double.valueOf(-0.0);
+
+    static double stringPrefixToNumber(String s, int start, int radix) {
+        return stringToNumber(s, start, s.length() - 1, radix, true);
+    }
 
-    public static final Double NaNobj = new Double(NaN);
+    static double stringToNumber(String s, int start, int end, int radix) {
+        return stringToNumber(s, start, end, radix, false);
+    }
 
     /*
      * Helper function for toNumber, parseInt, and TokenStream.getToken.
      */
-    static double stringToNumber(String s, int start, int radix) {
+    private static double stringToNumber(
+            String source, int sourceStart, int sourceEnd, int radix, boolean isPrefix) {
         char digitMax = '9';
         char lowerCaseBound = 'a';
         char upperCaseBound = 'A';
-        int len = s.length();
         if (radix < 10) {
             digitMax = (char) ('0' + radix - 1);
         }
@@ -479,23 +482,20 @@
         }
         int end;
         double sum = 0.0;
-        for (end=start; end < len; end++) {
-            char c = s.charAt(end);
+        for (end = sourceStart; end <= sourceEnd; end++) {
+            char c = source.charAt(end);
             int newDigit;
-            if ('0' <= c && c <= digitMax)
-                newDigit = c - '0';
-            else if ('a' <= c && c < lowerCaseBound)
-                newDigit = c - 'a' + 10;
-            else if ('A' <= c && c < upperCaseBound)
-                newDigit = c - 'A' + 10;
-            else
-                break;
-            sum = sum*radix + newDigit;
+            if ('0' <= c && c <= digitMax) newDigit = c - '0';
+            else if ('a' <= c && c < lowerCaseBound) newDigit = c - 'a' + 10;
+            else if ('A' <= c && c < upperCaseBound) newDigit = c - 'A' + 10;
+            else if (!isPrefix) return NaN; // isn't a prefix but found unexpected char
+            else break; // unexpected char
+            sum = sum * radix + newDigit;
         }
-        if (start == end) {
+        if (sourceStart == end) { // stopped right at the beginning
             return NaN;
         }
-        if (sum >= 9007199254740992.0) {
+        if (sum > NativeNumber.MAX_SAFE_INTEGER) {
             if (radix == 10) {
                 /* If we're accumulating a decimal number and the number
                  * is >= 2^53, then the result from the repeated multiply-add
@@ -503,13 +503,11 @@
                  * answer.
                  */
                 try {
-                    return Double.parseDouble(s.substring(start, end));
+                    return Double.parseDouble(source.substring(sourceStart, end));
                 } catch (NumberFormatException nfe) {
                     return NaN;
                 }
-            } else if (radix == 2 || radix == 4 || radix == 8 ||
-                       radix == 16 || radix == 32)
-            {
+            } else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32) {
                 /* The number may also be inaccurate for one of these bases.
                  * This happens if the addition in value*radix + digit causes
                  * a round-down to an even least significant mantissa bit
@@ -525,7 +523,7 @@
 
                 final int SKIP_LEADING_ZEROS = 0;
                 final int FIRST_EXACT_53_BITS = 1;
-                final int AFTER_BIT_53         = 2;
+                final int AFTER_BIT_53 = 2;
                 final int ZEROS_AFTER_54 = 3;
                 final int MIXED_AFTER_54 = 4;
 
@@ -535,78 +533,72 @@
                 boolean bit53 = false;
                 // bit54 is the 54th bit (the first dropped from the mantissa)
                 boolean bit54 = false;
+                int pos = sourceStart;
 
-                for (;;) {
+                for (; ; ) {
                     if (bitShiftInChar == 1) {
-                        if (start == end)
-                            break;
-                        digit = s.charAt(start++);
-                        if ('0' <= digit && digit <= '9')
-                            digit -= '0';
-                        else if ('a' <= digit && digit <= 'z')
-                            digit -= 'a' - 10;
-                        else
-                            digit -= 'A' - 10;
+                        if (pos == end) break;
+                        digit = source.charAt(pos++);
+                        if ('0' <= digit && digit <= '9') digit -= '0';
+                        else if ('a' <= digit && digit <= 'z') digit -= 'a' - 10;
+                        else digit -= 'A' - 10;
                         bitShiftInChar = radix;
                     }
                     bitShiftInChar >>= 1;
                     boolean bit = (digit & bitShiftInChar) != 0;
 
                     switch (state) {
-                      case SKIP_LEADING_ZEROS:
-                          if (bit) {
+                        case SKIP_LEADING_ZEROS:
+                            if (bit) {
+                                --exactBitsLimit;
+                                sum = 1.0;
+                                state = FIRST_EXACT_53_BITS;
+                            }
+                            break;
+                        case FIRST_EXACT_53_BITS:
+                            sum *= 2.0;
+                            if (bit) sum += 1.0;
                             --exactBitsLimit;
-                            sum = 1.0;
-                            state = FIRST_EXACT_53_BITS;
-                        }
+                            if (exactBitsLimit == 0) {
+                                bit53 = bit;
+                                state = AFTER_BIT_53;
+                            }
+                            break;
+                        case AFTER_BIT_53:
+                            bit54 = bit;
+                            factor = 2.0;
+                            state = ZEROS_AFTER_54;
+                            break;
+                        case ZEROS_AFTER_54:
+                            if (bit) {
+                                state = MIXED_AFTER_54;
+                            }
+                            // fallthrough
+                        case MIXED_AFTER_54:
+                            factor *= 2;
+                            break;
+                    }
+                }
+                switch (state) {
+                    case SKIP_LEADING_ZEROS:
+                        sum = 0.0;
                         break;
-                      case FIRST_EXACT_53_BITS:
-                           sum *= 2.0;
-                        if (bit)
-                            sum += 1.0;
-                        --exactBitsLimit;
-                        if (exactBitsLimit == 0) {
-                            bit53 = bit;
-                            state = AFTER_BIT_53;
-                        }
+                    case FIRST_EXACT_53_BITS:
+                    case AFTER_BIT_53:
+                        // do nothing
                         break;
-                      case AFTER_BIT_53:
-                        bit54 = bit;
-                        factor = 2.0;
-                        state = ZEROS_AFTER_54;
+                    case ZEROS_AFTER_54:
+                        // x1.1 -> x1 + 1 (round up)
+                        // x0.1 -> x0 (round down)
+                        if (bit54 & bit53) sum += 1.0;
+                        sum *= factor;
                         break;
-                      case ZEROS_AFTER_54:
-                        if (bit) {
-                            state = MIXED_AFTER_54;
-                        }
-                        // fallthrough
-                      case MIXED_AFTER_54:
-                        factor *= 2;
+                    case MIXED_AFTER_54:
+                        // x.100...1.. -> x + 1 (round up)
+                        // x.0anything -> x (round down)
+                        if (bit54) sum += 1.0;
+                        sum *= factor;
                         break;
-                    }
-                }
-                switch (state) {
-                  case SKIP_LEADING_ZEROS:
-                    sum = 0.0;
-                    break;
-                  case FIRST_EXACT_53_BITS:
-                  case AFTER_BIT_53:
-                    // do nothing
-                    break;
-                  case ZEROS_AFTER_54:
-                    // x1.1 -> x1 + 1 (round up)
-                    // x0.1 -> x0 (round down)
-                    if (bit54 & bit53)
-                        sum += 1.0;
-                    sum *= factor;
-                    break;
-                  case MIXED_AFTER_54:
-                    // x.100...1.. -> x + 1 (round up)
-                    // x.0anything -> x (round down)
-                    if (bit54)
-                        sum += 1.0;
-                    sum *= factor;
-                    break;
                 }
             }
             /* We don't worry about inaccurate numbers for any other base. */
@@ -614,70 +606,97 @@
         return sum;
     }
 
-
     /**
      * ToNumber applied to the String type
      *
-     * See ECMA 9.3.1
+     * <p>See the #sec-tonumber-applied-to-the-string-type section of ECMA
      */
     public static double toNumber(String s) {
-        int len = s.length();
+        final int len = s.length();
+
+        // Skip whitespace at the start
         int start = 0;
         char startChar;
-        for (;;) {
+        for (; ; ) {
             if (start == len) {
-                // Empty or contains only whitespace
+                // empty or contains only whitespace
                 return +0.0;
             }
             startChar = s.charAt(start);
-            if (!ScriptRuntime.isStrWhiteSpaceChar(startChar))
+            if (!ScriptRuntime.isStrWhiteSpaceChar(startChar)) {
+                // found first non-whitespace character
                 break;
+            }
             start++;
         }
 
+        // Skip whitespace at the end
+        int end = len - 1;
+        char endChar;
+        while (ScriptRuntime.isStrWhiteSpaceChar(endChar = s.charAt(end))) {
+            end--;
+        }
+
+        // Do not break scripts relying on old non-compliant conversion
+        // (see bug #368)
+        // 1. makes ToNumber parse only a valid prefix in hex literals (similar to 'parseInt()')
+        //    ToNumber('0x10 something') => 16
+        // 2. allows plus and minus signs for hexadecimal numbers
+        //    ToNumber('-0x10') => -16
+        // 3. disables support for binary ('0b10') and octal ('0o13') literals
+        //    ToNumber('0b1') => NaN
+        //    ToNumber('0o5') => NaN
+        final Context cx = Context.getCurrentContext();
+        final boolean oldParsingMode = cx == null || cx.getLanguageVersion() < Context.VERSION_ES6;
+
+        // Handle non-base10 numbers
         if (startChar == '0') {
-            if (start + 2 < len) {
-                int c1 = s.charAt(start + 1);
-                if (c1 == 'x' || c1 == 'X') {
-                    // A hexadecimal number
-                    return stringToNumber(s, start + 2, 16);
+            if (start + 2 <= end) {
+                final char radixC = s.charAt(start + 1);
+                int radix = -1;
+                if (radixC == 'x' || radixC == 'X') {
+                    radix = 16;
+                } else if (!oldParsingMode && (radixC == 'o' || radixC == 'O')) {
+                    radix = 8;
+                } else if (!oldParsingMode && (radixC == 'b' || radixC == 'B')) {
+                    radix = 2;
+                }
+                if (radix != -1) {
+                    if (oldParsingMode) {
+                        return stringPrefixToNumber(s, start + 2, radix);
+                    }
+                    return stringToNumber(s, start + 2, end, radix);
                 }
             }
-        } else if (startChar == '+' || startChar == '-') {
-            if (start + 3 < len && s.charAt(start + 1) == '0') {
-                int c2 = s.charAt(start + 2);
-                if (c2 == 'x' || c2 == 'X') {
-                    // A hexadecimal number with sign
-                    double val = stringToNumber(s, start + 3, 16);
+        } else if (oldParsingMode && (startChar == '+' || startChar == '-')) {
+            // If in old parsing mode, check for a signed hexadecimal number
+            if (start + 3 <= end && s.charAt(start + 1) == '0') {
+                final char radixC = s.charAt(start + 2);
+                if (radixC == 'x' || radixC == 'X') {
+                    double val = stringPrefixToNumber(s, start + 3, 16);
                     return startChar == '-' ? -val : val;
                 }
             }
         }
 
-        int end = len - 1;
-        char endChar;
-        while (ScriptRuntime.isStrWhiteSpaceChar(endChar = s.charAt(end)))
-            end--;
         if (endChar == 'y') {
             // check for "Infinity"
-            if (startChar == '+' || startChar == '-')
+            if (startChar == '+' || startChar == '-') {
                 start++;
-            if (start + 7 == end && s.regionMatches(start, "Infinity", 0, 8))
-                return startChar == '-'
-                    ? Double.NEGATIVE_INFINITY
-                    : Double.POSITIVE_INFINITY;
+            }
+            if (start + 7 == end && s.regionMatches(start, "Infinity", 0, 8)) {
+                return startChar == '-' ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
+            }
             return NaN;
         }
-        // A non-hexadecimal, non-infinity number:
+        // A base10, non-infinity number:
         // just try a normal floating point conversion
-        String sub = s.substring(start, end+1);
+        String sub = s.substring(start, end + 1);
         // Quick test to check string contains only valid characters because
         // Double.parseDouble() can be slow and accept input we want to reject
         for (int i = sub.length() - 1; i >= 0; i--) {
             char c = sub.charAt(i);
-            if (('0' <= c && c <= '9') || c == '.' ||
-                    c == 'e' || c == 'E'  ||
-                    c == '+' || c == '-')
+            if (('0' <= c && c <= '9') || c == '.' || c == 'e' || c == 'E' || c == '+' || c == '-')
                 continue;
             return NaN;
         }
@@ -688,51 +707,193 @@
         }
     }
 
+    /** Convert the value to a BigInt. */
+    public static BigInteger toBigInt(Object val) {
+        for (; ; ) {
+            if (val instanceof BigInteger) {
+                return (BigInteger) val;
+            }
+            if (val instanceof BigDecimal) {
+                return ((BigDecimal) val).toBigInteger();
+            }
+            if (val instanceof Number) {
+                if (val instanceof Long) {
+                    return BigInteger.valueOf(((Long) val));
+                } else {
+                    double d = ((Number) val).doubleValue();
+                    if (Double.isNaN(d) || Double.isInfinite(d)) {
+                        throw rangeErrorById(
+                                "msg.cant.convert.to.bigint.isnt.integer", toString(val));
+                    }
+                    BigDecimal bd = new BigDecimal(d, MathContext.UNLIMITED);
+                    try {
+                        return bd.toBigIntegerExact();
+                    } catch (ArithmeticException e) {
+                        throw rangeErrorById(
+                                "msg.cant.convert.to.bigint.isnt.integer", toString(val));
+                    }
+                }
+            }
+            if (val == null || Undefined.isUndefined(val)) {
+                throw typeErrorById("msg.cant.convert.to.bigint", toString(val));
+            }
+            if (val instanceof String) {
+                return toBigInt((String) val);
+            }
+            if (val instanceof CharSequence) {
+                return toBigInt(val.toString());
+            }
+            if (val instanceof Boolean) {
+                return ((Boolean) val).booleanValue() ? BigInteger.ONE : BigInteger.ZERO;
+            }
+            if (val instanceof Symbol) {
+                throw typeErrorById("msg.cant.convert.to.bigint", toString(val));
+            }
+            if (val instanceof Scriptable) {
+                val = ((Scriptable) val).getDefaultValue(BigIntegerClass);
+                if ((val instanceof Scriptable) && !isSymbol(val)) {
+                    throw errorWithClassName("msg.primitive.expected", val);
+                }
+                continue;
+            }
+            warnAboutNonJSObject(val);
+            return BigInteger.ZERO;
+        }
+    }
+
+    /** ToBigInt applied to the String type */
+    public static BigInteger toBigInt(String s) {
+        final int len = s.length();
+
+        // Skip whitespace at the start
+        int start = 0;
+        char startChar;
+        for (; ; ) {
+            if (start == len) {
+                // empty or contains only whitespace
+                return BigInteger.ZERO;
+            }
+            startChar = s.charAt(start);
+            if (!ScriptRuntime.isStrWhiteSpaceChar(startChar)) {
+                // found first non-whitespace character
+                break;
+            }
+            start++;
+        }
+
+        // Skip whitespace at the end
+        int end = len - 1;
+        while (ScriptRuntime.isStrWhiteSpaceChar(s.charAt(end))) {
+            end--;
+        }
+
+        // Handle non-base10 numbers
+        if (startChar == '0') {
+            if (start + 2 <= end) {
+                final char radixC = s.charAt(start + 1);
+                int radix = -1;
+                if (radixC == 'x' || radixC == 'X') {
+                    radix = 16;
+                } else if (radixC == 'o' || radixC == 'O') {
+                    radix = 8;
+                } else if (radixC == 'b' || radixC == 'B') {
+                    radix = 2;
+                }
+                if (radix != -1) {
+                    try {
+                        return new BigInteger(s.substring(start + 2, end + 1), radix);
+                    } catch (NumberFormatException ex) {
+                        throw syntaxErrorById("msg.bigint.bad.form");
+                    }
+                }
+            }
+        }
+
+        // A base10, non-infinity bigint:
+        // just try a normal biginteger conversion
+        String sub = s.substring(start, end + 1);
+        for (int i = sub.length() - 1; i >= 0; i--) {
+            char c = sub.charAt(i);
+            if (i == 0 && (c == '+' || c == '-')) {
+                continue;
+            }
+            if ('0' <= c && c <= '9') {
+                continue;
+            }
+            throw syntaxErrorById("msg.bigint.bad.form");
+        }
+        try {
+            return new BigInteger(sub);
+        } catch (NumberFormatException ex) {
+            throw syntaxErrorById("msg.bigint.bad.form");
+        }
+    }
+
+    /**
+     * Convert the value to a Numeric (Number or BigInt).
+     *
+     * <p>toNumber does not allow java.math.BigInteger. toNumeric allows java.math.BigInteger.
+     *
+     * <p>See ECMA 7.1.3 (v11.0).
+     */
+    public static Number toNumeric(Object val) {
+        if (val instanceof Number) {
+            return (Number) val;
+        }
+        return toNumber(val);
+    }
+
+    public static int toIndex(Object val) {
+        if (Undefined.isUndefined(val)) {
+            return 0;
+        }
+        double integerIndex = toInteger(val);
+        if (integerIndex < 0) {
+            throw rangeError("index out of range");
+        }
+        // ToLength
+        double index = Math.min(integerIndex, NativeNumber.MAX_SAFE_INTEGER);
+        if (integerIndex != index) {
+            throw rangeError("index out of range");
+        }
+        return (int) index;
+    }
+
     /**
-     * Helper function for builtin objects that use the varargs form.
-     * ECMA function formal arguments are undefined if not supplied;
-     * this function pads the argument array out to the expected
+     * Helper function for builtin objects that use the varargs form. ECMA function formal arguments
+     * are undefined if not supplied; this function pads the argument array out to the expected
      * length, if necessary.
      */
     public static Object[] padArguments(Object[] args, int count) {
-        if (count < args.length)
-            return args;
+        if (count < args.length) return args;
 
-        int i;
         Object[] result = new Object[count];
-        for (i = 0; i < args.length; i++) {
-            result[i] = args[i];
+        System.arraycopy(args, 0, result, 0, args.length);
+        if (args.length < count) {
+            Arrays.fill(result, args.length, count, Undefined.instance);
         }
-
-        for (; i < count; i++) {
-            result[i] = Undefined.instance;
-        }
-
         return result;
     }
 
-    public static String escapeString(String s)
-    {
+    public static String escapeString(String s) {
         return escapeString(s, '"');
     }
 
     /**
-     * For escaping strings printed by object and array literals; not quite
-     * the same as 'escape.'
+     * For escaping strings printed by object and array literals; not quite the same as 'escape.'
      */
-    public static String escapeString(String s, char escapeQuote)
-    {
+    public static String escapeString(String s, char escapeQuote) {
         if (!(escapeQuote == '"' || escapeQuote == '\'')) Kit.codeBug();
         StringBuilder sb = null;
 
-        for(int i = 0, L = s.length(); i != L; ++i) {
+        for (int i = 0, L = s.length(); i != L; ++i) {
             int c = s.charAt(i);
 
             if (' ' <= c && c <= '~' && c != escapeQuote && c != '\\') {
                 // an ordinary print character (like C isprint()) and not "
                 // or \ .
                 if (sb != null) {
-                    sb.append((char)c);
+                    sb.append((char) c);
                 }
                 continue;
             }
@@ -744,19 +905,35 @@
 
             int escape = -1;
             switch (c) {
-                case '\b':  escape = 'b';  break;
-                case '\f':  escape = 'f';  break;
-                case '\n':  escape = 'n';  break;
-                case '\r':  escape = 'r';  break;
-                case '\t':  escape = 't';  break;
-                case 0xb:   escape = 'v';  break; // Java lacks \v.
-                case ' ':   escape = ' ';  break;
-                case '\\':  escape = '\\'; break;
+                case '\b':
+                    escape = 'b';
+                    break;
+                case '\f':
+                    escape = 'f';
+                    break;
+                case '\n':
+                    escape = 'n';
+                    break;
+                case '\r':
+                    escape = 'r';
+                    break;
+                case '\t':
+                    escape = 't';
+                    break;
+                case 0xb:
+                    escape = 'v';
+                    break; // Java lacks \v.
+                case ' ':
+                    escape = ' ';
+                    break;
+                case '\\':
+                    escape = '\\';
+                    break;
             }
             if (escape >= 0) {
                 // an \escaped sort of character
                 sb.append('\\');
-                sb.append((char)escape);
+                sb.append((char) escape);
             } else if (c == escapeQuote) {
                 sb.append('\\');
                 sb.append(escapeQuote);
@@ -775,30 +952,26 @@
                 for (int shift = (hexSize - 1) * 4; shift >= 0; shift -= 4) {
                     int digit = 0xf & (c >> shift);
                     int hc = (digit < 10) ? '0' + digit : 'a' - 10 + digit;
-                    sb.append((char)hc);
+                    sb.append((char) hc);
                 }
             }
         }
         return (sb == null) ? s : sb.toString();
     }
 
-    static boolean isValidIdentifierName(String s, Context cx, boolean isStrict)
-    {
+    static boolean isValidIdentifierName(String s, Context cx, boolean isStrict) {
         int L = s.length();
-        if (L == 0)
-            return false;
-        if (!Character.isJavaIdentifierStart(s.charAt(0)))
-            return false;
+        if (L == 0) return false;
+        if (!Character.isJavaIdentifierStart(s.charAt(0))) return false;
         for (int i = 1; i != L; ++i) {
-            if (!Character.isJavaIdentifierPart(s.charAt(i)))
-                return false;
+            if (!Character.isJavaIdentifierPart(s.charAt(i))) return false;
         }
         return !TokenStream.isKeyword(s, cx.getLanguageVersion(), isStrict);
     }
 
     public static CharSequence toCharSequence(Object val) {
         if (val instanceof NativeString) {
-            return ((NativeString)val).toCharSequence();
+            return ((NativeString) val).toCharSequence();
         }
         return val instanceof CharSequence ? (CharSequence) val : toString(val);
     }
@@ -806,29 +979,32 @@
     /**
      * Convert the value to a string.
      *
-     * See ECMA 9.8.
+     * <p>See ECMA 9.8.
      */
     public static String toString(Object val) {
-        for (;;) {
+        for (; ; ) {
             if (val == null) {
                 return "null";
             }
-            if (val == Undefined.instance || val == Undefined.SCRIPTABLE_UNDEFINED) {
+            if (Undefined.isUndefined(val)) {
                 return "undefined";
             }
             if (val instanceof String) {
-                return (String)val;
+                return (String) val;
             }
             if (val instanceof CharSequence) {
                 return val.toString();
             }
+            if (val instanceof BigInteger) {
+                return val.toString();
+            }
             if (val instanceof Number) {
                 // XXX should we just teach NativeNumber.stringValue()
                 // about Numbers?
-                return numberToString(((Number)val).doubleValue(), 10);
+                return numberToString(((Number) val).doubleValue(), 10);
             }
             if (val instanceof Symbol) {
-                throw typeError0("msg.not.a.string");
+                throw typeErrorById("msg.not.a.string");
             }
             if (val instanceof Scriptable) {
                 val = ((Scriptable) val).getDefaultValue(StringClass);
@@ -841,64 +1017,58 @@
         }
     }
 
-    static String defaultObjectToString(Scriptable obj)
-    {
-        if (obj == null)
-            return "[object Null]";
-        if (Undefined.isUndefined(obj))
-            return "[object Undefined]";
+    static String defaultObjectToString(Scriptable obj) {
+        if (obj == null) return "[object Null]";
+        if (Undefined.isUndefined(obj)) return "[object Undefined]";
         return "[object " + obj.getClassName() + ']';
     }
 
-    public static String toString(Object[] args, int index)
-    {
+    public static String toString(Object[] args, int index) {
         return (index < args.length) ? toString(args[index]) : "undefined";
     }
 
-    /**
-     * Optimized version of toString(Object) for numbers.
-     */
+    /** Optimized version of toString(Object) for numbers. */
     public static String toString(double val) {
         return numberToString(val, 10);
     }
 
     public static String numberToString(double d, int base) {
         if ((base < 2) || (base > 36)) {
-            throw Context.reportRuntimeError1(
-                "msg.bad.radix", Integer.toString(base));
+            throw Context.reportRuntimeErrorById("msg.bad.radix", Integer.toString(base));
         }
 
-        if (d != d)
-            return "NaN";
-        if (d == Double.POSITIVE_INFINITY)
-            return "Infinity";
-        if (d == Double.NEGATIVE_INFINITY)
-            return "-Infinity";
-        if (d == 0.0)
-            return "0";
+        if (Double.isNaN(d)) return "NaN";
+        if (d == Double.POSITIVE_INFINITY) return "Infinity";
+        if (d == Double.NEGATIVE_INFINITY) return "-Infinity";
+        if (d == 0.0) return "0";
 
         if (base != 10) {
             return DToA.JS_dtobasestr(base, d);
-        } else {
-            // V8 FastDtoa can't convert all numbers, so try it first but
-            // fall back to old DToA in case it fails
-            String result = FastDtoa.numberToString(d);
-            if (result != null) {
-                return result;
-            }
-            StringBuilder buffer = new StringBuilder();
-            DToA.JS_dtostr(buffer, DToA.DTOSTR_STANDARD, 0, d);
-            return buffer.toString();
+        }
+        // V8 FastDtoa can't convert all numbers, so try it first but
+        // fall back to old DToA in case it fails
+        String result = FastDtoa.numberToString(d);
+        if (result != null) {
+            return result;
+        }
+        StringBuilder buffer = new StringBuilder();
+        DToA.JS_dtostr(buffer, DToA.DTOSTR_STANDARD, 0, d);
+        return buffer.toString();
+    }
+
+    public static String bigIntToString(BigInteger n, int base) {
+        if ((base < 2) || (base > 36)) {
+            throw rangeErrorById("msg.bad.radix", Integer.toString(base));
         }
 
+        return n.toString(base);
     }
 
-    static String uneval(Context cx, Scriptable scope, Object value)
-    {
+    static String uneval(Context cx, Scriptable scope, Object value) {
         if (value == null) {
             return "null";
         }
-        if (value == Undefined.instance) {
+        if (Undefined.isUndefined(value)) {
             return "undefined";
         }
         if (value instanceof CharSequence) {
@@ -910,7 +1080,7 @@
             return sb.toString();
         }
         if (value instanceof Number) {
-            double d = ((Number)value).doubleValue();
+            double d = ((Number) value).doubleValue();
             if (d == 0 && 1 / d < 0) {
                 return "-0";
             }
@@ -920,13 +1090,13 @@
             return toString(value);
         }
         if (value instanceof Scriptable) {
-            Scriptable obj = (Scriptable)value;
+            Scriptable obj = (Scriptable) value;
             // Wrapped Java objects won't have "toSource" and will report
             // errors for get()s of nonexistent name, so use has() first
             if (ScriptableObject.hasProperty(obj, "toSource")) {
                 Object v = ScriptableObject.getProperty(obj, "toSource");
                 if (v instanceof Function) {
-                    Function f = (Function)v;
+                    Function f = (Function) v;
                     return toString(f.call(cx, scope, obj, emptyArgs));
                 }
             }
@@ -936,9 +1106,8 @@
         return value.toString();
     }
 
-    static String defaultObjectToSource(Context cx, Scriptable scope,
-                                        Scriptable thisObj, Object[] args)
-    {
+    static String defaultObjectToSource(
+            Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         boolean toplevel, iterating;
         if (cx.iterating == null) {
             toplevel = true;
@@ -961,30 +1130,25 @@
             if (!iterating) {
                 cx.iterating.intern(thisObj); // stop recursion.
                 Object[] ids = thisObj.getIds();
-                for (int i=0; i < ids.length; i++) {
+                for (int i = 0; i < ids.length; i++) {
                     Object id = ids[i];
                     Object value;
                     if (id instanceof Integer) {
-                        int intId = ((Integer)id).intValue();
+                        int intId = ((Integer) id).intValue();
                         value = thisObj.get(intId, thisObj);
-                        if (value == Scriptable.NOT_FOUND)
-                            continue;   // a property has been removed
-                        if (i > 0)
-                            result.append(", ");
+                        if (value == Scriptable.NOT_FOUND) continue; // a property has been removed
+                        if (i > 0) result.append(", ");
                         result.append(intId);
                     } else {
-                        String strId = (String)id;
+                        String strId = (String) id;
                         value = thisObj.get(strId, thisObj);
-                        if (value == Scriptable.NOT_FOUND)
-                            continue;   // a property has been removed
-                        if (i > 0)
-                            result.append(", ");
+                        if (value == Scriptable.NOT_FOUND) continue; // a property has been removed
+                        if (i > 0) result.append(", ");
                         if (ScriptRuntime.isValidIdentifierName(strId, cx, cx.isStrictMode())) {
                             result.append(strId);
                         } else {
                             result.append('\'');
-                            result.append(
-                                ScriptRuntime.escapeString(strId, '\''));
+                            result.append(ScriptRuntime.escapeString(strId, '\''));
                             result.append('\'');
                         }
                     }
@@ -1005,54 +1169,44 @@
         return result.toString();
     }
 
-    public static Scriptable toObject(Scriptable scope, Object val)
-    {
+    public static Scriptable toObject(Scriptable scope, Object val) {
         if (val instanceof Scriptable) {
-            return (Scriptable)val;
+            return (Scriptable) val;
         }
         return toObject(Context.getContext(), scope, val);
     }
 
     /**
-     * <strong>Warning</strong>: This doesn't allow to resolve primitive
-     * prototype properly when many top scopes are involved
+     * <strong>Warning</strong>: This doesn't allow to resolve primitive prototype properly when
+     * many top scopes are involved
      *
      * @deprecated Use {@link #toObjectOrNull(Context, Object, Scriptable)} instead
      */
     @Deprecated
-    public static Scriptable toObjectOrNull(Context cx, Object obj)
-    {
+    public static Scriptable toObjectOrNull(Context cx, Object obj) {
         if (obj instanceof Scriptable) {
-            return (Scriptable)obj;
-        } else if (obj != null && obj != Undefined.instance) {
+            return (Scriptable) obj;
+        } else if (obj != null && !Undefined.isUndefined(obj)) {
             return toObject(cx, getTopCallScope(cx), obj);
         }
         return null;
     }
 
-    /**
-     * @param scope the scope that should be used to resolve primitive prototype
-     */
-    public static Scriptable toObjectOrNull(Context cx, Object obj,
-                                            Scriptable scope)
-    {
+    /** @param scope the scope that should be used to resolve primitive prototype */
+    public static Scriptable toObjectOrNull(Context cx, Object obj, Scriptable scope) {
         if (obj instanceof Scriptable) {
-            return (Scriptable)obj;
-        } else if (obj != null && obj != Undefined.instance) {
+            return (Scriptable) obj;
+        } else if (obj != null && !Undefined.isUndefined(obj)) {
             return toObject(cx, scope, obj);
         }
         return null;
     }
 
-    /**
-     * @deprecated Use {@link #toObject(Scriptable, Object)} instead.
-     */
+    /** @deprecated Use {@link #toObject(Scriptable, Object)} instead. */
     @Deprecated
-    public static Scriptable toObject(Scriptable scope, Object val,
-                                      Class<?> staticClass)
-    {
+    public static Scriptable toObject(Scriptable scope, Object val, Class<?> staticClass) {
         if (val instanceof Scriptable) {
-            return (Scriptable)val;
+            return (Scriptable) val;
         }
         return toObject(Context.getContext(), scope, val);
     }
@@ -1060,12 +1214,18 @@
     /**
      * Convert the value to an object.
      *
-     * See ECMA 9.9.
+     * <p>See ECMA 9.9.
      */
-    public static Scriptable toObject(Context cx, Scriptable scope, Object val)
-    {
+    public static Scriptable toObject(Context cx, Scriptable scope, Object val) {
+        if (val == null) {
+            throw typeErrorById("msg.null.to.object");
+        }
+        if (Undefined.isUndefined(val)) {
+            throw typeErrorById("msg.undef.to.object");
+        }
+
         if (isSymbol(val)) {
-            NativeSymbol result = new NativeSymbol((NativeSymbol)val);
+            NativeSymbol result = new NativeSymbol((NativeSymbol) val);
             setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Symbol);
             return result;
         }
@@ -1074,94 +1234,85 @@
         }
         if (val instanceof CharSequence) {
             // FIXME we want to avoid toString() here, especially for concat()
-            NativeString result = new NativeString((CharSequence)val);
+            NativeString result = new NativeString((CharSequence) val);
             setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.String);
             return result;
         }
+        if (cx.getLanguageVersion() >= Context.VERSION_ES6 && val instanceof BigInteger) {
+            NativeBigInt result = new NativeBigInt(((BigInteger) val));
+            setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.BigInt);
+            return result;
+        }
         if (val instanceof Number) {
-            NativeNumber result = new NativeNumber(((Number)val).doubleValue());
+            NativeNumber result = new NativeNumber(((Number) val).doubleValue());
             setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Number);
             return result;
         }
         if (val instanceof Boolean) {
-            NativeBoolean result = new NativeBoolean(((Boolean)val).booleanValue());
+            NativeBoolean result = new NativeBoolean(((Boolean) val).booleanValue());
             setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Boolean);
             return result;
         }
-        if (val == null) {
-            throw typeError0("msg.null.to.object");
-        }
-        if (val == Undefined.instance) {
-            throw typeError0("msg.undef.to.object");
-        }
 
         // Extension: Wrap as a LiveConnect object.
         Object wrapped = cx.getWrapFactory().wrap(cx, scope, val, null);
-        if (wrapped instanceof Scriptable)
-            return (Scriptable) wrapped;
+        if (wrapped instanceof Scriptable) return (Scriptable) wrapped;
         throw errorWithClassName("msg.invalid.type", val);
     }
 
-    /**
-     * @deprecated Use {@link #toObject(Context, Scriptable, Object)} instead.
-     */
+    /** @deprecated Use {@link #toObject(Context, Scriptable, Object)} instead. */
     @Deprecated
-    public static Scriptable toObject(Context cx, Scriptable scope, Object val,
-                                      Class<?> staticClass)
-    {
+    public static Scriptable toObject(
+            Context cx, Scriptable scope, Object val, Class<?> staticClass) {
         return toObject(cx, scope, val);
     }
 
-    /**
-     * @deprecated The method is only present for compatibility.
-     */
+    /** @deprecated The method is only present for compatibility. */
     @Deprecated
-    public static Object call(Context cx, Object fun, Object thisArg,
-                              Object[] args, Scriptable scope)
-    {
+    public static Object call(
+            Context cx, Object fun, Object thisArg, Object[] args, Scriptable scope) {
         if (!(fun instanceof Function)) {
             throw notFunctionError(toString(fun));
         }
-        Function function = (Function)fun;
+        Function function = (Function) fun;
         Scriptable thisObj = toObjectOrNull(cx, thisArg, scope);
         if (thisObj == null) {
-            throw undefCallError(thisObj, "function");
+            throw undefCallError(null, "function");
         }
         return function.call(cx, scope, thisObj, args);
     }
 
-    public static Scriptable newObject(Context cx, Scriptable scope,
-                                       String constructorName, Object[] args)
-    {
+    public static Scriptable newObject(
+            Context cx, Scriptable scope, String constructorName, Object[] args) {
         scope = ScriptableObject.getTopLevelScope(scope);
         Function ctor = getExistingCtor(cx, scope, constructorName);
-        if (args == null) { args = ScriptRuntime.emptyArgs; }
+        if (args == null) {
+            args = ScriptRuntime.emptyArgs;
+        }
         return ctor.construct(cx, scope, args);
     }
 
-    public static Scriptable newBuiltinObject(Context cx, Scriptable scope,
-                                              TopLevel.Builtins type,
-                                              Object[] args)
-    {
+    public static Scriptable newBuiltinObject(
+            Context cx, Scriptable scope, TopLevel.Builtins type, Object[] args) {
         scope = ScriptableObject.getTopLevelScope(scope);
         Function ctor = TopLevel.getBuiltinCtor(cx, scope, type);
-        if (args == null) { args = ScriptRuntime.emptyArgs; }
+        if (args == null) {
+            args = ScriptRuntime.emptyArgs;
+        }
         return ctor.construct(cx, scope, args);
     }
 
-    static Scriptable newNativeError(Context cx, Scriptable scope,
-                                     TopLevel.NativeErrors type, Object[] args)
-    {
+    static Scriptable newNativeError(
+            Context cx, Scriptable scope, TopLevel.NativeErrors type, Object[] args) {
         scope = ScriptableObject.getTopLevelScope(scope);
         Function ctor = TopLevel.getNativeErrorCtor(cx, scope, type);
-        if (args == null) { args = ScriptRuntime.emptyArgs; }
+        if (args == null) {
+            args = ScriptRuntime.emptyArgs;
+        }
         return ctor.construct(cx, scope, args);
     }
 
-    /**
-     *
-     * See ECMA 9.4.
-     */
+    /** See ECMA 9.4. */
     public static double toInteger(Object val) {
         return toInteger(toNumber(val));
     }
@@ -1169,33 +1320,31 @@
     // convenience method
     public static double toInteger(double d) {
         // if it's NaN
-        if (d != d)
-            return +0.0;
+        if (Double.isNaN(d)) return +0.0;
+
+        if ((d == 0.0) || Double.isInfinite(d)) return d;
+
+        if (d > 0.0) return Math.floor(d);
 
-        if (d == 0.0 ||
-            d == Double.POSITIVE_INFINITY ||
-            d == Double.NEGATIVE_INFINITY)
-            return d;
-
-        if (d > 0.0)
-            return Math.floor(d);
-        else
-            return Math.ceil(d);
+        return Math.ceil(d);
     }
 
     public static double toInteger(Object[] args, int index) {
         return (index < args.length) ? toInteger(args[index]) : +0.0;
     }
 
-    /**
-     *
-     * See ECMA 9.5.
-     */
-    public static int toInt32(Object val)
-    {
+    public static long toLength(Object[] args, int index) {
+        double len = toInteger(args, index);
+        if (len <= +0.0) {
+            return 0;
+        }
+        return (long) Math.min(len, NativeNumber.MAX_SAFE_INTEGER);
+    }
+
+    /** See ECMA 9.5. */
+    public static int toInt32(Object val) {
         // short circuit for common integer values
-        if (val instanceof Integer)
-            return ((Integer)val).intValue();
+        if (val instanceof Integer) return ((Integer) val).intValue();
 
         return toInt32(toNumber(val));
     }
@@ -1210,6 +1359,7 @@
 
     /**
      * See ECMA 9.6.
+     *
      * @return long value representing 32 bits unsigned integer
      */
     public static long toUint32(double d) {
@@ -1220,21 +1370,17 @@
         return toUint32(toNumber(val));
     }
 
-    /**
-     *
-     * See ECMA 9.7.
-     */
+    /** See ECMA 9.7. */
     public static char toUint16(Object val) {
         double d = toNumber(val);
-        return (char)DoubleConversion.doubleToInt32(d);
+        return (char) DoubleConversion.doubleToInt32(d);
     }
 
     // XXX: this is until setDefaultNamespace will learn how to store NS
     // properly and separates namespace form Scriptable.get etc.
     private static final String DEFAULT_NS_TAG = "__default_namespace__";
 
-    public static Object setDefaultNamespace(Object namespace, Context cx)
-    {
+    public static Object setDefaultNamespace(Object namespace, Context cx) {
         Scriptable scope = cx.currentActivationCall;
         if (scope == null) {
             scope = getTopCallScope(cx);
@@ -1246,9 +1392,11 @@
         // XXX : this should be in separated namesapce from Scriptable.get/put
         if (!scope.has(DEFAULT_NS_TAG, scope)) {
             // XXX: this is racy of cause
-            ScriptableObject.defineProperty(scope, DEFAULT_NS_TAG, ns,
-                                            ScriptableObject.PERMANENT
-                                            | ScriptableObject.DONTENUM);
+            ScriptableObject.defineProperty(
+                    scope,
+                    DEFAULT_NS_TAG,
+                    ns,
+                    ScriptableObject.PERMANENT | ScriptableObject.DONTENUM);
         } else {
             scope.put(DEFAULT_NS_TAG, scope, ns);
         }
@@ -1256,14 +1404,13 @@
         return Undefined.instance;
     }
 
-    public static Object searchDefaultNamespace(Context cx)
-    {
+    public static Object searchDefaultNamespace(Context cx) {
         Scriptable scope = cx.currentActivationCall;
         if (scope == null) {
             scope = getTopCallScope(cx);
         }
         Object nsObject;
-        for (;;) {
+        for (; ; ) {
             Scriptable parent = scope.getParentScope();
             if (parent == null) {
                 nsObject = ScriptableObject.getProperty(scope, DEFAULT_NS_TAG);
@@ -1286,29 +1433,23 @@
         return ScriptableObject.getProperty(scope, id);
     }
 
-    static Function getExistingCtor(Context cx, Scriptable scope,
-                                    String constructorName)
-    {
+    static Function getExistingCtor(Context cx, Scriptable scope, String constructorName) {
         Object ctorVal = ScriptableObject.getProperty(scope, constructorName);
         if (ctorVal instanceof Function) {
-            return (Function)ctorVal;
+            return (Function) ctorVal;
         }
         if (ctorVal == Scriptable.NOT_FOUND) {
-            throw Context.reportRuntimeError1(
-                "msg.ctor.not.found", constructorName);
-        } else {
-            throw Context.reportRuntimeError1(
-                "msg.not.ctor", constructorName);
+            throw Context.reportRuntimeErrorById("msg.ctor.not.found", constructorName);
         }
+        throw Context.reportRuntimeErrorById("msg.not.ctor", constructorName);
     }
 
     /**
-     * Return -1L if str is not an index, or the index value as lower 32
-     * bits of the result. Note that the result needs to be cast to an int
-     * in order to produce the actual index, which may be negative.
+     * Return -1L if str is not an index, or the index value as lower 32 bits of the result. Note
+     * that the result needs to be cast to an int in order to produce the actual index, which may be
+     * negative.
      */
-    public static long indexFromString(String str)
-    {
+    public static long indexFromString(String str) {
         // The length of the decimal string representation of
         //  Integer.MAX_VALUE, 2147483647
         final int MAX_VALUE_LENGTH = 10;
@@ -1327,9 +1468,7 @@
                 }
             }
             c -= '0';
-            if (0 <= c && c <= 9
-                && len <= (negate ? MAX_VALUE_LENGTH + 1 : MAX_VALUE_LENGTH))
-            {
+            if (0 <= c && c <= 9 && len <= (negate ? MAX_VALUE_LENGTH + 1 : MAX_VALUE_LENGTH)) {
                 // Use negative numbers to accumulate index to handle
                 // Integer.MIN_VALUE that is greater by 1 in absolute value
                 // then Integer.MAX_VALUE
@@ -1338,8 +1477,7 @@
                 i++;
                 if (index != 0) {
                     // Note that 00, 01, 000 etc. are not indexes
-                    while (i != len && 0 <= (c = str.charAt(i) - '0') && c <= 9)
-                    {
+                    while (i != len && 0 <= (c = str.charAt(i) - '0') && c <= 9) {
                         oldIndex = index;
                         index = 10 * index - c;
                         i++;
@@ -1347,12 +1485,13 @@
                 }
                 // Make sure all characters were consumed and that it couldn't
                 // have overflowed.
-                if (i == len &&
-                    (oldIndex > (Integer.MIN_VALUE / 10) ||
-                     (oldIndex == (Integer.MIN_VALUE / 10) &&
-                      c <= (negate ? -(Integer.MIN_VALUE % 10)
-                                   : (Integer.MAX_VALUE % 10)))))
-                {
+                if (i == len
+                        && (oldIndex > (Integer.MIN_VALUE / 10)
+                                || (oldIndex == (Integer.MIN_VALUE / 10)
+                                        && c
+                                                <= (negate
+                                                        ? -(Integer.MIN_VALUE % 10)
+                                                        : (Integer.MAX_VALUE % 10))))) {
                     return 0xFFFFFFFFL & (negate ? index : -index);
                 }
             }
@@ -1360,12 +1499,8 @@
         return -1L;
     }
 
-    /**
-     * If str is a decimal presentation of Uint32 value, return it as long.
-     * Othewise return -1L;
-     */
-    public static long testUint32String(String str)
-    {
+    /** If str is a decimal presentation of Uint32 value, return it as long. Othewise return -1L; */
+    public static long testUint32String(String str) {
         // The length of the decimal string representation of
         //  UINT32_MAX_VALUE, 4294967296
         final int MAX_VALUE_LENGTH = 10;
@@ -1396,26 +1531,21 @@
         return -1;
     }
 
-    /**
-     * If s represents index, then return index value wrapped as Integer
-     * and othewise return s.
-     */
-    static Object getIndexObject(String s)
-    {
+    /** If s represents index, then return index value wrapped as Integer and othewise return s. */
+    static Object getIndexObject(String s) {
         long indexTest = indexFromString(s);
         if (indexTest >= 0) {
-            return Integer.valueOf((int)indexTest);
+            return Integer.valueOf((int) indexTest);
         }
         return s;
     }
 
     /**
-     * If d is exact int value, return its value wrapped as Integer
-     * and othewise return d converted to String.
+     * If d is exact int value, return its value wrapped as Integer and othewise return d converted
+     * to String.
      */
-    static Object getIndexObject(double d)
-    {
-        int i = (int)d;
+    static Object getIndexObject(double d) {
+        int i = (int) d;
         if (i == d) {
             return Integer.valueOf(i);
         }
@@ -1423,34 +1553,51 @@
     }
 
     /**
-     * If toString(id) is a decimal presentation of int32 value, then id
-     * is index. In this case return null and make the index available
-     * as ScriptRuntime.lastIndexResult(cx). Otherwise return toString(id).
+     * Helper to return a string or an integer. Always use a null check on s.stringId to determine
+     * if the result is string or integer.
+     *
+     * @see ScriptRuntime#toStringIdOrIndex(Context, Object)
+     */
+    static final class StringIdOrIndex {
+        final String stringId;
+        final int index;
+
+        StringIdOrIndex(String stringId) {
+            this.stringId = stringId;
+            this.index = -1;
+        }
+
+        StringIdOrIndex(int index) {
+            this.stringId = null;
+            this.index = index;
+        }
+    }
+
+    /**
+     * If toString(id) is a decimal presentation of int32 value, then id is index. In this case
+     * return null and make the index available as ScriptRuntime.lastIndexResult(cx). Otherwise
+     * return toString(id).
      */
-    static String toStringIdOrIndex(Context cx, Object id)
-    {
+    static StringIdOrIndex toStringIdOrIndex(Context cx, Object id) {
         if (id instanceof Number) {
-            double d = ((Number)id).doubleValue();
-            int index = (int)d;
+            double d = ((Number) id).doubleValue();
+            int index = (int) d;
             if (index == d) {
-                storeIndexResult(cx, index);
-                return null;
+                return new StringIdOrIndex(index);
             }
-            return toString(id);
+            return new StringIdOrIndex(toString(id));
+        }
+        String s;
+        if (id instanceof String) {
+            s = (String) id;
         } else {
-            String s;
-            if (id instanceof String) {
-                s = (String)id;
-            } else {
-                s = toString(id);
-            }
-            long indexTest = indexFromString(s);
-            if (indexTest >= 0) {
-                storeIndexResult(cx, (int)indexTest);
-                return null;
-            }
-            return s;
+            s = toString(id);
+        }
+        long indexTest = indexFromString(s);
+        if (indexTest >= 0) {
+            return new StringIdOrIndex((int) indexTest);
         }
+        return new StringIdOrIndex(s);
     }
 
     /**
@@ -1459,16 +1606,12 @@
      * @deprecated Use {@link #getObjectElem(Object, Object, Context, Scriptable)} instead
      */
     @Deprecated
-    public static Object getObjectElem(Object obj, Object elem, Context cx)
-    {
+    public static Object getObjectElem(Object obj, Object elem, Context cx) {
         return getObjectElem(obj, elem, cx, getTopCallScope(cx));
     }
 
-    /**
-     * Call obj.[[Get]](id)
-     */
-    public static Object getObjectElem(Object obj, Object elem, Context cx, Scriptable scope)
-    {
+    /** Call obj.[[Get]](id) */
+    public static Object getObjectElem(Object obj, Object elem, Context cx, Scriptable scope) {
         Scriptable sobj = toObjectOrNull(cx, obj, scope);
         if (sobj == null) {
             throw undefReadError(obj, elem);
@@ -1476,23 +1619,21 @@
         return getObjectElem(sobj, elem, cx);
     }
 
-    public static Object getObjectElem(Scriptable obj, Object elem,
-                                       Context cx)
-    {
+    public static Object getObjectElem(Scriptable obj, Object elem, Context cx) {
 
         Object result;
 
         if (obj instanceof XMLObject) {
-            result = ((XMLObject)obj).get(cx, elem);
+            result = ((XMLObject) obj).get(cx, elem);
         } else if (isSymbol(elem)) {
-            result = ScriptableObject.getProperty(obj, (Symbol)elem);
+            result = ScriptableObject.getProperty(obj, (Symbol) elem);
         } else {
-            String s = toStringIdOrIndex(cx, elem);
-            if (s == null) {
-                int index = lastIndexResult(cx);
+            StringIdOrIndex s = toStringIdOrIndex(cx, elem);
+            if (s.stringId == null) {
+                int index = s.index;
                 result = ScriptableObject.getProperty(obj, index);
             } else {
-                result = ScriptableObject.getProperty(obj, s);
+                result = ScriptableObject.getProperty(obj, s.stringId);
             }
         }
 
@@ -1509,9 +1650,7 @@
      * @deprecated Use {@link #getObjectProp(Object, String, Context, Scriptable)} instead
      */
     @Deprecated
-    public static Object getObjectProp(Object obj, String property,
-                                       Context cx)
-    {
+    public static Object getObjectProp(Object obj, String property, Context cx) {
         return getObjectProp(obj, property, cx, getTopCallScope(cx));
     }
 
@@ -1520,9 +1659,7 @@
      *
      * @param scope the scope that should be used to resolve primitive prototype
      */
-    public static Object getObjectProp(Object obj, String property,
-                                       Context cx, Scriptable scope)
-    {
+    public static Object getObjectProp(Object obj, String property, Context cx, Scriptable scope) {
         Scriptable sobj = toObjectOrNull(cx, obj, scope);
         if (sobj == null) {
             throw undefReadError(obj, property);
@@ -1530,15 +1667,13 @@
         return getObjectProp(sobj, property, cx);
     }
 
-    public static Object getObjectProp(Scriptable obj, String property,
-                                       Context cx)
-    {
+    public static Object getObjectProp(Scriptable obj, String property, Context cx) {
 
         Object result = ScriptableObject.getProperty(obj, property);
         if (result == Scriptable.NOT_FOUND) {
             if (cx.hasFeature(Context.FEATURE_STRICT_MODE)) {
-                Context.reportWarning(ScriptRuntime.getMessage1(
-                    "msg.ref.undefined.prop", property));
+                Context.reportWarning(
+                        ScriptRuntime.getMessageById("msg.ref.undefined.prop", property));
             }
             result = Undefined.instance;
         }
@@ -1546,67 +1681,51 @@
         return result;
     }
 
-    /**
-     * @deprecated Use {@link #getObjectPropNoWarn(Object, String, Context, Scriptable)} instead
-     */
+    /** @deprecated Use {@link #getObjectPropNoWarn(Object, String, Context, Scriptable)} instead */
     @Deprecated
-    public static Object getObjectPropNoWarn(Object obj, String property,
-                                             Context cx)
-    {
+    public static Object getObjectPropNoWarn(Object obj, String property, Context cx) {
         return getObjectPropNoWarn(obj, property, cx, getTopCallScope(cx));
     }
 
-    public static Object getObjectPropNoWarn(Object obj, String property,
-                                             Context cx, Scriptable scope)
-    {
+    public static Object getObjectPropNoWarn(
+            Object obj, String property, Context cx, Scriptable scope) {
         Scriptable sobj = toObjectOrNull(cx, obj, scope);
         if (sobj == null) {
             throw undefReadError(obj, property);
         }
         Object result = ScriptableObject.getProperty(sobj, property);
         if (result == Scriptable.NOT_FOUND) {
-          return Undefined.instance;
+            return Undefined.instance;
         }
         return result;
     }
 
     /**
-     * A cheaper and less general version of the above for well-known argument
-     * types.
+     * A cheaper and less general version of the above for well-known argument types.
      *
      * @deprecated Use {@link #getObjectIndex(Object, double, Context, Scriptable)} instead
      */
     @Deprecated
-    public static Object getObjectIndex(Object obj, double dblIndex,
-                                        Context cx)
-    {
+    public static Object getObjectIndex(Object obj, double dblIndex, Context cx) {
         return getObjectIndex(obj, dblIndex, cx, getTopCallScope(cx));
     }
 
-    /**
-     * A cheaper and less general version of the above for well-known argument
-     * types.
-     */
-    public static Object getObjectIndex(Object obj, double dblIndex,
-                                        Context cx, Scriptable scope)
-    {
+    /** A cheaper and less general version of the above for well-known argument types. */
+    public static Object getObjectIndex(Object obj, double dblIndex, Context cx, Scriptable scope) {
         Scriptable sobj = toObjectOrNull(cx, obj, scope);
         if (sobj == null) {
             throw undefReadError(obj, toString(dblIndex));
         }
 
-        int index = (int)dblIndex;
+        int index = (int) dblIndex;
         if (index == dblIndex) {
             return getObjectIndex(sobj, index, cx);
-        } else {
-            String s = toString(dblIndex);
-            return getObjectProp(sobj, s, cx);
         }
+        String s = toString(dblIndex);
+        return getObjectProp(sobj, s, cx);
     }
 
-    public static Object getObjectIndex(Scriptable obj, int index,
-                                        Context cx)
-    {
+    public static Object getObjectIndex(Scriptable obj, int index, Context cx) {
         Object result = ScriptableObject.getProperty(obj, index);
         if (result == Scriptable.NOT_FOUND) {
             result = Undefined.instance;
@@ -1621,18 +1740,13 @@
      * @deprecated Use {@link #setObjectElem(Object, Object, Object, Context, Scriptable)} instead
      */
     @Deprecated
-    public static Object setObjectElem(Object obj, Object elem, Object value,
-                                       Context cx)
-    {
+    public static Object setObjectElem(Object obj, Object elem, Object value, Context cx) {
         return setObjectElem(obj, elem, value, cx, getTopCallScope(cx));
     }
 
-    /**
-     * Call obj.[[Put]](id, value)
-     */
-    public static Object setObjectElem(Object obj, Object elem, Object value,
-                                       Context cx, Scriptable scope)
-    {
+    /** Call obj.[[Put]](id, value) */
+    public static Object setObjectElem(
+            Object obj, Object elem, Object value, Context cx, Scriptable scope) {
         Scriptable sobj = toObjectOrNull(cx, obj, scope);
         if (sobj == null) {
             throw undefWriteError(obj, elem, value);
@@ -1640,20 +1754,17 @@
         return setObjectElem(sobj, elem, value, cx);
     }
 
-    public static Object setObjectElem(Scriptable obj, Object elem,
-                                       Object value, Context cx)
-    {
+    public static Object setObjectElem(Scriptable obj, Object elem, Object value, Context cx) {
         if (obj instanceof XMLObject) {
-            ((XMLObject)obj).put(cx, elem, value);
+            ((XMLObject) obj).put(cx, elem, value);
         } else if (isSymbol(elem)) {
-            ScriptableObject.putProperty(obj, (Symbol)elem, value);
+            ScriptableObject.putProperty(obj, (Symbol) elem, value);
         } else {
-            String s = toStringIdOrIndex(cx, elem);
-            if (s == null) {
-                int index = lastIndexResult(cx);
-                ScriptableObject.putProperty(obj, index, value);
+            StringIdOrIndex s = toStringIdOrIndex(cx, elem);
+            if (s.stringId == null) {
+                ScriptableObject.putProperty(obj, s.index, value);
             } else {
-                ScriptableObject.putProperty(obj, s, value);
+                ScriptableObject.putProperty(obj, s.stringId, value);
             }
         }
 
@@ -1666,203 +1777,161 @@
      * @deprecated Use {@link #setObjectProp(Object, String, Object, Context, Scriptable)} instead
      */
     @Deprecated
-    public static Object setObjectProp(Object obj, String property,
-                                       Object value, Context cx)
-    {
+    public static Object setObjectProp(Object obj, String property, Object value, Context cx) {
         return setObjectProp(obj, property, value, cx, getTopCallScope(cx));
     }
 
-    /**
-     * Version of setObjectElem when elem is a valid JS identifier name.
-     */
-    public static Object setObjectProp(Object obj, String property,
-                                       Object value, Context cx,
-                                       Scriptable scope)
-    {
+    /** Version of setObjectElem when elem is a valid JS identifier name. */
+    public static Object setObjectProp(
+            Object obj, String property, Object value, Context cx, Scriptable scope) {
+        if (!(obj instanceof Scriptable)
+                && cx.isStrictMode()
+                && cx.getLanguageVersion() >= Context.VERSION_1_8) {
+            throw undefWriteError(obj, property, value);
+        }
+
         Scriptable sobj = toObjectOrNull(cx, obj, scope);
         if (sobj == null) {
             throw undefWriteError(obj, property, value);
         }
+
         return setObjectProp(sobj, property, value, cx);
     }
 
-    public static Object setObjectProp(Scriptable obj, String property,
-                                       Object value, Context cx)
-    {
+    public static Object setObjectProp(Scriptable obj, String property, Object value, Context cx) {
         ScriptableObject.putProperty(obj, property, value);
         return value;
     }
 
     /**
-     * A cheaper and less general version of the above for well-known argument
-     * types.
+     * A cheaper and less general version of the above for well-known argument types.
      *
      * @deprecated Use {@link #setObjectIndex(Object, double, Object, Context, Scriptable)} instead
      */
     @Deprecated
-    public static Object setObjectIndex(Object obj, double dblIndex,
-                                        Object value, Context cx)
-    {
+    public static Object setObjectIndex(Object obj, double dblIndex, Object value, Context cx) {
         return setObjectIndex(obj, dblIndex, value, cx, getTopCallScope(cx));
     }
 
-    /**
-     * A cheaper and less general version of the above for well-known argument
-     * types.
-     */
-    public static Object setObjectIndex(Object obj, double dblIndex,
-                                        Object value, Context cx,
-                                        Scriptable scope)
-    {
+    /** A cheaper and less general version of the above for well-known argument types. */
+    public static Object setObjectIndex(
+            Object obj, double dblIndex, Object value, Context cx, Scriptable scope) {
         Scriptable sobj = toObjectOrNull(cx, obj, scope);
         if (sobj == null) {
             throw undefWriteError(obj, String.valueOf(dblIndex), value);
         }
 
-        int index = (int)dblIndex;
+        int index = (int) dblIndex;
         if (index == dblIndex) {
             return setObjectIndex(sobj, index, value, cx);
-        } else {
-            String s = toString(dblIndex);
-            return setObjectProp(sobj, s, value, cx);
         }
+        String s = toString(dblIndex);
+        return setObjectProp(sobj, s, value, cx);
     }
 
-    public static Object setObjectIndex(Scriptable obj, int index, Object value,
-                                        Context cx)
-    {
+    public static Object setObjectIndex(Scriptable obj, int index, Object value, Context cx) {
         ScriptableObject.putProperty(obj, index, value);
         return value;
     }
 
-    public static boolean deleteObjectElem(Scriptable target, Object elem,
-                                           Context cx)
-    {
+    public static boolean deleteObjectElem(Scriptable target, Object elem, Context cx) {
         if (isSymbol(elem)) {
             SymbolScriptable so = ScriptableObject.ensureSymbolScriptable(target);
-            Symbol s = (Symbol)elem;
+            Symbol s = (Symbol) elem;
             so.delete(s);
             return !so.has(s, target);
         }
-        String s = toStringIdOrIndex(cx, elem);
-        if (s == null) {
-            int index = lastIndexResult(cx);
-            target.delete(index);
-            return !target.has(index, target);
-        } else {
-            target.delete(s);
-            return !target.has(s, target);
+        StringIdOrIndex s = toStringIdOrIndex(cx, elem);
+        if (s.stringId == null) {
+            target.delete(s.index);
+            return !target.has(s.index, target);
         }
+        target.delete(s.stringId);
+        return !target.has(s.stringId, target);
     }
 
-    public static boolean hasObjectElem(Scriptable target, Object elem,
-                                        Context cx)
-    {
+    public static boolean hasObjectElem(Scriptable target, Object elem, Context cx) {
         boolean result;
 
         if (isSymbol(elem)) {
-            result = ScriptableObject.hasProperty(target, (Symbol)elem);
+            result = ScriptableObject.hasProperty(target, (Symbol) elem);
         } else {
-            String s = toStringIdOrIndex(cx, elem);
-            if (s == null) {
-                int index = lastIndexResult(cx);
-                result = ScriptableObject.hasProperty(target, index);
+            StringIdOrIndex s = toStringIdOrIndex(cx, elem);
+            if (s.stringId == null) {
+                result = ScriptableObject.hasProperty(target, s.index);
             } else {
-                result = ScriptableObject.hasProperty(target, s);
+                result = ScriptableObject.hasProperty(target, s.stringId);
             }
         }
 
         return result;
     }
 
-    public static Object refGet(Ref ref, Context cx)
-    {
+    public static Object refGet(Ref ref, Context cx) {
         return ref.get(cx);
     }
 
-    /**
-     * @deprecated Use {@link #refSet(Ref, Object, Context, Scriptable)} instead
-     */
+    /** @deprecated Use {@link #refSet(Ref, Object, Context, Scriptable)} instead */
     @Deprecated
-    public static Object refSet(Ref ref, Object value, Context cx)
-    {
+    public static Object refSet(Ref ref, Object value, Context cx) {
         return refSet(ref, value, cx, getTopCallScope(cx));
     }
 
-    public static Object refSet(Ref ref, Object value, Context cx,
-                                Scriptable scope)
-    {
+    public static Object refSet(Ref ref, Object value, Context cx, Scriptable scope) {
         return ref.set(cx, scope, value);
     }
 
-    public static Object refDel(Ref ref, Context cx)
-    {
+    public static Object refDel(Ref ref, Context cx) {
         return wrapBoolean(ref.delete(cx));
     }
 
-    static boolean isSpecialProperty(String s)
-    {
+    static boolean isSpecialProperty(String s) {
         return s.equals("__proto__") || s.equals("__parent__");
     }
 
-    /**
-     * @deprecated Use {@link #specialRef(Object, String, Context, Scriptable)} instead
-     */
+    /** @deprecated Use {@link #specialRef(Object, String, Context, Scriptable)} instead */
     @Deprecated
-    public static Ref specialRef(Object obj, String specialProperty,
-                                 Context cx)
-    {
+    public static Ref specialRef(Object obj, String specialProperty, Context cx) {
         return specialRef(obj, specialProperty, cx, getTopCallScope(cx));
     }
 
-    public static Ref specialRef(Object obj, String specialProperty,
-                                 Context cx, Scriptable scope)
-    {
+    public static Ref specialRef(Object obj, String specialProperty, Context cx, Scriptable scope) {
         return SpecialRef.createSpecial(cx, scope, obj, specialProperty);
     }
 
-    /**
-     * @deprecated Use {@link #delete(Object, Object, Context, Scriptable, boolean)} instead
-     */
+    /** @deprecated Use {@link #delete(Object, Object, Context, Scriptable, boolean)} instead */
     @Deprecated
-    public static Object delete(Object obj, Object id, Context cx)
-    {
+    public static Object delete(Object obj, Object id, Context cx) {
         return delete(obj, id, cx, false);
     }
 
     /**
      * The delete operator
      *
-     * See ECMA 11.4.1
+     * <p>See ECMA 11.4.1
      *
-     * In ECMA 0.19, the description of the delete operator (11.4.1)
-     * assumes that the [[Delete]] method returns a value. However,
-     * the definition of the [[Delete]] operator (8.6.2.5) does not
-     * define a return value. Here we assume that the [[Delete]]
-     * method doesn't return a value.
+     * <p>In ECMA 0.19, the description of the delete operator (11.4.1) assumes that the [[Delete]]
+     * method returns a value. However, the definition of the [[Delete]] operator (8.6.2.5) does not
+     * define a return value. Here we assume that the [[Delete]] method doesn't return a value.
      *
      * @deprecated Use {@link #delete(Object, Object, Context, Scriptable, boolean)} instead
      */
     @Deprecated
-    public static Object delete(Object obj, Object id, Context cx, boolean isName)
-    {
+    public static Object delete(Object obj, Object id, Context cx, boolean isName) {
         return delete(obj, id, cx, getTopCallScope(cx), isName);
     }
 
     /**
      * The delete operator
      *
-     * See ECMA 11.4.1
+     * <p>See ECMA 11.4.1
      *
-     * In ECMA 0.19, the description of the delete operator (11.4.1)
-     * assumes that the [[Delete]] method returns a value. However,
-     * the definition of the [[Delete]] operator (8.6.2.5) does not
-     * define a return value. Here we assume that the [[Delete]]
-     * method doesn't return a value.
-     */
-    public static Object delete(Object obj, Object id, Context cx,
-                                Scriptable scope, boolean isName)
-    {
+     * <p>In ECMA 0.19, the description of the delete operator (11.4.1) assumes that the [[Delete]]
+     * method returns a value. However, the definition of the [[Delete]] operator (8.6.2.5) does not
+     * define a return value. Here we assume that the [[Delete]] method doesn't return a value.
+     */
+    public static Object delete(
+            Object obj, Object id, Context cx, Scriptable scope, boolean isName) {
         Scriptable sobj = toObjectOrNull(cx, obj, scope);
         if (sobj == null) {
             if (isName) {
@@ -1874,11 +1943,8 @@
         return wrapBoolean(result);
     }
 
-    /**
-     * Looks up a name in the scope chain and returns its value.
-     */
-    public static Object name(Context cx, Scriptable scope, String name)
-    {
+    /** Looks up a name in the scope chain and returns its value. */
+    public static Object name(Context cx, Scriptable scope, String name) {
         Scriptable parent = scope.getParentScope();
         if (parent == null) {
             Object result = topScopeName(cx, scope, name);
@@ -1891,19 +1957,21 @@
         return nameOrFunction(cx, scope, parent, name, false);
     }
 
-    private static Object nameOrFunction(Context cx, Scriptable scope,
-                                         Scriptable parentScope, String name,
-                                         boolean asFunctionCall)
-    {
+    private static Object nameOrFunction(
+            Context cx,
+            Scriptable scope,
+            Scriptable parentScope,
+            String name,
+            boolean asFunctionCall) {
         Object result;
         Scriptable thisObj = scope; // It is used only if asFunctionCall==true.
 
         XMLObject firstXMLObject = null;
-        for (;;) {
+        for (; ; ) {
             if (scope instanceof NativeWith) {
                 Scriptable withObj = scope.getPrototype();
                 if (withObj instanceof XMLObject) {
-                    XMLObject xmlObj = (XMLObject)withObj;
+                    XMLObject xmlObj = (XMLObject) withObj;
                     if (xmlObj.has(name, xmlObj)) {
                         // function this should be the target object of with
                         thisObj = xmlObj;
@@ -1929,8 +1997,7 @@
                     if (asFunctionCall) {
                         // ECMA 262 requires that this for nested funtions
                         // should be top scope
-                        thisObj = ScriptableObject.
-                                      getTopLevelScope(parentScope);
+                        thisObj = ScriptableObject.getTopLevelScope(parentScope);
                     }
                     break;
                 }
@@ -1973,39 +2040,34 @@
         return result;
     }
 
-    private static Object topScopeName(Context cx, Scriptable scope,
-                                       String name)
-    {
+    private static Object topScopeName(Context cx, Scriptable scope, String name) {
         if (cx.useDynamicScope) {
             scope = checkDynamicScope(cx.topCallScope, scope);
         }
         return ScriptableObject.getProperty(scope, name);
     }
 
-
     /**
      * Returns the object in the scope chain that has a given property.
      *
-     * The order of evaluation of an assignment expression involves
-     * evaluating the lhs to a reference, evaluating the rhs, and then
-     * modifying the reference with the rhs value. This method is used
-     * to 'bind' the given name to an object containing that property
-     * so that the side effects of evaluating the rhs do not affect
-     * which property is modified.
-     * Typically used in conjunction with setName.
+     * <p>The order of evaluation of an assignment expression involves evaluating the lhs to a
+     * reference, evaluating the rhs, and then modifying the reference with the rhs value. This
+     * method is used to 'bind' the given name to an object containing that property so that the
+     * side effects of evaluating the rhs do not affect which property is modified. Typically used
+     * in conjunction with setName.
      *
-     * See ECMA 10.1.4
+     * <p>See ECMA 10.1.4
      */
-    public static Scriptable bind(Context cx, Scriptable scope, String id)
-    {
+    public static Scriptable bind(Context cx, Scriptable scope, String id) {
         Scriptable firstXMLObject = null;
         Scriptable parent = scope.getParentScope();
-        childScopesChecks: if (parent != null) {
+        childScopesChecks:
+        if (parent != null) {
             // Check for possibly nested "with" scopes first
             while (scope instanceof NativeWith) {
                 Scriptable withObj = scope.getPrototype();
                 if (withObj instanceof XMLObject) {
-                    XMLObject xmlObject = (XMLObject)withObj;
+                    XMLObject xmlObject = (XMLObject) withObj;
                     if (xmlObject.has(cx, id)) {
                         return xmlObject;
                     }
@@ -2023,7 +2085,7 @@
                     break childScopesChecks;
                 }
             }
-            for (;;) {
+            for (; ; ) {
                 if (ScriptableObject.hasProperty(scope, id)) {
                     return scope;
                 }
@@ -2046,9 +2108,8 @@
         return firstXMLObject;
     }
 
-    public static Object setName(Scriptable bound, Object value,
-                                 Context cx, Scriptable scope, String id)
-    {
+    public static Object setName(
+            Scriptable bound, Object value, Context cx, Scriptable scope, String id) {
         if (bound != null) {
             // TODO: we used to special-case XMLObject here, but putProperty
             // seems to work for E4X and it's better to optimize  the common case
@@ -2057,11 +2118,9 @@
             // "newname = 7;", where 'newname' has not yet
             // been defined, creates a new property in the
             // top scope unless strict mode is specified.
-            if (cx.hasFeature(Context.FEATURE_STRICT_MODE) ||
-                cx.hasFeature(Context.FEATURE_STRICT_VARS))
-            {
-                Context.reportWarning(
-                    ScriptRuntime.getMessage1("msg.assn.create.strict", id));
+            if (cx.hasFeature(Context.FEATURE_STRICT_MODE)
+                    || cx.hasFeature(Context.FEATURE_STRICT_VARS)) {
+                Context.reportWarning(ScriptRuntime.getMessageById("msg.assn.create.strict", id));
             }
             // Find the top scope by walking up the scope chain.
             bound = ScriptableObject.getTopLevelScope(scope);
@@ -2073,8 +2132,8 @@
         return value;
     }
 
-    public static Object strictSetName(Scriptable bound, Object value,
-            Context cx, Scriptable scope, String id) {
+    public static Object strictSetName(
+            Scriptable bound, Object value, Context cx, Scriptable scope, String id) {
         if (bound != null) {
             // TODO: The LeftHandSide also may not be a reference to a
             // data property with the attribute value {[[Writable]]:false},
@@ -2086,16 +2145,13 @@
             // seems to work for E4X and we should optimize  the common case
             ScriptableObject.putProperty(bound, id, value);
             return value;
-        } else {
-            // See ES5 8.7.2
-            String msg = "Assignment to undefined \"" + id + "\" in strict mode";
-            throw constructError("ReferenceError", msg);
         }
+        // See ES5 8.7.2
+        String msg = "Assignment to undefined \"" + id + "\" in strict mode";
+        throw constructError("ReferenceError", msg);
     }
 
-    public static Object setConst(Scriptable bound, Object value,
-                                 Context cx, String id)
-    {
+    public static Object setConst(Scriptable bound, Object value, Context cx, String id) {
         if (bound instanceof XMLObject) {
             bound.put(id, bound, value);
         } else {
@@ -2107,26 +2163,22 @@
     /**
      * This is the enumeration needed by the for..in statement.
      *
-     * See ECMA 12.6.3.
+     * <p>See ECMA 12.6.3.
      *
-     * IdEnumeration maintains a ObjToIntMap to make sure a given
-     * id is enumerated only once across multiple objects in a
-     * prototype chain.
-     *
-     * XXX - ECMA delete doesn't hide properties in the prototype,
-     * but js/ref does. This means that the js/ref for..in can
-     * avoid maintaining a hash table and instead perform lookups
-     * to see if a given property has already been enumerated.
+     * <p>IdEnumeration maintains a ObjToIntMap to make sure a given id is enumerated only once
+     * across multiple objects in a prototype chain.
      *
+     * <p>XXX - ECMA delete doesn't hide properties in the prototype, but js/ref does. This means
+     * that the js/ref for..in can avoid maintaining a hash table and instead perform lookups to see
+     * if a given property has already been enumerated.
      */
-    private static class IdEnumeration implements Serializable
-    {
+    private static class IdEnumeration implements Serializable {
         private static final long serialVersionUID = 1L;
         Scriptable obj;
         Object[] ids;
-        int index;
         ObjToIntMap used;
         Object currentId;
+        int index;
         int enumType; /* one of ENUM_INIT_KEYS, ENUM_INIT_VALUES,
                          ENUM_INIT_ARRAY, ENUMERATE_VALUES_IN_ORDER */
 
@@ -2136,23 +2188,18 @@
         Scriptable iterator;
     }
 
-    public static Scriptable toIterator(Context cx, Scriptable scope,
-                                        Scriptable obj, boolean keyOnly)
-    {
-        if (ScriptableObject.hasProperty(obj,
-            NativeIterator.ITERATOR_PROPERTY_NAME))
-        {
-            Object v = ScriptableObject.getProperty(obj,
-                NativeIterator.ITERATOR_PROPERTY_NAME);
+    public static Scriptable toIterator(
+            Context cx, Scriptable scope, Scriptable obj, boolean keyOnly) {
+        if (ScriptableObject.hasProperty(obj, NativeIterator.ITERATOR_PROPERTY_NAME)) {
+            Object v = ScriptableObject.getProperty(obj, NativeIterator.ITERATOR_PROPERTY_NAME);
             if (!(v instanceof Callable)) {
-               throw typeError0("msg.invalid.iterator");
+                throw typeErrorById("msg.invalid.iterator");
             }
             Callable f = (Callable) v;
-            Object[] args = new Object[] { keyOnly ? Boolean.TRUE
-                                                   : Boolean.FALSE };
+            Object[] args = new Object[] {keyOnly ? Boolean.TRUE : Boolean.FALSE};
             v = f.call(cx, scope, obj, args);
             if (!(v instanceof Scriptable)) {
-                throw typeError0("msg.iterator.primitive");
+                throw typeErrorById("msg.iterator.primitive");
             }
             return (Scriptable) v;
         }
@@ -2165,10 +2212,8 @@
      * @deprecated Use {@link #enumInit(Object, Context, Scriptable, int)} instead
      */
     @Deprecated
-    public static Object enumInit(Object value, Context cx, boolean enumValues)
-    {
-        return enumInit(value, cx, enumValues ? ENUMERATE_VALUES
-                                              : ENUMERATE_KEYS);
+    public static Object enumInit(Object value, Context cx, boolean enumValues) {
+        return enumInit(value, cx, enumValues ? ENUMERATE_VALUES : ENUMERATE_KEYS);
     }
 
     public static final int ENUMERATE_KEYS = 0;
@@ -2179,18 +2224,13 @@
     public static final int ENUMERATE_ARRAY_NO_ITERATOR = 5;
     public static final int ENUMERATE_VALUES_IN_ORDER = 6;
 
-    /**
-     * @deprecated Use {@link #enumInit(Object, Context, Scriptable, int)} instead
-     */
+    /** @deprecated Use {@link #enumInit(Object, Context, Scriptable, int)} instead */
     @Deprecated
-    public static Object enumInit(Object value, Context cx, int enumType)
-    {
+    public static Object enumInit(Object value, Context cx, int enumType) {
         return enumInit(value, cx, getTopCallScope(cx), enumType);
     }
 
-    public static Object enumInit(Object value, Context cx, Scriptable scope,
-                                  int enumType)
-    {
+    public static Object enumInit(Object value, Context cx, Scriptable scope, int enumType) {
         IdEnumeration x = new IdEnumeration();
         x.obj = toObjectOrNull(cx, value, scope);
         // "for of" loop
@@ -2206,12 +2246,15 @@
         }
         x.enumType = enumType;
         x.iterator = null;
-        if (enumType != ENUMERATE_KEYS_NO_ITERATOR &&
-            enumType != ENUMERATE_VALUES_NO_ITERATOR &&
-            enumType != ENUMERATE_ARRAY_NO_ITERATOR)
-        {
-            x.iterator = toIterator(cx, x.obj.getParentScope(), x.obj,
-                                    enumType == ScriptRuntime.ENUMERATE_KEYS);
+        if (enumType != ENUMERATE_KEYS_NO_ITERATOR
+                && enumType != ENUMERATE_VALUES_NO_ITERATOR
+                && enumType != ENUMERATE_ARRAY_NO_ITERATOR) {
+            x.iterator =
+                    toIterator(
+                            cx,
+                            x.obj.getParentScope(),
+                            x.obj,
+                            enumType == ScriptRuntime.ENUMERATE_KEYS);
         }
         if (x.iterator == null) {
             // enumInit should read all initial ids before returning
@@ -2223,48 +2266,42 @@
     }
 
     private static Object enumInitInOrder(Context cx, IdEnumeration x) {
-        if (!(x.obj instanceof ScriptableObject)) {
-            throw typeError1("msg.not.iterable", toString(x.obj));
+        if (!(x.obj instanceof SymbolScriptable)
+                || !ScriptableObject.hasProperty(x.obj, SymbolKey.ITERATOR)) {
+            throw typeErrorById("msg.not.iterable", toString(x.obj));
         }
 
-        ScriptableObject xo = (ScriptableObject)x.obj;
-        if (!ScriptableObject.hasProperty(xo, SymbolKey.ITERATOR)) {
-            throw typeError1("msg.not.iterable", toString(x.obj));
-        }
-        Object iterator = ScriptableObject.getProperty(xo, SymbolKey.ITERATOR);
+        Object iterator = ScriptableObject.getProperty(x.obj, SymbolKey.ITERATOR);
         if (!(iterator instanceof Callable)) {
-            throw typeError1("msg.not.iterable", toString(x.obj));
+            throw typeErrorById("msg.not.iterable", toString(x.obj));
         }
         Callable f = (Callable) iterator;
         Scriptable scope = x.obj.getParentScope();
         Object[] args = new Object[] {};
         Object v = f.call(cx, scope, x.obj, args);
         if (!(v instanceof Scriptable)) {
-            throw typeError1("msg.not.iterable", toString(x.obj));
+            throw typeErrorById("msg.not.iterable", toString(x.obj));
         }
         x.iterator = (Scriptable) v;
         return x;
     }
 
     public static void setEnumNumbers(Object enumObj, boolean enumNumbers) {
-        ((IdEnumeration)enumObj).enumNumbers = enumNumbers;
+        ((IdEnumeration) enumObj).enumNumbers = enumNumbers;
     }
 
-    public static Boolean enumNext(Object enumObj)
-    {
-        IdEnumeration x = (IdEnumeration)enumObj;
+    public static Boolean enumNext(Object enumObj) {
+        IdEnumeration x = (IdEnumeration) enumObj;
         if (x.iterator != null) {
             if (x.enumType == ENUMERATE_VALUES_IN_ORDER) {
                 return enumNextInOrder(x);
             }
             Object v = ScriptableObject.getProperty(x.iterator, "next");
-            if (!(v instanceof Callable))
-                return Boolean.FALSE;
+            if (!(v instanceof Callable)) return Boolean.FALSE;
             Callable f = (Callable) v;
             Context cx = Context.getContext();
             try {
-                x.currentId = f.call(cx, x.iterator.getParentScope(),
-                                     x.iterator, emptyArgs);
+                x.currentId = f.call(cx, x.iterator.getParentScope(), x.iterator, emptyArgs);
                 return Boolean.TRUE;
             } catch (JavaScriptException e) {
                 if (e.getValue() instanceof NativeIterator.StopIteration) {
@@ -2273,7 +2310,7 @@
                 throw e;
             }
         }
-        for (;;) {
+        for (; ; ) {
             if (x.obj == null) {
                 return Boolean.FALSE;
             }
@@ -2290,22 +2327,19 @@
                 continue;
             } else if (id instanceof String) {
                 String strId = (String) id;
-                if (!x.obj.has(strId, x.obj))
-                    continue;   // must have been deleted
+                if (!x.obj.has(strId, x.obj)) continue; // must have been deleted
                 x.currentId = strId;
             } else {
-                int intId = ((Number)id).intValue();
-                if (!x.obj.has(intId, x.obj))
-                    continue;   // must have been deleted
-                x.currentId = x.enumNumbers ? (Object) (Integer.valueOf(intId))
-                                            : String.valueOf(intId);
+                int intId = ((Number) id).intValue();
+                if (!x.obj.has(intId, x.obj)) continue; // must have been deleted
+                x.currentId =
+                        x.enumNumbers ? (Object) (Integer.valueOf(intId)) : String.valueOf(intId);
             }
             return Boolean.TRUE;
         }
     }
 
-    private static Boolean enumNextInOrder(IdEnumeration enumObj)
-    {
+    private static Boolean enumNextInOrder(IdEnumeration enumObj) {
         Object v = ScriptableObject.getProperty(enumObj.iterator, ES6Iterator.NEXT_METHOD);
         if (!(v instanceof Callable)) {
             throw notFunctionError(enumObj.iterator, ES6Iterator.NEXT_METHOD);
@@ -2316,58 +2350,56 @@
         Object r = f.call(cx, scope, enumObj.iterator, emptyArgs);
         Scriptable iteratorResult = toObject(cx, scope, r);
         Object done = ScriptableObject.getProperty(iteratorResult, ES6Iterator.DONE_PROPERTY);
-        if (done != ScriptableObject.NOT_FOUND && toBoolean(done)) {
+        if (done != Scriptable.NOT_FOUND && toBoolean(done)) {
             return Boolean.FALSE;
         }
-        enumObj.currentId = ScriptableObject.getProperty(iteratorResult, ES6Iterator.VALUE_PROPERTY);
+        enumObj.currentId =
+                ScriptableObject.getProperty(iteratorResult, ES6Iterator.VALUE_PROPERTY);
         return Boolean.TRUE;
     }
 
-    public static Object enumId(Object enumObj, Context cx)
-    {
-        IdEnumeration x = (IdEnumeration)enumObj;
+    public static Object enumId(Object enumObj, Context cx) {
+        IdEnumeration x = (IdEnumeration) enumObj;
         if (x.iterator != null) {
             return x.currentId;
         }
         switch (x.enumType) {
-          case ENUMERATE_KEYS:
-          case ENUMERATE_KEYS_NO_ITERATOR:
-            return x.currentId;
-          case ENUMERATE_VALUES:
-          case ENUMERATE_VALUES_NO_ITERATOR:
-            return enumValue(enumObj, cx);
-          case ENUMERATE_ARRAY:
-          case ENUMERATE_ARRAY_NO_ITERATOR:
-            Object[] elements = { x.currentId, enumValue(enumObj, cx) };
-            return cx.newArray(ScriptableObject.getTopLevelScope(x.obj), elements);
-          default:
-            throw Kit.codeBug();
+            case ENUMERATE_KEYS:
+            case ENUMERATE_KEYS_NO_ITERATOR:
+                return x.currentId;
+            case ENUMERATE_VALUES:
+            case ENUMERATE_VALUES_NO_ITERATOR:
+                return enumValue(enumObj, cx);
+            case ENUMERATE_ARRAY:
+            case ENUMERATE_ARRAY_NO_ITERATOR:
+                Object[] elements = {x.currentId, enumValue(enumObj, cx)};
+                return cx.newArray(ScriptableObject.getTopLevelScope(x.obj), elements);
+            default:
+                throw Kit.codeBug();
         }
     }
 
     public static Object enumValue(Object enumObj, Context cx) {
-        IdEnumeration x = (IdEnumeration)enumObj;
+        IdEnumeration x = (IdEnumeration) enumObj;
 
         Object result;
 
         if (isSymbol(x.currentId)) {
             SymbolScriptable so = ScriptableObject.ensureSymbolScriptable(x.obj);
-            result = so.get((Symbol)x.currentId, x.obj);
+            result = so.get((Symbol) x.currentId, x.obj);
         } else {
-            String s = toStringIdOrIndex(cx, x.currentId);
-            if (s == null) {
-                int index = lastIndexResult(cx);
-                result = x.obj.get(index, x.obj);
+            StringIdOrIndex s = toStringIdOrIndex(cx, x.currentId);
+            if (s.stringId == null) {
+                result = x.obj.get(s.index, x.obj);
             } else {
-                result = x.obj.get(s, x.obj);
+                result = x.obj.get(s.stringId, x.obj);
             }
         }
 
         return result;
     }
 
-    private static void enumChangeObject(IdEnumeration x)
-    {
+    private static void enumChangeObject(IdEnumeration x) {
         Object[] ids = null;
         while (x.obj != null) {
             ids = x.obj.getIds();
@@ -2391,63 +2423,93 @@
     }
 
     /**
-     * Prepare for calling name(...): return function corresponding to
-     * name and make current top scope available
-     * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
-     * The caller must call ScriptRuntime.lastStoredScriptable() immediately
-     * after calling this method.
+     * This is used to handle all the special cases that are required when invoking
+     * Object.fromEntries or constructing a NativeMap or NativeWeakMap from an iterable.
+     *
+     * @param cx the current context
+     * @param scope the current scope
+     * @param arg1 the iterable object.
+     * @param setter the setter to set the value
+     * @return true, if arg1 was iterable.
+     */
+    public static boolean loadFromIterable(
+            Context cx, Scriptable scope, Object arg1, BiConsumer<Object, Object> setter) {
+        if ((arg1 == null) || Undefined.isUndefined(arg1)) return false;
+
+        // Call the "[Symbol.iterator]" property as a function.
+        final Object ito = ScriptRuntime.callIterator(arg1, cx, scope);
+        if (Undefined.isUndefined(ito)) {
+            // Per spec, ignore if the iterator is undefined
+            return false;
+        }
+
+        // Finally, run through all the iterated values and add them!
+        try (IteratorLikeIterable it = new IteratorLikeIterable(cx, scope, ito)) {
+            for (Object val : it) {
+                Scriptable sVal = ScriptableObject.ensureScriptable(val);
+                if (sVal instanceof Symbol) {
+                    throw ScriptRuntime.typeErrorById(
+                            "msg.arg.not.object", ScriptRuntime.typeof(sVal));
+                }
+                Object finalKey = sVal.get(0, sVal);
+                if (finalKey == Scriptable.NOT_FOUND) {
+                    finalKey = Undefined.instance;
+                }
+                Object finalVal = sVal.get(1, sVal);
+                if (finalVal == Scriptable.NOT_FOUND) {
+                    finalVal = Undefined.instance;
+                }
+                setter.accept(finalKey, finalVal);
+            }
+        }
+        return true;
+    }
+    /**
+     * Prepare for calling name(...): return function corresponding to name and make current top
+     * scope available as ScriptRuntime.lastStoredScriptable() for consumption as thisObj. The
+     * caller must call ScriptRuntime.lastStoredScriptable() immediately after calling this method.
      */
-    public static Callable getNameFunctionAndThis(String name,
-                                                  Context cx,
-                                                  Scriptable scope)
-    {
+    public static Callable getNameFunctionAndThis(String name, Context cx, Scriptable scope) {
         Scriptable parent = scope.getParentScope();
         if (parent == null) {
             Object result = topScopeName(cx, scope, name);
             if (!(result instanceof Callable)) {
                 if (result == Scriptable.NOT_FOUND) {
                     throw notFoundError(scope, name);
-                } else {
-                    throw notFunctionError(result, name);
                 }
+                throw notFunctionError(result, name);
             }
             // Top scope is not NativeWith or NativeCall => thisObj == scope
             Scriptable thisObj = scope;
             storeScriptable(cx, thisObj);
-            return (Callable)result;
+            return (Callable) result;
         }
 
         // name will call storeScriptable(cx, thisObj);
-        return (Callable)nameOrFunction(cx, scope, parent, name, true);
+        return (Callable) nameOrFunction(cx, scope, parent, name, true);
     }
 
     /**
-     * Prepare for calling obj[id](...): return function corresponding to
-     * obj[id] and make obj properly converted to Scriptable available
-     * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
-     * The caller must call ScriptRuntime.lastStoredScriptable() immediately
+     * Prepare for calling obj[id](...): return function corresponding to obj[id] and make obj
+     * properly converted to Scriptable available as ScriptRuntime.lastStoredScriptable() for
+     * consumption as thisObj. The caller must call ScriptRuntime.lastStoredScriptable() immediately
      * after calling this method.
      *
      * @deprecated Use {@link #getElemFunctionAndThis(Object, Object, Context, Scriptable)} instead
      */
     @Deprecated
-    public static Callable getElemFunctionAndThis(Object obj,
-                                                  Object elem,
-                                                  Context cx)
-    {
+    public static Callable getElemFunctionAndThis(Object obj, Object elem, Context cx) {
         return getElemFunctionAndThis(obj, elem, cx, getTopCallScope(cx));
     }
 
     /**
-     * Prepare for calling obj[id](...): return function corresponding to
-     * obj[id] and make obj properly converted to Scriptable available
-     * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
-     * The caller must call ScriptRuntime.lastStoredScriptable() immediately
+     * Prepare for calling obj[id](...): return function corresponding to obj[id] and make obj
+     * properly converted to Scriptable available as ScriptRuntime.lastStoredScriptable() for
+     * consumption as thisObj. The caller must call ScriptRuntime.lastStoredScriptable() immediately
      * after calling this method.
      */
-    public static Callable getElemFunctionAndThis(Object obj, Object elem,
-                                                  Context cx, Scriptable scope)
-    {
+    public static Callable getElemFunctionAndThis(
+            Object obj, Object elem, Context cx, Scriptable scope) {
         Scriptable thisObj;
         Object value;
 
@@ -2456,21 +2518,20 @@
             if (thisObj == null) {
                 throw undefCallError(obj, String.valueOf(elem));
             }
-            value = ScriptableObject.getProperty(thisObj, (Symbol)elem);
+            value = ScriptableObject.getProperty(thisObj, (Symbol) elem);
 
         } else {
-            String str = toStringIdOrIndex(cx, elem);
-            if (str != null) {
-                return getPropFunctionAndThis(obj, str, cx, scope);
+            StringIdOrIndex s = toStringIdOrIndex(cx, elem);
+            if (s.stringId != null) {
+                return getPropFunctionAndThis(obj, s.stringId, cx, scope);
             }
-            int index = lastIndexResult(cx);
 
             thisObj = toObjectOrNull(cx, obj, scope);
             if (thisObj == null) {
                 throw undefCallError(obj, String.valueOf(elem));
             }
 
-            value = ScriptableObject.getProperty(thisObj, index);
+            value = ScriptableObject.getProperty(thisObj, s.index);
         }
 
         if (!(value instanceof Callable)) {
@@ -2478,46 +2539,37 @@
         }
 
         storeScriptable(cx, thisObj);
-        return (Callable)value;
+        return (Callable) value;
     }
 
     /**
-     * Prepare for calling obj.property(...): return function corresponding to
-     * obj.property and make obj properly converted to Scriptable available
-     * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
-     * The caller must call ScriptRuntime.lastStoredScriptable() immediately
-     * after calling this method.
-     * Warning: this doesn't allow to resolve primitive prototype properly when
-     * many top scopes are involved.
+     * Prepare for calling obj.property(...): return function corresponding to obj.property and make
+     * obj properly converted to Scriptable available as ScriptRuntime.lastStoredScriptable() for
+     * consumption as thisObj. The caller must call ScriptRuntime.lastStoredScriptable() immediately
+     * after calling this method. Warning: this doesn't allow to resolve primitive prototype
+     * properly when many top scopes are involved.
      *
      * @deprecated Use {@link #getPropFunctionAndThis(Object, String, Context, Scriptable)} instead
      */
     @Deprecated
-    public static Callable getPropFunctionAndThis(Object obj,
-                                                  String property,
-                                                  Context cx)
-    {
+    public static Callable getPropFunctionAndThis(Object obj, String property, Context cx) {
         return getPropFunctionAndThis(obj, property, cx, getTopCallScope(cx));
     }
 
     /**
-     * Prepare for calling obj.property(...): return function corresponding to
-     * obj.property and make obj properly converted to Scriptable available
-     * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
-     * The caller must call ScriptRuntime.lastStoredScriptable() immediately
+     * Prepare for calling obj.property(...): return function corresponding to obj.property and make
+     * obj properly converted to Scriptable available as ScriptRuntime.lastStoredScriptable() for
+     * consumption as thisObj. The caller must call ScriptRuntime.lastStoredScriptable() immediately
      * after calling this method.
      */
-    public static Callable getPropFunctionAndThis(Object obj,
-                                                  String property,
-                                                  Context cx, Scriptable scope)
-    {
+    public static Callable getPropFunctionAndThis(
+            Object obj, String property, Context cx, Scriptable scope) {
         Scriptable thisObj = toObjectOrNull(cx, obj, scope);
         return getPropFunctionAndThisHelper(obj, property, cx, thisObj);
     }
 
-    private static Callable getPropFunctionAndThisHelper(Object obj,
-          String property, Context cx, Scriptable thisObj)
-    {
+    private static Callable getPropFunctionAndThisHelper(
+            Object obj, String property, Context cx, Scriptable thisObj) {
         if (thisObj == null) {
             throw undefCallError(obj, property);
         }
@@ -2526,7 +2578,7 @@
         if (!(value instanceof Callable)) {
             Object noSuchMethod = ScriptableObject.getProperty(thisObj, "__noSuchMethod__");
             if (noSuchMethod instanceof Callable)
-                value = new NoSuchMethodShim((Callable)noSuchMethod, property);
+                value = new NoSuchMethodShim((Callable) noSuchMethod, property);
         }
 
         if (!(value instanceof Callable)) {
@@ -2534,26 +2586,24 @@
         }
 
         storeScriptable(cx, thisObj);
-        return (Callable)value;
+        return (Callable) value;
     }
 
     /**
-     * Prepare for calling <expression>(...): return function corresponding to
-     * <expression> and make parent scope of the function available
-     * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
-     * The caller must call ScriptRuntime.lastStoredScriptable() immediately
-     * after calling this method.
+     * Prepare for calling <expression>(...): return function corresponding to
+     * <expression> and make parent scope of the function available as
+     * ScriptRuntime.lastStoredScriptable() for consumption as thisObj. The caller must call
+     * ScriptRuntime.lastStoredScriptable() immediately after calling this method.
      */
-    public static Callable getValueFunctionAndThis(Object value, Context cx)
-    {
+    public static Callable getValueFunctionAndThis(Object value, Context cx) {
         if (!(value instanceof Callable)) {
             throw notFunctionError(value);
         }
 
-        Callable f = (Callable)value;
+        Callable f = (Callable) value;
         Scriptable thisObj = null;
         if (f instanceof Scriptable) {
-            thisObj = ((Scriptable)f).getParentScope();
+            thisObj = ((Scriptable) f).getParentScope();
         }
         if (thisObj == null) {
             if (cx.topCallScope == null) throw new IllegalStateException();
@@ -2573,61 +2623,80 @@
     }
 
     /**
-     * Perform function call in reference context. Should always
-     * return value that can be passed to
-     * {@link #refGet(Ref, Context)} or {@link #refSet(Ref, Object, Context)}
-     * arbitrary number of times.
-     * The args array reference should not be stored in any object that is
-     * can be GC-reachable after this method returns. If this is necessary,
-     * store args.clone(), not args array itself.
-     */
-    public static Ref callRef(Callable function, Scriptable thisObj,
-                              Object[] args, Context cx)
-    {
+     * Given an object, get the "Symbol.iterator" element, throw a TypeError if it is not present,
+     * then call the result, (throwing a TypeError if the result is not a function), and return that
+     * result, whatever it is.
+     */
+    public static Object callIterator(Object obj, Context cx, Scriptable scope) {
+        final Callable getIterator =
+                ScriptRuntime.getElemFunctionAndThis(obj, SymbolKey.ITERATOR, cx, scope);
+        final Scriptable iterable = ScriptRuntime.lastStoredScriptable(cx);
+        return getIterator.call(cx, scope, iterable, ScriptRuntime.emptyArgs);
+    }
+
+    /**
+     * Given an iterator result, return true if and only if there is a "done" property that's true.
+     */
+    public static boolean isIteratorDone(Context cx, Object result) {
+        if (!(result instanceof Scriptable)) {
+            return false;
+        }
+        final Object prop = getObjectProp((Scriptable) result, ES6Iterator.DONE_PROPERTY, cx);
+        return toBoolean(prop);
+    }
+
+    /**
+     * Perform function call in reference context. Should always return value that can be passed to
+     * {@link #refGet(Ref, Context)} or {@link #refSet(Ref, Object, Context)} arbitrary number of
+     * times. The args array reference should not be stored in any object that is can be
+     * GC-reachable after this method returns. If this is necessary, store args.clone(), not args
+     * array itself.
+     */
+    public static Ref callRef(Callable function, Scriptable thisObj, Object[] args, Context cx) {
         if (function instanceof RefCallable) {
-            RefCallable rfunction = (RefCallable)function;
+            RefCallable rfunction = (RefCallable) function;
             Ref ref = rfunction.refCall(cx, thisObj, args);
             if (ref == null) {
-                throw new IllegalStateException(rfunction.getClass().getName()+".refCall() returned null");
+                throw new IllegalStateException(
+                        rfunction.getClass().getName() + ".refCall() returned null");
             }
             return ref;
         }
         // No runtime support for now
-        String msg = getMessage1("msg.no.ref.from.function",
-                                 toString(function));
+        String msg = getMessageById("msg.no.ref.from.function", toString(function));
         throw constructError("ReferenceError", msg);
     }
 
     /**
      * Operator new.
      *
-     * See ECMA 11.2.2
+     * <p>See ECMA 11.2.2
      */
-    public static Scriptable newObject(Object fun, Context cx,
-                                       Scriptable scope, Object[] args)
-    {
+    public static Scriptable newObject(Object fun, Context cx, Scriptable scope, Object[] args) {
         if (!(fun instanceof Function)) {
             throw notFunctionError(fun);
         }
-        Function function = (Function)fun;
+        Function function = (Function) fun;
         return function.construct(cx, scope, args);
     }
 
-    public static Object callSpecial(Context cx, Callable fun,
-                                     Scriptable thisObj,
-                                     Object[] args, Scriptable scope,
-                                     Scriptable callerThis, int callType,
-                                     String filename, int lineNumber)
-    {
+    public static Object callSpecial(
+            Context cx,
+            Callable fun,
+            Scriptable thisObj,
+            Object[] args,
+            Scriptable scope,
+            Scriptable callerThis,
+            int callType,
+            String filename,
+            int lineNumber) {
         if (callType == Node.SPECIALCALL_EVAL) {
             if (thisObj.getParentScope() == null && NativeGlobal.isEvalFunction(fun)) {
-                return evalSpecial(cx, scope, callerThis, args,
-                                   filename, lineNumber);
+                return evalSpecial(cx, scope, callerThis, args, filename, lineNumber);
             }
         } else if (callType == Node.SPECIALCALL_WITH) {
             if (NativeWith.isWithFunction(fun)) {
-                throw Context.reportRuntimeError1("msg.only.from.new",
-                                                  "With");
+                throw Context.reportRuntimeErrorById("msg.only.from.new", "With");
             }
         } else {
             throw Kit.codeBug();
@@ -2636,13 +2705,11 @@
         return fun.call(cx, scope, thisObj, args);
     }
 
-    public static Object newSpecial(Context cx, Object fun,
-                                    Object[] args, Scriptable scope,
-                                    int callType)
-    {
+    public static Object newSpecial(
+            Context cx, Object fun, Object[] args, Scriptable scope, int callType) {
         if (callType == Node.SPECIALCALL_EVAL) {
             if (NativeGlobal.isEvalFunction(fun)) {
-                throw typeError1("msg.not.ctor", "eval");
+                throw typeErrorById("msg.not.ctor", "eval");
             }
         } else if (callType == Node.SPECIALCALL_WITH) {
             if (NativeWith.isWithFunction(fun)) {
@@ -2658,32 +2725,34 @@
     /**
      * Function.prototype.apply and Function.prototype.call
      *
-     * See Ecma 15.3.4.[34]
+     * <p>See Ecma 15.3.4.[34]
      */
-    public static Object applyOrCall(boolean isApply,
-                                     Context cx, Scriptable scope,
-                                     Scriptable thisObj, Object[] args)
-    {
+    public static Object applyOrCall(
+            boolean isApply, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         int L = args.length;
         Callable function = getCallable(thisObj);
 
         Scriptable callThis = null;
         if (L != 0) {
-            if  (cx.hasFeature(Context.FEATURE_OLD_UNDEF_NULL_THIS)) {
+            if (cx.hasFeature(Context.FEATURE_OLD_UNDEF_NULL_THIS)) {
                 callThis = toObjectOrNull(cx, args[0], scope);
             } else {
-                callThis = args[0] == Undefined.instance ? Undefined.SCRIPTABLE_UNDEFINED : toObjectOrNull(cx, args[0], scope);
+                callThis =
+                        args[0] == Undefined.instance
+                                ? Undefined.SCRIPTABLE_UNDEFINED
+                                : toObjectOrNull(cx, args[0], scope);
             }
         }
         if (callThis == null && cx.hasFeature(Context.FEATURE_OLD_UNDEF_NULL_THIS)) {
-            callThis = getTopCallScope(cx); // This covers the case of args[0] == (null|undefined) as well.
+            callThis =
+                    getTopCallScope(
+                            cx); // This covers the case of args[0] == (null|undefined) as well.
         }
 
         Object[] callArgs;
         if (isApply) {
             // Follow Ecma 15.3.4.3
-            callArgs = L <= 1 ? ScriptRuntime.emptyArgs :
-                getApplyArguments(cx, args[1]);
+            callArgs = L <= 1 ? ScriptRuntime.emptyArgs : getApplyArguments(cx, args[1]);
         } else {
             // Follow Ecma 15.3.4.4
             if (L <= 1) {
@@ -2697,28 +2766,38 @@
         return function.call(cx, scope, callThis, callArgs);
     }
 
-    static Object[] getApplyArguments(Context cx, Object arg1)
-    {
-        if (arg1 == null || arg1 == Undefined.instance) {
+    /** @return true if the passed in Scriptable looks like an array */
+    private static boolean isArrayLike(Scriptable obj) {
+        return obj != null
+                && (obj instanceof NativeArray
+                        || obj instanceof Arguments
+                        || ScriptableObject.hasProperty(obj, "length"));
+    }
+
+    static Object[] getApplyArguments(Context cx, Object arg1) {
+        if (arg1 == null || Undefined.isUndefined(arg1)) {
             return ScriptRuntime.emptyArgs;
-        } else if (arg1 instanceof NativeArray || arg1 instanceof Arguments) {
+        } else if (arg1 instanceof Scriptable && isArrayLike((Scriptable) arg1)) {
             return cx.getElements((Scriptable) arg1);
+        } else if (arg1 instanceof ScriptableObject) {
+            return ScriptRuntime.emptyArgs;
         } else {
-            throw ScriptRuntime.typeError0("msg.arg.isnt.array");
+            throw ScriptRuntime.typeErrorById("msg.arg.isnt.array");
         }
     }
 
-    static Callable getCallable(Scriptable thisObj)
-    {
+    static Callable getCallable(Scriptable thisObj) {
         Callable function;
         if (thisObj instanceof Callable) {
-            function = (Callable)thisObj;
+            function = (Callable) thisObj;
+        } else if (thisObj == null) {
+            throw ScriptRuntime.notFunctionError(null, null);
         } else {
             Object value = thisObj.getDefaultValue(ScriptRuntime.FunctionClass);
             if (!(value instanceof Callable)) {
                 throw ScriptRuntime.notFunctionError(value, thisObj);
             }
-            function = (Callable)value;
+            function = (Callable) value;
         }
         return function;
     }
@@ -2726,22 +2805,23 @@
     /**
      * The eval function property of the global object.
      *
-     * See ECMA 15.1.2.1
+     * <p>See ECMA 15.1.2.1
      */
-    public static Object evalSpecial(Context cx, Scriptable scope,
-                                     Object thisArg, Object[] args,
-                                     String filename, int lineNumber)
-    {
-        if (args.length < 1)
-            return Undefined.instance;
+    public static Object evalSpecial(
+            Context cx,
+            Scriptable scope,
+            Object thisArg,
+            Object[] args,
+            String filename,
+            int lineNumber) {
+        if (args.length < 1) return Undefined.instance;
         Object x = args[0];
         if (!(x instanceof CharSequence)) {
-            if (cx.hasFeature(Context.FEATURE_STRICT_MODE) ||
-                cx.hasFeature(Context.FEATURE_STRICT_EVAL))
-            {
-                throw Context.reportRuntimeError0("msg.eval.nonstring.strict");
+            if (cx.hasFeature(Context.FEATURE_STRICT_MODE)
+                    || cx.hasFeature(Context.FEATURE_STRICT_EVAL)) {
+                throw Context.reportRuntimeErrorById("msg.eval.nonstring.strict");
             }
-            String message = ScriptRuntime.getMessage0("msg.eval.nonstring");
+            String message = ScriptRuntime.getMessageById("msg.eval.nonstring");
             Context.reportWarning(message);
             return x;
         }
@@ -2754,61 +2834,63 @@
                 filename = "";
             }
         }
-        String sourceName = ScriptRuntime.
-            makeUrlForGeneratedScript(true, filename, lineNumber);
+        String sourceName = ScriptRuntime.makeUrlForGeneratedScript(true, filename, lineNumber);
 
         ErrorReporter reporter;
         reporter = DefaultErrorReporter.forEval(cx.getErrorReporter());
 
         Evaluator evaluator = Context.createInterpreter();
         if (evaluator == null) {
-            throw new JavaScriptException("Interpreter not present",
-                    filename, lineNumber);
+            throw new JavaScriptException("Interpreter not present", filename, lineNumber);
         }
 
         // Compile with explicit interpreter instance to force interpreter
         // mode.
-        Script script = cx.compileString(x.toString(), evaluator,
-                                         reporter, sourceName, 1, null);
+        Script script = cx.compileString(x.toString(), evaluator, reporter, sourceName, 1, null);
         evaluator.setEvalScriptFlag(script);
-        Callable c = (Callable)script;
-        return c.call(cx, scope, (Scriptable)thisArg, ScriptRuntime.emptyArgs);
+        Callable c = (Callable) script;
+        return c.call(cx, scope, (Scriptable) thisArg, ScriptRuntime.emptyArgs);
     }
 
-    /**
-     * The typeof operator
-     */
-    public static String typeof(Object value)
-    {
-        if (value == null)
-            return "object";
-        if (value == Undefined.instance)
-            return "undefined";
-        if (value instanceof ScriptableObject)
-            return ((ScriptableObject) value).getTypeOf();
-        if (value instanceof Scriptable)
-            return (value instanceof Callable) ? "function" : "object";
-        if (value instanceof CharSequence)
-            return "string";
-        if (value instanceof Number)
-            return "number";
-        if (value instanceof Boolean)
-            return "boolean";
+    /** The typeof operator */
+    public static String typeof(Object value) {
+        if (value == null) return "object";
+        if (value == Undefined.instance) return "undefined";
+        if (value instanceof Delegator) return typeof(((Delegator) value).getDelegee());
+        if (value instanceof ScriptableObject) return ((ScriptableObject) value).getTypeOf();
+        if (value instanceof Scriptable) return (value instanceof Callable) ? "function" : "object";
+        if (value instanceof CharSequence) return "string";
+        if (value instanceof BigInteger) return "bigint";
+        if (value instanceof Number) return "number";
+        if (value instanceof Boolean) return "boolean";
         throw errorWithClassName("msg.invalid.type", value);
     }
 
-    /**
-     * The typeof operator that correctly handles the undefined case
-     */
-    public static String typeofName(Scriptable scope, String id)
-    {
+    /** The typeof operator that correctly handles the undefined case */
+    public static String typeofName(Scriptable scope, String id) {
         Context cx = Context.getContext();
         Scriptable val = bind(cx, scope, id);
-        if (val == null)
-            return "undefined";
+        if (val == null) return "undefined";
         return typeof(getObjectProp(val, id, cx));
     }
 
+    public static boolean isObject(Object value) {
+        if (value == null) {
+            return false;
+        }
+        if (Undefined.isUndefined(value)) {
+            return false;
+        }
+        if (value instanceof ScriptableObject) {
+            String type = ((ScriptableObject) value).getTypeOf();
+            return "object".equals(type) || "function".equals(type);
+        }
+        if (value instanceof Scriptable) {
+            return (!(value instanceof Callable));
+        }
+        return false;
+    }
+
     // neg:
     // implement the '-' operator inline in the caller
     // as "-toNumber(val)"
@@ -2821,74 +2903,248 @@
     // implement the '~' operator inline in the caller
     // as "~toInt32(val)"
 
-    public static Object add(Object val1, Object val2, Context cx)
-    {
-        if(val1 instanceof Number && val2 instanceof Number) {
-            return wrapNumber(((Number)val1).doubleValue() +
-                              ((Number)val2).doubleValue());
+    public static Object add(Object val1, Object val2, Context cx) {
+        if (val1 instanceof BigInteger && val2 instanceof BigInteger) {
+            return ((BigInteger) val1).add((BigInteger) val2);
+        }
+        if ((val1 instanceof Number && val2 instanceof BigInteger)
+                || (val1 instanceof BigInteger && val2 instanceof Number)) {
+            throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
+        }
+        if (val1 instanceof Number && val2 instanceof Number) {
+            return wrapNumber(((Number) val1).doubleValue() + ((Number) val2).doubleValue());
+        }
+        if (val1 instanceof CharSequence && val2 instanceof CharSequence) {
+            // If we let this happen later, then the "getDefaultValue" logic
+            // undoes many optimizations
+            return new ConsString((CharSequence) val1, (CharSequence) val2);
         }
         if (val1 instanceof XMLObject) {
-            Object test = ((XMLObject)val1).addValues(cx, true, val2);
+            Object test = ((XMLObject) val1).addValues(cx, true, val2);
             if (test != Scriptable.NOT_FOUND) {
                 return test;
             }
         }
         if (val2 instanceof XMLObject) {
-            Object test = ((XMLObject)val2).addValues(cx, false, val1);
+            Object test = ((XMLObject) val2).addValues(cx, false, val1);
             if (test != Scriptable.NOT_FOUND) {
                 return test;
             }
         }
         if ((val1 instanceof Symbol) || (val2 instanceof Symbol)) {
-            throw typeError0("msg.not.a.number");
+            throw typeErrorById("msg.not.a.number");
+        }
+        if (val1 instanceof Scriptable) val1 = ((Scriptable) val1).getDefaultValue(null);
+        if (val2 instanceof Scriptable) val2 = ((Scriptable) val2).getDefaultValue(null);
+        if (!(val1 instanceof CharSequence) && !(val2 instanceof CharSequence)) {
+            Number num1 = val1 instanceof Number ? (Number) val1 : toNumeric(val1);
+            Number num2 = val2 instanceof Number ? (Number) val2 : toNumeric(val2);
+
+            if (num1 instanceof BigInteger && num2 instanceof BigInteger) {
+                return ((BigInteger) num1).add((BigInteger) num2);
+            }
+            if (num1 instanceof BigInteger || num2 instanceof BigInteger) {
+                throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
+            }
+            return num1.doubleValue() + num2.doubleValue();
         }
-        if (val1 instanceof Scriptable)
-            val1 = ((Scriptable) val1).getDefaultValue(null);
-        if (val2 instanceof Scriptable)
-            val2 = ((Scriptable) val2).getDefaultValue(null);
-        if (!(val1 instanceof CharSequence) && !(val2 instanceof CharSequence))
-            if ((val1 instanceof Number) && (val2 instanceof Number))
-                return wrapNumber(((Number)val1).doubleValue() +
-                                  ((Number)val2).doubleValue());
-            else
-                return wrapNumber(toNumber(val1) + toNumber(val2));
         return new ConsString(toCharSequence(val1), toCharSequence(val2));
     }
 
+    /**
+     * https://262.ecma-international.org/11.0/#sec-addition-operator-plus 5. Let lprim be ?
+     * ToPrimitive(lval). 7. If Type(lprim) is String or Type(rprim) is String, then a. Let lstr be
+     * ? ToString(lprim).
+     *
+     * <p>Should call toPrimitive before toCharSequence
+     *
+     * @deprecated Use {@link #add(Object, Object, Context)} instead
+     */
+    @Deprecated
     public static CharSequence add(CharSequence val1, Object val2) {
         return new ConsString(val1, toCharSequence(val2));
     }
 
+    /**
+     * https://262.ecma-international.org/11.0/#sec-addition-operator-plus 6. Let rprim be ?
+     * ToPrimitive(rval). 7. If Type(lprim) is String or Type(rprim) is String, then b. Let rstr be
+     * ? ToString(rprim).
+     *
+     * <p>Should call toPrimitive before toCharSequence
+     *
+     * @deprecated Use {@link #add(Object, Object, Context)} instead
+     */
+    @Deprecated
     public static CharSequence add(Object val1, CharSequence val2) {
         return new ConsString(toCharSequence(val1), val2);
     }
 
+    public static Number subtract(Number val1, Number val2) {
+        if (val1 instanceof BigInteger && val2 instanceof BigInteger) {
+            return ((BigInteger) val1).subtract((BigInteger) val2);
+        } else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
+            throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
+        } else {
+            return val1.doubleValue() - val2.doubleValue();
+        }
+    }
+
+    public static Number multiply(Number val1, Number val2) {
+        if (val1 instanceof BigInteger && val2 instanceof BigInteger) {
+            return ((BigInteger) val1).multiply((BigInteger) val2);
+        } else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
+            throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
+        } else {
+            return val1.doubleValue() * val2.doubleValue();
+        }
+    }
+
+    public static Number divide(Number val1, Number val2) {
+        if (val1 instanceof BigInteger && val2 instanceof BigInteger) {
+            if (val2.equals(BigInteger.ZERO)) {
+                throw ScriptRuntime.rangeErrorById("msg.division.zero");
+            }
+            return ((BigInteger) val1).divide((BigInteger) val2);
+        } else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
+            throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
+        } else {
+            return val1.doubleValue() / val2.doubleValue();
+        }
+    }
+
+    public static Number remainder(Number val1, Number val2) {
+        if (val1 instanceof BigInteger && val2 instanceof BigInteger) {
+            if (val2.equals(BigInteger.ZERO)) {
+                throw ScriptRuntime.rangeErrorById("msg.division.zero");
+            }
+            return ((BigInteger) val1).remainder((BigInteger) val2);
+        } else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
+            throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
+        } else {
+            return val1.doubleValue() % val2.doubleValue();
+        }
+    }
+
+    public static Number exponentiate(Number val1, Number val2) {
+        if (val1 instanceof BigInteger && val2 instanceof BigInteger) {
+            if (((BigInteger) val2).signum() == -1) {
+                throw ScriptRuntime.rangeErrorById("msg.bigint.negative.exponent");
+            }
+
+            try {
+                int intVal2 = ((BigInteger) val2).intValueExact();
+                return ((BigInteger) val1).pow(intVal2);
+            } catch (ArithmeticException e) {
+                // This is outside the scope of the ECMA262 specification.
+                throw ScriptRuntime.rangeErrorById("msg.bigint.out.of.range.arithmetic");
+            }
+        } else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
+            throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
+        } else {
+            return Math.pow(val1.doubleValue(), val2.doubleValue());
+        }
+    }
+
+    public static Number bitwiseAND(Number val1, Number val2) {
+        if (val1 instanceof BigInteger && val2 instanceof BigInteger) {
+            return ((BigInteger) val1).and((BigInteger) val2);
+        } else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
+            throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
+        } else {
+            int result = toInt32(val1.doubleValue()) & toInt32(val2.doubleValue());
+            return Double.valueOf(result);
+        }
+    }
+
+    public static Number bitwiseOR(Number val1, Number val2) {
+        if (val1 instanceof BigInteger && val2 instanceof BigInteger) {
+            return ((BigInteger) val1).or((BigInteger) val2);
+        } else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
+            throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
+        } else {
+            int result = toInt32(val1.doubleValue()) | toInt32(val2.doubleValue());
+            return Double.valueOf(result);
+        }
+    }
+
+    public static Number bitwiseXOR(Number val1, Number val2) {
+        if (val1 instanceof BigInteger && val2 instanceof BigInteger) {
+            return ((BigInteger) val1).xor((BigInteger) val2);
+        } else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
+            throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
+        } else {
+            int result = toInt32(val1.doubleValue()) ^ toInt32(val2.doubleValue());
+            return Double.valueOf(result);
+        }
+    }
+
+    public static Number leftShift(Number val1, Number val2) {
+        if (val1 instanceof BigInteger && val2 instanceof BigInteger) {
+            try {
+                int intVal2 = ((BigInteger) val2).intValueExact();
+                return ((BigInteger) val1).shiftLeft(intVal2);
+            } catch (ArithmeticException e) {
+                // This is outside the scope of the ECMA262 specification.
+                throw ScriptRuntime.rangeErrorById("msg.bigint.out.of.range.arithmetic");
+            }
+        } else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
+            throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
+        } else {
+            int result = toInt32(val1.doubleValue()) << toInt32(val2.doubleValue());
+            return Double.valueOf(result);
+        }
+    }
+
+    public static Number signedRightShift(Number val1, Number val2) {
+        if (val1 instanceof BigInteger && val2 instanceof BigInteger) {
+            try {
+                int intVal2 = ((BigInteger) val2).intValueExact();
+                return ((BigInteger) val1).shiftRight(intVal2);
+            } catch (ArithmeticException e) {
+                // This is outside the scope of the ECMA262 specification.
+                throw ScriptRuntime.rangeErrorById("msg.bigint.out.of.range.arithmetic");
+            }
+        } else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
+            throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
+        } else {
+            int result = toInt32(val1.doubleValue()) >> toInt32(val2.doubleValue());
+            return Double.valueOf(result);
+        }
+    }
+
+    public static Number bitwiseNOT(Number val) {
+        if (val instanceof BigInteger) {
+            return ((BigInteger) val).not();
+        } else {
+            int result = ~toInt32(val.doubleValue());
+            return Double.valueOf(result);
+        }
+    }
+
     /**
      * The method is only present for compatibility.
      *
      * @deprecated Use {@link #nameIncrDecr(Scriptable, String, Context, int)} instead
      */
     @Deprecated
-    public static Object nameIncrDecr(Scriptable scopeChain, String id,
-                                      int incrDecrMask)
-    {
+    public static Object nameIncrDecr(Scriptable scopeChain, String id, int incrDecrMask) {
         return nameIncrDecr(scopeChain, id, Context.getContext(), incrDecrMask);
     }
 
-    public static Object nameIncrDecr(Scriptable scopeChain, String id,
-                                      Context cx, int incrDecrMask)
-    {
+    public static Object nameIncrDecr(
+            Scriptable scopeChain, String id, Context cx, int incrDecrMask) {
         Scriptable target;
         Object value;
-      search: {
+        search:
+        {
             do {
                 if (cx.useDynamicScope && scopeChain.getParentScope() == null) {
                     scopeChain = checkDynamicScope(cx.topCallScope, scopeChain);
                 }
                 target = scopeChain;
                 do {
-                    if (target instanceof NativeWith &&
-                            target.getPrototype() instanceof XMLObject) {
+                    if (target instanceof NativeWith
+                            && target.getPrototype() instanceof XMLObject) {
                         break;
                     }
                     value = target.get(id, scopeChain);
@@ -2899,26 +3155,19 @@
                 } while (target != null);
                 scopeChain = scopeChain.getParentScope();
             } while (scopeChain != null);
-            throw notFoundError(scopeChain, id);
+            throw notFoundError(null, id);
         }
-        return doScriptableIncrDecr(target, id, scopeChain, value,
-                                    incrDecrMask);
+        return doScriptableIncrDecr(target, id, scopeChain, value, incrDecrMask);
     }
 
-    /**
-     * @deprecated Use {@link #propIncrDecr(Object, String, Context, Scriptable, int)} instead
-     */
+    /** @deprecated Use {@link #propIncrDecr(Object, String, Context, Scriptable, int)} instead */
     @Deprecated
-    public static Object propIncrDecr(Object obj, String id,
-                                      Context cx, int incrDecrMask)
-    {
+    public static Object propIncrDecr(Object obj, String id, Context cx, int incrDecrMask) {
         return propIncrDecr(obj, id, cx, getTopCallScope(cx), incrDecrMask);
     }
 
-    public static Object propIncrDecr(Object obj, String id,
-                                      Context cx, Scriptable scope,
-                                      int incrDecrMask)
-    {
+    public static Object propIncrDecr(
+            Object obj, String id, Context cx, Scriptable scope, int incrDecrMask) {
         Scriptable start = toObjectOrNull(cx, obj, scope);
         if (start == null) {
             throw undefReadError(obj, id);
@@ -2926,7 +3175,8 @@
 
         Scriptable target = start;
         Object value;
-      search: {
+        search:
+        {
             do {
                 value = target.get(id, start);
                 if (value != Scriptable.NOT_FOUND) {
@@ -2937,209 +3187,237 @@
             start.put(id, start, NaNobj);
             return NaNobj;
         }
-        return doScriptableIncrDecr(target, id, start, value,
-                                    incrDecrMask);
+        return doScriptableIncrDecr(target, id, start, value, incrDecrMask);
     }
 
-    private static Object doScriptableIncrDecr(Scriptable target,
-                                               String id,
-                                               Scriptable protoChainStart,
-                                               Object value,
-                                               int incrDecrMask)
-    {
-        boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
-        double number;
+    private static Object doScriptableIncrDecr(
+            Scriptable target,
+            String id,
+            Scriptable protoChainStart,
+            Object value,
+            int incrDecrMask) {
+        final boolean post = (incrDecrMask & Node.POST_FLAG) != 0;
+
+        Number number;
         if (value instanceof Number) {
-            number = ((Number)value).doubleValue();
+            number = (Number) value;
         } else {
-            number = toNumber(value);
-            if (post) {
-                // convert result to number
-                value = wrapNumber(number);
-            }
+            number = toNumeric(value);
         }
-        if ((incrDecrMask & Node.DECR_FLAG) == 0) {
-            ++number;
+
+        Number result;
+        if (number instanceof BigInteger) {
+            if ((incrDecrMask & Node.DECR_FLAG) == 0) {
+                result = ((BigInteger) number).add(BigInteger.ONE);
+            } else {
+                result = ((BigInteger) number).subtract(BigInteger.ONE);
+            }
         } else {
-            --number;
+            if ((incrDecrMask & Node.DECR_FLAG) == 0) {
+                result = number.doubleValue() + 1.0;
+            } else {
+                result = number.doubleValue() - 1.0;
+            }
         }
-        Number result = wrapNumber(number);
+
         target.put(id, protoChainStart, result);
         if (post) {
-            return value;
-        } else {
-            return result;
+            return number;
         }
+        return result;
     }
 
-    /**
-     * @deprecated Use {@link #elemIncrDecr(Object, Object, Context, Scriptable, int)} instead
-     */
+    /** @deprecated Use {@link #elemIncrDecr(Object, Object, Context, Scriptable, int)} instead */
     @Deprecated
-    public static Object elemIncrDecr(Object obj, Object index,
-                                      Context cx, int incrDecrMask)
-    {
+    public static Object elemIncrDecr(Object obj, Object index, Context cx, int incrDecrMask) {
         return elemIncrDecr(obj, index, cx, getTopCallScope(cx), incrDecrMask);
     }
 
-    public static Object elemIncrDecr(Object obj, Object index,
-                                      Context cx, Scriptable scope,
-                                      int incrDecrMask)
-    {
+    public static Object elemIncrDecr(
+            Object obj, Object index, Context cx, Scriptable scope, int incrDecrMask) {
         Object value = getObjectElem(obj, index, cx, scope);
-        boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
-        double number;
+        final boolean post = (incrDecrMask & Node.POST_FLAG) != 0;
+
+        Number number;
         if (value instanceof Number) {
-            number = ((Number)value).doubleValue();
+            number = (Number) value;
         } else {
-            number = toNumber(value);
-            if (post) {
-                // convert result to number
-                value = wrapNumber(number);
-            }
+            number = toNumeric(value);
         }
-        if ((incrDecrMask & Node.DECR_FLAG) == 0) {
-            ++number;
+
+        Number result;
+        if (number instanceof BigInteger) {
+            if ((incrDecrMask & Node.DECR_FLAG) == 0) {
+                result = ((BigInteger) number).add(BigInteger.ONE);
+            } else {
+                result = ((BigInteger) number).subtract(BigInteger.ONE);
+            }
         } else {
-            --number;
+            if ((incrDecrMask & Node.DECR_FLAG) == 0) {
+                result = number.doubleValue() + 1.0;
+            } else {
+                result = number.doubleValue() - 1.0;
+            }
         }
-        Number result = wrapNumber(number);
+
         setObjectElem(obj, index, result, cx, scope);
         if (post) {
-            return value;
-        } else {
-            return result;
+            return number;
         }
+        return result;
     }
 
-    /**
-     * @deprecated Use {@link #refIncrDecr(Ref, Context, Scriptable, int)} instead
-     */
+    /** @deprecated Use {@link #refIncrDecr(Ref, Context, Scriptable, int)} instead */
     @Deprecated
-    public static Object refIncrDecr(Ref ref, Context cx, int incrDecrMask)
-    {
+    public static Object refIncrDecr(Ref ref, Context cx, int incrDecrMask) {
         return refIncrDecr(ref, cx, getTopCallScope(cx), incrDecrMask);
     }
 
-    public static Object refIncrDecr(Ref ref, Context cx, Scriptable scope,
-                                     int incrDecrMask)
-    {
+    public static Object refIncrDecr(Ref ref, Context cx, Scriptable scope, int incrDecrMask) {
         Object value = ref.get(cx);
         boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
-        double number;
+
+        Number number;
         if (value instanceof Number) {
-            number = ((Number)value).doubleValue();
+            number = (Number) value;
         } else {
-            number = toNumber(value);
-            if (post) {
-                // convert result to number
-                value = wrapNumber(number);
-            }
+            number = toNumeric(value);
         }
-        if ((incrDecrMask & Node.DECR_FLAG) == 0) {
-            ++number;
+
+        Number result;
+        if (number instanceof BigInteger) {
+            if ((incrDecrMask & Node.DECR_FLAG) == 0) {
+                result = ((BigInteger) number).add(BigInteger.ONE);
+            } else {
+                result = ((BigInteger) number).subtract(BigInteger.ONE);
+            }
         } else {
-            --number;
+            if ((incrDecrMask & Node.DECR_FLAG) == 0) {
+                result = number.doubleValue() + 1.0;
+            } else {
+                result = number.doubleValue() - 1.0;
+            }
         }
-        Number result = wrapNumber(number);
+
         ref.set(cx, scope, result);
         if (post) {
-            return value;
-        } else {
-            return result;
+            return number;
+        }
+        return result;
+    }
+
+    public static Number negate(Number val) {
+        if (val instanceof BigInteger) {
+            return ((BigInteger) val).negate();
         }
+        return -val.doubleValue();
     }
 
     public static Object toPrimitive(Object val) {
         return toPrimitive(val, null);
     }
 
-    public static Object toPrimitive(Object val, Class<?> typeHint)
-    {
+    public static Object toPrimitive(Object val, Class<?> typeHint) {
         if (!(val instanceof Scriptable)) {
             return val;
         }
-        Scriptable s = (Scriptable)val;
+        Scriptable s = (Scriptable) val;
         Object result = s.getDefaultValue(typeHint);
         if ((result instanceof Scriptable) && !isSymbol(result))
-            throw typeError0("msg.bad.default.value");
+            throw typeErrorById("msg.bad.default.value");
         return result;
     }
 
     /**
      * Equality
      *
-     * See ECMA 11.9
+     * <p>See ECMA 11.9
      */
-    public static boolean eq(Object x, Object y)
-    {
-        if (x == null || x == Undefined.instance) {
-            if (y == null || y == Undefined.instance) {
+    public static boolean eq(Object x, Object y) {
+        if (x == null || Undefined.isUndefined(x)) {
+            if (y == null || Undefined.isUndefined(y)) {
                 return true;
             }
             if (y instanceof ScriptableObject) {
-                Object test = ((ScriptableObject)y).equivalentValues(x);
+                Object test = ((ScriptableObject) y).equivalentValues(x);
                 if (test != Scriptable.NOT_FOUND) {
-                    return ((Boolean)test).booleanValue();
+                    return ((Boolean) test).booleanValue();
                 }
             }
             return false;
+        } else if (x instanceof BigInteger) {
+            return eqBigInt((BigInteger) x, y);
         } else if (x instanceof Number) {
-            return eqNumber(((Number)x).doubleValue(), y);
+            return eqNumber(((Number) x).doubleValue(), y);
         } else if (x == y) {
             return true;
         } else if (x instanceof CharSequence) {
-            return eqString((CharSequence)x, y);
+            return eqString((CharSequence) x, y);
         } else if (x instanceof Boolean) {
-            boolean b = ((Boolean)x).booleanValue();
+            boolean b = ((Boolean) x).booleanValue();
             if (y instanceof Boolean) {
-                return b == ((Boolean)y).booleanValue();
+                return b == ((Boolean) y).booleanValue();
             }
             if (y instanceof ScriptableObject) {
-                Object test = ((ScriptableObject)y).equivalentValues(x);
+                Object test = ((ScriptableObject) y).equivalentValues(x);
                 if (test != Scriptable.NOT_FOUND) {
-                    return ((Boolean)test).booleanValue();
+                    return ((Boolean) test).booleanValue();
                 }
             }
             return eqNumber(b ? 1.0 : 0.0, y);
         } else if (x instanceof Scriptable) {
+            if (x instanceof Delegator) {
+                x = ((Delegator) x).getDelegee();
+                if (y instanceof Delegator) {
+                    return eq(x, ((Delegator) y).getDelegee());
+                }
+                if (x == y) {
+                    return true;
+                }
+            }
+            if (y instanceof Delegator && ((Delegator) y).getDelegee() == x) {
+                return true;
+            }
+
             if (y instanceof Scriptable) {
                 if (x instanceof ScriptableObject) {
-                    Object test = ((ScriptableObject)x).equivalentValues(y);
+                    Object test = ((ScriptableObject) x).equivalentValues(y);
                     if (test != Scriptable.NOT_FOUND) {
-                        return ((Boolean)test).booleanValue();
+                        return ((Boolean) test).booleanValue();
                     }
                 }
                 if (y instanceof ScriptableObject) {
-                    Object test = ((ScriptableObject)y).equivalentValues(x);
+                    Object test = ((ScriptableObject) y).equivalentValues(x);
                     if (test != Scriptable.NOT_FOUND) {
-                        return ((Boolean)test).booleanValue();
+                        return ((Boolean) test).booleanValue();
                     }
                 }
                 if (x instanceof Wrapper && y instanceof Wrapper) {
                     // See bug 413838. Effectively an extension to ECMA for
                     // the LiveConnect case.
-                    Object unwrappedX = ((Wrapper)x).unwrap();
-                    Object unwrappedY = ((Wrapper)y).unwrap();
-                    return unwrappedX == unwrappedY ||
-                           (isPrimitive(unwrappedX) &&
-                            isPrimitive(unwrappedY) &&
-                            eq(unwrappedX, unwrappedY));
+                    Object unwrappedX = ((Wrapper) x).unwrap();
+                    Object unwrappedY = ((Wrapper) y).unwrap();
+                    return unwrappedX == unwrappedY
+                            || (isPrimitive(unwrappedX)
+                                    && isPrimitive(unwrappedY)
+                                    && eq(unwrappedX, unwrappedY));
                 }
                 return false;
             } else if (y instanceof Boolean) {
                 if (x instanceof ScriptableObject) {
-                    Object test = ((ScriptableObject)x).equivalentValues(y);
+                    Object test = ((ScriptableObject) x).equivalentValues(y);
                     if (test != Scriptable.NOT_FOUND) {
-                        return ((Boolean)test).booleanValue();
+                        return ((Boolean) test).booleanValue();
                     }
                 }
-                double d = ((Boolean)y).booleanValue() ? 1.0 : 0.0;
+                double d = ((Boolean) y).booleanValue() ? 1.0 : 0.0;
                 return eqNumber(d, x);
+            } else if (y instanceof BigInteger) {
+                return eqBigInt((BigInteger) y, x);
             } else if (y instanceof Number) {
-                return eqNumber(((Number)y).doubleValue(), x);
+                return eqNumber(((Number) y).doubleValue(), x);
             } else if (y instanceof CharSequence) {
-                return eqString((CharSequence)y, x);
+                return eqString((CharSequence) y, x);
             }
             // covers the case when y == Undefined.instance as well
             return false;
@@ -3166,34 +3444,56 @@
         return eq(x, y);
     }
 
-    public static boolean isNaN(Object n) {
-        if (n == NaNobj) {
-            return true;
+    /** Implement "SameValueZero" from ECMA 7.2.9 */
+    public static boolean sameZero(Object x, Object y) {
+        if (!typeof(x).equals(typeof(y))) {
+            return false;
+        }
+        if (x instanceof BigInteger) {
+            return x.equals(y);
         }
+        if (x instanceof Number) {
+            if (isNaN(x) && isNaN(y)) {
+                return true;
+            }
+            final double dx = ((Number) x).doubleValue();
+            if (y instanceof Number) {
+                final double dy = ((Number) y).doubleValue();
+                if (((dx == negativeZero) && (dy == 0.0)) || ((dx == 0.0) && dy == negativeZero)) {
+                    return true;
+                }
+            }
+            return eqNumber(dx, y);
+        }
+        return eq(x, y);
+    }
+
+    public static boolean isNaN(Object n) {
         if (n instanceof Double) {
-            Double d = (Double)n;
-            return ((d == NaN) || Double.isNaN(d));
+            return ((Double) n).isNaN();
         }
         if (n instanceof Float) {
-            Float f = (Float)n;
-            return ((f == NaN) || Float.isNaN(f));
+            return ((Float) n).isNaN();
         }
         return false;
     }
 
     public static boolean isPrimitive(Object obj) {
-        return obj == null || obj == Undefined.instance ||
-                (obj instanceof Number) || (obj instanceof String) ||
-                (obj instanceof Boolean);
+        return obj == null
+                || Undefined.isUndefined(obj)
+                || (obj instanceof Number)
+                || (obj instanceof String)
+                || (obj instanceof Boolean);
     }
 
-    static boolean eqNumber(double x, Object y)
-    {
-        for (;;) {
-            if (y == null || y == Undefined.instance) {
+    static boolean eqNumber(double x, Object y) {
+        for (; ; ) {
+            if (y == null || Undefined.isUndefined(y)) {
                 return false;
+            } else if (y instanceof BigInteger) {
+                return eqBigInt((BigInteger) y, x);
             } else if (y instanceof Number) {
-                return x == ((Number)y).doubleValue();
+                return x == ((Number) y).doubleValue();
             } else if (y instanceof CharSequence) {
                 return x == toNumber(y);
             } else if (y instanceof Boolean) {
@@ -3203,9 +3503,9 @@
             } else if (y instanceof Scriptable) {
                 if (y instanceof ScriptableObject) {
                     Object xval = wrapNumber(x);
-                    Object test = ((ScriptableObject)y).equivalentValues(xval);
+                    Object test = ((ScriptableObject) y).equivalentValues(xval);
                     if (test != Scriptable.NOT_FOUND) {
-                        return ((Boolean)test).booleanValue();
+                        return ((Boolean) test).booleanValue();
                     }
                 }
                 y = toPrimitive(y);
@@ -3216,25 +3516,83 @@
         }
     }
 
-    private static boolean eqString(CharSequence x, Object y)
-    {
-        for (;;) {
-            if (y == null || y == Undefined.instance) {
+    static boolean eqBigInt(BigInteger x, Object y) {
+        for (; ; ) {
+            if (y == null || Undefined.isUndefined(y)) {
+                return false;
+            } else if (y instanceof BigInteger) {
+                return x.equals(y);
+            } else if (y instanceof Number) {
+                return eqBigInt(x, ((Number) y).doubleValue());
+            } else if (y instanceof CharSequence) {
+                BigInteger biy;
+                try {
+                    biy = toBigInt(y);
+                } catch (EcmaError e) {
+                    return false;
+                }
+                return x.equals(biy);
+            } else if (y instanceof Boolean) {
+                BigInteger biy = ((Boolean) y).booleanValue() ? BigInteger.ONE : BigInteger.ZERO;
+                return x.equals(biy);
+            } else if (isSymbol(y)) {
+                return false;
+            } else if (y instanceof Scriptable) {
+                if (y instanceof ScriptableObject) {
+                    Object test = ((ScriptableObject) y).equivalentValues(x);
+                    if (test != Scriptable.NOT_FOUND) {
+                        return ((Boolean) test).booleanValue();
+                    }
+                }
+                y = toPrimitive(y);
+            } else {
+                warnAboutNonJSObject(y);
+                return false;
+            }
+        }
+    }
+
+    private static boolean eqBigInt(BigInteger x, double y) {
+        if (Double.isNaN(y) || Double.isInfinite(y)) {
+            return false;
+        }
+
+        double d = Math.ceil(y);
+        if (d != y) {
+            return false;
+        }
+
+        BigDecimal bdx = new BigDecimal(x);
+        BigDecimal bdy = new BigDecimal(d, MathContext.UNLIMITED);
+        return bdx.compareTo(bdy) == 0;
+    }
+
+    private static boolean eqString(CharSequence x, Object y) {
+        for (; ; ) {
+            if (y == null || Undefined.isUndefined(y)) {
                 return false;
             } else if (y instanceof CharSequence) {
-                CharSequence c = (CharSequence)y;
+                CharSequence c = (CharSequence) y;
                 return x.length() == c.length() && x.toString().equals(c.toString());
+            } else if (y instanceof BigInteger) {
+                BigInteger bix;
+                try {
+                    bix = toBigInt(x);
+                } catch (EcmaError e) {
+                    return false;
+                }
+                return bix.equals(y);
             } else if (y instanceof Number) {
-                return toNumber(x.toString()) == ((Number)y).doubleValue();
+                return toNumber(x.toString()) == ((Number) y).doubleValue();
             } else if (y instanceof Boolean) {
                 return toNumber(x.toString()) == (((Boolean) y).booleanValue() ? 1.0 : 0.0);
             } else if (isSymbol(y)) {
                 return false;
             } else if (y instanceof Scriptable) {
                 if (y instanceof ScriptableObject) {
-                    Object test = ((ScriptableObject)y).equivalentValues(x.toString());
+                    Object test = ((ScriptableObject) y).equivalentValues(x.toString());
                     if (test != Scriptable.NOT_FOUND) {
-                        return ((Boolean)test).booleanValue();
+                        return ((Boolean) test).booleanValue();
                     }
                 }
                 y = toPrimitive(y);
@@ -3245,23 +3603,28 @@
             }
         }
     }
-    public static boolean shallowEq(Object x, Object y)
-    {
+
+    public static boolean shallowEq(Object x, Object y) {
         if (x == y) {
             if (!(x instanceof Number)) {
                 return true;
             }
             // NaN check
-            double d = ((Number)x).doubleValue();
-            return d == d;
+            double d = ((Number) x).doubleValue();
+            return !Double.isNaN(d);
         }
         if (x == null || x == Undefined.instance || x == Undefined.SCRIPTABLE_UNDEFINED) {
             if ((x == Undefined.instance && y == Undefined.SCRIPTABLE_UNDEFINED)
-                || (x == Undefined.SCRIPTABLE_UNDEFINED && y == Undefined.instance)) return true;
+                    || (x == Undefined.SCRIPTABLE_UNDEFINED && y == Undefined.instance))
+                return true;
             return false;
-        } else if (x instanceof Number) {
-            if (y instanceof Number) {
-                return ((Number)x).doubleValue() == ((Number)y).doubleValue();
+        } else if (x instanceof BigInteger) {
+            if (y instanceof BigInteger) {
+                return x.equals(y);
+            }
+        } else if (x instanceof Number && !(x instanceof BigInteger)) {
+            if (y instanceof Number && !(y instanceof BigInteger)) {
+                return ((Number) x).doubleValue() == ((Number) y).doubleValue();
             }
         } else if (x instanceof CharSequence) {
             if (y instanceof CharSequence) {
@@ -3273,7 +3636,19 @@
             }
         } else if (x instanceof Scriptable) {
             if (x instanceof Wrapper && y instanceof Wrapper) {
-                return ((Wrapper)x).unwrap() == ((Wrapper)y).unwrap();
+                return ((Wrapper) x).unwrap() == ((Wrapper) y).unwrap();
+            }
+            if (x instanceof Delegator) {
+                x = ((Delegator) x).getDelegee();
+                if (y instanceof Delegator) {
+                    return shallowEq(x, ((Delegator) y).getDelegee());
+                }
+                if (x == y) {
+                    return true;
+                }
+            }
+            if (y instanceof Delegator && ((Delegator) y).getDelegee() == x) {
+                return true;
             }
         } else {
             warnAboutNonJSObject(x);
@@ -3287,18 +3662,16 @@
      *
      * @return a instanceof b
      */
-    public static boolean instanceOf(Object a, Object b, Context cx)
-    {
+    public static boolean instanceOf(Object a, Object b, Context cx) {
         // Check RHS is an object
-        if (! (b instanceof Scriptable)) {
-            throw typeError0("msg.instanceof.not.object");
+        if (!(b instanceof Scriptable)) {
+            throw typeErrorById("msg.instanceof.not.object");
         }
 
         // for primitive values on LHS, return false
-        if (! (a instanceof Scriptable))
-            return false;
+        if (!(a instanceof Scriptable)) return false;
 
-        return ((Scriptable)b).hasInstance((Scriptable)a);
+        return ((Scriptable) b).hasInstance((Scriptable) a);
     }
 
     /**
@@ -3320,70 +3693,115 @@
     /**
      * The in operator.
      *
-     * This is a new JS 1.3 language feature.  The in operator mirrors
-     * the operation of the for .. in construct, and tests whether the
-     * rhs has the property given by the lhs.  It is different from the
-     * for .. in construct in that:
-     * <BR> - it doesn't perform ToObject on the right hand side
-     * <BR> - it returns true for DontEnum properties.
+     * <p>This is a new JS 1.3 language feature. The in operator mirrors the operation of the for ..
+     * in construct, and tests whether the rhs has the property given by the lhs. It is different
+     * from the for .. in construct in that: <br>
+     * - it doesn't perform ToObject on the right hand side <br>
+     * - it returns true for DontEnum properties.
+     *
      * @param a the left hand operand
      * @param b the right hand operand
-     *
      * @return true if property name or element number a is a property of b
      */
-    public static boolean in(Object a, Object b, Context cx)
-    {
+    public static boolean in(Object a, Object b, Context cx) {
         if (!(b instanceof Scriptable)) {
-            throw typeError0("msg.in.not.object");
+            throw typeErrorById("msg.in.not.object");
         }
 
-        return hasObjectElem((Scriptable)b, a, cx);
+        return hasObjectElem((Scriptable) b, a, cx);
     }
 
-    public static boolean cmp_LT(Object val1, Object val2)
-    {
-        double d1, d2;
+    public static boolean compare(Object val1, Object val2, int op) {
+        assert op == Token.GE || op == Token.LE || op == Token.GT || op == Token.LT;
+
         if (val1 instanceof Number && val2 instanceof Number) {
-            d1 = ((Number)val1).doubleValue();
-            d2 = ((Number)val2).doubleValue();
+            return compare((Number) val1, (Number) val2, op);
         } else {
             if ((val1 instanceof Symbol) || (val2 instanceof Symbol)) {
-                throw typeError0("msg.compare.symbol");
+                throw typeErrorById("msg.compare.symbol");
             }
-            if (val1 instanceof Scriptable)
+            if (val1 instanceof Scriptable) {
                 val1 = ((Scriptable) val1).getDefaultValue(NumberClass);
-            if (val2 instanceof Scriptable)
+            }
+            if (val2 instanceof Scriptable) {
                 val2 = ((Scriptable) val2).getDefaultValue(NumberClass);
+            }
             if (val1 instanceof CharSequence && val2 instanceof CharSequence) {
-                return val1.toString().compareTo(val2.toString()) < 0;
+                return compareTo(val1.toString(), val2.toString(), op);
             }
-            d1 = toNumber(val1);
-            d2 = toNumber(val2);
+            return compare(toNumeric(val1), toNumeric(val2), op);
         }
-        return d1 < d2;
     }
 
-    public static boolean cmp_LE(Object val1, Object val2)
-    {
-        double d1, d2;
-        if (val1 instanceof Number && val2 instanceof Number) {
-            d1 = ((Number)val1).doubleValue();
-            d2 = ((Number)val2).doubleValue();
-        } else {
-            if ((val1 instanceof Symbol) || (val2 instanceof Symbol)) {
-                throw typeError0("msg.compare.symbol");
+    public static boolean compare(Number val1, Number val2, int op) {
+        assert op == Token.GE || op == Token.LE || op == Token.GT || op == Token.LT;
+
+        if (val1 instanceof BigInteger && val2 instanceof BigInteger) {
+            return compareTo((BigInteger) val1, (BigInteger) val2, op);
+        } else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
+            BigDecimal bd1;
+            if (val1 instanceof BigInteger) {
+                bd1 = new BigDecimal((BigInteger) val1);
+            } else {
+                double d = val1.doubleValue();
+                if (Double.isNaN(d)) {
+                    return false;
+                } else if (d == Double.POSITIVE_INFINITY) {
+                    return op == Token.GE || op == Token.GT;
+                } else if (d == Double.NEGATIVE_INFINITY) {
+                    return op == Token.LE || op == Token.LT;
+                }
+                bd1 = new BigDecimal(d, MathContext.UNLIMITED);
             }
-            if (val1 instanceof Scriptable)
-                val1 = ((Scriptable) val1).getDefaultValue(NumberClass);
-            if (val2 instanceof Scriptable)
-                val2 = ((Scriptable) val2).getDefaultValue(NumberClass);
-            if (val1 instanceof CharSequence && val2 instanceof CharSequence) {
-                return val1.toString().compareTo(val2.toString()) <= 0;
+
+            BigDecimal bd2;
+            if (val2 instanceof BigInteger) {
+                bd2 = new BigDecimal((BigInteger) val2);
+            } else {
+                double d = val2.doubleValue();
+                if (Double.isNaN(d)) {
+                    return false;
+                } else if (d == Double.POSITIVE_INFINITY) {
+                    return op == Token.LE || op == Token.LT;
+                } else if (d == Double.NEGATIVE_INFINITY) {
+                    return op == Token.GE || op == Token.GT;
+                }
+                bd2 = new BigDecimal(d, MathContext.UNLIMITED);
             }
-            d1 = toNumber(val1);
-            d2 = toNumber(val2);
+
+            return compareTo(bd1, bd2, op);
+        }
+        return compareTo(val1.doubleValue(), val2.doubleValue(), op);
+    }
+
+    private static <T> boolean compareTo(Comparable<T> val1, T val2, int op) {
+        switch (op) {
+            case Token.GE:
+                return val1.compareTo(val2) >= 0;
+            case Token.LE:
+                return val1.compareTo(val2) <= 0;
+            case Token.GT:
+                return val1.compareTo(val2) > 0;
+            case Token.LT:
+                return val1.compareTo(val2) < 0;
+            default:
+                throw Kit.codeBug();
+        }
+    }
+
+    private static <T> boolean compareTo(double d1, double d2, int op) {
+        switch (op) {
+            case Token.GE:
+                return d1 >= d2;
+            case Token.LE:
+                return d1 <= d2;
+            case Token.GT:
+                return d1 > d2;
+            case Token.LT:
+                return d1 < d2;
+            default:
+                throw Kit.codeBug();
         }
-        return d1 <= d2;
     }
 
     // ------------------
@@ -3395,28 +3813,24 @@
         Class<?> globalClass = Kit.classOrNull(GLOBAL_CLASS);
         if (globalClass != null) {
             try {
-                Class<?>[] parm = { ScriptRuntime.ContextClass };
+                Class<?>[] parm = {ScriptRuntime.ContextClass};
                 Constructor<?> globalClassCtor = globalClass.getConstructor(parm);
-                Object[] arg = { cx };
+                Object[] arg = {cx};
                 return (ScriptableObject) globalClassCtor.newInstance(arg);
-            }
-            catch (RuntimeException e) {
+            } catch (RuntimeException e) {
                 throw e;
-            }
-            catch (Exception e) {
+            } catch (Exception e) {
                 // fall through...
             }
         }
         return new ImporterTopLevel(cx);
     }
 
-    public static boolean hasTopCall(Context cx)
-    {
+    public static boolean hasTopCall(Context cx) {
         return (cx.topCallScope != null);
     }
 
-    public static Scriptable getTopCallScope(Context cx)
-    {
+    public static Scriptable getTopCallScope(Context cx) {
         Scriptable scope = cx.topCallScope;
         if (scope == null) {
             throw new IllegalStateException();
@@ -3425,21 +3839,23 @@
     }
 
     /**
-     * @deprecated Use {@link #doTopCall(Callable, Context, Scriptable, Scriptable, Object[], boolean)} instead
+     * @deprecated Use {@link #doTopCall(Callable, Context, Scriptable, Scriptable, Object[],
+     *     boolean)} instead
      */
-    public static Object doTopCall(Callable callable,
-                                   Context cx, Scriptable scope,
-                                   Scriptable thisObj, Object[] args)
-    {
+    @Deprecated
+    public static Object doTopCall(
+            Callable callable, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         return doTopCall(callable, cx, scope, thisObj, args, cx.isTopLevelStrict);
     }
 
-    public static Object doTopCall(Callable callable,
-                                   Context cx, Scriptable scope,
-                                   Scriptable thisObj, Object[] args, boolean isTopLevelStrict)
-    {
-        if (scope == null)
-            throw new IllegalArgumentException();
+    public static Object doTopCall(
+            Callable callable,
+            Context cx,
+            Scriptable scope,
+            Scriptable thisObj,
+            Object[] args,
+            boolean isTopLevelStrict) {
+        if (scope == null) throw new IllegalArgumentException();
         if (cx.topCallScope != null) throw new IllegalStateException();
 
         Object result;
@@ -3466,20 +3882,18 @@
     }
 
     /**
-     * Return <tt>possibleDynamicScope</tt> if <tt>staticTopScope</tt>
-     * is present on its prototype chain and return <tt>staticTopScope</tt>
-     * otherwise.
-     * Should only be called when <tt>staticTopScope</tt> is top scope.
-     */
-    static Scriptable checkDynamicScope(Scriptable possibleDynamicScope,
-                                        Scriptable staticTopScope)
-    {
+     * Return <code>possibleDynamicScope</code> if <code>staticTopScope</code> is present on its
+     * prototype chain and return <code>staticTopScope</code> otherwise. Should only be called when
+     * <code>staticTopScope</code> is top scope.
+     */
+    static Scriptable checkDynamicScope(
+            Scriptable possibleDynamicScope, Scriptable staticTopScope) {
         // Return cx.topCallScope if scope
         if (possibleDynamicScope == staticTopScope) {
             return possibleDynamicScope;
         }
         Scriptable proto = possibleDynamicScope;
-        for (;;) {
+        for (; ; ) {
             proto = proto.getPrototype();
             if (proto == staticTopScope) {
                 return possibleDynamicScope;
@@ -3490,22 +3904,21 @@
         }
     }
 
-    public static void addInstructionCount(Context cx, int instructionsToAdd)
-    {
+    public static void addInstructionCount(Context cx, int instructionsToAdd) {
         cx.instructionCount += instructionsToAdd;
-        if (cx.instructionCount > cx.instructionThreshold)
-        {
+        if (cx.instructionCount > cx.instructionThreshold) {
             cx.observeInstructionCount(cx.instructionCount);
             cx.instructionCount = 0;
         }
     }
 
-    public static void initScript(NativeFunction funObj, Scriptable thisObj,
-                                  Context cx, Scriptable scope,
-                                  boolean evalScript)
-    {
-        if (cx.topCallScope == null)
-            throw new IllegalStateException();
+    public static void initScript(
+            NativeFunction funObj,
+            Scriptable thisObj,
+            Context cx,
+            Scriptable scope,
+            boolean evalScript) {
+        if (cx.topCallScope == null) throw new IllegalStateException();
 
         int varCount = funObj.getParamAndVarCount();
         if (varCount != 0) {
@@ -3517,7 +3930,7 @@
                 varScope = varScope.getParentScope();
             }
 
-            for (int i = varCount; i-- != 0;) {
+            for (int i = varCount; i-- != 0; ) {
                 String name = funObj.getParamOrVarName(i);
                 boolean isConst = funObj.getParamOrVarConst(i);
                 // Don't overwrite existing def if already defined in object
@@ -3526,10 +3939,12 @@
                     if (isConst) {
                         ScriptableObject.defineConstProperty(varScope, name);
                     } else if (!evalScript) {
-                        // Global var definitions are supposed to be DONTDELETE
-                        ScriptableObject.defineProperty(
-                            varScope, name, Undefined.instance,
-                            ScriptableObject.PERMANENT);
+                        if (!(funObj instanceof InterpretedFunction)
+                                || ((InterpretedFunction) funObj).hasFunctionNamed(name)) {
+                            // Global var definitions are supposed to be DONTDELETE
+                            ScriptableObject.defineProperty(
+                                    varScope, name, Undefined.instance, ScriptableObject.PERMANENT);
+                        }
                     } else {
                         varScope.put(name, varScope, Undefined.instance);
                     }
@@ -3541,73 +3956,61 @@
     }
 
     /**
-     * @deprecated Use {@link #createFunctionActivation(NativeFunction, Scriptable, Object[], boolean)} instead
+     * @deprecated Use {@link #createFunctionActivation(NativeFunction, Scriptable, Object[],
+     *     boolean)} instead
      */
     @Deprecated
-    public static Scriptable createFunctionActivation(NativeFunction funObj,
-                                                      Scriptable scope,
-                                                      Object[] args)
-    {
+    public static Scriptable createFunctionActivation(
+            NativeFunction funObj, Scriptable scope, Object[] args) {
         return createFunctionActivation(funObj, scope, args, false);
     }
 
-    public static Scriptable createFunctionActivation(NativeFunction funObj,
-                                                      Scriptable scope,
-                                                      Object[] args,
-                                                      boolean isStrict)
-    {
+    public static Scriptable createFunctionActivation(
+            NativeFunction funObj, Scriptable scope, Object[] args, boolean isStrict) {
         return new NativeCall(funObj, scope, args, false, isStrict);
     }
 
-    public static Scriptable createArrowFunctionActivation(NativeFunction funObj,
-                                                           Scriptable scope,
-                                                           Object[] args,
-                                                           boolean isStrict)
-    {
+    public static Scriptable createArrowFunctionActivation(
+            NativeFunction funObj, Scriptable scope, Object[] args, boolean isStrict) {
         return new NativeCall(funObj, scope, args, true, isStrict);
     }
 
-    public static void enterActivationFunction(Context cx,
-                                               Scriptable scope)
-    {
-        if (cx.topCallScope == null)
-            throw new IllegalStateException();
-        NativeCall call = (NativeCall)scope;
+    public static void enterActivationFunction(Context cx, Scriptable scope) {
+        if (cx.topCallScope == null) throw new IllegalStateException();
+        NativeCall call = (NativeCall) scope;
         call.parentActivationCall = cx.currentActivationCall;
         cx.currentActivationCall = call;
         call.defineAttributesForArguments();
     }
 
-    public static void exitActivationFunction(Context cx)
-    {
+    public static void exitActivationFunction(Context cx) {
         NativeCall call = cx.currentActivationCall;
         cx.currentActivationCall = call.parentActivationCall;
         call.parentActivationCall = null;
     }
 
-    static NativeCall findFunctionActivation(Context cx, Function f)
-    {
+    static NativeCall findFunctionActivation(Context cx, Function f) {
         NativeCall call = cx.currentActivationCall;
         while (call != null) {
-            if (call.function == f)
-                return call;
+            if (call.function == f) return call;
             call = call.parentActivationCall;
         }
         return null;
     }
 
-    public static Scriptable newCatchScope(Throwable t,
-                                           Scriptable lastCatchScope,
-                                           String exceptionName,
-                                           Context cx, Scriptable scope)
-    {
+    public static Scriptable newCatchScope(
+            Throwable t,
+            Scriptable lastCatchScope,
+            String exceptionName,
+            Context cx,
+            Scriptable scope) {
         Object obj;
         boolean cacheObj;
 
-    getObj:
+        getObj:
         if (t instanceof JavaScriptException) {
             cacheObj = false;
-            obj = ((JavaScriptException)t).getValue();
+            obj = ((JavaScriptException) t).getValue();
         } else {
             cacheObj = true;
 
@@ -3615,7 +4018,7 @@
             // the previous scope object
 
             if (lastCatchScope != null) {
-                NativeObject last = (NativeObject)lastCatchScope;
+                NativeObject last = (NativeObject) lastCatchScope;
                 obj = last.getAssociatedValue(t);
                 if (obj == null) Kit.codeBug();
                 break getObj;
@@ -3627,20 +4030,26 @@
             Throwable javaException = null;
 
             if (t instanceof EcmaError) {
-                EcmaError ee = (EcmaError)t;
+                EcmaError ee = (EcmaError) t;
                 re = ee;
                 type = TopLevel.NativeErrors.valueOf(ee.getName());
                 errorMsg = ee.getErrorMessage();
             } else if (t instanceof WrappedException) {
-                WrappedException we = (WrappedException)t;
+                WrappedException we = (WrappedException) t;
                 re = we;
                 javaException = we.getWrappedException();
-                type = TopLevel.NativeErrors.JavaException;
-                errorMsg = javaException.getClass().getName()
-                           +": "+javaException.getMessage();
+
+                if (!isVisible(cx, javaException)) {
+                    type = TopLevel.NativeErrors.InternalError;
+                    errorMsg = javaException.getMessage();
+                } else {
+                    type = TopLevel.NativeErrors.JavaException;
+                    errorMsg =
+                            javaException.getClass().getName() + ": " + javaException.getMessage();
+                }
             } else if (t instanceof EvaluatorException) {
                 // Pure evaluator exception, nor WrappedException instance
-                EvaluatorException ee = (EvaluatorException)t;
+                EvaluatorException ee = (EvaluatorException) t;
                 re = ee;
                 type = TopLevel.NativeErrors.InternalError;
                 errorMsg = ee.getMessage();
@@ -3663,9 +4072,9 @@
             int line = re.lineNumber();
             Object args[];
             if (line > 0) {
-                args = new Object[] { errorMsg, sourceUri, Integer.valueOf(line) };
+                args = new Object[] {errorMsg, sourceUri, Integer.valueOf(line)};
             } else {
-                args = new Object[] { errorMsg, sourceUri };
+                args = new Object[] {errorMsg, sourceUri};
             }
 
             Scriptable errorObject = newNativeError(cx, scope, type, args);
@@ -3675,33 +4084,40 @@
             }
 
             if (javaException != null && isVisible(cx, javaException)) {
-                Object wrap = cx.getWrapFactory().wrap(cx, scope, javaException,
-                                                       null);
+                Object wrap = cx.getWrapFactory().wrap(cx, scope, javaException, null);
                 ScriptableObject.defineProperty(
-                    errorObject, "javaException", wrap,
-                    ScriptableObject.PERMANENT | ScriptableObject.READONLY | ScriptableObject.DONTENUM);
+                        errorObject,
+                        "javaException",
+                        wrap,
+                        ScriptableObject.PERMANENT
+                                | ScriptableObject.READONLY
+                                | ScriptableObject.DONTENUM);
             }
             if (isVisible(cx, re)) {
                 Object wrap = cx.getWrapFactory().wrap(cx, scope, re, null);
                 ScriptableObject.defineProperty(
-                        errorObject, "rhinoException", wrap,
-                        ScriptableObject.PERMANENT | ScriptableObject.READONLY | ScriptableObject.DONTENUM);
+                        errorObject,
+                        "rhinoException",
+                        wrap,
+                        ScriptableObject.PERMANENT
+                                | ScriptableObject.READONLY
+                                | ScriptableObject.DONTENUM);
             }
             obj = errorObject;
         }
 
         NativeObject catchScopeObject = new NativeObject();
         // See ECMA 12.4
-        catchScopeObject.defineProperty(
-            exceptionName, obj, ScriptableObject.PERMANENT);
+        catchScopeObject.defineProperty(exceptionName, obj, ScriptableObject.PERMANENT);
 
         if (isVisible(cx, t)) {
             // Add special Rhino object __exception__ defined in the catch
             // scope that can be used to retrieve the Java exception associated
             // with the JavaScript exception (to get stack trace info, etc.)
             catchScopeObject.defineProperty(
-                "__exception__", Context.javaToJS(t, scope),
-                ScriptableObject.PERMANENT|ScriptableObject.DONTENUM);
+                    "__exception__",
+                    Context.javaToJS(t, scope),
+                    ScriptableObject.PERMANENT | ScriptableObject.DONTENUM);
         }
 
         if (cacheObj) {
@@ -3710,29 +4126,26 @@
         return catchScopeObject;
     }
 
-    public static Scriptable wrapException(Throwable t,
-                                           Scriptable scope,
-                                           Context cx) {
+    public static Scriptable wrapException(Throwable t, Scriptable scope, Context cx) {
         RhinoException re;
         String errorName;
         String errorMsg;
         Throwable javaException = null;
 
         if (t instanceof EcmaError) {
-            EcmaError ee = (EcmaError)t;
+            EcmaError ee = (EcmaError) t;
             re = ee;
             errorName = ee.getName();
             errorMsg = ee.getErrorMessage();
         } else if (t instanceof WrappedException) {
-            WrappedException we = (WrappedException)t;
+            WrappedException we = (WrappedException) t;
             re = we;
             javaException = we.getWrappedException();
             errorName = "JavaException";
-            errorMsg = javaException.getClass().getName()
-                       +": "+javaException.getMessage();
+            errorMsg = javaException.getClass().getName() + ": " + javaException.getMessage();
         } else if (t instanceof EvaluatorException) {
             // Pure evaluator exception, nor WrappedException instance
-            EvaluatorException ee = (EvaluatorException)t;
+            EvaluatorException ee = (EvaluatorException) t;
             re = ee;
             errorName = "InternalError";
             errorMsg = ee.getMessage();
@@ -3755,9 +4168,9 @@
         int line = re.lineNumber();
         Object args[];
         if (line > 0) {
-            args = new Object[] { errorMsg, sourceUri, Integer.valueOf(line) };
+            args = new Object[] {errorMsg, sourceUri, Integer.valueOf(line)};
         } else {
-            args = new Object[] { errorMsg, sourceUri };
+            args = new Object[] {errorMsg, sourceUri};
         }
 
         Scriptable errorObject = cx.newObject(scope, errorName, args);
@@ -3768,109 +4181,108 @@
         }
 
         if (javaException != null && isVisible(cx, javaException)) {
-            Object wrap = cx.getWrapFactory().wrap(cx, scope, javaException,
-                                                   null);
+            Object wrap = cx.getWrapFactory().wrap(cx, scope, javaException, null);
             ScriptableObject.defineProperty(
-                errorObject, "javaException", wrap,
-                ScriptableObject.PERMANENT | ScriptableObject.READONLY | ScriptableObject.DONTENUM);
+                    errorObject,
+                    "javaException",
+                    wrap,
+                    ScriptableObject.PERMANENT
+                            | ScriptableObject.READONLY
+                            | ScriptableObject.DONTENUM);
         }
         if (isVisible(cx, re)) {
             Object wrap = cx.getWrapFactory().wrap(cx, scope, re, null);
             ScriptableObject.defineProperty(
-                    errorObject, "rhinoException", wrap,
-                    ScriptableObject.PERMANENT | ScriptableObject.READONLY | ScriptableObject.DONTENUM);
+                    errorObject,
+                    "rhinoException",
+                    wrap,
+                    ScriptableObject.PERMANENT
+                            | ScriptableObject.READONLY
+                            | ScriptableObject.DONTENUM);
         }
         return errorObject;
     }
 
     private static boolean isVisible(Context cx, Object obj) {
         ClassShutter shutter = cx.getClassShutter();
-        return shutter == null ||
-            shutter.visibleToScripts(obj.getClass().getName());
+        return shutter == null || shutter.visibleToScripts(obj.getClass().getName());
     }
 
-    public static Scriptable enterWith(Object obj, Context cx,
-                                       Scriptable scope)
-    {
+    public static Scriptable enterWith(Object obj, Context cx, Scriptable scope) {
         Scriptable sobj = toObjectOrNull(cx, obj, scope);
         if (sobj == null) {
-            throw typeError1("msg.undef.with", toString(obj));
+            throw typeErrorById("msg.undef.with", toString(obj));
         }
         if (sobj instanceof XMLObject) {
-            XMLObject xmlObject = (XMLObject)sobj;
+            XMLObject xmlObject = (XMLObject) sobj;
             return xmlObject.enterWith(scope);
         }
         return new NativeWith(scope, sobj);
     }
 
-    public static Scriptable leaveWith(Scriptable scope)
-    {
-        NativeWith nw = (NativeWith)scope;
+    public static Scriptable leaveWith(Scriptable scope) {
+        NativeWith nw = (NativeWith) scope;
         return nw.getParentScope();
     }
 
-    public static Scriptable enterDotQuery(Object value, Scriptable scope)
-    {
+    public static Scriptable enterDotQuery(Object value, Scriptable scope) {
         if (!(value instanceof XMLObject)) {
             throw notXmlError(value);
         }
-        XMLObject object = (XMLObject)value;
+        XMLObject object = (XMLObject) value;
         return object.enterDotQuery(scope);
     }
 
-    public static Object updateDotQuery(boolean value, Scriptable scope)
-    {
+    public static Object updateDotQuery(boolean value, Scriptable scope) {
         // Return null to continue looping
-        NativeWith nw = (NativeWith)scope;
+        NativeWith nw = (NativeWith) scope;
         return nw.updateDotQuery(value);
     }
 
-    public static Scriptable leaveDotQuery(Scriptable scope)
-    {
-        NativeWith nw = (NativeWith)scope;
+    public static Scriptable leaveDotQuery(Scriptable scope) {
+        NativeWith nw = (NativeWith) scope;
         return nw.getParentScope();
     }
 
-    public static void setFunctionProtoAndParent(BaseFunction fn,
-                                                 Scriptable scope)
-    {
+    public static void setFunctionProtoAndParent(BaseFunction fn, Scriptable scope) {
+        setFunctionProtoAndParent(fn, scope, false);
+    }
+
+    public static void setFunctionProtoAndParent(
+            BaseFunction fn, Scriptable scope, boolean es6GeneratorFunction) {
         fn.setParentScope(scope);
-        fn.setPrototype(ScriptableObject.getFunctionPrototype(scope));
+        if (es6GeneratorFunction) {
+            fn.setPrototype(ScriptableObject.getGeneratorFunctionPrototype(scope));
+        } else {
+            fn.setPrototype(ScriptableObject.getFunctionPrototype(scope));
+        }
     }
 
-    public static void setObjectProtoAndParent(ScriptableObject object,
-                                               Scriptable scope)
-    {
+    public static void setObjectProtoAndParent(ScriptableObject object, Scriptable scope) {
         // Compared with function it always sets the scope to top scope
         scope = ScriptableObject.getTopLevelScope(scope);
         object.setParentScope(scope);
-        Scriptable proto
-            = ScriptableObject.getClassPrototype(scope, object.getClassName());
+        Scriptable proto = ScriptableObject.getClassPrototype(scope, object.getClassName());
         object.setPrototype(proto);
     }
 
-    public static void setBuiltinProtoAndParent(ScriptableObject object,
-                                                Scriptable scope,
-                                                TopLevel.Builtins type)
-    {
+    public static void setBuiltinProtoAndParent(
+            ScriptableObject object, Scriptable scope, TopLevel.Builtins type) {
         scope = ScriptableObject.getTopLevelScope(scope);
         object.setParentScope(scope);
         object.setPrototype(TopLevel.getBuiltinPrototype(scope, type));
     }
 
-
-    public static void initFunction(Context cx, Scriptable scope,
-                                    NativeFunction function, int type,
-                                    boolean fromEvalCode)
-    {
+    public static void initFunction(
+            Context cx, Scriptable scope, NativeFunction function, int type, boolean fromEvalCode) {
         if (type == FunctionNode.FUNCTION_STATEMENT) {
             String name = function.getFunctionName();
             if (name != null && name.length() != 0) {
                 if (!fromEvalCode) {
                     // ECMA specifies that functions defined in global and
                     // function scope outside eval should have DONTDELETE set.
-                    ScriptableObject.defineProperty
-                        (scope, name, function, ScriptableObject.PERMANENT);
+                    ScriptableObject.defineProperty(
+                            scope, name, function, ScriptableObject.PERMANENT);
                 } else {
                     scope.put(name, scope, function);
                 }
@@ -3891,10 +4303,8 @@
         }
     }
 
-    public static Scriptable newArrayLiteral(Object[] objects,
-                                             int[] skipIndices,
-                                             Context cx, Scriptable scope)
-    {
+    public static Scriptable newArrayLiteral(
+            Object[] objects, int[] skipIndices, Context cx, Scriptable scope) {
         final int SKIP_DENSITY = 2;
         int count = objects.length;
         int skipCount = 0;
@@ -3938,60 +4348,71 @@
     }
 
     /**
-     * This method is here for backward compat with existing compiled code.  It
-     * is called when an object literal is compiled.  The next instance will be
-     * the version called from new code.
+     * This method is here for backward compat with existing compiled code. It is called when an
+     * object literal is compiled. The next instance will be the version called from new code.
      * <strong>This method only present for compatibility.</strong>
-     * @deprecated Use {@link #newObjectLiteral(Object[], Object[], int[], Context, Scriptable)} instead
+     *
+     * @deprecated Use {@link #newObjectLiteral(Object[], Object[], int[], Context, Scriptable)}
+     *     instead
      */
     @Deprecated
-    public static Scriptable newObjectLiteral(Object[] propertyIds,
-                                              Object[] propertyValues,
-                                              Context cx, Scriptable scope)
-    {
+    public static Scriptable newObjectLiteral(
+            Object[] propertyIds, Object[] propertyValues, Context cx, Scriptable scope) {
         // Passing null for getterSetters means no getters or setters
         return newObjectLiteral(propertyIds, propertyValues, null, cx, scope);
     }
 
-    public static Scriptable newObjectLiteral(Object[] propertyIds,
-                                              Object[] propertyValues,
-                                              int [] getterSetters,
-                                              Context cx, Scriptable scope)
-    {
+    public static Scriptable newObjectLiteral(
+            Object[] propertyIds,
+            Object[] propertyValues,
+            int[] getterSetters,
+            Context cx,
+            Scriptable scope) {
         Scriptable object = cx.newObject(scope);
-        for (int i = 0, end = propertyIds.length; i != end; ++i) {
+        int end = propertyIds == null ? 0 : propertyIds.length;
+        for (int i = 0; i != end; ++i) {
             Object id = propertyIds[i];
+
+            // -1 for property getter, 1 for property setter, 0 for a regular value property
             int getterSetter = getterSetters == null ? 0 : getterSetters[i];
             Object value = propertyValues[i];
-            if (id instanceof String) {
-                if (getterSetter == 0) {
-                    if (isSpecialProperty((String)id)) {
-                        Ref ref = specialRef(object, (String)id, cx, scope);
+
+            if (getterSetter == 0) {
+                if (id instanceof Symbol) {
+                    Symbol sym = (Symbol) id;
+                    SymbolScriptable so = (SymbolScriptable) object;
+                    so.put(sym, object, value);
+                } else if (id instanceof Integer) {
+                    int index = ((Integer) id).intValue();
+                    object.put(index, object, value);
+                } else {
+                    String stringId = ScriptRuntime.toString(id);
+                    if (isSpecialProperty(stringId)) {
+                        Ref ref = specialRef(object, stringId, cx, scope);
                         ref.set(cx, scope, value);
                     } else {
-                        object.put((String)id, object, value);
+                        object.put(stringId, object, value);
                     }
-                } else {
-                    ScriptableObject so = (ScriptableObject)object;
-                    Callable getterOrSetter = (Callable)value;
-                    boolean isSetter = getterSetter == 1;
-                    so.setGetterOrSetter((String)id, 0, getterOrSetter, isSetter);
                 }
             } else {
-                int index = ((Integer)id).intValue();
-                object.put(index, object, value);
+                ScriptableObject so = (ScriptableObject) object;
+                Callable getterOrSetter = (Callable) value;
+                boolean isSetter = getterSetter == 1;
+                // XXX: Do we have to handle Symbol here.
+                // This will be required, when conputedprops are supported.
+                String key = id instanceof String ? (String) id : null;
+                int index = key == null ? ((Integer) id).intValue() : 0;
+                so.setGetterOrSetter(key, index, getterOrSetter, isSetter);
             }
         }
         return object;
     }
 
-    public static boolean isArrayObject(Object obj)
-    {
+    public static boolean isArrayObject(Object obj) {
         return obj instanceof NativeArray || obj instanceof Arguments;
     }
 
-    public static Object[] getArrayElements(Scriptable object)
-    {
+    public static Object[] getArrayElements(Scriptable object) {
         Context cx = Context.getContext();
         long longLen = NativeArray.getLengthProperty(cx, object);
         if (longLen > Integer.MAX_VALUE) {
@@ -4001,71 +4422,70 @@
         int len = (int) longLen;
         if (len == 0) {
             return ScriptRuntime.emptyArgs;
-        } else {
-            Object[] result = new Object[len];
-            for (int i=0; i < len; i++) {
-                Object elem = ScriptableObject.getProperty(object, i);
-                result[i] = (elem == Scriptable.NOT_FOUND) ? Undefined.instance
-                                                           : elem;
-            }
-            return result;
         }
+        Object[] result = new Object[len];
+        for (int i = 0; i < len; i++) {
+            Object elem = ScriptableObject.getProperty(object, i);
+            result[i] = (elem == Scriptable.NOT_FOUND) ? Undefined.instance : elem;
+        }
+        return result;
     }
 
     static void checkDeprecated(Context cx, String name) {
         int version = cx.getLanguageVersion();
         if (version >= Context.VERSION_1_4 || version == Context.VERSION_DEFAULT) {
-            String msg = getMessage1("msg.deprec.ctor", name);
-            if (version == Context.VERSION_DEFAULT)
-                Context.reportWarning(msg);
-            else
-                throw Context.reportRuntimeError(msg);
+            String msg = getMessageById("msg.deprec.ctor", name);
+            if (version == Context.VERSION_DEFAULT) Context.reportWarning(msg);
+            else throw Context.reportRuntimeError(msg);
         }
     }
 
-    public static String getMessage0(String messageId)
-    {
+    /** @deprecated Use {@link #getMessageById(String messageId, Object... args)} instead */
+    @Deprecated
+    public static String getMessage0(String messageId) {
         return getMessage(messageId, null);
     }
 
-    public static String getMessage1(String messageId, Object arg1)
-    {
+    /** @deprecated Use {@link #getMessageById(String messageId, Object... args)} instead */
+    @Deprecated
+    public static String getMessage1(String messageId, Object arg1) {
         Object[] arguments = {arg1};
         return getMessage(messageId, arguments);
     }
 
-    public static String getMessage2(
-        String messageId, Object arg1, Object arg2)
-    {
+    /** @deprecated Use {@link #getMessageById(String messageId, Object... args)} instead */
+    @Deprecated
+    public static String getMessage2(String messageId, Object arg1, Object arg2) {
         Object[] arguments = {arg1, arg2};
         return getMessage(messageId, arguments);
     }
 
-    public static String getMessage3(
-        String messageId, Object arg1, Object arg2, Object arg3)
-    {
+    /** @deprecated Use {@link #getMessageById(String messageId, Object... args)} instead */
+    @Deprecated
+    public static String getMessage3(String messageId, Object arg1, Object arg2, Object arg3) {
         Object[] arguments = {arg1, arg2, arg3};
         return getMessage(messageId, arguments);
     }
 
+    /** @deprecated Use {@link #getMessageById(String messageId, Object... args)} instead */
+    @Deprecated
     public static String getMessage4(
-        String messageId, Object arg1, Object arg2, Object arg3, Object arg4)
-    {
+            String messageId, Object arg1, Object arg2, Object arg3, Object arg4) {
         Object[] arguments = {arg1, arg2, arg3, arg4};
         return getMessage(messageId, arguments);
     }
 
     /**
-     * This is an interface defining a message provider. Create your
-     * own implementation to override the default error message provider.
+     * This is an interface defining a message provider. Create your own implementation to override
+     * the default error message provider.
      *
      * @author Mike Harm
      */
     public interface MessageProvider {
 
         /**
-         * Returns a textual message identified by the given messageId,
-         * parameterized by the given arguments.
+         * Returns a textual message identified by the given messageId, parameterized by the given
+         * arguments.
          *
          * @param messageId the identifier of the message
          * @param arguments the arguments to fill into the message
@@ -4073,21 +4493,26 @@
         String getMessage(String messageId, Object[] arguments);
     }
 
-    public static MessageProvider messageProvider = new DefaultMessageProvider();
+    public static final MessageProvider messageProvider = new DefaultMessageProvider();
 
-    public static String getMessage(String messageId, Object[] arguments)
-    {
+    /** @deprecated Use {@link #getMessageById(String messageId, Object... args)} instead */
+    @Deprecated
+    public static String getMessage(String messageId, Object[] arguments) {
         return messageProvider.getMessage(messageId, arguments);
     }
 
+    public static String getMessageById(String messageId, Object... args) {
+        return messageProvider.getMessage(messageId, args);
+    }
+
     /* OPT there's a noticable delay for the first error!  Maybe it'd
      * make sense to use a ListResourceBundle instead of a properties
      * file to avoid (synchronized) text parsing.
      */
     private static class DefaultMessageProvider implements MessageProvider {
+        @Override
         public String getMessage(String messageId, Object[] arguments) {
-            final String defaultResource
-                = "org.mozilla.javascript.resources.Messages";
+            final String defaultResource = "org.mozilla.javascript.resources.Messages";
 
             Context cx = Context.getCurrentContext();
             Locale locale = cx != null ? cx.getLocale() : Locale.getDefault();
@@ -4099,8 +4524,8 @@
             try {
                 formatString = rb.getString(messageId);
             } catch (java.util.MissingResourceException mre) {
-                throw new RuntimeException
-                    ("no message resource found for message property "+ messageId);
+                throw new RuntimeException(
+                        "no message resource found for message property " + messageId);
             }
 
             /*
@@ -4113,17 +4538,13 @@
         }
     }
 
-    public static EcmaError constructError(String error, String message)
-    {
+    public static EcmaError constructError(String error, String message) {
         int[] linep = new int[1];
         String filename = Context.getSourcePositionFromStack(linep);
         return constructError(error, message, filename, linep[0], null, 0);
     }
 
-    public static EcmaError constructError(String error,
-                                           String message,
-                                           int lineNumberDelta)
-    {
+    public static EcmaError constructError(String error, String message, int lineNumberDelta) {
         int[] linep = new int[1];
         String filename = Context.getSourcePositionFromStack(linep);
         if (linep[0] != 0) {
@@ -4132,104 +4553,99 @@
         return constructError(error, message, filename, linep[0], null, 0);
     }
 
-    public static EcmaError constructError(String error,
-                                           String message,
-                                           String sourceName,
-                                           int lineNumber,
-                                           String lineSource,
-                                           int columnNumber)
-    {
-        return new EcmaError(error, message, sourceName,
-                             lineNumber, lineSource, columnNumber);
+    public static EcmaError constructError(
+            String error,
+            String message,
+            String sourceName,
+            int lineNumber,
+            String lineSource,
+            int columnNumber) {
+        return new EcmaError(error, message, sourceName, lineNumber, lineSource, columnNumber);
     }
 
-    public static EcmaError rangeError(String message)
-    {
+    public static EcmaError rangeError(String message) {
         return constructError("RangeError", message);
     }
 
-    public static EcmaError typeError(String message)
-    {
+    public static EcmaError rangeErrorById(String messageId, Object... args) {
+        String msg = getMessageById(messageId, args);
+        return rangeError(msg);
+    }
+
+    public static EcmaError typeError(String message) {
         return constructError("TypeError", message);
     }
 
-    public static EcmaError typeError0(String messageId)
-    {
+    public static EcmaError typeErrorById(String messageId, Object... args) {
+        String msg = getMessageById(messageId, args);
+        return typeError(msg);
+    }
+
+    /** @deprecated Use {@link #typeErrorById(String messageId, Object... args)} instead */
+    @Deprecated
+    public static EcmaError typeError0(String messageId) {
         String msg = getMessage0(messageId);
         return typeError(msg);
     }
 
-    public static EcmaError typeError1(String messageId, Object arg1)
-    {
+    /** @deprecated Use {@link #typeErrorById(String messageId, Object... args)} instead */
+    @Deprecated
+    public static EcmaError typeError1(String messageId, Object arg1) {
         String msg = getMessage1(messageId, arg1);
         return typeError(msg);
     }
 
-    public static EcmaError typeError2(String messageId, Object arg1,
-                                       Object arg2)
-    {
+    /** @deprecated Use {@link #typeErrorById(String messageId, Object... args)} instead */
+    @Deprecated
+    public static EcmaError typeError2(String messageId, Object arg1, Object arg2) {
         String msg = getMessage2(messageId, arg1, arg2);
         return typeError(msg);
     }
 
-    public static EcmaError typeError3(String messageId, String arg1,
-                                       String arg2, String arg3)
-    {
+    /** @deprecated Use {@link #typeErrorById(String messageId, Object... args)} instead */
+    @Deprecated
+    public static EcmaError typeError3(String messageId, String arg1, String arg2, String arg3) {
         String msg = getMessage3(messageId, arg1, arg2, arg3);
         return typeError(msg);
     }
 
-    public static RuntimeException undefReadError(Object object, Object id)
-    {
-        return typeError2("msg.undef.prop.read", toString(object), toString(id));
+    public static RuntimeException undefReadError(Object object, Object id) {
+        return typeErrorById("msg.undef.prop.read", toString(object), toString(id));
     }
 
-    public static RuntimeException undefCallError(Object object, Object id)
-    {
-        return typeError2("msg.undef.method.call", toString(object), toString(id));
+    public static RuntimeException undefCallError(Object object, Object id) {
+        return typeErrorById("msg.undef.method.call", toString(object), toString(id));
     }
 
-    public static RuntimeException undefWriteError(Object object,
-                                                   Object id,
-                                                   Object value)
-    {
-        return typeError3("msg.undef.prop.write", toString(object), toString(id),
-                          toString(value));
+    public static RuntimeException undefWriteError(Object object, Object id, Object value) {
+        return typeErrorById(
+                "msg.undef.prop.write", toString(object), toString(id), toString(value));
     }
 
-    private static RuntimeException undefDeleteError(Object object, Object id)
-    {
-        throw typeError2("msg.undef.prop.delete", toString(object), toString(id));
+    private static RuntimeException undefDeleteError(Object object, Object id) {
+        throw typeErrorById("msg.undef.prop.delete", toString(object), toString(id));
     }
 
-    public static RuntimeException notFoundError(Scriptable object,
-                                                 String property)
-    {
+    public static RuntimeException notFoundError(Scriptable object, String property) {
         // XXX: use object to improve the error message
-        String msg = getMessage1("msg.is.not.defined", property);
+        String msg = getMessageById("msg.is.not.defined", property);
         throw constructError("ReferenceError", msg);
     }
 
-    public static RuntimeException notFunctionError(Object value)
-    {
+    public static RuntimeException notFunctionError(Object value) {
         return notFunctionError(value, value);
     }
 
-    public static RuntimeException notFunctionError(Object value,
-                                                    Object messageHelper)
-    {
+    public static RuntimeException notFunctionError(Object value, Object messageHelper) {
         // Use value for better error reporting
-        String msg = (messageHelper == null)
-                     ? "null" : messageHelper.toString();
+        String msg = (messageHelper == null) ? "null" : messageHelper.toString();
         if (value == Scriptable.NOT_FOUND) {
-            return typeError1("msg.function.not.found", msg);
+            return typeErrorById("msg.function.not.found", msg);
         }
-        return typeError2("msg.isnt.function", msg, typeof(value));
+        return typeErrorById("msg.isnt.function", msg, typeof(value));
     }
 
-    public static RuntimeException notFunctionError(Object obj, Object value,
-            String propertyName)
-    {
+    public static RuntimeException notFunctionError(Object obj, Object value, String propertyName) {
         // Use obj and value for better error reporting
         String objString = toString(obj);
         if (obj instanceof NativeFunction) {
@@ -4241,65 +4657,100 @@
             }
         }
         if (value == Scriptable.NOT_FOUND) {
-            return typeError2("msg.function.not.found.in", propertyName,
-                    objString);
+            return typeErrorById("msg.function.not.found.in", propertyName, objString);
         }
-        return typeError3("msg.isnt.function.in", propertyName, objString,
-                          typeof(value));
+        return typeErrorById("msg.isnt.function.in", propertyName, objString, typeof(value));
     }
 
-    private static RuntimeException notXmlError(Object value)
-    {
-        throw typeError1("msg.isnt.xml.object", toString(value));
+    private static RuntimeException notXmlError(Object value) {
+        throw typeErrorById("msg.isnt.xml.object", toString(value));
     }
 
-    private static void warnAboutNonJSObject(Object nonJSObject)
-    {
-        final String omitParam = ScriptRuntime.getMessage0("params.omit.non.js.object.warning");
+    public static EcmaError syntaxError(String message) {
+        return constructError("SyntaxError", message);
+    }
+
+    public static EcmaError syntaxErrorById(String messageId, Object... args) {
+        String msg = getMessageById(messageId, args);
+        return syntaxError(msg);
+    }
+
+    private static void warnAboutNonJSObject(Object nonJSObject) {
+        final String omitParam = ScriptRuntime.getMessageById("params.omit.non.js.object.warning");
         if (!"true".equals(omitParam)) {
-            String message = ScriptRuntime.getMessage2("msg.non.js.object.warning",nonJSObject,nonJSObject.getClass().getName());
+            String message =
+                    ScriptRuntime.getMessageById(
+                            "msg.non.js.object.warning",
+                            nonJSObject,
+                            nonJSObject.getClass().getName());
             Context.reportWarning(message);
             // Just to be sure that it would be noticed
             System.err.println(message);
         }
     }
 
-    public static RegExpProxy getRegExpProxy(Context cx)
-    {
+    public static RegExpProxy getRegExpProxy(Context cx) {
         return cx.getRegExpProxy();
     }
 
-    public static void setRegExpProxy(Context cx, RegExpProxy proxy)
-    {
+    public static void setRegExpProxy(Context cx, RegExpProxy proxy) {
         if (proxy == null) throw new IllegalArgumentException();
         cx.regExpProxy = proxy;
     }
 
-    public static RegExpProxy checkRegExpProxy(Context cx)
-    {
+    public static RegExpProxy checkRegExpProxy(Context cx) {
         RegExpProxy result = getRegExpProxy(cx);
         if (result == null) {
-            throw Context.reportRuntimeError0("msg.no.regexp");
+            throw Context.reportRuntimeErrorById("msg.no.regexp");
         }
         return result;
     }
 
-    public static Scriptable wrapRegExp(Context cx, Scriptable scope,
-                                        Object compiled) {
+    public static Scriptable wrapRegExp(Context cx, Scriptable scope, Object compiled) {
         return cx.getRegExpProxy().wrapRegExp(cx, scope, compiled);
     }
 
-    private static XMLLib currentXMLLib(Context cx)
-    {
+    public static Scriptable getTemplateLiteralCallSite(
+            Context cx, Scriptable scope, Object[] strings, int index) {
+        Object callsite = strings[index];
+
+        if (callsite instanceof Scriptable) return (Scriptable) callsite;
+
+        assert callsite instanceof String[];
+        String[] vals = (String[]) callsite;
+        assert (vals.length & 1) == 0;
+
+        ScriptableObject siteObj = (ScriptableObject) cx.newArray(scope, vals.length >>> 1);
+        ScriptableObject rawObj = (ScriptableObject) cx.newArray(scope, vals.length >>> 1);
+
+        siteObj.put("raw", siteObj, rawObj);
+        siteObj.setAttributes("raw", ScriptableObject.DONTENUM);
+
+        for (int i = 0, n = vals.length; i < n; i += 2) {
+            int idx = i >>> 1;
+            siteObj.put(idx, siteObj, (vals[i] == null ? Undefined.instance : vals[i]));
+
+            rawObj.put(idx, rawObj, vals[i + 1]);
+        }
+
+        AbstractEcmaObjectOperations.setIntegrityLevel(
+                cx, rawObj, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.FROZEN);
+        AbstractEcmaObjectOperations.setIntegrityLevel(
+                cx, siteObj, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.FROZEN);
+
+        strings[index] = siteObj;
+
+        return siteObj;
+    }
+
+    private static XMLLib currentXMLLib(Context cx) {
         // Scripts should be running to access this
-        if (cx.topCallScope == null)
-            throw new IllegalStateException();
+        if (cx.topCallScope == null) throw new IllegalStateException();
 
         XMLLib xmlLib = cx.cachedXMLLib;
         if (xmlLib == null) {
             xmlLib = XMLLib.extractFromScope(cx.topCallScope);
-            if (xmlLib == null)
-                throw new IllegalStateException();
+            if (xmlLib == null) throw new IllegalStateException();
             cx.cachedXMLLib = xmlLib;
         }
 
@@ -4312,8 +4763,7 @@
      * @param value Unescaped text
      * @return The escaped text
      */
-    public static String escapeAttributeValue(Object value, Context cx)
-    {
+    public static String escapeAttributeValue(Object value, Context cx) {
         XMLLib xmlLib = currentXMLLib(cx);
         return xmlLib.escapeAttributeValue(value);
     }
@@ -4324,152 +4774,130 @@
      * @param value Unescaped text
      * @return The escaped text
      */
-    public static String escapeTextValue(Object value, Context cx)
-    {
+    public static String escapeTextValue(Object value, Context cx) {
         XMLLib xmlLib = currentXMLLib(cx);
         return xmlLib.escapeTextValue(value);
     }
 
-    public static Ref memberRef(Object obj, Object elem,
-                                Context cx, int memberTypeFlags)
-    {
+    public static Ref memberRef(Object obj, Object elem, Context cx, int memberTypeFlags) {
         if (!(obj instanceof XMLObject)) {
             throw notXmlError(obj);
         }
-        XMLObject xmlObject = (XMLObject)obj;
+        XMLObject xmlObject = (XMLObject) obj;
         return xmlObject.memberRef(cx, elem, memberTypeFlags);
     }
 
-    public static Ref memberRef(Object obj, Object namespace, Object elem,
-                                Context cx, int memberTypeFlags)
-    {
+    public static Ref memberRef(
+            Object obj, Object namespace, Object elem, Context cx, int memberTypeFlags) {
         if (!(obj instanceof XMLObject)) {
             throw notXmlError(obj);
         }
-        XMLObject xmlObject = (XMLObject)obj;
+        XMLObject xmlObject = (XMLObject) obj;
         return xmlObject.memberRef(cx, namespace, elem, memberTypeFlags);
     }
 
-    public static Ref nameRef(Object name, Context cx,
-                              Scriptable scope, int memberTypeFlags)
-    {
+    public static Ref nameRef(Object name, Context cx, Scriptable scope, int memberTypeFlags) {
         XMLLib xmlLib = currentXMLLib(cx);
         return xmlLib.nameRef(cx, name, scope, memberTypeFlags);
     }
 
-    public static Ref nameRef(Object namespace, Object name, Context cx,
-                              Scriptable scope, int memberTypeFlags)
-    {
+    public static Ref nameRef(
+            Object namespace, Object name, Context cx, Scriptable scope, int memberTypeFlags) {
         XMLLib xmlLib = currentXMLLib(cx);
         return xmlLib.nameRef(cx, namespace, name, scope, memberTypeFlags);
     }
 
-    private static void storeIndexResult(Context cx, int index)
-    {
-        cx.scratchIndex = index;
-    }
-
-    static int lastIndexResult(Context cx)
-    {
-        return cx.scratchIndex;
-    }
-
-    public static void storeUint32Result(Context cx, long value)
-    {
-        if ((value >>> 32) != 0)
-            throw new IllegalArgumentException();
+    public static void storeUint32Result(Context cx, long value) {
+        if ((value >>> 32) != 0) throw new IllegalArgumentException();
         cx.scratchUint32 = value;
     }
 
-    public static long lastUint32Result(Context cx)
-    {
+    public static long lastUint32Result(Context cx) {
         long value = cx.scratchUint32;
-        if ((value >>> 32) != 0)
-            throw new IllegalStateException();
+        if ((value >>> 32) != 0) throw new IllegalStateException();
         return value;
     }
 
-    private static void storeScriptable(Context cx, Scriptable value)
-    {
+    private static void storeScriptable(Context cx, Scriptable value) {
         // The previously stored scratchScriptable should be consumed
-        if (cx.scratchScriptable != null)
-            throw new IllegalStateException();
+        if (cx.scratchScriptable != null) throw new IllegalStateException();
         cx.scratchScriptable = value;
     }
 
-    public static Scriptable lastStoredScriptable(Context cx)
-    {
+    public static Scriptable lastStoredScriptable(Context cx) {
         Scriptable result = cx.scratchScriptable;
         cx.scratchScriptable = null;
         return result;
     }
 
-    static String makeUrlForGeneratedScript
-        (boolean isEval, String masterScriptUrl, int masterScriptLine)
-    {
+    static String makeUrlForGeneratedScript(
+            boolean isEval, String masterScriptUrl, int masterScriptLine) {
         if (isEval) {
-            return masterScriptUrl+'#'+masterScriptLine+"(eval)";
-        } else {
-            return masterScriptUrl+'#'+masterScriptLine+"(Function)";
+            return masterScriptUrl + '#' + masterScriptLine + "(eval)";
         }
+        return masterScriptUrl + '#' + masterScriptLine + "(Function)";
     }
 
     static boolean isGeneratedScript(String sourceUrl) {
         // ALERT: this may clash with a valid URL containing (eval) or
         // (Function)
-        return sourceUrl.indexOf("(eval)") >= 0
-               || sourceUrl.indexOf("(Function)") >= 0;
+        return sourceUrl.indexOf("(eval)") >= 0 || sourceUrl.indexOf("(Function)") >= 0;
     }
 
     /**
-     * Not all "NativeSymbol" instances are actually symbols. So account for that here rather than just
-     * by using an "instanceof" check.
+     * Not all "NativeSymbol" instances are actually symbols. So account for that here rather than
+     * just by using an "instanceof" check.
      */
     static boolean isSymbol(Object obj) {
-        return (((obj instanceof NativeSymbol) &&
-                ((NativeSymbol)obj).isSymbol())) || (obj instanceof SymbolKey);
+        return (((obj instanceof NativeSymbol) && ((NativeSymbol) obj).isSymbol()))
+                || (obj instanceof SymbolKey);
     }
 
-    private static RuntimeException errorWithClassName(String msg, Object val)
-    {
-        return Context.reportRuntimeError1(msg, val.getClass().getName());
+    private static RuntimeException errorWithClassName(String msg, Object val) {
+        return Context.reportRuntimeErrorById(msg, val.getClass().getName());
     }
 
     /**
      * Equivalent to executing "new Error(message, sourceFileName, sourceLineNo)" from JavaScript.
+     *
      * @param cx the current context
      * @param scope the current scope
      * @param message the message
      * @return a JavaScriptException you should throw
      */
-    public static JavaScriptException throwError(Context cx, Scriptable scope,
-            String message) {
-      int[] linep = { 0 };
-      String filename = Context.getSourcePositionFromStack(linep);
-        final Scriptable error = newBuiltinObject(cx, scope,
-                TopLevel.Builtins.Error, new Object[] { message, filename, Integer.valueOf(linep[0]) });
+    public static JavaScriptException throwError(Context cx, Scriptable scope, String message) {
+        int[] linep = {0};
+        String filename = Context.getSourcePositionFromStack(linep);
+        final Scriptable error =
+                newBuiltinObject(
+                        cx,
+                        scope,
+                        TopLevel.Builtins.Error,
+                        new Object[] {message, filename, Integer.valueOf(linep[0])});
         return new JavaScriptException(error, filename, linep[0]);
     }
 
-
     /**
-     * Equivalent to executing "new $constructorName(message, sourceFileName, sourceLineNo)" from JavaScript.
+     * Equivalent to executing "new $constructorName(message, sourceFileName, sourceLineNo)" from
+     * JavaScript.
+     *
      * @param cx the current context
      * @param scope the current scope
      * @param message the message
      * @return a JavaScriptException you should throw
      */
-    public static JavaScriptException throwCustomError(Context cx, Scriptable scope, String constructorName,
-            String message) {
-      int[] linep = { 0 };
-      String filename = Context.getSourcePositionFromStack(linep);
-      final Scriptable error =  cx.newObject(scope, constructorName,
-        new Object[] { message, filename, Integer.valueOf(linep[0]) });
-      return new JavaScriptException(error, filename, linep[0]);
+    public static JavaScriptException throwCustomError(
+            Context cx, Scriptable scope, String constructorName, String message) {
+        int[] linep = {0};
+        String filename = Context.getSourcePositionFromStack(linep);
+        final Scriptable error =
+                cx.newObject(
+                        scope,
+                        constructorName,
+                        new Object[] {message, filename, Integer.valueOf(linep[0])});
+        return new JavaScriptException(error, filename, linep[0]);
     }
 
     public static final Object[] emptyArgs = new Object[0];
     public static final String[] emptyStrings = new String[0];
-
-
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ScriptStackElement.java rhino-1.7.14/src/org/mozilla/javascript/ScriptStackElement.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ScriptStackElement.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/ScriptStackElement.java	2022-01-06 22:57:21.000000000 +0100
@@ -14,7 +14,7 @@
  */
 public final class ScriptStackElement implements Serializable {
 
-    static final long serialVersionUID = -6416688260860477449L;
+    private static final long serialVersionUID = -6416688260860477449L;
 
     public final String fileName;
     public final String functionName;
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/SecureCaller.java rhino-1.7.14/src/org/mozilla/javascript/SecureCaller.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/SecureCaller.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/SecureCaller.java	2022-01-06 22:57:21.000000000 +0100
@@ -49,6 +49,7 @@
         // runtime permission
         final ClassLoader classLoader = (ClassLoader)AccessController.doPrivileged(
             new PrivilegedAction<Object>() {
+                @Override
                 public Object run() {
                     return thread.getContextClassLoader();
                 }
@@ -80,6 +81,7 @@
                     caller = (SecureCaller)AccessController.doPrivileged(
                             new PrivilegedExceptionAction<Object>()
                     {
+                        @Override
                         public Object run() throws Exception
                         {
                             ClassLoader effectiveClassLoader;
@@ -127,6 +129,7 @@
     {
         return (byte[])AccessController.doPrivileged(new PrivilegedAction<Object>()
         {
+            @Override
             public Object run()
             {
                 return loadBytecodePrivileged();
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/SecurityController.java rhino-1.7.14/src/org/mozilla/javascript/SecurityController.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/SecurityController.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/SecurityController.java	2022-01-06 22:57:21.000000000 +0100
@@ -89,10 +89,10 @@
      * The method uses the SecurityController instance associated with the
      * current {@link Context} to construct proper dynamic domain and create
      * corresponding class loader.
-     * <par>
+     * <p>
      * If no SecurityController is associated with the current {@link Context} ,
      * the method calls {@link Context#createClassLoader(ClassLoader parent)}.
-     *
+     * </p>
      * @param parent parent class loader. If null,
      *        {@link Context#getApplicationClassLoader()} will be used.
      * @param staticDomain static security domain.
@@ -141,7 +141,7 @@
      * allowed only if it is allowed according to the Java stack on the
      * moment of the <i>execWithDomain</i> call and <i>securityDomain</i>.
      * Any call to {@link #getDynamicSecurityDomain(Object)} during
-     * execution of <tt>callable.call(cx, scope, thisObj, args)</tt>
+     * execution of <code>callable.call(cx, scope, thisObj, args)</code>
      * should return a domain incorporate restrictions imposed by
      * <i>securityDomain</i> and Java stack on the moment of callWithDomain
      * invocation.
@@ -155,6 +155,7 @@
     {
         return execWithDomain(cx, scope, new Script()
         {
+            @Override
             public Object exec(Context cx, Scriptable scope)
             {
                 return callable.call(cx, scope, thisObj, args);
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/SecurityUtilities.java rhino-1.7.14/src/org/mozilla/javascript/SecurityUtilities.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/SecurityUtilities.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/SecurityUtilities.java	2022-01-06 22:57:21.000000000 +0100
@@ -26,6 +26,7 @@
         return AccessController.doPrivileged(
             new PrivilegedAction<String>()
             {
+                @Override
                 public String run()
                 {
                     return System.getProperty(name);
@@ -38,6 +39,7 @@
         return AccessController.doPrivileged(
                 new PrivilegedAction<ProtectionDomain>()
                 {
+                    @Override
                     public ProtectionDomain run()
                     {
                         return clazz.getProtectionDomain();
@@ -57,6 +59,7 @@
         if (securityManager instanceof RhinoSecurityManager) {
             return AccessController.doPrivileged(
                 new PrivilegedAction<ProtectionDomain>() {
+                    @Override
                     public ProtectionDomain run() {
                         Class<?> c = ((RhinoSecurityManager) securityManager)
                                     .getCurrentScriptClass();
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/serialize/ScriptableInputStream.java rhino-1.7.14/src/org/mozilla/javascript/serialize/ScriptableInputStream.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/serialize/ScriptableInputStream.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/serialize/ScriptableInputStream.java	2022-01-06 22:57:21.000000000 +0100
@@ -8,9 +8,15 @@
 
 package org.mozilla.javascript.serialize;
 
-import java.io.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
 
-import org.mozilla.javascript.*;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.Undefined;
+import org.mozilla.javascript.UniqueTag;
 
 /**
  * Class ScriptableInputStream is used to read in a JavaScript
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/serialize/ScriptableOutputStream.java rhino-1.7.14/src/org/mozilla/javascript/serialize/ScriptableOutputStream.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/serialize/ScriptableOutputStream.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/serialize/ScriptableOutputStream.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,12 +6,17 @@
 
 package org.mozilla.javascript.serialize;
 
-import java.util.Map;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
 import java.util.HashMap;
+import java.util.Map;
 import java.util.StringTokenizer;
-import java.io.*;
 
-import org.mozilla.javascript.*;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+import org.mozilla.javascript.UniqueTag;
 
 /**
  * Class ScriptableOutputStream is an ObjectOutputStream used
@@ -163,7 +168,7 @@
 
     static class PendingLookup implements Serializable
     {
-        static final long serialVersionUID = -2692990309789917727L;
+        private static final long serialVersionUID = -2692990309789917727L;
 
         PendingLookup(String name) { this.name = name; }
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/Slot.java rhino-1.7.14/src/org/mozilla/javascript/Slot.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/Slot.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/Slot.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,119 @@
+package org.mozilla.javascript;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+
+/**
+ * A Slot is the base class for all properties stored in the ScriptableObject class. There are a
+ * number of different types of slots. This base class represents an "ordinary" property such as a
+ * primitive type or another object. Separate classes are used to represent properties that have
+ * various types of getter and setter methods.
+ */
+public class Slot implements Serializable {
+    private static final long serialVersionUID = -6090581677123995491L;
+    Object name; // This can change due to caching
+    int indexOrHash;
+    private short attributes;
+    Object value;
+    transient Slot next; // next in hash table bucket
+    transient Slot orderedNext; // next in linked list
+
+    Slot(Object name, int indexOrHash, int attributes) {
+        this.name = name;
+        this.indexOrHash = indexOrHash;
+        this.attributes = (short) attributes;
+    }
+
+    /**
+     * Return true if this is a base-class "Slot". Sadly too much code breaks if we try to do this
+     * any other way.
+     */
+    boolean isValueSlot() {
+        return true;
+    }
+
+    /**
+     * Return true if this is a "setter slot" which, which we need to know for some legacy support.
+     */
+    boolean isSetterSlot() {
+        return false;
+    }
+
+    protected Slot(Slot oldSlot) {
+        name = oldSlot.name;
+        indexOrHash = oldSlot.indexOrHash;
+        attributes = oldSlot.attributes;
+        value = oldSlot.value;
+        next = oldSlot.next;
+        orderedNext = oldSlot.orderedNext;
+    }
+
+    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+        in.defaultReadObject();
+        if (name != null) {
+            indexOrHash = name.hashCode();
+        }
+    }
+
+    public boolean setValue(Object value, Scriptable owner, Scriptable start) {
+        if ((attributes & ScriptableObject.READONLY) != 0) {
+            if (Context.isCurrentContextStrict()) {
+                throw ScriptRuntime.typeErrorById("msg.modify.readonly", name);
+            }
+            return true;
+        }
+        if (owner == start) {
+            this.value = value;
+            return true;
+        }
+        return false;
+    }
+
+    public Object getValue(Scriptable start) {
+        return value;
+    }
+
+    int getAttributes() {
+        return attributes;
+    }
+
+    synchronized void setAttributes(int value) {
+        ScriptableObject.checkValidAttributes(value);
+        attributes = (short) value;
+    }
+
+    ScriptableObject getPropertyDescriptor(Context cx, Scriptable scope) {
+        return ScriptableObject.buildDataDescriptor(scope, value, attributes);
+    }
+
+    protected void throwNoSetterException(Scriptable start, Object newValue) {
+        Context cx = Context.getContext();
+        if (cx.isStrictMode()
+                ||
+                // Based on TC39 ES3.1 Draft of 9-Feb-2009, 8.12.4, step 2,
+                // we should throw a TypeError in this case.
+                cx.hasFeature(Context.FEATURE_STRICT_MODE)) {
+
+            String prop = "";
+            if (name != null) {
+                prop = "[" + start.getClassName() + "]." + name;
+            }
+            throw ScriptRuntime.typeErrorById(
+                    "msg.set.prop.no.setter", prop, Context.toString(newValue));
+        }
+    }
+
+    /**
+     * Return a JavaScript function that represents the "setter". This is used by some legacy
+     * functionality. Return null if there is no setter.
+     */
+    Function getSetterFunction(String name, Scriptable scope) {
+        return null;
+    }
+
+    /** Same for the "getter." */
+    Function getGetterFunction(String name, Scriptable scope) {
+        return null;
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/SlotMapContainer.java rhino-1.7.14/src/org/mozilla/javascript/SlotMapContainer.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/SlotMapContainer.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/SlotMapContainer.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,108 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+import java.util.Iterator;
+
+/**
+ * This class holds the various SlotMaps of various types, and knows how to atomically switch
+ * between them when we need to so that we use the right data structure at the right time.
+ */
+class SlotMapContainer implements SlotMap {
+
+    /**
+     * Once the object has this many properties in it, we will replace the EmbeddedSlotMap with
+     * HashSlotMap. We can adjust this parameter to balance performance for typical objects versus
+     * performance for huge objects with many collisions.
+     */
+    private static final int LARGE_HASH_SIZE = 2000;
+
+    private static final int DEFAULT_SIZE = 10;
+
+    protected SlotMap map;
+
+    SlotMapContainer() {
+        this(DEFAULT_SIZE);
+    }
+
+    SlotMapContainer(int initialSize) {
+        if (initialSize > LARGE_HASH_SIZE) {
+            map = new HashSlotMap();
+        } else {
+            map = new EmbeddedSlotMap();
+        }
+    }
+
+    @Override
+    public int size() {
+        return map.size();
+    }
+
+    public int dirtySize() {
+        return map.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return map.isEmpty();
+    }
+
+    @Override
+    public Slot modify(Object key, int index, int attributes) {
+        checkMapSize();
+        return map.modify(key, index, attributes);
+    }
+
+    @Override
+    public void replace(Slot oldSlot, Slot newSlot) {
+        map.replace(oldSlot, newSlot);
+    }
+
+    @Override
+    public Slot query(Object key, int index) {
+        return map.query(key, index);
+    }
+
+    @Override
+    public void add(Slot newSlot) {
+        checkMapSize();
+        map.add(newSlot);
+    }
+
+    @Override
+    public void remove(Object key, int index) {
+        map.remove(key, index);
+    }
+
+    @Override
+    public Iterator<Slot> iterator() {
+        return map.iterator();
+    }
+
+    public long readLock() {
+        // No locking in the default implementation
+        return 0L;
+    }
+
+    public void unlockRead(long stamp) {
+        // No locking in the default implementation
+    }
+
+    /**
+     * Before inserting a new item in the map, check and see if we need to expand from the embedded
+     * map to a HashMap that is more robust against large numbers of hash collisions.
+     */
+    protected void checkMapSize() {
+        if ((map instanceof EmbeddedSlotMap) && map.size() >= LARGE_HASH_SIZE) {
+            SlotMap newMap = new HashSlotMap();
+            for (Slot s : map) {
+                newMap.add(s);
+            }
+            map = newMap;
+        }
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/SlotMap.java rhino-1.7.14/src/org/mozilla/javascript/SlotMap.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/SlotMap.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/SlotMap.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,63 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+/**
+ * A SlotMap is an interface to the main data structure that contains all the "Slots" that back a
+ * ScriptableObject. It is the primary property map in Rhino. It is Iterable but does not implement
+ * java.util.Map because that comes with a bunch of overhead that we do not need.
+ *
+ * <p>This class generally has a bit of a strange interface, and its interactions with
+ * ScriptableObject are complex. Many attempts to make this interface more elegant have resulted in
+ * substantial performance regressions so we are doing the best that we can.
+ */
+public interface SlotMap extends Iterable<Slot> {
+
+    /** Return the size of the map. */
+    int size();
+
+    /** Return whether the map is empty. */
+    boolean isEmpty();
+
+    /**
+     * Return the Slot that matches EITHER "key" or "index". (It will use "key" if it is not null,
+     * and otherwise "index".) If no slot exists, then create a default slot class.
+     *
+     * @param key The key for the slot, which should be a String or a Symbol.
+     * @param index if key is zero, then this will be used as the key instead.
+     * @param attributes the attributes to be set on the slot if a new slot is created. Existing
+     *     slots will not be modified.
+     * @return a Slot, which will be created anew if no such slot exists.
+     */
+    Slot modify(Object key, int index, int attributes);
+
+    /**
+     * Retrieve the slot at EITHER key or index, or return null if the slot cannot be found.
+     *
+     * @param key The key for the slot, which should be a String or a Symbol.
+     * @param index if key is zero, then this will be used as the key instead.
+     * @return either the Slot that matched the key and index, or null
+     */
+    Slot query(Object key, int index);
+
+    /** Replace "slot" with a new slot. This is used to change slot types. */
+    void replace(Slot oldSlot, Slot newSlot);
+
+    /**
+     * Insert a new slot to the map. Both "name" and "indexOrHash" must be populated. Note that
+     * ScriptableObject generally adds slots via the "modify" method.
+     */
+    void add(Slot newSlot);
+
+    /**
+     * Remove the slot at either "key" or "index".
+     *
+     * @param key The key for the slot, which should be a String or a Symbol.
+     * @param index if key is zero, then this will be used as the key instead.
+     */
+    void remove(Object key, int index);
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/Sorting.java rhino-1.7.14/src/org/mozilla/javascript/Sorting.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/Sorting.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/Sorting.java	2022-01-06 22:57:21.000000000 +0100
@@ -1,17 +1,28 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
 package org.mozilla.javascript;
 
-import java.util.Arrays;
 import java.util.Comparator;
 
 public final class Sorting {
     private static final int SMALLSORT = 16;
 
-    public static void insertionSort(Object[] a, Comparator<Object> cmp)
+    private static final Sorting sorting = new Sorting();
+
+    private Sorting() {}
+
+    public static Sorting get() {
+        return sorting;
+    }
+
+    public void insertionSort(Object[] a, Comparator<Object> cmp)
     {
         insertionSort(a, 0, a.length - 1, cmp);
     }
 
-    public static void insertionSort(Object[] a, int start, int end, Comparator<Object> cmp)
+    private static void insertionSort(Object[] a, int start, int end, Comparator<Object> cmp)
     {
         int i = start;
         while (i <= end) {
@@ -32,12 +43,12 @@
     recursion is too deep, and then use insertion sort for the rest.
     This is the same basic algorithm used by the GNU Standard C++ library.
     */
-    public static void hybridSort(Object[] a, Comparator<Object> cmp)
+    public void hybridSort(Object[] a, Comparator<Object> cmp)
     {
         hybridSort(a, 0, a.length - 1, cmp, log2(a.length) * 2);
     }
 
-    private static void hybridSort(Object[] a, int start, int end, Comparator<Object> cmp, int maxdepth)
+    private void hybridSort(Object[] a, int start, int end, Comparator<Object> cmp, int maxdepth)
     {
         if (start < end) {
             if ((maxdepth == 0) || ((end - start) <= SMALLSORT)) {
@@ -51,31 +62,44 @@
     }
 
     /*
-    Quicksort-style partitioning, using the Hoare partition scheme described on Wikipedia.
+    Quicksort-style partitioning, using the Hoare partition scheme as coded by
+    Sedgewick at https://algs4.cs.princeton.edu/23quicksort/Quick.java.html.
     Use the "median of three" method to determine which index to pivot on, and then
     separate the array into two halves based on the pivot.
     */
-    private static int partition(Object[] a, int start, int end, Comparator<Object> cmp) {
-        Object pivot = a[median(start, end, start + ((end - start) / 2))];
-        int i = start - 1;
+    private int partition(Object[] a, int start, int end, Comparator<Object> cmp) {
+        final int p = median(a, start, end, cmp);
+        final Object pivot = a[p];
+        a[p] = a[start];
+        a[start] = pivot;
+
+        int i = start;
         int j = end + 1;
+
         while (true) {
-            do {
-                i++;
-            } while (cmp.compare(a[i], pivot) < 0);
-            do {
-                j--;
-            } while (cmp.compare(a[j], pivot) > 0);
+            while (cmp.compare(a[++i], pivot) < 0) {
+                if (i == end) {
+                    break;
+                }
+            }
+            while (cmp.compare(a[--j], pivot) >= 0) {
+                if (j == start) {
+                    break;
+                }
+            }
             if (i >= j) {
-                return j;
+                break;
             }
             swap(a, i, j);
         }
+
+        swap(a, start, j);
+        return j;
     }
 
     private static void swap(Object[] a, int l, int h)
     {
-        Object tmp = a[l];
+        final Object tmp = a[l];
         a[l] = a[h];
         a[h] = tmp;
     }
@@ -85,10 +109,28 @@
         return (int)(Math.log10(n) / Math.log10(2.0));
     }
 
-    private static int median(int n1, int n2, int n3)
+    /*
+    Return the index of the median of three elements in the specified array range -- the
+    first, the last, and the one in the middle.
+    */
+    public int median(final Object[] a, int start, int end, Comparator<Object> cmp)
     {
-        int[] a = {n1, n2, n3};
-        Arrays.sort(a);
-        return a[1];
+        final int m = start + ((end - start) / 2);
+        int smallest = start;
+
+        if (cmp.compare(a[smallest], a[m]) > 0) {
+            smallest = m;
+        }
+        if (cmp.compare(a[smallest], a[end]) > 0) {
+            smallest = end;
+        }
+
+        if (smallest == start) {
+            return (cmp.compare(a[m], a[end]) < 0) ? m : end;
+        }
+        if (smallest == m) {
+            return (cmp.compare(a[start], a[end]) < 0) ? start : end;
+        }
+        return (cmp.compare(a[start], a[m]) < 0) ? start : m;
     }
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/SpecialRef.java rhino-1.7.14/src/org/mozilla/javascript/SpecialRef.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/SpecialRef.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/SpecialRef.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,9 +6,8 @@
 
 package org.mozilla.javascript;
 
-class SpecialRef extends Ref
-{
-    static final long serialVersionUID = -7521596632456797847L;
+class SpecialRef extends Ref {
+    private static final long serialVersionUID = -7521596632456797847L;
 
     private static final int SPECIAL_NONE = 0;
     private static final int SPECIAL_PROTO = 1;
@@ -18,16 +17,13 @@
     private int type;
     private String name;
 
-    private SpecialRef(Scriptable target, int type, String name)
-    {
+    private SpecialRef(Scriptable target, int type, String name) {
         this.target = target;
         this.type = type;
         this.name = name;
     }
 
-    static Ref createSpecial(Context cx, Scriptable scope, Object object,
-                             String name)
-    {
+    static Ref createSpecial(Context cx, Scriptable scope, Object object, String name) {
         Scriptable target = ScriptRuntime.toObjectOrNull(cx, object, scope);
         if (target == null) {
             throw ScriptRuntime.undefReadError(object, name);
@@ -51,17 +47,16 @@
     }
 
     @Override
-    public Object get(Context cx)
-    {
+    public Object get(Context cx) {
         switch (type) {
-          case SPECIAL_NONE:
-            return ScriptRuntime.getObjectProp(target, name, cx);
-          case SPECIAL_PROTO:
-            return target.getPrototype();
-          case SPECIAL_PARENT:
-            return target.getParentScope();
-          default:
-            throw Kit.codeBug();
+            case SPECIAL_NONE:
+                return ScriptRuntime.getObjectProp(target, name, cx);
+            case SPECIAL_PROTO:
+                return target.getPrototype();
+            case SPECIAL_PARENT:
+                return target.getParentScope();
+            default:
+                throw Kit.codeBug();
         }
     }
 
@@ -72,46 +67,73 @@
     }
 
     @Override
-    public Object set(Context cx, Scriptable scope, Object value)
-    {
+    public Object set(Context cx, Scriptable scope, Object value) {
         switch (type) {
-          case SPECIAL_NONE:
-            return ScriptRuntime.setObjectProp(target, name, value, cx);
-          case SPECIAL_PROTO:
-          case SPECIAL_PARENT:
-            {
-                Scriptable obj = ScriptRuntime.toObjectOrNull(cx, value, scope);
-                if (obj != null) {
-                    // Check that obj does not contain on its prototype/scope
-                    // chain to prevent cycles
-                    Scriptable search = obj;
-                    do {
-                        if (search == target) {
-                            throw Context.reportRuntimeError1(
-                                "msg.cyclic.value", name);
+            case SPECIAL_NONE:
+                return ScriptRuntime.setObjectProp(target, name, value, cx);
+            case SPECIAL_PROTO:
+            case SPECIAL_PARENT:
+                {
+                    Scriptable obj = ScriptRuntime.toObjectOrNull(cx, value, scope);
+                    if (obj != null) {
+                        // Check that obj does not contain on its prototype/scope
+                        // chain to prevent cycles
+                        Scriptable search = obj;
+                        do {
+                            if (search == target) {
+                                throw Context.reportRuntimeErrorById("msg.cyclic.value", name);
+                            }
+                            if (type == SPECIAL_PROTO) {
+                                search = search.getPrototype();
+                            } else {
+                                search = search.getParentScope();
+                            }
+                        } while (search != null);
+                    }
+                    if (type == SPECIAL_PROTO) {
+                        if (target instanceof ScriptableObject
+                                && !((ScriptableObject) target).isExtensible()
+                                && cx.getLanguageVersion() >= Context.VERSION_1_8) {
+                            throw ScriptRuntime.typeErrorById("msg.not.extensible");
                         }
-                        if (type == SPECIAL_PROTO) {
-                            search = search.getPrototype();
+
+                        if (cx.getLanguageVersion() >= Context.VERSION_ES6) {
+                            final String typeOfTarget = ScriptRuntime.typeof(target);
+                            if ("function".equals(typeOfTarget)) {
+                                if (value == null) {
+                                    target.setPrototype(Undefined.SCRIPTABLE_UNDEFINED);
+                                    return value;
+                                }
+
+                                final String typeOfValue = ScriptRuntime.typeof(value);
+                                if ("object".equals(typeOfValue)
+                                        || "function".equals(typeOfValue)) {
+                                    target.setPrototype(obj);
+                                }
+                                return value;
+                            }
+
+                            final String typeOfValue = ScriptRuntime.typeof(value);
+                            if ((value != null && !"object".equals(typeOfValue))
+                                    || !"object".equals(typeOfTarget)) {
+                                return Undefined.instance;
+                            }
+                            target.setPrototype(obj);
                         } else {
-                            search = search.getParentScope();
+                            target.setPrototype(obj);
                         }
-                    } while (search != null);
-                }
-                if (type == SPECIAL_PROTO) {
-                    target.setPrototype(obj);
-                } else {
-                    target.setParentScope(obj);
+                    } else {
+                        target.setParentScope(obj);
+                    }
+                    return obj;
                 }
-                return obj;
-            }
-          default:
-            throw Kit.codeBug();
+            default:
+                throw Kit.codeBug();
         }
     }
 
     @Override
-    public boolean has(Context cx)
-    {
+    public boolean has(Context cx) {
         if (type == SPECIAL_NONE) {
             return ScriptRuntime.hasObjectElem(target, name, cx);
         }
@@ -119,12 +141,10 @@
     }
 
     @Override
-    public boolean delete(Context cx)
-    {
+    public boolean delete(Context cx) {
         if (type == SPECIAL_NONE) {
             return ScriptRuntime.deleteObjectElem(target, name, cx);
         }
         return false;
     }
 }
-
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/StackStyle.java rhino-1.7.14/src/org/mozilla/javascript/StackStyle.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/StackStyle.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/StackStyle.java	2022-01-06 22:57:21.000000000 +0100
@@ -4,27 +4,29 @@
 
 package org.mozilla.javascript;
 
-/**
- * This class distinguishes between the three different supported stack trace formats.
- */
-
+/** This class distinguishes between the three different supported stack trace formats. */
 public enum StackStyle {
     /**
-     * This is the default stack trace style in Rhino, which is like Java:
-     * <code>    at fileName:lineNumber (functionName)</code>
+     * This is the default stack trace style in Rhino, which is like Java: <code>
+     *     at fileName:lineNumber (functionName)</code>
      */
     RHINO,
 
     /**
-     * This stack trace style comes from the old Mozilla code:
-     * <code>functionName()@fileName:lineNumber</code>
+     * This stack trace style comes from the old Mozilla code: <code>
+     * functionName()@fileName:lineNumber</code>
      */
     MOZILLA,
 
     /**
-     * This stack trace style matches that output from V8, either:
-     * <code>    at functionName (fileName:lineNumber:columnNumber)</code>
-     * or, for anonymous functions:
+     * This is the same as MOZILLA but uses LF as speparator instead of the system dependent line
+     * separator.
+     */
+    MOZILLA_LF,
+
+    /**
+     * This stack trace style matches that output from V8, either: <code>
+     *     at functionName (fileName:lineNumber:columnNumber)</code> or, for anonymous functions:
      * <code>    at fileName:lineNumber:columnNumber</code>
      */
     V8
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/SymbolKey.java rhino-1.7.14/src/org/mozilla/javascript/SymbolKey.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/SymbolKey.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/SymbolKey.java	2022-01-06 22:57:21.000000000 +0100
@@ -12,7 +12,7 @@
 public class SymbolKey
     implements Symbol, Serializable
 {
-    static final long serialVersionUID = -6019782713330994754L;
+    private static final long serialVersionUID = -6019782713330994754L;
 
     // These are common SymbolKeys that are equivalent to well-known symbols
     // defined in ECMAScript.
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/ThreadSafeSlotMapContainer.java rhino-1.7.14/src/org/mozilla/javascript/ThreadSafeSlotMapContainer.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/ThreadSafeSlotMapContainer.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/ThreadSafeSlotMapContainer.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,156 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+import java.util.Iterator;
+import java.util.concurrent.locks.StampedLock;
+
+/**
+ * This class extends the SlotMapContainer so that we have thread-safe access to all the properties
+ * of an object.
+ */
+class ThreadSafeSlotMapContainer extends SlotMapContainer {
+
+    private final StampedLock lock = new StampedLock();
+
+    ThreadSafeSlotMapContainer() {}
+
+    ThreadSafeSlotMapContainer(int initialSize) {
+        super(initialSize);
+    }
+
+    @Override
+    public int size() {
+        long stamp = lock.tryOptimisticRead();
+        int s = map.size();
+        if (lock.validate(stamp)) {
+            return s;
+        }
+
+        stamp = lock.readLock();
+        try {
+            return map.size();
+        } finally {
+            lock.unlockRead(stamp);
+        }
+    }
+
+    @Override
+    public int dirtySize() {
+        assert (lock.isReadLocked());
+        return map.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        long stamp = lock.tryOptimisticRead();
+        boolean e = map.isEmpty();
+        if (lock.validate(stamp)) {
+            return e;
+        }
+
+        stamp = lock.readLock();
+        try {
+            return map.isEmpty();
+        } finally {
+            lock.unlockRead(stamp);
+        }
+    }
+
+    @Override
+    public Slot modify(Object key, int index, int attributes) {
+        final long stamp = lock.writeLock();
+        try {
+            checkMapSize();
+            return map.modify(key, index, attributes);
+        } finally {
+            lock.unlockWrite(stamp);
+        }
+    }
+
+    @Override
+    public void replace(Slot oldSlot, Slot newSlot) {
+        final long stamp = lock.writeLock();
+        try {
+            map.replace(oldSlot, newSlot);
+        } finally {
+            lock.unlockWrite(stamp);
+        }
+    }
+
+    @Override
+    public Slot query(Object key, int index) {
+        long stamp = lock.tryOptimisticRead();
+        Slot s = map.query(key, index);
+        if (lock.validate(stamp)) {
+            return s;
+        }
+
+        stamp = lock.readLock();
+        try {
+            return map.query(key, index);
+        } finally {
+            lock.unlockRead(stamp);
+        }
+    }
+
+    @Override
+    public void add(Slot newSlot) {
+        final long stamp = lock.writeLock();
+        try {
+            checkMapSize();
+            map.add(newSlot);
+        } finally {
+            lock.unlockWrite(stamp);
+        }
+    }
+
+    @Override
+    public void remove(Object key, int index) {
+        final long stamp = lock.writeLock();
+        try {
+            map.remove(key, index);
+        } finally {
+            lock.unlockWrite(stamp);
+        }
+    }
+
+    /**
+     * Take out a read lock on the slot map, if locking is implemented. The caller MUST call this
+     * method before using the iterator, and MUST NOT call this method otherwise.
+     */
+    @Override
+    public long readLock() {
+        return lock.readLock();
+    }
+
+    /**
+     * Unlock the lock taken out by readLock.
+     *
+     * @param stamp the value returned by readLock.
+     */
+    @Override
+    public void unlockRead(long stamp) {
+        lock.unlockRead(stamp);
+    }
+
+    @Override
+    public Iterator<Slot> iterator() {
+        assert (lock.isReadLocked());
+        return map.iterator();
+    }
+
+    /**
+     * Before inserting a new item in the map, check and see if we need to expand from the embedded
+     * map to a HashMap that is more robust against large numbers of hash collisions.
+     */
+    @Override
+    protected void checkMapSize() {
+        assert (lock.isWriteLocked());
+        super.checkMapSize();
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/Token.java rhino-1.7.14/src/org/mozilla/javascript/Token.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/Token.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/Token.java	2022-01-06 22:57:21.000000000 +0100
@@ -9,19 +9,18 @@
 /**
  * This class implements the JavaScript scanner.
  *
- * It is based on the C source files jsscan.c and jsscan.h
- * in the jsref package.
+ * <p>It is based on the C source files jsscan.c and jsscan.h in the jsref package.
  *
  * @see org.mozilla.javascript.Parser
- *
  * @author Mike McCabe
  * @author Brendan Eich
  */
-
-public class Token
-{
+public class Token {
     public static enum CommentType {
-        LINE, BLOCK_COMMENT, JSDOC, HTML
+        LINE,
+        BLOCK_COMMENT,
+        JSDOC,
+        HTML
     }
 
     // debug flags
@@ -29,217 +28,211 @@
     static final boolean printICode = false;
     static final boolean printNames = printTrees || printICode;
 
-    /**
-     * Token types.  These values correspond to JSTokenType values in
-     * jsscan.c.
-     */
-
-    public final static int
-    // start enum
-        ERROR          = -1, // well-known as the only code < EOF
-        EOF            = 0,  // end of file token - (not EOF_CHAR)
-        EOL            = 1,  // end of line
-
-        // Interpreter reuses the following as bytecodes
-        FIRST_BYTECODE_TOKEN    = 2,
-
-        ENTERWITH      = 2,
-        LEAVEWITH      = 3,
-        RETURN         = 4,
-        GOTO           = 5,
-        IFEQ           = 6,
-        IFNE           = 7,
-        SETNAME        = 8,
-        BITOR          = 9,
-        BITXOR         = 10,
-        BITAND         = 11,
-        EQ             = 12,
-        NE             = 13,
-        LT             = 14,
-        LE             = 15,
-        GT             = 16,
-        GE             = 17,
-        LSH            = 18,
-        RSH            = 19,
-        URSH           = 20,
-        ADD            = 21,
-        SUB            = 22,
-        MUL            = 23,
-        DIV            = 24,
-        MOD            = 25,
-        NOT            = 26,
-        BITNOT         = 27,
-        POS            = 28,
-        NEG            = 29,
-        NEW            = 30,
-        DELPROP        = 31,
-        TYPEOF         = 32,
-        GETPROP        = 33,
-        GETPROPNOWARN  = 34,
-        SETPROP        = 35,
-        GETELEM        = 36,
-        SETELEM        = 37,
-        CALL           = 38,
-        NAME           = 39,
-        NUMBER         = 40,
-        STRING         = 41,
-        NULL           = 42,
-        THIS           = 43,
-        FALSE          = 44,
-        TRUE           = 45,
-        SHEQ           = 46,   // shallow equality (===)
-        SHNE           = 47,   // shallow inequality (!==)
-        REGEXP         = 48,
-        BINDNAME       = 49,
-        THROW          = 50,
-        RETHROW        = 51, // rethrow caught exception: catch (e if ) use it
-        IN             = 52,
-        INSTANCEOF     = 53,
-        LOCAL_LOAD     = 54,
-        GETVAR         = 55,
-        SETVAR         = 56,
-        CATCH_SCOPE    = 57,
-        ENUM_INIT_KEYS = 58,
-        ENUM_INIT_VALUES = 59,
-        ENUM_INIT_ARRAY= 60,
-        ENUM_INIT_VALUES_IN_ORDER = 61,
-        ENUM_NEXT      = 62,
-        ENUM_ID        = 63,
-        THISFN         = 64,
-        RETURN_RESULT  = 65, // to return previously stored return result
-        ARRAYLIT       = 66, // array literal
-        OBJECTLIT      = 67, // object literal
-        GET_REF        = 68, // *reference
-        SET_REF        = 69, // *reference    = something
-        DEL_REF        = 70, // delete reference
-        REF_CALL       = 71, // f(args)    = something or f(args)++
-        REF_SPECIAL    = 72, // reference for special properties like __proto
-        YIELD          = 73,  // JS 1.7 yield pseudo keyword
-        STRICT_SETNAME = 74,
-
-        // For XML support:
-        DEFAULTNAMESPACE = 75, // default xml namespace =
-        ESCXMLATTR     = 76,
-        ESCXMLTEXT     = 77,
-        REF_MEMBER     = 78, // Reference for x. at y, x..y etc.
-        REF_NS_MEMBER  = 79, // Reference for x.ns::y, x..ns::y etc.
-        REF_NAME       = 80, // Reference for @y, @[y] etc.
-        REF_NS_NAME    = 81; // Reference for ns::y, @ns::y@[y] etc.
-
-        // End of interpreter bytecodes
-    public final static int
-        LAST_BYTECODE_TOKEN    = REF_NS_NAME,
-
-        TRY            = 82,
-        SEMI           = 83,  // semicolon
-        LB             = 84,  // left and right brackets
-        RB             = 85,
-        LC             = 86,  // left and right curlies (braces)
-        RC             = 87,
-        LP             = 88,  // left and right parentheses
-        RP             = 89,
-        COMMA          = 90,  // comma operator
-
-        ASSIGN         = 91,  // simple assignment  (=)
-        ASSIGN_BITOR   = 92,  // |=
-        ASSIGN_BITXOR  = 93,  // ^=
-        ASSIGN_BITAND  = 94,  // |=
-        ASSIGN_LSH     = 95,  // <<=
-        ASSIGN_RSH     = 96,  // >>=
-        ASSIGN_URSH    = 97,  // >>>=
-        ASSIGN_ADD     = 98,  // +=
-        ASSIGN_SUB     = 99,  // -=
-        ASSIGN_MUL     = 100,  // *=
-        ASSIGN_DIV     = 101,  // /=
-        ASSIGN_MOD     = 102;  // %=
-
-    public final static int
-        FIRST_ASSIGN   = ASSIGN,
-        LAST_ASSIGN    = ASSIGN_MOD,
-
-        HOOK           = 103, // conditional (?:)
-        COLON          = 104,
-        OR             = 105, // logical or (||)
-        AND            = 106, // logical and (&&)
-        INC            = 107, // increment/decrement (++ --)
-        DEC            = 108,
-        DOT            = 109, // member operator (.)
-        FUNCTION       = 110, // function keyword
-        EXPORT         = 111, // export keyword
-        IMPORT         = 112, // import keyword
-        IF             = 113, // if keyword
-        ELSE           = 114, // else keyword
-        SWITCH         = 115, // switch keyword
-        CASE           = 116, // case keyword
-        DEFAULT        = 117, // default keyword
-        WHILE          = 118, // while keyword
-        DO             = 119, // do keyword
-        FOR            = 120, // for keyword
-        BREAK          = 121, // break keyword
-        CONTINUE       = 122, // continue keyword
-        VAR            = 123, // var keyword
-        WITH           = 124, // with keyword
-        CATCH          = 125, // catch keyword
-        FINALLY        = 126, // finally keyword
-        VOID           = 127, // void keyword
-        RESERVED       = 128, // reserved keywords
-
-        EMPTY          = 129,
-
-        /* types used for the parse tree - these never get returned
-         * by the scanner.
-         */
-
-        BLOCK          = 130, // statement block
-        LABEL          = 131, // label
-        TARGET         = 132,
-        LOOP           = 133,
-        EXPR_VOID      = 134, // expression statement in functions
-        EXPR_RESULT    = 135, // expression statement in scripts
-        JSR            = 136,
-        SCRIPT         = 137, // top-level node for entire script
-        TYPEOFNAME     = 138, // for typeof(simple-name)
-        USE_STACK      = 139,
-        SETPROP_OP     = 140, // x.y op= something
-        SETELEM_OP     = 141, // x[y] op= something
-        LOCAL_BLOCK    = 142,
-        SET_REF_OP     = 143, // *reference op= something
-
-        // For XML support:
-        DOTDOT         = 144,  // member operator (..)
-        COLONCOLON     = 145,  // namespace::name
-        XML            = 146,  // XML type
-        DOTQUERY       = 147,  // .() -- e.g., x.emps.emp.(name == "terry")
-        XMLATTR        = 148,  // @
-        XMLEND         = 149,
-
-        // Optimizer-only-tokens
-        TO_OBJECT      = 150,
-        TO_DOUBLE      = 151,
-
-        GET            = 152,  // JS 1.5 get pseudo keyword
-        SET            = 153,  // JS 1.5 set pseudo keyword
-        LET            = 154,  // JS 1.7 let pseudo keyword
-        CONST          = 155,
-        SETCONST       = 156,
-        SETCONSTVAR    = 157,
-        ARRAYCOMP      = 158,  // array comprehension
-        LETEXPR        = 159,
-        WITHEXPR       = 160,
-        DEBUGGER       = 161,
-        COMMENT        = 162,
-        GENEXPR        = 163,
-        METHOD         = 164,  // ES6 MethodDefinition
-        ARROW          = 165,  // ES6 ArrowFunction
-        LAST_TOKEN     = 166;
+    /** Token types. These values correspond to JSTokenType values in jsscan.c. */
+    public static final int
+            // start enum
+            ERROR = -1, // well-known as the only code < EOF
+            EOF = 0, // end of file token - (not EOF_CHAR)
+            EOL = 1, // end of line
+
+            // Interpreter reuses the following as bytecodes
+            FIRST_BYTECODE_TOKEN = 2,
+            ENTERWITH = 2,
+            LEAVEWITH = 3,
+            RETURN = 4,
+            GOTO = 5,
+            IFEQ = 6,
+            IFNE = 7,
+            SETNAME = 8,
+            BITOR = 9,
+            BITXOR = 10,
+            BITAND = 11,
+            EQ = 12,
+            NE = 13,
+            LT = 14,
+            LE = 15,
+            GT = 16,
+            GE = 17,
+            LSH = 18,
+            RSH = 19,
+            URSH = 20,
+            ADD = 21,
+            SUB = 22,
+            MUL = 23,
+            DIV = 24,
+            MOD = 25,
+            NOT = 26,
+            BITNOT = 27,
+            POS = 28,
+            NEG = 29,
+            NEW = 30,
+            DELPROP = 31,
+            TYPEOF = 32,
+            GETPROP = 33,
+            GETPROPNOWARN = 34,
+            SETPROP = 35,
+            GETELEM = 36,
+            SETELEM = 37,
+            CALL = 38,
+            NAME = 39,
+            NUMBER = 40,
+            STRING = 41,
+            NULL = 42,
+            THIS = 43,
+            FALSE = 44,
+            TRUE = 45,
+            SHEQ = 46, // shallow equality (===)
+            SHNE = 47, // shallow inequality (!==)
+            REGEXP = 48,
+            BINDNAME = 49,
+            THROW = 50,
+            RETHROW = 51, // rethrow caught exception: catch (e if ) use it
+            IN = 52,
+            INSTANCEOF = 53,
+            LOCAL_LOAD = 54,
+            GETVAR = 55,
+            SETVAR = 56,
+            CATCH_SCOPE = 57,
+            ENUM_INIT_KEYS = 58,
+            ENUM_INIT_VALUES = 59,
+            ENUM_INIT_ARRAY = 60,
+            ENUM_INIT_VALUES_IN_ORDER = 61,
+            ENUM_NEXT = 62,
+            ENUM_ID = 63,
+            THISFN = 64,
+            RETURN_RESULT = 65, // to return previously stored return result
+            ARRAYLIT = 66, // array literal
+            OBJECTLIT = 67, // object literal
+            GET_REF = 68, // *reference
+            SET_REF = 69, // *reference    = something
+            DEL_REF = 70, // delete reference
+            REF_CALL = 71, // f(args)    = something or f(args)++
+            REF_SPECIAL = 72, // reference for special properties like __proto
+            YIELD = 73, // JS 1.7 yield pseudo keyword
+            STRICT_SETNAME = 74,
+            EXP = 75, // Exponentiation Operator
+
+            // For XML support:
+            DEFAULTNAMESPACE = 76, // default xml namespace =
+            ESCXMLATTR = 77,
+            ESCXMLTEXT = 78,
+            REF_MEMBER = 79, // Reference for x. at y, x..y etc.
+            REF_NS_MEMBER = 80, // Reference for x.ns::y, x..ns::y etc.
+            REF_NAME = 81, // Reference for @y, @[y] etc.
+            REF_NS_NAME = 82, // Reference for ns::y, @ns::y@[y] etc.
+            BIGINT = 83; // ES2020 BigInt
+
+    // End of interpreter bytecodes
+    public static final int LAST_BYTECODE_TOKEN = BIGINT,
+            TRY = 84,
+            SEMI = 85, // semicolon
+            LB = 86, // left and right brackets
+            RB = 87,
+            LC = 88, // left and right curlies (braces)
+            RC = 89,
+            LP = 90, // left and right parentheses
+            RP = 91,
+            COMMA = 92, // comma operator
+            ASSIGN = 93, // simple assignment  (=)
+            ASSIGN_BITOR = 94, // |=
+            ASSIGN_BITXOR = 95, // ^=
+            ASSIGN_BITAND = 96, // |=
+            ASSIGN_LSH = 97, // <<=
+            ASSIGN_RSH = 98, // >>=
+            ASSIGN_URSH = 99, // >>>=
+            ASSIGN_ADD = 100, // +=
+            ASSIGN_SUB = 101, // -=
+            ASSIGN_MUL = 102, // *=
+            ASSIGN_DIV = 103, // /=
+            ASSIGN_MOD = 104, // %=
+            ASSIGN_EXP = 105; // **=
+    public static final int FIRST_ASSIGN = ASSIGN,
+            LAST_ASSIGN = ASSIGN_EXP,
+            HOOK = 106, // conditional (?:)
+            COLON = 107,
+            OR = 108, // logical or (||)
+            AND = 109, // logical and (&&)
+            INC = 110, // increment/decrement (++ --)
+            DEC = 111,
+            DOT = 112, // member operator (.)
+            FUNCTION = 113, // function keyword
+            EXPORT = 114, // export keyword
+            IMPORT = 115, // import keyword
+            IF = 116, // if keyword
+            ELSE = 117, // else keyword
+            SWITCH = 118, // switch keyword
+            CASE = 119, // case keyword
+            DEFAULT = 120, // default keyword
+            WHILE = 121, // while keyword
+            DO = 122, // do keyword
+            FOR = 123, // for keyword
+            BREAK = 124, // break keyword
+            CONTINUE = 125, // continue keyword
+            VAR = 126, // var keyword
+            WITH = 127, // with keyword
+            CATCH = 128, // catch keyword
+            FINALLY = 129, // finally keyword
+            VOID = 130, // void keyword
+            RESERVED = 131, // reserved keywords
+            EMPTY = 132,
+
+            /* types used for the parse tree - these never get returned
+             * by the scanner.
+             */
+
+            BLOCK = 133, // statement block
+            LABEL = 134, // label
+            TARGET = 135,
+            LOOP = 136,
+            EXPR_VOID = 137, // expression statement in functions
+            EXPR_RESULT = 138, // expression statement in scripts
+            JSR = 139,
+            SCRIPT = 140, // top-level node for entire script
+            TYPEOFNAME = 141, // for typeof(simple-name)
+            USE_STACK = 142,
+            SETPROP_OP = 143, // x.y op= something
+            SETELEM_OP = 144, // x[y] op= something
+            LOCAL_BLOCK = 145,
+            SET_REF_OP = 146, // *reference op= something
+
+            // For XML support:
+            DOTDOT = 147, // member operator (..)
+            COLONCOLON = 148, // namespace::name
+            XML = 149, // XML type
+            DOTQUERY = 150, // .() -- e.g., x.emps.emp.(name == "terry")
+            XMLATTR = 151, // @
+            XMLEND = 152,
+
+            // Optimizer-only-tokens
+            TO_OBJECT = 153,
+            TO_DOUBLE = 154,
+            GET = 155, // JS 1.5 get pseudo keyword
+            SET = 156, // JS 1.5 set pseudo keyword
+            LET = 157, // JS 1.7 let pseudo keyword
+            CONST = 158,
+            SETCONST = 159,
+            SETCONSTVAR = 160,
+            ARRAYCOMP = 161, // array comprehension
+            LETEXPR = 162,
+            WITHEXPR = 163,
+            DEBUGGER = 164,
+            COMMENT = 165,
+            GENEXPR = 166,
+            METHOD = 167, // ES6 MethodDefinition
+            ARROW = 168, // ES6 ArrowFunction
+            YIELD_STAR = 169, // ES6 "yield *", a specialization of yield
+            TEMPLATE_LITERAL = 170, // template literal
+            TEMPLATE_CHARS = 171, // template literal - literal section
+            TEMPLATE_LITERAL_SUBST = 172, // template literal - substitution
+            TAGGED_TEMPLATE_LITERAL = 173, // template literal - tagged/handler
+            LAST_TOKEN = 173;
 
     /**
-     * Returns a name for the token.  If Rhino is compiled with certain
-     * hardcoded debugging flags in this file, it calls {@code #typeToName};
-     * otherwise it returns a string whose value is the token number.
+     * Returns a name for the token. If Rhino is compiled with certain hardcoded debugging flags in
+     * this file, it calls {@code #typeToName}; otherwise it returns a string whose value is the
+     * token number.
      */
-    public static String name(int token)
-    {
+    public static String name(int token) {
         if (!printNames) {
             return String.valueOf(token);
         }
@@ -247,178 +240,360 @@
     }
 
     /**
-     * Always returns a human-readable string for the token name.
-     * For instance, {@link #FINALLY} has the name "FINALLY".
+     * Always returns a human-readable string for the token name. For instance, {@link #FINALLY} has
+     * the name "FINALLY".
+     *
      * @param token the token code
      * @return the actual name for the token code
      */
     public static String typeToName(int token) {
         switch (token) {
-          case ERROR:           return "ERROR";
-          case EOF:             return "EOF";
-          case EOL:             return "EOL";
-          case ENTERWITH:       return "ENTERWITH";
-          case LEAVEWITH:       return "LEAVEWITH";
-          case RETURN:          return "RETURN";
-          case GOTO:            return "GOTO";
-          case IFEQ:            return "IFEQ";
-          case IFNE:            return "IFNE";
-          case SETNAME:         return "SETNAME";
-          case BITOR:           return "BITOR";
-          case BITXOR:          return "BITXOR";
-          case BITAND:          return "BITAND";
-          case EQ:              return "EQ";
-          case NE:              return "NE";
-          case LT:              return "LT";
-          case LE:              return "LE";
-          case GT:              return "GT";
-          case GE:              return "GE";
-          case LSH:             return "LSH";
-          case RSH:             return "RSH";
-          case URSH:            return "URSH";
-          case ADD:             return "ADD";
-          case SUB:             return "SUB";
-          case MUL:             return "MUL";
-          case DIV:             return "DIV";
-          case MOD:             return "MOD";
-          case NOT:             return "NOT";
-          case BITNOT:          return "BITNOT";
-          case POS:             return "POS";
-          case NEG:             return "NEG";
-          case NEW:             return "NEW";
-          case DELPROP:         return "DELPROP";
-          case TYPEOF:          return "TYPEOF";
-          case GETPROP:         return "GETPROP";
-          case GETPROPNOWARN:   return "GETPROPNOWARN";
-          case SETPROP:         return "SETPROP";
-          case GETELEM:         return "GETELEM";
-          case SETELEM:         return "SETELEM";
-          case CALL:            return "CALL";
-          case NAME:            return "NAME";
-          case NUMBER:          return "NUMBER";
-          case STRING:          return "STRING";
-          case NULL:            return "NULL";
-          case THIS:            return "THIS";
-          case FALSE:           return "FALSE";
-          case TRUE:            return "TRUE";
-          case SHEQ:            return "SHEQ";
-          case SHNE:            return "SHNE";
-          case REGEXP:          return "REGEXP";
-          case BINDNAME:        return "BINDNAME";
-          case THROW:           return "THROW";
-          case RETHROW:         return "RETHROW";
-          case IN:              return "IN";
-          case INSTANCEOF:      return "INSTANCEOF";
-          case LOCAL_LOAD:      return "LOCAL_LOAD";
-          case GETVAR:          return "GETVAR";
-          case SETVAR:          return "SETVAR";
-          case CATCH_SCOPE:     return "CATCH_SCOPE";
-          case ENUM_INIT_KEYS:  return "ENUM_INIT_KEYS";
-          case ENUM_INIT_VALUES:return "ENUM_INIT_VALUES";
-          case ENUM_INIT_ARRAY: return "ENUM_INIT_ARRAY";
-          case ENUM_INIT_VALUES_IN_ORDER: return "ENUM_INIT_VALUES_IN_ORDER";
-          case ENUM_NEXT:       return "ENUM_NEXT";
-          case ENUM_ID:         return "ENUM_ID";
-          case THISFN:          return "THISFN";
-          case RETURN_RESULT:   return "RETURN_RESULT";
-          case ARRAYLIT:        return "ARRAYLIT";
-          case OBJECTLIT:       return "OBJECTLIT";
-          case GET_REF:         return "GET_REF";
-          case SET_REF:         return "SET_REF";
-          case DEL_REF:         return "DEL_REF";
-          case REF_CALL:        return "REF_CALL";
-          case REF_SPECIAL:     return "REF_SPECIAL";
-          case DEFAULTNAMESPACE:return "DEFAULTNAMESPACE";
-          case ESCXMLTEXT:      return "ESCXMLTEXT";
-          case ESCXMLATTR:      return "ESCXMLATTR";
-          case REF_MEMBER:      return "REF_MEMBER";
-          case REF_NS_MEMBER:   return "REF_NS_MEMBER";
-          case REF_NAME:        return "REF_NAME";
-          case REF_NS_NAME:     return "REF_NS_NAME";
-          case TRY:             return "TRY";
-          case SEMI:            return "SEMI";
-          case LB:              return "LB";
-          case RB:              return "RB";
-          case LC:              return "LC";
-          case RC:              return "RC";
-          case LP:              return "LP";
-          case RP:              return "RP";
-          case COMMA:           return "COMMA";
-          case ASSIGN:          return "ASSIGN";
-          case ASSIGN_BITOR:    return "ASSIGN_BITOR";
-          case ASSIGN_BITXOR:   return "ASSIGN_BITXOR";
-          case ASSIGN_BITAND:   return "ASSIGN_BITAND";
-          case ASSIGN_LSH:      return "ASSIGN_LSH";
-          case ASSIGN_RSH:      return "ASSIGN_RSH";
-          case ASSIGN_URSH:     return "ASSIGN_URSH";
-          case ASSIGN_ADD:      return "ASSIGN_ADD";
-          case ASSIGN_SUB:      return "ASSIGN_SUB";
-          case ASSIGN_MUL:      return "ASSIGN_MUL";
-          case ASSIGN_DIV:      return "ASSIGN_DIV";
-          case ASSIGN_MOD:      return "ASSIGN_MOD";
-          case HOOK:            return "HOOK";
-          case COLON:           return "COLON";
-          case OR:              return "OR";
-          case AND:             return "AND";
-          case INC:             return "INC";
-          case DEC:             return "DEC";
-          case DOT:             return "DOT";
-          case FUNCTION:        return "FUNCTION";
-          case EXPORT:          return "EXPORT";
-          case IMPORT:          return "IMPORT";
-          case IF:              return "IF";
-          case ELSE:            return "ELSE";
-          case SWITCH:          return "SWITCH";
-          case CASE:            return "CASE";
-          case DEFAULT:         return "DEFAULT";
-          case WHILE:           return "WHILE";
-          case DO:              return "DO";
-          case FOR:             return "FOR";
-          case BREAK:           return "BREAK";
-          case CONTINUE:        return "CONTINUE";
-          case VAR:             return "VAR";
-          case WITH:            return "WITH";
-          case CATCH:           return "CATCH";
-          case FINALLY:         return "FINALLY";
-          case VOID:            return "VOID";
-          case RESERVED:        return "RESERVED";
-          case EMPTY:           return "EMPTY";
-          case BLOCK:           return "BLOCK";
-          case LABEL:           return "LABEL";
-          case TARGET:          return "TARGET";
-          case LOOP:            return "LOOP";
-          case EXPR_VOID:       return "EXPR_VOID";
-          case EXPR_RESULT:     return "EXPR_RESULT";
-          case JSR:             return "JSR";
-          case SCRIPT:          return "SCRIPT";
-          case TYPEOFNAME:      return "TYPEOFNAME";
-          case USE_STACK:       return "USE_STACK";
-          case SETPROP_OP:      return "SETPROP_OP";
-          case SETELEM_OP:      return "SETELEM_OP";
-          case LOCAL_BLOCK:     return "LOCAL_BLOCK";
-          case SET_REF_OP:      return "SET_REF_OP";
-          case DOTDOT:          return "DOTDOT";
-          case COLONCOLON:      return "COLONCOLON";
-          case XML:             return "XML";
-          case DOTQUERY:        return "DOTQUERY";
-          case XMLATTR:         return "XMLATTR";
-          case XMLEND:          return "XMLEND";
-          case TO_OBJECT:       return "TO_OBJECT";
-          case TO_DOUBLE:       return "TO_DOUBLE";
-          case GET:             return "GET";
-          case SET:             return "SET";
-          case LET:             return "LET";
-          case YIELD:           return "YIELD";
-          case CONST:           return "CONST";
-          case SETCONST:        return "SETCONST";
-          case ARRAYCOMP:       return "ARRAYCOMP";
-          case WITHEXPR:        return "WITHEXPR";
-          case LETEXPR:         return "LETEXPR";
-          case DEBUGGER:        return "DEBUGGER";
-          case COMMENT:         return "COMMENT";
-          case GENEXPR:         return "GENEXPR";
-          case METHOD:          return "METHOD";
-          case ARROW:           return "ARROW";
+            case ERROR:
+                return "ERROR";
+            case EOF:
+                return "EOF";
+            case EOL:
+                return "EOL";
+            case ENTERWITH:
+                return "ENTERWITH";
+            case LEAVEWITH:
+                return "LEAVEWITH";
+            case RETURN:
+                return "RETURN";
+            case GOTO:
+                return "GOTO";
+            case IFEQ:
+                return "IFEQ";
+            case IFNE:
+                return "IFNE";
+            case SETNAME:
+                return "SETNAME";
+            case BITOR:
+                return "BITOR";
+            case BITXOR:
+                return "BITXOR";
+            case BITAND:
+                return "BITAND";
+            case EQ:
+                return "EQ";
+            case NE:
+                return "NE";
+            case LT:
+                return "LT";
+            case LE:
+                return "LE";
+            case GT:
+                return "GT";
+            case GE:
+                return "GE";
+            case LSH:
+                return "LSH";
+            case RSH:
+                return "RSH";
+            case URSH:
+                return "URSH";
+            case ADD:
+                return "ADD";
+            case SUB:
+                return "SUB";
+            case MUL:
+                return "MUL";
+            case DIV:
+                return "DIV";
+            case MOD:
+                return "MOD";
+            case NOT:
+                return "NOT";
+            case BITNOT:
+                return "BITNOT";
+            case POS:
+                return "POS";
+            case NEG:
+                return "NEG";
+            case NEW:
+                return "NEW";
+            case DELPROP:
+                return "DELPROP";
+            case TYPEOF:
+                return "TYPEOF";
+            case GETPROP:
+                return "GETPROP";
+            case GETPROPNOWARN:
+                return "GETPROPNOWARN";
+            case SETPROP:
+                return "SETPROP";
+            case GETELEM:
+                return "GETELEM";
+            case SETELEM:
+                return "SETELEM";
+            case CALL:
+                return "CALL";
+            case NAME:
+                return "NAME";
+            case NUMBER:
+                return "NUMBER";
+            case STRING:
+                return "STRING";
+            case NULL:
+                return "NULL";
+            case THIS:
+                return "THIS";
+            case FALSE:
+                return "FALSE";
+            case TRUE:
+                return "TRUE";
+            case SHEQ:
+                return "SHEQ";
+            case SHNE:
+                return "SHNE";
+            case REGEXP:
+                return "REGEXP";
+            case BINDNAME:
+                return "BINDNAME";
+            case THROW:
+                return "THROW";
+            case RETHROW:
+                return "RETHROW";
+            case IN:
+                return "IN";
+            case INSTANCEOF:
+                return "INSTANCEOF";
+            case LOCAL_LOAD:
+                return "LOCAL_LOAD";
+            case GETVAR:
+                return "GETVAR";
+            case SETVAR:
+                return "SETVAR";
+            case CATCH_SCOPE:
+                return "CATCH_SCOPE";
+            case ENUM_INIT_KEYS:
+                return "ENUM_INIT_KEYS";
+            case ENUM_INIT_VALUES:
+                return "ENUM_INIT_VALUES";
+            case ENUM_INIT_ARRAY:
+                return "ENUM_INIT_ARRAY";
+            case ENUM_INIT_VALUES_IN_ORDER:
+                return "ENUM_INIT_VALUES_IN_ORDER";
+            case ENUM_NEXT:
+                return "ENUM_NEXT";
+            case ENUM_ID:
+                return "ENUM_ID";
+            case THISFN:
+                return "THISFN";
+            case RETURN_RESULT:
+                return "RETURN_RESULT";
+            case ARRAYLIT:
+                return "ARRAYLIT";
+            case OBJECTLIT:
+                return "OBJECTLIT";
+            case GET_REF:
+                return "GET_REF";
+            case SET_REF:
+                return "SET_REF";
+            case DEL_REF:
+                return "DEL_REF";
+            case REF_CALL:
+                return "REF_CALL";
+            case REF_SPECIAL:
+                return "REF_SPECIAL";
+            case DEFAULTNAMESPACE:
+                return "DEFAULTNAMESPACE";
+            case ESCXMLTEXT:
+                return "ESCXMLTEXT";
+            case ESCXMLATTR:
+                return "ESCXMLATTR";
+            case REF_MEMBER:
+                return "REF_MEMBER";
+            case REF_NS_MEMBER:
+                return "REF_NS_MEMBER";
+            case REF_NAME:
+                return "REF_NAME";
+            case REF_NS_NAME:
+                return "REF_NS_NAME";
+            case TRY:
+                return "TRY";
+            case SEMI:
+                return "SEMI";
+            case LB:
+                return "LB";
+            case RB:
+                return "RB";
+            case LC:
+                return "LC";
+            case RC:
+                return "RC";
+            case LP:
+                return "LP";
+            case RP:
+                return "RP";
+            case COMMA:
+                return "COMMA";
+            case ASSIGN:
+                return "ASSIGN";
+            case ASSIGN_BITOR:
+                return "ASSIGN_BITOR";
+            case ASSIGN_BITXOR:
+                return "ASSIGN_BITXOR";
+            case ASSIGN_BITAND:
+                return "ASSIGN_BITAND";
+            case ASSIGN_LSH:
+                return "ASSIGN_LSH";
+            case ASSIGN_RSH:
+                return "ASSIGN_RSH";
+            case ASSIGN_URSH:
+                return "ASSIGN_URSH";
+            case ASSIGN_ADD:
+                return "ASSIGN_ADD";
+            case ASSIGN_SUB:
+                return "ASSIGN_SUB";
+            case ASSIGN_MUL:
+                return "ASSIGN_MUL";
+            case ASSIGN_DIV:
+                return "ASSIGN_DIV";
+            case ASSIGN_MOD:
+                return "ASSIGN_MOD";
+            case ASSIGN_EXP:
+                return "ASSIGN_EXP";
+            case HOOK:
+                return "HOOK";
+            case COLON:
+                return "COLON";
+            case OR:
+                return "OR";
+            case AND:
+                return "AND";
+            case INC:
+                return "INC";
+            case DEC:
+                return "DEC";
+            case DOT:
+                return "DOT";
+            case FUNCTION:
+                return "FUNCTION";
+            case EXPORT:
+                return "EXPORT";
+            case IMPORT:
+                return "IMPORT";
+            case IF:
+                return "IF";
+            case ELSE:
+                return "ELSE";
+            case SWITCH:
+                return "SWITCH";
+            case CASE:
+                return "CASE";
+            case DEFAULT:
+                return "DEFAULT";
+            case WHILE:
+                return "WHILE";
+            case DO:
+                return "DO";
+            case FOR:
+                return "FOR";
+            case BREAK:
+                return "BREAK";
+            case CONTINUE:
+                return "CONTINUE";
+            case VAR:
+                return "VAR";
+            case WITH:
+                return "WITH";
+            case CATCH:
+                return "CATCH";
+            case FINALLY:
+                return "FINALLY";
+            case VOID:
+                return "VOID";
+            case RESERVED:
+                return "RESERVED";
+            case EMPTY:
+                return "EMPTY";
+            case BLOCK:
+                return "BLOCK";
+            case LABEL:
+                return "LABEL";
+            case TARGET:
+                return "TARGET";
+            case LOOP:
+                return "LOOP";
+            case EXPR_VOID:
+                return "EXPR_VOID";
+            case EXPR_RESULT:
+                return "EXPR_RESULT";
+            case JSR:
+                return "JSR";
+            case SCRIPT:
+                return "SCRIPT";
+            case TYPEOFNAME:
+                return "TYPEOFNAME";
+            case USE_STACK:
+                return "USE_STACK";
+            case SETPROP_OP:
+                return "SETPROP_OP";
+            case SETELEM_OP:
+                return "SETELEM_OP";
+            case LOCAL_BLOCK:
+                return "LOCAL_BLOCK";
+            case SET_REF_OP:
+                return "SET_REF_OP";
+            case DOTDOT:
+                return "DOTDOT";
+            case COLONCOLON:
+                return "COLONCOLON";
+            case XML:
+                return "XML";
+            case DOTQUERY:
+                return "DOTQUERY";
+            case XMLATTR:
+                return "XMLATTR";
+            case XMLEND:
+                return "XMLEND";
+            case TO_OBJECT:
+                return "TO_OBJECT";
+            case TO_DOUBLE:
+                return "TO_DOUBLE";
+            case GET:
+                return "GET";
+            case SET:
+                return "SET";
+            case LET:
+                return "LET";
+            case YIELD:
+                return "YIELD";
+            case EXP:
+                return "EXP";
+            case CONST:
+                return "CONST";
+            case SETCONST:
+                return "SETCONST";
+            case ARRAYCOMP:
+                return "ARRAYCOMP";
+            case WITHEXPR:
+                return "WITHEXPR";
+            case LETEXPR:
+                return "LETEXPR";
+            case DEBUGGER:
+                return "DEBUGGER";
+            case COMMENT:
+                return "COMMENT";
+            case GENEXPR:
+                return "GENEXPR";
+            case METHOD:
+                return "METHOD";
+            case ARROW:
+                return "ARROW";
+            case YIELD_STAR:
+                return "YIELD_STAR";
+            case BIGINT:
+                return "BIGINT";
+            case TEMPLATE_LITERAL:
+                return "TEMPLATE_LITERAL";
+            case TEMPLATE_CHARS:
+                return "TEMPLATE_CHARS";
+            case TEMPLATE_LITERAL_SUBST:
+                return "TEMPLATE_LITERAL_SUBST";
+            case TAGGED_TEMPLATE_LITERAL:
+                return "TAGGED_TEMPLATE_LITERAL";
         }
 
         // Token without name
@@ -426,56 +601,90 @@
     }
 
     /**
-     * Convert a keyword token to a name string for use with the
-     * {@link Context#FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER} feature.
+     * Convert a keyword token to a name string for use with the {@link
+     * Context#FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER} feature.
+     *
      * @param token A token
      * @return the corresponding name string
      */
     public static String keywordToName(int token) {
         switch (token) {
-            case Token.BREAK:      return "break";
-            case Token.CASE:       return "case";
-            case Token.CONTINUE:   return "continue";
-            case Token.DEFAULT:    return "default";
-            case Token.DELPROP:    return "delete";
-            case Token.DO:         return "do";
-            case Token.ELSE:       return "else";
-            case Token.FALSE:      return "false";
-            case Token.FOR:        return "for";
-            case Token.FUNCTION:   return "function";
-            case Token.IF:         return "if";
-            case Token.IN:         return "in";
-            case Token.LET:        return "let";
-            case Token.NEW:        return "new";
-            case Token.NULL:       return "null";
-            case Token.RETURN:     return "return";
-            case Token.SWITCH:     return "switch";
-            case Token.THIS:       return "this";
-            case Token.TRUE:       return "true";
-            case Token.TYPEOF:     return "typeof";
-            case Token.VAR:        return "var";
-            case Token.VOID:       return "void";
-            case Token.WHILE:      return "while";
-            case Token.WITH:       return "with";
-            case Token.YIELD:      return "yield";
-            case Token.CATCH:      return "catch";
-            case Token.CONST:      return "const";
-            case Token.DEBUGGER:   return "debugger";
-            case Token.FINALLY:    return "finally";
-            case Token.INSTANCEOF: return "instanceof";
-            case Token.THROW:      return "throw";
-            case Token.TRY:        return "try";
-            default:               return null;
+            case Token.BREAK:
+                return "break";
+            case Token.CASE:
+                return "case";
+            case Token.CONTINUE:
+                return "continue";
+            case Token.DEFAULT:
+                return "default";
+            case Token.DELPROP:
+                return "delete";
+            case Token.DO:
+                return "do";
+            case Token.ELSE:
+                return "else";
+            case Token.FALSE:
+                return "false";
+            case Token.FOR:
+                return "for";
+            case Token.FUNCTION:
+                return "function";
+            case Token.IF:
+                return "if";
+            case Token.IN:
+                return "in";
+            case Token.LET:
+                return "let";
+            case Token.NEW:
+                return "new";
+            case Token.NULL:
+                return "null";
+            case Token.RETURN:
+                return "return";
+            case Token.SWITCH:
+                return "switch";
+            case Token.THIS:
+                return "this";
+            case Token.TRUE:
+                return "true";
+            case Token.TYPEOF:
+                return "typeof";
+            case Token.VAR:
+                return "var";
+            case Token.VOID:
+                return "void";
+            case Token.WHILE:
+                return "while";
+            case Token.WITH:
+                return "with";
+            case Token.YIELD:
+                return "yield";
+            case Token.CATCH:
+                return "catch";
+            case Token.CONST:
+                return "const";
+            case Token.DEBUGGER:
+                return "debugger";
+            case Token.FINALLY:
+                return "finally";
+            case Token.INSTANCEOF:
+                return "instanceof";
+            case Token.THROW:
+                return "throw";
+            case Token.TRY:
+                return "try";
+            default:
+                return null;
         }
     }
 
     /**
      * Return true if the passed code is a valid Token constant.
+     *
      * @param code a potential token code
      * @return true if it's a known token
      */
     public static boolean isValidToken(int code) {
-        return code >= ERROR
-                && code <= LAST_TOKEN;
+        return code >= ERROR && code <= LAST_TOKEN;
     }
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/TokenStream.java rhino-1.7.14/src/org/mozilla/javascript/TokenStream.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/TokenStream.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/TokenStream.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,35 +6,37 @@
 
 package org.mozilla.javascript;
 
-import java.io.*;
+import java.io.IOException;
+import java.io.Reader;
+import java.math.BigInteger;
 
 /**
  * This class implements the JavaScript scanner.
  *
- * It is based on the C source files jsscan.c and jsscan.h
- * in the jsref package.
+ * <p>It is based on the C source files jsscan.c and jsscan.h in the jsref package.
  *
  * @see org.mozilla.javascript.Parser
- *
  * @author Mike McCabe
  * @author Brendan Eich
  */
-
-class TokenStream
-{
+class TokenStream {
     /*
      * For chars - because we need something out-of-range
      * to check.  (And checking EOF by exception is annoying.)
      * Note distinction from EOF token type!
      */
-    private final static int
-        EOF_CHAR = -1;
+    private static final int EOF_CHAR = -1;
+
+    /*
+     * Return value for readDigits() to signal the caller has
+     * to return an number format problem.
+     */
+    private static final int REPORT_NUMBER_FORMAT_ERROR = -2;
 
-    private final static char BYTE_ORDER_MARK = '\uFEFF';
+    private static final char BYTE_ORDER_MARK = '\uFEFF';
+    private static final char NUMERIC_SEPARATOR = '_';
 
-    TokenStream(Parser parser, Reader sourceReader, String sourceString,
-                int lineno)
-    {
+    TokenStream(Parser parser, Reader sourceReader, String sourceString, int lineno) {
         this.parser = parser;
         this.lineno = lineno;
         if (sourceReader != null) {
@@ -54,19 +56,21 @@
      * TokenStream; if getToken has been called since the passed token
      * was scanned, the op or string printed may be incorrect.
      */
-    String tokenToString(int token)
-    {
+    String tokenToString(int token) {
         if (Token.printTrees) {
             String name = Token.name(token);
 
             switch (token) {
-            case Token.STRING:
-            case Token.REGEXP:
-            case Token.NAME:
-                return name + " `" + this.string + "'";
+                case Token.STRING:
+                case Token.REGEXP:
+                case Token.NAME:
+                    return name + " `" + this.string + "'";
 
-            case Token.NUMBER:
-                return "NUMBER " + this.number;
+                case Token.NUMBER:
+                    return "NUMBER " + this.number;
+
+                case Token.BIGINT:
+                    return "BIGINT " + this.bigInt.toString();
             }
 
             return name;
@@ -74,380 +78,556 @@
         return "";
     }
 
-    static boolean isKeyword(String s, int version, boolean isStrict)
-    {
+    static boolean isKeyword(String s, int version, boolean isStrict) {
         return Token.EOF != stringToKeyword(s, version, isStrict);
     }
 
-    private static int stringToKeyword(String name, int version, boolean isStrict)
-    {
+    private static int stringToKeyword(String name, int version, boolean isStrict) {
         if (version < Context.VERSION_ES6) {
             return stringToKeywordForJS(name);
-        } else {
-            return stringToKeywordForES(name, isStrict);
         }
+        return stringToKeywordForES(name, isStrict);
     }
 
-    /**
-     * JavaScript 1.8 and earlier
-     */
-    private static int stringToKeywordForJS(String name)
-    {
-// #string_id_map#
-// The following assumes that Token.EOF == 0
-        final int
-            Id_break         = Token.BREAK,
-            Id_case          = Token.CASE,
-            Id_continue      = Token.CONTINUE,
-            Id_default       = Token.DEFAULT,
-            Id_delete        = Token.DELPROP,
-            Id_do            = Token.DO,
-            Id_else          = Token.ELSE,
-            Id_export        = Token.RESERVED,
-            Id_false         = Token.FALSE,
-            Id_for           = Token.FOR,
-            Id_function      = Token.FUNCTION,
-            Id_if            = Token.IF,
-            Id_in            = Token.IN,
-            Id_let           = Token.LET,  // reserved ES5 strict
-            Id_new           = Token.NEW,
-            Id_null          = Token.NULL,
-            Id_return        = Token.RETURN,
-            Id_switch        = Token.SWITCH,
-            Id_this          = Token.THIS,
-            Id_true          = Token.TRUE,
-            Id_typeof        = Token.TYPEOF,
-            Id_var           = Token.VAR,
-            Id_void          = Token.VOID,
-            Id_while         = Token.WHILE,
-            Id_with          = Token.WITH,
-            Id_yield         = Token.YIELD,  // reserved ES5 strict
-
-            // the following are #ifdef RESERVE_JAVA_KEYWORDS in jsscan.c
-            Id_abstract      = Token.RESERVED,  // ES3 only
-            Id_boolean       = Token.RESERVED,  // ES3 only
-            Id_byte          = Token.RESERVED,  // ES3 only
-            Id_catch         = Token.CATCH,
-            Id_char          = Token.RESERVED,  // ES3 only
-            Id_class         = Token.RESERVED,
-            Id_const         = Token.CONST,     // reserved
-            Id_debugger      = Token.DEBUGGER,
-            Id_double        = Token.RESERVED,  // ES3 only
-            Id_enum          = Token.RESERVED,
-            Id_extends       = Token.RESERVED,
-            Id_final         = Token.RESERVED,  // ES3 only
-            Id_finally       = Token.FINALLY,
-            Id_float         = Token.RESERVED,  // ES3 only
-            Id_goto          = Token.RESERVED,  // ES3 only
-            Id_implements    = Token.RESERVED,  // ES3, ES5 strict
-            Id_import        = Token.RESERVED,
-            Id_instanceof    = Token.INSTANCEOF,
-            Id_int           = Token.RESERVED,  // ES3
-            Id_interface     = Token.RESERVED,  // ES3, ES5 strict
-            Id_long          = Token.RESERVED,  // ES3 only
-            Id_native        = Token.RESERVED,  // ES3 only
-            Id_package       = Token.RESERVED,  // ES3, ES5 strict
-            Id_private       = Token.RESERVED,  // ES3, ES5 strict
-            Id_protected     = Token.RESERVED,  // ES3, ES5 strict
-            Id_public        = Token.RESERVED,  // ES3, ES5 strict
-            Id_short         = Token.RESERVED,  // ES3 only
-            Id_static        = Token.RESERVED,  // ES3, ES5 strict
-            Id_super         = Token.RESERVED,
-            Id_synchronized  = Token.RESERVED,  // ES3 only
-            Id_throw         = Token.THROW,
-            Id_throws        = Token.RESERVED,  // ES3 only
-            Id_transient     = Token.RESERVED,  // ES3 only
-            Id_try           = Token.TRY,
-            Id_volatile      = Token.RESERVED;  // ES3 only
+    /** JavaScript 1.8 and earlier */
+    private static int stringToKeywordForJS(String name) {
+        // The following assumes that Token.EOF == 0
+        final int Id_break = Token.BREAK,
+                Id_case = Token.CASE,
+                Id_continue = Token.CONTINUE,
+                Id_default = Token.DEFAULT,
+                Id_delete = Token.DELPROP,
+                Id_do = Token.DO,
+                Id_else = Token.ELSE,
+                Id_export = Token.RESERVED,
+                Id_false = Token.FALSE,
+                Id_for = Token.FOR,
+                Id_function = Token.FUNCTION,
+                Id_if = Token.IF,
+                Id_in = Token.IN,
+                Id_let = Token.LET, // reserved ES5 strict
+                Id_new = Token.NEW,
+                Id_null = Token.NULL,
+                Id_return = Token.RETURN,
+                Id_switch = Token.SWITCH,
+                Id_this = Token.THIS,
+                Id_true = Token.TRUE,
+                Id_typeof = Token.TYPEOF,
+                Id_var = Token.VAR,
+                Id_void = Token.VOID,
+                Id_while = Token.WHILE,
+                Id_with = Token.WITH,
+                Id_yield = Token.YIELD, // reserved ES5 strict
+
+                // the following are #ifdef RESERVE_JAVA_KEYWORDS in jsscan.c
+                Id_abstract = Token.RESERVED, // ES3 only
+                Id_boolean = Token.RESERVED, // ES3 only
+                Id_byte = Token.RESERVED, // ES3 only
+                Id_catch = Token.CATCH,
+                Id_char = Token.RESERVED, // ES3 only
+                Id_class = Token.RESERVED,
+                Id_const = Token.CONST, // reserved
+                Id_debugger = Token.DEBUGGER,
+                Id_double = Token.RESERVED, // ES3 only
+                Id_enum = Token.RESERVED,
+                Id_extends = Token.RESERVED,
+                Id_final = Token.RESERVED, // ES3 only
+                Id_finally = Token.FINALLY,
+                Id_float = Token.RESERVED, // ES3 only
+                Id_goto = Token.RESERVED, // ES3 only
+                Id_implements = Token.RESERVED, // ES3, ES5 strict
+                Id_import = Token.RESERVED,
+                Id_instanceof = Token.INSTANCEOF,
+                Id_int = Token.RESERVED, // ES3
+                Id_interface = Token.RESERVED, // ES3, ES5 strict
+                Id_long = Token.RESERVED, // ES3 only
+                Id_native = Token.RESERVED, // ES3 only
+                Id_package = Token.RESERVED, // ES3, ES5 strict
+                Id_private = Token.RESERVED, // ES3, ES5 strict
+                Id_protected = Token.RESERVED, // ES3, ES5 strict
+                Id_public = Token.RESERVED, // ES3, ES5 strict
+                Id_short = Token.RESERVED, // ES3 only
+                Id_static = Token.RESERVED, // ES3, ES5 strict
+                Id_super = Token.RESERVED,
+                Id_synchronized = Token.RESERVED, // ES3 only
+                Id_throw = Token.THROW,
+                Id_throws = Token.RESERVED, // ES3 only
+                Id_transient = Token.RESERVED, // ES3 only
+                Id_try = Token.TRY,
+                Id_volatile = Token.RESERVED; // ES3 only
 
         int id;
         String s = name;
-// #generated# Last update: 2007-04-18 13:53:30 PDT
-        L0: { id = 0; String X = null; int c;
-            L: switch (s.length()) {
-            case 2: c=s.charAt(1);
-                if (c=='f') { if (s.charAt(0)=='i') {id=Id_if; break L0;} }
-                else if (c=='n') { if (s.charAt(0)=='i') {id=Id_in; break L0;} }
-                else if (c=='o') { if (s.charAt(0)=='d') {id=Id_do; break L0;} }
-                break L;
-            case 3: switch (s.charAt(0)) {
-                case 'f': if (s.charAt(2)=='r' && s.charAt(1)=='o') {id=Id_for; break L0;} break L;
-                case 'i': if (s.charAt(2)=='t' && s.charAt(1)=='n') {id=Id_int; break L0;} break L;
-                case 'l': if (s.charAt(2)=='t' && s.charAt(1)=='e') {id=Id_let; break L0;} break L;
-                case 'n': if (s.charAt(2)=='w' && s.charAt(1)=='e') {id=Id_new; break L0;} break L;
-                case 't': if (s.charAt(2)=='y' && s.charAt(1)=='r') {id=Id_try; break L0;} break L;
-                case 'v': if (s.charAt(2)=='r' && s.charAt(1)=='a') {id=Id_var; break L0;} break L;
-                } break L;
-            case 4: switch (s.charAt(0)) {
-                case 'b': X="byte";id=Id_byte; break L;
-                case 'c': c=s.charAt(3);
-                    if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='a') {id=Id_case; break L0;} }
-                    else if (c=='r') { if (s.charAt(2)=='a' && s.charAt(1)=='h') {id=Id_char; break L0;} }
-                    break L;
-                case 'e': c=s.charAt(3);
-                    if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='l') {id=Id_else; break L0;} }
-                    else if (c=='m') { if (s.charAt(2)=='u' && s.charAt(1)=='n') {id=Id_enum; break L0;} }
-                    break L;
-                case 'g': X="goto";id=Id_goto; break L;
-                case 'l': X="long";id=Id_long; break L;
-                case 'n': X="null";id=Id_null; break L;
-                case 't': c=s.charAt(3);
-                    if (c=='e') { if (s.charAt(2)=='u' && s.charAt(1)=='r') {id=Id_true; break L0;} }
-                    else if (c=='s') { if (s.charAt(2)=='i' && s.charAt(1)=='h') {id=Id_this; break L0;} }
-                    break L;
-                case 'v': X="void";id=Id_void; break L;
-                case 'w': X="with";id=Id_with; break L;
-                } break L;
-            case 5: switch (s.charAt(2)) {
-                case 'a': X="class";id=Id_class; break L;
-                case 'e': c=s.charAt(0);
-                    if (c=='b') { X="break";id=Id_break; }
-                    else if (c=='y') { X="yield";id=Id_yield; }
-                    break L;
-                case 'i': X="while";id=Id_while; break L;
-                case 'l': X="false";id=Id_false; break L;
-                case 'n': c=s.charAt(0);
-                    if (c=='c') { X="const";id=Id_const; }
-                    else if (c=='f') { X="final";id=Id_final; }
-                    break L;
-                case 'o': c=s.charAt(0);
-                    if (c=='f') { X="float";id=Id_float; }
-                    else if (c=='s') { X="short";id=Id_short; }
-                    break L;
-                case 'p': X="super";id=Id_super; break L;
-                case 'r': X="throw";id=Id_throw; break L;
-                case 't': X="catch";id=Id_catch; break L;
-                } break L;
-            case 6: switch (s.charAt(1)) {
-                case 'a': X="native";id=Id_native; break L;
-                case 'e': c=s.charAt(0);
-                    if (c=='d') { X="delete";id=Id_delete; }
-                    else if (c=='r') { X="return";id=Id_return; }
-                    break L;
-                case 'h': X="throws";id=Id_throws; break L;
-                case 'm': X="import";id=Id_import; break L;
-                case 'o': X="double";id=Id_double; break L;
-                case 't': X="static";id=Id_static; break L;
-                case 'u': X="public";id=Id_public; break L;
-                case 'w': X="switch";id=Id_switch; break L;
-                case 'x': X="export";id=Id_export; break L;
-                case 'y': X="typeof";id=Id_typeof; break L;
-                } break L;
-            case 7: switch (s.charAt(1)) {
-                case 'a': X="package";id=Id_package; break L;
-                case 'e': X="default";id=Id_default; break L;
-                case 'i': X="finally";id=Id_finally; break L;
-                case 'o': X="boolean";id=Id_boolean; break L;
-                case 'r': X="private";id=Id_private; break L;
-                case 'x': X="extends";id=Id_extends; break L;
-                } break L;
-            case 8: switch (s.charAt(0)) {
-                case 'a': X="abstract";id=Id_abstract; break L;
-                case 'c': X="continue";id=Id_continue; break L;
-                case 'd': X="debugger";id=Id_debugger; break L;
-                case 'f': X="function";id=Id_function; break L;
-                case 'v': X="volatile";id=Id_volatile; break L;
-                } break L;
-            case 9: c=s.charAt(0);
-                if (c=='i') { X="interface";id=Id_interface; }
-                else if (c=='p') { X="protected";id=Id_protected; }
-                else if (c=='t') { X="transient";id=Id_transient; }
-                break L;
-            case 10: c=s.charAt(1);
-                if (c=='m') { X="implements";id=Id_implements; }
-                else if (c=='n') { X="instanceof";id=Id_instanceof; }
-                break L;
-            case 12: X="synchronized";id=Id_synchronized; break L;
-            }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-        }
-// #/generated#
-// #/string_id_map#
-        if (id == 0) { return Token.EOF; }
+        switch (s) {
+            case "break":
+                id = Id_break;
+                break;
+            case "case":
+                id = Id_case;
+                break;
+            case "continue":
+                id = Id_continue;
+                break;
+            case "default":
+                id = Id_default;
+                break;
+            case "delete":
+                id = Id_delete;
+                break;
+            case "do":
+                id = Id_do;
+                break;
+            case "else":
+                id = Id_else;
+                break;
+            case "export":
+                id = Id_export;
+                break;
+            case "false":
+                id = Id_false;
+                break;
+            case "for":
+                id = Id_for;
+                break;
+            case "function":
+                id = Id_function;
+                break;
+            case "if":
+                id = Id_if;
+                break;
+            case "in":
+                id = Id_in;
+                break;
+            case "let":
+                id = Id_let;
+                break;
+            case "new":
+                id = Id_new;
+                break;
+            case "null":
+                id = Id_null;
+                break;
+            case "return":
+                id = Id_return;
+                break;
+            case "switch":
+                id = Id_switch;
+                break;
+            case "this":
+                id = Id_this;
+                break;
+            case "true":
+                id = Id_true;
+                break;
+            case "typeof":
+                id = Id_typeof;
+                break;
+            case "var":
+                id = Id_var;
+                break;
+            case "void":
+                id = Id_void;
+                break;
+            case "while":
+                id = Id_while;
+                break;
+            case "with":
+                id = Id_with;
+                break;
+            case "yield":
+                id = Id_yield;
+                break;
+            case "abstract":
+                id = Id_abstract;
+                break;
+            case "boolean":
+                id = Id_boolean;
+                break;
+            case "byte":
+                id = Id_byte;
+                break;
+            case "catch":
+                id = Id_catch;
+                break;
+            case "char":
+                id = Id_char;
+                break;
+            case "class":
+                id = Id_class;
+                break;
+            case "const":
+                id = Id_const;
+                break;
+            case "debugger":
+                id = Id_debugger;
+                break;
+            case "double":
+                id = Id_double;
+                break;
+            case "enum":
+                id = Id_enum;
+                break;
+            case "extends":
+                id = Id_extends;
+                break;
+            case "final":
+                id = Id_final;
+                break;
+            case "finally":
+                id = Id_finally;
+                break;
+            case "float":
+                id = Id_float;
+                break;
+            case "goto":
+                id = Id_goto;
+                break;
+            case "implements":
+                id = Id_implements;
+                break;
+            case "import":
+                id = Id_import;
+                break;
+            case "instanceof":
+                id = Id_instanceof;
+                break;
+            case "int":
+                id = Id_int;
+                break;
+            case "interface":
+                id = Id_interface;
+                break;
+            case "long":
+                id = Id_long;
+                break;
+            case "native":
+                id = Id_native;
+                break;
+            case "package":
+                id = Id_package;
+                break;
+            case "private":
+                id = Id_private;
+                break;
+            case "protected":
+                id = Id_protected;
+                break;
+            case "public":
+                id = Id_public;
+                break;
+            case "short":
+                id = Id_short;
+                break;
+            case "static":
+                id = Id_static;
+                break;
+            case "super":
+                id = Id_super;
+                break;
+            case "synchronized":
+                id = Id_synchronized;
+                break;
+            case "throw":
+                id = Id_throw;
+                break;
+            case "throws":
+                id = Id_throws;
+                break;
+            case "transient":
+                id = Id_transient;
+                break;
+            case "try":
+                id = Id_try;
+                break;
+            case "volatile":
+                id = Id_volatile;
+                break;
+            default:
+                id = 0;
+                break;
+        }
+        if (id == 0) {
+            return Token.EOF;
+        }
         return id & 0xff;
     }
 
-    /**
-     * ECMAScript 6.
-     */
-    private static int stringToKeywordForES(String name, boolean isStrict)
-    {
-// #string_id_map#
-// The following assumes that Token.EOF == 0
+    /** ECMAScript 6. */
+    private static int stringToKeywordForES(String name, boolean isStrict) {
+        // The following assumes that Token.EOF == 0
         final int
-            // 11.6.2.1 Keywords (ECMAScript2015)
-            Id_break         = Token.BREAK,
-            Id_case          = Token.CASE,
-            Id_catch         = Token.CATCH,
-            Id_class         = Token.RESERVED,
-            Id_const         = Token.CONST,
-            Id_continue      = Token.CONTINUE,
-            Id_debugger      = Token.DEBUGGER,
-            Id_default       = Token.DEFAULT,
-            Id_delete        = Token.DELPROP,
-            Id_do            = Token.DO,
-            Id_else          = Token.ELSE,
-            Id_export        = Token.RESERVED,
-            Id_extends       = Token.RESERVED,
-            Id_finally       = Token.FINALLY,
-            Id_for           = Token.FOR,
-            Id_function      = Token.FUNCTION,
-            Id_if            = Token.IF,
-            Id_import        = Token.RESERVED,
-            Id_in            = Token.IN,
-            Id_instanceof    = Token.INSTANCEOF,
-            Id_new           = Token.NEW,
-            Id_return        = Token.RETURN,
-            Id_super         = Token.RESERVED,
-            Id_switch        = Token.SWITCH,
-            Id_this          = Token.THIS,
-            Id_throw         = Token.THROW,
-            Id_try           = Token.TRY,
-            Id_typeof        = Token.TYPEOF,
-            Id_var           = Token.VAR,
-            Id_void          = Token.VOID,
-            Id_while         = Token.WHILE,
-            Id_with          = Token.WITH,
-            Id_yield         = Token.YIELD,
-
-            // 11.6.2.2 Future Reserved Words
-            Id_await         = Token.RESERVED,
-            Id_enum          = Token.RESERVED,
-
-            // 11.6.2.2 NOTE Strict Future Reserved Words
-            Id_implements    = Token.RESERVED,
-            Id_interface     = Token.RESERVED,
-            Id_package       = Token.RESERVED,
-            Id_private       = Token.RESERVED,
-            Id_protected     = Token.RESERVED,
-            Id_public        = Token.RESERVED,
-
-            // 11.8 Literals
-            Id_false         = Token.FALSE,
-            Id_null          = Token.NULL,
-            Id_true          = Token.TRUE,
-
-            // Non ReservedWord, but Non IdentifierName in strict mode code.
-            // 12.1.1 Static Semantics: Early Errors
-            Id_let           = Token.LET,   // TODO : Valid IdentifierName in non-strict mode.
-            Id_static        = Token.RESERVED; 
+                // 11.6.2.1 Keywords (ECMAScript2015)
+                Id_break = Token.BREAK,
+                Id_case = Token.CASE,
+                Id_catch = Token.CATCH,
+                Id_class = Token.RESERVED,
+                Id_const = Token.CONST,
+                Id_continue = Token.CONTINUE,
+                Id_debugger = Token.DEBUGGER,
+                Id_default = Token.DEFAULT,
+                Id_delete = Token.DELPROP,
+                Id_do = Token.DO,
+                Id_else = Token.ELSE,
+                Id_export = Token.RESERVED,
+                Id_extends = Token.RESERVED,
+                Id_finally = Token.FINALLY,
+                Id_for = Token.FOR,
+                Id_function = Token.FUNCTION,
+                Id_if = Token.IF,
+                Id_import = Token.RESERVED,
+                Id_in = Token.IN,
+                Id_instanceof = Token.INSTANCEOF,
+                Id_new = Token.NEW,
+                Id_return = Token.RETURN,
+                Id_super = Token.RESERVED,
+                Id_switch = Token.SWITCH,
+                Id_this = Token.THIS,
+                Id_throw = Token.THROW,
+                Id_try = Token.TRY,
+                Id_typeof = Token.TYPEOF,
+                Id_var = Token.VAR,
+                Id_void = Token.VOID,
+                Id_while = Token.WHILE,
+                Id_with = Token.WITH,
+                Id_yield = Token.YIELD,
+
+                // 11.6.2.2 Future Reserved Words
+                Id_await = Token.RESERVED,
+                Id_enum = Token.RESERVED,
+
+                // 11.6.2.2 NOTE Strict Future Reserved Words
+                Id_implements = Token.RESERVED,
+                Id_interface = Token.RESERVED,
+                Id_package = Token.RESERVED,
+                Id_private = Token.RESERVED,
+                Id_protected = Token.RESERVED,
+                Id_public = Token.RESERVED,
+
+                // 11.8 Literals
+                Id_false = Token.FALSE,
+                Id_null = Token.NULL,
+                Id_true = Token.TRUE,
+
+                // Non ReservedWord, but Non IdentifierName in strict mode code.
+                // 12.1.1 Static Semantics: Early Errors
+                Id_let = Token.LET, // TODO : Valid IdentifierName in non-strict mode.
+                Id_static = Token.RESERVED;
 
-        int id;
+        int id = 0;
         String s = name;
-// #generated# Last update: 2007-04-18 13:53:30 PDT
-        L0: { id = 0; String X = null; int c;
-            L: switch (s.length()) {
-            case 2: c=s.charAt(1);
-                if (c=='f') { if (s.charAt(0)=='i') {id=Id_if; break L0;} }
-                else if (c=='n') { if (s.charAt(0)=='i') {id=Id_in; break L0;} }
-                else if (c=='o') { if (s.charAt(0)=='d') {id=Id_do; break L0;} }
-                break L;
-            case 3: switch (s.charAt(0)) {
-                case 'f': if (s.charAt(2)=='r' && s.charAt(1)=='o') {id=Id_for; break L0;} break L;
-                case 'l': if (s.charAt(2)=='t' && s.charAt(1)=='e') {id=Id_let; break L0;} break L;
-                case 'n': if (s.charAt(2)=='w' && s.charAt(1)=='e') {id=Id_new; break L0;} break L;
-                case 't': if (s.charAt(2)=='y' && s.charAt(1)=='r') {id=Id_try; break L0;} break L;
-                case 'v': if (s.charAt(2)=='r' && s.charAt(1)=='a') {id=Id_var; break L0;} break L;
-                } break L;
-            case 4: switch (s.charAt(0)) {
-                case 'c': c=s.charAt(3);
-                    if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='a') {id=Id_case; break L0;} }
-                    break L;
-                case 'e': c=s.charAt(3);
-                    if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='l') {id=Id_else; break L0;} }
-                    else if (c=='m') { if (s.charAt(2)=='u' && s.charAt(1)=='n') {id=Id_enum; break L0;} }
-                    break L;
-                case 'n': X="null";id=Id_null; break L;
-                case 't': c=s.charAt(3);
-                    if (c=='e') { if (s.charAt(2)=='u' && s.charAt(1)=='r') {id=Id_true; break L0;} }
-                    else if (c=='s') { if (s.charAt(2)=='i' && s.charAt(1)=='h') {id=Id_this; break L0;} }
-                    break L;
-                case 'v': X="void";id=Id_void; break L;
-                case 'w': X="with";id=Id_with; break L;
-                } break L;
-            case 5: switch (s.charAt(2)) {
-                case 'a': c=s.charAt(0);
-                    if (c=='c') { X="class";id=Id_class; }
-                    else if (c=='a') { X="await";id=Id_await; }
-                    break L;
-                case 'e': c=s.charAt(0);
-                    if (c=='b') { X="break";id=Id_break; }
-                    else if (c=='y') { X="yield";id=Id_yield; }
-                    break L;
-                case 'i': X="while";id=Id_while; break L;
-                case 'l': X="false";id=Id_false; break L;
-                case 'n': X="const";id=Id_const; break L;
-                case 'p': X="super";id=Id_super; break L;
-                case 'r': X="throw";id=Id_throw; break L;
-                case 't': X="catch";id=Id_catch; break L;
-                } break L;
-            case 6: switch (s.charAt(1)) {
-                case 'e': c=s.charAt(0);
-                    if (c=='d') { X="delete";id=Id_delete; }
-                    else if (c=='r') { X="return";id=Id_return; }
-                    break L;
-                case 'm': X="import";id=Id_import; break L;
-                case 't': if (isStrict) { X="static";id=Id_static; break L; }
-                case 'u': if (isStrict) { X="public";id=Id_public; break L; }
-                case 'w': X="switch";id=Id_switch; break L;
-                case 'x': X="export";id=Id_export; break L;
-                case 'y': X="typeof";id=Id_typeof; break L;
-                } break L;
-            case 7: switch (s.charAt(1)) {
-                case 'a': if (isStrict) { X="package";id=Id_package; break L; }
-                case 'e': X="default";id=Id_default; break L;
-                case 'i': X="finally";id=Id_finally; break L;
-                case 'r': if (isStrict) { X="private";id=Id_private; break L; }
-                case 'x': X="extends";id=Id_extends; break L;
-                } break L;
-            case 8: switch (s.charAt(0)) {
-                case 'c': X="continue";id=Id_continue; break L;
-                case 'd': X="debugger";id=Id_debugger; break L;
-                case 'f': X="function";id=Id_function; break L;
-                } break L;
-            case 9: c=s.charAt(0);
-                if (c=='i' && isStrict) { X="interface";id=Id_interface; }
-                else if (c=='p' && isStrict) { X="protected";id=Id_protected; }
-                break L;
-            case 10: c=s.charAt(1);
-                if (c=='m' && isStrict) { X="implements";id=Id_implements; }
-                else if (c=='n') { X="instanceof";id=Id_instanceof; }
-                break L;
-            }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-        }
-// #/generated#
-// #/string_id_map#
-        if (id == 0) { return Token.EOF; }
+        switch (s) {
+            case "break":
+                id = Id_break;
+                break;
+            case "case":
+                id = Id_case;
+                break;
+            case "catch":
+                id = Id_catch;
+                break;
+            case "class":
+                id = Id_class;
+                break;
+            case "const":
+                id = Id_const;
+                break;
+            case "continue":
+                id = Id_continue;
+                break;
+            case "debugger":
+                id = Id_debugger;
+                break;
+            case "default":
+                id = Id_default;
+                break;
+            case "delete":
+                id = Id_delete;
+                break;
+            case "do":
+                id = Id_do;
+                break;
+            case "else":
+                id = Id_else;
+                break;
+            case "export":
+                id = Id_export;
+                break;
+            case "extends":
+                id = Id_extends;
+                break;
+            case "finally":
+                id = Id_finally;
+                break;
+            case "for":
+                id = Id_for;
+                break;
+            case "function":
+                id = Id_function;
+                break;
+            case "if":
+                id = Id_if;
+                break;
+            case "import":
+                id = Id_import;
+                break;
+            case "in":
+                id = Id_in;
+                break;
+            case "instanceof":
+                id = Id_instanceof;
+                break;
+            case "new":
+                id = Id_new;
+                break;
+            case "return":
+                id = Id_return;
+                break;
+            case "super":
+                id = Id_super;
+                break;
+            case "switch":
+                id = Id_switch;
+                break;
+            case "this":
+                id = Id_this;
+                break;
+            case "throw":
+                id = Id_throw;
+                break;
+            case "try":
+                id = Id_try;
+                break;
+            case "typeof":
+                id = Id_typeof;
+                break;
+            case "var":
+                id = Id_var;
+                break;
+            case "void":
+                id = Id_void;
+                break;
+            case "while":
+                id = Id_while;
+                break;
+            case "with":
+                id = Id_with;
+                break;
+            case "yield":
+                id = Id_yield;
+                break;
+            case "await":
+                id = Id_await;
+                break;
+            case "enum":
+                id = Id_enum;
+                break;
+            case "implements":
+                if (isStrict) {
+                    id = Id_implements;
+                }
+                break;
+            case "interface":
+                if (isStrict) {
+                    id = Id_interface;
+                }
+                break;
+            case "package":
+                if (isStrict) {
+                    id = Id_package;
+                }
+                break;
+            case "private":
+                if (isStrict) {
+                    id = Id_private;
+                }
+                break;
+            case "protected":
+                if (isStrict) {
+                    id = Id_protected;
+                }
+                break;
+            case "public":
+                if (isStrict) {
+                    id = Id_public;
+                }
+                break;
+            case "false":
+                id = Id_false;
+                break;
+            case "null":
+                id = Id_null;
+                break;
+            case "true":
+                id = Id_true;
+                break;
+            case "let":
+                id = Id_let;
+                break;
+            case "static":
+                if (isStrict) {
+                    id = Id_static;
+                }
+                break;
+            default:
+                id = 0;
+                break;
+        }
+        if (id == 0) {
+            return Token.EOF;
+        }
         return id & 0xff;
     }
 
-    final String getSourceString() { return sourceString; }
+    final String getSourceString() {
+        return sourceString;
+    }
 
-    final int getLineno() { return lineno; }
+    final int getLineno() {
+        return lineno;
+    }
 
-    final String getString() { return string; }
+    final String getString() {
+        return string;
+    }
 
     final char getQuoteChar() {
         return (char) quoteChar;
     }
 
-    final double getNumber() { return number; }
-    final boolean isNumberBinary() { return isBinary; }
-    final boolean isNumberOldOctal() { return isOldOctal; }
-    final boolean isNumberOctal() { return isOctal; }
-    final boolean isNumberHex() { return isHex; }
+    final double getNumber() {
+        return number;
+    }
+
+    final BigInteger getBigInt() {
+        return bigInt;
+    }
+
+    final boolean isNumericBinary() {
+        return isBinary;
+    }
+
+    final boolean isNumericOldOctal() {
+        return isOldOctal;
+    }
 
-    final boolean eof() { return hitEOF; }
+    final boolean isNumericOctal() {
+        return isOctal;
+    }
+
+    final boolean isNumericHex() {
+        return isHex;
+    }
+
+    final boolean eof() {
+        return hitEOF;
+    }
 
-    final int getToken() throws IOException
-    {
+    final int getToken() throws IOException {
         int c;
 
-    retry:
-        for (;;) {
+        for (; ; ) {
             // Eat whitespace, possibly sensitive to newlines.
-            for (;;) {
+            for (; ; ) {
                 c = getChar();
                 if (c == EOF_CHAR) {
                     tokenBeg = cursor - 1;
@@ -488,7 +668,7 @@
                     c = '\\';
                 }
             } else {
-                identifierStart = Character.isJavaIdentifierStart((char)c);
+                identifierStart = Character.isJavaIdentifierStart((char) c);
                 if (identifierStart) {
                     stringBufferTop = 0;
                     addToString(c);
@@ -497,7 +677,7 @@
 
             if (identifierStart) {
                 boolean containsEscape = isUnicodeEscapeStart;
-                for (;;) {
+                for (; ; ) {
                     if (isUnicodeEscapeStart) {
                         // strictly speaking we should probably push-back
                         // all the bad characters if the <backslash>uXXXX
@@ -510,7 +690,9 @@
                             c = getChar();
                             escapeVal = Kit.xDigitToInt(c, escapeVal);
                             // Next check takes care about c < 0 and bad escape
-                            if (escapeVal < 0) { break; }
+                            if (escapeVal < 0) {
+                                break;
+                            }
                         }
                         if (escapeVal < 0) {
                             parser.addError("msg.invalid.escape");
@@ -526,13 +708,13 @@
                                 isUnicodeEscapeStart = true;
                                 containsEscape = true;
                             } else {
-                                parser.addError("msg.illegal.character");
+                                parser.addError("msg.illegal.character", c);
                                 return Token.ERROR;
                             }
                         } else {
-                            if (c == EOF_CHAR || c == BYTE_ORDER_MARK
-                                || !Character.isJavaIdentifierPart((char)c))
-                            {
+                            if (c == EOF_CHAR
+                                    || c == BYTE_ORDER_MARK
+                                    || !Character.isJavaIdentifierPart((char) c)) {
                                 break;
                             }
                             addToString(c);
@@ -547,19 +729,21 @@
                     // check if it's a keyword.
 
                     // Return the corresponding token if it's a keyword
-                    int result = stringToKeyword(str, parser.compilerEnv.getLanguageVersion(), parser.inUseStrictDirective());
+                    int result =
+                            stringToKeyword(
+                                    str,
+                                    parser.compilerEnv.getLanguageVersion(),
+                                    parser.inUseStrictDirective());
                     if (result != Token.EOF) {
-                        if ((result == Token.LET || result == Token.YIELD) &&
-                            parser.compilerEnv.getLanguageVersion()
-                               < Context.VERSION_1_7)
-                        {
+                        if ((result == Token.LET || result == Token.YIELD)
+                                && parser.compilerEnv.getLanguageVersion() < Context.VERSION_1_7) {
                             // LET and YIELD are tokens only in 1.7 and later
                             string = result == Token.LET ? "let" : "yield";
                             result = Token.NAME;
                         }
                         // Save the string in case we need to use in
                         // object literal definitions.
-                        this.string = (String)allStrings.intern(str);
+                        this.string = (String) allStrings.intern(str);
                         if (result != Token.RESERVED) {
                             return result;
                         } else if (parser.compilerEnv.getLanguageVersion() >= Context.VERSION_ES6) {
@@ -568,12 +752,15 @@
                             return result;
                         }
                     }
-                } else if (isKeyword(str, parser.compilerEnv.getLanguageVersion(), parser.inUseStrictDirective())) {
+                } else if (isKeyword(
+                        str,
+                        parser.compilerEnv.getLanguageVersion(),
+                        parser.inUseStrictDirective())) {
                     // If a string contains unicodes, and converted to a keyword,
                     // we convert the last character back to unicode
                     str = convertLastCharToHex(str);
                 }
-                this.string = (String)allStrings.intern(str);
+                this.string = (String) allStrings.intern(str);
                 return Token.NAME;
             }
 
@@ -606,55 +793,62 @@
                     }
                 }
 
-                boolean isEmpty = true;
-                if (base == 16) {
-                    while (0 <= Kit.xDigitToInt(c, 0)) {
-                        addToString(c);
-                        c = getChar();
-                        isEmpty = false;
+                int emptyDetector = stringBufferTop;
+                if (base == 10 || base == 16 || (base == 8 && !isOldOctal) || base == 2) {
+                    c = readDigits(base, c);
+                    if (c == REPORT_NUMBER_FORMAT_ERROR) {
+                        parser.addError("msg.caught.nfe");
+                        return Token.ERROR;
                     }
                 } else {
-                    while ('0' <= c && c <= '9') {
-                        if (base == 8 && c >= '8') {
-                            if (isOldOctal) {
-                                /*
-                                 * We permit 08 and 09 as decimal numbers, which
-                                 * makes our behavior a superset of the ECMA
-                                 * numeric grammar.  We might not always be so
-                                 * permissive, so we warn about it.
-                                 */
-                                parser.addWarning("msg.bad.octal.literal",
-                                                  c == '8' ? "8" : "9");
-                                base = 10;
-                            } else {
+                    while (isDigit(c)) {
+                        // finally the oldOctal case
+                        if (c >= '8') {
+                            /*
+                             * We permit 08 and 09 as decimal numbers, which
+                             * makes our behavior a superset of the ECMA
+                             * numeric grammar.  We might not always be so
+                             * permissive, so we warn about it.
+                             */
+                            parser.addWarning("msg.bad.octal.literal", c == '8' ? "8" : "9");
+                            base = 10;
+
+                            c = readDigits(base, c);
+                            if (c == REPORT_NUMBER_FORMAT_ERROR) {
                                 parser.addError("msg.caught.nfe");
                                 return Token.ERROR;
                             }
-                        } else if (base == 2 && c >= '2') {
-                            parser.addError("msg.caught.nfe");
-                            return Token.ERROR;
+                            break;
                         }
                         addToString(c);
                         c = getChar();
-                        isEmpty = false;
                     }
                 }
-                if (isEmpty && (isBinary || isOctal || isHex)) {
+                if (stringBufferTop == emptyDetector && (isBinary || isOctal || isHex)) {
                     parser.addError("msg.caught.nfe");
                     return Token.ERROR;
                 }
 
                 boolean isInteger = true;
+                boolean isBigInt = false;
 
-                if (base == 10 && (c == '.' || c == 'e' || c == 'E')) {
+                if (es6 && c == 'n') {
+                    isBigInt = true;
+                    c = getChar();
+                } else if (base == 10 && (c == '.' || c == 'e' || c == 'E')) {
                     isInteger = false;
                     if (c == '.') {
-                        do {
-                            addToString(c);
-                            c = getChar();
-                        } while (isDigit(c));
+                        isInteger = false;
+                        addToString(c);
+                        c = getChar();
+                        c = readDigits(base, c);
+                        if (c == REPORT_NUMBER_FORMAT_ERROR) {
+                            parser.addError("msg.caught.nfe");
+                            return Token.ERROR;
+                        }
                     }
                     if (c == 'e' || c == 'E') {
+                        isInteger = false;
                         addToString(c);
                         c = getChar();
                         if (c == '+' || c == '-') {
@@ -665,28 +859,45 @@
                             parser.addError("msg.missing.exponent");
                             return Token.ERROR;
                         }
-                        do {
-                            addToString(c);
-                            c = getChar();
-                        } while (isDigit(c));
+                        c = readDigits(base, c);
+                        if (c == REPORT_NUMBER_FORMAT_ERROR) {
+                            parser.addError("msg.caught.nfe");
+                            return Token.ERROR;
+                        }
                     }
                 }
                 ungetChar(c);
                 String numString = getStringFromBuffer();
                 this.string = numString;
 
+                // try to remove the separator in a fast way
+                int pos = numString.indexOf(NUMERIC_SEPARATOR);
+                if (pos != -1) {
+                    final char[] chars = numString.toCharArray();
+                    for (int i = pos + 1; i < chars.length; i++) {
+                        if (chars[i] != NUMERIC_SEPARATOR) {
+                            chars[pos++] = chars[i];
+                        }
+                    }
+                    numString = new String(chars, 0, pos);
+                }
+
+                if (isBigInt) {
+                    this.bigInt = new BigInteger(numString, base);
+                    return Token.BIGINT;
+                }
+
                 double dval;
                 if (base == 10 && !isInteger) {
                     try {
                         // Use Java conversion to number from string...
                         dval = Double.parseDouble(numString);
-                    }
-                    catch (NumberFormatException ex) {
+                    } catch (NumberFormatException ex) {
                         parser.addError("msg.caught.nfe");
                         return Token.ERROR;
                     }
                 } else {
-                    dval = ScriptRuntime.stringToNumber(numString, 0, base);
+                    dval = ScriptRuntime.stringPrefixToNumber(numString, 0, base);
                 }
 
                 this.number = dval;
@@ -703,10 +914,30 @@
                 quoteChar = c;
                 stringBufferTop = 0;
 
-                c = getChar(false);
-            strLoop: while (c != quoteChar) {
-                    if (c == '\n' || c == EOF_CHAR) {
-                        ungetChar(c);
+                c = getCharIgnoreLineEnd(false);
+                strLoop:
+                while (c != quoteChar) {
+                    boolean unterminated = false;
+                    if (c == EOF_CHAR) {
+                        unterminated = true;
+                    } else if (c == '\n') {
+                        switch (lineEndChar) {
+                            case '\n':
+                            case '\r':
+                                unterminated = true;
+                                break;
+                            case 0x2028: // <LS>
+                            case 0x2029: // <PS>
+                                // Line/Paragraph separators need to be included as is
+                                c = lineEndChar;
+                                break;
+                            default:
+                                break;
+                        }
+                    }
+
+                    if (unterminated) {
+                        ungetCharIgnoreLineEnd(c);
                         tokenEnd = cursor;
                         parser.addError("msg.unterminated.string.lit");
                         return Token.ERROR;
@@ -718,45 +949,57 @@
 
                         c = getChar();
                         switch (c) {
-                        case 'b': c = '\b'; break;
-                        case 'f': c = '\f'; break;
-                        case 'n': c = '\n'; break;
-                        case 'r': c = '\r'; break;
-                        case 't': c = '\t'; break;
-
-                        // \v a late addition to the ECMA spec,
-                        // it is not in Java, so use 0xb
-                        case 'v': c = 0xb; break;
+                            case 'b':
+                                c = '\b';
+                                break;
+                            case 'f':
+                                c = '\f';
+                                break;
+                            case 'n':
+                                c = '\n';
+                                break;
+                            case 'r':
+                                c = '\r';
+                                break;
+                            case 't':
+                                c = '\t';
+                                break;
 
-                        case 'u':
-                            // Get 4 hex digits; if the u escape is not
-                            // followed by 4 hex digits, use 'u' + the
-                            // literal character sequence that follows.
-                            int escapeStart = stringBufferTop;
-                            addToString('u');
-                            escapeVal = 0;
-                            for (int i = 0; i != 4; ++i) {
+                                // \v a late addition to the ECMA spec,
+                                // it is not in Java, so use 0xb
+                            case 'v':
+                                c = 0xb;
+                                break;
+
+                            case 'u':
+                                // Get 4 hex digits; if the u escape is not
+                                // followed by 4 hex digits, use 'u' + the
+                                // literal character sequence that follows.
+                                int escapeStart = stringBufferTop;
+                                addToString('u');
+                                escapeVal = 0;
+                                for (int i = 0; i != 4; ++i) {
+                                    c = getChar();
+                                    escapeVal = Kit.xDigitToInt(c, escapeVal);
+                                    if (escapeVal < 0) {
+                                        continue strLoop;
+                                    }
+                                    addToString(c);
+                                }
+                                // prepare for replace of stored 'u' sequence
+                                // by escape value
+                                stringBufferTop = escapeStart;
+                                c = escapeVal;
+                                break;
+                            case 'x':
+                                // Get 2 hex digits, defaulting to 'x'+literal
+                                // sequence, as above.
                                 c = getChar();
-                                escapeVal = Kit.xDigitToInt(c, escapeVal);
+                                escapeVal = Kit.xDigitToInt(c, 0);
                                 if (escapeVal < 0) {
+                                    addToString('x');
                                     continue strLoop;
                                 }
-                                addToString(c);
-                            }
-                            // prepare for replace of stored 'u' sequence
-                            // by escape value
-                            stringBufferTop = escapeStart;
-                            c = escapeVal;
-                            break;
-                        case 'x':
-                            // Get 2 hex digits, defaulting to 'x'+literal
-                            // sequence, as above.
-                            c = getChar();
-                            escapeVal = Kit.xDigitToInt(c, 0);
-                            if (escapeVal < 0) {
-                                addToString('x');
-                                continue strLoop;
-                            } else {
                                 int c1 = c;
                                 c = getChar();
                                 escapeVal = Kit.xDigitToInt(c, escapeVal);
@@ -764,36 +1007,34 @@
                                     addToString('x');
                                     addToString(c1);
                                     continue strLoop;
-                                } else {
-                                    // got 2 hex digits
-                                    c = escapeVal;
                                 }
-                            }
-                            break;
-
-                        case '\n':
-                            // Remove line terminator after escape to follow
-                            // SpiderMonkey and C/C++
-                            c = getChar();
-                            continue strLoop;
+                                // got 2 hex digits
+                                c = escapeVal;
+                                break;
 
-                        default:
-                            if ('0' <= c && c < '8') {
-                                int val = c - '0';
+                            case '\n':
+                                // Remove line terminator after escape to follow
+                                // SpiderMonkey and C/C++
                                 c = getChar();
+                                continue strLoop;
+
+                            default:
                                 if ('0' <= c && c < '8') {
-                                    val = 8 * val + c - '0';
+                                    int val = c - '0';
                                     c = getChar();
-                                    if ('0' <= c && c < '8' && val <= 037) {
-                                        // c is 3rd char of octal sequence only
-                                        // if the resulting val <= 0377
+                                    if ('0' <= c && c < '8') {
                                         val = 8 * val + c - '0';
                                         c = getChar();
+                                        if ('0' <= c && c < '8' && val <= 037) {
+                                            // c is 3rd char of octal sequence only
+                                            // if the resulting val <= 0377
+                                            val = 8 * val + c - '0';
+                                            c = getChar();
+                                        }
                                     }
+                                    ungetChar(c);
+                                    c = val;
                                 }
-                                ungetChar(c);
-                                c = val;
-                            }
                         }
                     }
                     addToString(c);
@@ -801,274 +1042,332 @@
                 }
 
                 String str = getStringFromBuffer();
-                this.string = (String)allStrings.intern(str);
+                this.string = (String) allStrings.intern(str);
                 return Token.STRING;
             }
 
             switch (c) {
-            case ';': return Token.SEMI;
-            case '[': return Token.LB;
-            case ']': return Token.RB;
-            case '{': return Token.LC;
-            case '}': return Token.RC;
-            case '(': return Token.LP;
-            case ')': return Token.RP;
-            case ',': return Token.COMMA;
-            case '?': return Token.HOOK;
-            case ':':
-                if (matchChar(':')) {
-                    return Token.COLONCOLON;
-                } else {
+                case ';':
+                    return Token.SEMI;
+                case '[':
+                    return Token.LB;
+                case ']':
+                    return Token.RB;
+                case '{':
+                    return Token.LC;
+                case '}':
+                    return Token.RC;
+                case '(':
+                    return Token.LP;
+                case ')':
+                    return Token.RP;
+                case ',':
+                    return Token.COMMA;
+                case '?':
+                    return Token.HOOK;
+                case ':':
+                    if (matchChar(':')) {
+                        return Token.COLONCOLON;
+                    }
                     return Token.COLON;
-                }
-            case '.':
-                if (matchChar('.')) {
-                    return Token.DOTDOT;
-                } else if (matchChar('(')) {
-                    return Token.DOTQUERY;
-                } else {
-                    return Token.DOT;
-                }
+                case '.':
+                    if (matchChar('.')) {
+                        return Token.DOTDOT;
+                    } else if (matchChar('(')) {
+                        return Token.DOTQUERY;
+                    } else {
+                        return Token.DOT;
+                    }
 
-            case '|':
-                if (matchChar('|')) {
-                    return Token.OR;
-                } else if (matchChar('=')) {
-                    return Token.ASSIGN_BITOR;
-                } else {
-                    return Token.BITOR;
-                }
+                case '|':
+                    if (matchChar('|')) {
+                        return Token.OR;
+                    } else if (matchChar('=')) {
+                        return Token.ASSIGN_BITOR;
+                    } else {
+                        return Token.BITOR;
+                    }
 
-            case '^':
-                if (matchChar('=')) {
-                    return Token.ASSIGN_BITXOR;
-                } else {
+                case '^':
+                    if (matchChar('=')) {
+                        return Token.ASSIGN_BITXOR;
+                    }
                     return Token.BITXOR;
-                }
 
-            case '&':
-                if (matchChar('&')) {
-                    return Token.AND;
-                } else if (matchChar('=')) {
-                    return Token.ASSIGN_BITAND;
-                } else {
-                    return Token.BITAND;
-                }
+                case '&':
+                    if (matchChar('&')) {
+                        return Token.AND;
+                    } else if (matchChar('=')) {
+                        return Token.ASSIGN_BITAND;
+                    } else {
+                        return Token.BITAND;
+                    }
 
-            case '=':
-                if (matchChar('=')) {
+                case '=':
                     if (matchChar('=')) {
-                        return Token.SHEQ;
-                    } else {
+                        if (matchChar('=')) {
+                            return Token.SHEQ;
+                        }
                         return Token.EQ;
+                    } else if (matchChar('>')) {
+                        return Token.ARROW;
+                    } else {
+                        return Token.ASSIGN;
                     }
-                } else if (matchChar('>')) {
-                    return Token.ARROW;
-                } else {
-                    return Token.ASSIGN;
-                }
 
-            case '!':
-                if (matchChar('=')) {
+                case '!':
                     if (matchChar('=')) {
-                        return Token.SHNE;
-                    } else {
+                        if (matchChar('=')) {
+                            return Token.SHNE;
+                        }
                         return Token.NE;
                     }
-                } else {
                     return Token.NOT;
-                }
 
-            case '<':
-                /* NB:treat HTML begin-comment as comment-till-eol */
-                if (matchChar('!')) {
-                    if (matchChar('-')) {
+                case '<':
+                    /* NB:treat HTML begin-comment as comment-till-eol */
+                    if (matchChar('!')) {
                         if (matchChar('-')) {
-                            tokenBeg = cursor - 4;
-                            skipLine();
-                            commentType = Token.CommentType.HTML;
-                            return Token.COMMENT;
+                            if (matchChar('-')) {
+                                tokenBeg = cursor - 4;
+                                skipLine();
+                                commentType = Token.CommentType.HTML;
+                                return Token.COMMENT;
+                            }
+                            ungetCharIgnoreLineEnd('-');
                         }
-                        ungetCharIgnoreLineEnd('-');
+                        ungetCharIgnoreLineEnd('!');
                     }
-                    ungetCharIgnoreLineEnd('!');
-                }
-                if (matchChar('<')) {
-                    if (matchChar('=')) {
-                        return Token.ASSIGN_LSH;
-                    } else {
+                    if (matchChar('<')) {
+                        if (matchChar('=')) {
+                            return Token.ASSIGN_LSH;
+                        }
                         return Token.LSH;
                     }
-                } else {
                     if (matchChar('=')) {
                         return Token.LE;
-                    } else {
-                        return Token.LT;
                     }
-                }
+                    return Token.LT;
 
-            case '>':
-                if (matchChar('>')) {
+                case '>':
                     if (matchChar('>')) {
-                        if (matchChar('=')) {
-                            return Token.ASSIGN_URSH;
-                        } else {
+                        if (matchChar('>')) {
+                            if (matchChar('=')) {
+                                return Token.ASSIGN_URSH;
+                            }
                             return Token.URSH;
                         }
-                    } else {
                         if (matchChar('=')) {
                             return Token.ASSIGN_RSH;
-                        } else {
-                            return Token.RSH;
                         }
+                        return Token.RSH;
                     }
-                } else {
                     if (matchChar('=')) {
                         return Token.GE;
-                    } else {
-                        return Token.GT;
                     }
-                }
+                    return Token.GT;
 
-            case '*':
-                if (matchChar('=')) {
-                    return Token.ASSIGN_MUL;
-                } else {
+                case '*':
+                    if (parser.compilerEnv.getLanguageVersion() >= Context.VERSION_ES6) {
+                        if (matchChar('*')) {
+                            if (matchChar('=')) {
+                                return Token.ASSIGN_EXP;
+                            }
+                            return Token.EXP;
+                        }
+                    }
+                    if (matchChar('=')) {
+                        return Token.ASSIGN_MUL;
+                    }
                     return Token.MUL;
-                }
 
-            case '/':
-                markCommentStart();
-                // is it a // comment?
-                if (matchChar('/')) {
-                    tokenBeg = cursor - 2;
-                    skipLine();
-                    commentType = Token.CommentType.LINE;
-                    return Token.COMMENT;
-                }
-                // is it a /* or /** comment?
-                if (matchChar('*')) {
-                    boolean lookForSlash = false;
-                    tokenBeg = cursor - 2;
-                    if (matchChar('*')) {
-                        lookForSlash = true;
-                        commentType = Token.CommentType.JSDOC;
-                    } else {
-                        commentType = Token.CommentType.BLOCK_COMMENT;
+                case '/':
+                    markCommentStart();
+                    // is it a // comment?
+                    if (matchChar('/')) {
+                        tokenBeg = cursor - 2;
+                        skipLine();
+                        commentType = Token.CommentType.LINE;
+                        return Token.COMMENT;
                     }
-                    for (;;) {
-                        c = getChar();
-                        if (c == EOF_CHAR) {
-                            tokenEnd = cursor - 1;
-                            parser.addError("msg.unterminated.comment");
-                            return Token.COMMENT;
-                        } else if (c == '*') {
+                    // is it a /* or /** comment?
+                    if (matchChar('*')) {
+                        boolean lookForSlash = false;
+                        tokenBeg = cursor - 2;
+                        if (matchChar('*')) {
                             lookForSlash = true;
-                        } else if (c == '/') {
-                            if (lookForSlash) {
-                                tokenEnd = cursor;
+                            commentType = Token.CommentType.JSDOC;
+                        } else {
+                            commentType = Token.CommentType.BLOCK_COMMENT;
+                        }
+                        for (; ; ) {
+                            c = getChar();
+                            if (c == EOF_CHAR) {
+                                tokenEnd = cursor - 1;
+                                parser.addError("msg.unterminated.comment");
                                 return Token.COMMENT;
+                            } else if (c == '*') {
+                                lookForSlash = true;
+                            } else if (c == '/') {
+                                if (lookForSlash) {
+                                    tokenEnd = cursor;
+                                    return Token.COMMENT;
+                                }
+                            } else {
+                                lookForSlash = false;
+                                tokenEnd = cursor;
                             }
-                        } else {
-                            lookForSlash = false;
-                            tokenEnd = cursor;
                         }
                     }
-                }
 
-                if (matchChar('=')) {
-                    return Token.ASSIGN_DIV;
-                } else {
+                    if (matchChar('=')) {
+                        return Token.ASSIGN_DIV;
+                    }
                     return Token.DIV;
-                }
 
-            case '%':
-                if (matchChar('=')) {
-                    return Token.ASSIGN_MOD;
-                } else {
+                case '%':
+                    if (matchChar('=')) {
+                        return Token.ASSIGN_MOD;
+                    }
                     return Token.MOD;
-                }
 
-            case '~':
-                return Token.BITNOT;
+                case '~':
+                    return Token.BITNOT;
 
-            case '+':
-                if (matchChar('=')) {
-                    return Token.ASSIGN_ADD;
-                } else if (matchChar('+')) {
-                    return Token.INC;
-                } else {
-                    return Token.ADD;
-                }
+                case '+':
+                    if (matchChar('=')) {
+                        return Token.ASSIGN_ADD;
+                    } else if (matchChar('+')) {
+                        return Token.INC;
+                    } else {
+                        return Token.ADD;
+                    }
 
-            case '-':
-                if (matchChar('=')) {
-                    c = Token.ASSIGN_SUB;
-                } else if (matchChar('-')) {
-                    if (!dirtyLine) {
-                        // treat HTML end-comment after possible whitespace
-                        // after line start as comment-until-eol
-                        if (matchChar('>')) {
-                            markCommentStart("--");
-                            skipLine();
-                            commentType = Token.CommentType.HTML;
-                            return Token.COMMENT;
+                case '-':
+                    if (matchChar('=')) {
+                        c = Token.ASSIGN_SUB;
+                    } else if (matchChar('-')) {
+                        if (!dirtyLine) {
+                            // treat HTML end-comment after possible whitespace
+                            // after line start as comment-until-eol
+                            if (matchChar('>')) {
+                                markCommentStart("--");
+                                skipLine();
+                                commentType = Token.CommentType.HTML;
+                                return Token.COMMENT;
+                            }
                         }
+                        c = Token.DEC;
+                    } else {
+                        c = Token.SUB;
+                    }
+                    dirtyLine = true;
+                    return c;
+
+                case '`':
+                    return Token.TEMPLATE_LITERAL;
+
+                default:
+                    parser.addError("msg.illegal.character", c);
+                    return Token.ERROR;
+            }
+        }
+    }
+
+    /*
+     * Helper to read the next digits according to the base
+     * and ignore the number separator if there is one.
+     */
+    private int readDigits(int base, int c) throws IOException {
+        if (isDigit(base, c)) {
+            addToString(c);
+
+            c = getChar();
+            if (c == EOF_CHAR) {
+                return EOF_CHAR;
+            }
+
+            while (true) {
+                if (c == NUMERIC_SEPARATOR) {
+                    // we do no peek here, we are optimistic for performance
+                    // reasons and because peekChar() only does an getChar/ungetChar.
+                    c = getChar();
+                    // if the line ends after the separator we have
+                    // to report this as an error
+                    if (c == '\n' || c == EOF_CHAR) {
+                        return REPORT_NUMBER_FORMAT_ERROR;
+                    }
+
+                    if (!isDigit(base, c)) {
+                        // bad luck we have to roll back
+                        ungetChar(c);
+                        return NUMERIC_SEPARATOR;
+                    }
+                    addToString(NUMERIC_SEPARATOR);
+                } else if (isDigit(base, c)) {
+                    addToString(c);
+                    c = getChar();
+                    if (c == EOF_CHAR) {
+                        return EOF_CHAR;
                     }
-                    c = Token.DEC;
                 } else {
-                    c = Token.SUB;
+                    return c;
                 }
-                dirtyLine = true;
-                return c;
-
-            default:
-                parser.addError("msg.illegal.character");
-                return Token.ERROR;
             }
         }
+        return c;
     }
 
-    private static boolean isAlpha(int c)
-    {
+    private static boolean isAlpha(int c) {
         // Use 'Z' < 'a'
         if (c <= 'Z') {
             return 'A' <= c;
-        } else {
-            return 'a' <= c && c <= 'z';
         }
+        return 'a' <= c && c <= 'z';
+    }
+
+    private static boolean isDigit(int base, int c) {
+        return (base == 10 && isDigit(c))
+                || (base == 16 && isHexDigit(c))
+                || (base == 8 && isOctalDigit(c))
+                || (base == 2 && isDualDigit(c));
+    }
+
+    private static boolean isDualDigit(int c) {
+        return '0' == c || c == '1';
+    }
+
+    private static boolean isOctalDigit(int c) {
+        return '0' <= c && c <= '7';
     }
 
-    static boolean isDigit(int c)
-    {
+    private static boolean isDigit(int c) {
         return '0' <= c && c <= '9';
     }
 
+    private static boolean isHexDigit(int c) {
+        return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');
+    }
+
     /* As defined in ECMA.  jsscan.c uses C isspace() (which allows
      * \v, I think.)  note that code in getChar() implicitly accepts
      * '\r' == \u000D as well.
      */
-    static boolean isJSSpace(int c)
-    {
+    private static boolean isJSSpace(int c) {
         if (c <= 127) {
             return c == 0x20 || c == 0x9 || c == 0xC || c == 0xB;
-        } else {
-            return c == 0xA0 || c == BYTE_ORDER_MARK
-                || Character.getType((char)c) == Character.SPACE_SEPARATOR;
         }
+        return c == 0xA0
+                || c == BYTE_ORDER_MARK
+                || Character.getType((char) c) == Character.SPACE_SEPARATOR;
     }
 
-    private static boolean isJSFormatChar(int c)
-    {
-        return c > 127 && Character.getType((char)c) == Character.FORMAT;
+    private static boolean isJSFormatChar(int c) {
+        return c > 127 && Character.getType((char) c) == Character.FORMAT;
     }
 
-    /**
-     * Parser calls the method when it gets / or /= in literal context.
-     */
-    void readRegExp(int startToken)
-        throws IOException
-    {
+    /** Parser calls the method when it gets / or /= in literal context. */
+    void readRegExp(int startToken) throws IOException {
         int start = tokenBeg;
         stringBufferTop = 0;
         if (startToken == Token.ASSIGN_DIV) {
@@ -1076,6 +1375,12 @@
             addToString('=');
         } else {
             if (startToken != Token.DIV) Kit.codeBug();
+            if (peekChar() == '*') {
+                tokenEnd = cursor - 1;
+                this.string = new String(stringBuffer, 0, stringBufferTop);
+                parser.reportError("msg.unterminated.re.lit");
+                return;
+            }
         }
 
         boolean inCharSet = false; // true if inside a '['..']' pair
@@ -1091,6 +1396,13 @@
             if (c == '\\') {
                 addToString(c);
                 c = getChar();
+                if (c == '\n' || c == EOF_CHAR) {
+                    ungetChar(c);
+                    tokenEnd = cursor - 1;
+                    this.string = new String(stringBuffer, 0, stringBufferTop);
+                    parser.reportError("msg.unterminated.re.lit");
+                    return;
+                }
             } else if (c == '[') {
                 inCharSet = true;
             } else if (c == ']') {
@@ -1101,26 +1413,21 @@
         int reEnd = stringBufferTop;
 
         while (true) {
-            if (matchChar('g'))
-                addToString('g');
-            else if (matchChar('i'))
-                addToString('i');
-            else if (matchChar('m'))
-                addToString('m');
-            else if (matchChar('y'))  // FireFox 3
-                addToString('y');
-            else
-                break;
+            if (matchChar('g')) addToString('g');
+            else if (matchChar('i')) addToString('i');
+            else if (matchChar('m')) addToString('m');
+            else if (matchChar('y')) // FireFox 3
+            addToString('y');
+            else break;
         }
-        tokenEnd = start + stringBufferTop + 2;  // include slashes
+        tokenEnd = start + stringBufferTop + 2; // include slashes
 
         if (isAlpha(peekChar())) {
             parser.reportError("msg.invalid.re.flag");
         }
 
         this.string = new String(stringBuffer, 0, reEnd);
-        this.regExpFlags = new String(stringBuffer, reEnd,
-                                      stringBufferTop - reEnd);
+        this.regExpFlags = new String(stringBuffer, reEnd, stringBufferTop - reEnd);
     }
 
     String readAndClearRegExpFlags() {
@@ -1129,67 +1436,331 @@
         return flags;
     }
 
-    boolean isXMLAttribute()
-    {
+    private StringBuilder rawString = new StringBuilder();
+
+    String getRawString() {
+        if (rawString.length() == 0) {
+            return "";
+        }
+        return rawString.toString();
+    }
+
+    private int getTemplateLiteralChar() throws IOException {
+        /*
+         * In Template Literals <CR><LF> and <CR> are normalized to <LF>
+         *
+         * Line and Paragraph separators (<LS> & <PS>) need to be included in the template strings as is
+         */
+        int c = getCharIgnoreLineEnd(false);
+
+        if (c == '\n') {
+            switch (lineEndChar) {
+                case '\r':
+                    // check whether dealing with a <CR><LF> sequence
+                    if (charAt(cursor) == '\n') {
+                        // consume the <LF> that followed the <CR>
+                        getCharIgnoreLineEnd(false);
+                    }
+                    break;
+                case 0x2028: // <LS>
+                case 0x2029: // <PS>
+                    // Line/Paragraph separators need to be included as is
+                    c = lineEndChar;
+                    break;
+                default:
+                    break;
+            }
+
+            // Adjust numbers: duplicates the logic in getChar thats skipped as getChar is called
+            // via getCharIgnoreLineEnd
+            lineEndChar = -1;
+            lineStart = sourceCursor - 1;
+            lineno++;
+        }
+
+        rawString.append((char) c);
+        return c;
+    }
+
+    private void ungetTemplateLiteralChar(int c) {
+        ungetCharIgnoreLineEnd(c);
+        rawString.setLength(rawString.length() - 1);
+    }
+
+    private boolean matchTemplateLiteralChar(int test) throws IOException {
+        int c = getTemplateLiteralChar();
+        if (c == test) {
+            return true;
+        }
+        ungetTemplateLiteralChar(c);
+        return false;
+    }
+
+    private int peekTemplateLiteralChar() throws IOException {
+        int c = getTemplateLiteralChar();
+        ungetTemplateLiteralChar(c);
+        return c;
+    }
+
+    int readTemplateLiteral(boolean isTaggedLiteral) throws IOException {
+        rawString.setLength(0);
+        stringBufferTop = 0;
+        boolean hasInvalidEscapeSequences = false;
+
+        while (true) {
+            int c = getTemplateLiteralChar();
+            switch (c) {
+                case EOF_CHAR:
+                    this.string = hasInvalidEscapeSequences ? null : getStringFromBuffer();
+                    tokenEnd = cursor - 1; // restore tokenEnd
+                    parser.reportError("msg.unexpected.eof");
+                    return Token.ERROR;
+                case '`':
+                    rawString.setLength(rawString.length() - 1); // don't include "`"
+                    this.string = hasInvalidEscapeSequences ? null : getStringFromBuffer();
+                    return Token.TEMPLATE_LITERAL;
+                case '$':
+                    if (matchTemplateLiteralChar('{')) {
+                        rawString.setLength(rawString.length() - 2); // don't include "${"
+                        this.string = hasInvalidEscapeSequences ? null : getStringFromBuffer();
+                        this.tokenEnd = cursor - 1; // don't include "{"
+                        return Token.TEMPLATE_LITERAL_SUBST;
+                    } else {
+                        addToString(c);
+                        break;
+                    }
+                case '\\':
+                    // LineContinuation ::
+                    //   \ LineTerminatorSequence
+                    // EscapeSequence ::
+                    //   CharacterEscapeSequence
+                    //   0 [LA not DecimalDigit]
+                    //   HexEscapeSequence
+                    //   UnicodeEscapeSequence
+                    // CharacterEscapeSequence ::
+                    //   SingleEscapeCharacter
+                    //   NonEscapeCharacter
+                    // SingleEscapeCharacter ::
+                    //   ' "  \  b f n r t v
+                    // NonEscapeCharacter ::
+                    //   SourceCharacter but not one of EscapeCharacter or LineTerminator
+                    // EscapeCharacter ::
+                    //   SingleEscapeCharacter
+                    //   DecimalDigit
+                    //   x
+                    //   u
+                    c = getTemplateLiteralChar();
+                    switch (c) {
+                        case '\n':
+                        case '\u2028':
+                        case '\u2029':
+                            continue;
+                        case '\'':
+                        case '"':
+                        case '\\':
+                            // use as-is
+                            break;
+                        case 'b':
+                            c = '\b';
+                            break;
+                        case 'f':
+                            c = '\f';
+                            break;
+                        case 'n':
+                            c = '\n';
+                            break;
+                        case 'r':
+                            c = '\r';
+                            break;
+                        case 't':
+                            c = '\t';
+                            break;
+                        case 'v':
+                            c = 0xb;
+                            break;
+                        case 'x':
+                            {
+                                int escapeVal = 0;
+                                for (int i = 0; i < 2; i++) {
+                                    if (peekTemplateLiteralChar() == '`') {
+                                        escapeVal = -1;
+                                        break;
+                                    }
+                                    escapeVal =
+                                            Kit.xDigitToInt(getTemplateLiteralChar(), escapeVal);
+                                }
+
+                                if (escapeVal < 0) {
+                                    if (isTaggedLiteral) {
+                                        hasInvalidEscapeSequences = true;
+                                        continue;
+                                    } else {
+                                        parser.reportError("msg.syntax");
+                                        return Token.ERROR;
+                                    }
+                                }
+                                c = escapeVal;
+                                break;
+                            }
+                        case 'u':
+                            {
+                                int escapeVal = 0;
+
+                                if (matchTemplateLiteralChar('{')) {
+                                    for (; ; ) {
+                                        if (peekTemplateLiteralChar() == '`') {
+                                            escapeVal = -1;
+                                            break;
+                                        }
+                                        c = getTemplateLiteralChar();
+
+                                        if (c == '}') {
+                                            break;
+                                        }
+                                        escapeVal = Kit.xDigitToInt(c, escapeVal);
+                                    }
+
+                                    if (escapeVal < 0 || escapeVal > 0x10FFFF) {
+                                        if (isTaggedLiteral) {
+                                            hasInvalidEscapeSequences = true;
+                                            continue;
+                                        } else {
+                                            parser.reportError("msg.syntax");
+                                            return Token.ERROR;
+                                        }
+                                    }
+
+                                    if (escapeVal > 0xFFFF) {
+                                        addToString(Character.highSurrogate(escapeVal));
+                                        addToString(Character.lowSurrogate(escapeVal));
+                                        continue;
+                                    }
+                                    c = escapeVal;
+                                    break;
+                                }
+
+                                for (int i = 0; i < 4; i++) {
+                                    if (peekTemplateLiteralChar() == '`') {
+                                        escapeVal = -1;
+                                        break;
+                                    }
+                                    escapeVal =
+                                            Kit.xDigitToInt(getTemplateLiteralChar(), escapeVal);
+                                }
+
+                                if (escapeVal < 0) {
+                                    if (isTaggedLiteral) {
+                                        hasInvalidEscapeSequences = true;
+                                        continue;
+                                    } else {
+                                        parser.reportError("msg.syntax");
+                                        return Token.ERROR;
+                                    }
+                                }
+                                c = escapeVal;
+                                break;
+                            }
+                        case '0':
+                            {
+                                int d = peekTemplateLiteralChar();
+                                if (d >= '0' && d <= '9') {
+                                    if (isTaggedLiteral) {
+                                        hasInvalidEscapeSequences = true;
+                                        continue;
+                                    } else {
+                                        parser.reportError("msg.syntax");
+                                        return Token.ERROR;
+                                    }
+                                }
+                                c = 0x00;
+                                break;
+                            }
+                        case '1':
+                        case '2':
+                        case '3':
+                        case '4':
+                        case '5':
+                        case '6':
+                        case '7':
+                        case '8':
+                        case '9':
+                            if (isTaggedLiteral) {
+                                hasInvalidEscapeSequences = true;
+                                continue;
+                            } else {
+                                parser.reportError("msg.syntax");
+                                return Token.ERROR;
+                            }
+                        default:
+                            // use as-is
+                            break;
+                    }
+                    addToString(c);
+                    break;
+                default:
+                    addToString(c);
+                    break;
+            }
+        }
+    }
+
+    boolean isXMLAttribute() {
         return xmlIsAttribute;
     }
 
-    int getFirstXMLToken() throws IOException
-    {
+    int getFirstXMLToken() throws IOException {
         xmlOpenTagsCount = 0;
         xmlIsAttribute = false;
         xmlIsTagContent = false;
-        if (!canUngetChar())
-            return Token.ERROR;
+        if (!canUngetChar()) return Token.ERROR;
         ungetChar('<');
         return getNextXMLToken();
     }
 
-    int getNextXMLToken() throws IOException
-    {
+    int getNextXMLToken() throws IOException {
         tokenBeg = cursor;
         stringBufferTop = 0; // remember the XML
 
         for (int c = getChar(); c != EOF_CHAR; c = getChar()) {
             if (xmlIsTagContent) {
                 switch (c) {
-                case '>':
-                    addToString(c);
-                    xmlIsTagContent = false;
-                    xmlIsAttribute = false;
-                    break;
-                case '/':
-                    addToString(c);
-                    if (peekChar() == '>') {
-                        c = getChar();
+                    case '>':
                         addToString(c);
                         xmlIsTagContent = false;
-                        xmlOpenTagsCount--;
-                    }
-                    break;
-                case '{':
-                    ungetChar(c);
-                    this.string = getStringFromBuffer();
-                    return Token.XML;
-                case '\'':
-                case '"':
-                    addToString(c);
-                    if (!readQuotedString(c)) return Token.ERROR;
-                    break;
-                case '=':
-                    addToString(c);
-                    xmlIsAttribute = true;
-                    break;
-                case ' ':
-                case '\t':
-                case '\r':
-                case '\n':
-                    addToString(c);
-                    break;
-                default:
-                    addToString(c);
-                    xmlIsAttribute = false;
-                    break;
+                        xmlIsAttribute = false;
+                        break;
+                    case '/':
+                        addToString(c);
+                        if (peekChar() == '>') {
+                            c = getChar();
+                            addToString(c);
+                            xmlIsTagContent = false;
+                            xmlOpenTagsCount--;
+                        }
+                        break;
+                    case '{':
+                        ungetChar(c);
+                        this.string = getStringFromBuffer();
+                        return Token.XML;
+                    case '\'':
+                    case '"':
+                        addToString(c);
+                        if (!readQuotedString(c)) return Token.ERROR;
+                        break;
+                    case '=':
+                        addToString(c);
+                        xmlIsAttribute = true;
+                        break;
+                    case ' ':
+                    case '\t':
+                    case '\r':
+                    case '\n':
+                        addToString(c);
+                        break;
+                    default:
+                        addToString(c);
+                        xmlIsAttribute = false;
+                        break;
                 }
 
                 if (!xmlIsTagContent && xmlOpenTagsCount == 0) {
@@ -1198,94 +1769,93 @@
                 }
             } else {
                 switch (c) {
-                case '<':
-                    addToString(c);
-                    c = peekChar();
-                    switch (c) {
-                    case '!':
-                        c = getChar(); // Skip !
+                    case '<':
                         addToString(c);
                         c = peekChar();
                         switch (c) {
-                        case '-':
-                            c = getChar(); // Skip -
-                            addToString(c);
-                            c = getChar();
-                            if (c == '-') {
+                            case '!':
+                                c = getChar(); // Skip !
                                 addToString(c);
-                                if(!readXmlComment()) return Token.ERROR;
-                            } else {
-                                // throw away the string in progress
-                                stringBufferTop = 0;
-                                this.string = null;
-                                parser.addError("msg.XML.bad.form");
-                                return Token.ERROR;
-                            }
-                            break;
-                        case '[':
-                            c = getChar(); // Skip [
-                            addToString(c);
-                            if (getChar() == 'C' &&
-                                getChar() == 'D' &&
-                                getChar() == 'A' &&
-                                getChar() == 'T' &&
-                                getChar() == 'A' &&
-                                getChar() == '[')
-                            {
-                                addToString('C');
-                                addToString('D');
-                                addToString('A');
-                                addToString('T');
-                                addToString('A');
-                                addToString('[');
-                                if (!readCDATA()) return Token.ERROR;
-
-                            } else {
-                                // throw away the string in progress
-                                stringBufferTop = 0;
-                                this.string = null;
-                                parser.addError("msg.XML.bad.form");
-                                return Token.ERROR;
-                            }
-                            break;
-                        default:
-                            if(!readEntity()) return Token.ERROR;
-                            break;
-                        }
-                        break;
-                    case '?':
-                        c = getChar(); // Skip ?
-                        addToString(c);
-                        if (!readPI()) return Token.ERROR;
-                        break;
-                    case '/':
-                        // End tag
-                        c = getChar(); // Skip /
-                        addToString(c);
-                        if (xmlOpenTagsCount == 0) {
-                            // throw away the string in progress
-                            stringBufferTop = 0;
-                            this.string = null;
-                            parser.addError("msg.XML.bad.form");
-                            return Token.ERROR;
+                                c = peekChar();
+                                switch (c) {
+                                    case '-':
+                                        c = getChar(); // Skip -
+                                        addToString(c);
+                                        c = getChar();
+                                        if (c == '-') {
+                                            addToString(c);
+                                            if (!readXmlComment()) return Token.ERROR;
+                                        } else {
+                                            // throw away the string in progress
+                                            stringBufferTop = 0;
+                                            this.string = null;
+                                            parser.addError("msg.XML.bad.form");
+                                            return Token.ERROR;
+                                        }
+                                        break;
+                                    case '[':
+                                        c = getChar(); // Skip [
+                                        addToString(c);
+                                        if (getChar() == 'C'
+                                                && getChar() == 'D'
+                                                && getChar() == 'A'
+                                                && getChar() == 'T'
+                                                && getChar() == 'A'
+                                                && getChar() == '[') {
+                                            addToString('C');
+                                            addToString('D');
+                                            addToString('A');
+                                            addToString('T');
+                                            addToString('A');
+                                            addToString('[');
+                                            if (!readCDATA()) return Token.ERROR;
+
+                                        } else {
+                                            // throw away the string in progress
+                                            stringBufferTop = 0;
+                                            this.string = null;
+                                            parser.addError("msg.XML.bad.form");
+                                            return Token.ERROR;
+                                        }
+                                        break;
+                                    default:
+                                        if (!readEntity()) return Token.ERROR;
+                                        break;
+                                }
+                                break;
+                            case '?':
+                                c = getChar(); // Skip ?
+                                addToString(c);
+                                if (!readPI()) return Token.ERROR;
+                                break;
+                            case '/':
+                                // End tag
+                                c = getChar(); // Skip /
+                                addToString(c);
+                                if (xmlOpenTagsCount == 0) {
+                                    // throw away the string in progress
+                                    stringBufferTop = 0;
+                                    this.string = null;
+                                    parser.addError("msg.XML.bad.form");
+                                    return Token.ERROR;
+                                }
+                                xmlIsTagContent = true;
+                                xmlOpenTagsCount--;
+                                break;
+                            default:
+                                // Start tag
+                                xmlIsTagContent = true;
+                                xmlOpenTagsCount++;
+                                break;
                         }
-                        xmlIsTagContent = true;
-                        xmlOpenTagsCount--;
                         break;
+                    case '{':
+                        ungetChar(c);
+                        this.string = getStringFromBuffer();
+                        return Token.XML;
                     default:
-                        // Start tag
-                        xmlIsTagContent = true;
-                        xmlOpenTagsCount++;
+                        addToString(c);
                         break;
-                    }
-                    break;
-                case '{':
-                    ungetChar(c);
-                    this.string = getStringFromBuffer();
-                    return Token.XML;
-                default:
-                    addToString(c);
-                    break;
                 }
             }
         }
@@ -1297,11 +1867,8 @@
         return Token.ERROR;
     }
 
-    /**
-     *
-     */
-    private boolean readQuotedString(int quote) throws IOException
-    {
+    /** */
+    private boolean readQuotedString(int quote) throws IOException {
         for (int c = getChar(); c != EOF_CHAR; c = getChar()) {
             addToString(c);
             if (c == quote) return true;
@@ -1313,12 +1880,9 @@
         return false;
     }
 
-    /**
-     *
-     */
-    private boolean readXmlComment() throws IOException
-    {
-        for (int c = getChar(); c != EOF_CHAR;) {
+    /** */
+    private boolean readXmlComment() throws IOException {
+        for (int c = getChar(); c != EOF_CHAR; ) {
             addToString(c);
             if (c == '-' && peekChar() == '-') {
                 c = getChar();
@@ -1327,9 +1891,8 @@
                     c = getChar(); // Skip >
                     addToString(c);
                     return true;
-                } else {
-                    continue;
                 }
+                continue;
             }
             c = getChar();
         }
@@ -1340,12 +1903,9 @@
         return false;
     }
 
-    /**
-     *
-     */
-    private boolean readCDATA() throws IOException
-    {
-        for (int c = getChar(); c != EOF_CHAR;) {
+    /** */
+    private boolean readCDATA() throws IOException {
+        for (int c = getChar(); c != EOF_CHAR; ) {
             addToString(c);
             if (c == ']' && peekChar() == ']') {
                 c = getChar();
@@ -1354,9 +1914,8 @@
                     c = getChar(); // Skip >
                     addToString(c);
                     return true;
-                } else {
-                    continue;
                 }
+                continue;
             }
             c = getChar();
         }
@@ -1367,22 +1926,19 @@
         return false;
     }
 
-    /**
-     *
-     */
-    private boolean readEntity() throws IOException
-    {
+    /** */
+    private boolean readEntity() throws IOException {
         int declTags = 1;
         for (int c = getChar(); c != EOF_CHAR; c = getChar()) {
             addToString(c);
             switch (c) {
-            case '<':
-                declTags++;
-                break;
-            case '>':
-                declTags--;
-                if (declTags == 0) return true;
-                break;
+                case '<':
+                    declTags++;
+                    break;
+                case '>':
+                    declTags--;
+                    if (declTags == 0) return true;
+                    break;
             }
         }
 
@@ -1392,11 +1948,8 @@
         return false;
     }
 
-    /**
-     *
-     */
-    private boolean readPI() throws IOException
-    {
+    /** */
+    private boolean readPI() throws IOException {
         for (int c = getChar(); c != EOF_CHAR; c = getChar()) {
             addToString(c);
             if (c == '?' && peekChar() == '>') {
@@ -1412,21 +1965,19 @@
         return false;
     }
 
-    private String getStringFromBuffer()
-    {
+    private String getStringFromBuffer() {
         tokenEnd = cursor;
         return new String(stringBuffer, 0, stringBufferTop);
     }
 
-    private void addToString(int c)
-    {
+    private void addToString(int c) {
         int N = stringBufferTop;
         if (N == stringBuffer.length) {
             char[] tmp = new char[stringBuffer.length * 2];
             System.arraycopy(stringBuffer, 0, tmp, 0, N);
             stringBuffer = tmp;
         }
-        stringBuffer[N] = (char)c;
+        stringBuffer[N] = (char) c;
         stringBufferTop = N + 1;
     }
 
@@ -1434,47 +1985,44 @@
         return ungetCursor == 0 || ungetBuffer[ungetCursor - 1] != '\n';
     }
 
-    private void ungetChar(int c)
-    {
+    private void ungetChar(int c) {
         // can not unread past across line boundary
-        if (ungetCursor != 0 && ungetBuffer[ungetCursor - 1] == '\n')
-            Kit.codeBug();
+        if (ungetCursor != 0 && ungetBuffer[ungetCursor - 1] == '\n') Kit.codeBug();
         ungetBuffer[ungetCursor++] = c;
         cursor--;
     }
 
-    private boolean matchChar(int test) throws IOException
-    {
+    private boolean matchChar(int test) throws IOException {
         int c = getCharIgnoreLineEnd();
         if (c == test) {
             tokenEnd = cursor;
             return true;
-        } else {
-            ungetCharIgnoreLineEnd(c);
-            return false;
         }
+        ungetCharIgnoreLineEnd(c);
+        return false;
     }
 
-    private int peekChar() throws IOException
-    {
+    private int peekChar() throws IOException {
         int c = getChar();
         ungetChar(c);
         return c;
     }
 
-    private int getChar() throws IOException
-    {
-        return getChar(true);
+    private int getChar() throws IOException {
+        return getChar(true, false);
+    }
+
+    private int getChar(boolean skipFormattingChars) throws IOException {
+        return getChar(skipFormattingChars, false);
     }
 
-    private int getChar(boolean skipFormattingChars) throws IOException
-    {
+    private int getChar(boolean skipFormattingChars, boolean ignoreLineEnd) throws IOException {
         if (ungetCursor != 0) {
             cursor++;
             return ungetBuffer[--ungetCursor];
         }
 
-        for(;;) {
+        for (; ; ) {
             int c;
             if (sourceString != null) {
                 if (sourceCursor == sourceEnd) {
@@ -1494,7 +2042,7 @@
                 c = sourceBuffer[sourceCursor++];
             }
 
-            if (lineEndChar >= 0) {
+            if (!ignoreLineEnd && lineEndChar >= 0) {
                 if (lineEndChar == '\r' && c == '\n') {
                     lineEndChar = '\n';
                     continue;
@@ -1523,74 +2071,33 @@
         }
     }
 
-    private int getCharIgnoreLineEnd() throws IOException
-    {
-        if (ungetCursor != 0) {
-            cursor++;
-            return ungetBuffer[--ungetCursor];
-        }
-
-        for(;;) {
-            int c;
-            if (sourceString != null) {
-                if (sourceCursor == sourceEnd) {
-                    hitEOF = true;
-                    return EOF_CHAR;
-                }
-                cursor++;
-                c = sourceString.charAt(sourceCursor++);
-            } else {
-                if (sourceCursor == sourceEnd) {
-                    if (!fillSourceBuffer()) {
-                        hitEOF = true;
-                        return EOF_CHAR;
-                    }
-                }
-                cursor++;
-                c = sourceBuffer[sourceCursor++];
-            }
+    private int getCharIgnoreLineEnd() throws IOException {
+        return getChar(true, true);
+    }
 
-            if (c <= 127) {
-                if (c == '\n' || c == '\r') {
-                    lineEndChar = c;
-                    c = '\n';
-                }
-            } else {
-                if (c == BYTE_ORDER_MARK) return c; // BOM is considered whitespace
-                if (isJSFormatChar(c)) {
-                    continue;
-                }
-                if (ScriptRuntime.isJSLineTerminator(c)) {
-                    lineEndChar = c;
-                    c = '\n';
-                }
-            }
-            return c;
-        }
+    private int getCharIgnoreLineEnd(boolean skipFormattingChars) throws IOException {
+        return getChar(skipFormattingChars, true);
     }
 
-    private void ungetCharIgnoreLineEnd(int c)
-    {
+    private void ungetCharIgnoreLineEnd(int c) {
         ungetBuffer[ungetCursor++] = c;
         cursor--;
     }
 
-    private void skipLine() throws IOException
-    {
+    private void skipLine() throws IOException {
         // skip to end of line
         int c;
-        while ((c = getChar()) != EOF_CHAR && c != '\n') { }
+        while ((c = getChar()) != EOF_CHAR && c != '\n') {}
         ungetChar(c);
         tokenEnd = cursor;
     }
 
-    /**
-     * Returns the offset into the current line.
-     */
-    final int getOffset()
-    {
+    /** Returns the offset into the current line. */
+    final int getOffset() {
         int n = sourceCursor - lineStart;
-        if (lineEndChar >= 0) { --n; }
+        if (lineEndChar >= 0) {
+            --n;
+        }
         return n;
     }
 
@@ -1603,30 +2110,30 @@
                 return EOF_CHAR;
             }
             return sourceString.charAt(index);
-        } else {
-            if (index >= sourceEnd) {
-                int oldSourceCursor = sourceCursor;
-                try {
-                    if (!fillSourceBuffer()) { return EOF_CHAR; }
-                } catch (IOException ioe) {
-                    // ignore it, we're already displaying an error...
+        }
+        if (index >= sourceEnd) {
+            int oldSourceCursor = sourceCursor;
+            try {
+                if (!fillSourceBuffer()) {
                     return EOF_CHAR;
                 }
-                // index recalculuation as fillSourceBuffer can move saved
-                // line buffer and change sourceCursor
-                index -= (oldSourceCursor - sourceCursor);
+            } catch (IOException ioe) {
+                // ignore it, we're already displaying an error...
+                return EOF_CHAR;
             }
-            return sourceBuffer[index];
+            // index recalculuation as fillSourceBuffer can move saved
+            // line buffer and change sourceCursor
+            index -= (oldSourceCursor - sourceCursor);
         }
+        return sourceBuffer[index];
     }
 
     private final String substring(int beginIndex, int endIndex) {
         if (sourceString != null) {
             return sourceString.substring(beginIndex, endIndex);
-        } else {
-            int count = endIndex - beginIndex;
-            return new String(sourceBuffer, beginIndex, count);
         }
+        int count = endIndex - beginIndex;
+        return new String(sourceBuffer, beginIndex, count);
     }
 
     final String getLine() {
@@ -1640,7 +2147,7 @@
         } else {
             // Read until the end of line
             int lineLength = lineEnd - lineStart;
-            for (;; ++lineLength) {
+            for (; ; ++lineLength) {
                 int c = charAt(lineStart + lineLength);
                 if (c == EOF_CHAR || ScriptRuntime.isJSLineTerminator(c)) {
                     break;
@@ -1688,18 +2195,15 @@
         linep[1] = offset;
         if (lines == 0) {
             return getLine();
-        } else {
-            return substring(start, end);
         }
+        return substring(start, end);
     }
 
-    private boolean fillSourceBuffer() throws IOException
-    {
+    private boolean fillSourceBuffer() throws IOException {
         if (sourceString != null) Kit.codeBug();
         if (sourceEnd == sourceBuffer.length) {
             if (lineStart != 0 && !isMarkingComment()) {
-                System.arraycopy(sourceBuffer, lineStart, sourceBuffer, 0,
-                                 sourceEnd - lineStart);
+                System.arraycopy(sourceBuffer, lineStart, sourceBuffer, 0, sourceEnd - lineStart);
                 sourceEnd -= lineStart;
                 sourceCursor -= lineStart;
                 lineStart = 0;
@@ -1709,8 +2213,7 @@
                 sourceBuffer = tmp;
             }
         }
-        int n = sourceReader.read(sourceBuffer, sourceEnd,
-                                  sourceBuffer.length - sourceEnd);
+        int n = sourceReader.read(sourceBuffer, sourceEnd, sourceBuffer.length - sourceEnd);
         if (n < 0) {
             return false;
         }
@@ -1718,36 +2221,29 @@
         return true;
     }
 
-    /**
-     * Return the current position of the scanner cursor.
-     */
+    /** Return the current position of the scanner cursor. */
     public int getCursor() {
         return cursor;
     }
 
-    /**
-     * Return the absolute source offset of the last scanned token.
-     */
+    /** Return the absolute source offset of the last scanned token. */
     public int getTokenBeg() {
         return tokenBeg;
     }
 
-    /**
-     * Return the absolute source end-offset of the last scanned token.
-     */
+    /** Return the absolute source end-offset of the last scanned token. */
     public int getTokenEnd() {
         return tokenEnd;
     }
 
-    /**
-     * Return tokenEnd - tokenBeg
-     */
+    /** Return tokenEnd - tokenBeg */
     public int getTokenLength() {
         return tokenEnd - tokenBeg;
     }
 
     /**
      * Return the type of the last scanned comment.
+     *
      * @return type of last scanned comment, or 0 if none have been scanned.
      */
     public Token.CommentType getCommentType() {
@@ -1769,31 +2265,28 @@
         return commentCursor != -1;
     }
 
-     final String getAndResetCurrentComment() {
+    final String getAndResetCurrentComment() {
         if (sourceString != null) {
             if (isMarkingComment()) Kit.codeBug();
             return sourceString.substring(tokenBeg, tokenEnd);
-        } else {
-            if (!isMarkingComment()) Kit.codeBug();
-            StringBuilder comment = new StringBuilder(commentPrefix);
-            comment.append(sourceBuffer, commentCursor,
-                getTokenLength() - commentPrefix.length());
-            commentCursor = -1;
-            return comment.toString();
         }
-    }
-
-    private String convertLastCharToHex(String str) {
-      int lastIndex = str.length()-1;
-      StringBuffer buf = new StringBuffer(
-          str.substring(0, lastIndex));
-      buf.append("\\u");
-      String hexCode = Integer.toHexString(str.charAt(lastIndex));
-      for (int i = 0; i < 4-hexCode.length(); ++i) {
-        buf.append('0');
-      }
-      buf.append(hexCode);
-      return buf.toString();
+        if (!isMarkingComment()) Kit.codeBug();
+        StringBuilder comment = new StringBuilder(commentPrefix);
+        comment.append(sourceBuffer, commentCursor, getTokenLength() - commentPrefix.length());
+        commentCursor = -1;
+        return comment.toString();
+    }
+
+    private static String convertLastCharToHex(String str) {
+        int lastIndex = str.length() - 1;
+        StringBuilder buf = new StringBuilder(str.substring(0, lastIndex));
+        buf.append("\\u");
+        String hexCode = Integer.toHexString(str.charAt(lastIndex));
+        for (int i = 0; i < 4 - hexCode.length(); ++i) {
+            buf.append('0');
+        }
+        buf.append(hexCode);
+        return buf.toString();
     }
 
     // stuff other than whitespace since start of line
@@ -1807,6 +2300,7 @@
     // code.
     private String string = "";
     private double number;
+    private BigInteger bigInt;
     private boolean isBinary;
     private boolean isOldOctal;
     private boolean isOctal;
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/TopLevel.java rhino-1.7.14/src/org/mozilla/javascript/TopLevel.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/TopLevel.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/TopLevel.java	2022-01-06 22:57:21.000000000 +0100
@@ -4,44 +4,36 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
- package org.mozilla.javascript;
+package org.mozilla.javascript;
 
 import java.util.EnumMap;
 
 /**
- * A top-level scope object that provides special means to cache and preserve
- * the initial values of the built-in constructor properties for better
- * ECMAScript compliance.
+ * A top-level scope object that provides special means to cache and preserve the initial values of
+ * the built-in constructor properties for better ECMAScript compliance.
  *
- * <p>ECMA 262 requires that most constructors used internally construct
- * objects with the original prototype object as value of their [[Prototype]]
- * internal property. Since built-in global constructors are defined as
- * writable and deletable, this means they should be cached to protect against
- * redefinition at runtime.</p>
+ * <p>ECMA 262 requires that most constructors used internally construct objects with the original
+ * prototype object as value of their [[Prototype]] internal property. Since built-in global
+ * constructors are defined as writable and deletable, this means they should be cached to protect
+ * against redefinition at runtime.
  *
- * <p>In order to implement this efficiently, this class provides a mechanism
- * to access the original built-in global constructors and their prototypes
- * via numeric class-ids. To make use of this, the new
- * {@link ScriptRuntime#newBuiltinObject ScriptRuntime.newBuiltinObject} and
- * {@link ScriptRuntime#setBuiltinProtoAndParent ScriptRuntime.setBuiltinProtoAndParent}
- * methods should be used to create and initialize objects of built-in classes
- * instead of their generic counterparts.</p>
+ * <p>In order to implement this efficiently, this class provides a mechanism to access the original
+ * built-in global constructors and their prototypes via numeric class-ids. To make use of this, the
+ * new {@link ScriptRuntime#newBuiltinObject ScriptRuntime.newBuiltinObject} and {@link
+ * ScriptRuntime#setBuiltinProtoAndParent ScriptRuntime.setBuiltinProtoAndParent} methods should be
+ * used to create and initialize objects of built-in classes instead of their generic counterparts.
  *
- * <p>Calling {@link org.mozilla.javascript.Context#initStandardObjects()}
- * with an instance of this class as argument will automatically cache
- * built-in classes after initialization. For other setups involving
- * top-level scopes that inherit global properties from their proptotypes
- * (e.g. with dynamic scopes) embeddings should explicitly call
- * {@link #cacheBuiltins()} to initialize the class cache for each top-level
- * scope.</p>
+ * <p>Calling {@link org.mozilla.javascript.Context#initStandardObjects()} with an instance of this
+ * class as argument will automatically cache built-in classes after initialization. For other
+ * setups involving top-level scopes that inherit global properties from their proptotypes (e.g.
+ * with dynamic scopes) embeddings should explicitly call {@link #cacheBuiltins(Scriptable,
+ * boolean)} to initialize the class cache for each top-level scope.
  */
 public class TopLevel extends IdScriptableObject {
 
-    static final long serialVersionUID = -4648046356662472260L;
+    private static final long serialVersionUID = -4648046356662472260L;
 
-    /**
-     * An enumeration of built-in ECMAScript objects.
-     */
+    /** An enumeration of built-in ECMAScript objects. */
     public enum Builtins {
         /** The built-in Object type. */
         Object,
@@ -60,12 +52,14 @@
         /** The built-in Error type. */
         Error,
         /** The built-in Symbol type. */
-        Symbol
+        Symbol,
+        /** The built-in GeneratorFunction type. */
+        GeneratorFunction,
+        /** The built-in BigInt type. */
+        BigInt
     }
 
-    /**
-     * An enumeration of built-in native errors. [ECMAScript 5 - 15.11.6]
-     */
+    /** An enumeration of built-in native errors. [ECMAScript 5 - 15.11.6] */
     enum NativeErrors {
         /** Basic Error */
         Error,
@@ -96,73 +90,88 @@
     }
 
     /**
-     * Cache the built-in ECMAScript objects to protect them against
-     * modifications by the script. This method is called automatically by
-     * {@link ScriptRuntime#initStandardObjects ScriptRuntime.initStandardObjects}
-     * if the scope argument is an instance of this class. It only has to be
-     * called by the embedding if a top-level scope is not initialized through
-     * <code>initStandardObjects()</code>.
+     * Cache the built-in ECMAScript objects to protect them against modifications by the script.
+     * This method is called automatically by {@link ScriptRuntime#initStandardObjects
+     * ScriptRuntime.initStandardObjects} if the scope argument is an instance of this class. It
+     * only has to be called by the embedding if a top-level scope is not initialized through <code>
+     * initStandardObjects()</code>.
      */
-    public void cacheBuiltins() {
+    public void cacheBuiltins(Scriptable scope, boolean sealed) {
         ctors = new EnumMap<Builtins, BaseFunction>(Builtins.class);
         for (Builtins builtin : Builtins.values()) {
             Object value = ScriptableObject.getProperty(this, builtin.name());
             if (value instanceof BaseFunction) {
-                ctors.put(builtin, (BaseFunction)value);
+                ctors.put(builtin, (BaseFunction) value);
+            } else if (builtin == Builtins.GeneratorFunction) {
+                // Handle weird situation of "GeneratorFunction" being a real constructor
+                // which is never registered in the top-level scope
+                ctors.put(
+                        builtin,
+                        (BaseFunction) BaseFunction.initAsGeneratorFunction(scope, sealed));
             }
         }
         errors = new EnumMap<NativeErrors, BaseFunction>(NativeErrors.class);
         for (NativeErrors error : NativeErrors.values()) {
             Object value = ScriptableObject.getProperty(this, error.name());
             if (value instanceof BaseFunction) {
-                errors.put(error, (BaseFunction)value);
+                errors.put(error, (BaseFunction) value);
             }
         }
     }
 
+    /** Clears the cache, this is necessary, when standard objects are reinitialized. */
+    void clearCache() {
+        ctors = null;
+        errors = null;
+    }
+
     /**
-     * Static helper method to get a built-in object constructor with the given
-     * <code>type</code> from the given <code>scope</code>. If the scope is not
-     * an instance of this class or does have a cache of built-ins,
-     * the constructor is looked up via normal property lookup.
+     * Static helper method to get a built-in object constructor with the given <code>type</code>
+     * from the given <code>scope</code>. If the scope is not an instance of this class or does have
+     * a cache of built-ins, the constructor is looked up via normal property lookup.
      *
      * @param cx the current Context
      * @param scope the top-level scope
      * @param type the built-in type
      * @return the built-in constructor
      */
-    public static Function getBuiltinCtor(Context cx,
-                                          Scriptable scope,
-                                          Builtins type) {
+    public static Function getBuiltinCtor(Context cx, Scriptable scope, Builtins type) {
         // must be called with top level scope
         assert scope.getParentScope() == null;
         if (scope instanceof TopLevel) {
-            Function result = ((TopLevel)scope).getBuiltinCtor(type);
+            Function result = ((TopLevel) scope).getBuiltinCtor(type);
             if (result != null) {
                 return result;
             }
         }
         // fall back to normal constructor lookup
-        return ScriptRuntime.getExistingCtor(cx, scope, type.name());
+        String typeName;
+        if (type == Builtins.GeneratorFunction) {
+            // GeneratorFunction isn't stored in scope with that name, but in case
+            // we end up falling back to this value then we have to
+            // look this up using a hidden name.
+            typeName = BaseFunction.GENERATOR_FUNCTION_CLASS;
+        } else {
+            typeName = type.name();
+        }
+        return ScriptRuntime.getExistingCtor(cx, scope, typeName);
     }
 
     /**
-     * Static helper method to get a native error constructor with the given
-     * <code>type</code> from the given <code>scope</code>. If the scope is not
-     * an instance of this class or does have a cache of native errors,
-     * the constructor is looked up via normal property lookup.
+     * Static helper method to get a native error constructor with the given <code>type</code> from
+     * the given <code>scope</code>. If the scope is not an instance of this class or does have a
+     * cache of native errors, the constructor is looked up via normal property lookup.
      *
      * @param cx the current Context
      * @param scope the top-level scope
      * @param type the native error type
      * @return the native error constructor
      */
-    static Function getNativeErrorCtor(Context cx, Scriptable scope,
-                                       NativeErrors type) {
+    static Function getNativeErrorCtor(Context cx, Scriptable scope, NativeErrors type) {
         // must be called with top level scope
         assert scope.getParentScope() == null;
         if (scope instanceof TopLevel) {
-            Function result = ((TopLevel)scope).getNativeErrorCtor(type);
+            Function result = ((TopLevel) scope).getNativeErrorCtor(type);
             if (result != null) {
                 return result;
             }
@@ -172,34 +181,41 @@
     }
 
     /**
-     * Static helper method to get a built-in object prototype with the given
-     * <code>type</code> from the given <code>scope</code>. If the scope is not
-     * an instance of this class or does have a cache of built-ins,
-     * the prototype is looked up via normal property lookup.
+     * Static helper method to get a built-in object prototype with the given <code>type</code> from
+     * the given <code>scope</code>. If the scope is not an instance of this class or does have a
+     * cache of built-ins, the prototype is looked up via normal property lookup.
      *
      * @param scope the top-level scope
      * @param type the built-in type
      * @return the built-in prototype
      */
-    public static Scriptable getBuiltinPrototype(Scriptable scope,
-                                                 Builtins type) {
+    public static Scriptable getBuiltinPrototype(Scriptable scope, Builtins type) {
         // must be called with top level scope
         assert scope.getParentScope() == null;
         if (scope instanceof TopLevel) {
-            Scriptable result = ((TopLevel)scope)
-                    .getBuiltinPrototype(type);
+            Scriptable result = ((TopLevel) scope).getBuiltinPrototype(type);
             if (result != null) {
                 return result;
             }
         }
         // fall back to normal prototype lookup
-        return ScriptableObject.getClassPrototype(scope, type.name());
+        String typeName;
+        if (type == Builtins.GeneratorFunction) {
+            // GeneratorFunction isn't stored in scope with that name, but in case
+            // we end up falling back to this value then we have to
+            // look this up using a hidden name.
+            typeName = BaseFunction.GENERATOR_FUNCTION_CLASS;
+        } else {
+            typeName = type.name();
+        }
+        return ScriptableObject.getClassPrototype(scope, typeName);
     }
 
     /**
-     * Get the cached built-in object constructor from this scope with the
-     * given <code>type</code>. Returns null if {@link #cacheBuiltins()} has not
-     * been called on this object.
+     * Get the cached built-in object constructor from this scope with the given <code>type</code>.
+     * Returns null if {@link #cacheBuiltins(Scriptable, boolean)} has not been called on this
+     * object.
+     *
      * @param type the built-in type
      * @return the built-in constructor
      */
@@ -208,9 +224,9 @@
     }
 
     /**
-     * Get the cached native error constructor from this scope with the
-     * given <code>type</code>. Returns null if {@link #cacheBuiltins()} has not
-     * been called on this object.
+     * Get the cached native error constructor from this scope with the given <code>type</code>.
+     * Returns null if {@link #cacheBuiltins()} has not been called on this object.
+     *
      * @param type the native error type
      * @return the native error constructor
      */
@@ -219,9 +235,10 @@
     }
 
     /**
-     * Get the cached built-in object prototype from this scope with the
-     * given <code>type</code>. Returns null if {@link #cacheBuiltins()} has not
-     * been called on this object.
+     * Get the cached built-in object prototype from this scope with the given <code>type</code>.
+     * Returns null if {@link #cacheBuiltins(Scriptable, boolean)} has not been called on this
+     * object.
+     *
      * @param type the built-in type
      * @return the built-in prototype
      */
@@ -230,5 +247,4 @@
         Object proto = func != null ? func.getPrototypeProperty() : null;
         return proto instanceof Scriptable ? (Scriptable) proto : null;
     }
-
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/ByteIo.java rhino-1.7.14/src/org/mozilla/javascript/typedarrays/ByteIo.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/ByteIo.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/typedarrays/ByteIo.java	2022-01-06 22:57:21.000000000 +0100
@@ -8,9 +8,9 @@
 
 public class ByteIo
 {
-    public static Object readInt8(byte[] buf, int offset)
+    public static Byte readInt8(byte[] buf, int offset)
     {
-        return buf[offset];
+        return Byte.valueOf(buf[offset]);
     }
 
     public static void writeInt8(byte[] buf, int offset, int val)
@@ -18,9 +18,9 @@
         buf[offset] = (byte)val;
     }
 
-    public static Object readUint8(byte[] buf, int offset)
+    public static Integer readUint8(byte[] buf, int offset)
     {
-        return buf[offset] & 0xff;
+        return Integer.valueOf(buf[offset] & 0xff);
     }
 
     public static void writeUint8(byte[] buf, int offset, int val)
@@ -52,9 +52,9 @@
         }
     }
 
-    public static Object readInt16(byte[] buf, int offset, boolean littleEndian)
+    public static Short readInt16(byte[] buf, int offset, boolean littleEndian)
     {
-        return doReadInt16(buf, offset, littleEndian);
+        return Short.valueOf(doReadInt16(buf, offset, littleEndian));
     }
 
     public static void writeInt16(byte[] buf, int offset, int val, boolean littleEndian)
@@ -62,9 +62,9 @@
         doWriteInt16(buf, offset, val, littleEndian);
     }
 
-    public static Object readUint16(byte[] buf, int offset, boolean littleEndian)
+    public static Integer readUint16(byte[] buf, int offset, boolean littleEndian)
     {
-        return doReadInt16(buf, offset, littleEndian) & 0xffff;
+        return Integer.valueOf(doReadInt16(buf, offset, littleEndian) & 0xffff);
     }
 
     public static void writeUint16(byte[] buf, int offset, int val, boolean littleEndian)
@@ -72,20 +72,20 @@
         doWriteInt16(buf, offset, val & 0xffff, littleEndian);
     }
 
-    public static Object readInt32(byte[] buf, int offset, boolean littleEndian)
+    public static Integer readInt32(byte[] buf, int offset, boolean littleEndian)
     {
         if (littleEndian) {
-            return
+            return Integer.valueOf(
                 (buf[offset]      & 0xff) |
                 ((buf[offset + 1] & 0xff) << 8) |
                 ((buf[offset + 2] & 0xff) << 16) |
-                ((buf[offset + 3] & 0xff) << 24);
+                ((buf[offset + 3] & 0xff) << 24));
         }
-        return
+        return Integer.valueOf(
             ((buf[offset]     & 0xff) << 24) |
             ((buf[offset + 1] & 0xff) << 16) |
             ((buf[offset + 2] & 0xff) << 8) |
-            (buf[offset + 3]  & 0xff);
+            (buf[offset + 3]  & 0xff));
     }
 
     public static void writeInt32(byte[] buf, int offset, int val, boolean littleEndian)
@@ -138,7 +138,7 @@
 
     public static Object readUint32(byte[] buf, int offset, boolean littleEndian)
     {
-        return readUint32Primitive(buf, offset, littleEndian);
+        return Long.valueOf(readUint32Primitive(buf, offset, littleEndian));
     }
 
     public static long readUint64Primitive(byte[] buf, int offset, boolean littleEndian)
@@ -188,10 +188,10 @@
         }
     }
 
-    public static Object readFloat32(byte[] buf, int offset, boolean littleEndian)
+    public static Float readFloat32(byte[] buf, int offset, boolean littleEndian)
     {
         long base = readUint32Primitive(buf, offset, littleEndian);
-        return Float.intBitsToFloat((int)base);
+        return Float.valueOf(Float.intBitsToFloat((int)base));
     }
 
     public static void writeFloat32(byte[] buf, int offset, double val, boolean littleEndian)
@@ -200,10 +200,10 @@
         writeUint32(buf, offset, base, littleEndian);
     }
 
-    public static Object readFloat64(byte[] buf, int offset, boolean littleEndian)
+    public static Double readFloat64(byte[] buf, int offset, boolean littleEndian)
     {
         long base = readUint64Primitive(buf, offset, littleEndian);
-        return Double.longBitsToDouble(base);
+        return Double.valueOf(Double.longBitsToDouble(base));
     }
 
     public static void writeFloat64(byte[] buf, int offset, double val, boolean littleEndian)
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/Conversions.java rhino-1.7.14/src/org/mozilla/javascript/typedarrays/Conversions.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/Conversions.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/typedarrays/Conversions.java	2022-01-06 22:57:21.000000000 +0100
@@ -14,33 +14,14 @@
 
 public class Conversions
 {
-    public static final int EIGHT_BIT = 1 << 8;
-    public static final int SIXTEEN_BIT = 1 << 16;
-    public static final long THIRTYTWO_BIT = 1L << 32L;
-
     public static int toInt8(Object arg)
     {
-        int iv;
-        if (arg instanceof Integer) {
-            iv = (Integer)arg;
-        } else {
-            iv = ScriptRuntime.toInt32(arg);
-        }
-
-        int int8Bit = iv % EIGHT_BIT;
-        return (int8Bit >= (1 << 7)) ? (int8Bit - EIGHT_BIT) : int8Bit;
+        return (byte)ScriptRuntime.toInt32(arg);
     }
 
     public static int toUint8(Object arg)
     {
-        int iv;
-        if (arg instanceof Integer) {
-            iv = ((Integer)arg);
-        } else {
-            iv = ScriptRuntime.toInt32(arg);
-        }
-
-        return iv % EIGHT_BIT;
+        return ScriptRuntime.toInt32(arg) & 0xff;
     }
 
     public static int toUint8Clamp(Object arg)
@@ -69,39 +50,21 @@
 
     public static int toInt16(Object arg)
     {
-        int iv;
-        if (arg instanceof Integer) {
-            iv = ((Integer)arg);
-        } else {
-            iv = ScriptRuntime.toInt32(arg);
-        }
-
-        int int16Bit = iv % SIXTEEN_BIT;
-        return (int16Bit >= (1 << 15)) ? (int16Bit - SIXTEEN_BIT) : int16Bit;
+        return (short)ScriptRuntime.toInt32(arg);
     }
 
     public static int toUint16(Object arg)
     {
-        int iv;
-        if (arg instanceof Integer) {
-            iv = ((Integer)arg);
-        } else {
-            iv = ScriptRuntime.toInt32(arg);
-        }
-
-        return iv % SIXTEEN_BIT;
+        return ScriptRuntime.toInt32(arg) & 0xffff;
     }
 
     public static int toInt32(Object arg)
     {
-        long lv = (long)ScriptRuntime.toNumber(arg);
-        long int32Bit = lv % THIRTYTWO_BIT;
-        return (int)((int32Bit >= (1L << 31L)) ? (int32Bit - THIRTYTWO_BIT) : int32Bit);
+        return ScriptRuntime.toInt32(arg);
     }
 
     public static long toUint32(Object arg)
     {
-        long lv = (long)ScriptRuntime.toNumber(arg);
-        return lv % THIRTYTWO_BIT;
+        return ScriptRuntime.toUint32(arg);
     }
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeArrayBuffer.java rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeArrayBuffer.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeArrayBuffer.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeArrayBuffer.java	2022-01-06 22:57:21.000000000 +0100
@@ -14,68 +14,66 @@
 import org.mozilla.javascript.Undefined;
 
 /**
- * A NativeArrayBuffer is the backing buffer for a typed array. Used inside JavaScript code,
- * it implements the ArrayBuffer interface. Used directly from Java, it simply holds a byte array.
+ * A NativeArrayBuffer is the backing buffer for a typed array. Used inside JavaScript code, it
+ * implements the ArrayBuffer interface. Used directly from Java, it simply holds a byte array.
  */
-
-public class NativeArrayBuffer
-    extends IdScriptableObject
-{
+public class NativeArrayBuffer extends IdScriptableObject {
     private static final long serialVersionUID = 3110411773054879549L;
 
     public static final String CLASS_NAME = "ArrayBuffer";
 
     private static final byte[] EMPTY_BUF = new byte[0];
 
-    public static final NativeArrayBuffer EMPTY_BUFFER = new NativeArrayBuffer();
-
     final byte[] buffer;
 
     @Override
-    public String getClassName()
-    {
+    public String getClassName() {
         return CLASS_NAME;
     }
 
-    public static void init(Context cx, Scriptable scope, boolean sealed)
-    {
+    public static void init(Context cx, Scriptable scope, boolean sealed) {
         NativeArrayBuffer na = new NativeArrayBuffer();
         na.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
     }
 
-    /**
-     * Create an empty buffer.
-     */
-    public NativeArrayBuffer()
-    {
+    /** Create an empty buffer. */
+    public NativeArrayBuffer() {
         buffer = EMPTY_BUF;
     }
 
-    /**
-     * Create a buffer of the specified length in bytes.
-     */
-    public NativeArrayBuffer(int len)
-    {
-        if (len < 0) {
-            throw ScriptRuntime.constructError("RangeError", "Negative array length " + len);
+    /** Create a buffer of the specified length in bytes. */
+    public NativeArrayBuffer(double len) {
+        if (len >= Integer.MAX_VALUE) {
+            throw ScriptRuntime.rangeError("length parameter (" + len + ") is too large ");
+        }
+        if (len == Double.NEGATIVE_INFINITY) {
+            throw ScriptRuntime.rangeError("Negative array length " + len);
+        }
+
+        // support rounding
+        if (len <= -1) {
+            throw ScriptRuntime.rangeError("Negative array length " + len);
         }
-        if (len == 0) {
+
+        int intLen = ScriptRuntime.toInt32(len);
+        if (intLen < 0) {
+            throw ScriptRuntime.rangeError("Negative array length " + len);
+        }
+        if (intLen == 0) {
             buffer = EMPTY_BUF;
         } else {
-            buffer = new byte[len];
+            buffer = new byte[intLen];
         }
     }
 
-    /**
-     * Get the number of bytes in the buffer.
-     */
+    /** Get the number of bytes in the buffer. */
     public int getLength() {
         return buffer.length;
     }
 
     /**
-     * Return the actual bytes that back the buffer. This is a reference to the real buffer,
-     * so changes to bytes here will be reflected in the actual object and all its views.
+     * Return the actual bytes that back the buffer. This is a reference to the real buffer, so
+     * changes to bytes here will be reflected in the actual object and all its views.
      */
     public byte[] getBuffer() {
         return buffer;
@@ -86,19 +84,21 @@
     /**
      * Return a new buffer that represents a slice of this buffer's content, starting at position
      * "start" and ending at position "end". Both values will be "clamped" as per the JavaScript
-     * spec so that invalid values may be passed and will be adjusted up or down accordingly.
-     * This method will return a new buffer that contains a copy of the original buffer. Changes
-     * there will not affect the content of the buffer.
+     * spec so that invalid values may be passed and will be adjusted up or down accordingly. This
+     * method will return a new buffer that contains a copy of the original buffer. Changes there
+     * will not affect the content of the buffer.
      *
      * @param s the position where the new buffer will start
      * @param e the position where it will end
      */
-    public NativeArrayBuffer slice(int s, int e)
-    {
-        // Handle negative start and and as relative to start
+    public NativeArrayBuffer slice(double s, double e) {
+        // Handle negative start as relative to start
         // Clamp as per the spec to between 0 and length
-        int end = Math.max(0, Math.min(buffer.length, (e < 0 ? buffer.length + e : e)));
-        int start = Math.min(end, Math.max(0, (s < 0 ? buffer.length + s : s)));
+        int end =
+                ScriptRuntime.toInt32(
+                        Math.max(0, Math.min(buffer.length, (e < 0 ? buffer.length + e : e))));
+        int start =
+                ScriptRuntime.toInt32(Math.min(end, Math.max(0, (s < 0 ? buffer.length + s : s))));
         int len = end - start;
 
         NativeArrayBuffer newBuf = new NativeArrayBuffer(len);
@@ -109,111 +109,103 @@
     // Function-calling dispatcher
 
     @Override
-    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
-                             Scriptable thisObj, Object[] args)
-    {
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         if (!f.hasTag(CLASS_NAME)) {
             return super.execIdCall(f, cx, scope, thisObj, args);
         }
         int id = f.methodId();
         switch (id) {
-        case ConstructorId_isView:
-            return (isArg(args, 0) && (args[0] instanceof NativeArrayBufferView));
-
-        case Id_constructor:
-            int length = isArg(args, 0) ? ScriptRuntime.toInt32(args[0]) : 0;
-            return new NativeArrayBuffer(length);
-
-        case Id_slice:
-            NativeArrayBuffer self = realThis(thisObj, f);
-            int start = isArg(args, 0) ? ScriptRuntime.toInt32(args[0]) : 0;
-            int end = isArg(args, 1) ? ScriptRuntime.toInt32(args[1]) : self.buffer.length;
-            return self.slice(start, end);
+            case ConstructorId_isView:
+                return Boolean.valueOf(
+                        (isArg(args, 0) && (args[0] instanceof NativeArrayBufferView)));
+
+            case Id_constructor:
+                double length = isArg(args, 0) ? ScriptRuntime.toNumber(args[0]) : 0;
+                return new NativeArrayBuffer(length);
+
+            case Id_slice:
+                NativeArrayBuffer self = realThis(thisObj, f);
+                double start = isArg(args, 0) ? ScriptRuntime.toNumber(args[0]) : 0;
+                double end = isArg(args, 1) ? ScriptRuntime.toNumber(args[1]) : self.buffer.length;
+                return self.slice(start, end);
         }
         throw new IllegalArgumentException(String.valueOf(id));
     }
 
-    private static NativeArrayBuffer realThis(Scriptable thisObj, IdFunctionObject f)
-    {
-        if (!(thisObj instanceof NativeArrayBuffer))
-            throw incompatibleCallError(f);
-        return (NativeArrayBuffer)thisObj;
+    private static NativeArrayBuffer realThis(Scriptable thisObj, IdFunctionObject f) {
+        return ensureType(thisObj, NativeArrayBuffer.class, f);
     }
 
-    private static boolean isArg(Object[] args, int i)
-    {
+    private static boolean isArg(Object[] args, int i) {
         return ((args.length > i) && !Undefined.instance.equals(args[i]));
     }
 
     @Override
-    protected void initPrototypeId(int id)
-    {
+    protected void initPrototypeId(int id) {
         String s;
         int arity;
         switch (id) {
-        case Id_constructor:            arity = 1; s = "constructor"; break;
-        case Id_slice:                  arity = 1; s = "slice"; break;
-        default: throw new IllegalArgumentException(String.valueOf(id));
+            case Id_constructor:
+                arity = 1;
+                s = "constructor";
+                break;
+            case Id_slice:
+                arity = 2;
+                s = "slice";
+                break;
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
         }
         initPrototypeMethod(CLASS_NAME, id, s, arity);
     }
 
-// #string_id_map#
-
     @Override
-    protected int findPrototypeId(String s)
-    {
+    protected int findPrototypeId(String s) {
         int id;
-// #generated#
-        L0: { id = 0; String X = null;
-            int s_length = s.length();
-            if (s_length==5) { X="slice";id=Id_slice; }
-            else if (s_length==6) { X="isView";id=Id_isView; }
-            else if (s_length==11) { X="constructor";id=Id_constructor; }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-            break L0;
+        switch (s) {
+            case "constructor":
+                id = Id_constructor;
+                break;
+            case "slice":
+                id = Id_slice;
+                break;
+            default:
+                id = 0;
+                break;
         }
-// #/generated#
         return id;
     }
 
     // Table of all functions
-    private static final int
-        Id_constructor          = 1,
-        Id_slice                = 2,
-        Id_isView               = 3,
-        MAX_PROTOTYPE_ID        = Id_isView;
-
-// #/string_id_map#
+    private static final int Id_constructor = 1, Id_slice = 2, MAX_PROTOTYPE_ID = Id_slice;
 
     // Constructor (aka static) functions here
 
-    private static final int ConstructorId_isView = -Id_isView;
+    private static final int ConstructorId_isView = -1;
 
     @Override
-    protected void fillConstructorProperties(IdFunctionObject ctor)
-    {
+    protected void fillConstructorProperties(IdFunctionObject ctor) {
         addIdFunctionProperty(ctor, CLASS_NAME, ConstructorId_isView, "isView", 1);
     }
 
     // Properties here
 
     @Override
-    protected int getMaxInstanceId()
-    {
+    protected int getMaxInstanceId() {
         return MAX_INSTANCE_ID;
     }
 
     @Override
-    protected String getInstanceIdName(int id)
-    {
-        if (id == Id_byteLength) { return "byteLength"; }
+    protected String getInstanceIdName(int id) {
+        if (id == Id_byteLength) {
+            return "byteLength";
+        }
         return super.getInstanceIdName(id);
     }
 
     @Override
-    protected Object getInstanceIdValue(int id)
-    {
+    protected Object getInstanceIdValue(int id) {
         if (id == Id_byteLength) {
             return ScriptRuntime.wrapInt(buffer.length);
         }
@@ -221,8 +213,7 @@
     }
 
     @Override
-    protected int findInstanceIdInfo(String s)
-    {
+    protected int findInstanceIdInfo(String s) {
         if ("byteLength".equals(s)) {
             return instanceIdInfo(READONLY | PERMANENT, Id_byteLength);
         }
@@ -230,7 +221,5 @@
     }
 
     // Table of all properties
-    private static final int
-        Id_byteLength           = 1,
-        MAX_INSTANCE_ID         = Id_byteLength;
+    private static final int Id_byteLength = 1, MAX_INSTANCE_ID = Id_byteLength;
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeArrayBufferView.java rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeArrayBufferView.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeArrayBufferView.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeArrayBufferView.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,20 +6,21 @@
 
 package org.mozilla.javascript.typedarrays;
 
+import org.mozilla.javascript.Context;
 import org.mozilla.javascript.IdScriptableObject;
 import org.mozilla.javascript.ScriptRuntime;
 import org.mozilla.javascript.Undefined;
 
 /**
  * This class is the abstract parent for all views of the array. It shows a view of the underlying
- * NativeArrayBuffer. Many views may simultaneously share the same buffer, and changes to one will affect all.
+ * NativeArrayBuffer. Many views may simultaneously share the same buffer, and changes to one will
+ * affect all.
  */
-
-public abstract class NativeArrayBufferView
-    extends IdScriptableObject
-{
+public abstract class NativeArrayBufferView extends IdScriptableObject {
     private static final long serialVersionUID = 6884475582973958419L;
 
+    private static Boolean useLittleEndian = null;
+
     /** Many view objects can share the same backing array */
     protected final NativeArrayBuffer arrayBuffer;
     /** The offset, in bytes, from the start of the backing array */
@@ -27,110 +28,109 @@
     /** The length, in bytes, of the portion of the backing array that we use */
     protected final int byteLength;
 
-    public NativeArrayBufferView()
-    {
-        arrayBuffer = NativeArrayBuffer.EMPTY_BUFFER;
+    public NativeArrayBufferView() {
+        arrayBuffer = new NativeArrayBuffer();
         offset = 0;
         byteLength = 0;
     }
 
-    protected NativeArrayBufferView(NativeArrayBuffer ab, int offset, int byteLength)
-    {
+    protected NativeArrayBufferView(NativeArrayBuffer ab, int offset, int byteLength) {
         this.offset = offset;
         this.byteLength = byteLength;
         this.arrayBuffer = ab;
     }
 
-    /**
-     * Return the buffer that backs this view.
-     */
+    /** Return the buffer that backs this view. */
     public NativeArrayBuffer getBuffer() {
         return arrayBuffer;
     }
 
-    /**
-     * Return the offset in bytes from the start of the buffer that this view represents.
-     */
+    /** Return the offset in bytes from the start of the buffer that this view represents. */
     public int getByteOffset() {
         return offset;
     }
 
-    /**
-     * Return the length, in bytes, of the part of the buffer that this view represents.
-     */
+    /** Return the length, in bytes, of the part of the buffer that this view represents. */
     public int getByteLength() {
         return byteLength;
     }
 
-    protected static boolean isArg(Object[] args, int i)
-    {
+    protected static boolean useLittleEndian() {
+        if (useLittleEndian == null) {
+            Context ctx = Context.getCurrentContext();
+            // for some unit tests this might be null
+            if (ctx == null) {
+                return false;
+            }
+            useLittleEndian = Boolean.valueOf(ctx.hasFeature(Context.FEATURE_LITTLE_ENDIAN));
+        }
+        return useLittleEndian.booleanValue();
+    }
+
+    protected static boolean isArg(Object[] args, int i) {
         return ((args.length > i) && !Undefined.instance.equals(args[i]));
     }
 
     // Property dispatcher
 
     @Override
-    protected int getMaxInstanceId()
-    {
+    protected int getMaxInstanceId() {
         return MAX_INSTANCE_ID;
     }
 
     @Override
-    protected String getInstanceIdName(int id)
-    {
+    protected String getInstanceIdName(int id) {
         switch (id) {
-        case Id_buffer: return "buffer";
-        case Id_byteOffset: return "byteOffset";
-        case Id_byteLength: return "byteLength";
-        default: return super.getInstanceIdName(id);
+            case Id_buffer:
+                return "buffer";
+            case Id_byteOffset:
+                return "byteOffset";
+            case Id_byteLength:
+                return "byteLength";
+            default:
+                return super.getInstanceIdName(id);
         }
     }
 
     @Override
-    protected Object getInstanceIdValue(int id)
-    {
+    protected Object getInstanceIdValue(int id) {
         switch (id) {
-        case Id_buffer:
-            return arrayBuffer;
-        case Id_byteOffset:
-            return ScriptRuntime.wrapInt(offset);
-        case Id_byteLength:
-            return ScriptRuntime.wrapInt(byteLength);
-        default:
-            return super.getInstanceIdValue(id);
+            case Id_buffer:
+                return arrayBuffer;
+            case Id_byteOffset:
+                return ScriptRuntime.wrapInt(offset);
+            case Id_byteLength:
+                return ScriptRuntime.wrapInt(byteLength);
+            default:
+                return super.getInstanceIdValue(id);
         }
     }
 
-// #string_id_map#
-
     @Override
-    protected int findInstanceIdInfo(String s)
-    {
+    protected int findInstanceIdInfo(String s) {
         int id;
-// #generated# Last update: 2014-12-08 17:32:09 PST
-        L0: { id = 0; String X = null; int c;
-            int s_length = s.length();
-            if (s_length==6) { X="buffer";id=Id_buffer; }
-            else if (s_length==10) {
-                c=s.charAt(4);
-                if (c=='L') { X="byteLength";id=Id_byteLength; }
-                else if (c=='O') { X="byteOffset";id=Id_byteOffset; }
-            }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-            break L0;
+        switch (s) {
+            case "buffer":
+                id = Id_buffer;
+                break;
+            case "byteOffset":
+                id = Id_byteOffset;
+                break;
+            case "byteLength":
+                id = Id_byteLength;
+                break;
+            default:
+                id = 0;
+                break;
         }
-// #/generated#
         if (id == 0) {
             return super.findInstanceIdInfo(s);
         }
         return instanceIdInfo(READONLY | PERMANENT, id);
     }
 
-    private static final int
-        Id_buffer               = 1,
-        Id_byteOffset           = 2,
-        Id_byteLength           = 3,
-        MAX_INSTANCE_ID         = Id_byteLength;
+    private static final int Id_buffer = 1, Id_byteOffset = 2, Id_byteLength = 3;
 
-// #/string_id_map#
+    // to be visible by subclasses
+    protected static final int MAX_INSTANCE_ID = Id_byteLength;
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeDataView.java rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeDataView.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeDataView.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeDataView.java	2022-01-06 22:57:21.000000000 +0100
@@ -13,366 +13,443 @@
 import org.mozilla.javascript.Undefined;
 
 /**
- * This class represents the JavaScript "DataView" interface, which allows direct manipulations of the
- * bytes in a NativeArrayBuffer. Java programmers would be best off getting the underling "byte[]" array
- * from the NativeArrayBuffer and manipulating it directly, perhaps using the "ByteIo" class as a helper.
+ * This class represents the JavaScript "DataView" interface, which allows direct manipulations of
+ * the bytes in a NativeArrayBuffer. Java programmers would be best off getting the underling
+ * "byte[]" array from the NativeArrayBuffer and manipulating it directly, perhaps using the
+ * "ByteIo" class as a helper.
  */
-
-public class NativeDataView
-    extends NativeArrayBufferView
-{
+public class NativeDataView extends NativeArrayBufferView {
     private static final long serialVersionUID = 1427967607557438968L;
 
     public static final String CLASS_NAME = "DataView";
 
-    public NativeDataView()
-    {
+    public NativeDataView() {
         super();
     }
 
-    public NativeDataView(NativeArrayBuffer ab, int offset, int length)
-    {
+    public NativeDataView(NativeArrayBuffer ab, int offset, int length) {
         super(ab, offset, length);
     }
 
     @Override
-    public String getClassName()
-    {
+    public String getClassName() {
         return CLASS_NAME;
     }
 
-    public static void init(Context cx, Scriptable scope, boolean sealed)
-    {
+    public static void init(Context cx, Scriptable scope, boolean sealed) {
         NativeDataView dv = new NativeDataView();
         dv.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
     }
 
-    private void rangeCheck(int offset, int len)
-    {
-        if ((offset < 0) || ((offset + len) > byteLength)) {
-            throw ScriptRuntime.constructError("RangeError", "offset out of range");
+    private static int determinePos(Object[] args) {
+        if (isArg(args, 0)) {
+            double doublePos = ScriptRuntime.toNumber(args[0]);
+            if (Double.isInfinite(doublePos)) {
+                throw ScriptRuntime.rangeError("offset out of range");
+            }
+            return ScriptRuntime.toInt32(doublePos);
         }
+        return 0;
     }
 
-    private void checkOffset(Object[] args, int pos)
-    {
-        if (args.length <= pos) {
-            throw ScriptRuntime.constructError("TypeError", "missing required offset parameter");
-        }
-        if (Undefined.instance.equals(args[pos])) {
-            throw ScriptRuntime.constructError("RangeError", "invalid offset");
+    private void rangeCheck(int pos, int len) {
+        if ((pos < 0) || ((pos + len) > byteLength)) {
+            throw ScriptRuntime.rangeError("offset out of range");
         }
     }
 
-    private void checkValue(Object[] args, int pos)
-    {
-        if (args.length <= pos) {
-            throw ScriptRuntime.constructError("TypeError", "missing required value parameter");
+    private static NativeDataView realThis(Scriptable thisObj, IdFunctionObject f) {
+        return ensureType(thisObj, NativeDataView.class, f);
+    }
+
+    private static NativeDataView js_constructor(Object[] args) {
+        if (!isArg(args, 0) || !(args[0] instanceof NativeArrayBuffer)) {
+            throw ScriptRuntime.constructError("TypeError", "Missing parameters");
         }
-        if (Undefined.instance.equals(args[pos])) {
-            throw ScriptRuntime.constructError("RangeError", "invalid value parameter");
+
+        NativeArrayBuffer ab = (NativeArrayBuffer) args[0];
+
+        int pos;
+        if (isArg(args, 1)) {
+            double doublePos = ScriptRuntime.toNumber(args[1]);
+            if (Double.isInfinite(doublePos)) {
+                throw ScriptRuntime.rangeError("offset out of range");
+            }
+            pos = ScriptRuntime.toInt32(doublePos);
+        } else {
+            pos = 0;
         }
-    }
 
-    private static NativeDataView realThis(Scriptable thisObj, IdFunctionObject f)
-    {
-        if (!(thisObj instanceof NativeDataView))
-            throw incompatibleCallError(f);
-        return (NativeDataView)thisObj;
-    }
+        int len;
+        if (isArg(args, 2)) {
+            double doublePos = ScriptRuntime.toNumber(args[2]);
+            if (Double.isInfinite(doublePos)) {
+                throw ScriptRuntime.rangeError("offset out of range");
+            }
+            len = ScriptRuntime.toInt32(doublePos);
+        } else {
+            len = ab.getLength() - pos;
+        }
 
-    private NativeDataView js_constructor(NativeArrayBuffer ab, int offset, int length)
-    {
-        if (length < 0) {
-            throw ScriptRuntime.constructError("RangeError", "length out of range");
+        if (len < 0) {
+            throw ScriptRuntime.rangeError("length out of range");
         }
-        if ((offset < 0) || ((offset + length) > ab.getLength())) {
-            throw ScriptRuntime.constructError("RangeError", "offset out of range");
+        if ((pos < 0) || ((pos + len) > ab.getLength())) {
+            throw ScriptRuntime.rangeError("offset out of range");
         }
-        return new NativeDataView(ab, offset, length);
+        return new NativeDataView(ab, pos, len);
     }
 
-    private Object js_getInt(int bytes, boolean signed, Object[] args)
-    {
-        checkOffset(args, 0);
-
-        int pos = ScriptRuntime.toInt32(args[0]);
+    private Object js_getInt(int bytes, boolean signed, Object[] args) {
+        int pos = determinePos(args);
         rangeCheck(pos, bytes);
 
-        boolean littleEndian =
-            (isArg(args, 1) && (bytes > 1) && ScriptRuntime.toBoolean(args[1]));
+        boolean littleEndian = isArg(args, 1) && (bytes > 1) && ScriptRuntime.toBoolean(args[1]);
 
         switch (bytes) {
-        case 1:
-            return (signed ? ByteIo.readInt8(arrayBuffer.buffer, offset + pos) :
-                             ByteIo.readUint8(arrayBuffer.buffer, offset + pos));
-        case 2:
-            return (signed ? ByteIo.readInt16(arrayBuffer.buffer, offset + pos, littleEndian) :
-                             ByteIo.readUint16(arrayBuffer.buffer, offset + pos, littleEndian));
-        case 4:
-            return (signed ? ByteIo.readInt32(arrayBuffer.buffer, offset + pos, littleEndian) :
-                             ByteIo.readUint32(arrayBuffer.buffer, offset + pos, littleEndian));
-        default:
-            throw new AssertionError();
+            case 1:
+                if (signed) {
+                    return ByteIo.readInt8(arrayBuffer.buffer, offset + pos);
+                } else {
+                    return ByteIo.readUint8(arrayBuffer.buffer, offset + pos);
+                }
+            case 2:
+                if (signed) {
+                    return ByteIo.readInt16(arrayBuffer.buffer, offset + pos, littleEndian);
+                } else {
+                    return ByteIo.readUint16(arrayBuffer.buffer, offset + pos, littleEndian);
+                }
+            case 4:
+                return signed
+                        ? ByteIo.readInt32(arrayBuffer.buffer, offset + pos, littleEndian)
+                        : ByteIo.readUint32(arrayBuffer.buffer, offset + pos, littleEndian);
+            default:
+                throw new AssertionError();
         }
     }
 
-    private Object js_getFloat(int bytes, Object[] args)
-    {
-        checkOffset(args, 0);
-
-        int pos = ScriptRuntime.toInt32(args[0]);
+    private Object js_getFloat(int bytes, Object[] args) {
+        int pos = determinePos(args);
         rangeCheck(pos, bytes);
 
-        boolean littleEndian =
-            (isArg(args, 1) && (bytes > 1) && ScriptRuntime.toBoolean(args[1]));
+        boolean littleEndian = isArg(args, 1) && (bytes > 1) && ScriptRuntime.toBoolean(args[1]);
 
         switch (bytes) {
-        case 4:
-            return ByteIo.readFloat32(arrayBuffer.buffer, offset + pos, littleEndian);
-        case 8:
-            return ByteIo.readFloat64(arrayBuffer.buffer, offset + pos, littleEndian);
-        default:
-            throw new AssertionError();
+            case 4:
+                return ByteIo.readFloat32(arrayBuffer.buffer, offset + pos, littleEndian);
+            case 8:
+                return ByteIo.readFloat64(arrayBuffer.buffer, offset + pos, littleEndian);
+            default:
+                throw new AssertionError();
         }
     }
 
-    private void js_setInt(int bytes, boolean signed, Object[] args)
-    {
-        checkOffset(args, 0);
-        checkValue(args, 1);
+    private void js_setInt(int bytes, boolean signed, Object[] args) {
+        int pos = determinePos(args);
+        if (pos < 0) {
+            throw ScriptRuntime.rangeError("offset out of range");
+        }
 
-        int pos = ScriptRuntime.toInt32(args[0]);
-        rangeCheck(pos, bytes);
+        boolean littleEndian = isArg(args, 2) && (bytes > 1) && ScriptRuntime.toBoolean(args[2]);
 
-        boolean littleEndian =
-            (isArg(args, 2) && (bytes > 1) && ScriptRuntime.toBoolean(args[2]));
+        Object val = ScriptRuntime.zeroObj;
+        if (args.length > 1) {
+            val = args[1];
+        }
 
         switch (bytes) {
-        case 1:
-            if (signed) {
-                ByteIo.writeInt8(arrayBuffer.buffer, offset + pos, Conversions.toInt8(args[1]));
-            } else {
-                ByteIo.writeUint8(arrayBuffer.buffer, offset + pos, Conversions.toUint8(args[1]));
-            }
-            break;
-        case 2:
-            if (signed) {
-                ByteIo.writeInt16(arrayBuffer.buffer, offset + pos, Conversions.toInt16(args[1]), littleEndian);
-            } else {
-                ByteIo.writeUint16(arrayBuffer.buffer, offset + pos, Conversions.toUint16(args[1]), littleEndian);
-            }
-            break;
-        case 4:
-            if (signed) {
-                ByteIo.writeInt32(arrayBuffer.buffer, offset + pos, Conversions.toInt32(args[1]), littleEndian);
-            } else {
-                ByteIo.writeUint32(arrayBuffer.buffer, offset + pos, Conversions.toUint32(args[1]), littleEndian);
-            }
-            break;
-        default:
-            throw new AssertionError();
+            case 1:
+                if (signed) {
+                    int value = Conversions.toInt8(val);
+                    if (pos + bytes > byteLength) {
+                        throw ScriptRuntime.rangeError("offset out of range");
+                    }
+                    ByteIo.writeInt8(arrayBuffer.buffer, offset + pos, value);
+                } else {
+                    int value = Conversions.toUint8(val);
+                    if (pos + bytes > byteLength) {
+                        throw ScriptRuntime.rangeError("offset out of range");
+                    }
+                    ByteIo.writeUint8(arrayBuffer.buffer, offset + pos, value);
+                }
+                break;
+            case 2:
+                if (signed) {
+                    int value = Conversions.toInt16(val);
+                    if (pos + bytes > byteLength) {
+                        throw ScriptRuntime.rangeError("offset out of range");
+                    }
+                    ByteIo.writeInt16(arrayBuffer.buffer, offset + pos, value, littleEndian);
+                } else {
+                    int value = Conversions.toUint16(val);
+                    if (pos + bytes > byteLength) {
+                        throw ScriptRuntime.rangeError("offset out of range");
+                    }
+                    ByteIo.writeUint16(arrayBuffer.buffer, offset + pos, value, littleEndian);
+                }
+                break;
+            case 4:
+                if (signed) {
+                    int value = Conversions.toInt32(val);
+                    if (pos + bytes > byteLength) {
+                        throw ScriptRuntime.rangeError("offset out of range");
+                    }
+                    ByteIo.writeInt32(arrayBuffer.buffer, offset + pos, value, littleEndian);
+                } else {
+                    long value = Conversions.toUint32(val);
+                    if (pos + bytes > byteLength) {
+                        throw ScriptRuntime.rangeError("offset out of range");
+                    }
+                    ByteIo.writeUint32(arrayBuffer.buffer, offset + pos, value, littleEndian);
+                }
+                break;
+            default:
+                throw new AssertionError();
         }
     }
 
-    private void js_setFloat(int bytes, Object[] args)
-    {
-        checkOffset(args, 0);
-        checkValue(args, 1);
+    private void js_setFloat(int bytes, Object[] args) {
+        int pos = determinePos(args);
+        if (pos < 0) {
+            throw ScriptRuntime.rangeError("offset out of range");
+        }
+
+        boolean littleEndian = isArg(args, 2) && (bytes > 1) && ScriptRuntime.toBoolean(args[2]);
 
-        int pos = ScriptRuntime.toInt32(args[0]);
-        rangeCheck(pos, bytes);
+        double val = Double.NaN;
+        if (args.length > 1) {
+            val = ScriptRuntime.toNumber(args[1]);
+        }
 
-        boolean littleEndian =
-            (isArg(args, 2) && (bytes > 1) && ScriptRuntime.toBoolean(args[2]));
-        double val = ScriptRuntime.toNumber(args[1]);
+        if (pos + bytes > byteLength) {
+            throw ScriptRuntime.rangeError("offset out of range");
+        }
 
         switch (bytes) {
-        case 4:
-            ByteIo.writeFloat32(arrayBuffer.buffer, offset + pos, val, littleEndian);
-            break;
-        case 8:
-            ByteIo.writeFloat64(arrayBuffer.buffer, offset + pos, val, littleEndian);
-            break;
-        default:
-            throw new AssertionError();
+            case 4:
+                ByteIo.writeFloat32(arrayBuffer.buffer, offset + pos, val, littleEndian);
+                break;
+            case 8:
+                ByteIo.writeFloat64(arrayBuffer.buffer, offset + pos, val, littleEndian);
+                break;
+            default:
+                throw new AssertionError();
         }
     }
 
     // Function dispatcher
 
     @Override
-    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
-                             Scriptable thisObj, Object[] args)
-    {
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         if (!f.hasTag(getClassName())) {
             return super.execIdCall(f, cx, scope, thisObj, args);
         }
         int id = f.methodId();
         switch (id) {
-        case Id_constructor:
-            if (isArg(args, 0) && (args[0] instanceof NativeArrayBuffer)) {
-                NativeArrayBuffer ab = (NativeArrayBuffer)args[0];
-                int off = isArg(args, 1) ? ScriptRuntime.toInt32(args[1]) : 0;
-                int len = isArg(args, 2) ? ScriptRuntime.toInt32(args[2]) : ab.getLength() - off;
-                return js_constructor(ab, off, len);
-            } else {
-                throw ScriptRuntime.constructError("TypeError", "Missing parameters");
-            }
-        case Id_getInt8:
-            return realThis(thisObj, f).js_getInt(1, true, args);
-        case Id_getUint8:
-            return realThis(thisObj, f).js_getInt(1, false, args);
-        case Id_getInt16:
-            return realThis(thisObj, f).js_getInt(2, true, args);
-        case Id_getUint16:
-            return realThis(thisObj, f).js_getInt(2, false, args);
-        case Id_getInt32:
-            return realThis(thisObj, f).js_getInt(4, true, args);
-        case Id_getUint32:
-            return realThis(thisObj, f).js_getInt(4, false, args);
-        case Id_getFloat32:
-            return realThis(thisObj, f).js_getFloat(4, args);
-        case Id_getFloat64:
-            return realThis(thisObj, f).js_getFloat(8, args);
-        case Id_setInt8:
-            realThis(thisObj, f).js_setInt(1, true, args);
-            return Undefined.instance;
-        case Id_setUint8:
-            realThis(thisObj, f).js_setInt(1, false, args);
-            return Undefined.instance;
-        case Id_setInt16:
-            realThis(thisObj, f).js_setInt(2, true, args);
-            return Undefined.instance;
-        case Id_setUint16:
-            realThis(thisObj, f).js_setInt(2, false, args);
-            return Undefined.instance;
-        case Id_setInt32:
-            realThis(thisObj, f).js_setInt(4, true, args);
-            return Undefined.instance;
-        case Id_setUint32:
-            realThis(thisObj, f).js_setInt(4, false, args);
-            return Undefined.instance;
-        case Id_setFloat32:
-            realThis(thisObj, f).js_setFloat(4, args);
-            return Undefined.instance;
-        case Id_setFloat64:
-            realThis(thisObj, f).js_setFloat(8, args);
-            return Undefined.instance;
+            case Id_constructor:
+                return js_constructor(args);
+            case Id_getInt8:
+                return realThis(thisObj, f).js_getInt(1, true, args);
+            case Id_getUint8:
+                return realThis(thisObj, f).js_getInt(1, false, args);
+            case Id_getInt16:
+                return realThis(thisObj, f).js_getInt(2, true, args);
+            case Id_getUint16:
+                return realThis(thisObj, f).js_getInt(2, false, args);
+            case Id_getInt32:
+                return realThis(thisObj, f).js_getInt(4, true, args);
+            case Id_getUint32:
+                return realThis(thisObj, f).js_getInt(4, false, args);
+            case Id_getFloat32:
+                return realThis(thisObj, f).js_getFloat(4, args);
+            case Id_getFloat64:
+                return realThis(thisObj, f).js_getFloat(8, args);
+            case Id_setInt8:
+                realThis(thisObj, f).js_setInt(1, true, args);
+                return Undefined.instance;
+            case Id_setUint8:
+                realThis(thisObj, f).js_setInt(1, false, args);
+                return Undefined.instance;
+            case Id_setInt16:
+                realThis(thisObj, f).js_setInt(2, true, args);
+                return Undefined.instance;
+            case Id_setUint16:
+                realThis(thisObj, f).js_setInt(2, false, args);
+                return Undefined.instance;
+            case Id_setInt32:
+                realThis(thisObj, f).js_setInt(4, true, args);
+                return Undefined.instance;
+            case Id_setUint32:
+                realThis(thisObj, f).js_setInt(4, false, args);
+                return Undefined.instance;
+            case Id_setFloat32:
+                realThis(thisObj, f).js_setFloat(4, args);
+                return Undefined.instance;
+            case Id_setFloat64:
+                realThis(thisObj, f).js_setFloat(8, args);
+                return Undefined.instance;
         }
         throw new IllegalArgumentException(String.valueOf(id));
     }
 
     @Override
-    protected void initPrototypeId(int id)
-    {
+    protected void initPrototypeId(int id) {
         String s;
         int arity;
         switch (id) {
-        case Id_constructor:    arity = 1; s = "constructor"; break;
-        case Id_getInt8:        arity = 1; s = "getInt8"; break;
-        case Id_getUint8:       arity = 1; s = "getUint8"; break;
-        case Id_getInt16:       arity = 1; s = "getInt16"; break;
-        case Id_getUint16:      arity = 1; s = "getUint16"; break;
-        case Id_getInt32:       arity = 1; s = "getInt32"; break;
-        case Id_getUint32:      arity = 1; s = "getUint32"; break;
-        case Id_getFloat32:     arity = 1; s = "getFloat32"; break;
-        case Id_getFloat64:     arity = 1; s = "getFloat64"; break;
-        case Id_setInt8:        arity = 2; s = "setInt8"; break;
-        case Id_setUint8:       arity = 2; s = "setUint8"; break;
-        case Id_setInt16:       arity = 2; s = "setInt16"; break;
-        case Id_setUint16:      arity = 2; s = "setUint16"; break;
-        case Id_setInt32:       arity = 2; s = "setInt32"; break;
-        case Id_setUint32:      arity = 2; s = "setUint32"; break;
-        case Id_setFloat32:     arity = 2; s = "setFloat32"; break;
-        case Id_setFloat64:     arity = 2; s = "setFloat64"; break;
-        default: throw new IllegalArgumentException(String.valueOf(id));
+            case Id_constructor:
+                arity = 1;
+                s = "constructor";
+                break;
+            case Id_getInt8:
+                arity = 1;
+                s = "getInt8";
+                break;
+            case Id_getUint8:
+                arity = 1;
+                s = "getUint8";
+                break;
+            case Id_getInt16:
+                arity = 1;
+                s = "getInt16";
+                break;
+            case Id_getUint16:
+                arity = 1;
+                s = "getUint16";
+                break;
+            case Id_getInt32:
+                arity = 1;
+                s = "getInt32";
+                break;
+            case Id_getUint32:
+                arity = 1;
+                s = "getUint32";
+                break;
+            case Id_getFloat32:
+                arity = 1;
+                s = "getFloat32";
+                break;
+            case Id_getFloat64:
+                arity = 1;
+                s = "getFloat64";
+                break;
+            case Id_setInt8:
+                arity = 2;
+                s = "setInt8";
+                break;
+            case Id_setUint8:
+                arity = 2;
+                s = "setUint8";
+                break;
+            case Id_setInt16:
+                arity = 2;
+                s = "setInt16";
+                break;
+            case Id_setUint16:
+                arity = 2;
+                s = "setUint16";
+                break;
+            case Id_setInt32:
+                arity = 2;
+                s = "setInt32";
+                break;
+            case Id_setUint32:
+                arity = 2;
+                s = "setUint32";
+                break;
+            case Id_setFloat32:
+                arity = 2;
+                s = "setFloat32";
+                break;
+            case Id_setFloat64:
+                arity = 2;
+                s = "setFloat64";
+                break;
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
         }
         initPrototypeMethod(getClassName(), id, s, arity);
     }
 
-// #string_id_map#
-
     @Override
-    protected int findPrototypeId(String s)
-    {
+    protected int findPrototypeId(String s) {
         int id;
-// #generated# Last update: 2014-12-08 17:26:24 PST
-        L0: { id = 0; String X = null; int c;
-            L: switch (s.length()) {
-            case 7: c=s.charAt(0);
-                if (c=='g') { X="getInt8";id=Id_getInt8; }
-                else if (c=='s') { X="setInt8";id=Id_setInt8; }
-                break L;
-            case 8: c=s.charAt(6);
-                if (c=='1') {
-                    c=s.charAt(0);
-                    if (c=='g') { X="getInt16";id=Id_getInt16; }
-                    else if (c=='s') { X="setInt16";id=Id_setInt16; }
-                }
-                else if (c=='3') {
-                    c=s.charAt(0);
-                    if (c=='g') { X="getInt32";id=Id_getInt32; }
-                    else if (c=='s') { X="setInt32";id=Id_setInt32; }
-                }
-                else if (c=='t') {
-                    c=s.charAt(0);
-                    if (c=='g') { X="getUint8";id=Id_getUint8; }
-                    else if (c=='s') { X="setUint8";id=Id_setUint8; }
-                }
-                break L;
-            case 9: c=s.charAt(0);
-                if (c=='g') {
-                    c=s.charAt(8);
-                    if (c=='2') { X="getUint32";id=Id_getUint32; }
-                    else if (c=='6') { X="getUint16";id=Id_getUint16; }
-                }
-                else if (c=='s') {
-                    c=s.charAt(8);
-                    if (c=='2') { X="setUint32";id=Id_setUint32; }
-                    else if (c=='6') { X="setUint16";id=Id_setUint16; }
-                }
-                break L;
-            case 10: c=s.charAt(0);
-                if (c=='g') {
-                    c=s.charAt(9);
-                    if (c=='2') { X="getFloat32";id=Id_getFloat32; }
-                    else if (c=='4') { X="getFloat64";id=Id_getFloat64; }
-                }
-                else if (c=='s') {
-                    c=s.charAt(9);
-                    if (c=='2') { X="setFloat32";id=Id_setFloat32; }
-                    else if (c=='4') { X="setFloat64";id=Id_setFloat64; }
-                }
-                break L;
-            case 11: X="constructor";id=Id_constructor; break L;
-            }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-            break L0;
+        switch (s) {
+            case "constructor":
+                id = Id_constructor;
+                break;
+            case "getInt8":
+                id = Id_getInt8;
+                break;
+            case "getUint8":
+                id = Id_getUint8;
+                break;
+            case "getInt16":
+                id = Id_getInt16;
+                break;
+            case "getUint16":
+                id = Id_getUint16;
+                break;
+            case "getInt32":
+                id = Id_getInt32;
+                break;
+            case "getUint32":
+                id = Id_getUint32;
+                break;
+            case "getFloat32":
+                id = Id_getFloat32;
+                break;
+            case "getFloat64":
+                id = Id_getFloat64;
+                break;
+            case "setInt8":
+                id = Id_setInt8;
+                break;
+            case "setUint8":
+                id = Id_setUint8;
+                break;
+            case "setInt16":
+                id = Id_setInt16;
+                break;
+            case "setUint16":
+                id = Id_setUint16;
+                break;
+            case "setInt32":
+                id = Id_setInt32;
+                break;
+            case "setUint32":
+                id = Id_setUint32;
+                break;
+            case "setFloat32":
+                id = Id_setFloat32;
+                break;
+            case "setFloat64":
+                id = Id_setFloat64;
+                break;
+            default:
+                id = 0;
+                break;
         }
-// #/generated#
         return id;
     }
 
-    private static final int
-        Id_constructor     = 1,
-        Id_getInt8         = 2,
-        Id_getUint8        = 3,
-        Id_getInt16        = 4,
-        Id_getUint16       = 5,
-        Id_getInt32        = 6,
-        Id_getUint32       = 7,
-        Id_getFloat32      = 8,
-        Id_getFloat64      = 9,
-        Id_setInt8         = 10,
-        Id_setUint8        = 11,
-        Id_setInt16        = 12,
-        Id_setUint16       = 13,
-        Id_setInt32        = 14,
-        Id_setUint32       = 15,
-        Id_setFloat32      = 16,
-        Id_setFloat64      = 17,
-        MAX_PROTOTYPE_ID   = Id_setFloat64;
-
-// #/string_id_map#
+    private static final int Id_constructor = 1,
+            Id_getInt8 = 2,
+            Id_getUint8 = 3,
+            Id_getInt16 = 4,
+            Id_getUint16 = 5,
+            Id_getInt32 = 6,
+            Id_getUint32 = 7,
+            Id_getFloat32 = 8,
+            Id_getFloat64 = 9,
+            Id_setInt8 = 10,
+            Id_setUint8 = 11,
+            Id_setInt16 = 12,
+            Id_setUint16 = 13,
+            Id_setInt32 = 14,
+            Id_setUint32 = 15,
+            Id_setFloat32 = 16,
+            Id_setFloat64 = 17,
+            MAX_PROTOTYPE_ID = Id_setFloat64;
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeFloat32Array.java rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeFloat32Array.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeFloat32Array.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeFloat32Array.java	2022-01-06 22:57:21.000000000 +0100
@@ -14,7 +14,7 @@
 
 /**
  * An array view that stores 32-bit quantities and implements the JavaScript "loat32Array" interface.
- * It also implements List<Float> for direct manipulation in Java.
+ * It also implements List<Float> for direct manipulation in Java.
  */
 
 public class NativeFloat32Array
@@ -36,7 +36,7 @@
 
     public NativeFloat32Array(int len)
     {
-        this(new NativeArrayBuffer(len * BYTES_PER_ELEMENT), 0, len);
+        this(new NativeArrayBuffer((double)len * BYTES_PER_ELEMENT), 0, len);
     }
 
     @Override
@@ -52,7 +52,7 @@
     }
 
     @Override
-    protected NativeTypedArrayView construct(NativeArrayBuffer ab, int off, int len)
+    protected NativeFloat32Array construct(NativeArrayBuffer ab, int off, int len)
     {
         return new NativeFloat32Array(ab, off, len);
     }
@@ -64,12 +64,9 @@
     }
 
     @Override
-    protected NativeTypedArrayView realThis(Scriptable thisObj, IdFunctionObject f)
+    protected NativeFloat32Array realThis(Scriptable thisObj, IdFunctionObject f)
     {
-        if (!(thisObj instanceof NativeFloat32Array)) {
-            throw incompatibleCallError(f);
-        }
-        return (NativeFloat32Array)thisObj;
+        return ensureType(thisObj, NativeFloat32Array.class, f);
     }
 
     @Override
@@ -78,7 +75,7 @@
         if (checkIndex(index)) {
             return Undefined.instance;
         }
-        return ByteIo.readFloat32(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, false);
+        return ByteIo.readFloat32(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, useLittleEndian());
     }
 
     @Override
@@ -88,7 +85,7 @@
             return Undefined.instance;
         }
         double val = ScriptRuntime.toNumber(c);
-        ByteIo.writeFloat32(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, val, false);
+        ByteIo.writeFloat32(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, val, useLittleEndian());
         return null;
     }
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeFloat64Array.java rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeFloat64Array.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeFloat64Array.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeFloat64Array.java	2022-01-06 22:57:21.000000000 +0100
@@ -14,7 +14,7 @@
 
 /**
  * An array view that stores 64-bit quantities and implements the JavaScript "Float64Array" interface.
- * It also implements List<Double> for direct manipulation in Java.
+ * It also implements List<Double> for direct manipulation in Java.
  */
 
 public class NativeFloat64Array
@@ -36,7 +36,7 @@
 
     public NativeFloat64Array(int len)
     {
-        this(new NativeArrayBuffer(len * BYTES_PER_ELEMENT), 0, len);
+        this(new NativeArrayBuffer((double)len * BYTES_PER_ELEMENT), 0, len);
     }
 
     @Override
@@ -52,7 +52,7 @@
     }
 
     @Override
-    protected NativeTypedArrayView construct(NativeArrayBuffer ab, int off, int len)
+    protected NativeFloat64Array construct(NativeArrayBuffer ab, int off, int len)
     {
         return new NativeFloat64Array(ab, off, len);
     }
@@ -64,12 +64,9 @@
     }
 
     @Override
-    protected NativeTypedArrayView realThis(Scriptable thisObj, IdFunctionObject f)
+    protected NativeFloat64Array realThis(Scriptable thisObj, IdFunctionObject f)
     {
-        if (!(thisObj instanceof NativeFloat64Array)) {
-            throw incompatibleCallError(f);
-        }
-        return (NativeFloat64Array)thisObj;
+        return ensureType(thisObj, NativeFloat64Array.class, f);
     }
 
     @Override
@@ -78,8 +75,8 @@
         if (checkIndex(index)) {
             return Undefined.instance;
         }
-        long base = ByteIo.readUint64Primitive(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, false);
-        return Double.longBitsToDouble(base);
+        long base = ByteIo.readUint64Primitive(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, useLittleEndian());
+        return Double.valueOf(Double.longBitsToDouble(base));
     }
 
     @Override
@@ -90,7 +87,7 @@
         }
         double val = ScriptRuntime.toNumber(c);
         long base = Double.doubleToLongBits(val);
-        ByteIo.writeUint64(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, base, false);
+        ByteIo.writeUint64(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, base, useLittleEndian());
         return null;
     }
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeInt16Array.java rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeInt16Array.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeInt16Array.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeInt16Array.java	2022-01-06 22:57:21.000000000 +0100
@@ -13,7 +13,7 @@
 
 /**
  * An array view that stores 16-bit quantities and implements the JavaScript "Int16Array" interface.
- * It also implements List<Short> for direct manipulation in Java.
+ * It also implements List<Short> for direct manipulation in Java.
  */
 
 public class NativeInt16Array
@@ -35,7 +35,7 @@
 
     public NativeInt16Array(int len)
     {
-        this(new NativeArrayBuffer(len * BYTES_PER_ELEMENT), 0, len);
+        this(new NativeArrayBuffer((double)len * BYTES_PER_ELEMENT), 0, len);
     }
 
     @Override
@@ -51,7 +51,7 @@
     }
 
     @Override
-    protected NativeTypedArrayView construct(NativeArrayBuffer ab, int off, int len)
+    protected NativeInt16Array construct(NativeArrayBuffer ab, int off, int len)
     {
         return new NativeInt16Array(ab, off, len);
     }
@@ -63,12 +63,9 @@
     }
 
     @Override
-    protected NativeTypedArrayView realThis(Scriptable thisObj, IdFunctionObject f)
+    protected NativeInt16Array realThis(Scriptable thisObj, IdFunctionObject f)
     {
-        if (!(thisObj instanceof NativeInt16Array)) {
-            throw incompatibleCallError(f);
-        }
-        return (NativeInt16Array)thisObj;
+        return ensureType(thisObj, NativeInt16Array.class, f);
     }
 
     @Override
@@ -77,7 +74,7 @@
         if (checkIndex(index)) {
             return Undefined.instance;
         }
-        return ByteIo.readInt16(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, false);
+        return ByteIo.readInt16(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, useLittleEndian());
     }
 
     @Override
@@ -87,7 +84,7 @@
             return Undefined.instance;
         }
         int val = Conversions.toInt16(c);
-        ByteIo.writeInt16(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, val, false);
+        ByteIo.writeInt16(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, val, useLittleEndian());
         return null;
     }
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeInt32Array.java rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeInt32Array.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeInt32Array.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeInt32Array.java	2022-01-06 22:57:21.000000000 +0100
@@ -14,7 +14,7 @@
 
 /**
  * An array view that stores 32-bit quantities and implements the JavaScript "Int32Array" interface.
- * It also implements List<Integer> for direct manipulation in Java.
+ * It also implements List<Integer> for direct manipulation in Java.
  */
 
 public class NativeInt32Array
@@ -36,7 +36,7 @@
 
     public NativeInt32Array(int len)
     {
-        this(new NativeArrayBuffer(len * BYTES_PER_ELEMENT), 0, len);
+        this(new NativeArrayBuffer((double)len * BYTES_PER_ELEMENT), 0, len);
     }
 
     @Override
@@ -52,7 +52,7 @@
     }
 
     @Override
-    protected NativeTypedArrayView construct(NativeArrayBuffer ab, int off, int len)
+    protected NativeInt32Array construct(NativeArrayBuffer ab, int off, int len)
     {
         return new NativeInt32Array(ab, off, len);
     }
@@ -64,12 +64,9 @@
     }
 
     @Override
-    protected NativeTypedArrayView realThis(Scriptable thisObj, IdFunctionObject f)
+    protected NativeInt32Array realThis(Scriptable thisObj, IdFunctionObject f)
     {
-        if (!(thisObj instanceof NativeInt32Array)) {
-            throw incompatibleCallError(f);
-        }
-        return (NativeInt32Array)thisObj;
+        return ensureType(thisObj, NativeInt32Array.class, f);
     }
 
     @Override
@@ -78,7 +75,7 @@
         if (checkIndex(index)) {
             return Undefined.instance;
         }
-        return ByteIo.readInt32(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, false);
+        return ByteIo.readInt32(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, useLittleEndian());
     }
 
     @Override
@@ -88,7 +85,7 @@
             return Undefined.instance;
         }
         int val = ScriptRuntime.toInt32(c);
-        ByteIo.writeInt32(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, val, false);
+        ByteIo.writeInt32(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, val, useLittleEndian());
         return null;
     }
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeInt8Array.java rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeInt8Array.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeInt8Array.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeInt8Array.java	2022-01-06 22:57:21.000000000 +0100
@@ -13,7 +13,7 @@
 
 /**
  * An array view that stores 8-bit quantities and implements the JavaScript "Int8Array" interface.
- * It also implements List<Byte> for direct manipulation in Java.
+ * It also implements List<Byte> for direct manipulation in Java.
  */
 
 public class NativeInt8Array
@@ -50,7 +50,7 @@
     }
 
     @Override
-    protected NativeTypedArrayView construct(NativeArrayBuffer ab, int off, int len)
+    protected NativeInt8Array construct(NativeArrayBuffer ab, int off, int len)
     {
         return new NativeInt8Array(ab, off, len);
     }
@@ -62,12 +62,9 @@
     }
 
     @Override
-    protected NativeTypedArrayView realThis(Scriptable thisObj, IdFunctionObject f)
+    protected NativeInt8Array realThis(Scriptable thisObj, IdFunctionObject f)
     {
-        if (!(thisObj instanceof NativeInt8Array)) {
-            throw incompatibleCallError(f);
-        }
-        return (NativeInt8Array)thisObj;
+        return ensureType(thisObj, NativeInt8Array.class, f);
     }
 
     @Override
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeTypedArrayIterator.java rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeTypedArrayIterator.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeTypedArrayIterator.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeTypedArrayIterator.java	2022-01-06 22:57:21.000000000 +0100
@@ -52,7 +52,7 @@
     public T next()
     {
         if (hasNext()) {
-            T ret = (T)view.get(position);
+            T ret = view.get(position);
             lastPosition = position;
             position++;
             return ret;
@@ -66,7 +66,7 @@
         if (hasPrevious()) {
             position--;
             lastPosition = position;
-            return (T)view.get(position);
+            return view.get(position);
         }
         throw new NoSuchElementException();
     }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeTypedArrayView.java rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeTypedArrayView.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeTypedArrayView.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeTypedArrayView.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,44 +6,42 @@
 
 package org.mozilla.javascript.typedarrays;
 
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.RandomAccess;
 import org.mozilla.javascript.Context;
 import org.mozilla.javascript.ExternalArrayData;
 import org.mozilla.javascript.IdFunctionObject;
 import org.mozilla.javascript.NativeArray;
 import org.mozilla.javascript.NativeArrayIterator;
+import org.mozilla.javascript.NativeArrayIterator.ARRAY_ITERATOR_TYPE;
 import org.mozilla.javascript.ScriptRuntime;
 import org.mozilla.javascript.Scriptable;
 import org.mozilla.javascript.Symbol;
 import org.mozilla.javascript.SymbolKey;
 import org.mozilla.javascript.Undefined;
-
-import java.lang.reflect.Array;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.RandomAccess;
+import org.mozilla.javascript.Wrapper;
 
 /**
- * This class is the abstract parent for all of the various typed arrays. Each one
- * shows a view of a specific NativeArrayBuffer, and modifications here will affect the rest.
+ * This class is the abstract parent for all of the various typed arrays. Each one shows a view of a
+ * specific NativeArrayBuffer, and modifications here will affect the rest.
  */
+public abstract class NativeTypedArrayView<T> extends NativeArrayBufferView
+        implements List<T>, RandomAccess, ExternalArrayData {
+    private static final long serialVersionUID = -4963053773152251274L;
 
-public abstract class NativeTypedArrayView<T>
-    extends NativeArrayBufferView
-    implements List<T>, RandomAccess, ExternalArrayData
-{
     /** The length, in elements, of the array */
     protected final int length;
 
-    protected NativeTypedArrayView()
-    {
+    protected NativeTypedArrayView() {
         super();
         length = 0;
     }
 
-    protected NativeTypedArrayView(NativeArrayBuffer ab, int off, int len, int byteLen)
-    {
+    protected NativeTypedArrayView(NativeArrayBuffer ab, int off, int len, int byteLen) {
         super(ab, off, byteLen);
         length = len;
     }
@@ -51,31 +49,25 @@
     // Array properties implementation
 
     @Override
-    public Object get(int index, Scriptable start)
-    {
+    public Object get(int index, Scriptable start) {
         return js_get(index);
     }
 
     @Override
-    public boolean has(int index, Scriptable start)
-    {
-        return ((index > 0) && (index < length));
+    public boolean has(int index, Scriptable start) {
+        return !checkIndex(index);
     }
 
     @Override
-    public void put(int index, Scriptable start, Object val)
-    {
+    public void put(int index, Scriptable start, Object val) {
         js_set(index, val);
     }
 
     @Override
-    public void delete(int index)
-    {
-    }
+    public void delete(int index) {}
 
     @Override
-    public Object[] getIds()
-    {
+    public Object[] getIds() {
         Object[] ret = new Object[length];
         for (int i = 0; i < length; i++) {
             ret[i] = Integer.valueOf(i);
@@ -85,53 +77,65 @@
 
     // Actual functions
 
-    protected boolean checkIndex(int index)
-    {
-       return ((index < 0) || (index >= length));
+    protected boolean checkIndex(int index) {
+        return ((index < 0) || (index >= length));
     }
 
     /**
-     * Return the number of bytes represented by each element in the array. This can be useful
-     * when wishing to manipulate the byte array directly from Java.
+     * Return the number of bytes represented by each element in the array. This can be useful when
+     * wishing to manipulate the byte array directly from Java.
      */
     public abstract int getBytesPerElement();
 
-    protected abstract NativeTypedArrayView construct(NativeArrayBuffer ab, int off, int len);
+    protected abstract NativeTypedArrayView<T> construct(NativeArrayBuffer ab, int off, int len);
+
     protected abstract Object js_get(int index);
+
     protected abstract Object js_set(int index, Object c);
-    protected abstract NativeTypedArrayView realThis(Scriptable thisObj, IdFunctionObject f);
 
-    private NativeArrayBuffer makeArrayBuffer(Context cx, Scriptable scope, int length)
-    {
-        return (NativeArrayBuffer)cx.newObject(scope, NativeArrayBuffer.CLASS_NAME,
-                                               new Object[] { length });
+    protected abstract NativeTypedArrayView<T> realThis(Scriptable thisObj, IdFunctionObject f);
+
+    private NativeArrayBuffer makeArrayBuffer(Context cx, Scriptable scope, int length) {
+        return (NativeArrayBuffer)
+                cx.newObject(
+                        scope,
+                        NativeArrayBuffer.CLASS_NAME,
+                        new Object[] {Double.valueOf((double) length * getBytesPerElement())});
     }
 
-    private NativeTypedArrayView js_constructor(Context cx, Scriptable scope, Object[] args)
-    {
+    private NativeTypedArrayView<T> js_constructor(Context cx, Scriptable scope, Object[] args) {
         if (!isArg(args, 0)) {
-            return construct(NativeArrayBuffer.EMPTY_BUFFER, 0, 0);
+            return construct(new NativeArrayBuffer(), 0, 0);
+        }
+
+        final Object arg0 = args[0];
+        if (arg0 == null) {
+            return construct(new NativeArrayBuffer(), 0, 0);
+        }
 
-        } else if ((args[0] instanceof Number) || (args[0] instanceof String)) {
+        if ((arg0 instanceof Number) || (arg0 instanceof String)) {
             // Create a zeroed-out array of a certain length
-            int length = ScriptRuntime.toInt32(args[0]);
-            NativeArrayBuffer buffer = makeArrayBuffer(cx, scope, length * getBytesPerElement());
+            int length = ScriptRuntime.toInt32(arg0);
+            NativeArrayBuffer buffer = makeArrayBuffer(cx, scope, length);
             return construct(buffer, 0, length);
+        }
 
-        } else if (args[0] instanceof NativeTypedArrayView) {
+        if (arg0 instanceof NativeTypedArrayView) {
             // Copy elements from the old array and convert them into our own
-            NativeTypedArrayView src = (NativeTypedArrayView)args[0];
-            NativeArrayBuffer na = makeArrayBuffer(cx, scope, src.length * getBytesPerElement());
-            NativeTypedArrayView v = construct(na, 0, src.length);
+            @SuppressWarnings("unchecked")
+            NativeTypedArrayView<T> src = (NativeTypedArrayView<T>) arg0;
+            NativeArrayBuffer na = makeArrayBuffer(cx, scope, src.length);
+            NativeTypedArrayView<T> v = construct(na, 0, src.length);
 
             for (int i = 0; i < src.length; i++) {
                 v.js_set(i, src.js_get(i));
             }
             return v;
+        }
 
-        } else if (args[0] instanceof NativeArrayBuffer) {
+        if (arg0 instanceof NativeArrayBuffer) {
             // Make a slice of an existing buffer, with shared storage
-            NativeArrayBuffer na = (NativeArrayBuffer)args[0];
+            NativeArrayBuffer na = (NativeArrayBuffer) arg0;
             int byteOff = isArg(args, 1) ? ScriptRuntime.toInt32(args[1]) : 0;
 
             int byteLen;
@@ -142,45 +146,64 @@
             }
 
             if ((byteOff < 0) || (byteOff > na.buffer.length)) {
-                throw ScriptRuntime.constructError("RangeError", "offset out of range");
+                throw ScriptRuntime.rangeError("offset out of range");
             }
             if ((byteLen < 0) || ((byteOff + byteLen) > na.buffer.length)) {
-                throw ScriptRuntime.constructError("RangeError", "length out of range");
+                throw ScriptRuntime.rangeError("length out of range");
             }
             if ((byteOff % getBytesPerElement()) != 0) {
-                throw ScriptRuntime.constructError("RangeError", "offset must be a multiple of the byte size");
+                throw ScriptRuntime.rangeError("offset must be a multiple of the byte size");
             }
             if ((byteLen % getBytesPerElement()) != 0) {
-                throw ScriptRuntime.constructError("RangeError", "offset and buffer must be a multiple of the byte size");
+                throw ScriptRuntime.rangeError(
+                        "offset and buffer must be a multiple of the byte size");
             }
 
             return construct(na, byteOff, byteLen / getBytesPerElement());
+        }
 
-        } else if (args[0] instanceof NativeArray) {
+        if (arg0 instanceof NativeArray) {
             // Copy elements of the array and convert them to the correct type
-            List l = (List)args[0];
-            NativeArrayBuffer na = makeArrayBuffer(cx, scope, l.size() * getBytesPerElement());
-            NativeTypedArrayView v = construct(na, 0, l.size());
-            int p = 0;
-            for (Object o : l) {
-                v.js_set(p, o);
-                p++;
+            NativeArray array = (NativeArray) arg0;
+
+            NativeArrayBuffer na = makeArrayBuffer(cx, scope, array.size());
+            NativeTypedArrayView<T> v = construct(na, 0, array.size());
+            for (int i = 0; i < array.size(); i++) {
+                // we have to call this here to get the raw value;
+                // null has to be forewoded as null
+                final Object value = array.get(i, array);
+                if (value == Scriptable.NOT_FOUND || value == Undefined.instance) {
+                    v.js_set(i, ScriptRuntime.NaNobj);
+                } else if (value instanceof Wrapper) {
+                    v.js_set(i, ((Wrapper) value).unwrap());
+                } else {
+                    v.js_set(i, value);
+                }
             }
             return v;
+        }
 
-        } else {
-            throw ScriptRuntime.constructError("Error", "invalid argument");
+        if (ScriptRuntime.isArrayObject(arg0)) {
+            // Copy elements of the array and convert them to the correct type
+            Object[] arrayElements = ScriptRuntime.getArrayElements((Scriptable) arg0);
+
+            NativeArrayBuffer na = makeArrayBuffer(cx, scope, arrayElements.length);
+            NativeTypedArrayView<T> v = construct(na, 0, arrayElements.length);
+            for (int i = 0; i < arrayElements.length; i++) {
+                v.js_set(i, arrayElements[i]);
+            }
+            return v;
         }
+        throw ScriptRuntime.constructError("Error", "invalid argument");
     }
 
-    private void setRange(NativeTypedArrayView v, int off)
-    {
+    private void setRange(NativeTypedArrayView<T> v, int off) {
         if (off >= length) {
-            throw ScriptRuntime.constructError("RangeError", "offset out of range");
+            throw ScriptRuntime.rangeError("offset out of range");
         }
 
         if (v.length > (length - off)) {
-            throw ScriptRuntime.constructError("RangeError", "source array too long");
+            throw ScriptRuntime.rangeError("source array too long");
         }
 
         if (v.arrayBuffer == arrayBuffer) {
@@ -199,13 +222,12 @@
         }
     }
 
-    private void setRange(NativeArray a, int off)
-    {
+    private void setRange(NativeArray a, int off) {
         if (off > length) {
-            throw ScriptRuntime.constructError("RangeError", "offset out of range");
+            throw ScriptRuntime.rangeError("offset out of range");
         }
         if ((off + a.size()) > length) {
-            throw ScriptRuntime.constructError("RangeError", "offset + length out of range");
+            throw ScriptRuntime.rangeError("offset + length out of range");
         }
 
         int pos = off;
@@ -215,8 +237,7 @@
         }
     }
 
-    private Object js_subarray(Context cx, Scriptable scope, int s, int e)
-    {
+    private Object js_subarray(Context cx, Scriptable scope, int s, int e) {
         int start = (s < 0 ? length + s : s);
         int end = (e < 0 ? length + e : e);
 
@@ -226,74 +247,89 @@
         int len = Math.max(0, (end - start));
         int byteOff = Math.min(start * getBytesPerElement(), arrayBuffer.getLength());
 
-        return
-            cx.newObject(scope, getClassName(),
-                         new Object[]{arrayBuffer, byteOff, len});
+        return cx.newObject(
+                scope,
+                getClassName(),
+                new Object[] {arrayBuffer, Integer.valueOf(byteOff), Integer.valueOf(len)});
     }
 
     // Dispatcher
 
     @Override
-    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
-                             Scriptable thisObj, Object[] args)
-    {
+    public Object execIdCall(
+            IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
         if (!f.hasTag(getClassName())) {
             return super.execIdCall(f, cx, scope, thisObj, args);
         }
         int id = f.methodId();
         switch (id) {
-        case Id_constructor:
-            return js_constructor(cx, scope, args);
-
-        case Id_get:
-            if (args.length > 0) {
-                return realThis(thisObj, f).js_get(ScriptRuntime.toInt32(args[0]));
-            } else {
-                throw ScriptRuntime.constructError("Error", "invalid arguments");
-            }
+            case Id_constructor:
+                if (thisObj != null && cx.getLanguageVersion() >= Context.VERSION_ES6) {
+                    throw ScriptRuntime.typeErrorById("msg.only.from.new", getClassName());
+                }
+                return js_constructor(cx, scope, args);
 
-        case Id_set:
-            if (args.length > 0) {
-                NativeTypedArrayView self = realThis(thisObj, f);
-                if (args[0] instanceof NativeTypedArrayView) {
-                    int offset = isArg(args, 1) ? ScriptRuntime.toInt32(args[1]) : 0;
-                    self.setRange((NativeTypedArrayView)args[0], offset);
-                    return Undefined.instance;
+            case Id_toString:
+                NativeTypedArrayView<T> realThis = realThis(thisObj, f);
+                final int arrayLength = realThis.getArrayLength();
+                final StringBuilder builder = new StringBuilder();
+                if (arrayLength > 0) {
+                    builder.append(ScriptRuntime.toString(realThis.js_get(0)));
                 }
-                if (args[0] instanceof NativeArray) {
-                    int offset = isArg(args, 1) ? ScriptRuntime.toInt32(args[1]) : 0;
-                    self.setRange((NativeArray)args[0], offset);
-                    return Undefined.instance;
+                for (int i = 1; i < arrayLength; i++) {
+                    builder.append(',');
+                    builder.append(ScriptRuntime.toString(realThis.js_get(i)));
                 }
-                if (args[0] instanceof Scriptable) {
-                    // Tests show that we need to ignore a non-array object
-                    return Undefined.instance;
+                return builder.toString();
+
+            case Id_get:
+                if (args.length > 0) {
+                    return realThis(thisObj, f).js_get(ScriptRuntime.toInt32(args[0]));
                 }
-                if (isArg(args, 2)) {
-                    return self.js_set(ScriptRuntime.toInt32(args[0]), args[1]);
+                throw ScriptRuntime.constructError("Error", "invalid arguments");
+
+            case Id_set:
+                if (args.length > 0) {
+                    NativeTypedArrayView<T> self = realThis(thisObj, f);
+                    if (args[0] instanceof NativeTypedArrayView) {
+                        int offset = isArg(args, 1) ? ScriptRuntime.toInt32(args[1]) : 0;
+                        @SuppressWarnings("unchecked")
+                        NativeTypedArrayView<T> nativeView = (NativeTypedArrayView<T>) args[0];
+                        self.setRange(nativeView, offset);
+                        return Undefined.instance;
+                    }
+                    if (args[0] instanceof NativeArray) {
+                        int offset = isArg(args, 1) ? ScriptRuntime.toInt32(args[1]) : 0;
+                        self.setRange((NativeArray) args[0], offset);
+                        return Undefined.instance;
+                    }
+                    if (args[0] instanceof Scriptable) {
+                        // Tests show that we need to ignore a non-array object
+                        return Undefined.instance;
+                    }
+                    if (isArg(args, 2)) {
+                        return self.js_set(ScriptRuntime.toInt32(args[0]), args[1]);
+                    }
                 }
-            }
-            throw ScriptRuntime.constructError("Error", "invalid arguments");
+                throw ScriptRuntime.constructError("Error", "invalid arguments");
 
-        case Id_subarray:
-            if (args.length > 0) {
-                NativeTypedArrayView self = realThis(thisObj, f);
-                int start = ScriptRuntime.toInt32(args[0]);
+            case Id_subarray:
+                NativeTypedArrayView<T> self = realThis(thisObj, f);
+                int start = isArg(args, 0) ? ScriptRuntime.toInt32(args[0]) : 0;
                 int end = isArg(args, 1) ? ScriptRuntime.toInt32(args[1]) : self.length;
-                return self.js_subarray(cx, scope, start, end);
-            } else {
+                if (cx.getLanguageVersion() >= Context.VERSION_ES6 || args.length > 0) {
+                    return self.js_subarray(cx, scope, start, end);
+                }
                 throw ScriptRuntime.constructError("Error", "invalid arguments");
-            }
 
-        case SymbolId_iterator:
-            return new NativeArrayIterator(scope, thisObj);
+            case SymbolId_iterator:
+                return new NativeArrayIterator(scope, thisObj, ARRAY_ITERATOR_TYPE.VALUES);
         }
         throw new IllegalArgumentException(String.valueOf(id));
     }
 
     @Override
-    protected void initPrototypeId(int id)
-    {
+    protected void initPrototypeId(int id) {
         if (id == SymbolId_iterator) {
             initPrototypeMethod(getClassName(), id, SymbolKey.ITERATOR, "[Symbol.iterator]", 0);
             return;
@@ -302,141 +338,158 @@
         String s, fnName = null;
         int arity;
         switch (id) {
-        case Id_constructor:        arity = 1; s = "constructor"; break;
-        case Id_get:                arity = 1; s = "get"; break;
-        case Id_set:                arity = 2; s = "set"; break;
-        case Id_subarray:           arity = 2; s = "subarray"; break;
-        default: throw new IllegalArgumentException(String.valueOf(id));
+            case Id_constructor:
+                arity = 3;
+                s = "constructor";
+                break;
+            case Id_toString:
+                arity = 0;
+                s = "toString";
+                break;
+            case Id_get:
+                arity = 1;
+                s = "get";
+                break;
+            case Id_set:
+                arity = 2;
+                s = "set";
+                break;
+            case Id_subarray:
+                arity = 2;
+                s = "subarray";
+                break;
+            default:
+                throw new IllegalArgumentException(String.valueOf(id));
         }
         initPrototypeMethod(getClassName(), id, s, fnName, arity);
     }
 
     @Override
-    protected int findPrototypeId(Symbol k)
-    {
+    protected int findPrototypeId(Symbol k) {
         if (SymbolKey.ITERATOR.equals(k)) {
             return SymbolId_iterator;
         }
         return 0;
     }
 
-    // #string_id_map#
-
     @Override
-    protected int findPrototypeId(String s)
-    {
+    protected int findPrototypeId(String s) {
         int id;
-// #generated# Last update: 2016-03-04 20:59:23 GMT
-        L0: { id = 0; String X = null; int c;
-            int s_length = s.length();
-            if (s_length==3) {
-                c=s.charAt(0);
-                if (c=='g') { if (s.charAt(2)=='t' && s.charAt(1)=='e') {id=Id_get; break L0;} }
-                else if (c=='s') { if (s.charAt(2)=='t' && s.charAt(1)=='e') {id=Id_set; break L0;} }
-            }
-            else if (s_length==8) { X="subarray";id=Id_subarray; }
-            else if (s_length==11) { X="constructor";id=Id_constructor; }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-            break L0;
+        switch (s) {
+            case "constructor":
+                id = Id_constructor;
+                break;
+            case "toString":
+                id = Id_toString;
+                break;
+            case "get":
+                id = Id_get;
+                break;
+            case "set":
+                id = Id_set;
+                break;
+            case "subarray":
+                id = Id_subarray;
+                break;
+            default:
+                id = 0;
+                break;
         }
-// #/generated#
         return id;
     }
 
     // Table of all functions
-    private static final int
-        Id_constructor          = 1,
-        Id_get                  = 2,
-        Id_set                  = 3,
-        Id_subarray             = 4,
-        SymbolId_iterator       = 5;
-
-    protected static final int
-        MAX_PROTOTYPE_ID        = SymbolId_iterator;
+    private static final int Id_constructor = 1,
+            Id_toString = 2,
+            Id_get = 3,
+            Id_set = 4,
+            Id_subarray = 5,
+            SymbolId_iterator = 6;
 
-// #/string_id_map#
+    protected static final int MAX_PROTOTYPE_ID = SymbolId_iterator;
 
     // Constructor properties
 
     @Override
-    protected void fillConstructorProperties(IdFunctionObject ctor)
-    {
-        ctor.put("BYTES_PER_ELEMENT", ctor, ScriptRuntime.wrapInt(getBytesPerElement()));
+    protected void fillConstructorProperties(IdFunctionObject ctor) {
+        ctor.defineProperty(
+                "BYTES_PER_ELEMENT",
+                ScriptRuntime.wrapInt(getBytesPerElement()),
+                DONTENUM | PERMANENT | READONLY);
+
+        super.fillConstructorProperties(ctor);
     }
 
     // Property dispatcher
 
     @Override
-    protected int getMaxInstanceId()
-    {
+    protected int getMaxInstanceId() {
         return MAX_INSTANCE_ID;
     }
 
     @Override
-    protected String getInstanceIdName(int id)
-    {
+    protected String getInstanceIdName(int id) {
         switch (id) {
-        case Id_length: return "length";
-        case Id_BYTES_PER_ELEMENT: return "BYTES_PER_ELEMENT";
-        default: return super.getInstanceIdName(id);
+            case Id_length:
+                return "length";
+            case Id_BYTES_PER_ELEMENT:
+                return "BYTES_PER_ELEMENT";
+            default:
+                return super.getInstanceIdName(id);
         }
     }
 
     @Override
-    protected Object getInstanceIdValue(int id)
-    {
+    protected Object getInstanceIdValue(int id) {
         switch (id) {
-        case Id_length:
-            return ScriptRuntime.wrapInt(length);
-        case Id_BYTES_PER_ELEMENT:
-            return ScriptRuntime.wrapInt(getBytesPerElement());
-        default:
-            return super.getInstanceIdValue(id);
+            case Id_length:
+                return ScriptRuntime.wrapInt(length);
+            case Id_BYTES_PER_ELEMENT:
+                return ScriptRuntime.wrapInt(getBytesPerElement());
+            default:
+                return super.getInstanceIdValue(id);
         }
     }
 
-// #string_id_map#
-
     @Override
-    protected int findInstanceIdInfo(String s)
-    {
+    protected int findInstanceIdInfo(String s) {
         int id;
-// #generated# Last update: 2014-12-08 17:33:28 PST
-        L0: { id = 0; String X = null;
-            int s_length = s.length();
-            if (s_length==6) { X="length";id=Id_length; }
-            else if (s_length==17) { X="BYTES_PER_ELEMENT";id=Id_BYTES_PER_ELEMENT; }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-            break L0;
+        switch (s) {
+            case "length":
+                id = Id_length;
+                break;
+            case "BYTES_PER_ELEMENT":
+                id = Id_BYTES_PER_ELEMENT;
+                break;
+            default:
+                id = 0;
+                break;
         }
-// #/generated#
         if (id == 0) {
             return super.findInstanceIdInfo(s);
         }
+        if (id == Id_BYTES_PER_ELEMENT) {
+            return instanceIdInfo(DONTENUM | READONLY | PERMANENT, id);
+        }
         return instanceIdInfo(READONLY | PERMANENT, id);
     }
 
     /*
      * These must not conflict with ids in the parent since we delegate there for property dispatching.
      */
-    private static final int
-        Id_length               = 10,
-        Id_BYTES_PER_ELEMENT    = 11,
-        MAX_INSTANCE_ID         = Id_BYTES_PER_ELEMENT;
-
-// #/string_id_map#
+    private static final int Id_length = NativeArrayBufferView.MAX_INSTANCE_ID + 1,
+            Id_BYTES_PER_ELEMENT = Id_length + 1,
+            MAX_INSTANCE_ID = Id_BYTES_PER_ELEMENT;
 
     // External Array implementation
 
     @Override
-    public Object getArrayElement(int index)
-    {
+    public Object getArrayElement(int index) {
         return js_get(index);
     }
 
     @Override
-    public void setArrayElement(int index, Object value)
-    {
+    public void setArrayElement(int index, Object value) {
         js_set(index, value);
     }
 
@@ -448,26 +501,8 @@
     // Abstract List implementation
 
     @SuppressWarnings("unused")
-    public int size()
-    {
-        return length;
-    }
-
-    @SuppressWarnings("unused")
-    public boolean isEmpty()
-    {
-        return (length == 0);
-    }
-
-    @SuppressWarnings("unused")
-    public boolean contains(Object o)
-    {
-        return (indexOf(o) >= 0);
-    }
-
-    @SuppressWarnings("unused")
-    public boolean containsAll(Collection<?> objects)
-    {
+    @Override
+    public boolean containsAll(Collection<?> objects) {
         for (Object o : objects) {
             if (!contains(o)) {
                 return false;
@@ -477,8 +512,8 @@
     }
 
     @SuppressWarnings("unused")
-    public int indexOf(Object o)
-    {
+    @Override
+    public int indexOf(Object o) {
         for (int i = 0; i < length; i++) {
             if (o.equals(js_get(i))) {
                 return i;
@@ -488,8 +523,8 @@
     }
 
     @SuppressWarnings("unused")
-    public int lastIndexOf(Object o)
-    {
+    @Override
+    public int lastIndexOf(Object o) {
         for (int i = length - 1; i >= 0; i--) {
             if (o.equals(js_get(i))) {
                 return i;
@@ -499,8 +534,8 @@
     }
 
     @SuppressWarnings("unused")
-    public Object[] toArray()
-    {
+    @Override
+    public Object[] toArray() {
         Object[] a = new Object[length];
         for (int i = 0; i < length; i++) {
             a[i] = js_get(i);
@@ -508,20 +543,20 @@
         return a;
     }
 
-    @SuppressWarnings("unused")
-    public <U> U[] toArray(U[] ts)
-    {
+    @SuppressWarnings({"unused", "unchecked"})
+    @Override
+    public <U> U[] toArray(U[] ts) {
         U[] a;
 
         if (ts.length >= length) {
             a = ts;
         } else {
-            a = (U[])Array.newInstance(ts.getClass().getComponentType(), length);
+            a = (U[]) Array.newInstance(ts.getClass().getComponentType(), length);
         }
 
         for (int i = 0; i < length; i++) {
             try {
-                a[i] = (U)js_get(i);
+                a[i] = (U) js_get(i);
             } catch (ClassCastException cce) {
                 throw new ArrayStoreException();
             }
@@ -529,11 +564,32 @@
         return a;
     }
 
+    @SuppressWarnings("unused")
+    @Override
+    public int size() {
+        return length;
+    }
+
+    @SuppressWarnings("unused")
     @Override
-    public boolean equals(Object o)
-    {
+    public boolean isEmpty() {
+        return (length == 0);
+    }
+
+    @SuppressWarnings("unused")
+    @Override
+    public boolean contains(Object o) {
+        return (indexOf(o) >= 0);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public boolean equals(Object o) {
+        if (o == null) {
+            return false;
+        }
         try {
-            NativeTypedArrayView<T> v = (NativeTypedArrayView<T>)o;
+            NativeTypedArrayView<T> v = (NativeTypedArrayView<T>) o;
             if (length != v.length) {
                 return false;
             }
@@ -549,30 +605,29 @@
     }
 
     @Override
-    public int hashCode()
-    {
+    public int hashCode() {
         int hc = 0;
         for (int i = 0; i < length; i++) {
             hc += js_get(i).hashCode();
         }
-        return 0;
+        return hc;
     }
 
     @SuppressWarnings("unused")
-    public Iterator<T> iterator()
-    {
+    @Override
+    public Iterator<T> iterator() {
         return new NativeTypedArrayIterator<T>(this, 0);
     }
 
     @SuppressWarnings("unused")
-    public ListIterator<T> listIterator()
-    {
+    @Override
+    public ListIterator<T> listIterator() {
         return new NativeTypedArrayIterator<T>(this, 0);
     }
 
     @SuppressWarnings("unused")
-    public ListIterator<T> listIterator(int start)
-    {
+    @Override
+    public ListIterator<T> listIterator(int start) {
         if (checkIndex(start)) {
             throw new IndexOutOfBoundsException();
         }
@@ -580,62 +635,62 @@
     }
 
     @SuppressWarnings("unused")
-    public List<T> subList(int i, int i2)
-    {
+    @Override
+    public List<T> subList(int i, int i2) {
         throw new UnsupportedOperationException();
     }
 
     @SuppressWarnings("unused")
-    public boolean add(T aByte)
-    {
+    @Override
+    public boolean add(T aByte) {
         throw new UnsupportedOperationException();
     }
 
     @SuppressWarnings("unused")
-    public void add(int i, T aByte)
-    {
+    @Override
+    public void add(int i, T aByte) {
         throw new UnsupportedOperationException();
     }
 
     @SuppressWarnings("unused")
-    public boolean addAll(Collection<? extends T> bytes)
-    {
+    @Override
+    public boolean addAll(Collection<? extends T> bytes) {
         throw new UnsupportedOperationException();
     }
 
     @SuppressWarnings("unused")
-    public boolean addAll(int i, Collection<? extends T> bytes)
-    {
+    @Override
+    public boolean addAll(int i, Collection<? extends T> bytes) {
         throw new UnsupportedOperationException();
     }
 
     @SuppressWarnings("unused")
-    public void clear()
-    {
+    @Override
+    public void clear() {
         throw new UnsupportedOperationException();
     }
 
     @SuppressWarnings("unused")
-    public T remove(int i)
-    {
+    @Override
+    public T remove(int i) {
         throw new UnsupportedOperationException();
     }
 
     @SuppressWarnings("unused")
-    public boolean remove(Object o)
-    {
+    @Override
+    public boolean remove(Object o) {
         throw new UnsupportedOperationException();
     }
 
     @SuppressWarnings("unused")
-    public boolean removeAll(Collection<?> objects)
-    {
+    @Override
+    public boolean removeAll(Collection<?> objects) {
         throw new UnsupportedOperationException();
     }
 
     @SuppressWarnings("unused")
-    public boolean retainAll(Collection<?> objects)
-    {
+    @Override
+    public boolean retainAll(Collection<?> objects) {
         throw new UnsupportedOperationException();
     }
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeUint16Array.java rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeUint16Array.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeUint16Array.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeUint16Array.java	2022-01-06 22:57:21.000000000 +0100
@@ -13,7 +13,7 @@
 
 /**
  * An array view that stores 16-bit quantities and implements the JavaScript "Uint16Array" interface.
- * It also implements List<Integer> for direct manipulation in Java.
+ * It also implements List<Integer> for direct manipulation in Java.
  */
 
 public class NativeUint16Array
@@ -35,7 +35,7 @@
 
     public NativeUint16Array(int len)
     {
-        this(new NativeArrayBuffer(len * BYTES_PER_ELEMENT), 0, len);
+        this(new NativeArrayBuffer((double)len * BYTES_PER_ELEMENT), 0, len);
     }
 
     @Override
@@ -51,7 +51,7 @@
     }
 
     @Override
-    protected NativeTypedArrayView construct(NativeArrayBuffer ab, int off, int len)
+    protected NativeUint16Array construct(NativeArrayBuffer ab, int off, int len)
     {
         return new NativeUint16Array(ab, off, len);
     }
@@ -63,12 +63,9 @@
     }
 
     @Override
-    protected NativeTypedArrayView realThis(Scriptable thisObj, IdFunctionObject f)
+    protected NativeUint16Array realThis(Scriptable thisObj, IdFunctionObject f)
     {
-        if (!(thisObj instanceof NativeUint16Array)) {
-            throw incompatibleCallError(f);
-        }
-        return (NativeUint16Array)thisObj;
+        return ensureType(thisObj, NativeUint16Array.class, f);
     }
 
     @Override
@@ -77,7 +74,7 @@
         if (checkIndex(index)) {
             return Undefined.instance;
         }
-        return ByteIo.readUint16(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, false);
+        return ByteIo.readUint16(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, useLittleEndian());
     }
 
     @Override
@@ -87,7 +84,7 @@
             return Undefined.instance;
         }
         int val = Conversions.toUint16(c);
-        ByteIo.writeUint16(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, val, false);
+        ByteIo.writeUint16(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, val, useLittleEndian());
         return null;
     }
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeUint32Array.java rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeUint32Array.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeUint32Array.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeUint32Array.java	2022-01-06 22:57:21.000000000 +0100
@@ -13,7 +13,7 @@
 
 /**
  * An array view that stores 32-bit quantities and implements the JavaScript "Uint32Array" interface.
- * It also implements List<Long> for direct manipulation in Java.
+ * It also implements List<Long> for direct manipulation in Java.
  */
 
 public class NativeUint32Array
@@ -35,7 +35,7 @@
 
     public NativeUint32Array(int len)
     {
-        this(new NativeArrayBuffer(len * BYTES_PER_ELEMENT), 0, len);
+        this(new NativeArrayBuffer((double)len * BYTES_PER_ELEMENT), 0, len);
     }
 
     @Override
@@ -51,7 +51,7 @@
     }
 
     @Override
-    protected NativeTypedArrayView construct(NativeArrayBuffer ab, int off, int len)
+    protected NativeUint32Array construct(NativeArrayBuffer ab, int off, int len)
     {
         return new NativeUint32Array(ab, off, len);
     }
@@ -63,12 +63,9 @@
     }
 
     @Override
-    protected NativeTypedArrayView realThis(Scriptable thisObj, IdFunctionObject f)
+    protected NativeUint32Array realThis(Scriptable thisObj, IdFunctionObject f)
     {
-        if (!(thisObj instanceof NativeUint32Array)) {
-            throw incompatibleCallError(f);
-        }
-        return (NativeUint32Array)thisObj;
+        return ensureType(thisObj, NativeUint32Array.class, f);
     }
 
     @Override
@@ -77,7 +74,7 @@
         if (checkIndex(index)) {
             return Undefined.instance;
         }
-        return ByteIo.readUint32(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, false);
+        return ByteIo.readUint32(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, useLittleEndian());
     }
 
     @Override
@@ -87,7 +84,7 @@
             return Undefined.instance;
         }
         long val = Conversions.toUint32(c);
-        ByteIo.writeUint32(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, val, false);
+        ByteIo.writeUint32(arrayBuffer.buffer, (index * BYTES_PER_ELEMENT) + offset, val, useLittleEndian());
         return null;
     }
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeUint8Array.java rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeUint8Array.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeUint8Array.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeUint8Array.java	2022-01-06 22:57:21.000000000 +0100
@@ -13,7 +13,7 @@
 
 /**
  * An array view that stores 8-bit quantities and implements the JavaScript "Uint8Array" interface.
- * It also implements List<Integer> for direct manipulation in Java.
+ * It also implements List<Integer> for direct manipulation in Java.
  */
 
 public class NativeUint8Array
@@ -50,7 +50,7 @@
     }
 
     @Override
-    protected NativeTypedArrayView construct(NativeArrayBuffer ab, int off, int len)
+    protected NativeUint8Array construct(NativeArrayBuffer ab, int off, int len)
     {
         return new NativeUint8Array(ab, off, len);
     }
@@ -62,12 +62,9 @@
     }
 
     @Override
-    protected NativeTypedArrayView realThis(Scriptable thisObj, IdFunctionObject f)
+    protected NativeUint8Array realThis(Scriptable thisObj, IdFunctionObject f)
     {
-        if (!(thisObj instanceof NativeUint8Array)) {
-            throw incompatibleCallError(f);
-        }
-        return (NativeUint8Array)thisObj;
+        return ensureType(thisObj, NativeUint8Array.class, f);
     }
 
     @Override
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeUint8ClampedArray.java rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeUint8ClampedArray.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeUint8ClampedArray.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeUint8ClampedArray.java	2022-01-06 22:57:21.000000000 +0100
@@ -13,8 +13,8 @@
 
 /**
  * An array view that stores 8-bit quantities and implements the JavaScript "Uint8ClampedArray" interface.
- * It also implements List<Integer> for direct manipulation in Java. Bytes inserted that fall out of the range
- * (0 <= X < 256) will be adjusted so that they match before insertion.
+ * It also implements List<Integer> for direct manipulation in Java. Bytes inserted that fall out of the range
+ * (0 <= X < 256) will be adjusted so that they match before insertion.
  */
 
 public class NativeUint8ClampedArray
@@ -51,7 +51,7 @@
     }
 
     @Override
-    protected NativeTypedArrayView construct(NativeArrayBuffer ab, int off, int len)
+    protected NativeUint8ClampedArray construct(NativeArrayBuffer ab, int off, int len)
     {
         return new NativeUint8ClampedArray(ab, off, len);
     }
@@ -63,12 +63,9 @@
     }
 
     @Override
-    protected NativeTypedArrayView realThis(Scriptable thisObj, IdFunctionObject f)
+    protected NativeUint8ClampedArray realThis(Scriptable thisObj, IdFunctionObject f)
     {
-        if (!(thisObj instanceof NativeUint8ClampedArray)) {
-            throw incompatibleCallError(f);
-        }
-        return (NativeUint8ClampedArray)thisObj;
+        return ensureType(thisObj, NativeUint8ClampedArray.class, f);
     }
 
     @Override
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/UintMap.java rhino-1.7.14/src/org/mozilla/javascript/UintMap.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/UintMap.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/UintMap.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,27 +6,24 @@
 
 package org.mozilla.javascript;
 
-import java.io.Serializable;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
+import java.io.Serializable;
 
 /**
- * Map to associate non-negative integers to objects or integers.
- * The map does not synchronize any of its operation, so either use
- * it from a single thread or do own synchronization or perform all mutation
- * operations on one thread before passing the map to others.
+ * Map to associate non-negative integers to objects or integers. The map does not synchronize any
+ * of its operation, so either use it from a single thread or do own synchronization or perform all
+ * mutation operations on one thread before passing the map to others.
  *
  * @author Igor Bukanov
- *
  */
+ at SuppressWarnings("unused")
+public class UintMap implements Serializable {
+    private static final long serialVersionUID = 4242698212885848444L;
 
-public class UintMap implements Serializable
-{
-    static final long serialVersionUID = 4242698212885848444L;
-
-// Map implementation via hashtable,
-// follows "The Art of Computer Programming" by Donald E. Knuth
+    // Map implementation via hashtable,
+    // follows "The Art of Computer Programming" by Donald E. Knuth
 
     public UintMap() {
         this(4);
@@ -37,7 +34,7 @@
         // Table grow when number of stored keys >= 3/4 of max capacity
         int minimalCapacity = initialCapacity * 4 / 3;
         int i;
-        for (i = 2; (1 << i) < minimalCapacity; ++i) { }
+        for (i = 2; (1 << i) < minimalCapacity; ++i) {}
         power = i;
         if (check && power < 2) Kit.codeBug();
     }
@@ -57,6 +54,7 @@
 
     /**
      * Get object value assigned with key.
+     *
      * @return key object value or null if key is absent
      */
     public Object getObject(int key) {
@@ -72,6 +70,7 @@
 
     /**
      * Get integer value assigned with key.
+     *
      * @return key integer value or defaultValue if key is absent
      */
     public int getInt(int key, int defaultValue) {
@@ -88,8 +87,8 @@
 
     /**
      * Get integer value assigned with key.
-     * @return key integer value or defaultValue if key does not exist or does
-     * not have int value
+     *
+     * @return key integer value or defaultValue if key does not exist or does not have int value
      * @throws RuntimeException if key does not exist
      */
     public int getExistingInt(int key) {
@@ -106,10 +105,7 @@
         return 0;
     }
 
-    /**
-     * Set object value of the key.
-     * If key does not exist, also set its int value to 0.
-     */
+    /** Set object value of the key. If key does not exist, also set its int value to 0. */
     public void put(int key, Object value) {
         if (key < 0) Kit.codeBug();
         int index = ensureIndex(key, false);
@@ -119,10 +115,7 @@
         values[index] = value;
     }
 
-    /**
-     * Set int value of the key.
-     * If key does not exist, also set its object value to null.
-     */
+    /** Set int value of the key. If key does not exist, also set its object value to null. */
     public void put(int key, int value) {
         if (key < 0) Kit.codeBug();
         int index = ensureIndex(key, true);
@@ -147,8 +140,12 @@
             --keyCount;
             // Allow to GC value and make sure that new key with the deleted
             // slot shall get proper default values
-            if (values != null) { values[index] = null; }
-            if (ivaluesShift != 0) { keys[ivaluesShift + index] = 0; }
+            if (values != null) {
+                values[index] = null;
+            }
+            if (ivaluesShift != 0) {
+                keys[ivaluesShift + index] = 0;
+            }
         }
     }
 
@@ -188,9 +185,7 @@
         if (shift >= 0) {
             return ((fraction >>> shift) & mask) | 1;
         }
-        else {
-            return (fraction & (mask >>> -shift)) | 1;
-        }
+        return (fraction & (mask >>> -shift)) | 1;
     }
 
     private int findIndex(int key) {
@@ -199,7 +194,9 @@
             int fraction = key * A;
             int index = fraction >>> (32 - power);
             int entry = keys[index];
-            if (entry == key) { return index; }
+            if (entry == key) {
+                return index;
+            }
             if (entry != EMPTY) {
                 // Search in table after first failed attempt
                 int mask = (1 << power) - 1;
@@ -212,15 +209,17 @@
                     }
                     index = (index + step) & mask;
                     entry = keys[index];
-                    if (entry == key) { return index; }
+                    if (entry == key) {
+                        return index;
+                    }
                 } while (entry != EMPTY);
             }
         }
         return -1;
     }
 
-// Insert key that is not present to table without deleted entries
-// and enough free space
+    // Insert key that is not present to table without deleted entries
+    // and enough free space
     private int insertNewKey(int key) {
         if (check && occupiedCount != keyCount) Kit.codeBug();
         if (check && keyCount == 1 << power) Kit.codeBug();
@@ -256,14 +255,18 @@
         int oldShift = ivaluesShift;
         if (oldShift == 0 && !ensureIntSpace) {
             keys = new int[N];
+        } else {
+            ivaluesShift = N;
+            keys = new int[N * 2];
         }
-        else {
-            ivaluesShift = N; keys = new int[N * 2];
+        for (int i = 0; i != N; ++i) {
+            keys[i] = EMPTY;
         }
-        for (int i = 0; i != N; ++i) { keys[i] = EMPTY; }
 
         Object[] oldValues = values;
-        if (oldValues != null) { values = new Object[N]; }
+        if (oldValues != null) {
+            values = new Object[N];
+        }
 
         int oldCount = keyCount;
         occupiedCount = 0;
@@ -285,7 +288,7 @@
         }
     }
 
-// Ensure key index creating one if necessary
+    // Ensure key index creating one if necessary
     private int ensureIndex(int key, boolean intType) {
         int index = -1;
         int firstDeleted = -1;
@@ -294,9 +297,13 @@
             int fraction = key * A;
             index = fraction >>> (32 - power);
             int entry = keys[index];
-            if (entry == key) { return index; }
+            if (entry == key) {
+                return index;
+            }
             if (entry != EMPTY) {
-                if (entry == DELETED) { firstDeleted = index; }
+                if (entry == DELETED) {
+                    firstDeleted = index;
+                }
                 // Search in table after first failed attempt
                 int mask = (1 << power) - 1;
                 int step = tableLookupStep(fraction, mask, power);
@@ -308,7 +315,9 @@
                     }
                     index = (index + step) & mask;
                     entry = keys[index];
-                    if (entry == key) { return index; }
+                    if (entry == key) {
+                        return index;
+                    }
                     if (entry == DELETED && firstDeleted < 0) {
                         firstDeleted = index;
                     }
@@ -316,12 +325,10 @@
             }
         }
         // Inserting of new key
-        if (check && keys != null && keys[index] != EMPTY)
-            Kit.codeBug();
+        if (check && keys != null && keys[index] != EMPTY) Kit.codeBug();
         if (firstDeleted >= 0) {
             index = firstDeleted;
-        }
-        else {
+        } else {
             // Need to consume empty entry: check occupation level
             if (keys == null || occupiedCount * 4 >= (1 << power) * 3) {
                 // Too litle unused entries: rehash
@@ -335,9 +342,7 @@
         return index;
     }
 
-    private void writeObject(ObjectOutputStream out)
-        throws IOException
-    {
+    private void writeObject(ObjectOutputStream out) throws IOException {
         out.defaultWriteObject();
 
         int count = keyCount;
@@ -363,9 +368,7 @@
         }
     }
 
-    private void readObject(ObjectInputStream in)
-        throws IOException, ClassNotFoundException
-    {
+    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
         in.defaultReadObject();
 
         int writtenKeyCount = keyCount;
@@ -378,7 +381,7 @@
             if (hasIntValues) {
                 keys = new int[2 * N];
                 ivaluesShift = N;
-            }else {
+            } else {
                 keys = new int[N];
             }
             for (int i = 0; i != N; ++i) {
@@ -401,17 +404,17 @@
         }
     }
 
-// A == golden_ratio * (1 << 32) = ((sqrt(5) - 1) / 2) * (1 << 32)
-// See Knuth etc.
+    // A == golden_ratio * (1 << 32) = ((sqrt(5) - 1) / 2) * (1 << 32)
+    // See Knuth etc.
     private static final int A = 0x9e3779b9;
 
     private static final int EMPTY = -1;
     private static final int DELETED = -2;
 
-// Structure of kyes and values arrays (N == 1 << power):
-// keys[0 <= i < N]: key value or EMPTY or DELETED mark
-// values[0 <= i < N]: value of key at keys[i]
-// keys[N <= i < 2N]: int values of keys at keys[i - N]
+    // Structure of kyes and values arrays (N == 1 << power):
+    // keys[0 <= i < N]: key value or EMPTY or DELETED mark
+    // values[0 <= i < N]: value of key at keys[i]
+    // keys[N <= i < 2N]: int values of keys at keys[i - N]
 
     private transient int[] keys;
     private transient Object[] values;
@@ -424,203 +427,203 @@
     // values associated with keys
     private transient int ivaluesShift;
 
-// If true, enables consitency checks
+    // If true, enables consitency checks
     private static final boolean check = false;
 
-/* TEST START
-
-    public static void main(String[] args) {
-        if (!check) {
-            System.err.println("Set check to true and re-run");
-            throw new RuntimeException("Set check to true and re-run");
-        }
-
-        UintMap map;
-        map = new UintMap();
-        testHash(map, 2);
-        map = new UintMap();
-        testHash(map, 10 * 1000);
-        map = new UintMap(30 * 1000);
-        testHash(map, 10 * 100);
-        map.clear();
-        testHash(map, 4);
-        map = new UintMap(0);
-        testHash(map, 10 * 100);
-    }
+    /* TEST START
 
-    private static void testHash(UintMap map, int N) {
-        System.out.print("."); System.out.flush();
-        for (int i = 0; i != N; ++i) {
-            map.put(i, i);
-            check(i == map.getInt(i, -1));
+        public static void main(String[] args) {
+            if (!check) {
+                System.err.println("Set check to true and re-run");
+                throw new RuntimeException("Set check to true and re-run");
+            }
+
+            UintMap map;
+            map = new UintMap();
+            testHash(map, 2);
+            map = new UintMap();
+            testHash(map, 10 * 1000);
+            map = new UintMap(30 * 1000);
+            testHash(map, 10 * 100);
+            map.clear();
+            testHash(map, 4);
+            map = new UintMap(0);
+            testHash(map, 10 * 100);
         }
 
-        System.out.print("."); System.out.flush();
-        for (int i = 0; i != N; ++i) {
-            map.put(i, i);
-            check(i == map.getInt(i, -1));
-        }
+        private static void testHash(UintMap map, int N) {
+            System.out.print("."); System.out.flush();
+            for (int i = 0; i != N; ++i) {
+                map.put(i, i);
+                check(i == map.getInt(i, -1));
+            }
 
-        System.out.print("."); System.out.flush();
-        for (int i = 0; i != N; ++i) {
-            map.put(i, new Integer(i));
-            check(-1 == map.getInt(i, -1));
-            Integer obj = (Integer)map.getObject(i);
-            check(obj != null && i == obj.intValue());
-        }
+            System.out.print("."); System.out.flush();
+            for (int i = 0; i != N; ++i) {
+                map.put(i, i);
+                check(i == map.getInt(i, -1));
+            }
 
-        check(map.size() == N);
+            System.out.print("."); System.out.flush();
+            for (int i = 0; i != N; ++i) {
+                map.put(i, Integer.valueOf(i));
+                check(-1 == map.getInt(i, -1));
+                Integer obj = (Integer)map.getObject(i);
+                check(obj != null && i == obj.intValue());
+            }
 
-        System.out.print("."); System.out.flush();
-        int[] keys = map.getKeys();
-        check(keys.length == N);
-        for (int i = 0; i != N; ++i) {
-            int key = keys[i];
-            check(map.has(key));
-            check(!map.isIntType(key));
-            check(map.isObjectType(key));
-            Integer obj = (Integer) map.getObject(key);
-            check(obj != null && key == obj.intValue());
-        }
+            check(map.size() == N);
 
+            System.out.print("."); System.out.flush();
+            int[] keys = map.getKeys();
+            check(keys.length == N);
+            for (int i = 0; i != N; ++i) {
+                int key = keys[i];
+                check(map.has(key));
+                check(!map.isIntType(key));
+                check(map.isObjectType(key));
+                Integer obj = (Integer) map.getObject(key);
+                check(obj != null && key == obj.intValue());
+            }
 
-        System.out.print("."); System.out.flush();
-        for (int i = 0; i != N; ++i) {
-            check(-1 == map.getInt(i, -1));
-        }
 
-        System.out.print("."); System.out.flush();
-        for (int i = 0; i != N; ++i) {
-            map.put(i * i, i);
-            check(i == map.getInt(i * i, -1));
-        }
+            System.out.print("."); System.out.flush();
+            for (int i = 0; i != N; ++i) {
+                check(-1 == map.getInt(i, -1));
+            }
 
-        System.out.print("."); System.out.flush();
-        for (int i = 0; i != N; ++i) {
-            check(i == map.getInt(i * i, -1));
-        }
+            System.out.print("."); System.out.flush();
+            for (int i = 0; i != N; ++i) {
+                map.put(i * i, i);
+                check(i == map.getInt(i * i, -1));
+            }
 
-        System.out.print("."); System.out.flush();
-        for (int i = 0; i != N; ++i) {
-            map.put(i * i, new Integer(i));
-            check(-1 == map.getInt(i * i, -1));
-            map.remove(i * i);
-            check(!map.has(i * i));
-            map.put(i * i, i);
-            check(map.isIntType(i * i));
-            check(null == map.getObject(i * i));
-            map.remove(i * i);
-            check(!map.isObjectType(i * i));
-            check(!map.isIntType(i * i));
-        }
+            System.out.print("."); System.out.flush();
+            for (int i = 0; i != N; ++i) {
+                check(i == map.getInt(i * i, -1));
+            }
 
-        int old_size = map.size();
-        for (int i = 0; i != N; ++i) {
-            map.remove(i * i);
-            check(map.size() == old_size);
-        }
+            System.out.print("."); System.out.flush();
+            for (int i = 0; i != N; ++i) {
+                map.put(i * i, Integer.valueOf(i));
+                check(-1 == map.getInt(i * i, -1));
+                map.remove(i * i);
+                check(!map.has(i * i));
+                map.put(i * i, i);
+                check(map.isIntType(i * i));
+                check(null == map.getObject(i * i));
+                map.remove(i * i);
+                check(!map.isObjectType(i * i));
+                check(!map.isIntType(i * i));
+            }
 
-        System.out.print("."); System.out.flush();
-        map.clear();
-        check(map.size() == 0);
-        for (int i = 0; i != N; ++i) {
-            map.put(i * i, i);
-            map.put(i * i + 1, new Double(i+0.5));
-        }
-        checkSameMaps(map, (UintMap)writeAndRead(map));
+            int old_size = map.size();
+            for (int i = 0; i != N; ++i) {
+                map.remove(i * i);
+                check(map.size() == old_size);
+            }
 
-        System.out.print("."); System.out.flush();
-        map = new UintMap(0);
-        checkSameMaps(map, (UintMap)writeAndRead(map));
-        map = new UintMap(1);
-        checkSameMaps(map, (UintMap)writeAndRead(map));
-        map = new UintMap(1000);
-        checkSameMaps(map, (UintMap)writeAndRead(map));
+            System.out.print("."); System.out.flush();
+            map.clear();
+            check(map.size() == 0);
+            for (int i = 0; i != N; ++i) {
+                map.put(i * i, i);
+                map.put(i * i + 1, Double.valueOf(i+0.5));
+            }
+            checkSameMaps(map, (UintMap)writeAndRead(map));
 
-        System.out.print("."); System.out.flush();
-        map = new UintMap(N / 10);
-        for (int i = 0; i != N; ++i) {
-            map.put(2*i+1, i);
-        }
-        checkSameMaps(map, (UintMap)writeAndRead(map));
+            System.out.print("."); System.out.flush();
+            map = new UintMap(0);
+            checkSameMaps(map, (UintMap)writeAndRead(map));
+            map = new UintMap(1);
+            checkSameMaps(map, (UintMap)writeAndRead(map));
+            map = new UintMap(1000);
+            checkSameMaps(map, (UintMap)writeAndRead(map));
 
-        System.out.print("."); System.out.flush();
-        map = new UintMap(N / 10);
-        for (int i = 0; i != N; ++i) {
-            map.put(2*i+1, i);
-        }
-        for (int i = 0; i != N / 2; ++i) {
-            map.remove(2*i+1);
-        }
-        checkSameMaps(map, (UintMap)writeAndRead(map));
+            System.out.print("."); System.out.flush();
+            map = new UintMap(N / 10);
+            for (int i = 0; i != N; ++i) {
+                map.put(2*i+1, i);
+            }
+            checkSameMaps(map, (UintMap)writeAndRead(map));
+
+            System.out.print("."); System.out.flush();
+            map = new UintMap(N / 10);
+            for (int i = 0; i != N; ++i) {
+                map.put(2*i+1, i);
+            }
+            for (int i = 0; i != N / 2; ++i) {
+                map.remove(2*i+1);
+            }
+            checkSameMaps(map, (UintMap)writeAndRead(map));
+
+            System.out.print("."); System.out.flush();
+            map = new UintMap();
+            for (int i = 0; i != N; ++i) {
+                map.put(2*i+1, Double.valueOf(i + 10));
+            }
+            for (int i = 0; i != N / 2; ++i) {
+                map.remove(2*i+1);
+            }
+            checkSameMaps(map, (UintMap)writeAndRead(map));
+
+            System.out.println(); System.out.flush();
 
-        System.out.print("."); System.out.flush();
-        map = new UintMap();
-        for (int i = 0; i != N; ++i) {
-            map.put(2*i+1, new Double(i + 10));
-        }
-        for (int i = 0; i != N / 2; ++i) {
-            map.remove(2*i+1);
         }
-        checkSameMaps(map, (UintMap)writeAndRead(map));
 
-        System.out.println(); System.out.flush();
+        private static void checkSameMaps(UintMap map1, UintMap map2) {
+            check(map1.size() == map2.size());
+            int[] keys = map1.getKeys();
+            check(keys.length == map1.size());
+            for (int i = 0; i != keys.length; ++i) {
+                int key = keys[i];
+                check(map2.has(key));
+                check(map1.isObjectType(key) == map2.isObjectType(key));
+                check(map1.isIntType(key) == map2.isIntType(key));
+                Object o1 = map1.getObject(key);
+                Object o2 = map2.getObject(key);
+                if (map1.isObjectType(key)) {
+                    check(o1.equals(o2));
+                }else {
+                    check(map1.getObject(key) == null);
+                    check(map2.getObject(key) == null);
+                }
+                if (map1.isIntType(key)) {
+                    check(map1.getExistingInt(key) == map2.getExistingInt(key));
+                }else {
+                    check(map1.getInt(key, -10) == -10);
+                    check(map1.getInt(key, -11) == -11);
+                    check(map2.getInt(key, -10) == -10);
+                    check(map2.getInt(key, -11) == -11);
+                }
+            }
+        }
 
-    }
+        private static void check(boolean condition) {
+            if (!condition) Kit.codeBug();
+        }
 
-    private static void checkSameMaps(UintMap map1, UintMap map2) {
-        check(map1.size() == map2.size());
-        int[] keys = map1.getKeys();
-        check(keys.length == map1.size());
-        for (int i = 0; i != keys.length; ++i) {
-            int key = keys[i];
-            check(map2.has(key));
-            check(map1.isObjectType(key) == map2.isObjectType(key));
-            check(map1.isIntType(key) == map2.isIntType(key));
-            Object o1 = map1.getObject(key);
-            Object o2 = map2.getObject(key);
-            if (map1.isObjectType(key)) {
-                check(o1.equals(o2));
-            }else {
-                check(map1.getObject(key) == null);
-                check(map2.getObject(key) == null);
-            }
-            if (map1.isIntType(key)) {
-                check(map1.getExistingInt(key) == map2.getExistingInt(key));
-            }else {
-                check(map1.getInt(key, -10) == -10);
-                check(map1.getInt(key, -11) == -11);
-                check(map2.getInt(key, -10) == -10);
-                check(map2.getInt(key, -11) == -11);
-            }
-        }
-    }
-
-    private static void check(boolean condition) {
-        if (!condition) Kit.codeBug();
-    }
-
-    private static Object writeAndRead(Object obj) {
-        try {
-            java.io.ByteArrayOutputStream
-                bos = new java.io.ByteArrayOutputStream();
-            java.io.ObjectOutputStream
-                out = new java.io.ObjectOutputStream(bos);
-            out.writeObject(obj);
-            out.close();
-            byte[] data = bos.toByteArray();
-            java.io.ByteArrayInputStream
-                bis = new java.io.ByteArrayInputStream(data);
-            java.io.ObjectInputStream
-                in = new java.io.ObjectInputStream(bis);
-            Object result = in.readObject();
-            in.close();
-            return result;
-        }catch (Exception ex) {
-            ex.printStackTrace();
-            throw new RuntimeException("Unexpected");
+        private static Object writeAndRead(Object obj) {
+            try {
+                java.io.ByteArrayOutputStream
+                    bos = new java.io.ByteArrayOutputStream();
+                java.io.ObjectOutputStream
+                    out = new java.io.ObjectOutputStream(bos);
+                out.writeObject(obj);
+                out.close();
+                byte[] data = bos.toByteArray();
+                java.io.ByteArrayInputStream
+                    bis = new java.io.ByteArrayInputStream(data);
+                java.io.ObjectInputStream
+                    in = new java.io.ObjectInputStream(bis);
+                Object result = in.readObject();
+                in.close();
+                return result;
+            }catch (Exception ex) {
+                ex.printStackTrace();
+                throw new RuntimeException("Unexpected");
+            }
         }
-    }
 
-// TEST END */
+    // TEST END */
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/Undefined.java rhino-1.7.14/src/org/mozilla/javascript/Undefined.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/Undefined.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/Undefined.java	2022-01-06 22:57:21.000000000 +0100
@@ -7,25 +7,31 @@
 package org.mozilla.javascript;
 
 import java.io.Serializable;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
 
 /**
  * This class implements the Undefined value in JavaScript.
+ *
+ * <p>We represent "undefined" internally using two static objects -- "Undefined.instance" and
+ * SCRIPTABLE_UNDEFINED. Java code that needs to make something undefined should generally use the
+ * first, and use the second if a Scriptable object is absolutely required.
+ *
+ * <p>Java code that needs to test whether something is undefined <b>must</b> use the "isUndefined"
+ * method because of the multiple internal representations.
  */
-public class Undefined implements Serializable
-{
-    static final long serialVersionUID = 9195680630202616767L;
+public class Undefined implements Serializable {
+    private static final long serialVersionUID = 9195680630202616767L;
 
+    /**
+     * This is the standard value for "undefined" in Rhino. Java code that needs to represent
+     * "undefined" should use this object (rather than a new instance of this class).
+     */
     public static final Object instance = new Undefined();
 
-    private Undefined()
-    {
-    }
+    private static final int instanceHash = System.identityHashCode(instance);
+
+    private Undefined() {}
 
-    public Object readResolve()
-    {
+    public Object readResolve() {
         return instance;
     }
 
@@ -37,26 +43,109 @@
     @Override
     public int hashCode() {
         // All instances of Undefined are equivalent!
-        return 0;
+        return instanceHash;
     }
 
-    public static final Scriptable SCRIPTABLE_UNDEFINED;
+    /**
+     * An alternate representation of undefined, to be used only when we need to pass it to a method
+     * that takes as Scriptable as a parameter. This is used when we need to pass undefined as the
+     * "this" parmeter of a Callable instance, because we cannot change that interface without
+     * breaking backward compatibility.
+     */
+    public static final Scriptable SCRIPTABLE_UNDEFINED =
+            new Scriptable() {
+                @Override
+                public String getClassName() {
+                    return "undefined";
+                }
 
-    static {
-        SCRIPTABLE_UNDEFINED = (Scriptable) Proxy.newProxyInstance(Undefined.class.getClassLoader(), new Class[]{Scriptable.class}, new InvocationHandler() {
-            @Override
-            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-                if (method.getName().equals("toString")) return "undefined";
-                if (method.getName().equals("equals")) {
-                    return args.length > 0 && isUndefined(args[0]);
-                }
-                throw new UnsupportedOperationException("undefined doesn't support " + method.getName());
-            }
-        });
-    }
+                @Override
+                public Object get(String name, Scriptable start) {
+                    return NOT_FOUND;
+                }
+
+                @Override
+                public Object get(int index, Scriptable start) {
+                    return NOT_FOUND;
+                }
+
+                @Override
+                public boolean has(String name, Scriptable start) {
+                    return false;
+                }
+
+                @Override
+                public boolean has(int index, Scriptable start) {
+                    return false;
+                }
+
+                @Override
+                public void put(String name, Scriptable start, Object value) {}
+
+                @Override
+                public void put(int index, Scriptable start, Object value) {}
+
+                @Override
+                public void delete(String name) {}
+
+                @Override
+                public void delete(int index) {}
+
+                @Override
+                public Scriptable getPrototype() {
+                    return null;
+                }
+
+                @Override
+                public void setPrototype(Scriptable prototype) {}
+
+                @Override
+                public Scriptable getParentScope() {
+                    return null;
+                }
+
+                @Override
+                public void setParentScope(Scriptable parent) {}
+
+                @Override
+                public Object[] getIds() {
+                    return ScriptRuntime.emptyArgs;
+                }
+
+                @Override
+                public Object getDefaultValue(Class<?> hint) {
+                    if (hint == null || hint == ScriptRuntime.StringClass) {
+                        return toString();
+                    }
+                    return null;
+                }
+
+                @Override
+                public boolean hasInstance(Scriptable instance) {
+                    return false;
+                }
+
+                @Override
+                public String toString() {
+                    return "undefined";
+                }
+
+                @Override
+                public boolean equals(Object obj) {
+                    return isUndefined(obj) || super.equals(obj);
+                }
+
+                @Override
+                public int hashCode() {
+                    return instanceHash;
+                }
+            };
 
-    public static boolean isUndefined(Object obj)
-    {
+    /**
+     * Safely test whether "obj" is undefined. Java code must use this function rather than testing
+     * the value directly since we have two representations of undefined in Rhino.
+     */
+    public static boolean isUndefined(Object obj) {
         return Undefined.instance == obj || Undefined.SCRIPTABLE_UNDEFINED == obj;
     }
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/UnhandledRejectionTracker.java rhino-1.7.14/src/org/mozilla/javascript/UnhandledRejectionTracker.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/UnhandledRejectionTracker.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/src/org/mozilla/javascript/UnhandledRejectionTracker.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,77 @@
+package org.mozilla.javascript;
+
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * This class is responsible for handling tracking of unhandled Promise rejections. These come up
+ * when a Promise is either rejected or an exception is thrown and there is no "catch" handler set
+ * up. There is one of these tracker objects for each Context class.
+ *
+ * <p>Different frameworks will choose different ways to handle unhandled rejections. As a result,
+ * Rhino does nothing by default.
+ *
+ * <p>However, if "trackUnhandledPromiseRejections" is called on the Context object, then Rhino will
+ * track them in this object. It is the responsibility of the product embedding Rhino to
+ * periodically check for unhandled rejections in this class and either remove them or terminate the
+ * script and allow the context and its tracker to be garbage- collected.
+ *
+ * <p>Note that if "trackUnhandledPromiseRejections" is set, and rejections are not handled, then
+ * Promise objects will accumulate in this object and cause a memory leak.
+ */
+public class UnhandledRejectionTracker {
+    private boolean enabled = false;
+    private static final IdentityHashMap<NativePromise, NativePromise> unhandled =
+            new IdentityHashMap<>(0);
+
+    /**
+     * Iterate through all the rejected promises that have not yet been handled. As each promise is
+     * handled by this method, it is removed from this tracker and will not appear again. This is
+     * useful for delivering unhandled promise notifications to users one time via a callback.
+     */
+    public void process(Consumer<Object> handler) {
+        Iterator<NativePromise> it = unhandled.values().iterator();
+        while (it.hasNext()) {
+            try {
+                handler.accept(it.next().getResult());
+            } finally {
+                // Always remove even if handler throws
+                it.remove();
+            }
+        }
+    }
+
+    /**
+     * Return a collection of all of the results of any unhandled rejected promises. This does not
+     * remove unhandled promises from the collection, but reports the current state. It is useful
+     * for command-line tools.
+     *
+     * @return a read-only collection of promise results. To clear them, call "process".
+     */
+    public List<Object> enumerate() {
+        ArrayList<Object> ret = new ArrayList<>();
+        for (NativePromise result : unhandled.values()) {
+            ret.add(result.getResult());
+        }
+        return ret;
+    }
+
+    void enable(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    void promiseRejected(NativePromise p) {
+        if (enabled) {
+            unhandled.put(p, p);
+        }
+    }
+
+    void promiseHandled(NativePromise p) {
+        if (enabled) {
+            unhandled.remove(p);
+        }
+    }
+}
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/UniqueTag.java rhino-1.7.14/src/org/mozilla/javascript/UniqueTag.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/UniqueTag.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/UniqueTag.java	2022-01-06 22:57:21.000000000 +0100
@@ -19,7 +19,7 @@
  */
 public final class UniqueTag implements Serializable
 {
-    static final long serialVersionUID = -4320556826714577259L;
+    private static final long serialVersionUID = -4320556826714577259L;
 
     private static final int ID_NOT_FOUND    = 1;
     private static final int ID_NULL_VALUE   = 2;
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/v8dtoa/DoubleConversion.java rhino-1.7.14/src/org/mozilla/javascript/v8dtoa/DoubleConversion.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/v8dtoa/DoubleConversion.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/v8dtoa/DoubleConversion.java	2022-01-06 22:57:21.000000000 +0100
@@ -55,9 +55,8 @@
         long significand = d64 & kSignificandMask;
         if (!isDenormal(d64)) {
             return significand + kHiddenBit;
-        } else {
-            return significand;
         }
+        return significand;
     }
 
     // Returns true if the double is a denormal.
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/v8dtoa/DoubleHelper.java rhino-1.7.14/src/org/mozilla/javascript/v8dtoa/DoubleHelper.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/v8dtoa/DoubleHelper.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/v8dtoa/DoubleHelper.java	2022-01-06 22:57:21.000000000 +0100
@@ -72,9 +72,8 @@
         long significand = d64 & kSignificandMask;
         if (!isDenormal(d64)) {
             return significand + kHiddenBit;
-        } else {
-            return significand;
         }
+        return significand;
     }
 
     // Returns true if the double is a denormal.
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/VMBridge.java rhino-1.7.14/src/org/mozilla/javascript/VMBridge.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/VMBridge.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/VMBridge.java	2022-01-06 22:57:21.000000000 +0100
@@ -8,9 +8,8 @@
 
 package org.mozilla.javascript;
 
+import java.lang.reflect.AccessibleObject;
 import java.lang.reflect.Method;
-import java.lang.reflect.Member;
-import java.util.Iterator;
 
 public abstract class VMBridge
 {
@@ -21,9 +20,7 @@
     {
         String[] classNames = {
             "org.mozilla.javascript.VMBridge_custom",
-            "org.mozilla.javascript.jdk15.VMBridge_jdk15",
-            "org.mozilla.javascript.jdk13.VMBridge_jdk13",
-            "org.mozilla.javascript.jdk11.VMBridge_jdk11",
+            "org.mozilla.javascript.jdk18.VMBridge_jdk18",
         };
         for (int i = 0; i != classNames.length; ++i) {
             String className = classNames[i];
@@ -60,7 +57,7 @@
 
     /**
      * Associate {@link Context} instance with the current thread or remove
-     * the current association if <tt>cx</tt> is null.
+     * the current association if <code>cx</code> is null.
      *
      * @param contextHelper The result of {@link #getThreadContextHelper()}
      *                      called from the current thread.
@@ -68,11 +65,6 @@
     protected abstract void setContext(Object contextHelper, Context cx);
 
     /**
-     * Return the ClassLoader instance associated with the current thread.
-     */
-    protected abstract ClassLoader getCurrentThreadClassLoader();
-
-    /**
      * In many JVMSs, public methods in private
      * classes are not accessible by default (Sun Bug #4071593).
      * VMBridge instance should try to workaround that via, for example,
@@ -83,7 +75,7 @@
      * @return true if it was possible to make method accessible
      *         or false otherwise.
      */
-    protected abstract boolean tryToMakeAccessible(Object accessibleObject);
+    protected abstract boolean tryToMakeAccessible(AccessibleObject accessible);
 
     /**
      * Create helper object to create later proxies implementing the specified
@@ -96,54 +88,22 @@
      *
      * @param interfaces Array with one or more interface class objects.
      */
-    protected Object getInterfaceProxyHelper(ContextFactory cf,
-                                             Class<?>[] interfaces)
-    {
-        throw Context.reportRuntimeError(
-            "VMBridge.getInterfaceProxyHelper is not supported");
-    }
+    protected abstract Object getInterfaceProxyHelper(ContextFactory cf,
+                                             Class<?>[] interfaces);
 
     /**
      * Create proxy object for {@link InterfaceAdapter}. The proxy should call
      * {@link InterfaceAdapter#invoke(ContextFactory, Object, Scriptable,
-     *                                Method, Object[])}
+     *                                Object, Method, Object[])}
      * as implementation of interface methods associated with
-     * <tt>proxyHelper</tt>. {@link Method}
+     * <code>proxyHelper</code>. {@link Method}
      *
      * @param proxyHelper The result of the previous call to
      *        {@link #getInterfaceProxyHelper(ContextFactory, Class[])}.
      */
-    protected Object newInterfaceProxy(Object proxyHelper,
+    protected abstract Object newInterfaceProxy(Object proxyHelper,
                                        ContextFactory cf,
                                        InterfaceAdapter adapter,
                                        Object target,
-                                       Scriptable topScope)
-    {
-        throw Context.reportRuntimeError(
-            "VMBridge.newInterfaceProxy is not supported");
-    }
-
-    /**
-     * Returns whether or not a given member (method or constructor)
-     * has variable arguments.
-     * Variable argument methods have only been supported in Java since
-     * JDK 1.5.
-     */
-    protected abstract boolean isVarArgs(Member member);
-
-    /**
-     * If "obj" is a java.util.Iterator or a java.lang.Iterable, return a
-     * wrapping as a JavaScript Iterator. Otherwise, return null.
-     * This method is in VMBridge since Iterable is a JDK 1.5 addition.
-     */
-    public Iterator<?> getJavaIterator(Context cx, Scriptable scope, Object obj) {
-        if (obj instanceof Wrapper) {
-            Object unwrapped = ((Wrapper) obj).unwrap();
-            Iterator<?> iterator = null;
-            if (unwrapped instanceof Iterator)
-                iterator = (Iterator<?>) unwrapped;
-            return iterator;
-        }
-        return null;
-    }
+                                       Scriptable topScope);
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/WrapFactory.java rhino-1.7.14/src/org/mozilla/javascript/WrapFactory.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/WrapFactory.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/WrapFactory.java	2022-01-06 22:57:21.000000000 +0100
@@ -8,61 +8,63 @@
 
 package org.mozilla.javascript;
 
+import java.math.BigInteger;
+import java.util.List;
+import java.util.Map;
+
 /**
- * Embeddings that wish to provide their own custom wrappings for Java
- * objects may extend this class and call
- * {@link Context#setWrapFactory(WrapFactory)}
- * Once an instance of this class or an extension of this class is enabled
- * for a given context (by calling setWrapFactory on that context), Rhino
- * will call the methods of this class whenever it needs to wrap a value
- * resulting from a call to a Java method or an access to a Java field.
+ * Embeddings that wish to provide their own custom wrappings for Java objects may extend this class
+ * and call {@link Context#setWrapFactory(WrapFactory)} Once an instance of this class or an
+ * extension of this class is enabled for a given context (by calling setWrapFactory on that
+ * context), Rhino will call the methods of this class whenever it needs to wrap a value resulting
+ * from a call to a Java method or an access to a Java field.
  *
  * @see org.mozilla.javascript.Context#setWrapFactory(WrapFactory)
  * @since 1.5 Release 4
  */
-public class WrapFactory
-{
+public class WrapFactory {
     /**
      * Wrap the object.
-     * <p>
-     * The value returned must be one of
+     *
+     * <p>The value returned must be one of
+     *
      * <UL>
-     * <LI>java.lang.Boolean</LI>
-     * <LI>java.lang.String</LI>
-     * <LI>java.lang.Number</LI>
-     * <LI>org.mozilla.javascript.Scriptable objects</LI>
-     * <LI>The value returned by Context.getUndefinedValue()</LI>
-     * <LI>null</LI>
+     *   <LI>java.lang.Boolean
+     *   <LI>java.lang.String
+     *   <LI>java.lang.Number
+     *   <LI>org.mozilla.javascript.Scriptable objects
+     *   <LI>The value returned by Context.getUndefinedValue()
+     *   <LI>null
      * </UL>
+     *
      * @param cx the current Context for this thread
      * @param scope the scope of the executing script
      * @param obj the object to be wrapped. Note it can be null.
-     * @param staticType type hint. If security restrictions prevent to wrap
-              object based on its class, staticType will be used instead.
+     * @param staticType type hint. If security restrictions prevent to wrap object based on its
+     *     class, staticType will be used instead.
      * @return the wrapped value.
      */
-    public Object wrap(Context cx, Scriptable scope,
-                       Object obj, Class<?> staticType)
-    {
-        if (obj == null || obj == Undefined.instance
-            || obj instanceof Scriptable)
-        {
+    public Object wrap(Context cx, Scriptable scope, Object obj, Class<?> staticType) {
+        if (obj == null || obj == Undefined.instance || obj instanceof Scriptable) {
             return obj;
         }
         if (staticType != null && staticType.isPrimitive()) {
-            if (staticType == Void.TYPE)
-                return Undefined.instance;
-            if (staticType == Character.TYPE)
-                return Integer.valueOf(((Character) obj).charValue());
+            if (staticType == Void.TYPE) return Undefined.instance;
+            if (staticType == Character.TYPE) return Integer.valueOf(((Character) obj).charValue());
             return obj;
         }
         if (!isJavaPrimitiveWrap()) {
-            if (obj instanceof String || obj instanceof Number
-                || obj instanceof Boolean)
-            {
+            if (obj instanceof String
+                    || obj instanceof Boolean
+                    || obj instanceof Integer
+                    || obj instanceof Short
+                    || obj instanceof Long
+                    || obj instanceof Float
+                    || obj instanceof Double
+                    || obj instanceof BigInteger) {
                 return obj;
             } else if (obj instanceof Character) {
-                return String.valueOf(((Character)obj).charValue());
+                return String.valueOf(((Character) obj).charValue());
             }
         }
         Class<?> cls = obj.getClass();
@@ -74,15 +76,15 @@
 
     /**
      * Wrap an object newly created by a constructor call.
+     *
      * @param cx the current Context for this thread
      * @param scope the scope of the executing script
      * @param obj the object to be wrapped
      * @return the wrapped value.
      */
-    public Scriptable wrapNewObject(Context cx, Scriptable scope, Object obj)
-    {
+    public Scriptable wrapNewObject(Context cx, Scriptable scope, Object obj) {
         if (obj instanceof Scriptable) {
-            return (Scriptable)obj;
+            return (Scriptable) obj;
         }
         Class<?> cls = obj.getClass();
         if (cls.isArray()) {
@@ -92,35 +94,37 @@
     }
 
     /**
-     * Wrap Java object as Scriptable instance to allow full access to its
-     * methods and fields from JavaScript.
-     * <p>
-     * {@link #wrap(Context, Scriptable, Object, Class)} and
-     * {@link #wrapNewObject(Context, Scriptable, Object)} call this method
-     * when they can not convert <tt>javaObject</tt> to JavaScript primitive
-     * value or JavaScript array.
-     * <p>
-     * Subclasses can override the method to provide custom wrappers
-     * for Java objects.
+     * Wrap Java object as Scriptable instance to allow full access to its methods and fields from
+     * JavaScript.
+     *
+     * <p>{@link #wrap(Context, Scriptable, Object, Class)} and {@link #wrapNewObject(Context,
+     * Scriptable, Object)} call this method when they can not convert <code>javaObject</code> to
+     * JavaScript primitive value or JavaScript array.
+     *
+     * <p>Subclasses can override the method to provide custom wrappers for Java objects.
+     *
      * @param cx the current Context for this thread
      * @param scope the scope of the executing script
      * @param javaObject the object to be wrapped
-     * @param staticType type hint. If security restrictions prevent to wrap
-                object based on its class, staticType will be used instead.
+     * @param staticType type hint. If security restrictions prevent to wrap object based on its
+     *     class, staticType will be used instead.
      * @return the wrapped value which shall not be null
      */
-    public Scriptable wrapAsJavaObject(Context cx, Scriptable scope,
-                                       Object javaObject, Class<?> staticType)
-    {
+    public Scriptable wrapAsJavaObject(
+            Context cx, Scriptable scope, Object javaObject, Class<?> staticType) {
+        if (List.class.isAssignableFrom(javaObject.getClass())) {
+            return new NativeJavaList(scope, javaObject);
+        } else if (Map.class.isAssignableFrom(javaObject.getClass())) {
+            return new NativeJavaMap(scope, javaObject);
+        }
         return new NativeJavaObject(scope, javaObject, staticType);
     }
 
     /**
-     * Wrap a Java class as Scriptable instance to allow access to its static
-     * members and fields and use as constructor from JavaScript.
-     * <p>
-     * Subclasses can override this method to provide custom wrappers for
-     * Java classes.
+     * Wrap a Java class as Scriptable instance to allow access to its static members and fields and
+     * use as constructor from JavaScript.
+     *
+     * <p>Subclasses can override this method to provide custom wrappers for Java classes.
      *
      * @param cx the current Context for this thread
      * @param scope the scope of the executing script
@@ -128,33 +132,24 @@
      * @return the wrapped value which shall not be null
      * @since 1.7R3
      */
-    public Scriptable wrapJavaClass(Context cx, Scriptable scope,
-                                    Class<?> javaClass)
-    {
+    public Scriptable wrapJavaClass(Context cx, Scriptable scope, Class<?> javaClass) {
         return new NativeJavaClass(scope, javaClass);
     }
 
     /**
-     * Return <code>false</code> if result of Java method, which is instance of
-     * <code>String</code>, <code>Number</code>, <code>Boolean</code> and
-     * <code>Character</code>, should be used directly as JavaScript primitive
-     * type.
-     * By default the method returns true to indicate that instances of
-     * <code>String</code>, <code>Number</code>, <code>Boolean</code> and
-     * <code>Character</code> should be wrapped as any other Java object and
-     * scripts can access any Java method available in these objects.
-     * Use {@link #setJavaPrimitiveWrap(boolean)} to change this.
+     * Return <code>false</code> if result of Java method, which is instance of <code>String</code>,
+     * <code>Number</code>, <code>Boolean</code> and <code>Character</code>, should be used directly
+     * as JavaScript primitive type. By default the method returns true to indicate that instances
+     * of <code>String</code>, <code>Number</code>, <code>Boolean</code> and <code>Character</code>
+     * should be wrapped as any other Java object and scripts can access any Java method available
+     * in these objects. Use {@link #setJavaPrimitiveWrap(boolean)} to change this.
      */
-    public final boolean isJavaPrimitiveWrap()
-    {
+    public final boolean isJavaPrimitiveWrap() {
         return javaPrimitiveWrap;
     }
 
-    /**
-     * @see #isJavaPrimitiveWrap()
-     */
-    public final void setJavaPrimitiveWrap(boolean value)
-    {
+    /** @see #isJavaPrimitiveWrap() */
+    public final void setJavaPrimitiveWrap(boolean value) {
         Context cx = Context.getCurrentContext();
         if (cx != null && cx.isSealed()) {
             Context.onSealedMutation();
@@ -163,5 +158,4 @@
     }
 
     private boolean javaPrimitiveWrap = true;
-
 }
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/WrappedException.java rhino-1.7.14/src/org/mozilla/javascript/WrappedException.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/WrappedException.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/WrappedException.java	2022-01-06 22:57:21.000000000 +0100
@@ -16,16 +16,16 @@
  */
 public class WrappedException extends EvaluatorException
 {
-    static final long serialVersionUID = -1551979216966520648L;
+    private static final long serialVersionUID = -1551979216966520648L;
 
     /**
      * @see Context#throwAsScriptRuntimeEx(Throwable e)
      */
     public WrappedException(Throwable exception)
     {
-        super("Wrapped "+exception.toString());
+        super("Wrapped " + exception);
         this.exception = exception;
-        Kit.initCause(this, exception);
+        this.initCause(exception);
 
         int[] linep = { 0 };
         String sourceName = Context.getSourcePositionFromStack(linep);
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/xml/XMLLib.java rhino-1.7.14/src/org/mozilla/javascript/xml/XMLLib.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/xml/XMLLib.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/xml/XMLLib.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,7 +6,11 @@
 
 package org.mozilla.javascript.xml;
 
-import org.mozilla.javascript.*;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Ref;
+import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
 
 public abstract class XMLLib
 {
@@ -56,7 +60,7 @@
         if (lib != null) {
             return lib;
         }
-        String msg = ScriptRuntime.getMessage0("msg.XML.not.available");
+        String msg = ScriptRuntime.getMessageById("msg.XML.not.available");
         throw Context.reportRuntimeError(msg);
     }
 
diff -Nru rhino-1.7.7.2/src/org/mozilla/javascript/xml/XMLObject.java rhino-1.7.14/src/org/mozilla/javascript/xml/XMLObject.java
--- rhino-1.7.7.2/src/org/mozilla/javascript/xml/XMLObject.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/src/org/mozilla/javascript/xml/XMLObject.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,7 +6,11 @@
 
 package org.mozilla.javascript.xml;
 
-import org.mozilla.javascript.*;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.IdScriptableObject;
+import org.mozilla.javascript.NativeWith;
+import org.mozilla.javascript.Ref;
+import org.mozilla.javascript.Scriptable;
 
 /**
  *  This Interface describes what all XML objects (XML, XMLList) should have in common.
@@ -14,9 +18,9 @@
  */
 public abstract class XMLObject extends IdScriptableObject
 {
-    
-    static final long serialVersionUID = 8455156490438576500L;
-    
+
+    private static final long serialVersionUID = 8455156490438576500L;
+
     public XMLObject()
     {
     }
@@ -80,7 +84,7 @@
     public abstract NativeWith enterDotQuery(Scriptable scope);
 
     /**
-     * Custom <tt>+</tt> operator.
+     * Custom <code>+</code> operator.
      * Should return {@link Scriptable#NOT_FOUND} if this object does not have
      * custom addition operator for the given value,
      * or the result of the addition operation.
diff -Nru rhino-1.7.7.2/tools/ci/linux/release.sh rhino-1.7.14/tools/ci/linux/release.sh
--- rhino-1.7.7.2/tools/ci/linux/release.sh	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/tools/ci/linux/release.sh	2022-01-06 22:57:21.000000000 +0100
@@ -2,6 +2,6 @@
 
 BUILDROOT=${BUILDROOT:-github/rhino}
 
-(cd $BUILDROOT; ./gradlew clean jar sourceJar rhinoJavadocJar distZip)
+(cd $BUILDROOT; ./gradlew clean jar sourceJar javadocJar distZip)
 testStatus=$?
 exit ${testStatus}
diff -Nru rhino-1.7.7.2/toolsrc/build.xml rhino-1.7.14/toolsrc/build.xml
--- rhino-1.7.7.2/toolsrc/build.xml	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/build.xml	1970-01-01 01:00:00.000000000 +0100
@@ -1,49 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-
-<!--
-Build file for Rhino using Ant (see http://jakarta.apache.org/ant/index.html)
-Requires Ant version 1.2
--->
-<project name="toolsrc" default="compile" basedir="..">
-
-  <target name="properties">
-    <property file="build.properties"/>
-  </target>
-
-  <target name="compile" depends="properties">
-    <javac srcdir="toolsrc"
-           destdir="${classes}"
-           includes="org/**/*.java"
-           deprecation="on"
-           debug="${debug}"
-           includeAntRuntime="false"
-           encoding="UTF-8"
-           target="${target-jvm}"
-           source="${source-level}"
-    >
-    </javac>
-    <copy todir="${classes}">
-      <fileset dir="toolsrc" includes="org/**/*.properties" />
-    </copy>
-  </target>
-
-  <target name="copy-source" depends="properties">
-    <mkdir dir="${dist.dir}/toolsrc"/>
-    <copy todir="${dist.dir}/toolsrc">
-      <fileset dir="toolsrc"
-               includes="**/*.java,**/*.properties,**/*.xml" />
-    </copy>
-  </target>
-
-  <target name="clean" depends="properties">
-    <delete includeEmptyDirs="true">
-      <fileset dir="${classes}"
-               includes="org/mozilla/javascript/tools/**"/>
-    </delete>
-  </target>
-
-</project>
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/debugger/build.xml rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/debugger/build.xml
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/debugger/build.xml	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/debugger/build.xml	1970-01-01 01:00:00.000000000 +0100
@@ -1,96 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-
-<project name="toolsrc" default="help" basedir=".">
-
-  <target name="properties">
-    <property name="swing-ex-url" value="http://java.sun.com/products/jfc/tsc/articles/treetable2/downloads/src.zip"/>
-    <available file="downloaded/AbstractCellEditor.java"
-               property="swing-ex-available"/>
-  </target>
-
-  <target name="get-swing-ex" unless="swing-ex-available">
-    <!-- Download source from Sun's site, unzip it, remove
-         the files we don't need, and change the package
-    -->
-    <mkdir dir="downloaded"/>
-    <get src="${swing-ex-url}" dest="downloaded/swingExSrc.zip"/>
-    <unzip src="downloaded/swingExSrc.zip" dest="downloaded/">
-      <patternset>
-          <include name="AbstractCellEditor.java"/>
-          <include name="JTreeTable.java"/>
-          <include name="TreeTableModel.java"/>
-          <include name="TreeTableModelAdapter.java"/>
-      </patternset>
-    </unzip>
-    <replace file="downloaded/AbstractCellEditor.java">
-       <replacetoken>import java.awt.Component;</replacetoken>
-       <replacevalue>
-package org.mozilla.javascript.tools.debugger.downloaded;
-       </replacevalue>
-    </replace>
-    <replace file="downloaded/AbstractCellEditor.java">
-       <replacetoken>import java.awt.event.*;</replacetoken>
-       <replacevalue></replacevalue>
-    </replace>
-    <replace file="downloaded/AbstractCellEditor.java">
-       <replacetoken>import java.awt.AWTEvent;</replacetoken>
-       <replacevalue></replacevalue>
-    </replace>
-    <replace file="downloaded/AbstractCellEditor.java">
-       <replacetoken>import java.io.Serializable;</replacetoken>
-       <replacevalue></replacevalue>
-    </replace>
-    <replace file="downloaded/JTreeTable.java">
-       <replacetoken>import javax.swing.*;</replacetoken>
-       <replacevalue>
-         package org.mozilla.javascript.tools.debugger.downloaded;
-         import javax.swing.*;
-       </replacevalue>
-    </replace>
-    <replace file="downloaded/JTreeTable.java">
-       <replacetoken>class ListToTreeSelectionModelWrapper</replacetoken>
-       <replacevalue>public class ListToTreeSelectionModelWrapper</replacevalue>
-    </replace>
-    <replace file="downloaded/JTreeTable.java">
-       <replacetoken>ListSelectionModel getListSelectionModel</replacetoken>
-       <replacevalue>public ListSelectionModel getListSelectionModel</replacevalue>
-    </replace>
-    <replace file="downloaded/JTreeTable.java">
-       <replacetoken>import java.awt.Rectangle;</replacetoken>
-       <replacevalue></replacevalue>
-    </replace>
-    <replace file="downloaded/TreeTableModel.java">
-       <replacetoken>import javax.swing.tree.TreeModel;</replacetoken>
-       <replacevalue>
-         package org.mozilla.javascript.tools.debugger.downloaded;
-         import javax.swing.tree.TreeModel;
-       </replacevalue>
-    </replace>
-    <replace file="downloaded/TreeTableModelAdapter.java">
-       <replacetoken>import javax.swing.JTree;</replacetoken>
-       <replacevalue>
-         package org.mozilla.javascript.tools.debugger.downloaded;
-         import javax.swing.JTree;
-       </replacevalue>
-    </replace>
-    <delete file="downloaded/swingExSrc.zip"/>
-  </target>
-
-  <target name="download" depends="properties,get-swing-ex"/>
-
-  <target name="help" depends="properties">
-<echo>The following targets are available with this build file:
-
- download    Download ${swing-ex-url}
-             and extract the necessary files from it.
-
- help        Print this help.
-
-</echo>
-  </target>
-
-</project>
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/debugger/Dim.java rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/debugger/Dim.java
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/debugger/Dim.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/debugger/Dim.java	2022-01-06 22:57:21.000000000 +0100
@@ -5,11 +5,33 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 package org.mozilla.javascript.tools.debugger;
 
-import org.mozilla.javascript.*;
-import org.mozilla.javascript.debug.*;
-import java.util.*;
-import java.io.*;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.net.URL;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.mozilla.javascript.Callable;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.ContextAction;
+import org.mozilla.javascript.ContextFactory;
+import org.mozilla.javascript.ImporterTopLevel;
+import org.mozilla.javascript.Kit;
+import org.mozilla.javascript.NativeCall;
+import org.mozilla.javascript.ObjArray;
+import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+import org.mozilla.javascript.SecurityUtilities;
+import org.mozilla.javascript.Undefined;
+import org.mozilla.javascript.debug.DebugFrame;
+import org.mozilla.javascript.debug.DebuggableObject;
+import org.mozilla.javascript.debug.DebuggableScript;
+import org.mozilla.javascript.debug.Debugger;
 
 /**
  * Dim or Debugger Implementation for Rhino.
@@ -996,6 +1018,7 @@
          * Performs the action given by {@link #type} with the attached
          * {@link ContextFactory}.
          */
+        @SuppressWarnings("unchecked")
         private void withContext() {
             dim.contextFactory.call(this);
         }
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/debugger/Main.java rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/debugger/Main.java
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/debugger/Main.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/debugger/Main.java	2022-01-06 22:57:21.000000000 +0100
@@ -11,7 +11,10 @@
 
 import javax.swing.JFrame;
 
-import org.mozilla.javascript.*;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.ContextFactory;
+import org.mozilla.javascript.Kit;
+import org.mozilla.javascript.Scriptable;
 import org.mozilla.javascript.tools.shell.Global;
 
 /**
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/debugger/SwingGui.java rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/debugger/SwingGui.java
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/debugger/SwingGui.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/debugger/SwingGui.java	2022-01-06 22:57:21.000000000 +0100
@@ -5,19 +5,15 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 package org.mozilla.javascript.tools.debugger;
 
-import javax.swing.*;
-import javax.swing.text.*;
-import javax.swing.event.*;
-import javax.swing.table.*;
-import java.awt.EventQueue;
-import java.awt.ActiveEvent;
 import java.awt.AWTEvent;
+import java.awt.ActiveEvent;
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Component;
 import java.awt.Container;
 import java.awt.Dimension;
 import java.awt.Event;
+import java.awt.EventQueue;
 import java.awt.Font;
 import java.awt.FontMetrics;
 import java.awt.Frame;
@@ -30,120 +26,152 @@
 import java.awt.Polygon;
 import java.awt.Rectangle;
 import java.awt.Toolkit;
-import java.awt.event.*;
-
-import java.util.List;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.awt.event.ContainerEvent;
+import java.awt.event.ContainerListener;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowListener;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.io.Reader;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.EventObject;
-import java.util.Map;
 import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 import java.util.Properties;
-import java.io.*;
+import java.util.TreeMap;
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.DefaultListModel;
+import javax.swing.JButton;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JDesktopPane;
+import javax.swing.JDialog;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JInternalFrame;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTable;
+import javax.swing.JTextArea;
+import javax.swing.JToolBar;
+import javax.swing.JTree;
+import javax.swing.JViewport;
+import javax.swing.KeyStroke;
+import javax.swing.ListSelectionModel;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.WindowConstants;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.event.InternalFrameAdapter;
+import javax.swing.event.InternalFrameEvent;
+import javax.swing.event.PopupMenuEvent;
+import javax.swing.event.PopupMenuListener;
+import javax.swing.event.TreeModelListener;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.TableModel;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.Segment;
 import javax.swing.tree.DefaultTreeCellRenderer;
 import javax.swing.tree.TreePath;
-import java.lang.reflect.Method;
-
 import org.mozilla.javascript.Kit;
 import org.mozilla.javascript.SecurityUtilities;
-
-import org.mozilla.javascript.tools.shell.ConsoleTextArea;
-
 import org.mozilla.javascript.tools.debugger.treetable.JTreeTable;
 import org.mozilla.javascript.tools.debugger.treetable.TreeTableModel;
 import org.mozilla.javascript.tools.debugger.treetable.TreeTableModelAdapter;
+import org.mozilla.javascript.tools.shell.ConsoleTextArea;
 
-/**
- * GUI for the Rhino debugger.
- */
+/** GUI for the Rhino debugger. */
 public class SwingGui extends JFrame implements GuiCallback {
 
-    /**
-     * Serializable magic number.
-     */
+    /** Serializable magic number. */
     private static final long serialVersionUID = -8217029773456711621L;
 
-    /**
-     * The debugger.
-     */
+    /** The debugger. */
     Dim dim;
 
-    /**
-     * The action to run when the 'Exit' menu item is chosen or the
-     * frame is closed.
-     */
+    /** The action to run when the 'Exit' menu item is chosen or the frame is closed. */
     private Runnable exitAction;
 
-    /**
-     * The {@link JDesktopPane} that holds the script windows.
-     */
+    /** The {@link JDesktopPane} that holds the script windows. */
     private JDesktopPane desk;
 
-    /**
-     * The {@link JPanel} that shows information about the context.
-     */
+    /** The {@link JPanel} that shows information about the context. */
     private ContextWindow context;
 
-    /**
-     * The menu bar.
-     */
+    /** The menu bar. */
     private Menubar menubar;
 
-    /**
-     * The tool bar.
-     */
+    /** The tool bar. */
     private JToolBar toolBar;
 
-    /**
-     * The console that displays I/O from the script.
-     */
+    /** The console that displays I/O from the script. */
     private JSInternalConsole console;
 
     /**
-     * The {@link JSplitPane} that separates {@link #desk} from
-     * {@link org.mozilla.javascript.Context}.
+     * The {@link JSplitPane} that separates {@link #desk} from {@link
+     * org.mozilla.javascript.Context}.
      */
     private JSplitPane split1;
 
-    /**
-     * The status bar.
-     */
+    /** The status bar. */
     private JLabel statusBar;
 
-    /**
-     * Hash table of internal frame names to the internal frames themselves.
-     */
-    private final Map<String,JFrame> toplevels =
-        Collections.synchronizedMap(new HashMap<String,JFrame>());
+    /** Hash table of internal frame names to the internal frames themselves. */
+    private final Map<String, JFrame> toplevels =
+            Collections.synchronizedMap(new HashMap<String, JFrame>());
+
+    /** Hash table of script URLs to their internal frames. */
+    private final Map<String, FileWindow> fileWindows =
+            Collections.synchronizedMap(new TreeMap<String, FileWindow>());
 
-    /**
-     * Hash table of script URLs to their internal frames.
-     */
-    private final Map<String,FileWindow> fileWindows =
-        Collections.synchronizedMap(new HashMap<String,FileWindow>());
-
-
-    /**
-     * The {@link FileWindow} that last had the focus.
-     */
+    /** The {@link FileWindow} that last had the focus. */
     private FileWindow currentWindow;
 
-    /**
-     * File choose dialog for loading a script.
-     */
+    /** File choose dialog for loading a script. */
     JFileChooser dlg;
 
     /**
-     * The AWT EventQueue.  Used for manually pumping AWT events from
-     * {@link #dispatchNextGuiEvent()}.
+     * The AWT EventQueue. Used for manually pumping AWT events from {@link
+     * #dispatchNextGuiEvent()}.
      */
     private EventQueue awtEventQueue;
 
-    /**
-     * Creates a new SwingGui.
-     */
+    /** Creates a new SwingGui. */
     public SwingGui(Dim dim, String title) {
         super(title);
         this.dim = dim;
@@ -151,31 +179,22 @@
         dim.setGuiCallback(this);
     }
 
-    /**
-     * Returns the Menubar of this debugger frame.
-     */
+    /** Returns the Menubar of this debugger frame. */
     public Menubar getMenubar() {
         return menubar;
     }
 
-    /**
-     * Sets the {@link Runnable} that will be run when the "Exit" menu
-     * item is chosen.
-     */
+    /** Sets the {@link Runnable} that will be run when the "Exit" menu item is chosen. */
     public void setExitAction(Runnable r) {
         exitAction = r;
     }
 
-    /**
-     * Returns the debugger console component.
-     */
+    /** Returns the debugger console component. */
     public JSInternalConsole getConsole() {
         return console;
     }
 
-    /**
-     * Sets the visibility of the debugger GUI.
-     */
+    /** Sets the visibility of the debugger GUI. */
     @Override
     public void setVisible(boolean b) {
         super.setVisible(b);
@@ -193,30 +212,23 @@
         }
     }
 
-    /**
-     * Records a new internal frame.
-     */
+    /** Records a new internal frame. */
     void addTopLevel(String key, JFrame frame) {
         if (frame != this) {
             toplevels.put(key, frame);
         }
     }
 
-    /**
-     * Constructs the debugger GUI.
-     */
+    /** Constructs the debugger GUI. */
     private void init() {
         menubar = new Menubar(this);
         setJMenuBar(menubar);
         toolBar = new JToolBar();
         JButton button;
-        JButton breakButton, goButton, stepIntoButton,
-            stepOverButton, stepOutButton;
-        String [] toolTips = {"Break (Pause)",
-                              "Go (F5)",
-                              "Step Into (F11)",
-                              "Step Over (F7)",
-                              "Step Out (F8)"};
+        JButton breakButton, goButton, stepIntoButton, stepOverButton, stepOutButton;
+        String[] toolTips = {
+            "Break (Pause)", "Go (F5)", "Step Into (F11)", "Step Over (F7)", "Step Out (F8)"
+        };
         int count = 0;
         button = breakButton = new JButton("Break");
         button.setToolTipText("Break");
@@ -288,8 +300,7 @@
         context.setPreferredSize(new Dimension(600, 120));
         context.setMinimumSize(new Dimension(50, 50));
 
-        split1 = new JSplitPane(JSplitPane.VERTICAL_SPLIT, desk,
-                                          context);
+        split1 = new JSplitPane(JSplitPane.VERTICAL_SPLIT, desk, context);
         split1.setOneTouchExpandable(true);
         SwingGui.setResizeWeight(split1, 0.66);
         contentPane.add(split1, BorderLayout.CENTER);
@@ -299,7 +310,7 @@
         dlg = new JFileChooser();
 
         javax.swing.filechooser.FileFilter filter =
-            new javax.swing.filechooser.FileFilter() {
+                new javax.swing.filechooser.FileFilter() {
                     @Override
                     public boolean accept(File f) {
                         if (f.isDirectory()) {
@@ -307,7 +318,7 @@
                         }
                         String n = f.getName();
                         int i = n.lastIndexOf('.');
-                        if (i > 0 && i < n.length() -1) {
+                        if (i > 0 && i < n.length() - 1) {
                             String ext = n.substring(i + 1).toLowerCase();
                             if (ext.equals("js")) {
                                 return true;
@@ -322,17 +333,16 @@
                     }
                 };
         dlg.addChoosableFileFilter(filter);
-        addWindowListener(new WindowAdapter() {
-                @Override
-                public void windowClosing(WindowEvent e) {
-                    exit();
-                }
-            });
+        addWindowListener(
+                new WindowAdapter() {
+                    @Override
+                    public void windowClosing(WindowEvent e) {
+                        exit();
+                    }
+                });
     }
 
-    /**
-     * Runs the {@link #exitAction}.
-     */
+    /** Runs the {@link #exitAction}. */
     private void exit() {
         if (exitAction != null) {
             SwingUtilities.invokeLater(exitAction);
@@ -340,9 +350,7 @@
         dim.setReturnValue(Dim.EXIT);
     }
 
-    /**
-     * Returns the {@link FileWindow} for the given URL.
-     */
+    /** Returns the {@link FileWindow} for the given URL. */
     FileWindow getFileWindow(String url) {
         if (url == null || url.equals("<stdin>")) {
             return null;
@@ -350,9 +358,7 @@
         return fileWindows.get(url);
     }
 
-    /**
-     * Returns a short version of the given URL.
-     */
+    /** Returns a short version of the given URL. */
     static String getShortName(String url) {
         int lastSlash = url.lastIndexOf('/');
         if (lastSlash < 0) {
@@ -365,21 +371,19 @@
         return shortName;
     }
 
-    /**
-     * Closes the given {@link FileWindow}.
-     */
+    /** Closes the given {@link FileWindow}. */
     void removeWindow(FileWindow w) {
         fileWindows.remove(w.getUrl());
         JMenu windowMenu = getWindowMenu();
         int count = windowMenu.getItemCount();
-        JMenuItem lastItem = windowMenu.getItem(count -1);
+        JMenuItem lastItem = windowMenu.getItem(count - 1);
         String name = getShortName(w.getUrl());
         for (int i = 5; i < count; i++) {
             JMenuItem item = windowMenu.getItem(i);
             if (item == null) continue; // separator
             String text = item.getText();
-            //1 D:\foo.js
-            //2 D:\bar.js
+            // 1 D:\foo.js
+            // 2 D:\bar.js
             int pos = text.indexOf(' ');
             if (text.substring(pos + 1).equals(name)) {
                 windowMenu.remove(item);
@@ -393,21 +397,19 @@
                     windowMenu.remove(4);
                 } else {
                     int j = i - 4;
-                    for (;i < count -1; i++) {
+                    for (; i < count - 1; i++) {
                         JMenuItem thisItem = windowMenu.getItem(i);
                         if (thisItem != null) {
-                            //1 D:\foo.js
-                            //2 D:\bar.js
+                            // 1 D:\foo.js
+                            // 2 D:\bar.js
                             text = thisItem.getText();
                             if (text.equals("More Windows...")) {
                                 break;
-                            } else {
-                                pos = text.indexOf(' ');
-                                thisItem.setText((char)('0' + j) + " " +
-                                                 text.substring(pos + 1));
-                                thisItem.setMnemonic('0' + j);
-                                j++;
                             }
+                            pos = text.indexOf(' ');
+                            thisItem.setText((char) ('0' + j) + " " + text.substring(pos + 1));
+                            thisItem.setMnemonic('0' + j);
+                            j++;
                         }
                     }
                     if (count - 6 == 0 && lastItem != item) {
@@ -422,9 +424,7 @@
         windowMenu.revalidate();
     }
 
-    /**
-     * Shows the line at which execution in the given stack frame just stopped.
-     */
+    /** Shows the line at which execution in the given stack frame just stopped. */
     void showStopLine(Dim.StackFrame frame) {
         String sourceName = frame.getUrl();
         if (sourceName == null || sourceName.equals("<stdin>")) {
@@ -442,22 +442,39 @@
     }
 
     /**
-     * Shows a {@link FileWindow} for the given source, creating it
-     * if it doesn't exist yet. if <code>lineNumber</code> is greater
-     * than -1, it indicates the line number to select and display.
+     * Shows a {@link FileWindow} for the given source, creating it if it doesn't exist yet. if
+     * <code>lineNumber</code> is greater than -1, it indicates the line number to select and
+     * display.
+     *
      * @param sourceUrl the source URL
      * @param lineNumber the line number to select, or -1
      */
     protected void showFileWindow(String sourceUrl, int lineNumber) {
-        FileWindow w = getFileWindow(sourceUrl);
-        if (w == null) {
+        FileWindow w;
+        if (sourceUrl != null) {
+            w = getFileWindow(sourceUrl);
+        } else {
+            JInternalFrame f = getSelectedFrame();
+            if (f != null && f instanceof FileWindow) {
+                w = (FileWindow) f;
+            } else {
+                w = currentWindow;
+            }
+        }
+        if (w == null && sourceUrl != null) {
             Dim.SourceInfo si = dim.sourceInfo(sourceUrl);
             createFileWindow(si, -1);
             w = getFileWindow(sourceUrl);
         }
+        if (w == null) {
+            return;
+        }
         if (lineNumber > -1) {
-            int start = w.getPosition(lineNumber-1);
-            int end = w.getPosition(lineNumber)-1;
+            int start = w.getPosition(lineNumber - 1);
+            int end = w.getPosition(lineNumber) - 1;
+            if (start <= 0) {
+                return;
+            }
             w.textArea.select(start);
             w.textArea.setCaretPosition(start);
             w.textArea.moveCaretPosition(end);
@@ -476,9 +493,7 @@
         }
     }
 
-    /**
-     * Creates and shows a new {@link FileWindow} for the given source.
-     */
+    /** Creates and shows a new {@link FileWindow} for the given source. */
     protected void createFileWindow(Dim.SourceInfo sourceInfo, int line) {
         boolean activate = true;
 
@@ -490,7 +505,7 @@
                 currentWindow.setPosition(-1);
             }
             try {
-                w.setPosition(w.textArea.getLineStartOffset(line-1));
+                w.setPosition(w.textArea.getLineStartOffset(line - 1));
             } catch (BadLocationException exc) {
                 try {
                     w.setPosition(w.textArea.getLineStartOffset(0));
@@ -517,12 +532,13 @@
     }
 
     /**
-     * Update the source text for <code>sourceInfo</code>. This returns true
-     * if a {@link FileWindow} for the given source exists and could be updated.
-     * Otherwise, this does nothing and returns false.
+     * Update the source text for <code>sourceInfo</code>. This returns true if a {@link FileWindow}
+     * for the given source exists and could be updated. Otherwise, this does nothing and returns
+     * false.
+     *
      * @param sourceInfo the source info
-     * @return true if a {@link FileWindow} for the given source exists
-     *              and could be updated, false otherwise.
+     * @return true if a {@link FileWindow} for the given source exists and could be updated, false
+     *     otherwise.
      */
     protected boolean updateFileWindow(Dim.SourceInfo sourceInfo) {
         String fileName = sourceInfo.url();
@@ -535,10 +551,7 @@
         return false;
     }
 
-    /**
-     * Moves the current position in the given {@link FileWindow} to the
-     * given line.
-     */
+    /** Moves the current position in the given {@link FileWindow} to the given line. */
     private void setFilePosition(FileWindow w, int line) {
         boolean activate = true;
         JTextArea ta = w.textArea;
@@ -549,7 +562,7 @@
                     currentWindow = null;
                 }
             } else {
-                int loc = ta.getLineStartOffset(line-1);
+                int loc = ta.getLineStartOffset(line - 1);
                 if (currentWindow != null && currentWindow != w) {
                     currentWindow.setPosition(-1);
                 }
@@ -566,34 +579,29 @@
             desk.getDesktopManager().activateFrame(w);
             try {
                 w.show();
-                w.toFront();  // required for correct frame layering (JDK 1.4.1)
+                w.toFront(); // required for correct frame layering (JDK 1.4.1)
                 w.setSelected(true);
             } catch (Exception exc) {
             }
         }
     }
 
-    /**
-     * Handles script interruption.
-     */
-    void enterInterruptImpl(Dim.StackFrame lastFrame,
-                            String threadTitle, String alertMessage) {
+    /** Handles script interruption. */
+    void enterInterruptImpl(Dim.StackFrame lastFrame, String threadTitle, String alertMessage) {
         statusBar.setText("Thread: " + threadTitle);
 
         showStopLine(lastFrame);
 
         if (alertMessage != null) {
-            MessageDialogWrapper.showMessageDialog(this,
-                                                   alertMessage,
-                                                   "Exception in Script",
-                                                   JOptionPane.ERROR_MESSAGE);
+            MessageDialogWrapper.showMessageDialog(
+                    this, alertMessage, "Exception in Script", JOptionPane.ERROR_MESSAGE);
         }
 
         updateEnabled(true);
 
         Dim.ContextData contextData = lastFrame.contextData();
 
-        JComboBox ctx = context.context;
+        JComboBox<String> ctx = context.context;
         List<String> toolTips = context.toolTips;
         context.disableUpdate();
         int frameCount = contextData.frameCount();
@@ -620,16 +628,12 @@
         ctx.setMinimumSize(new Dimension(50, ctx.getMinimumSize().height));
     }
 
-    /**
-     * Returns the 'Window' menu.
-     */
+    /** Returns the 'Window' menu. */
     private JMenu getWindowMenu() {
         return menubar.getMenu(3);
     }
 
-    /**
-     * Displays a {@link JFileChooser} and returns the selected filename.
-     */
+    /** Displays a {@link JFileChooser} and returns the selected filename. */
     private String chooseFile(String title) {
         dlg.setDialogTitle(title);
         File CWD = null;
@@ -656,25 +660,20 @@
         return null;
     }
 
-    /**
-     * Returns the current selected internal frame.
-     */
+    /** Returns the current selected internal frame. */
     private JInternalFrame getSelectedFrame() {
-       JInternalFrame[] frames = desk.getAllFrames();
-       for (int i = 0; i < frames.length; i++) {
-           if (frames[i].isShowing()) {
-               return frames[i];
-           }
-       }
-       return frames[frames.length - 1];
+        JInternalFrame[] frames = desk.getAllFrames();
+        for (int i = 0; i < frames.length; i++) {
+            if (frames[i].isShowing()) {
+                return frames[i];
+            }
+        }
+        return frames[frames.length - 1];
     }
 
-    /**
-     * Enables or disables the menu and tool bars with respect to the
-     * state of script execution.
-     */
+    /** Enables or disables the menu and tool bars with respect to the state of script execution. */
     private void updateEnabled(boolean interrupted) {
-        ((Menubar)getJMenuBar()).updateEnabled(interrupted);
+        ((Menubar) getJMenuBar()).updateEnabled(interrupted);
         for (int ci = 0, cc = toolBar.getComponentCount(); ci < cc; ci++) {
             boolean enableButton;
             if (ci == 0) {
@@ -701,37 +700,29 @@
     }
 
     /**
-     * Calls {@link JSplitPane#setResizeWeight} via reflection.
-     * For compatibility, since JDK < 1.3 does not have this method.
+     * Calls {@link JSplitPane#setResizeWeight} via reflection. For compatibility, since JDK <
+     * 1.3 does not have this method.
      */
     static void setResizeWeight(JSplitPane pane, double weight) {
         try {
-            Method m = JSplitPane.class.getMethod("setResizeWeight",
-                                                  new Class[]{double.class});
-            m.invoke(pane, new Object[]{new Double(weight)});
+            Method m = JSplitPane.class.getMethod("setResizeWeight", new Class[] {double.class});
+            m.invoke(pane, new Object[] {Double.valueOf(weight)});
         } catch (NoSuchMethodException exc) {
         } catch (IllegalAccessException exc) {
         } catch (java.lang.reflect.InvocationTargetException exc) {
         }
     }
 
-    /**
-     * Reads the file with the given name and returns its contents as a String.
-     */
+    /** Reads the file with the given name and returns its contents as a String. */
     private String readFile(String fileName) {
         String text;
         try {
-            Reader r = new FileReader(fileName);
-            try {
+            try (Reader r = new FileReader(fileName)) {
                 text = Kit.readReader(r);
-            } finally {
-                r.close();
             }
         } catch (IOException ex) {
-            MessageDialogWrapper.showMessageDialog(this,
-                                                   ex.getMessage(),
-                                                   "Error reading "+fileName,
-                                                   JOptionPane.ERROR_MESSAGE);
+            MessageDialogWrapper.showMessageDialog(
+                    this, ex.getMessage(), "Error reading " + fileName, JOptionPane.ERROR_MESSAGE);
             text = null;
         }
         return text;
@@ -739,21 +730,17 @@
 
     // GuiCallback
 
-    /**
-     * Called when the source text for a script has been updated.
-     */
+    /** Called when the source text for a script has been updated. */
+    @Override
     public void updateSourceText(Dim.SourceInfo sourceInfo) {
         RunProxy proxy = new RunProxy(this, RunProxy.UPDATE_SOURCE_TEXT);
         proxy.sourceInfo = sourceInfo;
         SwingUtilities.invokeLater(proxy);
     }
 
-    /**
-     * Called when the interrupt loop has been entered.
-     */
-    public void enterInterrupt(Dim.StackFrame lastFrame,
-                               String threadTitle,
-                               String alertMessage) {
+    /** Called when the interrupt loop has been entered. */
+    @Override
+    public void enterInterrupt(Dim.StackFrame lastFrame, String threadTitle, String alertMessage) {
         if (SwingUtilities.isEventDispatchThread()) {
             enterInterruptImpl(lastFrame, threadTitle, alertMessage);
         } else {
@@ -765,16 +752,14 @@
         }
     }
 
-    /**
-     * Returns whether the current thread is the GUI event thread.
-     */
+    /** Returns whether the current thread is the GUI event thread. */
+    @Override
     public boolean isGuiEventThread() {
         return SwingUtilities.isEventDispatchThread();
     }
 
-    /**
-     * Processes the next GUI event.
-     */
+    /** Processes the next GUI event. */
+    @Override
     public void dispatchNextGuiEvent() throws InterruptedException {
         EventQueue queue = awtEventQueue;
         if (queue == null) {
@@ -783,30 +768,28 @@
         }
         AWTEvent event = queue.getNextEvent();
         if (event instanceof ActiveEvent) {
-            ((ActiveEvent)event).dispatch();
+            ((ActiveEvent) event).dispatch();
         } else {
             Object source = event.getSource();
             if (source instanceof Component) {
-                Component comp = (Component)source;
+                Component comp = (Component) source;
                 comp.dispatchEvent(event);
             } else if (source instanceof MenuComponent) {
-                ((MenuComponent)source).dispatchEvent(event);
+                ((MenuComponent) source).dispatchEvent(event);
             }
         }
     }
 
     // ActionListener
 
-    /**
-     * Performs an action from the menu or toolbar.
-     */
+    /** Performs an action from the menu or toolbar. */
     public void actionPerformed(ActionEvent e) {
         String cmd = e.getActionCommand();
         int returnValue = -1;
         if (cmd.equals("Cut") || cmd.equals("Copy") || cmd.equals("Paste")) {
             JInternalFrame f = getSelectedFrame();
             if (f != null && f instanceof ActionListener) {
-                ((ActionListener)f).actionPerformed(e);
+                ((ActionListener) f).actionPerformed(e);
             }
         } else if (cmd.equals("Step Over")) {
             returnValue = Dim.STEP_OVER;
@@ -843,8 +826,7 @@
                 }
             }
         } else if (cmd.equals("More Windows...")) {
-            MoreWindows dlg = new MoreWindows(this, fileWindows,
-                                              "Window", "Files");
+            MoreWindows dlg = new MoreWindows(this, fileWindows, "Window", "Files");
             dlg.showDialog(this);
         } else if (cmd.equals("Console")) {
             if (console.isIcon()) {
@@ -857,28 +839,47 @@
         } else if (cmd.equals("Copy")) {
         } else if (cmd.equals("Paste")) {
         } else if (cmd.equals("Go to function...")) {
-            FindFunction dlg = new FindFunction(this, "Go to function",
-                                                "Function");
+            FindFunction dlg = new FindFunction(this, "Go to function", "Function");
             dlg.showDialog(this);
+        } else if (cmd.equals("Go to line...")) {
+            final String s =
+                    (String)
+                            JOptionPane.showInputDialog(
+                                    this,
+                                    "Line number",
+                                    "Go to line...",
+                                    JOptionPane.QUESTION_MESSAGE,
+                                    null,
+                                    null,
+                                    null);
+            if (s == null || s.trim().length() == 0) {
+                return;
+            }
+            try {
+                final int line = Integer.parseInt(s);
+                showFileWindow(null, line);
+            } catch (final NumberFormatException nfe) {
+                // ignore
+            }
         } else if (cmd.equals("Tile")) {
             JInternalFrame[] frames = desk.getAllFrames();
             int count = frames.length;
             int rows, cols;
-            rows = cols = (int)Math.sqrt(count);
-            if (rows*cols < count) {
+            rows = cols = (int) Math.sqrt(count);
+            if (rows * cols < count) {
                 cols++;
                 if (rows * cols < count) {
                     rows++;
                 }
             }
             Dimension size = desk.getSize();
-            int w = size.width/cols;
-            int h = size.height/rows;
+            int w = size.width / cols;
+            int h = size.height / rows;
             int x = 0;
             int y = 0;
             for (int i = 0; i < rows; i++) {
                 for (int j = 0; j < cols; j++) {
-                    int index = (i*cols) + j;
+                    int index = (i * cols) + j;
                     if (index >= frames.length) {
                         break;
                     }
@@ -888,8 +889,7 @@
                         f.setMaximum(false);
                     } catch (Exception exc) {
                     }
-                    desk.getDesktopManager().setBoundsForFrame(f, x, y,
-                                                               w, h);
+                    desk.getDesktopManager().setBoundsForFrame(f, x, y, w, h);
                     x += w;
                 }
                 y += h;
@@ -903,7 +903,7 @@
             h = desk.getHeight();
             int d = h / count;
             if (d > 30) d = 30;
-            for (int i = count -1; i >= 0; i--, x += d, y += d) {
+            for (int i = count - 1; i >= 0; i--, x += d, y += d) {
                 JInternalFrame f = frames[i];
                 try {
                     f.setIcon(false);
@@ -918,7 +918,7 @@
         } else {
             Object obj = getFileWindow(cmd);
             if (obj != null) {
-                FileWindow w = (FileWindow)obj;
+                FileWindow w = (FileWindow) obj;
                 try {
                     if (w.isIcon()) {
                         w.setIcon(false);
@@ -937,17 +937,11 @@
     }
 }
 
-/**
- * Helper class for showing a message dialog.
- */
+/** Helper class for showing a message dialog. */
 class MessageDialogWrapper {
 
-    /**
-     * Shows a message dialog, wrapping the <code>msg</code> at 60
-     * columns.
-     */
-    public static void showMessageDialog(Component parent, String msg,
-                                         String title, int flags) {
+    /** Shows a message dialog, wrapping the <code>msg</code> at 60 columns. */
+    public static void showMessageDialog(Component parent, String msg, String title, int flags) {
         if (msg.length() > 60) {
             StringBuilder buf = new StringBuilder();
             int len = msg.length();
@@ -978,41 +972,25 @@
     }
 }
 
-/**
- * Extension of JTextArea for script evaluation input.
- */
-class EvalTextArea
-    extends JTextArea
-    implements KeyListener, DocumentListener {
+/** Extension of JTextArea for script evaluation input. */
+class EvalTextArea extends JTextArea implements KeyListener, DocumentListener {
 
-    /**
-     * Serializable magic number.
-     */
+    /** Serializable magic number. */
     private static final long serialVersionUID = -3918033649601064194L;
 
-    /**
-     * The debugger GUI.
-     */
+    /** The debugger GUI. */
     private SwingGui debugGui;
 
-    /**
-     * History of expressions that have been evaluated
-     */
+    /** History of expressions that have been evaluated */
     private List<String> history;
 
-    /**
-     * Index of the selected history item.
-     */
+    /** Index of the selected history item. */
     private int historyIndex = -1;
 
-    /**
-     * Position in the display where output should go.
-     */
+    /** Position in the display where output should go. */
     private int outputMark;
 
-    /**
-     * Creates a new EvalTextArea.
-     */
+    /** Creates a new EvalTextArea. */
     public EvalTextArea(SwingGui debugGui) {
         this.debugGui = debugGui;
         history = Collections.synchronizedList(new ArrayList<String>());
@@ -1020,23 +998,19 @@
         doc.addDocumentListener(this);
         addKeyListener(this);
         setLineWrap(true);
-        setFont(new Font("Monospaced", 0, 12));
+        setFont(new Font("Monospaced", 0, Math.max(12, UIManager.getFont("Label.font").getSize())));
         append("% ");
         outputMark = doc.getLength();
     }
 
-    /**
-     * Selects a subrange of the text.
-     */
+    /** Selects a subrange of the text. */
     @Override
     public void select(int start, int end) {
-        //requestFocus();
+        // requestFocus();
         super.select(start, end);
     }
 
-    /**
-     * Called when Enter is pressed.
-     */
+    /** Called when Enter is pressed. */
     private synchronized void returnPressed() {
         Document doc = getDocument();
         int len = doc.getLength();
@@ -1049,8 +1023,8 @@
         String text = segment.toString();
         if (debugGui.dim.stringIsCompilableUnit(text)) {
             if (text.trim().length() > 0) {
-               history.add(text);
-               historyIndex = history.size();
+                history.add(text);
+                historyIndex = history.size();
             }
             append("\n");
             String result = debugGui.dim.eval(text);
@@ -1065,9 +1039,7 @@
         }
     }
 
-    /**
-     * Writes output into the text area.
-     */
+    /** Writes output into the text area. */
     public synchronized void write(String str) {
         insert(str, outputMark);
         int len = str.length();
@@ -1077,9 +1049,8 @@
 
     // KeyListener
 
-    /**
-     * Called when a key is pressed.
-     */
+    /** Called when a key is pressed. */
+    @Override
     public void keyPressed(KeyEvent e) {
         int code = e.getKeyCode();
         if (code == KeyEvent.VK_BACK_SPACE || code == KeyEvent.VK_LEFT) {
@@ -1087,19 +1058,19 @@
                 e.consume();
             }
         } else if (code == KeyEvent.VK_HOME) {
-           int caretPos = getCaretPosition();
-           if (caretPos == outputMark) {
-               e.consume();
-           } else if (caretPos > outputMark) {
-               if (!e.isControlDown()) {
-                   if (e.isShiftDown()) {
-                       moveCaretPosition(outputMark);
-                   } else {
-                       setCaretPosition(outputMark);
-                   }
-                   e.consume();
-               }
-           }
+            int caretPos = getCaretPosition();
+            if (caretPos == outputMark) {
+                e.consume();
+            } else if (caretPos > outputMark) {
+                if (!e.isControlDown()) {
+                    if (e.isShiftDown()) {
+                        moveCaretPosition(outputMark);
+                    } else {
+                        setCaretPosition(outputMark);
+                    }
+                    e.consume();
+                }
+            }
         } else if (code == KeyEvent.VK_ENTER) {
             returnPressed();
             e.consume();
@@ -1107,7 +1078,7 @@
             historyIndex--;
             if (historyIndex >= 0) {
                 if (historyIndex >= history.size()) {
-                    historyIndex = history.size() -1;
+                    historyIndex = history.size() - 1;
                 }
                 if (historyIndex >= 0) {
                     String str = history.get(historyIndex);
@@ -1126,7 +1097,9 @@
             int caretPos = outputMark;
             if (history.size() > 0) {
                 historyIndex++;
-                if (historyIndex < 0) {historyIndex = 0;}
+                if (historyIndex < 0) {
+                    historyIndex = 0;
+                }
                 int len = getDocument().getLength();
                 if (historyIndex < history.size()) {
                     String str = history.get(historyIndex);
@@ -1142,9 +1115,8 @@
         }
     }
 
-    /**
-     * Called when a key is typed.
-     */
+    /** Called when a key is typed. */
+    @Override
     public void keyTyped(KeyEvent e) {
         int keyChar = e.getKeyChar();
         if (keyChar == 0x8 /* KeyEvent.VK_BACK_SPACE */) {
@@ -1156,17 +1128,14 @@
         }
     }
 
-    /**
-     * Called when a key is released.
-     */
-    public synchronized void keyReleased(KeyEvent e) {
-    }
+    /** Called when a key is released. */
+    @Override
+    public synchronized void keyReleased(KeyEvent e) {}
 
     // DocumentListener
 
-    /**
-     * Called when text was inserted into the text area.
-     */
+    /** Called when text was inserted into the text area. */
+    @Override
     public synchronized void insertUpdate(DocumentEvent e) {
         int len = e.getLength();
         int off = e.getOffset();
@@ -1175,9 +1144,8 @@
         }
     }
 
-    /**
-     * Called when text was removed from the text area.
-     */
+    /** Called when text was removed from the text area. */
+    @Override
     public synchronized void removeUpdate(DocumentEvent e) {
         int len = e.getLength();
         int off = e.getOffset();
@@ -1190,40 +1158,28 @@
         }
     }
 
-    /**
-     * Attempts to clean up the damage done by {@link #updateUI()}.
-     */
+    /** Attempts to clean up the damage done by {@link #updateUI()}. */
     public synchronized void postUpdateUI() {
-        //requestFocus();
+        // requestFocus();
         setCaret(getCaret());
         select(outputMark, outputMark);
     }
 
-    /**
-     * Called when text has changed in the text area.
-     */
-    public synchronized void changedUpdate(DocumentEvent e) {
-    }
+    /** Called when text has changed in the text area. */
+    @Override
+    public synchronized void changedUpdate(DocumentEvent e) {}
 }
 
-/**
- * An internal frame for evaluating script.
- */
+/** An internal frame for evaluating script. */
 class EvalWindow extends JInternalFrame implements ActionListener {
 
-    /**
-     * Serializable magic number.
-     */
+    /** Serializable magic number. */
     private static final long serialVersionUID = -2860585845212160176L;
 
-    /**
-     * The text area into which expressions can be typed.
-     */
+    /** The text area into which expressions can be typed. */
     private EvalTextArea evalTextArea;
 
-    /**
-     * Creates a new EvalWindow.
-     */
+    /** Creates a new EvalWindow. */
     public EvalWindow(String name, SwingGui debugGui) {
         super(name, true, false, true, true);
         evalTextArea = new EvalTextArea(debugGui);
@@ -1231,14 +1187,12 @@
         evalTextArea.setColumns(80);
         JScrollPane scroller = new JScrollPane(evalTextArea);
         setContentPane(scroller);
-        //scroller.setPreferredSize(new Dimension(600, 400));
+        // scroller.setPreferredSize(new Dimension(600, 400));
         pack();
         setVisible(true);
     }
 
-    /**
-     * Sets whether the text area is enabled.
-     */
+    /** Sets whether the text area is enabled. */
     @Override
     public void setEnabled(boolean b) {
         super.setEnabled(b);
@@ -1247,9 +1201,8 @@
 
     // ActionListener
 
-    /**
-     * Performs an action on the text area.
-     */
+    /** Performs an action on the text area. */
+    @Override
     public void actionPerformed(ActionEvent e) {
         String cmd = e.getActionCommand();
         if (cmd.equals("Cut")) {
@@ -1262,19 +1215,13 @@
     }
 }
 
-/**
- * Internal frame for the console.
- */
+/** Internal frame for the console. */
 class JSInternalConsole extends JInternalFrame implements ActionListener {
 
-    /**
-     * Serializable magic number.
-     */
+    /** Serializable magic number. */
     private static final long serialVersionUID = -5523468828771087292L;
 
-    /**
-     * Creates a new JSInternalConsole.
-     */
+    /** Creates a new JSInternalConsole. */
     public JSInternalConsole(String name) {
         super(name, true, false, true, true);
         consoleTextArea = new ConsoleTextArea(null);
@@ -1283,49 +1230,41 @@
         JScrollPane scroller = new JScrollPane(consoleTextArea);
         setContentPane(scroller);
         pack();
-        addInternalFrameListener(new InternalFrameAdapter() {
-                @Override
-                public void internalFrameActivated(InternalFrameEvent e) {
-                    // hack
-                    if (consoleTextArea.hasFocus()) {
-                        consoleTextArea.getCaret().setVisible(false);
-                        consoleTextArea.getCaret().setVisible(true);
+        addInternalFrameListener(
+                new InternalFrameAdapter() {
+                    @Override
+                    public void internalFrameActivated(InternalFrameEvent e) {
+                        // hack
+                        if (consoleTextArea.hasFocus()) {
+                            consoleTextArea.getCaret().setVisible(false);
+                            consoleTextArea.getCaret().setVisible(true);
+                        }
                     }
-                }
-            });
+                });
     }
 
-    /**
-     * The console text area.
-     */
+    /** The console text area. */
     ConsoleTextArea consoleTextArea;
 
-    /**
-     * Returns the input stream of the console text area.
-     */
+    /** Returns the input stream of the console text area. */
     public InputStream getIn() {
         return consoleTextArea.getIn();
     }
 
-    /**
-     * Returns the output stream of the console text area.
-     */
+    /** Returns the output stream of the console text area. */
     public PrintStream getOut() {
         return consoleTextArea.getOut();
     }
 
-    /**
-     * Returns the error stream of the console text area.
-     */
+    /** Returns the error stream of the console text area. */
     public PrintStream getErr() {
         return consoleTextArea.getErr();
     }
 
     // ActionListener
 
-    /**
-     * Performs an action on the text area.
-     */
+    /** Performs an action on the text area. */
+    @Override
     public void actionPerformed(ActionEvent e) {
         String cmd = e.getActionCommand();
         if (cmd.equals("Cut")) {
@@ -1338,29 +1277,19 @@
     }
 }
 
-/**
- * Popup menu class for right-clicking on {@link FileTextArea}s.
- */
+/** Popup menu class for right-clicking on {@link FileTextArea}s. */
 class FilePopupMenu extends JPopupMenu {
 
-    /**
-     * Serializable magic number.
-     */
+    /** Serializable magic number. */
     private static final long serialVersionUID = 3589525009546013565L;
 
-    /**
-     * The popup x position.
-     */
+    /** The popup x position. */
     int x;
 
-    /**
-     * The popup y position.
-     */
+    /** The popup y position. */
     int y;
 
-    /**
-     * Creates a new FilePopupMenu.
-     */
+    /** Creates a new FilePopupMenu. */
     public FilePopupMenu(FileTextArea w) {
         JMenuItem item;
         add(item = new JMenuItem("Set Breakpoint"));
@@ -1371,9 +1300,7 @@
         item.addActionListener(w);
     }
 
-    /**
-     * Displays the menu at the given coordinates.
-     */
+    /** Displays the menu at the given coordinates. */
     public void show(JComponent comp, int x, int y) {
         this.x = x;
         this.y = y;
@@ -1381,43 +1308,30 @@
     }
 }
 
-/**
- * Text area to display script source.
- */
-class FileTextArea
-    extends JTextArea
-    implements ActionListener, PopupMenuListener, KeyListener, MouseListener {
+/** Text area to display script source. */
+class FileTextArea extends JTextArea
+        implements ActionListener, PopupMenuListener, KeyListener, MouseListener {
 
-    /**
-     * Serializable magic number.
-     */
+    /** Serializable magic number. */
     private static final long serialVersionUID = -25032065448563720L;
 
-    /**
-     * The owning {@link FileWindow}.
-     */
+    /** The owning {@link FileWindow}. */
     private FileWindow w;
 
-    /**
-     * The popup menu.
-     */
+    /** The popup menu. */
     private FilePopupMenu popup;
 
-    /**
-     * Creates a new FileTextArea.
-     */
+    /** Creates a new FileTextArea. */
     public FileTextArea(FileWindow w) {
         this.w = w;
         popup = new FilePopupMenu(this);
         popup.addPopupMenuListener(this);
         addMouseListener(this);
         addKeyListener(this);
-        setFont(new Font("Monospaced", 0, 12));
+        setFont(new Font("Monospaced", 0, Math.max(12, UIManager.getFont("Label.font").getSize())));
     }
 
-    /**
-     * Moves the selection to the given offset.
-     */
+    /** Moves the selection to the given offset. */
     public void select(int pos) {
         if (pos >= 0) {
             try {
@@ -1427,35 +1341,32 @@
                     select(pos, pos);
                 } else {
                     try {
-                        Rectangle nrect =
-                            modelToView(getLineStartOffset(line + 1));
+                        Rectangle nrect = modelToView(getLineStartOffset(line + 1));
                         if (nrect != null) {
                             rect = nrect;
                         }
                     } catch (Exception exc) {
                     }
-                    JViewport vp = (JViewport)getParent();
+                    JViewport vp = (JViewport) getParent();
                     Rectangle viewRect = vp.getViewRect();
                     if (viewRect.y + viewRect.height > rect.y) {
                         // need to scroll up
                         select(pos, pos);
                     } else {
                         // need to scroll down
-                        rect.y += (viewRect.height - rect.height)/2;
+                        rect.y += (viewRect.height - rect.height) / 2;
                         scrollRectToVisible(rect);
                         select(pos, pos);
                     }
                 }
             } catch (BadLocationException exc) {
                 select(pos, pos);
-                //exc.printStackTrace();
+                // exc.printStackTrace();
             }
         }
     }
 
-    /**
-     * Checks if the popup menu should be shown.
-     */
+    /** Checks if the popup menu should be shown. */
     private void checkPopup(MouseEvent e) {
         if (e.isPopupTrigger()) {
             popup.show(this, e.getX(), e.getY());
@@ -1464,66 +1375,52 @@
 
     // MouseListener
 
-    /**
-     * Called when a mouse button is pressed.
-     */
+    /** Called when a mouse button is pressed. */
+    @Override
     public void mousePressed(MouseEvent e) {
         checkPopup(e);
     }
 
-    /**
-     * Called when the mouse is clicked.
-     */
+    /** Called when the mouse is clicked. */
+    @Override
     public void mouseClicked(MouseEvent e) {
         checkPopup(e);
         requestFocus();
         getCaret().setVisible(true);
     }
 
-    /**
-     * Called when the mouse enters the component.
-     */
-    public void mouseEntered(MouseEvent e) {
-    }
+    /** Called when the mouse enters the component. */
+    @Override
+    public void mouseEntered(MouseEvent e) {}
 
-    /**
-     * Called when the mouse exits the component.
-     */
-    public void mouseExited(MouseEvent e) {
-    }
+    /** Called when the mouse exits the component. */
+    @Override
+    public void mouseExited(MouseEvent e) {}
 
-    /**
-     * Called when a mouse button is released.
-     */
+    /** Called when a mouse button is released. */
+    @Override
     public void mouseReleased(MouseEvent e) {
         checkPopup(e);
     }
 
     // PopupMenuListener
 
-    /**
-     * Called before the popup menu will become visible.
-     */
-    public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
-    }
+    /** Called before the popup menu will become visible. */
+    @Override
+    public void popupMenuWillBecomeVisible(PopupMenuEvent e) {}
 
-    /**
-     * Called before the popup menu will become invisible.
-     */
-    public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
-    }
+    /** Called before the popup menu will become invisible. */
+    @Override
+    public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {}
 
-    /**
-     * Called when the popup menu is cancelled.
-     */
-    public void popupMenuCanceled(PopupMenuEvent e) {
-    }
+    /** Called when the popup menu is cancelled. */
+    @Override
+    public void popupMenuCanceled(PopupMenuEvent e) {}
 
     // ActionListener
 
-    /**
-     * Performs an action.
-     */
+    /** Performs an action. */
+    @Override
     public void actionPerformed(ActionEvent e) {
         int pos = viewToModel(new Point(popup.x, popup.y));
         popup.setVisible(false);
@@ -1544,118 +1441,99 @@
 
     // KeyListener
 
-    /**
-     * Called when a key is pressed.
-     */
+    /** Called when a key is pressed. */
+    @Override
     public void keyPressed(KeyEvent e) {
         switch (e.getKeyCode()) {
-        case KeyEvent.VK_BACK_SPACE:
-        case KeyEvent.VK_ENTER:
-        case KeyEvent.VK_DELETE:
-        case KeyEvent.VK_TAB:
-            e.consume();
-            break;
+            case KeyEvent.VK_BACK_SPACE:
+            case KeyEvent.VK_ENTER:
+            case KeyEvent.VK_DELETE:
+            case KeyEvent.VK_TAB:
+                e.consume();
+                break;
         }
     }
 
-    /**
-     * Called when a key is typed.
-     */
+    /** Called when a key is typed. */
+    @Override
     public void keyTyped(KeyEvent e) {
         e.consume();
     }
 
-    /**
-     * Called when a key is released.
-     */
+    /** Called when a key is released. */
+    @Override
     public void keyReleased(KeyEvent e) {
         e.consume();
     }
 }
 
-/**
- * Dialog to list the available windows.
- */
+/** Dialog to list the available windows. */
 class MoreWindows extends JDialog implements ActionListener {
 
-    /**
-     * Serializable magic number.
-     */
+    /** Serializable magic number. */
     private static final long serialVersionUID = 5177066296457377546L;
 
-    /**
-     * Last selected value.
-     */
+    /** Last selected value. */
     private String value;
 
-    /**
-     * The list component.
-     */
-    private JList list;
+    /** The list component. */
+    private JList<String> list;
 
-    /**
-     * Our parent frame.
-     */
+    /** Our parent frame. */
     private SwingGui swingGui;
 
-    /**
-     * The "Select" button.
-     */
+    /** The "Select" button. */
     private JButton setButton;
 
-    /**
-     * The "Cancel" button.
-     */
+    /** The "Cancel" button. */
     private JButton cancelButton;
 
-    /**
-     * Creates a new MoreWindows.
-     */
-    MoreWindows(SwingGui frame, Map<String,FileWindow> fileWindows, String title,
-                String labelText) {
+    /** Creates a new MoreWindows. */
+    MoreWindows(
+            SwingGui frame, Map<String, FileWindow> fileWindows, String title, String labelText) {
         super(frame, title, true);
         this.swingGui = frame;
-        //buttons
+        // buttons
         cancelButton = new JButton("Cancel");
         setButton = new JButton("Select");
         cancelButton.addActionListener(this);
         setButton.addActionListener(this);
         getRootPane().setDefaultButton(setButton);
 
-        //dim part of the dialog
-        list = new JList(new DefaultListModel());
-        DefaultListModel model = (DefaultListModel)list.getModel();
+        // dim part of the dialog
+        list = new JList<>(new DefaultListModel<String>());
+        DefaultListModel<String> model = (DefaultListModel<String>) list.getModel();
         model.clear();
-        //model.fireIntervalRemoved(model, 0, size);
-        for (String data: fileWindows.keySet()) {
+        // model.fireIntervalRemoved(model, 0, size);
+        for (String data : fileWindows.keySet()) {
             model.addElement(data);
         }
         list.setSelectedIndex(0);
-        //model.fireIntervalAdded(model, 0, data.length);
+        // model.fireIntervalAdded(model, 0, data.length);
         setButton.setEnabled(true);
         list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
         list.addMouseListener(new MouseHandler());
         JScrollPane listScroller = new JScrollPane(list);
         listScroller.setPreferredSize(new Dimension(320, 240));
-        //XXX: Must do the following, too, or else the scroller thinks
-        //XXX: it's taller than it is:
+        // XXX: Must do the following, too, or else the scroller thinks
+        // XXX: it's taller than it is:
         listScroller.setMinimumSize(new Dimension(250, 80));
         listScroller.setAlignmentX(LEFT_ALIGNMENT);
 
-        //Create a container so that we can add a title around
-        //the scroll pane.  Can't add a title directly to the
-        //scroll pane because its background would be white.
-        //Lay out the label and scroll pane from top to button.
+        // Create a container so that we can add a title around
+        // the scroll pane.  Can't add a title directly to the
+        // scroll pane because its background would be white.
+        // Lay out the label and scroll pane from top to button.
         JPanel listPane = new JPanel();
         listPane.setLayout(new BoxLayout(listPane, BoxLayout.Y_AXIS));
         JLabel label = new JLabel(labelText);
-        label.setLabelFor (list);
+        label.setLabelFor(list);
         listPane.add(label);
-        listPane.add(Box.createRigidArea(new Dimension(0,5)));
+        listPane.add(Box.createRigidArea(new Dimension(0, 5)));
         listPane.add(listScroller);
-        listPane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+        listPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
 
-        //Lay out the buttons from left to right.
+        // Lay out the buttons from left to right.
         JPanel buttonPane = new JPanel();
         buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.X_AXIS));
         buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
@@ -1664,27 +1542,26 @@
         buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
         buttonPane.add(setButton);
 
-        //Put everything together, using the content pane's BorderLayout.
+        // Put everything together, using the content pane's BorderLayout.
         Container contentPane = getContentPane();
         contentPane.add(listPane, BorderLayout.CENTER);
         contentPane.add(buttonPane, BorderLayout.SOUTH);
         pack();
-        addKeyListener(new KeyAdapter() {
-                @Override
-                public void keyPressed(KeyEvent ke) {
-                    int code = ke.getKeyCode();
-                    if (code == KeyEvent.VK_ESCAPE) {
-                        ke.consume();
-                        value = null;
-                        setVisible(false);
+        addKeyListener(
+                new KeyAdapter() {
+                    @Override
+                    public void keyPressed(KeyEvent ke) {
+                        int code = ke.getKeyCode();
+                        if (code == KeyEvent.VK_ESCAPE) {
+                            ke.consume();
+                            value = null;
+                            setVisible(false);
+                        }
                     }
-                }
-            });
+                });
     }
 
-    /**
-     * Shows the dialog.
-     */
+    /** Shows the dialog. */
     public String showDialog(Component comp) {
         value = null;
         setLocationRelativeTo(comp);
@@ -1694,24 +1571,21 @@
 
     // ActionListener
 
-    /**
-     * Performs an action.
-     */
+    /** Performs an action. */
+    @Override
     public void actionPerformed(ActionEvent e) {
         String cmd = e.getActionCommand();
         if (cmd.equals("Cancel")) {
             setVisible(false);
             value = null;
         } else if (cmd.equals("Select")) {
-            value = (String)list.getSelectedValue();
+            value = list.getSelectedValue();
             setVisible(false);
             swingGui.showFileWindow(value, -1);
         }
     }
 
-    /**
-     * MouseListener implementation for {@link #list}.
-     */
+    /** MouseListener implementation for {@link #list}. */
     private class MouseHandler extends MouseAdapter {
         @Override
         public void mouseClicked(MouseEvent e) {
@@ -1722,44 +1596,28 @@
     }
 }
 
-/**
- * Find function dialog.
- */
+/** Find function dialog. */
 class FindFunction extends JDialog implements ActionListener {
 
-    /**
-     * Serializable magic number.
-     */
+    /** Serializable magic number. */
     private static final long serialVersionUID = 559491015232880916L;
 
-    /**
-     * Last selected function.
-     */
+    /** Last selected function. */
     private String value;
 
-    /**
-     * List of functions.
-     */
-    private JList list;
+    /** List of functions. */
+    private JList<String> list;
 
-    /**
-     * The debug GUI frame.
-     */
+    /** The debug GUI frame. */
     private SwingGui debugGui;
 
-    /**
-     * The "Select" button.
-     */
+    /** The "Select" button. */
     private JButton setButton;
 
-    /**
-     * The "Cancel" button.
-     */
+    /** The "Cancel" button. */
     private JButton cancelButton;
 
-    /**
-     * Creates a new FindFunction.
-     */
+    /** Creates a new FindFunction. */
     public FindFunction(SwingGui debugGui, String title, String labelText) {
         super(debugGui, title, true);
         this.debugGui = debugGui;
@@ -1770,8 +1628,8 @@
         setButton.addActionListener(this);
         getRootPane().setDefaultButton(setButton);
 
-        list = new JList(new DefaultListModel());
-        DefaultListModel model = (DefaultListModel)list.getModel();
+        list = new JList<>(new DefaultListModel<String>());
+        DefaultListModel<String> model = (DefaultListModel<String>) list.getModel();
         model.clear();
 
         String[] a = debugGui.dim.functionNames();
@@ -1789,20 +1647,20 @@
         listScroller.setMinimumSize(new Dimension(250, 80));
         listScroller.setAlignmentX(LEFT_ALIGNMENT);
 
-        //Create a container so that we can add a title around
-        //the scroll pane.  Can't add a title directly to the
-        //scroll pane because its background would be white.
-        //Lay out the label and scroll pane from top to button.
+        // Create a container so that we can add a title around
+        // the scroll pane.  Can't add a title directly to the
+        // scroll pane because its background would be white.
+        // Lay out the label and scroll pane from top to button.
         JPanel listPane = new JPanel();
         listPane.setLayout(new BoxLayout(listPane, BoxLayout.Y_AXIS));
         JLabel label = new JLabel(labelText);
-        label.setLabelFor (list);
+        label.setLabelFor(list);
         listPane.add(label);
-        listPane.add(Box.createRigidArea(new Dimension(0,5)));
+        listPane.add(Box.createRigidArea(new Dimension(0, 5)));
         listPane.add(listScroller);
-        listPane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+        listPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
 
-        //Lay out the buttons from left to right.
+        // Lay out the buttons from left to right.
         JPanel buttonPane = new JPanel();
         buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.X_AXIS));
         buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
@@ -1811,27 +1669,26 @@
         buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
         buttonPane.add(setButton);
 
-        //Put everything together, using the content pane's BorderLayout.
+        // Put everything together, using the content pane's BorderLayout.
         Container contentPane = getContentPane();
         contentPane.add(listPane, BorderLayout.CENTER);
         contentPane.add(buttonPane, BorderLayout.SOUTH);
         pack();
-        addKeyListener(new KeyAdapter() {
-                @Override
-                public void keyPressed(KeyEvent ke) {
-                    int code = ke.getKeyCode();
-                    if (code == KeyEvent.VK_ESCAPE) {
-                        ke.consume();
-                        value = null;
-                        setVisible(false);
+        addKeyListener(
+                new KeyAdapter() {
+                    @Override
+                    public void keyPressed(KeyEvent ke) {
+                        int code = ke.getKeyCode();
+                        if (code == KeyEvent.VK_ESCAPE) {
+                            ke.consume();
+                            value = null;
+                            setVisible(false);
+                        }
                     }
-                }
-            });
+                });
     }
 
-    /**
-     * Shows the dialog.
-     */
+    /** Shows the dialog. */
     public String showDialog(Component comp) {
         value = null;
         setLocationRelativeTo(comp);
@@ -1841,9 +1698,8 @@
 
     // ActionListener
 
-    /**
-     * Performs an action.
-     */
+    /** Performs an action. */
+    @Override
     public void actionPerformed(ActionEvent e) {
         String cmd = e.getActionCommand();
         if (cmd.equals("Cancel")) {
@@ -1854,7 +1710,7 @@
                 return;
             }
             try {
-                value = (String)list.getSelectedValue();
+                value = list.getSelectedValue();
             } catch (ArrayIndexOutOfBoundsException exc) {
                 return;
             }
@@ -1869,9 +1725,7 @@
         }
     }
 
-    /**
-     * MouseListener implementation for {@link #list}.
-     */
+    /** MouseListener implementation for {@link #list}. */
     class MouseHandler extends MouseAdapter {
         @Override
         public void mouseClicked(MouseEvent e) {
@@ -1882,38 +1736,26 @@
     }
 }
 
-/**
- * Gutter for FileWindows.
- */
+/** Gutter for FileWindows. */
 class FileHeader extends JPanel implements MouseListener {
 
-    /**
-     * Serializable magic number.
-     */
+    /** Serializable magic number. */
     private static final long serialVersionUID = -2858905404778259127L;
 
-    /**
-     * The line that the mouse was pressed on.
-     */
+    /** The line that the mouse was pressed on. */
     private int pressLine = -1;
 
-    /**
-     * The owning FileWindow.
-     */
+    /** The owning FileWindow. */
     private FileWindow fileWindow;
 
-    /**
-     * Creates a new FileHeader.
-     */
+    /** Creates a new FileHeader. */
     public FileHeader(FileWindow fileWindow) {
         this.fileWindow = fileWindow;
         addMouseListener(this);
         update();
     }
 
-    /**
-     * Updates the gutter.
-     */
+    /** Updates the gutter. */
     public void update() {
         FileTextArea textArea = fileWindow.textArea;
         Font font = textArea.getFont();
@@ -1932,9 +1774,7 @@
         setSize(d);
     }
 
-    /**
-     * Paints the component.
-     */
+    /** Paints the component. */
     @Override
     public void paint(Graphics g) {
         super.paint(g);
@@ -2001,15 +1841,12 @@
 
     // MouseListener
 
-    /**
-     * Called when the mouse enters the component.
-     */
-    public void mouseEntered(MouseEvent e) {
-    }
+    /** Called when the mouse enters the component. */
+    @Override
+    public void mouseEntered(MouseEvent e) {}
 
-    /**
-     * Called when a mouse button is pressed.
-     */
+    /** Called when a mouse button is pressed. */
+    @Override
     public void mousePressed(MouseEvent e) {
         Font font = fileWindow.textArea.getFont();
         FontMetrics metrics = getFontMetrics(font);
@@ -2017,29 +1854,23 @@
         pressLine = e.getY() / h;
     }
 
-    /**
-     * Called when the mouse is clicked.
-     */
-    public void mouseClicked(MouseEvent e) {
-    }
+    /** Called when the mouse is clicked. */
+    @Override
+    public void mouseClicked(MouseEvent e) {}
 
-    /**
-     * Called when the mouse exits the component.
-     */
-    public void mouseExited(MouseEvent e) {
-    }
+    /** Called when the mouse exits the component. */
+    @Override
+    public void mouseExited(MouseEvent e) {}
 
-    /**
-     * Called when a mouse button is released.
-     */
+    /** Called when a mouse button is released. */
+    @Override
     public void mouseReleased(MouseEvent e) {
-        if (e.getComponent() == this
-                && (e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) {
+        if (e.getComponent() == this && (e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) {
             int y = e.getY();
             Font font = fileWindow.textArea.getFont();
             FontMetrics metrics = getFontMetrics(font);
             int h = metrics.getHeight();
-            int line = y/h;
+            int line = y / h;
             if (line == pressLine) {
                 fileWindow.toggleBreakPoint(line + 1);
             } else {
@@ -2049,49 +1880,31 @@
     }
 }
 
-/**
- * An internal frame for script files.
- */
+/** An internal frame for script files. */
 class FileWindow extends JInternalFrame implements ActionListener {
 
-    /**
-     * Serializable magic number.
-     */
+    /** Serializable magic number. */
     private static final long serialVersionUID = -6212382604952082370L;
 
-    /**
-     * The debugger GUI.
-     */
+    /** The debugger GUI. */
     private SwingGui debugGui;
 
-    /**
-     * The SourceInfo object that describes the file.
-     */
+    /** The SourceInfo object that describes the file. */
     private Dim.SourceInfo sourceInfo;
 
-    /**
-     * The FileTextArea that displays the file.
-     */
+    /** The FileTextArea that displays the file. */
     FileTextArea textArea;
 
-    /**
-     * The FileHeader that is the gutter for {@link #textArea}.
-     */
+    /** The FileHeader that is the gutter for {@link #textArea}. */
     private FileHeader fileHeader;
 
-    /**
-     * Scroll pane for containing {@link #textArea}.
-     */
+    /** Scroll pane for containing {@link #textArea}. */
     private JScrollPane p;
 
-    /**
-     * The current offset position.
-     */
+    /** The current offset position. */
     int currentPos;
 
-    /**
-     * Loads the file.
-     */
+    /** Loads the file. */
     void load() {
         String url = getUrl();
         if (url != null) {
@@ -2102,9 +1915,7 @@
         }
     }
 
-    /**
-     * Returns the offset position for the given line.
-     */
+    /** Returns the offset position for the given line. */
     public int getPosition(int line) {
         int result = -1;
         try {
@@ -2114,16 +1925,12 @@
         return result;
     }
 
-    /**
-     * Returns whether the given line has a breakpoint.
-     */
+    /** Returns whether the given line has a breakpoint. */
     public boolean isBreakPoint(int line) {
         return sourceInfo.breakableLine(line) && sourceInfo.breakpoint(line);
     }
 
-    /**
-     * Toggles the breakpoint on the given line.
-     */
+    /** Toggles the breakpoint on the given line. */
     public void toggleBreakPoint(int line) {
         if (!isBreakPoint(line)) {
             setBreakPoint(line);
@@ -2132,9 +1939,7 @@
         }
     }
 
-    /**
-     * Sets a breakpoint on the given line.
-     */
+    /** Sets a breakpoint on the given line. */
     public void setBreakPoint(int line) {
         if (sourceInfo.breakableLine(line)) {
             boolean changed = sourceInfo.breakpoint(line, true);
@@ -2144,9 +1949,7 @@
         }
     }
 
-    /**
-     * Clears a breakpoint from the given line.
-     */
+    /** Clears a breakpoint from the given line. */
     public void clearBreakPoint(int line) {
         if (sourceInfo.breakableLine(line)) {
             boolean changed = sourceInfo.breakpoint(line, false);
@@ -2156,12 +1959,9 @@
         }
     }
 
-    /**
-     * Creates a new FileWindow.
-     */
+    /** Creates a new FileWindow. */
     public FileWindow(SwingGui debugGui, Dim.SourceInfo sourceInfo) {
-        super(SwingGui.getShortName(sourceInfo.url()),
-              true, true, true, true);
+        super(SwingGui.getShortName(sourceInfo.url()), true, true, true, true);
         this.debugGui = debugGui;
         this.sourceInfo = sourceInfo;
         updateToolTip();
@@ -2179,9 +1979,7 @@
         textArea.select(0);
     }
 
-    /**
-     * Updates the tool tip contents.
-     */
+    /** Updates the tool tip contents. */
     private void updateToolTip() {
         // Try to set tool tip on frame. On Mac OS X 10.5,
         // the number of components is different, so try to be safe.
@@ -2194,20 +1992,16 @@
         Component c = getComponent(n);
         // this will work at least for Metal L&F
         if (c != null && c instanceof JComponent) {
-            ((JComponent)c).setToolTipText(getUrl());
+            ((JComponent) c).setToolTipText(getUrl());
         }
     }
 
-    /**
-     * Returns the URL of the source.
-     */
+    /** Returns the URL of the source. */
     public String getUrl() {
         return sourceInfo.url();
     }
 
-    /**
-     * Called when the text of the script has changed.
-     */
+    /** Called when the text of the script has changed. */
     public void updateText(Dim.SourceInfo sourceInfo) {
         this.sourceInfo = sourceInfo;
         String newText = sourceInfo.source();
@@ -2223,27 +2017,21 @@
         fileHeader.repaint();
     }
 
-    /**
-     * Sets the cursor position.
-     */
+    /** Sets the cursor position. */
     public void setPosition(int pos) {
         textArea.select(pos);
         currentPos = pos;
         fileHeader.repaint();
     }
 
-    /**
-     * Selects a range of characters.
-     */
+    /** Selects a range of characters. */
     public void select(int start, int end) {
         int docEnd = textArea.getDocument().getLength();
         textArea.select(docEnd, docEnd);
         textArea.select(start, end);
     }
 
-    /**
-     * Disposes this FileWindow.
-     */
+    /** Disposes this FileWindow. */
     @Override
     public void dispose() {
         debugGui.removeWindow(this);
@@ -2252,9 +2040,8 @@
 
     // ActionListener
 
-    /**
-     * Performs an action.
-     */
+    /** Performs an action. */
+    @Override
     public void actionPerformed(ActionEvent e) {
         String cmd = e.getActionCommand();
         if (cmd.equals("Cut")) {
@@ -2267,34 +2054,22 @@
     }
 }
 
-/**
- * Table model class for watched expressions.
- */
+/** Table model class for watched expressions. */
 class MyTableModel extends AbstractTableModel {
 
-    /**
-     * Serializable magic number.
-     */
+    /** Serializable magic number. */
     private static final long serialVersionUID = 2971618907207577000L;
 
-    /**
-     * The debugger GUI.
-     */
+    /** The debugger GUI. */
     private SwingGui debugGui;
 
-    /**
-     * List of watched expressions.
-     */
+    /** List of watched expressions. */
     private List<String> expressions;
 
-    /**
-     * List of values from evaluated from {@link #expressions}.
-     */
+    /** List of values from evaluated from {@link #expressions}. */
     private List<String> values;
 
-    /**
-     * Creates a new MyTableModel.
-     */
+    /** Creates a new MyTableModel. */
     public MyTableModel(SwingGui debugGui) {
         this.debugGui = debugGui;
         expressions = Collections.synchronizedList(new ArrayList<String>());
@@ -2303,86 +2078,75 @@
         values.add("");
     }
 
-    /**
-     * Returns the number of columns in the table (2).
-     */
+    /** Returns the number of columns in the table (2). */
+    @Override
     public int getColumnCount() {
         return 2;
     }
 
-    /**
-     * Returns the number of rows in the table.
-     */
+    /** Returns the number of rows in the table. */
+    @Override
     public int getRowCount() {
         return expressions.size();
     }
 
-    /**
-     * Returns the name of the given column.
-     */
+    /** Returns the name of the given column. */
     @Override
     public String getColumnName(int column) {
         switch (column) {
-        case 0:
-            return "Expression";
-        case 1:
-            return "Value";
+            case 0:
+                return "Expression";
+            case 1:
+                return "Value";
         }
         return null;
     }
 
-    /**
-     * Returns whether the given cell is editable.
-     */
+    /** Returns whether the given cell is editable. */
     @Override
     public boolean isCellEditable(int row, int column) {
         return true;
     }
 
-    /**
-     * Returns the value in the given cell.
-     */
+    /** Returns the value in the given cell. */
+    @Override
     public Object getValueAt(int row, int column) {
         switch (column) {
-        case 0:
-            return expressions.get(row);
-        case 1:
-            return values.get(row);
+            case 0:
+                return expressions.get(row);
+            case 1:
+                return values.get(row);
         }
         return "";
     }
 
-    /**
-     * Sets the value in the given cell.
-     */
+    /** Sets the value in the given cell. */
     @Override
     public void setValueAt(Object value, int row, int column) {
         switch (column) {
-        case 0:
-            String expr = value.toString();
-            expressions.set(row, expr);
-            String result = "";
-            if (expr.length() > 0) {
-                result = debugGui.dim.eval(expr);
-                if (result == null) result = "";
-            }
-            values.set(row, result);
-            updateModel();
-            if (row + 1 == expressions.size()) {
-                expressions.add("");
-                values.add("");
-                fireTableRowsInserted(row + 1, row + 1);
-            }
-            break;
-        case 1:
-            // just reset column 2; ignore edits
-            fireTableDataChanged();
+            case 0:
+                String expr = value.toString();
+                expressions.set(row, expr);
+                String result = "";
+                if (expr.length() > 0) {
+                    result = debugGui.dim.eval(expr);
+                    if (result == null) result = "";
+                }
+                values.set(row, result);
+                updateModel();
+                if (row + 1 == expressions.size()) {
+                    expressions.add("");
+                    values.add("");
+                    fireTableRowsInserted(row + 1, row + 1);
+                }
+                break;
+            case 1:
+                // just reset column 2; ignore edits
+                fireTableDataChanged();
         }
     }
 
-    /**
-     * Re-evaluates the expressions in the table.
-     */
+    /** Re-evaluates the expressions in the table. */
     void updateModel() {
         for (int i = 0; i < expressions.size(); ++i) {
             String expr = expressions.get(i);
@@ -2400,70 +2164,44 @@
     }
 }
 
-/**
- * A table for evaluated expressions.
- */
+/** A table for evaluated expressions. */
 class Evaluator extends JTable {
 
-    /**
-     * Serializable magic number.
-     */
+    /** Serializable magic number. */
     private static final long serialVersionUID = 8133672432982594256L;
 
-    /**
-     * The {@link TableModel} for this table.
-     */
+    /** The {@link TableModel} for this table. */
     MyTableModel tableModel;
 
-    /**
-     * Creates a new Evaluator.
-     */
+    /** Creates a new Evaluator. */
     public Evaluator(SwingGui debugGui) {
         super(new MyTableModel(debugGui));
-        tableModel = (MyTableModel)getModel();
+        tableModel = (MyTableModel) getModel();
     }
 }
 
-/**
- * Tree model for script object inspection.
- */
+/** Tree model for script object inspection. */
 class VariableModel implements TreeTableModel {
 
-    /**
-     * Serializable magic number.
-     */
-    private static final String[] cNames = { " Name", " Value" };
+    /** Serializable magic number. */
+    private static final String[] cNames = {" Name", " Value"};
 
-    /**
-     * Tree column types.
-     */
-    private static final Class<?>[] cTypes =
-        { TreeTableModel.class, String.class };
+    /** Tree column types. */
+    private static final Class<?>[] cTypes = {TreeTableModel.class, String.class};
 
-    /**
-     * Empty {@link VariableNode} array.
-     */
+    /** Empty {@link VariableNode} array. */
     private static final VariableNode[] CHILDLESS = new VariableNode[0];
 
-    /**
-     * The debugger.
-     */
+    /** The debugger. */
     private Dim debugger;
 
-    /**
-     * The root node.
-     */
+    /** The root node. */
     private VariableNode root;
 
-    /**
-     * Creates a new VariableModel.
-     */
-    public VariableModel() {
-    }
+    /** Creates a new VariableModel. */
+    public VariableModel() {}
 
-    /**
-     * Creates a new VariableModel.
-     */
+    /** Creates a new VariableModel. */
     public VariableModel(Dim debugger, Object scope) {
         this.debugger = debugger;
         this.root = new VariableNode(scope, "this");
@@ -2471,9 +2209,8 @@
 
     // TreeTableModel
 
-    /**
-     * Returns the root node of the tree.
-     */
+    /** Returns the root node of the tree. */
+    @Override
     public Object getRoot() {
         if (debugger == null) {
             return null;
@@ -2481,9 +2218,8 @@
         return root;
     }
 
-    /**
-     * Returns the number of children of the given node.
-     */
+    /** Returns the number of children of the given node. */
+    @Override
     public int getChildCount(Object nodeObj) {
         if (debugger == null) {
             return 0;
@@ -2492,9 +2228,8 @@
         return children(node).length;
     }
 
-    /**
-     * Returns a child of the given node.
-     */
+    /** Returns a child of the given node. */
+    @Override
     public Object getChild(Object nodeObj, int i) {
         if (debugger == null) {
             return null;
@@ -2503,9 +2238,8 @@
         return children(node)[i];
     }
 
-    /**
-     * Returns whether the given node is a leaf node.
-     */
+    /** Returns whether the given node is a leaf node. */
+    @Override
     public boolean isLeaf(Object nodeObj) {
         if (debugger == null) {
             return true;
@@ -2514,9 +2248,8 @@
         return children(node).length == 0;
     }
 
-    /**
-     * Returns the index of a node under its parent.
-     */
+    /** Returns the index of a node under its parent. */
+    @Override
     public int getIndexOfChild(Object parentObj, Object childObj) {
         if (debugger == null) {
             return -1;
@@ -2532,86 +2265,82 @@
         return -1;
     }
 
-    /**
-     * Returns whether the given cell is editable.
-     */
+    /** Returns whether the given cell is editable. */
+    @Override
     public boolean isCellEditable(Object node, int column) {
         return column == 0;
     }
 
-    /**
-     * Sets the value at the given cell.
-     */
-    public void setValueAt(Object value, Object node, int column) { }
+    /** Sets the value at the given cell. */
+    @Override
+    public void setValueAt(Object value, Object node, int column) {}
 
-    /**
-     * Adds a TreeModelListener to this tree.
-     */
-    public void addTreeModelListener(TreeModelListener l) { }
+    /** Adds a TreeModelListener to this tree. */
+    @Override
+    public void addTreeModelListener(TreeModelListener l) {}
 
-    /**
-     * Removes a TreeModelListener from this tree.
-     */
-    public void removeTreeModelListener(TreeModelListener l) { }
+    /** Removes a TreeModelListener from this tree. */
+    @Override
+    public void removeTreeModelListener(TreeModelListener l) {}
 
-    public void valueForPathChanged(TreePath path, Object newValue) { }
+    @Override
+    public void valueForPathChanged(TreePath path, Object newValue) {}
 
     // TreeTableNode
 
-    /**
-     * Returns the number of columns.
-     */
+    /** Returns the number of columns. */
+    @Override
     public int getColumnCount() {
         return cNames.length;
     }
 
-    /**
-     * Returns the name of the given column.
-     */
+    /** Returns the name of the given column. */
+    @Override
     public String getColumnName(int column) {
         return cNames[column];
     }
 
-    /**
-     * Returns the type of value stored in the given column.
-     */
+    /** Returns the type of value stored in the given column. */
+    @Override
     public Class<?> getColumnClass(int column) {
         return cTypes[column];
     }
 
-    /**
-     * Returns the value at the given cell.
-     */
+    /** Returns the value at the given cell. */
+    @Override
     public Object getValueAt(Object nodeObj, int column) {
-        if (debugger == null) { return null; }
-        VariableNode node = (VariableNode)nodeObj;
+        if (debugger == null) {
+            return null;
+        }
+        VariableNode node = (VariableNode) nodeObj;
         switch (column) {
-        case 0: // Name
-            return node.toString();
-        case 1: // Value
-            String result;
-            try {
-                result = debugger.objectToString(getValue(node));
-            } catch (RuntimeException exc) {
-                result = exc.getMessage();
-            }
-            StringBuilder buf = new StringBuilder();
-            int len = result.length();
-            for (int i = 0; i < len; i++) {
-                char ch = result.charAt(i);
-                if (Character.isISOControl(ch)) {
-                    ch = ' ';
+            case 0: // Name
+                return node.toString();
+            case 1: // Value
+                String result;
+                try {
+                    result = debugger.objectToString(getValue(node));
+                } catch (RuntimeException exc) {
+                    result = exc.getMessage();
+                    if (result == null) {
+                        result = exc.toString();
+                    }
+                }
+                StringBuilder buf = new StringBuilder();
+                int len = result.length();
+                for (int i = 0; i < len; i++) {
+                    char ch = result.charAt(i);
+                    if (Character.isISOControl(ch)) {
+                        ch = ' ';
+                    }
+                    buf.append(ch);
                 }
-                buf.append(ch);
-            }
-            return buf.toString();
+                return buf.toString();
         }
         return null;
     }
 
-    /**
-     * Returns an array of the children of the given node.
-     */
+    /** Returns an array of the children of the given node. */
     private VariableNode[] children(VariableNode node) {
         if (node.children != null) {
             return node.children;
@@ -2624,24 +2353,25 @@
         if (ids == null || ids.length == 0) {
             children = CHILDLESS;
         } else {
-            Arrays.sort(ids, new Comparator<Object>() {
-                    public int compare(Object l, Object r)
-                    {
-                        if (l instanceof String) {
-                            if (r instanceof Integer) {
-                                return -1;
+            Arrays.sort(
+                    ids,
+                    new Comparator<Object>() {
+                        @Override
+                        public int compare(Object l, Object r) {
+                            if (l instanceof String) {
+                                if (r instanceof Integer) {
+                                    return -1;
+                                }
+                                return ((String) l).compareToIgnoreCase((String) r);
                             }
-                            return ((String)l).compareToIgnoreCase((String)r);
-                        } else {
                             if (r instanceof String) {
                                 return 1;
                             }
-                            int lint = ((Integer)l).intValue();
-                            int rint = ((Integer)r).intValue();
+                            int lint = ((Integer) l).intValue();
+                            int rint = ((Integer) r).intValue();
                             return lint - rint;
                         }
-                    }
-            });
+                    });
             children = new VariableNode[ids.length];
             for (int i = 0; i != ids.length; ++i) {
                 children[i] = new VariableNode(value, ids[i]);
@@ -2651,9 +2381,7 @@
         return children;
     }
 
-    /**
-     * Returns the value of the given node.
-     */
+    /** Returns the value of the given node. */
     public Object getValue(VariableNode node) {
         try {
             return debugger.getObjectProperty(node.object, node.id);
@@ -2662,66 +2390,44 @@
         }
     }
 
-    /**
-     * A variable node in the tree.
-     */
+    /** A variable node in the tree. */
     private static class VariableNode {
 
-        /**
-         * The script object.
-         */
+        /** The script object. */
         private Object object;
 
-        /**
-         * The object name.  Either a String or an Integer.
-         */
+        /** The object name. Either a String or an Integer. */
         private Object id;
 
-        /**
-         * Array of child nodes.  This is filled with the properties of
-         * the object.
-         */
+        /** Array of child nodes. This is filled with the properties of the object. */
         private VariableNode[] children;
 
-        /**
-         * Creates a new VariableNode.
-         */
+        /** Creates a new VariableNode. */
         public VariableNode(Object object, Object id) {
             this.object = object;
             this.id = id;
         }
 
-        /**
-         * Returns a string representation of this node.
-         */
+        /** Returns a string representation of this node. */
         @Override
         public String toString() {
-            return id instanceof String
-                ? (String) id : "[" + ((Integer) id).intValue() + "]";
+            return id instanceof String ? (String) id : "[" + ((Integer) id).intValue() + "]";
         }
     }
 }
 
-/**
- * A tree table for browsing script objects.
- */
+/** A tree table for browsing script objects. */
 class MyTreeTable extends JTreeTable {
 
-    /**
-     * Serializable magic number.
-     */
+    /** Serializable magic number. */
     private static final long serialVersionUID = 3457265548184453049L;
 
-    /**
-     * Creates a new MyTreeTable.
-     */
+    /** Creates a new MyTreeTable. */
     public MyTreeTable(VariableModel model) {
         super(model);
     }
 
-    /**
-     * Initializes a tree for this tree table.
-     */
+    /** Initializes a tree for this tree table. */
     public JTree resetTree(TreeTableModel treeTableModel) {
         tree = new TreeTableCellRenderer(treeTableModel);
 
@@ -2729,8 +2435,7 @@
         super.setModel(new TreeTableModelAdapter(treeTableModel, tree));
 
         // Force the JTable and JTree to share their row selection models.
-        ListToTreeSelectionModelWrapper selectionWrapper = new
-            ListToTreeSelectionModelWrapper();
+        ListToTreeSelectionModelWrapper selectionWrapper = new ListToTreeSelectionModelWrapper();
         tree.setSelectionModel(selectionWrapper);
         setSelectionModel(selectionWrapper.getListSelectionModel());
 
@@ -2738,16 +2443,18 @@
         if (tree.getRowHeight() < 1) {
             // Metal looks better like this.
             setRowHeight(18);
+        } else if (tree.getRowHeight() != getRowHeight()) {
+            tree.setRowHeight(getRowHeight());
         }
 
         // Install the tree editor renderer and editor.
         setDefaultRenderer(TreeTableModel.class, tree);
         setDefaultEditor(TreeTableModel.class, new TreeTableCellEditor());
         setShowGrid(true);
-        setIntercellSpacing(new Dimension(1,1));
+        setIntercellSpacing(new Dimension(1, 1));
         tree.setRootVisible(false);
         tree.setShowsRootHandles(true);
-        DefaultTreeCellRenderer r = (DefaultTreeCellRenderer)tree.getCellRenderer();
+        DefaultTreeCellRenderer r = (DefaultTreeCellRenderer) tree.getCellRenderer();
         r.setOpenIcon(null);
         r.setClosedIcon(null);
         r.setLeafIcon(null);
@@ -2755,42 +2462,50 @@
     }
 
     /**
-     * Returns whether the cell under the coordinates of the mouse
-     * in the {@link EventObject} is editable.
+     * Returns whether the cell under the coordinates of the mouse in the {@link EventObject} is
+     * editable.
      */
     public boolean isCellEditable(EventObject e) {
         if (e instanceof MouseEvent) {
-            MouseEvent me = (MouseEvent)e;
+            MouseEvent me = (MouseEvent) e;
             // If the modifiers are not 0 (or the left mouse button),
             // tree may try and toggle the selection, and table
             // will then try and toggle, resulting in the
             // selection remaining the same. To avoid this, we
             // only dispatch when the modifiers are 0 (or the left mouse
             // button).
-            if (me.getModifiers() == 0 ||
-                ((me.getModifiers() & (InputEvent.BUTTON1_MASK|1024)) != 0 &&
-                 (me.getModifiers() &
-                  (InputEvent.SHIFT_MASK |
-                   InputEvent.CTRL_MASK |
-                   InputEvent.ALT_MASK |
-                   InputEvent.BUTTON2_MASK |
-                   InputEvent.BUTTON3_MASK |
-                   64   | //SHIFT_DOWN_MASK
-                   128  | //CTRL_DOWN_MASK
-                   512  | // ALT_DOWN_MASK
-                   2048 | //BUTTON2_DOWN_MASK
-                   4096   //BUTTON3_DOWN_MASK
-                   )) == 0)) {
+            if (me.getModifiers() == 0
+                    || ((me.getModifiers() & (InputEvent.BUTTON1_MASK | 1024)) != 0
+                            && (me.getModifiers()
+                                            & (InputEvent.SHIFT_MASK
+                                                    | InputEvent.CTRL_MASK
+                                                    | InputEvent.ALT_MASK
+                                                    | InputEvent.BUTTON2_MASK
+                                                    | InputEvent.BUTTON3_MASK
+                                                    | 64
+                                                    | // SHIFT_DOWN_MASK
+                                                    128
+                                                    | // CTRL_DOWN_MASK
+                                                    512
+                                                    | // ALT_DOWN_MASK
+                                                    2048
+                                                    | // BUTTON2_DOWN_MASK
+                                                    4096 // BUTTON3_DOWN_MASK
+                                            ))
+                                    == 0)) {
                 int row = rowAtPoint(me.getPoint());
-                for (int counter = getColumnCount() - 1; counter >= 0;
-                     counter--) {
+                for (int counter = getColumnCount() - 1; counter >= 0; counter--) {
                     if (TreeTableModel.class == getColumnClass(counter)) {
-                        MouseEvent newME = new MouseEvent
-                            (MyTreeTable.this.tree, me.getID(),
-                             me.getWhen(), me.getModifiers(),
-                             me.getX() - getCellRect(row, counter, true).x,
-                             me.getY(), me.getClickCount(),
-                             me.isPopupTrigger());
+                        MouseEvent newME =
+                                new MouseEvent(
+                                        MyTreeTable.this.tree,
+                                        me.getID(),
+                                        me.getWhen(),
+                                        me.getModifiers(),
+                                        me.getX() - getCellRect(row, counter, true).x,
+                                        me.getY(),
+                                        me.getClickCount(),
+                                        me.isPopupTrigger());
                         MyTreeTable.this.tree.dispatchEvent(newME);
                         break;
                     }
@@ -2808,79 +2523,49 @@
     }
 }
 
-/**
- * Panel that shows information about the context.
- */
+/** Panel that shows information about the context. */
 class ContextWindow extends JPanel implements ActionListener {
 
-    /**
-     * Serializable magic number.
-     */
+    /** Serializable magic number. */
     private static final long serialVersionUID = 2306040975490228051L;
 
-    /**
-     * The debugger GUI.
-     */
+    /** The debugger GUI. */
     private SwingGui debugGui;
 
-    /**
-     * The combo box that holds the stack frames.
-     */
-    JComboBox context;
+    /** The combo box that holds the stack frames. */
+    JComboBox<String> context;
 
-    /**
-     * Tool tips for the stack frames.
-     */
+    /** Tool tips for the stack frames. */
     List<String> toolTips;
 
-    /**
-     * Tabbed pane for "this" and "locals".
-     */
+    /** Tabbed pane for "this" and "locals". */
     private JTabbedPane tabs;
 
-    /**
-     * Tabbed pane for "watch" and "evaluate".
-     */
+    /** Tabbed pane for "watch" and "evaluate". */
     private JTabbedPane tabs2;
 
-    /**
-     * The table showing the "this" object.
-     */
+    /** The table showing the "this" object. */
     private MyTreeTable thisTable;
 
-    /**
-     * The table showing the stack local variables.
-     */
+    /** The table showing the stack local variables. */
     private MyTreeTable localsTable;
 
-    /**
-     * The {@link #evaluator}'s table model.
-     */
+    /** The {@link #evaluator}'s table model. */
     private MyTableModel tableModel;
 
-    /**
-     * The script evaluator table.
-     */
+    /** The script evaluator table. */
     private Evaluator evaluator;
 
-    /**
-     * The script evaluation text area.
-     */
+    /** The script evaluation text area. */
     private EvalTextArea cmdLine;
 
-    /**
-     * The split pane.
-     */
+    /** The split pane. */
     JSplitPane split;
 
-    /**
-     * Whether the ContextWindow is enabled.
-     */
+    /** Whether the ContextWindow is enabled. */
     private boolean enabled;
 
-    /**
-     * Creates a new ContextWindow.
-     */
+    /** Creates a new ContextWindow. */
     public ContextWindow(final SwingGui debugGui) {
         this.debugGui = debugGui;
         enabled = false;
@@ -2895,7 +2580,7 @@
         p2.setLayout(new GridLayout());
         p1.add(t1);
         JLabel label = new JLabel("Context:");
-        context = new JComboBox();
+        context = new JComboBox<>();
         context.setLightWeightPopupEnabled(false);
         toolTips = Collections.synchronizedList(new java.util.ArrayList<String>());
         label.setBorder(context.getBorder());
@@ -2916,17 +2601,17 @@
         layout.setConstraints(context, c);
         left.add(context);
         tabs = new JTabbedPane(SwingConstants.BOTTOM);
-        tabs.setPreferredSize(new Dimension(500,300));
+        tabs.setPreferredSize(new Dimension(500, 300));
         thisTable = new MyTreeTable(new VariableModel());
         JScrollPane jsp = new JScrollPane(thisTable);
-        jsp.getViewport().setViewSize(new Dimension(5,2));
+        jsp.getViewport().setViewSize(new Dimension(5, 2));
         tabs.add("this", jsp);
         localsTable = new MyTreeTable(new VariableModel());
         localsTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
         localsTable.setPreferredSize(null);
         jsp = new JScrollPane(localsTable);
         tabs.add("Locals", jsp);
-        c.weightx  = c.weighty = 1;
+        c.weightx = c.weighty = 1;
         c.gridheight = GridBagConstraints.REMAINDER;
         c.fill = GridBagConstraints.BOTH;
         c.anchor = GridBagConstraints.WEST;
@@ -2934,7 +2619,7 @@
         left.add(tabs);
         evaluator = new Evaluator(debugGui);
         cmdLine = new EvalTextArea(debugGui);
-        //cmdLine.requestFocus();
+        // cmdLine.requestFocus();
         tableModel = evaluator.tableModel;
         jsp = new JScrollPane(evaluator);
         JToolBar t2 = new JToolBar();
@@ -2942,13 +2627,12 @@
         tabs2 = new JTabbedPane(SwingConstants.BOTTOM);
         tabs2.add("Watch", jsp);
         tabs2.add("Evaluate", new JScrollPane(cmdLine));
-        tabs2.setPreferredSize(new Dimension(500,300));
+        tabs2.setPreferredSize(new Dimension(500, 300));
         t2.setLayout(new GridLayout());
         t2.add(tabs2);
         p2.add(t2);
         evaluator.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
-        split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
-                               p1, p2);
+        split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, p1, p2);
         split.setOneTouchExpandable(true);
         SwingGui.setResizeWeight(split, 0.5);
         setLayout(new BorderLayout());
@@ -2961,147 +2645,161 @@
         final JSplitPane finalSplit = split;
         final JPanel finalThis = this;
 
-        ComponentListener clistener = new ComponentListener() {
-                boolean t2Docked = true;
-                void check(Component comp) {
-                    Component thisParent = finalThis.getParent();
-                    if (thisParent == null) {
-                        return;
-                    }
-                    Component parent = finalT1.getParent();
-                    boolean leftDocked = true;
-                    boolean rightDocked = true;
-                    boolean adjustVerticalSplit = false;
-                    if (parent != null) {
-                        if (parent != finalP1) {
-                            while (!(parent instanceof JFrame)) {
-                                parent = parent.getParent();
+        ComponentListener clistener =
+                new ComponentListener() {
+                    boolean t2Docked = true;
+
+                    void check(Component comp) {
+                        Component thisParent = finalThis.getParent();
+                        if (thisParent == null) {
+                            return;
+                        }
+                        Component parent = finalT1.getParent();
+                        boolean leftDocked = true;
+                        boolean rightDocked = true;
+                        boolean adjustVerticalSplit = false;
+                        if (parent != null) {
+                            if (parent != finalP1) {
+                                while (!(parent instanceof JFrame)) {
+                                    parent = parent.getParent();
+                                }
+                                JFrame frame = (JFrame) parent;
+                                debugGui.addTopLevel("Variables", frame);
+
+                                // We need the following hacks because:
+                                // - We want an undocked toolbar to be
+                                //   resizable.
+                                // - We are using JToolbar as a container of a
+                                //   JComboBox. Without this JComboBox's popup
+                                //   can get left floating when the toolbar is
+                                //   re-docked.
+                                //
+                                // We make the frame resizable and then
+                                // remove JToolbar's window listener
+                                // and insert one of our own that first ensures
+                                // the JComboBox's popup window is closed
+                                // and then calls JToolbar's window listener.
+                                if (!frame.isResizable()) {
+                                    frame.setResizable(true);
+                                    frame.setDefaultCloseOperation(
+                                            WindowConstants.DO_NOTHING_ON_CLOSE);
+                                    final WindowListener[] l =
+                                            frame.getListeners(WindowListener.class);
+                                    frame.removeWindowListener(l[0]);
+                                    frame.addWindowListener(
+                                            new WindowAdapter() {
+                                                @Override
+                                                public void windowClosing(WindowEvent e) {
+                                                    context.hidePopup();
+                                                    l[0].windowClosing(e);
+                                                }
+                                            });
+                                    // adjustVerticalSplit = true;
+                                }
+                                leftDocked = false;
+                            } else {
+                                leftDocked = true;
                             }
-                            JFrame frame = (JFrame)parent;
-                            debugGui.addTopLevel("Variables", frame);
-
-                            // We need the following hacks because:
-                            // - We want an undocked toolbar to be
-                            //   resizable.
-                            // - We are using JToolbar as a container of a
-                            //   JComboBox. Without this JComboBox's popup
-                            //   can get left floating when the toolbar is
-                            //   re-docked.
-                            //
-                            // We make the frame resizable and then
-                            // remove JToolbar's window listener
-                            // and insert one of our own that first ensures
-                            // the JComboBox's popup window is closed
-                            // and then calls JToolbar's window listener.
-                            if (!frame.isResizable()) {
+                        }
+                        parent = finalT2.getParent();
+                        if (parent != null) {
+                            if (parent != finalP2) {
+                                while (!(parent instanceof JFrame)) {
+                                    parent = parent.getParent();
+                                }
+                                JFrame frame = (JFrame) parent;
+                                debugGui.addTopLevel("Evaluate", frame);
                                 frame.setResizable(true);
-                                frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
-                                final WindowListener[] l =
-                                    frame.getListeners(WindowListener.class);
-                                frame.removeWindowListener(l[0]);
-                                frame.addWindowListener(new WindowAdapter() {
-                                        @Override
-                                        public void windowClosing(WindowEvent e) {
-                                            context.hidePopup();
-                                            l[0].windowClosing(e);
-                                        }
-                                    });
-                                //adjustVerticalSplit = true;
+                                rightDocked = false;
+                            } else {
+                                rightDocked = true;
                             }
-                            leftDocked = false;
-                        } else {
-                            leftDocked = true;
                         }
-                    }
-                    parent = finalT2.getParent();
-                    if (parent != null) {
-                        if (parent != finalP2) {
-                            while (!(parent instanceof JFrame)) {
-                                parent = parent.getParent();
+                        if (leftDocked && t2Docked && rightDocked && t2Docked) {
+                            // no change
+                            return;
+                        }
+                        t2Docked = rightDocked;
+                        JSplitPane split = (JSplitPane) thisParent;
+                        if (leftDocked) {
+                            if (rightDocked) {
+                                finalSplit.setDividerLocation(0.5);
+                            } else {
+                                finalSplit.setDividerLocation(1.0);
+                            }
+                            if (adjustVerticalSplit) {
+                                split.setDividerLocation(0.66);
                             }
-                            JFrame frame = (JFrame)parent;
-                            debugGui.addTopLevel("Evaluate", frame);
-                            frame.setResizable(true);
-                            rightDocked = false;
+
+                        } else if (rightDocked) {
+                            finalSplit.setDividerLocation(0.0);
+                            split.setDividerLocation(0.66);
                         } else {
-                            rightDocked = true;
+                            // both undocked
+                            split.setDividerLocation(1.0);
                         }
                     }
-                    if (leftDocked && t2Docked && rightDocked && t2Docked) {
-                        // no change
-                        return;
-                    }
-                    t2Docked = rightDocked;
-                    JSplitPane split = (JSplitPane)thisParent;
-                    if (leftDocked) {
-                        if (rightDocked) {
-                            finalSplit.setDividerLocation(0.5);
-                        } else {
-                            finalSplit.setDividerLocation(1.0);
-                        }
-                        if (adjustVerticalSplit) {
-                            split.setDividerLocation(0.66);
-                        }
 
-                    } else if (rightDocked) {
-                            finalSplit.setDividerLocation(0.0);
-                            split.setDividerLocation(0.66);
-                    } else {
-                        // both undocked
-                        split.setDividerLocation(1.0);
+                    @Override
+                    public void componentHidden(ComponentEvent e) {
+                        check(e.getComponent());
                     }
-                }
-                public void componentHidden(ComponentEvent e) {
-                    check(e.getComponent());
-                }
-                public void componentMoved(ComponentEvent e) {
-                    check(e.getComponent());
-                }
-                public void componentResized(ComponentEvent e) {
-                    check(e.getComponent());
-                }
-                public void componentShown(ComponentEvent e) {
-                    check(e.getComponent());
-                }
-            };
-        p1.addContainerListener(new ContainerListener() {
-            public void componentAdded(ContainerEvent e) {
-                Component thisParent = finalThis.getParent();
-                JSplitPane split = (JSplitPane)thisParent;
-                if (e.getChild() == finalT1) {
-                    if (finalT2.getParent() == finalP2) {
-                        // both docked
-                        finalSplit.setDividerLocation(0.5);
-                    } else {
-                        // left docked only
-                        finalSplit.setDividerLocation(1.0);
+
+                    @Override
+                    public void componentMoved(ComponentEvent e) {
+                        check(e.getComponent());
                     }
-                    split.setDividerLocation(0.66);
-                }
-            }
-            public void componentRemoved(ContainerEvent e) {
-                Component thisParent = finalThis.getParent();
-                JSplitPane split = (JSplitPane)thisParent;
-                if (e.getChild() == finalT1) {
-                    if (finalT2.getParent() == finalP2) {
-                        // right docked only
-                        finalSplit.setDividerLocation(0.0);
-                        split.setDividerLocation(0.66);
-                    } else {
-                        // both undocked
-                        split.setDividerLocation(1.0);
+
+                    @Override
+                    public void componentResized(ComponentEvent e) {
+                        check(e.getComponent());
                     }
-                }
-            }
-            });
+
+                    @Override
+                    public void componentShown(ComponentEvent e) {
+                        check(e.getComponent());
+                    }
+                };
+        p1.addContainerListener(
+                new ContainerListener() {
+                    @Override
+                    public void componentAdded(ContainerEvent e) {
+                        Component thisParent = finalThis.getParent();
+                        JSplitPane split = (JSplitPane) thisParent;
+                        if (e.getChild() == finalT1) {
+                            if (finalT2.getParent() == finalP2) {
+                                // both docked
+                                finalSplit.setDividerLocation(0.5);
+                            } else {
+                                // left docked only
+                                finalSplit.setDividerLocation(1.0);
+                            }
+                            split.setDividerLocation(0.66);
+                        }
+                    }
+
+                    @Override
+                    public void componentRemoved(ContainerEvent e) {
+                        Component thisParent = finalThis.getParent();
+                        JSplitPane split = (JSplitPane) thisParent;
+                        if (e.getChild() == finalT1) {
+                            if (finalT2.getParent() == finalP2) {
+                                // right docked only
+                                finalSplit.setDividerLocation(0.0);
+                                split.setDividerLocation(0.66);
+                            } else {
+                                // both undocked
+                                split.setDividerLocation(1.0);
+                            }
+                        }
+                    }
+                });
         t1.addComponentListener(clistener);
         t2.addComponentListener(clistener);
         setEnabled(false);
     }
 
-    /**
-     * Enables or disables the component.
-     */
+    /** Enables or disables the component. */
     @Override
     public void setEnabled(boolean enabled) {
         context.setEnabled(enabled);
@@ -3111,30 +2809,27 @@
         cmdLine.setEnabled(enabled);
     }
 
-    /**
-     * Disables updating of the component.
-     */
+    /** Disables updating of the component. */
     public void disableUpdate() {
         enabled = false;
     }
 
-    /**
-     * Enables updating of the component.
-     */
+    /** Enables updating of the component. */
     public void enableUpdate() {
         enabled = true;
     }
 
     // ActionListener
 
-    /**
-     * Performs an action.
-     */
+    /** Performs an action. */
+    @Override
     public void actionPerformed(ActionEvent e) {
         if (!enabled) return;
         if (e.getActionCommand().equals("ContextSwitch")) {
             Dim.ContextData contextData = debugGui.dim.currentContextData();
-            if (contextData == null) { return; }
+            if (contextData == null) {
+                return;
+            }
             int frameIndex = context.getSelectedIndex();
             context.setToolTipText(toolTips.get(frameIndex));
             int frameCount = contextData.frameCount();
@@ -3159,78 +2854,52 @@
     }
 }
 
-/**
- * The debugger frame menu bar.
- */
+/** The debugger frame menu bar. */
 class Menubar extends JMenuBar implements ActionListener {
 
-    /**
-     * Serializable magic number.
-     */
+    /** Serializable magic number. */
     private static final long serialVersionUID = 3217170497245911461L;
 
-    /**
-     * Items that are enabled only when interrupted.
-     */
+    /** Items that are enabled only when interrupted. */
     private List<JMenuItem> interruptOnlyItems =
-        Collections.synchronizedList(new ArrayList<JMenuItem>());
+            Collections.synchronizedList(new ArrayList<JMenuItem>());
 
-    /**
-     * Items that are enabled only when running.
-     */
-    private List<JMenuItem> runOnlyItems =
-        Collections.synchronizedList(new ArrayList<JMenuItem>());
+    /** Items that are enabled only when running. */
+    private List<JMenuItem> runOnlyItems = Collections.synchronizedList(new ArrayList<JMenuItem>());
 
-    /**
-     * The debugger GUI.
-     */
+    /** The debugger GUI. */
     private SwingGui debugGui;
 
-    /**
-     * The menu listing the internal frames.
-     */
+    /** The menu listing the internal frames. */
     private JMenu windowMenu;
 
-    /**
-     * The "Break on exceptions" menu item.
-     */
+    /** The "Break on exceptions" menu item. */
     private JCheckBoxMenuItem breakOnExceptions;
 
-    /**
-     * The "Break on enter" menu item.
-     */
+    /** The "Break on enter" menu item. */
     private JCheckBoxMenuItem breakOnEnter;
 
-    /**
-     * The "Break on return" menu item.
-     */
+    /** The "Break on return" menu item. */
     private JCheckBoxMenuItem breakOnReturn;
 
-    /**
-     * Creates a new Menubar.
-     */
+    /** Creates a new Menubar. */
     Menubar(SwingGui debugGui) {
         super();
         this.debugGui = debugGui;
-        String[] fileItems  = {"Open...", "Run...", "", "Exit"};
-        String[] fileCmds  = {"Open", "Load", "", "Exit"};
+        String[] fileItems = {"Open...", "Run...", "", "Exit"};
+        String[] fileCmds = {"Open", "Load", "", "Exit"};
         char[] fileShortCuts = {'0', 'N', 0, 'X'};
-        int[] fileAccelerators = {KeyEvent.VK_O,
-                                  KeyEvent.VK_N,
-                                  0,
-                                  KeyEvent.VK_Q};
-        String[] editItems = {"Cut", "Copy", "Paste", "Go to function..."};
-        char[] editShortCuts = {'T', 'C', 'P', 'F'};
+        int[] fileAccelerators = {KeyEvent.VK_O, KeyEvent.VK_N, 0, KeyEvent.VK_Q};
+        String[] editItems = {"Cut", "Copy", "Paste", "Go to function...", "Go to line..."};
+        char[] editShortCuts = {'T', 'C', 'P', 'F', 'L'};
+        int[] editAccelerators = {0, 0, 0, 0, KeyEvent.VK_L};
         String[] debugItems = {"Break", "Go", "Step Into", "Step Over", "Step Out"};
         char[] debugShortCuts = {'B', 'G', 'I', 'O', 'T'};
         String[] plafItems = {"Metal", "Windows", "Motif"};
-        char [] plafShortCuts = {'M', 'W', 'F'};
-        int[] debugAccelerators = {KeyEvent.VK_PAUSE,
-                                   KeyEvent.VK_F5,
-                                   KeyEvent.VK_F11,
-                                   KeyEvent.VK_F7,
-                                   KeyEvent.VK_F8,
-                                   0, 0};
+        char[] plafShortCuts = {'M', 'W', 'F'};
+        int[] debugAccelerators = {
+            KeyEvent.VK_PAUSE, KeyEvent.VK_F5, KeyEvent.VK_F11, KeyEvent.VK_F7, KeyEvent.VK_F8, 0, 0
+        };
 
         JMenu fileMenu = new JMenu("File");
         fileMenu.setMnemonic('F');
@@ -3246,8 +2915,7 @@
             if (fileItems[i].length() == 0) {
                 fileMenu.addSeparator();
             } else {
-                JMenuItem item = new JMenuItem(fileItems[i],
-                                               fileShortCuts[i]);
+                JMenuItem item = new JMenuItem(fileItems[i], fileShortCuts[i]);
                 item.setActionCommand(fileCmds[i]);
                 item.addActionListener(this);
                 fileMenu.add(item);
@@ -3258,20 +2926,21 @@
             }
         }
         for (int i = 0; i < editItems.length; ++i) {
-            JMenuItem item = new JMenuItem(editItems[i],
-                                           editShortCuts[i]);
+            JMenuItem item = new JMenuItem(editItems[i], editShortCuts[i]);
             item.addActionListener(this);
             editMenu.add(item);
+            if (editAccelerators[i] != 0) {
+                KeyStroke k = KeyStroke.getKeyStroke(editAccelerators[i], Event.CTRL_MASK);
+                item.setAccelerator(k);
+            }
         }
         for (int i = 0; i < plafItems.length; ++i) {
-            JMenuItem item = new JMenuItem(plafItems[i],
-                                           plafShortCuts[i]);
+            JMenuItem item = new JMenuItem(plafItems[i], plafShortCuts[i]);
             item.addActionListener(this);
             plafMenu.add(item);
         }
         for (int i = 0; i < debugItems.length; ++i) {
-            JMenuItem item = new JMenuItem(debugItems[i],
-                                           debugShortCuts[i]);
+            JMenuItem item = new JMenuItem(debugItems[i], debugShortCuts[i]);
             item.addActionListener(this);
             if (debugAccelerators[i] != 0) {
                 KeyStroke k = KeyStroke.getKeyStroke(debugAccelerators[i], 0);
@@ -3304,7 +2973,7 @@
 
         add(fileMenu);
         add(editMenu);
-        //add(plafMenu);
+        // add(plafMenu);
         add(debugMenu);
         JMenuItem item;
         windowMenu.add(item = new JMenuItem("Cascade", 'A'));
@@ -3319,39 +2988,30 @@
         updateEnabled(false);
     }
 
-    /**
-     * Returns the "Break on exceptions" menu item.
-     */
+    /** Returns the "Break on exceptions" menu item. */
     public JCheckBoxMenuItem getBreakOnExceptions() {
         return breakOnExceptions;
     }
 
-    /**
-     * Returns the "Break on enter" menu item.
-     */
+    /** Returns the "Break on enter" menu item. */
     public JCheckBoxMenuItem getBreakOnEnter() {
         return breakOnEnter;
     }
 
-    /**
-     * Returns the "Break on return" menu item.
-     */
+    /** Returns the "Break on return" menu item. */
     public JCheckBoxMenuItem getBreakOnReturn() {
         return breakOnReturn;
     }
 
-    /**
-     * Returns the "Debug" menu.
-     */
+    /** Returns the "Debug" menu. */
     public JMenu getDebugMenu() {
         return getMenu(2);
     }
 
     // ActionListener
 
-    /**
-     * Performs an action.
-     */
+    /** Performs an action. */
+    @Override
     public void actionPerformed(ActionEvent e) {
         String cmd = e.getActionCommand();
         String plaf_name = null;
@@ -3379,13 +3039,11 @@
             SwingUtilities.updateComponentTreeUI(debugGui);
             SwingUtilities.updateComponentTreeUI(debugGui.dlg);
         } catch (Exception ignored) {
-            //ignored.printStackTrace();
+            // ignored.printStackTrace();
         }
     }
 
-    /**
-     * Adds a file to the window menu.
-     */
+    /** Adds a file to the window menu. */
     public void addFile(String url) {
         int count = windowMenu.getItemCount();
         JMenuItem item;
@@ -3393,11 +3051,10 @@
             windowMenu.addSeparator();
             count++;
         }
-        JMenuItem lastItem = windowMenu.getItem(count -1);
+        JMenuItem lastItem = windowMenu.getItem(count - 1);
         boolean hasMoreWin = false;
         int maxWin = 5;
-        if (lastItem != null &&
-           lastItem.getText().equals("More Windows...")) {
+        if (lastItem != null && lastItem.getText().equals("More Windows...")) {
             hasMoreWin = true;
             maxWin++;
         }
@@ -3413,7 +3070,11 @@
             }
             String shortName = SwingGui.getShortName(url);
 
-            windowMenu.add(item = new JMenuItem((char)('0' + (count-4)) + " " + shortName, '0' + (count - 4)));
+            windowMenu.add(
+                    item =
+                            new JMenuItem(
+                                    (char) ('0' + (count - 4)) + " " + shortName,
+                                    '0' + (count - 4)));
             if (hasMoreWin) {
                 windowMenu.add(lastItem);
             }
@@ -3424,9 +3085,7 @@
         item.addActionListener(this);
     }
 
-    /**
-     * Updates the enabledness of menu items.
-     */
+    /** Updates the enabledness of menu items. */
     public void updateEnabled(boolean interrupted) {
         for (int i = 0; i != interruptOnlyItems.size(); ++i) {
             JMenuItem item = interruptOnlyItems.get(i);
@@ -3441,8 +3100,8 @@
 }
 
 /**
- * Class to consolidate all cases that require to implement Runnable
- * to avoid class generation bloat.
+ * Class to consolidate all cases that require to implement Runnable to avoid class generation
+ * bloat.
  */
 class RunProxy implements Runnable {
 
@@ -3452,98 +3111,79 @@
     static final int UPDATE_SOURCE_TEXT = 3;
     static final int ENTER_INTERRUPT = 4;
 
-    /**
-     * The debugger GUI.
-     */
+    /** The debugger GUI. */
     private SwingGui debugGui;
 
-    /**
-     * The type of Runnable this object is.  Takes one of the constants
-     * defined in this class.
-     */
+    /** The type of Runnable this object is. Takes one of the constants defined in this class. */
     private int type;
 
-    /**
-     * The name of the file to open or load.
-     */
+    /** The name of the file to open or load. */
     String fileName;
 
-    /**
-     * The source text to update.
-     */
+    /** The source text to update. */
     String text;
 
-    /**
-     * The source for which to update the text.
-     */
+    /** The source for which to update the text. */
     Dim.SourceInfo sourceInfo;
 
-    /**
-     * The frame to interrupt in.
-     */
+    /** The frame to interrupt in. */
     Dim.StackFrame lastFrame;
 
-    /**
-     * The name of the interrupted thread.
-     */
+    /** The name of the interrupted thread. */
     String threadTitle;
 
-    /**
-     * The message of the exception thrown that caused the thread
-     * interruption, if any.
-     */
+    /** The message of the exception thrown that caused the thread interruption, if any. */
     String alertMessage;
 
-    /**
-     * Creates a new RunProxy.
-     */
+    /** Creates a new RunProxy. */
     public RunProxy(SwingGui debugGui, int type) {
         this.debugGui = debugGui;
         this.type = type;
     }
 
-    /**
-     * Runs this Runnable.
-     */
+    /** Runs this Runnable. */
+    @Override
     public void run() {
         switch (type) {
-          case OPEN_FILE:
-            try {
-                debugGui.dim.compileScript(fileName, text);
-            } catch (RuntimeException ex) {
-                MessageDialogWrapper.showMessageDialog(
-                    debugGui, ex.getMessage(), "Error Compiling "+fileName,
-                    JOptionPane.ERROR_MESSAGE);
-            }
-            break;
+            case OPEN_FILE:
+                try {
+                    debugGui.dim.compileScript(fileName, text);
+                } catch (RuntimeException ex) {
+                    MessageDialogWrapper.showMessageDialog(
+                            debugGui,
+                            ex.getMessage(),
+                            "Error Compiling " + fileName,
+                            JOptionPane.ERROR_MESSAGE);
+                }
+                break;
 
-          case LOAD_FILE:
-            try {
-                debugGui.dim.evalScript(fileName, text);
-            } catch (RuntimeException ex) {
-                MessageDialogWrapper.showMessageDialog(
-                    debugGui, ex.getMessage(), "Run error for "+fileName,
-                    JOptionPane.ERROR_MESSAGE);
-            }
-            break;
-
-          case UPDATE_SOURCE_TEXT:
-            {
-                String fileName = sourceInfo.url();
-                if (!debugGui.updateFileWindow(sourceInfo) &&
-                        !fileName.equals("<stdin>")) {
-                    debugGui.createFileWindow(sourceInfo, -1);
-                }
-            }
-            break;
-
-          case ENTER_INTERRUPT:
-            debugGui.enterInterruptImpl(lastFrame, threadTitle, alertMessage);
-            break;
+            case LOAD_FILE:
+                try {
+                    debugGui.dim.evalScript(fileName, text);
+                } catch (RuntimeException ex) {
+                    MessageDialogWrapper.showMessageDialog(
+                            debugGui,
+                            ex.getMessage(),
+                            "Run error for " + fileName,
+                            JOptionPane.ERROR_MESSAGE);
+                }
+                break;
 
-          default:
-            throw new IllegalArgumentException(String.valueOf(type));
+            case UPDATE_SOURCE_TEXT:
+                {
+                    String fileName = sourceInfo.url();
+                    if (!debugGui.updateFileWindow(sourceInfo) && !fileName.equals("<stdin>")) {
+                        debugGui.createFileWindow(sourceInfo, -1);
+                    }
+                }
+                break;
+
+            case ENTER_INTERRUPT:
+                debugGui.enterInterruptImpl(lastFrame, threadTitle, alertMessage);
+                break;
 
+            default:
+                throw new IllegalArgumentException(String.valueOf(type));
         }
     }
 }
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/debugger/treetable/AbstractCellEditor.java rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/debugger/treetable/AbstractCellEditor.java
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/debugger/treetable/AbstractCellEditor.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/debugger/treetable/AbstractCellEditor.java	2022-01-06 22:57:21.000000000 +0100
@@ -31,10 +31,13 @@
 
 package org.mozilla.javascript.tools.debugger.treetable;
 
-import javax.swing.*;
-import javax.swing.event.*;
 import java.util.EventObject;
 
+import javax.swing.CellEditor;
+import javax.swing.event.CellEditorListener;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.EventListenerList;
+
 public class AbstractCellEditor implements CellEditor {
 
     protected EventListenerList listenerList = new EventListenerList();
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/debugger/treetable/JTreeTable.java rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/debugger/treetable/JTreeTable.java
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/debugger/treetable/JTreeTable.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/debugger/treetable/JTreeTable.java	2022-01-06 22:57:21.000000000 +0100
@@ -33,19 +33,27 @@
 
 package org.mozilla.javascript.tools.debugger.treetable;
 
-import javax.swing.*;
-import javax.swing.event.*;
-import javax.swing.tree.*;
-import javax.swing.table.*;
-
-import java.awt.Dimension;
 import java.awt.Component;
+import java.awt.Dimension;
 import java.awt.Graphics;
-
 import java.awt.event.MouseEvent;
-
 import java.util.EventObject;
 
+import javax.swing.JTable;
+import javax.swing.JTree;
+import javax.swing.ListSelectionModel;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.table.TableCellEditor;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import javax.swing.tree.DefaultTreeSelectionModel;
+import javax.swing.tree.TreeCellRenderer;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreePath;
+
 /**
  * This example shows how to create a simple JTreeTable component,
  * by using a JTree as a renderer (and editor) for the cells in a
@@ -154,6 +162,9 @@
 
         public TreeTableCellRenderer(TreeModel model) {
             super(model);
+
+            // Make sure that there is no border.
+            setBorder(null);
         }
 
         /**
@@ -187,8 +198,7 @@
         public void setRowHeight(int rowHeight) {
             if (rowHeight > 0) {
                 super.setRowHeight(rowHeight);
-                if (JTreeTable.this != null &&
-                    JTreeTable.this.getRowHeight() != rowHeight) {
+                if (JTreeTable.this.getRowHeight() != rowHeight) {
                     JTreeTable.this.setRowHeight(getRowHeight());
                 }
             }
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/debugger/treetable/TreeTableModelAdapter.java rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/debugger/treetable/TreeTableModelAdapter.java
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/debugger/treetable/TreeTableModelAdapter.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/debugger/treetable/TreeTableModelAdapter.java	2022-01-06 22:57:21.000000000 +0100
@@ -35,12 +35,12 @@
 
 import javax.swing.JTree;
 import javax.swing.SwingUtilities;
-import javax.swing.table.AbstractTableModel;
-import javax.swing.tree.TreePath;
 import javax.swing.event.TreeExpansionEvent;
 import javax.swing.event.TreeExpansionListener;
 import javax.swing.event.TreeModelEvent;
 import javax.swing.event.TreeModelListener;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.tree.TreePath;
 
 /**
  * This is a wrapper class takes a TreeTableModel and implements
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/idswitch/CodePrinter.java rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/idswitch/CodePrinter.java
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/idswitch/CodePrinter.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/idswitch/CodePrinter.java	1970-01-01 01:00:00.000000000 +0100
@@ -1,181 +0,0 @@
-/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-package org.mozilla.javascript.tools.idswitch;
-
-class CodePrinter {
-
-// length of u-type escape like \u12AB
-    private static final int LITERAL_CHAR_MAX_SIZE = 6;
-
-    private String lineTerminator = "\n";
-
-    private int indentStep = 4;
-    private int indentTabSize = 8;
-
-    private char[] buffer = new char[1 << 12]; // 4K
-    private int offset;
-
-    public String getLineTerminator() { return lineTerminator; }
-    public void setLineTerminator(String value) { lineTerminator = value; }
-
-    public int getIndentStep() { return indentStep; }
-    public void setIndentStep(int char_count) { indentStep = char_count; }
-
-    public int getIndentTabSize() {    return indentTabSize; }
-    public void setIndentTabSize(int tab_size) { indentTabSize = tab_size; }
-
-    public void clear() {
-        offset = 0;
-    }
-
-    private int ensure_area(int area_size) {
-        int begin = offset;
-        int end = begin + area_size;
-        if (end > buffer.length) {
-            int new_capacity = buffer.length * 2;
-            if (end > new_capacity) { new_capacity = end; }
-            char[] tmp = new char[new_capacity];
-            System.arraycopy(buffer, 0, tmp, 0, begin);
-            buffer = tmp;
-        }
-        return begin;
-    }
-
-    private int add_area(int area_size) {
-        int pos = ensure_area(area_size);
-        offset = pos + area_size;
-        return pos;
-    }
-
-    public int getOffset() {
-        return offset;
-    }
-
-    public int getLastChar() {
-        return offset == 0 ? -1 : buffer[offset - 1];
-    }
-
-    public void p(char c) {
-        int pos = add_area(1);
-        buffer[pos] = c;
-    }
-
-    public void p(String s) {
-        int l = s.length();
-        int pos = add_area(l);
-        s.getChars(0, l, buffer, pos);
-    }
-
-    public final void p(char[] array) {
-        p(array, 0, array.length);
-    }
-
-    public void p(char[] array, int begin, int end) {
-        int l = end - begin;
-        int pos = add_area(l);
-        System.arraycopy(array, begin, buffer, pos, l);
-    }
-
-    public void p(int i) {
-        p(Integer.toString(i));
-    }
-
-    public void qchar(int c) {
-        int pos = ensure_area(2 + LITERAL_CHAR_MAX_SIZE);
-        buffer[pos] = '\'';
-        pos = put_string_literal_char(pos + 1, c, false);
-        buffer[pos] = '\'';
-        offset = pos + 1;
-    }
-
-    public void qstring(String s) {
-        int l = s.length();
-        int pos = ensure_area(2 + LITERAL_CHAR_MAX_SIZE * l);
-        buffer[pos] = '"';
-        ++pos;
-        for (int i = 0; i != l; ++i) {
-            pos = put_string_literal_char(pos, s.charAt(i), true);
-        }
-        buffer[pos] = '"';
-        offset = pos + 1;
-    }
-
-    private int put_string_literal_char(int pos, int c, boolean in_string) {
-        boolean backslash_symbol = true;
-        switch (c) {
-            case '\b': c = 'b'; break;
-            case '\t': c = 't'; break;
-            case '\n': c = 'n'; break;
-            case '\f': c = 'f'; break;
-            case '\r': c = 'r'; break;
-            case '\'': backslash_symbol = !in_string; break;
-            case '"': backslash_symbol = in_string; break;
-            default: backslash_symbol = false;
-        }
-
-        if (backslash_symbol) {
-            buffer[pos] = '\\';
-            buffer[pos + 1] = (char)c;
-            pos += 2;
-        }
-        else if (' ' <= c && c <= 126) {
-            buffer[pos] = (char)c;
-            ++pos;
-        }
-        else {
-            buffer[pos] = '\\';
-            buffer[pos + 1] = 'u';
-            buffer[pos + 2] = digit_to_hex_letter(0xF & (c >> 12));
-            buffer[pos + 3] = digit_to_hex_letter(0xF & (c >> 8));
-            buffer[pos + 4] = digit_to_hex_letter(0xF & (c >> 4));
-            buffer[pos + 5] = digit_to_hex_letter(0xF & c);
-            pos += 6;
-        }
-        return pos;
-    }
-
-    private static char digit_to_hex_letter(int d) {
-        return (char)((d < 10) ? '0' + d : 'A' - 10 + d);
-    }
-
-    public void indent(int level) {
-        int visible_size = indentStep * level;
-        int indent_size, tab_count;
-        if (indentTabSize <= 0) {
-            tab_count = 0; indent_size = visible_size;
-        }
-        else {
-            tab_count = visible_size / indentTabSize;
-            indent_size = tab_count + visible_size % indentTabSize;
-        }
-        int pos = add_area(indent_size);
-        int tab_end = pos + tab_count;
-        int indent_end = pos + indent_size;
-        for (; pos != tab_end; ++pos) {    buffer[pos] = '\t'; }
-        for (; pos != indent_end; ++pos) {    buffer[pos] = ' '; }
-    }
-
-    public void nl() {
-        p('\n');
-    }
-
-    public void line(int indent_level, String s) {
-        indent(indent_level); p(s); nl();
-    }
-
-    public void erase(int begin, int end) {
-        System.arraycopy(buffer, end, buffer, begin, offset - end);
-        offset -= end - begin;
-    }
-
-    @Override
-    public String toString() {
-        return new String(buffer, 0, offset);
-    }
-
-
-
-}
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/idswitch/FileBody.java rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/idswitch/FileBody.java
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/idswitch/FileBody.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/idswitch/FileBody.java	1970-01-01 01:00:00.000000000 +0100
@@ -1,159 +0,0 @@
-/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-package org.mozilla.javascript.tools.idswitch;
-
-import java.io.IOException;
-import java.io.Reader;
-import java.io.Writer;
-
-public class FileBody {
-
-    private static class ReplaceItem {
-        ReplaceItem next;
-        int begin;
-        int end;
-        String replacement;
-
-        ReplaceItem(int begin, int end, String text) {
-            this.begin = begin;
-            this.end = end;
-            this.replacement = text;
-        }
-    }
-
-    private char[] buffer = new char[1 << 14]; // 16K
-    private int bufferEnd;
-    private int lineBegin;
-    private int lineEnd;
-    private int nextLineStart;
-
-    private int lineNumber;
-
-    ReplaceItem firstReplace;
-    ReplaceItem lastReplace;
-
-
-    public char[] getBuffer() { return buffer; }
-
-    public void readData(Reader r) throws IOException {
-        int capacity = buffer.length;
-        int offset = 0;
-        for (;;) {
-            int n_read = r.read(buffer, offset, capacity - offset);
-            if (n_read < 0) { break; }
-            offset += n_read;
-            if (capacity == offset) {
-                capacity *= 2;
-                char[] tmp = new char[capacity];
-                System.arraycopy(buffer, 0, tmp, 0, offset);
-                buffer = tmp;
-            }
-        }
-        bufferEnd = offset;
-    }
-
-    public void writeInitialData(Writer w) throws IOException {
-        w.write(buffer, 0, bufferEnd);
-    }
-
-    public void writeData(Writer w) throws IOException {
-        int offset = 0;
-        for (ReplaceItem x = firstReplace; x != null; x = x.next) {
-            int before_replace = x.begin - offset;
-            if (before_replace > 0) {
-                w.write(buffer, offset, before_replace);
-            }
-            w.write(x.replacement);
-            offset = x.end;
-        }
-        int tail = bufferEnd - offset;
-        if (tail != 0) {
-            w.write(buffer, offset, tail);
-        }
-    }
-
-    public boolean wasModified() { return firstReplace != null; }
-
-    public boolean setReplacement(int begin, int end, String text) {
-        if (equals(text, buffer, begin, end)) { return false; }
-
-        ReplaceItem item = new ReplaceItem(begin, end, text);
-        if (firstReplace == null) {
-            firstReplace = lastReplace = item;
-        }
-        else if (begin < firstReplace.begin) {
-            item.next = firstReplace;
-            firstReplace = item;
-        }
-        else {
-            ReplaceItem cursor = firstReplace;
-            ReplaceItem next = cursor.next;
-            while (next != null) {
-                if (begin < next.begin) {
-                    item.next = next;
-                    cursor.next = item;
-                    break;
-                }
-                cursor = next;
-                next = next.next;
-            }
-            if (next == null) {
-                lastReplace.next = item;
-            }
-        }
-
-        return true;
-    }
-
-    public int getLineNumber() { return lineNumber; }
-
-    public int getLineBegin() { return lineBegin; }
-
-    public int getLineEnd() { return lineEnd; }
-
-    public void startLineLoop() {
-        lineNumber = 0;
-        lineBegin = lineEnd = nextLineStart = 0;
-    }
-
-    public boolean nextLine() {
-        if (nextLineStart == bufferEnd) {
-            lineNumber = 0; return false;
-        }
-        int i; int c = 0;
-        for (i = nextLineStart; i != bufferEnd; ++i) {
-            c = buffer[i];
-            if (c == '\n' || c == '\r') { break; }
-        }
-        lineBegin = nextLineStart;
-        lineEnd = i;
-        if (i == bufferEnd) {
-            nextLineStart = i;
-        }
-        else if (c == '\r' && i + 1 != bufferEnd && buffer[i + 1] == '\n') {
-            nextLineStart = i + 2;
-        }
-        else {
-            nextLineStart = i + 1;
-        }
-        ++lineNumber;
-        return true;
-    }
-
-    private static boolean equals(String str, char[] array, int begin, int end)
-    {
-        if (str.length() == end - begin) {
-            for (int i = begin, j = 0; i != end; ++i, ++j) {
-                if (array[i] != str.charAt(j)) { return false; }
-            }
-            return true;
-        }
-        return false;
-    }
-
-}
-
-
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/idswitch/IdValuePair.java rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/idswitch/IdValuePair.java
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/idswitch/IdValuePair.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/idswitch/IdValuePair.java	1970-01-01 01:00:00.000000000 +0100
@@ -1,26 +0,0 @@
-/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-package org.mozilla.javascript.tools.idswitch;
-
-public class IdValuePair
-{
-    public final int idLength;
-    public final String id;
-    public final String value;
-
-    private int lineNumber;
-
-    public IdValuePair(String id, String value) {
-        this.idLength = id.length();
-        this.id = id;
-        this.value = value;
-    }
-
-    public int getLineNumber() { return lineNumber; }
-
-    public void setLineNumber(int value) { lineNumber = value; }
-}
-
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/idswitch/Main.java rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/idswitch/Main.java
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/idswitch/Main.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/idswitch/Main.java	1970-01-01 01:00:00.000000000 +0100
@@ -1,580 +0,0 @@
-/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-package org.mozilla.javascript.tools.idswitch;
-
-import java.io.*;
-import java.util.*;
-import java.text.SimpleDateFormat;
-
-import org.mozilla.javascript.EvaluatorException;
-import org.mozilla.javascript.tools.ToolErrorReporter;
-
-public class Main {
-
-    private static final String SWITCH_TAG_STR = "string_id_map";
-    private static final String GENERATED_TAG_STR = "generated";
-    private static final String STRING_TAG_STR = "string";
-
-    private static final int
-        NORMAL_LINE        = 0,
-        SWITCH_TAG         = 1,
-        GENERATED_TAG      = 2,
-        STRING_TAG         = 3;
-
-    private final List<IdValuePair> all_pairs = new ArrayList<IdValuePair>();
-
-    private ToolErrorReporter R;
-    private CodePrinter P;
-    private FileBody body;
-    private String source_file;
-
-    private int tag_definition_end;
-
-    private int tag_value_start;
-    private int tag_value_end;
-
-    private static boolean is_value_type(int id) {
-        if (id == STRING_TAG) { return true; }
-        return false;
-    }
-
-    private static String tag_name(int id) {
-        switch (id) {
-            case SWITCH_TAG: return SWITCH_TAG_STR;
-            case -SWITCH_TAG: return "/" + SWITCH_TAG_STR;
-            case GENERATED_TAG: return GENERATED_TAG_STR;
-            case -GENERATED_TAG: return "/" + GENERATED_TAG_STR;
-        }
-        return "";
-    }
-
-    void process_file(String file_path) throws IOException {
-        source_file = file_path;
-
-        body = new FileBody();
-
-        InputStream is;
-        if (file_path.equals("-")) {
-            is = System.in;
-        }
-        else {
-            is = new FileInputStream(file_path);
-        }
-        try {
-            Reader r = new InputStreamReader(is, "ASCII");
-            body.readData(r);
-        }
-        finally { is.close(); }
-
-        process_file();
-
-        if (body.wasModified()) {
-            OutputStream os;
-            if (file_path.equals("-")) {
-                os = System.out;
-            }
-            else {
-                os = new FileOutputStream(file_path);
-            }
-
-            try {
-                Writer w = new OutputStreamWriter(os);
-                body.writeData(w);
-                w.flush();
-            }
-            finally { os.close(); }
-        }
-    }
-
-    private void process_file() {
-        int cur_state = 0;
-        char[] buffer = body.getBuffer();
-
-        int generated_begin = -1, generated_end = -1;
-        int time_stamp_begin = -1, time_stamp_end = -1;
-
-        body.startLineLoop();
-        while (body.nextLine()) {
-            int begin = body.getLineBegin();
-            int end = body.getLineEnd();
-
-            int tag_id = extract_line_tag_id(buffer, begin, end);
-            boolean bad_tag = false;
-            switch (cur_state) {
-                case NORMAL_LINE:
-                    if (tag_id == SWITCH_TAG) {
-                        cur_state = SWITCH_TAG;
-                        all_pairs.clear();
-                        generated_begin = -1;
-                    }
-                    else if (tag_id == -SWITCH_TAG) {
-                        bad_tag = true;
-                    }
-                    break;
-                case SWITCH_TAG:
-                    if (tag_id == 0) {
-                        look_for_id_definitions(buffer, begin, end, false);
-                    }
-                    else if (tag_id == STRING_TAG) {
-                        look_for_id_definitions(buffer, begin, end, true);
-                    }
-                    else if (tag_id == GENERATED_TAG) {
-                        if (generated_begin >= 0) { bad_tag = true; }
-                        else {
-                            cur_state = GENERATED_TAG;
-                            time_stamp_begin = tag_definition_end;
-                            time_stamp_end = end;
-                        }
-                    }
-                    else if (tag_id == -SWITCH_TAG) {
-                        cur_state = 0;
-                        if (generated_begin >= 0 && !all_pairs.isEmpty()) {
-                            generate_java_code();
-                            String code = P.toString();
-                            boolean different = body.setReplacement
-                                (generated_begin, generated_end, code);
-                            if (different) {
-                                String stamp = get_time_stamp();
-                                body.setReplacement
-                                    (time_stamp_begin, time_stamp_end, stamp);
-                            }
-                        }
-
-                        break;
-                    }
-                    else {
-                        bad_tag = true;
-                    }
-                    break;
-                case GENERATED_TAG:
-                    if (tag_id == 0) {
-                        if (generated_begin < 0) { generated_begin = begin; }
-                    }
-                    else if (tag_id == -GENERATED_TAG) {
-                        if (generated_begin < 0) { generated_begin = begin; }
-                        cur_state = SWITCH_TAG;
-                        generated_end = begin;
-                    }
-                    else {
-                        bad_tag = true;
-                    }
-                    break;
-            }
-            if (bad_tag) {
-                String text = ToolErrorReporter.getMessage(
-                    "msg.idswitch.bad_tag_order", tag_name(tag_id));
-                throw R.runtimeError
-                    (text, source_file, body.getLineNumber(), null, 0);
-            }
-        }
-
-        if (cur_state != 0) {
-            String text = ToolErrorReporter.getMessage(
-                "msg.idswitch.file_end_in_switch", tag_name(cur_state));
-            throw R.runtimeError
-                (text, source_file, body.getLineNumber(), null, 0);
-        }
-    }
-
-    private String get_time_stamp() {
-        SimpleDateFormat f = new SimpleDateFormat
-            (" 'Last update:' yyyy-MM-dd HH:mm:ss z");
-        return f.format(new Date());
-    }
-
-    private void generate_java_code() {
-
-        P.clear();
-
-        IdValuePair[] pairs = new IdValuePair[all_pairs.size()];
-        all_pairs.toArray(pairs);
-
-        SwitchGenerator g = new SwitchGenerator();
-        g.char_tail_test_threshold = 2;
-        g.setReporter(R);
-        g.setCodePrinter(P);
-
-        g.generateSwitch(pairs, "0");
-    }
-
-    private int extract_line_tag_id(char[] array, int cursor, int end) {
-        int id = 0;
-        cursor = skip_white_space(array, cursor, end);
-        int after_leading_white_space = cursor;
-        cursor = look_for_slash_slash(array, cursor, end);
-        if (cursor != end) {
-            boolean at_line_start = (after_leading_white_space + 2 == cursor);
-            cursor = skip_white_space(array, cursor, end);
-            if (cursor != end && array[cursor] == '#') {
-                ++cursor;
-
-                boolean end_tag = false;
-                if (cursor != end && array[cursor] == '/') {
-                    ++cursor; end_tag = true;
-                }
-
-                int tag_start = cursor;
-
-                for (; cursor != end; ++cursor) {
-                    int c = array[cursor];
-                    if (c == '#' || c == '=' ||is_white_space(c)) { break; }
-                }
-
-                if (cursor != end) {
-                    int tag_end = cursor;
-                    cursor = skip_white_space(array, cursor, end);
-                    if (cursor != end) {
-                        int c = array[cursor];
-                        if (c == '=' || c == '#') {
-                            id = get_tag_id
-                                (array, tag_start, tag_end, at_line_start);
-                            if (id != 0) {
-                                String bad = null;
-                                if (c == '#') {
-                                    if (end_tag) {
-                                        id = -id;
-                                        if (is_value_type(id)) {
-                                            bad = "msg.idswitch.no_end_usage";
-                                        }
-                                    }
-                                    tag_definition_end = cursor + 1;
-                                }
-                                else  {
-                                    if (end_tag) {
-                                        bad = "msg.idswitch.no_end_with_value";
-                                    }
-                                    else if (!is_value_type(id)) {
-                                        bad = "msg.idswitch.no_value_allowed";
-                                    }
-                                    id = extract_tag_value
-                                        (array, cursor + 1, end, id);
-                                }
-                                if (bad != null) {
-                                    String s = ToolErrorReporter.getMessage(
-                                        bad, tag_name(id));
-                                    throw R.runtimeError
-                                        (s, source_file, body.getLineNumber(),
-                                         null, 0);
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        return id;
-    }
-
-// Return position after first of // or end if not found
-    private int look_for_slash_slash(char[] array, int cursor, int end) {
-        while (cursor + 2 <= end) {
-            int c = array[cursor++];
-            if (c == '/') {
-                c = array[cursor++];
-                if (c == '/') {
-                    return cursor;
-                }
-            }
-        }
-        return end;
-    }
-
-    private int extract_tag_value(char[] array, int cursor, int end, int id) {
-        // cursor points after #[^#=]+=
-        // ALERT: implement support for quoted strings
-        boolean found = false;
-        cursor = skip_white_space(array, cursor, end);
-        if (cursor != end) {
-            int value_start = cursor;
-            int value_end = cursor;
-            while (cursor != end) {
-                int c = array[cursor];
-                if (is_white_space(c)) {
-                    int after_space = skip_white_space(array, cursor + 1, end);
-                    if (after_space != end && array[after_space] == '#') {
-                        value_end = cursor;
-                        cursor = after_space;
-                        break;
-                    }
-                    cursor = after_space + 1;
-                }
-                else if (c == '#') {
-                    value_end = cursor;
-                    break;
-                }
-                else {
-                    ++cursor;
-                }
-            }
-            if (cursor != end) {
-                // array[cursor] is '#' here
-                found = true;
-                tag_value_start = value_start;
-                tag_value_end = value_end;
-                tag_definition_end = cursor + 1;
-            }
-        }
-        return (found) ? id : 0;
-    }
-
-    private int get_tag_id
-        (char[] array, int begin, int end, boolean at_line_start)
-    {
-        if (at_line_start) {
-            if (equals(SWITCH_TAG_STR, array, begin, end)) {
-                return SWITCH_TAG;
-            }
-            if (equals(GENERATED_TAG_STR, array, begin, end)) {
-                return GENERATED_TAG;
-            }
-        }
-        if (equals(STRING_TAG_STR, array, begin, end)) {
-            return STRING_TAG;
-        }
-        return 0;
-    }
-
-    private void look_for_id_definitions
-        (char[] array, int begin, int end, boolean use_tag_value_as_string)
-    {
-    // Look for the pattern
-    // '^[ \t]+Id_([a-zA-Z0-9_]+)[ \t]*=.*$'
-    // where \1 gives field or method name
-        int cursor = begin;
-        // Skip tab and spaces at the beginning
-        cursor = skip_white_space(array, cursor, end);
-        int id_start = cursor;
-        int name_start = skip_matched_prefix("Id_", array, cursor, end);
-        if (name_start >= 0) {
-            // Found Id_ prefix
-            cursor = name_start;
-            cursor = skip_name_char(array, cursor, end);
-            int name_end = cursor;
-            if (name_start != name_end) {
-                cursor = skip_white_space(array, cursor, end);
-                if (cursor != end) {
-                    if (array[cursor] == '=') {
-                        int id_end = name_end;
-                        if (use_tag_value_as_string) {
-                            name_start = tag_value_start;
-                            name_end = tag_value_end;
-                        }
-                        // Got the match
-                        add_id(array, id_start, id_end, name_start, name_end);
-                    }
-                }
-            }
-        }
-    }
-
-    private void add_id
-        (char[] array, int id_start, int id_end, int name_start, int name_end)
-    {
-        String name = new String(array, name_start, name_end - name_start);
-        String value = new String(array, id_start, id_end - id_start);
-
-        IdValuePair pair = new IdValuePair(name, value);
-
-        pair.setLineNumber(body.getLineNumber());
-
-        all_pairs.add(pair);
-    }
-
-    private static boolean is_white_space(int c) {
-        return c == ' ' || c == '\t';
-    }
-
-    private static int skip_white_space(char[] array, int begin, int end) {
-        int cursor = begin;
-        for (; cursor != end; ++cursor) {
-            int c = array[cursor];
-            if (!is_white_space(c)) { break; }
-        }
-        return cursor;
-    }
-
-    private static int skip_matched_prefix
-        (String prefix, char[] array, int begin, int end)
-    {
-        int cursor = -1;
-        int prefix_length = prefix.length();
-        if (prefix_length <= end - begin) {
-            cursor = begin;
-            for (int i = 0; i != prefix_length; ++i, ++cursor) {
-                if (prefix.charAt(i) != array[cursor]) {
-                    cursor = -1; break;
-                }
-            }
-        }
-        return cursor;
-    }
-
-    private static boolean equals(String str, char[] array, int begin, int end)
-    {
-        if (str.length() == end - begin) {
-            for (int i = begin, j = 0; i != end; ++i, ++j) {
-                if (array[i] != str.charAt(j)) { return false; }
-            }
-            return true;
-        }
-        return false;
-    }
-
-    private static int skip_name_char(char[] array, int begin, int end) {
-        int cursor = begin;
-        for (; cursor != end; ++cursor) {
-            int c = array[cursor];
-            if (!('a' <= c && c <= 'z') && !('A' <= c && c <= 'Z')) {
-                if (!('0' <= c && c <= '9')) {
-                    if (c != '_') {
-                        break;
-                    }
-                }
-            }
-        }
-        return cursor;
-    }
-
-    public static void main(String[] args) {
-        Main self = new Main();
-        int status = self.exec(args);
-        System.exit(status);
-    }
-
-    private int exec(String[] args) {
-        R = new ToolErrorReporter(true, System.err);
-
-        int arg_count = process_options(args);
-
-        if (arg_count == 0) {
-            option_error(ToolErrorReporter.getMessage(
-                             "msg.idswitch.no_file_argument"));
-            return -1;
-        }
-        if (arg_count > 1) {
-            option_error(ToolErrorReporter.getMessage(
-                             "msg.idswitch.too_many_arguments"));
-            return -1;
-        }
-
-        P = new CodePrinter();
-        P.setIndentStep(4);
-        P.setIndentTabSize(0);
-
-        try {
-            process_file(args[0]);
-        }
-        catch (IOException ex) {
-            print_error(ToolErrorReporter.getMessage(
-                            "msg.idswitch.io_error", ex.toString()));
-            return -1;
-        }
-        catch (EvaluatorException ex) {
-            return -1;
-        }
-        return 0;
-    }
-
-    private int process_options(String[] args) {
-
-        int status = 1;
-
-        boolean show_usage = false;
-        boolean show_version = false;
-
-        int N = args.length;
-        L: for (int i = 0; i != N; ++i) {
-            String arg = args[i];
-            int arg_length = arg.length();
-            if (arg_length >= 2) {
-                if (arg.charAt(0) == '-') {
-                    if (arg.charAt(1) == '-') {
-                        if (arg_length == 2) {
-                            args[i] = null; break;
-                        }
-                        if (arg.equals("--help")) {
-                            show_usage = true;
-                        }
-                        else if (arg.equals("--version")) {
-                            show_version = true;
-                        }
-                        else {
-                            option_error(ToolErrorReporter.getMessage(
-                                             "msg.idswitch.bad_option", arg));
-                            status = -1; break L;
-                        }
-                    }
-                    else {
-                        for (int j = 1; j != arg_length; ++j) {
-                            char c = arg.charAt(j);
-                            switch (c) {
-                                case 'h': show_usage = true; break;
-                                default:
-                                    option_error(
-                                        ToolErrorReporter.getMessage(
-                                            "msg.idswitch.bad_option_char",
-                                            String.valueOf(c)));
-                                    status = -1;
-                                    break L;
-                            }
-
-                        }
-                    }
-                    args[i] = null;
-                }
-            }
-        }
-
-        if (status == 1) {
-            if (show_usage) { show_usage(); status = 0; }
-            if (show_version) { show_version(); status = 0; }
-        }
-
-        if (status != 1) { System.exit(status); }
-
-        return remove_nulls(args);
-    }
-
-    private void show_usage() {
-        System.out.println(
-            ToolErrorReporter.getMessage("msg.idswitch.usage"));
-        System.out.println();
-    }
-
-    private void show_version() {
-        System.out.println(
-            ToolErrorReporter.getMessage("msg.idswitch.version"));
-    }
-
-    private void option_error(String str) {
-        print_error(
-            ToolErrorReporter.getMessage("msg.idswitch.bad_invocation", str));
-    }
-
-    private void print_error(String text) {
-        System.err.println(text);
-    }
-
-    private int remove_nulls(String[] array) {
-        int N = array.length;
-        int cursor = 0;
-        for (; cursor != N; ++cursor) {
-            if (array[cursor] == null) { break; }
-        }
-        int destination = cursor;
-        if (cursor != N) {
-            ++cursor;
-            for (; cursor != N; ++cursor) {
-                String elem = array[cursor];
-                if (elem != null) {
-                    array[destination] = elem; ++destination;
-                }
-            }
-        }
-        return destination;
-    }
-}
-
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/idswitch/README rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/idswitch/README
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/idswitch/README	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/idswitch/README	1970-01-01 01:00:00.000000000 +0100
@@ -1,154 +0,0 @@
-This Source Code Form is subject to the terms of the Mozilla Public
-License, v. 2.0. If a copy of the MPL was not distributed with this
-file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-USING IDSWITCH GENERATOR TOOL
-
-Usage:
-java org.mozilla.javascript.tools.idswitch.Main <SINGLE-JAVA-SOURCE>
-
-The main purpose of this utility is to generate Java code to map strings to some ids that can be used, for example, in a switch statement.
-
-The utility scans the input file for lines with the following structure:
-
-// #string_id_map#
-... <DEFINITION AREA>
-// #generated#
-...<GENERATED AREA>
-// #/generated#
-... <DEFINITION AREA>
-// #/string_id_map#
-
-Then every line in <DEFINITION AREA> is scanned for the pattern:
-^[ \t]*Id_([0-9a-zA-Z_]+)[ \t]*=.*$
-
-Each such patterns adds a mapping form string $1 to Id_\$ or if the line also contains the pattern //\s*#string=\s*([^#]+)\s*#, then it adds map of $1 in this pattern to Id_\$
-
-After that lines in <GENERATED AREA> are replaced by a code block that sets variable "id" to Id_<name> if variable "s" equals <name> (or value defined by //#string=...# construction in the line with Id_<name>) or 0 otherwise.
-If the new code for <GENERATED AREA> is identical to old one, the file is not touched otherwise <GENERATED AREA> is overwritten by the new code and a time stamp is appended after #generated#.
-
-For example, if file x.java contains:
-
-// #string_id_map#
-
-    private int getId(String s) {
-        int id;
-// #generated# Initial version
-// #/generated#
-        return id;
-    }
-
-    private static final int
-        Id_x       = 1,
-        Id_y       = 2,
-        Id_hello   = 3, // #string = Hello, World! #
-        Id_symbols = 4, // #string=<<*Symbols*>>#
-        Id_nice    = 5,
-        Id_for     = 6,
-        Id_bar     = 7;
-
-// #/string_id_map#
-
-....
-
-    private double getFieldValue(String s) {
-
-// #string_id_map#
-    final int
-        Id_field1 = 1,
-        Id_field2 = 2,
-        Id_field3 = 3,
-        Id_one_more_field = 4; // #string = ONE%MORE%FIELD#
-
-        int id;
-// #generated# Initial version
-// #/generated#
-// #/string_id_map#
-        switch (id) {
-            case Id_field1: return field1;
-            case Id_field2: return field2;
-            case Id_field3: return field3;
-            case Id_one_more_field: return one_more_field;
-        }
-        throw new RuntimeException("No such field");
-    }
-
-
-then invocation
-
-java org.mozilla.javascript.tools.idswitch.Main x.java
-
-would replace that by a code fragment similar to:
-
-// #string_id_map#
-
-    private int getId(String s) {
-        int id;
-// #generated# Last update: 2001-05-25 18:00:24 GMT+02:00
-        L0: { id = 0; String X = null; int c;
-            L: switch (s.length()) {
-            case 1: c=s.charAt(0);
-                if (c=='x') { id=Id_x; break L0; }
-                else if (c=='y') { id=Id_y; break L0; }
-                break L;
-            case 3: c=s.charAt(0);
-                if (c=='b') { if (s.charAt(2)=='r' && s.charAt(1)=='a') {id=Id_bar; break L0;} }
-                else if (c=='f') { if (s.charAt(2)=='r' && s.charAt(1)=='o') {id=Id_for; break L0;} }
-                break L;
-            case 4: X="nice";id=Id_nice; break L;
-            case 13: c=s.charAt(0);
-                if (c=='<') { X="<<*Symbols*>>";id=Id_symbols; }
-                else if (c=='H') { X="Hello, World!";id=Id_hello; }
-                break L;
-            }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-        }
-// #/generated#
-        return id;
-    }
-
-    private static final int
-        Id_x       = 1,
-        Id_y       = 2,
-        Id_hello   = 3, // #string = Hello, World! #
-        Id_symbols = 4, // #string=<<*Symbols*>>#
-        Id_nice    = 5,
-        Id_for     = 6,
-        Id_bar     = 7;
-
-// #/string_id_map#
-
-....
-
-    private double getFieldValue(String s) {
-
-// #string_id_map#
-    final int
-        Id_field1 = 1,
-        Id_field2 = 2,
-        Id_field3 = 3,
-        Id_one_more_field = 4; // #string = ONE%MORE%FIELD#
-
-        int id;
-// #generated# Last update: 2001-05-25 16:48:50 GMT+02:00
-        L0: { id = 0; String X = null; int c;
-            int s_length = s.length();
-            if (s_length==6) {
-                c=s.charAt(5);
-                if (c=='1') { X="field1";id=Id_field1; }
-                else if (c=='2') { X="field2";id=Id_field2; }
-                else if (c=='3') { X="field3";id=Id_field3; }
-            }
-            else if (s_length==14) { X="ONE%MORE%FIELD";id=Id_one_more_field; }
-            if (X!=null && X!=s && !X.equals(s)) id = 0;
-        }
-// #/generated#
-// #/string_id_map#
-        switch (id) {
-            case Id_field1: return field1;
-            case Id_field2: return field2;
-            case Id_field3: return field3;
-            case Id_one_more_field: return one_more_field;
-        }
-        throw new RuntimeException("No such field");
-    }
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/idswitch/SwitchGenerator.java rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/idswitch/SwitchGenerator.java
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/idswitch/SwitchGenerator.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/idswitch/SwitchGenerator.java	1970-01-01 01:00:00.000000000 +0100
@@ -1,459 +0,0 @@
-/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-package org.mozilla.javascript.tools.idswitch;
-
-import org.mozilla.javascript.EvaluatorException;
-import org.mozilla.javascript.tools.ToolErrorReporter;
-
-public class SwitchGenerator {
-
-    String v_switch_label = "L0";
-    String v_label = "L";
-    String v_s = "s";
-    String v_c = "c";
-    String v_guess = "X";
-    String v_id = "id";
-    String v_length_suffix = "_length";
-
-    int use_if_threshold = 3;
-    int char_tail_test_threshold = 2;
-
-    private IdValuePair[] pairs;
-    private String default_value;
-    private int[] columns;
-    private boolean c_was_defined;
-
-    private CodePrinter P;
-    private ToolErrorReporter R;
-    private String source_file;
-
-    public CodePrinter getCodePrinter() { return P; }
-    public void setCodePrinter(CodePrinter value) { P = value; }
-
-    public ToolErrorReporter getReporter() { return R; }
-    public void setReporter(ToolErrorReporter value) { R = value; }
-
-    public String getSourceFileName() { return source_file; }
-    public void setSourceFileName(String value) { source_file = value; }
-
-    public void generateSwitch(String[] pairs, String default_value) {
-        int N = pairs.length / 2;
-        IdValuePair[] id_pairs = new IdValuePair[N];
-        for (int i = 0; i != N; ++i) {
-            id_pairs[i] = new IdValuePair(pairs[2 * i], pairs[2 * i + 1]);
-        }
-        generateSwitch(id_pairs, default_value);
-
-    }
-
-    public void generateSwitch(IdValuePair[] pairs, String default_value) {
-        int begin = 0;
-        int end = pairs.length;
-        if (begin == end) { return; }
-        this.pairs = pairs;
-        this.default_value = default_value;
-
-        generate_body(begin, end, 2);
-    }
-
-    private void generate_body(int begin, int end, int indent_level) {
-        P.indent(indent_level);
-        P.p(v_switch_label); P.p(": { ");
-        P.p(v_id); P.p(" = "); P.p(default_value);
-        P.p("; String "); P.p(v_guess); P.p(" = null;");
-
-        c_was_defined = false;
-        int c_def_begin = P.getOffset();
-        P.p(" int "); P.p(v_c); P.p(';');
-        int c_def_end = P.getOffset();
-        P.nl();
-
-        generate_length_switch(begin, end, indent_level + 1);
-
-        if (!c_was_defined) {
-            P.erase(c_def_begin, c_def_end);
-        }
-
-        P.indent(indent_level + 1);
-        P.p("if ("); P.p(v_guess); P.p("!=null && ");
-        P.p(v_guess); P.p("!="); P.p(v_s);
-        P.p(" && !"); P.p(v_guess); P.p(".equals("); P.p(v_s); P.p(")) ");
-        P.p(v_id); P.p(" = "); P.p(default_value); P.p(";"); P.nl();
-
-        // Add break at end of block to suppress warning for unused label
-        P.indent(indent_level + 1);
-        P.p("break "); P.p(v_switch_label); P.p(";"); P.nl();
-
-        P.line(indent_level, "}");
-    }
-
-    private void generate_length_switch(int begin, int end, int indent_level) {
-
-        sort_pairs(begin, end, -1);
-
-        check_all_is_different(begin, end);
-
-        int lengths_count = count_different_lengths(begin, end);
-
-        columns = new int[pairs[end  - 1].idLength];
-
-        boolean use_if;
-        if (lengths_count <= use_if_threshold) {
-            use_if = true;
-            if (lengths_count != 1) {
-                P.indent(indent_level);
-                P.p("int "); P.p(v_s); P.p(v_length_suffix);
-                P.p(" = "); P.p(v_s); P.p(".length();");
-                P.nl();
-            }
-        }
-        else {
-            use_if = false;
-            P.indent(indent_level);
-            P.p(v_label); P.p(": switch (");
-            P.p(v_s); P.p(".length()) {");
-            P.nl();
-        }
-
-        int same_length_begin = begin;
-        int cur_l = pairs[begin].idLength, l = 0;
-        for (int i = begin;;) {
-            ++i;
-            if (i == end || (l = pairs[i].idLength) != cur_l) {
-                int next_indent;
-                if (use_if) {
-                    P.indent(indent_level);
-                    if (same_length_begin != begin) { P.p("else "); }
-                    P.p("if (");
-                    if (lengths_count == 1) {
-                        P.p(v_s); P.p(".length()==");
-                    }
-                    else {
-                        P.p(v_s); P.p(v_length_suffix); P.p("==");
-                    }
-                    P.p(cur_l);
-                    P.p(") {");
-                    next_indent = indent_level + 1;
-                }
-                else {
-                    P.indent(indent_level);
-                    P.p("case "); P.p(cur_l); P.p(":");
-                    next_indent = indent_level + 1;
-                }
-                generate_letter_switch
-                    (same_length_begin, i, next_indent, !use_if, use_if);
-                if (use_if) {
-                    P.p("}"); P.nl();
-                }
-                else {
-                    P.p("break "); P.p(v_label); P.p(";"); P.nl();
-                }
-
-                if (i == end) { break; }
-                same_length_begin = i;
-                cur_l = l;
-            }
-        }
-
-        if (!use_if) {
-            P.indent(indent_level); P.p("}"); P.nl();
-        }
-
-    }
-
-    private void generate_letter_switch
-        (int begin, int end,
-         int indent_level, boolean label_was_defined, boolean inside_if)
-    {
-        int L = pairs[begin].idLength;
-
-        for (int i = 0; i != L; ++i) {
-            columns[i] = i;
-        }
-
-        generate_letter_switch_r
-            (begin, end, L, indent_level, label_was_defined, inside_if);
-    }
-
-
-    private boolean generate_letter_switch_r
-        (int begin, int end, int L,
-         int indent_level, boolean label_was_defined, boolean inside_if)
-    {
-        boolean next_is_unreachable = false;
-        if (begin + 1 == end) {
-            P.p(' ');
-            IdValuePair pair = pairs[begin];
-            if (L > char_tail_test_threshold) {
-                P.p(v_guess); P.p("="); P.qstring(pair.id); P.p(";");
-                P.p(v_id); P.p("="); P.p(pair.value); P.p(";");
-            }
-            else {
-                if (L == 0) {
-                    next_is_unreachable = true;
-                    P.p(v_id); P.p("="); P.p(pair.value);
-                    P.p("; break "); P.p(v_switch_label); P.p(";");
-                }
-                else {
-                    P.p("if (");
-                    int column = columns[0];
-                    P.p(v_s); P.p(".charAt("); P.p(column); P.p(")==");
-                    P.qchar(pair.id.charAt(column));
-                    for (int i = 1; i != L; ++i) {
-                        P.p(" && ");
-                        column = columns[i];
-                        P.p(v_s); P.p(".charAt("); P.p(column); P.p(")==");
-                        P.qchar(pair.id.charAt(column));
-                    }
-                    P.p(") {");
-                    P.p(v_id); P.p("="); P.p(pair.value);
-                    P.p("; break "); P.p(v_switch_label); P.p(";}");
-                }
-            }
-            P.p(' ');
-            return next_is_unreachable;
-        }
-
-        int max_column_index = find_max_different_column(begin, end, L);
-        int max_column = columns[max_column_index];
-        int count = count_different_chars(begin, end, max_column);
-
-        columns[max_column_index] = columns[L - 1];
-
-        if (inside_if) { P.nl(); P.indent(indent_level); }
-        else { P.p(' '); }
-
-        boolean use_if;
-        if (count <= use_if_threshold) {
-            use_if = true;
-            c_was_defined = true;
-            P.p(v_c); P.p("="); P.p(v_s);
-            P.p(".charAt("); P.p(max_column); P.p(");");
-        }
-        else {
-            use_if = false;
-            if (!label_was_defined) {
-                label_was_defined = true;
-                P.p(v_label); P.p(": ");
-            }
-            P.p("switch ("); P.p(v_s);
-            P.p(".charAt("); P.p(max_column); P.p(")) {");
-        }
-
-        int same_char_begin = begin;
-        int cur_ch = pairs[begin].id.charAt(max_column), ch = 0;
-        for (int i = begin;;) {
-            ++i;
-            if (i == end || (ch = pairs[i].id.charAt(max_column)) != cur_ch) {
-                int next_indent;
-                if (use_if) {
-                    P.nl(); P.indent(indent_level);
-                    if (same_char_begin != begin) { P.p("else "); }
-                    P.p("if ("); P.p(v_c); P.p("==");
-                    P.qchar(cur_ch); P.p(") {");
-                    next_indent = indent_level + 1;
-                }
-                else {
-                    P.nl(); P.indent(indent_level);
-                    P.p("case "); P.qchar(cur_ch); P.p(":");
-                    next_indent = indent_level + 1;
-                }
-                boolean after_unreachable = generate_letter_switch_r
-                    (same_char_begin, i, L - 1,
-                     next_indent, label_was_defined, use_if);
-                if (use_if) {
-                    P.p("}");
-                }
-                else {
-                    if (!after_unreachable) {
-                        P.p("break "); P.p(v_label); P.p(";");
-                    }
-                }
-                if (i == end) { break; }
-                same_char_begin = i;
-                cur_ch = ch;
-            }
-        }
-
-        if (use_if) {
-            P.nl();
-            if (inside_if) { P.indent(indent_level - 1); }
-            else { P.indent(indent_level); }
-        }
-        else {
-            P.nl(); P.indent(indent_level); P.p("}");
-            if (inside_if) { P.nl(); P.indent(indent_level - 1);}
-            else { P.p(' '); }
-        }
-
-        columns[max_column_index] = max_column;
-
-        return next_is_unreachable;
-    }
-
-
-    private int count_different_lengths(int begin, int end) {
-        int lengths_count = 0;
-        int cur_l = -1;
-        for (; begin != end; ++begin) {
-            int l = pairs[begin].idLength;
-            if (cur_l != l) {
-                ++lengths_count; cur_l = l;
-            }
-        }
-        return lengths_count;
-    }
-
-    private int find_max_different_column(int begin, int end, int L) {
-        int max_count = 0;
-        int max_index = 0;
-
-        for (int i = 0; i != L; ++i) {
-            int column = columns[i];
-            sort_pairs(begin, end, column);
-            int count = count_different_chars(begin, end, column);
-            if (count == end - begin) { return i; }
-            if (max_count < count) {
-                max_count = count;
-                max_index = i;
-            }
-        }
-
-        if (max_index != L - 1) {
-            sort_pairs(begin, end, columns[max_index]);
-        }
-
-        return max_index;
-    }
-
-    private int count_different_chars(int begin, int end, int column) {
-        int chars_count = 0;
-        int cur_ch = -1;
-        for (; begin != end; ++begin) {
-            int ch = pairs[begin].id.charAt(column);
-            if (ch != cur_ch) {
-                ++chars_count; cur_ch = ch;
-            }
-        }
-        return chars_count;
-    }
-
-    private void check_all_is_different(int begin, int end) {
-        if (begin != end) {
-            IdValuePair prev = pairs[begin];
-            while (++begin != end) {
-                IdValuePair current = pairs[begin];
-                if (prev.id.equals(current.id)) {
-                    throw on_same_pair_fail(prev, current);
-                }
-                prev = current;
-            }
-        }
-    }
-
-    private EvaluatorException on_same_pair_fail(IdValuePair a, IdValuePair b) {
-        int line1 = a.getLineNumber(), line2 = b.getLineNumber();
-        if (line2 > line1) { int tmp = line1; line1 = line2; line2 = tmp; }
-        String error_text = ToolErrorReporter.getMessage(
-            "msg.idswitch.same_string", a.id, new Integer(line2));
-        return R.runtimeError(error_text, source_file, line1, null, 0);
-    }
-
-    private void sort_pairs(int begin, int end, int comparator) {
-        heap4Sort(pairs, begin, end - begin, comparator);
-    }
-
-    private static boolean bigger
-        (IdValuePair a, IdValuePair b, int comparator)
-    {
-        if (comparator < 0) {
-        // For length selection switch it is enough to compare just length,
-        // but to detect same strings full comparison is essential
-            //return a.idLength > b.idLength;
-            int diff = a.idLength - b.idLength;
-            if (diff != 0) { return diff > 0; }
-            return a.id.compareTo(b.id) > 0;
-        }
-        else {
-            return a.id.charAt(comparator) > b.id.charAt(comparator);
-        }
-    }
-
-    private static void heap4Sort
-        (IdValuePair[] array, int offset, int size, int comparator)
-    {
-        if (size <= 1) { return; }
-        makeHeap4(array, offset, size, comparator);
-        while (size > 1) {
-            --size;
-            IdValuePair v1 = array[offset + size];
-            IdValuePair v2 = array[offset + 0];
-            array[offset + size] = v2;
-            array[offset + 0] = v1;
-            heapify4(array, offset, size, 0, comparator);
-        }
-    }
-
-    private static void makeHeap4
-        (IdValuePair[] array, int offset, int size, int comparator)
-    {
-        for (int i = ((size + 2) >> 2); i != 0;) {
-            --i;
-            heapify4(array, offset, size, i, comparator);
-        }
-    }
-
-    private static void heapify4
-        (IdValuePair[] array, int offset, int size, int i, int comparator)
-    {
-        int new_i1, new_i2, new_i3;
-        IdValuePair i_val = array[offset + i];
-        for (;;) {
-            int base = (i << 2);
-            new_i1 = base | 1;
-            new_i2 = base | 2;
-            new_i3 = base | 3;
-            int new_i4 = base + 4;
-            if (new_i4 >= size) { break; }
-            IdValuePair val1 = array[offset + new_i1];
-            IdValuePair val2 = array[offset + new_i2];
-            IdValuePair val3 = array[offset + new_i3];
-            IdValuePair val4 = array[offset + new_i4];
-            if (bigger(val2, val1, comparator)) {
-                val1 = val2; new_i1 = new_i2;
-            }
-            if (bigger(val4, val3, comparator)) {
-                val3 = val4; new_i3 = new_i4;
-            }
-            if (bigger(val3, val1, comparator)) {
-                val1 = val3; new_i1 = new_i3;
-            }
-            if (bigger(i_val, val1, comparator)) { return; }
-            array[offset + i] = val1;
-            array[offset + new_i1] = i_val;
-            i = new_i1;
-        }
-        if (new_i1 < size) {
-            IdValuePair val1 = array[offset + new_i1];
-            if (new_i2 != size) {
-                IdValuePair val2 = array[offset + new_i2];
-                if (bigger(val2, val1, comparator)) {
-                    val1 = val2; new_i1 = new_i2;
-                }
-                if (new_i3 != size) {
-                    IdValuePair val3 = array[offset + new_i3];
-                    if (bigger(val3, val1, comparator)) {
-                        val1 = val3; new_i1 = new_i3;
-                    }
-                }
-            }
-            if (bigger(val1, i_val, comparator)) {
-                array[offset + i] = val1;
-                array[offset + new_i1] = i_val;
-            }
-        }
-    }
-}
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/jsc/Main.java rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/jsc/Main.java
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/jsc/Main.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/jsc/Main.java	2022-01-06 22:57:21.000000000 +0100
@@ -4,9 +4,15 @@
 
 package org.mozilla.javascript.tools.jsc;
 
-import java.io.*;
-import java.util.*;
-import org.mozilla.javascript.*;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.mozilla.javascript.CompilerEnvirons;
 import org.mozilla.javascript.optimizer.ClassCompiler;
 import org.mozilla.javascript.tools.SourceReader;
 import org.mozilla.javascript.tools.ToolErrorReporter;
@@ -258,8 +264,8 @@
             for (int j = 0; j != compiled.length; j += 2) {
                 String className = (String)compiled[j];
                 byte[] bytes = (byte[])compiled[j + 1];
-                File outfile = getOutputFile(targetTopDir, className);
                 try {
+                    File outfile = getOutputFile(targetTopDir, className);
                     FileOutputStream os = new FileOutputStream(outfile);
                     try {
                         os.write(bytes);
@@ -291,7 +297,7 @@
         return null;
     }
 
-    private File getOutputFile(File parentDir, String className)
+    private File getOutputFile(File parentDir, String className) throws IOException
     {
         String path = className.replace('.', File.separatorChar);
         path = path.concat(".class");
@@ -300,7 +306,9 @@
         if (dirPath != null) {
             File dir = new File(dirPath);
             if (!dir.exists()) {
-                dir.mkdirs();
+                if (!dir.mkdirs()) {
+                    throw new IOException("Error making output directory " + dirPath);
+                }
             }
         }
         return f;
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/resources/Messages.properties rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/resources/Messages.properties
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/resources/Messages.properties	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/resources/Messages.properties	2022-01-06 22:57:21.000000000 +0100
@@ -195,59 +195,3 @@
 
 msg.shell.bad.function.scope =\
     Wrong scope object for shell function: {0}
-
-msg.idswitch.same_string =\
-    The string {0} is used second time in the switch code. \
-    Previous occurrence was at line {1}
-
-msg.idswitch.file_end_in_switch =\
-    End of file inside tag {0}
-
-msg.idswitch.bad_tag_order =\
-    String switch tag {0} is not allowed here
-
-msg.idswitch.no_end_with_value =\
-    End for tag {0} can not contain value
-
-msg.idswitch.no_value_allowed =\
-    Tag {0} can not contain value
-
-msg.idswitch.no_end_usage =\
-    Tag {0} can not be used as end tag
-
-msg.idswitch.no_file_argument =\
-    File argument should be given
-
-msg.idswitch.too_many_arguments =\
-    Too many arguments are given
-
-msg.idswitch.bad_option =\
-    Invalid option {0}
-
-msg.idswitch.bad_option_char =\
-    Invalid option letter {0}
-
-msg.idswitch.bad_invocation =\
-StringIdMap: {0}\n\
-For more information, try\n\
-java org.mozilla.javascript.tools.idswitch.StringIdMap --help
-
-msg.idswitch.io_error =\
-StringIdMap: IO error, {0}
-
-msg.idswitch.usage = \
-Usage: java org.mozilla.javascript.tools.idswitch.StringIdMap [OPTIONS] JAVA_SOURCE_FILE\n\
-Generates efficient string dispatch code in JAVA_SOURCE_FILE.\n\
-The resulting Java source fragment replaces the old dispatch code.\n\
-If JAVA_SOURCE_FILE is -, standard input is used for Java source and the\n\
-result is sent to standard output.\n\
-\n\
-\  -h, --help          display this help and exit\n\
-\      --version       display version information and exit\n\
-\n\
-Note: the original file will be overwritten without any backup actions\n\
-\      and all code inside #generated# tag will be replaced by new one.
-
-msg.idswitch.version = \
-org.mozilla.javascript.tools.idswitch.StringIdMap version 0.2
-
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/shell/ConsoleTextArea.java rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/ConsoleTextArea.java
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/shell/ConsoleTextArea.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/ConsoleTextArea.java	2022-01-06 22:57:21.000000000 +0100
@@ -4,11 +4,21 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 package org.mozilla.javascript.tools.shell;
-import java.io.*;
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-import javax.swing.event.*;
+
+import java.awt.Font;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+import javax.swing.JTextArea;
+import javax.swing.SwingUtilities;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
 import javax.swing.text.Document;
 import javax.swing.text.Segment;
 
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/shell/Environment.java rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/Environment.java
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/shell/Environment.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/Environment.java	2022-01-06 22:57:21.000000000 +0100
@@ -14,12 +14,12 @@
 
 package org.mozilla.javascript.tools.shell;
 
-import org.mozilla.javascript.Scriptable;
+import java.util.Map;
+
 import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.Scriptable;
 import org.mozilla.javascript.ScriptableObject;
 
-import java.util.Map;
-
 /**
  * Environment, intended to be instantiated at global scope, provides
  * a natural way to access System properties from JavaScript.
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/shell/Global.java rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/Global.java
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/shell/Global.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/Global.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,32 +6,61 @@
 
 package org.mozilla.javascript.tools.shell;
 
-import java.io.*;
-import java.net.*;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.ObjectInputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.Reader;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
 import java.nio.charset.Charset;
-import java.lang.reflect.*;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.regex.Pattern;
 import java.util.regex.Matcher;
-import org.mozilla.javascript.*;
+import java.util.regex.Pattern;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.ContextAction;
+import org.mozilla.javascript.ContextFactory;
+import org.mozilla.javascript.ErrorReporter;
+import org.mozilla.javascript.Function;
+import org.mozilla.javascript.ImporterTopLevel;
+import org.mozilla.javascript.NativeArray;
+import org.mozilla.javascript.RhinoException;
+import org.mozilla.javascript.Script;
+import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+import org.mozilla.javascript.Synchronizer;
+import org.mozilla.javascript.Undefined;
+import org.mozilla.javascript.Wrapper;
 import org.mozilla.javascript.commonjs.module.Require;
 import org.mozilla.javascript.commonjs.module.RequireBuilder;
 import org.mozilla.javascript.commonjs.module.provider.SoftCachingModuleScriptProvider;
 import org.mozilla.javascript.commonjs.module.provider.UrlModuleSourceProvider;
+import org.mozilla.javascript.serialize.ScriptableInputStream;
+import org.mozilla.javascript.serialize.ScriptableOutputStream;
 import org.mozilla.javascript.tools.ToolErrorReporter;
-import org.mozilla.javascript.serialize.*;
 
 /**
- * This class provides for sharing functions across multiple threads.
- * This is of particular interest to server applications.
+ * This class provides for sharing functions across multiple threads. This is of particular interest
+ * to server applications.
  *
  * @author Norris Boyd
  */
-public class Global extends ImporterTopLevel
-{
+public class Global extends ImporterTopLevel {
     static final long serialVersionUID = 4029130780977538005L;
 
     NativeArray history;
@@ -43,15 +72,12 @@
     private boolean sealedStdLib = false;
     boolean initialized;
     private QuitAction quitAction;
-    private String[] prompts = { "js> ", "  > " };
-    private HashMap<String,String> doctestCanonicalizations;
+    private String[] prompts = {"js> ", "  > "};
+    private HashMap<String, String> doctestCanonicalizations;
 
-    public Global()
-    {
-    }
+    public Global() {}
 
-    public Global(Context cx)
-    {
+    public Global(Context cx) {
         init(cx);
     }
 
@@ -59,32 +85,23 @@
         return initialized;
     }
 
-    /**
-     * Set the action to call from quit().
-     */
-    public void initQuitAction(QuitAction quitAction)
-    {
-        if (quitAction == null)
-            throw new IllegalArgumentException("quitAction is null");
-        if (this.quitAction != null)
-            throw new IllegalArgumentException("The method is once-call.");
+    /** Set the action to call from quit(). */
+    public void initQuitAction(QuitAction quitAction) {
+        if (quitAction == null) throw new IllegalArgumentException("quitAction is null");
+        if (this.quitAction != null) throw new IllegalArgumentException("The method is once-call.");
 
         this.quitAction = quitAction;
     }
 
-    public void init(ContextFactory factory)
-    {
-        factory.call(new ContextAction() {
-                public Object run(Context cx)
-                {
+    public void init(ContextFactory factory) {
+        factory.call(
+                cx -> {
                     init(cx);
                     return null;
-                }
-            });
+                });
     }
 
-    public void init(Context cx)
-    {
+    public void init(Context cx) {
         // Define some global functions particular to the shell. Note
         // that these functions are not part of ECMA.
         initStandardObjects(cx, sealedStdLib);
@@ -110,15 +127,13 @@
             "version",
             "write"
         };
-        defineFunctionProperties(names, Global.class,
-                                 ScriptableObject.DONTENUM);
+        defineFunctionProperties(names, Global.class, ScriptableObject.DONTENUM);
 
         // Set up "environment" in the global scope to provide access to the
         // System environment variables.
         Environment.defineClass(this);
         Environment environment = new Environment(this);
-        defineProperty("environment", environment,
-                       ScriptableObject.DONTENUM);
+        defineProperty("environment", environment, ScriptableObject.DONTENUM);
 
         history = (NativeArray) cx.newArray(this, 0);
         defineProperty("history", history, ScriptableObject.DONTENUM);
@@ -126,8 +141,7 @@
         initialized = true;
     }
 
-    public Require installRequire(Context cx, List<String> modulePath,
-                                  boolean sandboxed) {
+    public Require installRequire(Context cx, List<String> modulePath, boolean sandboxed) {
         RequireBuilder rb = new RequireBuilder();
         rb.setSandboxed(sandboxed);
         List<URI> uris = new ArrayList<URI>();
@@ -151,8 +165,7 @@
             }
         }
         rb.setModuleScriptProvider(
-                new SoftCachingModuleScriptProvider(
-                        new UrlModuleSourceProvider(uris, null)));
+                new SoftCachingModuleScriptProvider(new UrlModuleSourceProvider(uris, null)));
         Require require = rb.createRequire(cx, this);
         require.install(this);
         return require;
@@ -161,52 +174,37 @@
     /**
      * Print a help message.
      *
-     * This method is defined as a JavaScript function.
+     * <p>This method is defined as a JavaScript function.
      */
-    public static void help(Context cx, Scriptable thisObj,
-                            Object[] args, Function funObj)
-    {
+    public static void help(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
         PrintStream out = getInstance(funObj).getOut();
         out.println(ToolErrorReporter.getMessage("msg.help"));
     }
 
-    public static void gc(Context cx, Scriptable thisObj,
-            Object[] args, Function funObj)
-    {
+    public static void gc(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
         System.gc();
     }
 
-
     /**
      * Print the string values of its arguments.
      *
-     * This method is defined as a JavaScript function.
-     * Note that its arguments are of the "varargs" form, which
-     * allows it to handle an arbitrary number of arguments
-     * supplied to the JavaScript function.
-     *
+     * <p>This method is defined as a JavaScript function. Note that its arguments are of the
+     * "varargs" form, which allows it to handle an arbitrary number of arguments supplied to the
+     * JavaScript function.
      */
-    public static Object print(Context cx, Scriptable thisObj,
-                               Object[] args, Function funObj)
-    {
+    public static Object print(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
         return doPrint(args, funObj, true);
     }
 
-    /**
-     * Print just as in "print," but without the trailing newline.
-     */
-    public static Object write(Context cx, Scriptable thisObj,
-                               Object[] args, Function funObj)
-    {
+    /** Print just as in "print," but without the trailing newline. */
+    public static Object write(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
         return doPrint(args, funObj, false);
     }
 
-    private static Object doPrint(Object[] args, Function funObj, boolean newline)
-    {
+    private static Object doPrint(Object[] args, Function funObj, boolean newline) {
         PrintStream out = getInstance(funObj).getOut();
-        for (int i=0; i < args.length; i++) {
-            if (i > 0)
-                out.print(" ");
+        for (int i = 0; i < args.length; i++) {
+            if (i > 0) out.print(" ");
 
             // Convert the arbitrary JavaScript value into a string form.
             String s = Context.toString(args[i]);
@@ -220,18 +218,14 @@
     }
 
     /**
-     * Call embedding-specific quit action passing its argument as
-     * int32 exit code.
+     * Call embedding-specific quit action passing its argument as int32 exit code.
      *
-     * This method is defined as a JavaScript function.
+     * <p>This method is defined as a JavaScript function.
      */
-    public static void quit(Context cx, Scriptable thisObj,
-                            Object[] args, Function funObj)
-    {
+    public static void quit(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
         Global global = getInstance(funObj);
         if (global.quitAction != null) {
-            int exitCode = (args.length == 0 ? 0
-                            : ScriptRuntime.toInt32(args[0]));
+            int exitCode = (args.length == 0 ? 0 : ScriptRuntime.toInt32(args[0]));
             global.quitAction.quit(cx, exitCode);
         }
     }
@@ -239,90 +233,74 @@
     /**
      * Get and set the language version.
      *
-     * This method is defined as a JavaScript function.
+     * <p>This method is defined as a JavaScript function.
      */
-    public static double version(Context cx, Scriptable thisObj,
-                                 Object[] args, Function funObj)
-    {
-        double result = cx.getLanguageVersion();
+    public static double version(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
         if (args.length > 0) {
             double d = Context.toNumber(args[0]);
             cx.setLanguageVersion((int) d);
         }
-        return result;
+        return cx.getLanguageVersion();
     }
 
     /**
      * Load and execute a set of JavaScript source files.
      *
-     * This method is defined as a JavaScript function.
-     *
+     * <p>This method is defined as a JavaScript function.
      */
-    public static void load(Context cx, Scriptable thisObj,
-                            Object[] args, Function funObj)
-    {
+    public static void load(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
         for (Object arg : args) {
             String file = Context.toString(arg);
             try {
                 Main.processFile(cx, thisObj, file);
             } catch (IOException ioex) {
-                String msg = ToolErrorReporter.getMessage(
-                        "msg.couldnt.read.source", file, ioex.getMessage());
+                String msg =
+                        ToolErrorReporter.getMessage(
+                                "msg.couldnt.read.source", file, ioex.getMessage());
                 throw Context.reportRuntimeError(msg);
             } catch (VirtualMachineError ex) {
                 // Treat StackOverflow and OutOfMemory as runtime errors
                 ex.printStackTrace();
-                String msg = ToolErrorReporter.getMessage(
-                        "msg.uncaughtJSException", ex.toString());
+                String msg = ToolErrorReporter.getMessage("msg.uncaughtJSException", ex.toString());
                 throw Context.reportRuntimeError(msg);
             }
         }
     }
 
     /**
-     * Load a Java class that defines a JavaScript object using the
-     * conventions outlined in ScriptableObject.defineClass.
-     * <p>
-     * This method is defined as a JavaScript function.
-     * @exception IllegalAccessException if access is not available
-     *            to a reflected class member
-     * @exception InstantiationException if unable to instantiate
-     *            the named class
-     * @exception InvocationTargetException if an exception is thrown
-     *            during execution of methods of the named class
+     * Load a Java class that defines a JavaScript object using the conventions outlined in
+     * ScriptableObject.defineClass.
+     *
+     * <p>This method is defined as a JavaScript function.
+     *
+     * @exception IllegalAccessException if access is not available to a reflected class member
+     * @exception InstantiationException if unable to instantiate the named class
+     * @exception InvocationTargetException if an exception is thrown during execution of methods of
+     *     the named class
      * @see org.mozilla.javascript.ScriptableObject#defineClass(Scriptable,Class)
      */
     @SuppressWarnings({"unchecked"})
-    public static void defineClass(Context cx, Scriptable thisObj,
-                                   Object[] args, Function funObj)
-        throws IllegalAccessException, InstantiationException,
-               InvocationTargetException
-    {
+    public static void defineClass(Context cx, Scriptable thisObj, Object[] args, Function funObj)
+            throws IllegalAccessException, InstantiationException, InvocationTargetException {
         Class<?> clazz = getClass(args);
         if (!Scriptable.class.isAssignableFrom(clazz)) {
             throw reportRuntimeError("msg.must.implement.Scriptable");
         }
-        ScriptableObject.defineClass(thisObj, (Class<? extends Scriptable>)clazz);
+        ScriptableObject.defineClass(thisObj, (Class<? extends Scriptable>) clazz);
     }
 
     /**
      * Load and execute a script compiled to a class file.
-     * <p>
-     * This method is defined as a JavaScript function.
-     * When called as a JavaScript function, a single argument is
-     * expected. This argument should be the name of a class that
-     * implements the Script interface, as will any script
-     * compiled by jsc.
-     *
-     * @exception IllegalAccessException if access is not available
-     *            to the class
-     * @exception InstantiationException if unable to instantiate
-     *            the named class
+     *
+     * <p>This method is defined as a JavaScript function. When called as a JavaScript function, a
+     * single argument is expected. This argument should be the name of a class that implements the
+     * Script interface, as will any script compiled by jsc.
+     *
+     * @exception IllegalAccessException if access is not available to the class
+     * @exception InstantiationException if unable to instantiate the named class
      */
-    public static void loadClass(Context cx, Scriptable thisObj,
-                                 Object[] args, Function funObj)
-        throws IllegalAccessException, InstantiationException
-    {
+    public static void loadClass(Context cx, Scriptable thisObj, Object[] args, Function funObj)
+            throws IllegalAccessException, InstantiationException {
         Class<?> clazz = getClass(args);
         if (!Script.class.isAssignableFrom(clazz)) {
             throw reportRuntimeError("msg.must.implement.Script");
@@ -337,27 +315,23 @@
         }
         Object arg0 = args[0];
         if (arg0 instanceof Wrapper) {
-            Object wrapped = ((Wrapper)arg0).unwrap();
-            if (wrapped instanceof Class)
-                return (Class<?>)wrapped;
+            Object wrapped = ((Wrapper) arg0).unwrap();
+            if (wrapped instanceof Class) return (Class<?>) wrapped;
         }
         String className = Context.toString(args[0]);
         try {
             return Class.forName(className);
-        }
-        catch (ClassNotFoundException cnfe) {
+        } catch (ClassNotFoundException cnfe) {
             throw reportRuntimeError("msg.class.not.found", className);
         }
     }
 
-    public static void serialize(Context cx, Scriptable thisObj,
-                                 Object[] args, Function funObj)
-        throws IOException
-    {
+    public static void serialize(Context cx, Scriptable thisObj, Object[] args, Function funObj)
+            throws IOException {
         if (args.length < 2) {
             throw Context.reportRuntimeError(
-                "Expected an object to serialize and a filename to write " +
-                "the serialization to");
+                    "Expected an object to serialize and a filename to write "
+                            + "the serialization to");
         }
         Object obj = args[0];
         String filename = Context.toString(args[1]);
@@ -368,13 +342,10 @@
         out.close();
     }
 
-    public static Object deserialize(Context cx, Scriptable thisObj,
-                                     Object[] args, Function funObj)
-        throws IOException, ClassNotFoundException
-    {
+    public static Object deserialize(Context cx, Scriptable thisObj, Object[] args, Function funObj)
+            throws IOException, ClassNotFoundException {
         if (args.length < 1) {
-            throw Context.reportRuntimeError(
-                "Expected a filename to read the serialization from");
+            throw Context.reportRuntimeError("Expected a filename to read the serialization from");
         }
         String filename = Context.toString(args[0]);
         FileInputStream fis = new FileInputStream(filename);
@@ -387,23 +358,18 @@
 
     public String[] getPrompts(Context cx) {
         if (ScriptableObject.hasProperty(this, "prompts")) {
-            Object promptsJS = ScriptableObject.getProperty(this,
-                                                            "prompts");
+            Object promptsJS = ScriptableObject.getProperty(this, "prompts");
             if (promptsJS instanceof Scriptable) {
                 Scriptable s = (Scriptable) promptsJS;
-                if (ScriptableObject.hasProperty(s, 0) &&
-                    ScriptableObject.hasProperty(s, 1))
-                {
+                if (ScriptableObject.hasProperty(s, 0) && ScriptableObject.hasProperty(s, 1)) {
                     Object elem0 = ScriptableObject.getProperty(s, 0);
                     if (elem0 instanceof Function) {
-                        elem0 = ((Function) elem0).call(cx, this, s,
-                                new Object[0]);
+                        elem0 = ((Function) elem0).call(cx, this, s, new Object[0]);
                     }
                     prompts[0] = Context.toString(elem0);
                     Object elem1 = ScriptableObject.getProperty(s, 1);
                     if (elem1 instanceof Function) {
-                        elem1 = ((Function) elem1).call(cx, this, s,
-                                new Object[0]);
+                        elem1 = ((Function) elem1).call(cx, this, s, new Object[0]);
                     }
                     prompts[1] = Context.toString(elem1);
                 }
@@ -413,21 +379,20 @@
     }
 
     /**
-     * Example: doctest("js> function f() {\n  >   return 3;\n  > }\njs> f();\n3\n"); returns 2
-     * (since 2 tests were executed).
+     * Example: doctest("js> function f() {\n > return 3;\n > }\njs> f();\n3\n");
+     * returns 2 (since 2 tests were executed).
      */
-    public static Object doctest(Context cx, Scriptable thisObj,
-        Object[] args, Function funObj) {
+    public static Object doctest(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
         if (args.length == 0) {
             return Boolean.FALSE;
         }
         String session = Context.toString(args[0]);
         Global global = getInstance(funObj);
-        return new Integer(global.runDoctest(cx, global, session, null, 0));
+        return global.runDoctest(cx, global, session, null, 0);
     }
 
-    public int runDoctest(Context cx, Scriptable scope, String session,
-        String sourceName, int lineNumber) {
+    public int runDoctest(
+            Context cx, Scriptable scope, String session, String sourceName, int lineNumber) {
         doctestCanonicalizations = new HashMap<String, String>();
         String[] lines = session.split("\r\n?|\n");
         String prompt0 = this.prompts[0].trim();
@@ -438,18 +403,18 @@
             i++; // skip lines that don't look like shell sessions
         }
         while (i < lines.length) {
-            String inputString = lines[i].trim().substring(prompt0.length());
-            inputString += "\n";
+            StringBuilder inputString =
+                    new StringBuilder(lines[i].trim().substring(prompt0.length()));
+            inputString.append('\n');
             i++;
             while (i < lines.length && lines[i].trim().startsWith(prompt1)) {
-                inputString += lines[i].trim().substring(prompt1.length());
-                inputString += "\n";
+                inputString.append(lines[i].trim().substring(prompt1.length()));
+                inputString.append('\n');
                 i++;
             }
-            String expectedString = "";
-            while (i < lines.length &&
-                !lines[i].trim().startsWith(prompt0)) {
-                expectedString += lines[i] + "\n";
+            StringBuilder expectedString = new StringBuilder();
+            while (i < lines.length && !lines[i].trim().startsWith(prompt0)) {
+                expectedString.append(lines[i]).append('\n');
                 i++;
             }
             PrintStream savedOut = this.getOut();
@@ -463,11 +428,12 @@
             cx.setErrorReporter(new ToolErrorReporter(false, this.getErr()));
             try {
                 testCount++;
-                Object result = cx.evaluateString(scope, inputString,
-                    "doctest input", 1, null);
-                if (result != Context.getUndefinedValue() &&
-                    !(result instanceof Function &&
-                        inputString.trim().startsWith("function"))) {
+                String finalInputString = inputString.toString();
+                Object result =
+                        cx.evaluateString(scope, finalInputString, "doctest input", 1, null);
+                if (result != Context.getUndefinedValue()
+                        && !(result instanceof Function
+                                && finalInputString.trim().startsWith("function"))) {
                     resultString = Context.toString(result);
                 }
             } catch (RhinoException e) {
@@ -478,14 +444,18 @@
                 cx.setErrorReporter(savedErrorReporter);
                 resultString += err.toString() + out.toString();
             }
-            if (!doctestOutputMatches(expectedString, resultString)) {
-                String message = "doctest failure running:\n" +
-                    inputString +
-                    "expected: " + expectedString +
-                    "actual: " + resultString + "\n";
+            if (!doctestOutputMatches(expectedString.toString(), resultString)) {
+                String message =
+                        "doctest failure running:\n"
+                                + inputString
+                                + "expected: "
+                                + expectedString
+                                + "actual: "
+                                + resultString
+                                + "\n";
                 if (sourceName != null) {
-                    throw Context.reportRuntimeError(message, sourceName,
-                        lineNumber + i - 1, null, 0);
+                    throw Context.reportRuntimeError(
+                            message, sourceName, lineNumber + i - 1, null, 0);
                 } else {
                     throw Context.reportRuntimeError(message);
                 }
@@ -495,26 +465,22 @@
     }
 
     /**
-     * Compare actual result of doctest to expected, modulo some
-     * acceptable differences. Currently just trims the strings
-     * before comparing, but should ignore differences in line numbers
-     * for error messages for example.
+     * Compare actual result of doctest to expected, modulo some acceptable differences. Currently
+     * just trims the strings before comparing, but should ignore differences in line numbers for
+     * error messages for example.
      *
      * @param expected the expected string
      * @param actual the actual string
-     * @return true iff actual matches expected modulo some acceptable
-     *      differences
+     * @return true iff actual matches expected modulo some acceptable differences
      */
     private boolean doctestOutputMatches(String expected, String actual) {
         expected = expected.trim();
         actual = actual.trim().replace("\r\n", "\n");
-        if (expected.equals(actual))
-            return true;
-        for (Map.Entry<String,String> entry: doctestCanonicalizations.entrySet()) {
+        if (expected.equals(actual)) return true;
+        for (Map.Entry<String, String> entry : doctestCanonicalizations.entrySet()) {
             expected = expected.replace(entry.getKey(), entry.getValue());
         }
-        if (expected.equals(actual))
-            return true;
+        if (expected.equals(actual)) return true;
         // java.lang.Object.toString() prints out a unique hex number associated
         // with each object. This number changes from run to run, so we want to
         // ignore differences between these numbers in the output. We search for a
@@ -524,16 +490,12 @@
         Pattern p = Pattern.compile("@[0-9a-fA-F]+");
         Matcher expectedMatcher = p.matcher(expected);
         Matcher actualMatcher = p.matcher(actual);
-        for (;;) {
-            if (!expectedMatcher.find())
-                return false;
-            if (!actualMatcher.find())
-                return false;
-            if (actualMatcher.start() != expectedMatcher.start())
-                return false;
+        for (; ; ) {
+            if (!expectedMatcher.find()) return false;
+            if (!actualMatcher.find()) return false;
+            if (actualMatcher.start() != expectedMatcher.start()) return false;
             int start = expectedMatcher.start();
-            if (!expected.substring(0, start).equals(actual.substring(0, start)))
-                return false;
+            if (!expected.substring(0, start).equals(actual.substring(0, start))) return false;
             String expectedGroup = expectedMatcher.group();
             String actualGroup = actualMatcher.group();
             String mapping = doctestCanonicalizations.get(expectedGroup);
@@ -543,26 +505,17 @@
             } else if (!actualGroup.equals(mapping)) {
                 return false; // wrong object!
             }
-            if (expected.equals(actual))
-                return true;
+            if (expected.equals(actual)) return true;
         }
     }
 
     /**
-     * The spawn function runs a given function or script in a different
-     * thread.
+     * The spawn function runs a given function or script in a different thread.
      *
-     * js> function g() { a = 7; }
-     * js> a = 3;
-     * 3
-     * js> spawn(g)
-     * Thread[Thread-1,5,main]
-     * js> a
-     * 3
+     * <p>js> function g() { a = 7; } js> a = 3; 3 js> spawn(g) Thread[Thread-1,5,main]
+     * js> a 3
      */
-    public static Object spawn(Context cx, Scriptable thisObj, Object[] args,
-                               Function funObj)
-    {
+    public static Object spawn(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
         Scriptable scope = funObj.getParentScope();
         Runner runner;
         if (args.length != 0 && args[0] instanceof Function) {
@@ -570,7 +523,9 @@
             if (args.length > 1 && args[1] instanceof Scriptable) {
                 newArgs = cx.getElements((Scriptable) args[1]);
             }
-            if (newArgs == null) { newArgs = ScriptRuntime.emptyArgs; }
+            if (newArgs == null) {
+                newArgs = ScriptRuntime.emptyArgs;
+            }
             runner = new Runner(scope, (Function) args[0], newArgs);
         } else if (args.length != 0 && args[0] instanceof Script) {
             runner = new Runner(scope, (Script) args[0]);
@@ -584,81 +539,62 @@
     }
 
     /**
-     * The sync function creates a synchronized function (in the sense
-     * of a Java synchronized method) from an existing function. The
-     * new function synchronizes on the the second argument if it is
-     * defined, or otherwise the <code>this</code> object of
-     * its invocation.
-     * js> var o = { f : sync(function(x) {
-     *       print("entry");
-     *       Packages.java.lang.Thread.sleep(x*1000);
-     *       print("exit");
-     *     })};
-     * js> spawn(function() {o.f(5);});
-     * Thread[Thread-0,5,main]
-     * entry
-     * js> spawn(function() {o.f(5);});
-     * Thread[Thread-1,5,main]
-     * js>
-     * exit
-     * entry
-     * exit
+     * The sync function creates a synchronized function (in the sense of a Java synchronized
+     * method) from an existing function. The new function synchronizes on the the second argument
+     * if it is defined, or otherwise the <code>this</code> object of its invocation. js> var o =
+     * { f : sync(function(x) { print("entry"); Packages.java.lang.Thread.sleep(x*1000);
+     * print("exit"); })}; js> spawn(function() {o.f(5);}); Thread[Thread-0,5,main] entry js>
+     * spawn(function() {o.f(5);}); Thread[Thread-1,5,main] js> exit entry exit
      */
-    public static Object sync(Context cx, Scriptable thisObj, Object[] args,
-                              Function funObj)
-    {
+    public static Object sync(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
         if (args.length >= 1 && args.length <= 2 && args[0] instanceof Function) {
             Object syncObject = null;
             if (args.length == 2 && args[1] != Undefined.instance) {
                 syncObject = args[1];
             }
-            return new Synchronizer((Function)args[0], syncObject);
-        }
-        else {
+            return new Synchronizer((Function) args[0], syncObject);
+        } else {
             throw reportRuntimeError("msg.sync.args");
         }
     }
 
     /**
-     * Execute the specified command with the given argument and options
-     * as a separate process and return the exit status of the process.
-     * <p>
-     * Usage:
+     * Execute the specified command with the given argument and options as a separate process and
+     * return the exit status of the process.
+     *
+     * <p>Usage:
+     *
      * <pre>
      * runCommand(command)
      * runCommand(command, arg1, ..., argN)
      * runCommand(command, arg1, ..., argN, options)
      * </pre>
-     * All except the last arguments to runCommand are converted to strings
-     * and denote command name and its arguments. If the last argument is a
-     * JavaScript object, it is an option object. Otherwise it is converted to
-     * string denoting the last argument and options objects assumed to be
-     * empty.
-     * The following properties of the option object are processed:
+     *
+     * All except the last arguments to runCommand are converted to strings and denote command name
+     * and its arguments. If the last argument is a JavaScript object, it is an option object.
+     * Otherwise it is converted to string denoting the last argument and options objects assumed to
+     * be empty. The following properties of the option object are processed:
+     *
      * <ul>
-     * <li><tt>args</tt> - provides an array of additional command arguments
-     * <li><tt>env</tt> - explicit environment object. All its enumerable
-     *   properties define the corresponding environment variable names.
-     * <li><tt>input</tt> - the process input. If it is not
-     *   java.io.InputStream, it is converted to string and sent to the process
-     *   as its input. If not specified, no input is provided to the process.
-     * <li><tt>output</tt> - the process output instead of
-     *   java.lang.System.out. If it is not instance of java.io.OutputStream,
-     *   the process output is read, converted to a string, appended to the
-     *   output property value converted to string and put as the new value of
-     *   the output property.
-     * <li><tt>err</tt> - the process error output instead of
-     *   java.lang.System.err. If it is not instance of java.io.OutputStream,
-     *   the process error output is read, converted to a string, appended to
-     *   the err property value converted to string and put as the new
-     *   value of the err property.
-     * <li><tt>dir</tt> - the working direcotry to run the commands.
+     *   <li><code>args</code> - provides an array of additional command arguments
+     *   <li><code>env</code> - explicit environment object. All its enumerable properties define
+     *       the corresponding environment variable names.
+     *   <li><code>input</code> - the process input. If it is not java.io.InputStream, it is
+     *       converted to string and sent to the process as its input. If not specified, no input is
+     *       provided to the process.
+     *   <li><code>output</code> - the process output instead of java.lang.System.out. If it is not
+     *       instance of java.io.OutputStream, the process output is read, converted to a string,
+     *       appended to the output property value converted to string and put as the new value of
+     *       the output property.
+     *   <li><code>err</code> - the process error output instead of java.lang.System.err. If it is
+     *       not instance of java.io.OutputStream, the process error output is read, converted to a
+     *       string, appended to the err property value converted to string and put as the new value
+     *       of the err property.
+     *   <li><code>dir</code> - the working direcotry to run the commands.
      * </ul>
      */
-    public static Object runCommand(Context cx, Scriptable thisObj,
-                                    Object[] args, Function funObj)
-        throws IOException
-    {
+    public static Object runCommand(Context cx, Scriptable thisObj, Object[] args, Function funObj)
+            throws IOException {
         int L = args.length;
         if (L == 0 || (L == 1 && args[0] instanceof Scriptable)) {
             throw reportRuntimeError("msg.runCommand.bad.args");
@@ -672,7 +608,7 @@
         Scriptable params = null;
         Object[] addArgs = null;
         if (args[L - 1] instanceof Scriptable) {
-            params = (Scriptable)args[L - 1];
+            params = (Scriptable) args[L - 1];
             --L;
             Object envObj = ScriptableObject.getProperty(params, "env");
             if (envObj != Scriptable.NOT_FOUND) {
@@ -682,29 +618,29 @@
                     if (!(envObj instanceof Scriptable)) {
                         throw reportRuntimeError("msg.runCommand.bad.env");
                     }
-                    Scriptable envHash = (Scriptable)envObj;
+                    Scriptable envHash = (Scriptable) envObj;
                     Object[] ids = ScriptableObject.getPropertyIds(envHash);
                     environment = new String[ids.length];
                     for (int i = 0; i != ids.length; ++i) {
                         Object keyObj = ids[i], val;
                         String key;
                         if (keyObj instanceof String) {
-                            key = (String)keyObj;
+                            key = (String) keyObj;
                             val = ScriptableObject.getProperty(envHash, key);
                         } else {
-                            int ikey = ((Number)keyObj).intValue();
+                            int ikey = ((Number) keyObj).intValue();
                             key = Integer.toString(ikey);
                             val = ScriptableObject.getProperty(envHash, ikey);
                         }
                         if (val == ScriptableObject.NOT_FOUND) {
                             val = Undefined.instance;
                         }
-                        environment[i] = key+'='+ScriptRuntime.toString(val);
+                        environment[i] = key + '=' + ScriptRuntime.toString(val);
                     }
                 }
             }
             Object wdObj = ScriptableObject.getProperty(params, "dir");
-            if(wdObj != Scriptable.NOT_FOUND){
+            if (wdObj != Scriptable.NOT_FOUND) {
                 wd = new File(ScriptRuntime.toString(wdObj));
             }
 
@@ -730,17 +666,16 @@
             }
             Object addArgsObj = ScriptableObject.getProperty(params, "args");
             if (addArgsObj != Scriptable.NOT_FOUND) {
-                Scriptable s = Context.toObject(addArgsObj,
-                                                getTopLevelScope(thisObj));
+                Scriptable s = Context.toObject(addArgsObj, getTopLevelScope(thisObj));
                 addArgs = cx.getElements(s);
             }
         }
         Global global = getInstance(funObj);
         if (out == null) {
-            out = (global != null) ? global.getOut() : System.out;
+            out = global.getOut();
         }
         if (err == null) {
-            err = (global != null) ? global.getErr() : System.err;
+            err = global.getErr();
         }
         // If no explicit input stream, do not send any input to process,
         // in particular, do not use System.in to avoid deadlocks
@@ -767,21 +702,15 @@
             ScriptableObject.putProperty(params, "err", s);
         }
 
-        return new Integer(exitCode);
+        return exitCode;
     }
 
-    /**
-     * The seal function seals all supplied arguments.
-     */
-    public static void seal(Context cx, Scriptable thisObj, Object[] args,
-                            Function funObj)
-    {
+    /** The seal function seals all supplied arguments. */
+    public static void seal(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
         for (int i = 0; i != args.length; ++i) {
             Object arg = args[i];
-            if (!(arg instanceof ScriptableObject) || arg == Undefined.instance)
-            {
-                if (!(arg instanceof Scriptable) || arg == Undefined.instance)
-                {
+            if (!(arg instanceof ScriptableObject) || arg == Undefined.instance) {
+                if (!(arg instanceof Scriptable) || arg == Undefined.instance) {
                     throw reportRuntimeError("msg.shell.seal.not.object");
                 } else {
                     throw reportRuntimeError("msg.shell.seal.not.scriptable");
@@ -791,27 +720,25 @@
 
         for (int i = 0; i != args.length; ++i) {
             Object arg = args[i];
-            ((ScriptableObject)arg).sealObject();
+            ((ScriptableObject) arg).sealObject();
         }
     }
 
     /**
-     * The readFile reads the given file content and convert it to a string
-     * using the specified character coding or default character coding if
-     * explicit coding argument is not given.
-     * <p>
-     * Usage:
+     * The readFile reads the given file content and convert it to a string using the specified
+     * character coding or default character coding if explicit coding argument is not given.
+     *
+     * <p>Usage:
+     *
      * <pre>
      * readFile(filePath)
      * readFile(filePath, charCoding)
      * </pre>
-     * The first form converts file's context to string using the default
-     * character coding.
+     *
+     * The first form converts file's context to string using the default character coding.
      */
-    public static Object readFile(Context cx, Scriptable thisObj, Object[] args,
-                                  Function funObj)
-        throws IOException
-    {
+    public static Object readFile(Context cx, Scriptable thisObj, Object[] args, Function funObj)
+            throws IOException {
         if (args.length == 0) {
             throw reportRuntimeError("msg.shell.readFile.bad.args");
         }
@@ -825,23 +752,21 @@
     }
 
     /**
-     * The readUrl opens connection to the given URL, read all its data
-     * and converts them to a string
-     * using the specified character coding or default character coding if
-     * explicit coding argument is not given.
-     * <p>
-     * Usage:
+     * The readUrl opens connection to the given URL, read all its data and converts them to a
+     * string using the specified character coding or default character coding if explicit coding
+     * argument is not given.
+     *
+     * <p>Usage:
+     *
      * <pre>
      * readUrl(url)
      * readUrl(url, charCoding)
      * </pre>
-     * The first form converts file's context to string using the default
-     * charCoding.
+     *
+     * The first form converts file's context to string using the default charCoding.
      */
-    public static Object readUrl(Context cx, Scriptable thisObj, Object[] args,
-                                 Function funObj)
-        throws IOException
-    {
+    public static Object readUrl(Context cx, Scriptable thisObj, Object[] args, Function funObj)
+            throws IOException {
         if (args.length == 0) {
             throw reportRuntimeError("msg.shell.readUrl.bad.args");
         }
@@ -854,15 +779,10 @@
         return readUrl(url, charCoding, false);
     }
 
-    /**
-     * Convert the argument to int32 number.
-     */
-    public static Object toint32(Context cx, Scriptable thisObj, Object[] args,
-                                 Function funObj)
-    {
+    /** Convert the argument to int32 number. */
+    public static Object toint32(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
         Object arg = (args.length != 0 ? args[0] : Undefined.instance);
-        if (arg instanceof Integer)
-            return arg;
+        if (arg instanceof Integer) return arg;
         return ScriptRuntime.wrapInt(ScriptRuntime.toInt32(arg));
     }
 
@@ -911,39 +831,38 @@
         errStream = err;
     }
 
-    public void setSealedStdLib(boolean value)
-    {
+    public void setSealedStdLib(boolean value) {
         sealedStdLib = value;
     }
 
-    private static Global getInstance(Function function)
-    {
+    private static Global getInstance(Function function) {
         Scriptable scope = function.getParentScope();
         if (!(scope instanceof Global))
-            throw reportRuntimeError("msg.bad.shell.function.scope",
-                                     String.valueOf(scope));
-        return (Global)scope;
+            throw reportRuntimeError("msg.bad.shell.function.scope", String.valueOf(scope));
+        return (Global) scope;
     }
 
     /**
-     * Runs the given process using Runtime.exec().
-     * If any of in, out, err is null, the corresponding process stream will
-     * be closed immediately, otherwise it will be closed as soon as
-     * all data will be read from/written to process
+     * Runs the given process using Runtime.exec(). If any of in, out, err is null, the
+     * corresponding process stream will be closed immediately, otherwise it will be closed as soon
+     * as all data will be read from/written to process
      *
      * @return Exit value of process.
      * @throws IOException If there was an error executing the process.
      */
-    private static int runProcess(String[] cmd, String[] environment,
-                                  File wd, InputStream in, OutputStream out,
-                                  OutputStream err)
-        throws IOException
-    {
+    private static int runProcess(
+            String[] cmd,
+            String[] environment,
+            File wd,
+            InputStream in,
+            OutputStream out,
+            OutputStream err)
+            throws IOException {
         Process p;
         if (environment == null) {
-            p = Runtime.getRuntime().exec(cmd,null,wd);
+            p = Runtime.getRuntime().exec(cmd, null, wd);
         } else {
-            p = Runtime.getRuntime().exec(cmd, environment,wd);
+            p = Runtime.getRuntime().exec(cmd, environment, wd);
         }
 
         try {
@@ -972,7 +891,7 @@
             }
 
             // wait for process completion
-            for (;;) {
+            for (; ; ) {
                 try {
                     p.waitFor();
                     if (outThread != null) {
@@ -995,13 +914,11 @@
         }
     }
 
-    static void pipe(boolean fromProcess, InputStream from, OutputStream to)
-        throws IOException
-    {
+    static void pipe(boolean fromProcess, InputStream from, OutputStream to) throws IOException {
         try {
             final int SIZE = 4096;
             byte[] buffer = new byte[SIZE];
-            for (;;) {
+            for (; ; ) {
                 int n;
                 if (!fromProcess) {
                     n = from.read(buffer, 0, SIZE);
@@ -1013,7 +930,9 @@
                         break;
                     }
                 }
-                if (n < 0) { break; }
+                if (n < 0) {
+                    break;
+                }
                 if (fromProcess) {
                     to.write(buffer, 0, n);
                     to.flush();
@@ -1041,25 +960,25 @@
         }
     }
 
-    private static InputStream toInputStream(Object value)
-        throws IOException
-    {
+    private static InputStream toInputStream(Object value) throws IOException {
         InputStream is = null;
         String s = null;
         if (value instanceof Wrapper) {
-            Object unwrapped = ((Wrapper)value).unwrap();
+            Object unwrapped = ((Wrapper) value).unwrap();
             if (unwrapped instanceof InputStream) {
-                is = (InputStream)unwrapped;
+                is = (InputStream) unwrapped;
             } else if (unwrapped instanceof byte[]) {
-                is = new ByteArrayInputStream((byte[])unwrapped);
+                is = new ByteArrayInputStream((byte[]) unwrapped);
             } else if (unwrapped instanceof Reader) {
-                s = readReader((Reader)unwrapped);
+                s = readReader((Reader) unwrapped);
             } else if (unwrapped instanceof char[]) {
-                s = new String((char[])unwrapped);
+                s = new String((char[]) unwrapped);
             }
         }
         if (is == null) {
-            if (s == null) { s = ScriptRuntime.toString(value); }
+            if (s == null) {
+                s = ScriptRuntime.toString(value);
+            }
             is = new ByteArrayInputStream(s.getBytes());
         }
         return is;
@@ -1068,18 +987,16 @@
     private static OutputStream toOutputStream(Object value) {
         OutputStream os = null;
         if (value instanceof Wrapper) {
-            Object unwrapped = ((Wrapper)value).unwrap();
+            Object unwrapped = ((Wrapper) value).unwrap();
             if (unwrapped instanceof OutputStream) {
-                os = (OutputStream)unwrapped;
+                os = (OutputStream) unwrapped;
             }
         }
         return os;
     }
 
-    private static String readUrl(String filePath, String charCoding,
-                                  boolean urlIsFile)
-        throws IOException
-    {
+    private static String readUrl(String filePath, String charCoding, boolean urlIsFile)
+            throws IOException {
         int chunkLength;
         InputStream is = null;
         try {
@@ -1088,8 +1005,7 @@
                 URLConnection uc = urlObj.openConnection();
                 is = uc.getInputStream();
                 chunkLength = uc.getContentLength();
-                if (chunkLength <= 0)
-                    chunkLength = 1024;
+                if (chunkLength <= 0) chunkLength = 1024;
                 if (charCoding == null) {
                     String type = uc.getContentType();
                     if (type != null) {
@@ -1104,11 +1020,12 @@
                     throw new IOException("Cannot read file: " + filePath);
                 }
                 long length = f.length();
-                chunkLength = (int)length;
-                if (chunkLength != length)
-                    throw new IOException("Too big file size: "+length);
+                chunkLength = (int) length;
+                if (chunkLength != length) throw new IOException("Too big file size: " + length);
 
-                if (chunkLength == 0) { return ""; }
+                if (chunkLength == 0) {
+                    return "";
+                }
 
                 is = new FileInputStream(f);
             }
@@ -1122,32 +1039,30 @@
             return readReader(r, chunkLength);
 
         } finally {
-            if (is != null)
-                is.close();
+            if (is != null) is.close();
         }
     }
 
     /**
      * The readline reads one line from the standard input. "Prompt" is optional.
-     * <p>
-     * Usage:
+     *
+     * <p>Usage:
+     *
      * <pre>
      * readline(prompt)
      * </pre>
      */
-     public static Object readline(Context cx, Scriptable thisObj, Object[] args, Function funObj)
-         throws IOException
-     {
-         Global self = getInstance(funObj);
-
-         if (args.length > 0) {
-             return self.console.readLine(Context.toString(args[0]));
-         }
-         return self.console.readLine();
-     }
+    public static Object readline(Context cx, Scriptable thisObj, Object[] args, Function funObj)
+            throws IOException {
+        Global self = getInstance(funObj);
+
+        if (args.length > 0) {
+            return self.console.readLine(Context.toString(args[0]));
+        }
+        return self.console.readLine();
+    }
 
-    private static String getCharCodingFromType(String type)
-    {
+    private static String getCharCodingFromType(String type) {
         int i = type.indexOf(';');
         if (i >= 0) {
             int end = type.length();
@@ -1156,8 +1071,7 @@
                 ++i;
             }
             String charset = "charset";
-            if (charset.regionMatches(true, 0, type, i, charset.length()))
-            {
+            if (charset.regionMatches(true, 0, type, i, charset.length())) {
                 i += charset.length();
                 while (i != end && type.charAt(i) <= ' ') {
                     ++i;
@@ -1170,7 +1084,7 @@
                     if (i != end) {
                         // i is at the start of non-empty
                         // charCoding spec
-                        while (type.charAt(end -1) <= ' ') {
+                        while (type.charAt(end - 1) <= ' ') {
                             --end;
                         }
                         return type.substring(i, end);
@@ -1181,20 +1095,18 @@
         return null;
     }
 
-    private static String readReader(Reader reader)
-        throws IOException
-    {
+    private static String readReader(Reader reader) throws IOException {
         return readReader(reader, 4096);
     }
 
-    private static String readReader(Reader reader, int initialBufferSize)
-        throws IOException
-    {
+    private static String readReader(Reader reader, int initialBufferSize) throws IOException {
         char[] buffer = new char[initialBufferSize];
         int offset = 0;
-        for (;;) {
+        for (; ; ) {
             int n = reader.read(buffer, offset, buffer.length - offset);
-            if (n < 0) { break;    }
+            if (n < 0) {
+                break;
+            }
             offset += n;
             if (offset == buffer.length) {
                 char[] tmp = new char[buffer.length * 2];
@@ -1210,15 +1122,13 @@
         return Context.reportRuntimeError(message);
     }
 
-    static RuntimeException reportRuntimeError(String msgId, String msgArg)
-    {
+    static RuntimeException reportRuntimeError(String msgId, String msgArg) {
         String message = ToolErrorReporter.getMessage(msgId, msgArg);
         return Context.reportRuntimeError(message);
     }
 }
 
-
-class Runner implements Runnable, ContextAction {
+class Runner implements Runnable, ContextAction<Object> {
 
     Runner(Scriptable scope, Function func, Object[] args) {
         this.scope = scope;
@@ -1231,17 +1141,13 @@
         s = script;
     }
 
-    public void run()
-    {
+    public void run() {
         factory.call(this);
     }
 
-    public Object run(Context cx)
-    {
-        if (f != null)
-            return f.call(cx, scope, scope, args);
-        else
-            return s.exec(cx, scope);
+    public Object run(Context cx) {
+        if (f != null) return f.call(cx, scope, scope, args);
+        else return s.exec(cx, scope);
     }
 
     ContextFactory factory;
@@ -1273,4 +1179,3 @@
     private InputStream from;
     private OutputStream to;
 }
-
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/shell/JavaPolicySecurity.java rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/JavaPolicySecurity.java
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/shell/JavaPolicySecurity.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/JavaPolicySecurity.java	2022-01-06 22:57:21.000000000 +0100
@@ -7,12 +7,23 @@
 package org.mozilla.javascript.tools.shell;
 
 import java.io.IOException;
-import java.security.*;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.security.AccessControlContext;
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Policy;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
 import java.util.Enumeration;
 
-import org.mozilla.javascript.*;
+import org.mozilla.javascript.Callable;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.GeneratedClassLoader;
+import org.mozilla.javascript.Scriptable;
 
 public class JavaPolicySecurity extends SecurityProxy
 {
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/shell/Main.java rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/Main.java
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/shell/Main.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/Main.java	2022-01-06 22:57:21.000000000 +0100
@@ -11,12 +11,12 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintStream;
-import java.io.UnsupportedEncodingException;
 import java.lang.ref.ReferenceQueue;
 import java.lang.ref.SoftReference;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
@@ -24,11 +24,11 @@
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-
 import org.mozilla.javascript.Context;
 import org.mozilla.javascript.ContextAction;
 import org.mozilla.javascript.Function;
 import org.mozilla.javascript.GeneratedClassLoader;
+import org.mozilla.javascript.JavaScriptException;
 import org.mozilla.javascript.Kit;
 import org.mozilla.javascript.NativeArray;
 import org.mozilla.javascript.RhinoException;
@@ -44,21 +44,19 @@
 /**
  * The shell program.
  *
- * Can execute scripts interactively or in batch mode at the command line.
- * An example of controlling the JavaScript engine.
+ * <p>Can execute scripts interactively or in batch mode at the command line. An example of
+ * controlling the JavaScript engine.
  *
  * @author Norris Boyd
  */
-public class Main
-{
-    public static ShellContextFactory
-        shellContextFactory = new ShellContextFactory();
+public class Main {
+    public static ShellContextFactory shellContextFactory = new ShellContextFactory();
 
     public static Global global = new Global();
-    static protected ToolErrorReporter errorReporter;
-    static protected int exitCode = 0;
-    static private final int EXITCODE_RUNTIME_ERROR = 3;
-    static private final int EXITCODE_FILE_NOT_FOUND = 4;
+    protected static ToolErrorReporter errorReporter;
+    protected static int exitCode = 0;
+    private static final int EXITCODE_RUNTIME_ERROR = 3;
+    private static final int EXITCODE_FILE_NOT_FOUND = 4;
     static boolean processStdin = true;
     static List<String> fileList = new ArrayList<String>();
     static List<String> modulePath;
@@ -67,17 +65,14 @@
     static boolean useRequire = false;
     static Require require;
     private static SecurityProxy securityImpl;
-    private final static ScriptCache scriptCache = new ScriptCache(32);
+    private static final ScriptCache scriptCache = new ScriptCache(32);
 
     static {
         global.initQuitAction(new IProxy(IProxy.SYSTEM_EXIT));
     }
 
-    /**
-     * Proxy class to avoid proliferation of anonymous classes.
-     */
-    private static class IProxy implements ContextAction, QuitAction
-    {
+    /** Proxy class to avoid proliferation of anonymous classes. */
+    private static class IProxy implements ContextAction<Object>, QuitAction {
         private static final int PROCESS_FILES = 1;
         private static final int EVAL_INLINE_SCRIPT = 2;
         private static final int SYSTEM_EXIT = 3;
@@ -85,29 +80,40 @@
         private int type;
         String[] args;
         String scriptText;
+        private final Timers timers = new Timers();
 
-        IProxy(int type)
-        {
+        IProxy(int type) {
             this.type = type;
         }
 
-        public Object run(Context cx)
-        {
+        @Override
+        public Object run(Context cx) {
+            cx.setTrackUnhandledPromiseRejections(true);
+            timers.install(global);
             if (useRequire) {
                 require = global.installRequire(cx, modulePath, sandboxed);
             }
             if (type == PROCESS_FILES) {
                 processFiles(cx, args);
+                printPromiseWarnings(cx);
             } else if (type == EVAL_INLINE_SCRIPT) {
                 evalInlineScript(cx, scriptText);
             } else {
                 throw Kit.codeBug();
             }
+            try {
+                timers.runAllTimers(cx, global);
+            } catch (JavaScriptException e) {
+                ToolErrorReporter.reportException(cx.getErrorReporter(), e);
+                exitCode = EXITCODE_RUNTIME_ERROR;
+            } catch (InterruptedException ie) {
+                // Shell has no facility to handle interrupts so stop now
+            }
             return null;
         }
 
-        public void quit(Context cx, int exitCode)
-        {
+        @Override
+        public void quit(Context cx, int exitCode) {
             if (type == SYSTEM_EXIT) {
                 System.exit(exitCode);
                 return;
@@ -119,10 +125,9 @@
     /**
      * Main entry point.
      *
-     * Process arguments as would a normal Java program. Also
-     * create a new Context and associate it with the current thread.
-     * Then set up the execution environment and begin to
-     * execute scripts.
+     * <p>Process arguments as would a normal Java program. Also create a new Context and associate
+     * it with the current thread. Then set up the execution environment and begin to execute
+     * scripts.
      */
     public static void main(String args[]) {
         try {
@@ -139,11 +144,8 @@
         }
     }
 
-    /**
-     *  Execute the given arguments, but don't System.exit at the end.
-     */
-    public static int exec(String origArgs[])
-    {
+    /** Execute the given arguments, but don't System.exit at the end. */
+    public static int exec(String origArgs[]) {
         errorReporter = new ToolErrorReporter(false, global.getErr());
         shellContextFactory.setErrorReporter(errorReporter);
         String[] args = processOptions(origArgs);
@@ -163,33 +165,30 @@
         return exitCode;
     }
 
-    static void processFiles(Context cx, String[] args)
-    {
+    static void processFiles(Context cx, String[] args) {
         // define "arguments" array in the top-level object:
         // need to allocate new array since newArray requires instances
         // of exactly Object[], not ObjectSubclass[]
         Object[] array = new Object[args.length];
         System.arraycopy(args, 0, array, 0, args.length);
         Scriptable argsObj = cx.newArray(global, array);
-        global.defineProperty("arguments", argsObj,
-                              ScriptableObject.DONTENUM);
+        global.defineProperty("arguments", argsObj, ScriptableObject.DONTENUM);
 
-        for (String file: fileList) {
+        for (String file : fileList) {
             try {
                 processSource(cx, file);
             } catch (IOException ioex) {
-                Context.reportError(ToolErrorReporter.getMessage(
-                        "msg.couldnt.read.source", file, ioex.getMessage()));
+                Context.reportError(
+                        ToolErrorReporter.getMessage(
+                                "msg.couldnt.read.source", file, ioex.getMessage()));
                 exitCode = EXITCODE_FILE_NOT_FOUND;
             } catch (RhinoException rex) {
-                ToolErrorReporter.reportException(
-                    cx.getErrorReporter(), rex);
+                ToolErrorReporter.reportException(cx.getErrorReporter(), rex);
                 exitCode = EXITCODE_RUNTIME_ERROR;
             } catch (VirtualMachineError ex) {
                 // Treat StackOverflow and OutOfMemory as runtime errors
                 ex.printStackTrace();
-                String msg = ToolErrorReporter.getMessage(
-                    "msg.uncaughtJSException", ex.toString());
+                String msg = ToolErrorReporter.getMessage("msg.uncaughtJSException", ex.toString());
                 Context.reportError(msg);
                 exitCode = EXITCODE_RUNTIME_ERROR;
             }
@@ -203,21 +202,18 @@
                 script.exec(cx, getShellScope());
             }
         } catch (RhinoException rex) {
-            ToolErrorReporter.reportException(
-                    cx.getErrorReporter(), rex);
+            ToolErrorReporter.reportException(cx.getErrorReporter(), rex);
             exitCode = EXITCODE_RUNTIME_ERROR;
         } catch (VirtualMachineError ex) {
             // Treat StackOverflow and OutOfMemory as runtime errors
             ex.printStackTrace();
-            String msg = ToolErrorReporter.getMessage(
-                    "msg.uncaughtJSException", ex.toString());
+            String msg = ToolErrorReporter.getMessage("msg.uncaughtJSException", ex.toString());
             Context.reportError(msg);
             exitCode = EXITCODE_RUNTIME_ERROR;
         }
     }
 
-    public static Global getGlobal()
-    {
+    public static Global getGlobal() {
         return global;
     }
 
@@ -247,18 +243,15 @@
                 }
             }
             return new ModuleScope(global, uri, null);
-        } else {
-            return global;
         }
+        return global;
     }
 
-    /**
-     * Parse arguments.
-     */
-    public static String[] processOptions(String args[])
-    {
+    /** Parse arguments. */
+    public static String[] processOptions(String args[]) {
         String usageError;
-        goodUsage: for (int i = 0; ; ++i) {
+        goodUsage:
+        for (int i = 0; ; ++i) {
             if (i == args.length) {
                 return new String[0];
             }
@@ -268,7 +261,7 @@
                 fileList.add(arg);
                 mainModule = arg;
                 String[] result = new String[args.length - i - 1];
-                System.arraycopy(args, i+1, result, 0, args.length - i - 1);
+                System.arraycopy(args, i + 1, result, 0, args.length - i - 1);
                 return result;
             }
             if (arg.equals("-version")) {
@@ -392,11 +385,12 @@
                 shellContextFactory.setGeneratingDebug(true);
                 continue;
             }
-            if (arg.equals("-?") ||
-                arg.equals("-help")) {
+            if (arg.equals("-?") || arg.equals("-help")) {
                 // print usage message
-                global.getOut().println(
-                    ToolErrorReporter.getMessage("msg.shell.usage", Main.class.getName()));
+                global.getOut()
+                        .println(
+                                ToolErrorReporter.getMessage(
+                                        "msg.shell.usage", Main.class.getName()));
                 exitCode = 1;
                 return null;
             }
@@ -404,21 +398,18 @@
             break goodUsage;
         }
         // print error and usage message
-        global.getOut().println(
-            ToolErrorReporter.getMessage("msg.shell.invalid", usageError));
-        global.getOut().println(
-            ToolErrorReporter.getMessage("msg.shell.usage", Main.class.getName()));
+        global.getOut().println(ToolErrorReporter.getMessage("msg.shell.invalid", usageError));
+        global.getOut()
+                .println(ToolErrorReporter.getMessage("msg.shell.usage", Main.class.getName()));
         exitCode = 1;
         return null;
     }
 
-    private static void initJavaPolicySecuritySupport()
-    {
+    private static void initJavaPolicySecuritySupport() {
         Throwable exObj;
         try {
-            Class<?> cl = Class.forName
-                ("org.mozilla.javascript.tools.shell.JavaPolicySecurity");
-            securityImpl = (SecurityProxy)cl.newInstance();
+            Class<?> cl = Class.forName("org.mozilla.javascript.tools.shell.JavaPolicySecurity");
+            securityImpl = (SecurityProxy) cl.newInstance();
             SecurityController.initGlobal(securityImpl);
             return;
         } catch (ClassNotFoundException ex) {
@@ -430,22 +421,18 @@
         } catch (LinkageError ex) {
             exObj = ex;
         }
-        throw Kit.initCause(new IllegalStateException(
-            "Can not load security support: "+exObj), exObj);
+        throw new IllegalStateException("Can not load security support: " + exObj, exObj);
     }
 
     /**
      * Evaluate JavaScript source.
      *
      * @param cx the current context
-     * @param filename the name of the file to compile, or null
-     *                 for interactive mode.
+     * @param filename the name of the file to compile, or null for interactive mode.
      * @throws IOException if the source could not be read
      * @throws RhinoException thrown during evaluation of source
      */
-    public static void processSource(Context cx, String filename)
-            throws IOException
-    {
+    public static void processSource(Context cx, String filename) throws IOException {
         if (filename == null || filename.equals("-")) {
             Scriptable scope = getShellScope();
             Charset cs;
@@ -466,18 +453,16 @@
             while (!hitEOF) {
                 String[] prompts = global.getPrompts(cx);
                 String prompt = null;
-                if (filename == null)
-                    prompt = prompts[0];
+                if (filename == null) prompt = prompts[0];
                 console.flush();
-                String source = "";
+                StringBuilder source = new StringBuilder();
 
                 // Collect lines of source to compile.
                 while (true) {
                     String newline;
                     try {
                         newline = console.readLine(prompt);
-                    }
-                    catch (IOException ioe) {
+                    } catch (IOException ioe) {
                         console.println(ioe.toString());
                         break;
                     }
@@ -485,40 +470,38 @@
                         hitEOF = true;
                         break;
                     }
-                    source = source + newline + "\n";
+                    source.append(newline).append('\n');
                     lineno++;
-                    if (cx.stringIsCompilableUnit(source))
-                        break;
+                    if (cx.stringIsCompilableUnit(source.toString())) break;
                     prompt = prompts[1];
                 }
                 try {
-                    Script script = cx.compileString(source, "<stdin>", lineno, null);
+                    String finalSource = source.toString();
+                    Script script = cx.compileString(finalSource, "<stdin>", lineno, null);
                     if (script != null) {
                         Object result = script.exec(cx, scope);
                         // Avoid printing out undefined or function definitions.
-                        if (result != Context.getUndefinedValue() &&
-                                !(result instanceof Function &&
-                                        source.trim().startsWith("function")))
-                        {
+                        if (result != Context.getUndefinedValue()
+                                && !(result instanceof Function
+                                        && finalSource.trim().startsWith("function"))) {
                             try {
                                 console.println(Context.toString(result));
                             } catch (RhinoException rex) {
-                                ToolErrorReporter.reportException(
-                                        cx.getErrorReporter(), rex);
+                                ToolErrorReporter.reportException(cx.getErrorReporter(), rex);
                             }
                         }
                         NativeArray h = global.history;
-                        h.put((int)h.getLength(), h, source);
+                        h.put((int) h.getLength(), h, source);
                     }
+                    printPromiseWarnings(cx);
                 } catch (RhinoException rex) {
-                    ToolErrorReporter.reportException(
-                        cx.getErrorReporter(), rex);
+                    ToolErrorReporter.reportException(cx.getErrorReporter(), rex);
                     exitCode = EXITCODE_RUNTIME_ERROR;
                 } catch (VirtualMachineError ex) {
                     // Treat StackOverflow and OutOfMemory as runtime errors
                     ex.printStackTrace();
-                    String msg = ToolErrorReporter.getMessage(
-                        "msg.uncaughtJSException", ex.toString());
+                    String msg =
+                            ToolErrorReporter.getMessage("msg.uncaughtJSException", ex.toString());
                     Context.reportError(msg);
                     exitCode = EXITCODE_RUNTIME_ERROR;
                 }
@@ -536,26 +519,24 @@
         try {
             processFile(cx, scope, filename);
         } catch (IOException ioex) {
-            Context.reportError(ToolErrorReporter.getMessage(
-                    "msg.couldnt.read.source", filename, ioex.getMessage()));
+            Context.reportError(
+                    ToolErrorReporter.getMessage(
+                            "msg.couldnt.read.source", filename, ioex.getMessage()));
             exitCode = EXITCODE_FILE_NOT_FOUND;
         } catch (RhinoException rex) {
-            ToolErrorReporter.reportException(
-                    cx.getErrorReporter(), rex);
+            ToolErrorReporter.reportException(cx.getErrorReporter(), rex);
             exitCode = EXITCODE_RUNTIME_ERROR;
         } catch (VirtualMachineError ex) {
             // Treat StackOverflow and OutOfMemory as runtime errors
             ex.printStackTrace();
-            String msg = ToolErrorReporter.getMessage(
-                    "msg.uncaughtJSException", ex.toString());
+            String msg = ToolErrorReporter.getMessage("msg.uncaughtJSException", ex.toString());
             Context.reportError(msg);
             exitCode = EXITCODE_RUNTIME_ERROR;
         }
     }
 
     public static void processFile(Context cx, Scriptable scope, String filename)
-            throws IOException
-    {
+            throws IOException {
         if (securityImpl == null) {
             processFileSecure(cx, scope, filename, null);
         } else {
@@ -563,8 +544,7 @@
         }
     }
 
-    static void processFileSecure(Context cx, Scriptable scope,
-                                  String path, Object securityDomain)
+    static void processFileSecure(Context cx, Scriptable scope, String path, Object securityDomain)
             throws IOException {
 
         boolean isClass = path.endsWith(".class");
@@ -577,7 +557,7 @@
 
         if (script == null) {
             if (isClass) {
-                script = loadCompiledScript(cx, path, (byte[])source, securityDomain);
+                script = loadCompiledScript(cx, path, (byte[]) source, securityDomain);
             } else {
                 String strSrc = (String) source;
                 // Support the executable script #! syntax:  If
@@ -607,13 +587,9 @@
 
         if (source != null) {
             if (source instanceof String) {
-                try {
-                    bytes = ((String)source).getBytes("UTF-8");
-                } catch (UnsupportedEncodingException ue) {
-                    bytes = ((String)source).getBytes();
-                }
+                bytes = ((String) source).getBytes(StandardCharsets.UTF_8);
             } else {
-                bytes = (byte[])source;
+                bytes = (byte[]) source;
             }
             try {
                 MessageDigest md = MessageDigest.getInstance("MD5");
@@ -627,10 +603,9 @@
         return digest;
     }
 
-    private static Script loadCompiledScript(Context cx, String path,
-                                             byte[] data, Object securityDomain)
-            throws FileNotFoundException
-    {
+    private static Script loadCompiledScript(
+            Context cx, String path, byte[] data, Object securityDomain)
+            throws FileNotFoundException {
         if (data == null) {
             throw new FileNotFoundException(path);
         }
@@ -650,7 +625,8 @@
         }
         String name = path.substring(nameStart, nameEnd);
         try {
-            GeneratedClassLoader loader = SecurityController.createLoader(cx.getApplicationClassLoader(), securityDomain);
+            GeneratedClassLoader loader =
+                    SecurityController.createLoader(cx.getApplicationClassLoader(), securityDomain);
             Class<?> clazz = loader.defineClass(name, data);
             loader.linkClass(clazz);
             if (!Script.class.isAssignableFrom(clazz)) {
@@ -666,6 +642,25 @@
         }
     }
 
+    private static void printPromiseWarnings(Context cx) {
+        List<Object> unhandled = cx.getUnhandledPromiseTracker().enumerate();
+        if (!unhandled.isEmpty()) {
+            Object result = unhandled.get(0);
+            String msg = "Unhandled rejected promise: " + Context.toString(result);
+            if (result instanceof Scriptable) {
+                Object stack = ScriptableObject.getProperty((Scriptable) result, "stack");
+                if (stack != null && stack != Scriptable.NOT_FOUND) {
+                    msg += '\n' + Context.toString(stack);
+                }
+            }
+            System.out.println(msg);
+            if (unhandled.size() > 1) {
+                System.out.println(
+                        "  and " + (unhandled.size() - 1) + " other unhandled rejected promises");
+            }
+        }
+    }
+
     public static InputStream getIn() {
         return getGlobal().getIn();
     }
@@ -691,23 +686,21 @@
     }
 
     /**
-     * Read file or url specified by <tt>path</tt>.
-     * @return file or url content as <tt>byte[]</tt> or as <tt>String</tt> if
-     * <tt>convertToString</tt> is true.
+     * Read file or url specified by <code>path</code>.
+     *
+     * @return file or url content as <code>byte[]</code> or as <code>String</code> if <code>
+     *     convertToString</code> is true.
      */
-    private static Object readFileOrUrl(String path, boolean convertToString)
-            throws IOException
-    {
-        return SourceReader.readFileOrUrl(path, convertToString,
-                shellContextFactory.getCharacterEncoding());
+    private static Object readFileOrUrl(String path, boolean convertToString) throws IOException {
+        return SourceReader.readFileOrUrl(
+                path, convertToString, shellContextFactory.getCharacterEncoding());
     }
 
     static class ScriptReference extends SoftReference<Script> {
         String path;
         byte[] digest;
 
-        ScriptReference(String path, byte[] digest,
-                        Script script, ReferenceQueue<Script> queue) {
+        ScriptReference(String path, byte[] digest, Script script, ReferenceQueue<Script> queue) {
             super(script, queue);
             this.path = path;
             this.digest = digest;
@@ -715,6 +708,7 @@
     }
 
     static class ScriptCache extends LinkedHashMap<String, ScriptReference> {
+        private static final long serialVersionUID = -6866856136258508615L;
         ReferenceQueue<Script> queue;
         int capacity;
 
@@ -731,7 +725,7 @@
 
         ScriptReference get(String path, byte[] digest) {
             ScriptReference ref;
-            while((ref = (ScriptReference) queue.poll()) != null) {
+            while ((ref = (ScriptReference) queue.poll()) != null) {
                 remove(ref.path);
             }
             ref = get(path);
@@ -745,6 +739,5 @@
         void put(String path, byte[] digest, Script script) {
             put(path, new ScriptReference(path, digest, script, queue));
         }
-
     }
 }
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/shell/SecurityProxy.java rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/SecurityProxy.java
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/shell/SecurityProxy.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/SecurityProxy.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,7 +6,9 @@
 
 package org.mozilla.javascript.tools.shell;
 
-import org.mozilla.javascript.*;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.SecurityController;
 
 public abstract class SecurityProxy extends SecurityController
 {
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/shell/ShellConsole.java rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/ShellConsole.java
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/shell/ShellConsole.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/ShellConsole.java	2022-01-06 22:57:21.000000000 +0100
@@ -257,10 +257,9 @@
             if (line != null) {
                 buffer = line.getBytes(cs);
                 return buffer.length;
-            } else {
-                buffer = EMPTY;
-                return -1;
             }
+            buffer = EMPTY;
+            return -1;
         }
     }
 
@@ -326,8 +325,8 @@
 
     /**
      * Provides a specialized {@link ShellConsole} to handle line editing,
-     * history and completion. Relies on the JLine library (see
-     * <http://jline.sourceforge.net>).
+     * history and completion. Relies on the JLine library
+     * (see <a href="http://jline.sourceforge.net">http://jline.sourceforge.net</a>).
      */
     public static ShellConsole getConsole(Scriptable scope, Charset cs) {
         // We don't want a compile-time dependency on the JLine jar, so use
@@ -426,6 +425,7 @@
     }
 
     @SuppressWarnings({"unchecked"})
+    @Override
     public Object invoke(Object proxy, Method method, Object[] args) {
         if (method.equals(this.completeMethod)) {
             int result = complete((String)args[0], ((Integer) args[1]).intValue(),
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/shell/ShellContextFactory.java rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/ShellContextFactory.java
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/shell/ShellContextFactory.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/ShellContextFactory.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,7 +6,9 @@
 
 package org.mozilla.javascript.tools.shell;
 
-import org.mozilla.javascript.*;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.ContextFactory;
+import org.mozilla.javascript.ErrorReporter;
 
 public class ShellContextFactory extends ContextFactory
 {
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/shell/ShellLine.java rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/ShellLine.java
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/shell/ShellLine.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/ShellLine.java	2022-01-06 22:57:21.000000000 +0100
@@ -13,8 +13,8 @@
 
 /**
  * Provides a specialized input stream for consoles to handle line
- * editing, history and completion. Relies on the JLine library (see
- * <http://jline.sourceforge.net>).
+ * editing, history and completion. Relies on the JLine library
+ * (see <a href="http://jline.sourceforge.net">http://jline.sourceforge.net</a>).
  */
 @Deprecated
 public class ShellLine {
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/shell/Timers.java rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/Timers.java
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/shell/Timers.java	1970-01-01 01:00:00.000000000 +0100
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/Timers.java	2022-01-06 22:57:21.000000000 +0100
@@ -0,0 +1,160 @@
+package org.mozilla.javascript.tools.shell;
+
+import java.util.HashMap;
+import java.util.PriorityQueue;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Function;
+import org.mozilla.javascript.LambdaFunction;
+import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+import org.mozilla.javascript.Undefined;
+
+/**
+ * This class supports the "setTimeout" and "clearTimeout" methods of semi-standard JavaScript. It
+ * does it within a single thread by keeping track of a queue of timeout objects, and then it blocks
+ * the thread. It's used solely within the Shell right now.
+ */
+public class Timers {
+    private int lastId = 0;
+    private final HashMap<Integer, Timeout> timers = new HashMap<>();
+    private final PriorityQueue<Timeout> timerQueue = new PriorityQueue<>();
+
+    /**
+     * Initialize the "setTimeout" and "clearTimeout" functions on the specified scope.
+     *
+     * @param scope the scope where the functions should be defined
+     */
+    public void install(Scriptable scope) {
+        LambdaFunction setTimeout =
+                new LambdaFunction(
+                        scope,
+                        "setTimeout",
+                        1,
+                        (Context lcx, Scriptable lscope, Scriptable thisObj, Object[] args) ->
+                                setTimeout(args));
+        ScriptableObject.defineProperty(scope, "setTimeout", setTimeout, ScriptableObject.DONTENUM);
+        LambdaFunction clearTimeout =
+                new LambdaFunction(
+                        scope,
+                        "clearTimeout",
+                        1,
+                        (Context lcx, Scriptable lscope, Scriptable thisObj, Object[] args) ->
+                                clearTimeout(args));
+        ScriptableObject.defineProperty(
+                scope, "clearTimeout", clearTimeout, ScriptableObject.DONTENUM);
+    }
+
+    /**
+     * Execute all pending timers and microtasks, blocking the thread if we need to wait for any
+     * timers to time out.
+     *
+     * @param cx The Context to use to execute microtasks and timer functions
+     * @param scope the global scope
+     * @throws InterruptedException if the thread is interrupted while sleeping
+     */
+    public void runAllTimers(Context cx, Scriptable scope) throws InterruptedException {
+        boolean executed;
+        do {
+            cx.processMicrotasks();
+            executed = executeNext(cx, scope);
+        } while (executed);
+        cx.processMicrotasks();
+    }
+
+    /**
+     * Put up to one task on the context's "microtask queue." If the next task is not ready to run
+     * for some time, then block the calling thread until the time is up.
+     *
+     * @param cx the context
+     * @param scope the current scope
+     * @return true if something was placed on the queue, and false if the queue is empty
+     * @throws InterruptedException if the thread was interrupted
+     */
+    private boolean executeNext(Context cx, Scriptable scope) throws InterruptedException {
+        Timeout t = timerQueue.peek();
+        if (t == null) {
+            return false;
+        }
+        long remaining = t.expiration - System.currentTimeMillis();
+        if (remaining > 0) {
+            Thread.sleep(remaining);
+        }
+        timerQueue.remove();
+        timers.remove(t.id);
+        cx.enqueueMicrotask(() -> t.func.call(cx, scope, scope, t.funcArgs));
+        return true;
+    }
+
+    private Object setTimeout(Object[] args) {
+        if (args.length == 0) {
+            throw ScriptRuntime.typeError("Expected function parameter");
+        }
+        if (!(args[0] instanceof Function)) {
+            throw ScriptRuntime.typeError("Expected first argument to be a function");
+        }
+
+        int id = ++lastId;
+        Timeout t = new Timeout();
+        t.id = id;
+        t.func = (Function) args[0];
+        int delay = 0;
+        if (args.length > 1) {
+            delay = ScriptRuntime.toInt32(args[1]);
+        }
+        t.expiration = System.currentTimeMillis() + delay;
+        if (args.length > 2) {
+            t.funcArgs = new Object[args.length - 2];
+            System.arraycopy(args, 2, t.funcArgs, 0, t.funcArgs.length);
+        }
+
+        timers.put(id, t);
+        timerQueue.add(t);
+        return id;
+    }
+
+    private Object clearTimeout(Object[] args) {
+        if (args.length == 0) {
+            throw ScriptRuntime.typeError("Expected function parameter");
+        }
+        int id = ScriptRuntime.toInt32(args[0]);
+        Timeout t = timers.remove(id);
+        if (t != null) {
+            timerQueue.remove(t);
+        }
+        return Undefined.instance;
+    }
+
+    /**
+     * An object to go on the priority queue.
+     *
+     * <p>Note: this class has a natural ordering that is inconsistent with equals.
+     */
+    private static final class Timeout implements Comparable<Timeout> {
+        int id;
+        Function func;
+        Object[] funcArgs = ScriptRuntime.emptyArgs;
+        long expiration;
+
+        @Override
+        public int compareTo(Timeout o) {
+            return Long.compare(expiration, o.expiration);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            try {
+                return expiration == ((Timeout) obj).expiration;
+            } catch (ClassCastException cce) {
+                return false;
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            // This private class should never go in a HashMap.
+            assert false;
+            return (int) expiration;
+        }
+    }
+}
diff -Nru rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/ToolErrorReporter.java rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/ToolErrorReporter.java
--- rhino-1.7.7.2/toolsrc/org/mozilla/javascript/tools/ToolErrorReporter.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/ToolErrorReporter.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,11 +6,19 @@
 
 package org.mozilla.javascript.tools;
 
-import org.mozilla.javascript.*;
-
+import java.io.PrintStream;
 import java.text.MessageFormat;
-import java.io.*;
-import java.util.*;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.EcmaError;
+import org.mozilla.javascript.ErrorReporter;
+import org.mozilla.javascript.EvaluatorException;
+import org.mozilla.javascript.JavaScriptException;
+import org.mozilla.javascript.RhinoException;
+import org.mozilla.javascript.SecurityUtilities;
+import org.mozilla.javascript.WrappedException;
 
 /**
  * Error reporter for tools.
diff -Nru rhino-1.7.7.2/xmlimplsrc/build.xml rhino-1.7.14/xmlimplsrc/build.xml
--- rhino-1.7.7.2/xmlimplsrc/build.xml	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/xmlimplsrc/build.xml	1970-01-01 01:00:00.000000000 +0100
@@ -1,69 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<project name="xmlimplsrc" basedir=".." default="compile">
-  <!--
-    Properties which affect this build file:
-
-    no-e4x: Will cause E4X not to be built
-  -->
-
-  <property file="build.local.properties"/>
-  <property file="build.properties"/>
-
-  <!--
-    Provide support for the old name for skipping E4X compilation, in case someone is still using it
-  -->
-  <condition property="no-e4x">
-    <isset property="without-xmlimpl" />
-  </condition>
-
-  <target name="compile" unless="no-e4x">
-    <antcall target="e4x-compile" />
-    <antcall target="no-e4x-compile" />
-  </target>
-
-  <available property="jdk1.5?" classname="java.lang.ProcessBuilder" />
-
-  <target name="e4x-compile" if="jdk1.5?">
-    <javac
-      srcdir="xmlimplsrc"
-      destdir="${classes}"
-      deprecation="on"
-      debug="${debug}"
-      includeAntRuntime="false"
-      target="${target-jvm}"
-      source="${source-level}"
-    />
-  </target>
-
-  <target name="no-e4x-compile" unless="jdk1.5?">
-    <echo>
-      Skipping DOM E4X implementation; JDK 1.5+ currently required for compilation.
-      <!--
-        If the compiler is outfitted with DOM3 using the endorsed standards
-        override mechanism, presumably the code could be built under 1.4.
-        Not tested.
-      -->
-    </echo>
-  </target>
-
-  <target name="copy-source">
-    <mkdir dir="${dist.dir}/xmlimplsrc"/>
-    <copy todir="${dist.dir}/xmlimplsrc">
-      <fileset dir="xmlimplsrc"
-               includes="**/*.java,**/*.properties,**/*.xml"
-      />
-    </copy>
-  </target>
-
-  <target name="clean">
-    <delete includeEmptyDirs="true">
-      <fileset dir="${classes}"
-               includes="org/mozilla/javascript/xmlimpl/**"
-      />
-    </delete>
-  </target>
-</project>
diff -Nru rhino-1.7.7.2/xmlimplsrc/org/mozilla/javascript/xmlimpl/Namespace.java rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/Namespace.java
--- rhino-1.7.7.2/xmlimplsrc/org/mozilla/javascript/xmlimpl/Namespace.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/Namespace.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,7 +6,12 @@
 
 package org.mozilla.javascript.xmlimpl;
 
-import org.mozilla.javascript.*;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.IdFunctionObject;
+import org.mozilla.javascript.IdScriptableObject;
+import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.Undefined;
 
 /**
  * Class Namespace
@@ -219,9 +224,7 @@
     }
 
     private Namespace realThis(Scriptable thisObj, IdFunctionObject f) {
-        if(!(thisObj instanceof Namespace))
-            throw incompatibleCallError(f);
-        return (Namespace)thisObj;
+        return ensureType(thisObj, Namespace.class, f);
     }
 
     Namespace newNamespace(String uri) {
diff -Nru rhino-1.7.7.2/xmlimplsrc/org/mozilla/javascript/xmlimpl/QName.java rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/QName.java
--- rhino-1.7.7.2/xmlimplsrc/org/mozilla/javascript/xmlimpl/QName.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/QName.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,7 +6,12 @@
 
 package org.mozilla.javascript.xmlimpl;
 
-import org.mozilla.javascript.*;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.IdFunctionObject;
+import org.mozilla.javascript.IdScriptableObject;
+import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.Undefined;
 
 /**
  * Class QName
@@ -247,9 +252,7 @@
 
     private QName realThis(Scriptable thisObj, IdFunctionObject f)
     {
-        if(!(thisObj instanceof QName))
-            throw incompatibleCallError(f);
-        return (QName)thisObj;
+        return ensureType(thisObj, QName.class, f);
     }
 
     QName newQName(XMLLibImpl lib, String q_uri, String q_localName, String q_prefix) {
@@ -262,8 +265,6 @@
             ns = XmlNode.Namespace.create(q_prefix, q_uri);
         } else if (q_uri != null) {
             ns = XmlNode.Namespace.create(q_uri);
-        } else {
-            ns = null;
         }
         if (q_localName != null && q_localName.equals("*")) q_localName = null;
         return create(lib, this.getParentScope(), prototype, XmlNode.QName.create(ns, q_localName));
@@ -352,7 +353,7 @@
             if (!"*".equals(localName)) {
                 sb.append("null, ");
             }
-        } else {
+        } else if (uri != null) {
             Namespace.toSourceImpl(prefix, uri, sb);
             sb.append(", ");
         }
diff -Nru rhino-1.7.7.2/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLCtor.java rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLCtor.java
--- rhino-1.7.7.2/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLCtor.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLCtor.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,7 +6,12 @@
 
 package org.mozilla.javascript.xmlimpl;
 
-import org.mozilla.javascript.*;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.IdFunctionObject;
+import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+import org.mozilla.javascript.Undefined;
 
 class XMLCtor extends IdFunctionObject
 {
diff -Nru rhino-1.7.7.2/xmlimplsrc/org/mozilla/javascript/xmlimpl/XML.java rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XML.java
--- rhino-1.7.7.2/xmlimplsrc/org/mozilla/javascript/xmlimpl/XML.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XML.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,7 +6,10 @@
 
 package org.mozilla.javascript.xmlimpl;
 
-import org.mozilla.javascript.*;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.Undefined;
 import org.mozilla.javascript.xml.XMLObject;
 
 class XML extends XMLObjectImpl {
@@ -192,6 +195,7 @@
     }
 
     //    See ECMA 357, 11_2_2_1, Semantics, 3_f.
+    @SuppressWarnings("deprecation")
     @Override
     public Scriptable getExtraMethodSource(Context cx) {
         if (hasSimpleContent()) {
diff -Nru rhino-1.7.7.2/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLLibImpl.java rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLLibImpl.java
--- rhino-1.7.7.2/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLLibImpl.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLLibImpl.java	2022-01-06 22:57:21.000000000 +0100
@@ -8,20 +8,29 @@
 
 import java.io.Serializable;
 
-import org.mozilla.javascript.*;
-import org.mozilla.javascript.xml.*;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Kit;
+import org.mozilla.javascript.Node;
+import org.mozilla.javascript.Ref;
+import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.Undefined;
+import org.mozilla.javascript.Wrapper;
+import org.mozilla.javascript.xml.XMLLib;
+import org.mozilla.javascript.xml.XMLObject;
 import org.xml.sax.SAXException;
 
 public final class XMLLibImpl extends XMLLib implements Serializable {
-    private static final long serialVersionUID = 1L;
 
     //
     //    EXPERIMENTAL Java interface
     //
+   
+    private static final long serialVersionUID = 1L;
 
     /**
-        This experimental interface is undocumented.
-     */
+      This experimental interface is undocumented.
+    */
     public static org.w3c.dom.Node toDomNode(Object xmlObject) {
         //    Could return DocumentFragment for XMLList
         //    Probably a single node for XMLList with one element
diff -Nru rhino-1.7.7.2/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLList.java rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLList.java
--- rhino-1.7.7.2/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLList.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLList.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,10 +6,17 @@
 
 package org.mozilla.javascript.xmlimpl;
 
-import org.mozilla.javascript.*;
-import org.mozilla.javascript.xml.*;
 import java.util.ArrayList;
 
+import org.mozilla.javascript.Callable;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Function;
+import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+import org.mozilla.javascript.Undefined;
+import org.mozilla.javascript.xml.XMLObject;
+
 class XMLList extends XMLObjectImpl implements Function {
     static final long serialVersionUID = -4543618751670781135L;
 
@@ -713,7 +720,7 @@
         String methodName = isApply ? "apply" : "call";
         if(!(thisObj instanceof XMLList) ||
             ((XMLList)thisObj).targetProperty == null)
-            throw ScriptRuntime.typeError1("msg.isnt.function",
+            throw ScriptRuntime.typeErrorById("msg.isnt.function",
                 methodName);
 
         return ScriptRuntime.applyOrCall(isApply, cx, scope, thisObj, args);
@@ -760,7 +767,7 @@
             return applyOrCall(isApply, cx, scope, thisObj, args);
 
         if (!(thisObj instanceof XMLObject)) {
-            throw ScriptRuntime.typeError1("msg.incompat.call", methodName);
+            throw ScriptRuntime.typeErrorById("msg.incompat.call", methodName);
         }
         Object func = null;
         Scriptable sobj = thisObj;
@@ -787,7 +794,7 @@
     }
 
     public Scriptable construct(Context cx, Scriptable scope, Object[] args) {
-        throw ScriptRuntime.typeError1("msg.not.ctor", "XMLList");
+        throw ScriptRuntime.typeErrorById("msg.not.ctor", "XMLList");
     }
 }
 
diff -Nru rhino-1.7.7.2/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLName.java rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLName.java
--- rhino-1.7.7.2/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLName.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLName.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,7 +6,12 @@
 
 package org.mozilla.javascript.xmlimpl;
 
-import org.mozilla.javascript.*;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.EcmaError;
+import org.mozilla.javascript.Kit;
+import org.mozilla.javascript.Ref;
+import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.Undefined;
 
 class XMLName extends Ref {
     static final long serialVersionUID = 3832176310755686977L;
@@ -329,6 +334,7 @@
         return xmlObject.getXMLProperty(this);
     }
 
+    @SuppressWarnings("deprecation")
     @Override
     public Object set(Context cx, Object value) {
         if (xmlObject == null) {
diff -Nru rhino-1.7.7.2/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlNode.java rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlNode.java
--- rhino-1.7.7.2/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlNode.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlNode.java	2022-01-06 22:57:21.000000000 +0100
@@ -21,12 +21,14 @@
 import org.w3c.dom.UserDataHandler;
 
 class XmlNode implements Serializable {
+    private static final long serialVersionUID = 1L;
+    
     private static final String XML_NAMESPACES_NAMESPACE_URI = "http://www.w3.org/2000/xmlns/";
-
+    
     private static final String USER_DATA_XMLNODE_KEY = XmlNode.class.getName();
-
+    
     private static final boolean DOM_LEVEL_3 = true;
-
+    
     private static XmlNode getUserData(Node node) {
         if (DOM_LEVEL_3) {
             return (XmlNode)node.getUserData(USER_DATA_XMLNODE_KEY);
@@ -95,8 +97,6 @@
         return createImpl( other.dom.cloneNode(true) );
     }
 
-    private static final long serialVersionUID = 1L;
-
     private UserDataHandler events = new XmlNodeUserDataHandler();
 
     private Node dom;
@@ -342,9 +342,8 @@
 
         Namespace[] getNamespaces() {
             ArrayList<Namespace> rv = new ArrayList<Namespace>();
-            for (String prefix: map.keySet()) {
-                String uri = map.get(prefix);
-                Namespace n = Namespace.create(prefix, uri);
+            for (Map.Entry<String, String> e : map.entrySet()) {
+                Namespace n = Namespace.create(e.getKey(), e.getValue());
                 if (!n.isEmpty()) {
                     rv.add(n);
                 }
@@ -540,10 +539,7 @@
     }
 
     void replaceWith(XmlNode other) {
-        Node replacement = other.dom;
-        if (replacement.getOwnerDocument() != this.dom.getOwnerDocument()) {
-            replacement = this.dom.getOwnerDocument().importNode(replacement, true);
-        }
+        Node replacement = this.dom.getOwnerDocument().importNode(other.dom, true);
         this.dom.getParentNode().replaceChild(replacement, this.dom);
     }
 
@@ -745,13 +741,7 @@
                 if (node != null) {
                     lookupPrefix(node);
                 } else {
-                    if (namespace.getUri().equals("")) {
-                        namespace.setPrefix("");
-                    } else {
-                        //    TODO    I am not sure this is right, but if we are creating a standalone node, I think we can set the
-                        //            default namespace on the node itself and not worry about setting a prefix for that namespace.
-                        namespace.setPrefix("");
-                    }
+                    namespace.setPrefix("");
                 }
             }
             return qualify(namespace.getPrefix(), localName);
diff -Nru rhino-1.7.7.2/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java
--- rhino-1.7.7.2/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,8 +6,16 @@
 
 package org.mozilla.javascript.xmlimpl;
 
-import org.mozilla.javascript.*;
-import org.mozilla.javascript.xml.*;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.IdFunctionObject;
+import org.mozilla.javascript.Kit;
+import org.mozilla.javascript.NativeWith;
+import org.mozilla.javascript.Node;
+import org.mozilla.javascript.Ref;
+import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.Undefined;
+import org.mozilla.javascript.xml.XMLObject;
 
 /**
  *  This abstract class describes what all XML objects (XML, XMLList) should
@@ -16,6 +24,7 @@
  * @see XML
  */
 abstract class XMLObjectImpl extends XMLObject {
+    private static final long serialVersionUID = -2553684605738101761L;
     private static final Object XMLOBJECT_TAG = "XMLObject";
     private XMLLibImpl lib;
     private boolean prototypeFlag;
@@ -611,9 +620,7 @@
         }
 
         // All (XML|XMLList).prototype methods require thisObj to be XML
-        if (!(thisObj instanceof XMLObjectImpl))
-            throw incompatibleCallError(f);
-        XMLObjectImpl realThis = (XMLObjectImpl)thisObj;
+        XMLObjectImpl realThis = ensureType(thisObj, XMLObjectImpl.class, f);
 
         XML xml = realThis.getXML();
         switch (id) {
diff -Nru rhino-1.7.7.2/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlProcessor.java rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlProcessor.java
--- rhino-1.7.7.2/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlProcessor.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlProcessor.java	2022-01-06 22:57:21.000000000 +0100
@@ -4,22 +4,27 @@
 
 package org.mozilla.javascript.xmlimpl;
 
-import org.w3c.dom.*;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.ParserConfigurationException;
-
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.Serializable;
-import java.util.List;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.LinkedBlockingDeque;
-
-import org.mozilla.javascript.*;
-
-//    Disambiguate from org.mozilla.javascript.Node
-import org.w3c.dom.Node;
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.TransformerConfigurationException;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.ScriptRuntime;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node; // Disambiguate from org.mozilla.javascript.Node
+import org.w3c.dom.NodeList;
+import org.w3c.dom.ProcessingInstruction;
+import org.w3c.dom.Text;
 import org.xml.sax.ErrorHandler;
 import org.xml.sax.SAXParseException;
 
@@ -40,21 +45,92 @@
 
     private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
         stream.defaultReadObject();
-        this.dom = javax.xml.parsers.DocumentBuilderFactory.newInstance();
+        this.dom = DocumentBuilderFactory.newInstance();
         this.dom.setNamespaceAware(true);
         this.dom.setIgnoringComments(false);
+        // create TF and set settings to secure it from XSLT attacks if given a malicious node in
+        // toXMLString
         this.xform = javax.xml.transform.TransformerFactory.newInstance();
+        Context ctx = Context.getCurrentContext();
+        if (ctx == null || ctx.hasFeature(Context.FEATURE_ENABLE_XML_SECURE_PARSING)) {
+            configureSecureDBF(this.dom);
+            configureSecureTF(this.xform);
+        }
         int poolSize = Runtime.getRuntime().availableProcessors() * 2;
         this.documentBuilderPool = new LinkedBlockingDeque<DocumentBuilder>(poolSize);
     }
 
+    /*
+     * Secure implementation of a DocumentBuilderFactory to prevent XXE and SSRF attacks
+     */
+    private void configureSecureDBF(DocumentBuilderFactory dbf) {
+        try {
+            // This feature is required to be supported by all DocumentBuilderFactories.
+            dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+            // Disallow XIncludeAware as it is an SSRF target using xi:include.
+            // This should also be supported on all XML processors.
+            dbf.setXIncludeAware(false);
+        } catch (ParserConfigurationException e) {
+            throw new RuntimeException(
+                    "XML parser (DocumentBuilderFactory) cannot be securely configured.", e);
+        }
+
+        // The rest of these features should be set for the best security by default.
+        // However, not all XML processing implementations support them.
+        // So we will attempt to set each one but continue if we can't.
+
+        try {
+            // Prevent File attacks in DBF
+            // Disallow all doctypes, removing all ENTITY-type tags as a vector
+            dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+        } catch (ParserConfigurationException e) {
+            // Ignore this, because it will not work on all implementations
+        }
+
+        try {
+            // Prevent SSRF attacks in DBF
+            // Do not load external dtds, if the underlying DocBuilderFactory is set for a
+            // validation mode
+            dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+        } catch (ParserConfigurationException e) {
+            // Ignore this, because it will not work on all implementations
+        }
+    }
+
+    /*
+     * Secure implementation of a TransformerFactory to prevent XXE and SSRF attacks
+     */
+    private void configureSecureTF(javax.xml.transform.TransformerFactory xform) {
+        try {
+            // Disallow all XXEs and SSRF via external calls for DTDs or Stylesheets.
+            // This feature is required to be supported by all TransformerFactory implementations.
+            xform.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+        } catch (TransformerConfigurationException e) {
+            throw new RuntimeException(
+                    "XML parser (TransformerFactory) cannot be securely configured.", e);
+        }
+
+        // These next parameters make extra-sure that we have a secure configuration,
+        // but are not supported on all implementations.
+        try {
+            xform.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
+        } catch (IllegalArgumentException e) {
+            // Ignore this, because it will not work on all implementations
+        }
+
+        try {
+            xform.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
+        } catch (IllegalArgumentException e) {
+            // Ignore this, because it will not work on all implementations
+        }
+    }
+
     private static class RhinoSAXErrorHandler implements ErrorHandler, Serializable {
 
         private static final long serialVersionUID = 6918417235413084055L;
 
         private void throwError(SAXParseException e) {
-            throw ScriptRuntime.constructError("TypeError", e.getMessage(),
-                e.getLineNumber() - 1);
+            throw ScriptRuntime.constructError("TypeError", e.getMessage(), e.getLineNumber() - 1);
         }
 
         public void error(SAXParseException e) {
@@ -72,10 +148,17 @@
 
     XmlProcessor() {
         setDefault();
-        this.dom = javax.xml.parsers.DocumentBuilderFactory.newInstance();
+        this.dom = DocumentBuilderFactory.newInstance();
         this.dom.setNamespaceAware(true);
         this.dom.setIgnoringComments(false);
+        // create TF and set settings to secure it from XSLT attacks if given a malicious node in
+        // toXMLString
         this.xform = javax.xml.transform.TransformerFactory.newInstance();
+        Context ctx = Context.getCurrentContext();
+        if (ctx == null || ctx.hasFeature(Context.FEATURE_ENABLE_XML_SECURE_PARSING)) {
+            configureSecureDBF(this.dom);
+            configureSecureTF(this.xform);
+        }
         int poolSize = Runtime.getRuntime().availableProcessors() * 2;
         this.documentBuilderPool = new LinkedBlockingDeque<DocumentBuilder>(poolSize);
     }
@@ -129,17 +212,18 @@
     }
 
     private String toXmlNewlines(String rv) {
-        StringBuilder nl = new StringBuilder();
-        for (int i=0; i<rv.length(); i++) {
-            if (rv.charAt(i) == '\r') {
-                if (rv.charAt(i+1) == '\n') {
+        StringBuilder nl = new StringBuilder(rv.length());
+        for (int i = 0; i < rv.length(); i++) {
+            char ch = rv.charAt(i);
+            if (ch == '\r') {
+                if (rv.charAt(i + 1) == '\n') {
                     //    DOS, do nothing and skip the \r
                 } else {
                     //    Macintosh, substitute \n
                     nl.append('\n');
                 }
             } else {
-                nl.append(rv.charAt(i));
+                nl.append(ch);
             }
         }
         return nl.toString();
@@ -150,10 +234,9 @@
     }
 
     // Get from pool, or create one without locking, if needed.
-    private DocumentBuilder getDocumentBuilderFromPool()
-            throws ParserConfigurationException {
+    private DocumentBuilder getDocumentBuilderFromPool() throws ParserConfigurationException {
         DocumentBuilder builder = documentBuilderPool.pollFirst();
-        if (builder == null){
+        if (builder == null) {
             builder = getDomFactory().newDocumentBuilder();
         }
         builder.setErrorHandler(errorHandler);
@@ -165,6 +248,11 @@
     private void returnDocumentBuilderToPool(DocumentBuilder db) {
         try {
             db.reset();
+            // DocumentBuilders are supposed to be namespace-aware.
+            // This is a sanity check for DocumentBuilder's resettability (a known bug in Android).
+            if (!db.isNamespaceAware()) {
+                return;
+            }
             documentBuilderPool.offerFirst(db);
         } catch (UnsupportedOperationException e) {
             // document builders that don't support reset() can't be pooled
@@ -176,7 +264,7 @@
             list.add(node);
         }
         if (node.getChildNodes() != null) {
-            for (int i=0; i<node.getChildNodes().getLength(); i++) {
+            for (int i = 0; i < node.getChildNodes().getLength(); i++) {
                 addProcessingInstructionsTo(list, node.getChildNodes().item(i));
             }
         }
@@ -187,7 +275,7 @@
             list.add(node);
         }
         if (node.getChildNodes() != null) {
-            for (int i=0; i<node.getChildNodes().getLength(); i++) {
+            for (int i = 0; i < node.getChildNodes().getLength(); i++) {
                 addProcessingInstructionsTo(list, node.getChildNodes().item(i));
             }
         }
@@ -195,7 +283,7 @@
 
     private void addTextNodesToRemoveAndTrim(List<Node> toRemove, Node node) {
         if (node instanceof Text) {
-            Text text = (Text)node;
+            Text text = (Text) node;
             boolean BUG_369394_IS_VALID = false;
             if (!BUG_369394_IS_VALID) {
                 text.setData(text.getData().trim());
@@ -209,7 +297,7 @@
             }
         }
         if (node.getChildNodes() != null) {
-            for (int i=0; i<node.getChildNodes().getLength(); i++) {
+            for (int i = 0; i < node.getChildNodes().getLength(); i++) {
                 addTextNodesToRemoveAndTrim(toRemove, node.getChildNodes().item(i));
             }
         }
@@ -219,39 +307,44 @@
         //    See ECMA357 10.3.1
         DocumentBuilder builder = null;
         try {
-            String syntheticXml = "<parent xmlns=\"" + defaultNamespaceUri +
-                "\">" + xml + "</parent>";
+            String syntheticXml =
+                    "<parent xmlns=\"" + defaultNamespaceUri + "\">" + xml + "</parent>";
             builder = getDocumentBuilderFromPool();
-            Document document = builder.parse( new org.xml.sax.InputSource(new java.io.StringReader(syntheticXml)) );
+            Document document =
+                    builder.parse(
+                            new org.xml.sax.InputSource(new java.io.StringReader(syntheticXml)));
             if (ignoreProcessingInstructions) {
                 List<Node> list = new java.util.ArrayList<Node>();
                 addProcessingInstructionsTo(list, document);
-                for (Node node: list) {
+                for (Node node : list) {
                     node.getParentNode().removeChild(node);
                 }
             }
             if (ignoreComments) {
                 List<Node> list = new java.util.ArrayList<Node>();
                 addCommentsTo(list, document);
-                for (Node node: list) {
+                for (Node node : list) {
                     node.getParentNode().removeChild(node);
                 }
             }
             if (ignoreWhitespace) {
-                //    Apparently JAXP setIgnoringElementContentWhitespace() has a different meaning, it appears from the Javadoc
-                //    Refers to element-only content models, which means we would need to have a validating parser and DTD or schema
+                //    Apparently JAXP setIgnoringElementContentWhitespace() has a different meaning,
+                // it appears from the Javadoc
+                //    Refers to element-only content models, which means we would need to have a
+                // validating parser and DTD or schema
                 //    so that it would know which whitespace to ignore.
 
                 //    Instead we will try to delete it ourselves.
                 List<Node> list = new java.util.ArrayList<Node>();
                 addTextNodesToRemoveAndTrim(list, document);
-                for (Node node: list) {
+                for (Node node : list) {
                     node.getParentNode().removeChild(node);
                 }
             }
             NodeList rv = document.getDocumentElement().getChildNodes();
             if (rv.getLength() > 1) {
-                throw ScriptRuntime.constructError("SyntaxError", "XML objects may contain at most one node.");
+                throw ScriptRuntime.constructError(
+                        "SyntaxError", "XML objects may contain at most one node.");
             } else if (rv.getLength() == 0) {
                 Node node = document.createTextNode("");
                 return node;
@@ -265,8 +358,7 @@
         } catch (javax.xml.parsers.ParserConfigurationException e) {
             throw new RuntimeException(e);
         } finally {
-            if (builder != null)
-                returnDocumentBuilderToPool(builder);
+            if (builder != null) returnDocumentBuilderToPool(builder);
         }
     }
 
@@ -280,8 +372,7 @@
             //    TODO    How to handle these runtime errors?
             throw new RuntimeException(ex);
         } finally {
-            if (builder != null)
-                returnDocumentBuilderToPool(builder);
+            if (builder != null) returnDocumentBuilderToPool(builder);
         }
     }
 
@@ -289,10 +380,12 @@
     private String toString(Node node) {
         javax.xml.transform.dom.DOMSource source = new javax.xml.transform.dom.DOMSource(node);
         java.io.StringWriter writer = new java.io.StringWriter();
-        javax.xml.transform.stream.StreamResult result = new javax.xml.transform.stream.StreamResult(writer);
+        javax.xml.transform.stream.StreamResult result =
+                new javax.xml.transform.stream.StreamResult(writer);
         try {
             javax.xml.transform.Transformer transformer = xform.newTransformer();
-            transformer.setOutputProperty(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes");
+            transformer.setOutputProperty(
+                    javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes");
             transformer.setOutputProperty(javax.xml.transform.OutputKeys.INDENT, "no");
             transformer.setOutputProperty(javax.xml.transform.OutputKeys.METHOD, "xml");
             transformer.transform(source, result);
@@ -317,12 +410,12 @@
         String elementText = toString(e);
         int begin = elementText.indexOf('"');
         int end = elementText.lastIndexOf('"');
-        return elementText.substring(begin+1,end);
+        return elementText.substring(begin + 1, end);
     }
 
     String escapeTextValue(Object value) {
         if (value instanceof XMLObjectImpl) {
-            return ((XMLObjectImpl)value).toXMLString();
+            return ((XMLObjectImpl) value).toXMLString();
         }
 
         String text = ScriptRuntime.toString(value);
@@ -346,7 +439,7 @@
 
     private String elementToXmlString(Element element) {
         //    TODO    My goodness ECMA is complicated (see 10.2.1).  We'll try this first.
-        Element copy = (Element)element.cloneNode(true);
+        Element copy = (Element) element.cloneNode(true);
         if (prettyPrint) {
             beautifyElement(copy, 0);
         }
@@ -358,52 +451,53 @@
         StringBuilder s = new StringBuilder();
         int indentLevel = 0;
         if (prettyPrint) {
-            for (int i=0; i<indentLevel; i++) {
+            for (int i = 0; i < indentLevel; i++) {
                 s.append(' ');
             }
         }
         if (node instanceof Text) {
-            String data = ((Text)node).getData();
+            String data = ((Text) node).getData();
             //    TODO Does Java trim() work same as XMLWhitespace?
             String v = (prettyPrint) ? data.trim() : data;
             s.append(escapeElementValue(v));
             return s.toString();
         }
         if (node instanceof Attr) {
-            String value = ((Attr)node).getValue();
+            String value = ((Attr) node).getValue();
             s.append(escapeAttributeValue(value));
             return s.toString();
         }
         if (node instanceof Comment) {
-            s.append("<!--" + ((Comment)node).getNodeValue() + "-->");
+            s.append("<!--" + ((Comment) node).getNodeValue() + "-->");
             return s.toString();
         }
         if (node instanceof ProcessingInstruction) {
-            ProcessingInstruction pi = (ProcessingInstruction)node;
+            ProcessingInstruction pi = (ProcessingInstruction) node;
             s.append("<?" + pi.getTarget() + " " + pi.getData() + "?>");
             return s.toString();
         }
-        s.append(elementToXmlString((Element)node));
+        s.append(elementToXmlString((Element) node));
         return s.toString();
     }
 
     private void beautifyElement(Element e, int indent) {
         StringBuilder s = new StringBuilder();
         s.append('\n');
-        for (int i=0; i<indent; i++) {
+        for (int i = 0; i < indent; i++) {
             s.append(' ');
         }
         String afterContent = s.toString();
-        for (int i=0; i<prettyIndent; i++) {
+        for (int i = 0; i < prettyIndent; i++) {
             s.append(' ');
         }
         String beforeContent = s.toString();
 
-        //    We "mark" all the nodes first; if we tried to do this loop otherwise, it would behave unexpectedly (the inserted nodes
+        //    We "mark" all the nodes first; if we tried to do this loop otherwise, it would behave
+        // unexpectedly (the inserted nodes
         //    would contribute to the length and it might never terminate).
         ArrayList<Node> toIndent = new ArrayList<Node>();
         boolean indentChildren = false;
-        for (int i=0; i<e.getChildNodes().getLength(); i++) {
+        for (int i = 0; i < e.getChildNodes().getLength(); i++) {
             if (i == 1) indentChildren = true;
             if (e.getChildNodes().item(i) instanceof Text) {
                 toIndent.add(e.getChildNodes().item(i));
@@ -413,19 +507,18 @@
             }
         }
         if (indentChildren) {
-            for (int i=0; i<toIndent.size(); i++) {
-                e.insertBefore(e.getOwnerDocument().createTextNode(beforeContent),
-                        toIndent.get(i));
+            for (int i = 0; i < toIndent.size(); i++) {
+                e.insertBefore(e.getOwnerDocument().createTextNode(beforeContent), toIndent.get(i));
             }
         }
         NodeList nodes = e.getChildNodes();
         ArrayList<Element> list = new ArrayList<Element>();
-        for (int i=0; i < nodes.getLength(); i++) {
+        for (int i = 0; i < nodes.getLength(); i++) {
             if (nodes.item(i) instanceof Element) {
-                list.add((Element)nodes.item(i));
+                list.add((Element) nodes.item(i));
             }
         }
-        for (Element elem: list) {
+        for (Element elem : list) {
             beautifyElement(elem, indent + prettyIndent);
         }
         if (indentChildren) {
diff -Nru rhino-1.7.7.2/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLWithScope.java rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLWithScope.java
--- rhino-1.7.7.2/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLWithScope.java	2017-09-28 00:57:34.000000000 +0200
+++ rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLWithScope.java	2022-01-06 22:57:21.000000000 +0100
@@ -6,8 +6,9 @@
 
 package org.mozilla.javascript.xmlimpl;
 
-import org.mozilla.javascript.*;
-import org.mozilla.javascript.xml.*;
+import org.mozilla.javascript.NativeWith;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.xml.XMLObject;
 
 final class XMLWithScope extends NativeWith
 {
-------------- next part --------------
 /tmp/mPzyKKlCwi/rhino-1.7.14/docs/compat/favico.ico                                                        |binary
 rhino-1.7.14/CODE_OF_CONDUCT.md                                                                            |   15 
 rhino-1.7.14/NOTICE-tools.txt                                                                              |   37 
 rhino-1.7.14/README.md                                                                                     |   91 
 rhino-1.7.14/RELEASE-NOTES.md                                                                              |  585 +
 rhino-1.7.14/RELEASE-STEPS.md                                                                              |   76 
 rhino-1.7.14/apiClasses.properties                                                                         |   36 
 rhino-1.7.14/build-date                                                                                    |    1 
 rhino-1.7.14/build.gradle                                                                                  |  407 
 rhino-1.7.14/build.properties                                                                              |   26 
 rhino-1.7.14/build.xml                                                                                     |  335 
 rhino-1.7.14/checkstyle.xml                                                                                |    3 
 rhino-1.7.14/debian/changelog                                                                              |   38 
 rhino-1.7.14/debian/clean                                                                                  |    1 
 rhino-1.7.14/debian/control                                                                                |   25 
 rhino-1.7.14/debian/copyright                                                                              |    3 
 rhino-1.7.14/debian/librhino-java-doc.doc-base                                                             |   10 
 rhino-1.7.14/debian/librhino-java-doc.examples                                                             |    1 
 rhino-1.7.14/debian/librhino-java.manifest                                                                 |   12 
 rhino-1.7.14/debian/patches/03_public_getSourcePositionFromStack.patch                                     |   18 
 rhino-1.7.14/debian/patches/05_modify-usage.patch                                                          |   16 
 rhino-1.7.14/debian/patches/06_preserve-backward-compatibility.patch                                       |  172 
 rhino-1.7.14/debian/patches/07_fix-context-implementation-version.patch                                    |   18 
 rhino-1.7.14/debian/patches/08_fix-jar-version-number.patch                                                |   14 
 rhino-1.7.14/debian/patches/preserve-backward-compatibility.patch                                          |  196 
 rhino-1.7.14/debian/patches/public_getSourcePositionFromStack.patch                                        |   24 
 rhino-1.7.14/debian/patches/script-engine.patch                                                            | 1303 --
 rhino-1.7.14/debian/patches/series                                                                         |    8 
 rhino-1.7.14/debian/pom.xml                                                                                |   33 
 rhino-1.7.14/debian/rhino.1                                                                                |  112 
 rhino-1.7.14/debian/rhino.manpages                                                                         |    2 
 rhino-1.7.14/debian/rules                                                                                  |   23 
 rhino-1.7.14/debian/source/lintian-overrides                                                               |    4 
 rhino-1.7.14/gradle.properties                                                                             |    7 
 rhino-1.7.14/man/rhino.1                                                                                   |   42 
 rhino-1.7.14/maven/maven-pom.xml                                                                           |   41 
 rhino-1.7.14/maven/maven-snapshot-deploy.sh                                                                |   15 
 rhino-1.7.14/maven/maven-staging-deploy.sh                                                                 |   51 
 rhino-1.7.14/release-steps.txt                                                                             |   37 
 rhino-1.7.14/spotbugs-exclude.xml                                                                          |  139 
 rhino-1.7.14/src/META-INF/services/javax.script.ScriptEngineFactory                                        |    1 
 rhino-1.7.14/src/build.xml                                                                                 |   76 
 rhino-1.7.14/src/manifest                                                                                  |    6 
 rhino-1.7.14/src/org/mozilla/classfile/ByteCode.java                                                       |   43 
 rhino-1.7.14/src/org/mozilla/classfile/ClassFileField.java                                                 |   63 
 rhino-1.7.14/src/org/mozilla/classfile/ClassFileMethod.java                                                |   66 
 rhino-1.7.14/src/org/mozilla/classfile/ClassFileWriter.java                                                | 3754 ++-----
 rhino-1.7.14/src/org/mozilla/classfile/ConstantEntry.java                                                  |   57 
 rhino-1.7.14/src/org/mozilla/classfile/ConstantPool.java                                                   |  418 
 rhino-1.7.14/src/org/mozilla/classfile/ExceptionTableEntry.java                                            |   24 
 rhino-1.7.14/src/org/mozilla/classfile/FieldOrMethodRef.java                                               |   59 
 rhino-1.7.14/src/org/mozilla/classfile/SuperBlock.java                                                     |  163 
 rhino-1.7.14/src/org/mozilla/classfile/TypeInfo.java                                                       |  235 
 rhino-1.7.14/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java                                  |  172 
 rhino-1.7.14/src/org/mozilla/javascript/AccessorSlot.java                                                  |  202 
 rhino-1.7.14/src/org/mozilla/javascript/Arguments.java                                                     |  375 
 rhino-1.7.14/src/org/mozilla/javascript/ArrowFunction.java                                                 |   25 
 rhino-1.7.14/src/org/mozilla/javascript/BaseFunction.java                                                  |  582 -
 rhino-1.7.14/src/org/mozilla/javascript/BoundFunction.java                                                 |   20 
 rhino-1.7.14/src/org/mozilla/javascript/Callable.java                                                      |    3 
 rhino-1.7.14/src/org/mozilla/javascript/ClassCache.java                                                    |  184 
 rhino-1.7.14/src/org/mozilla/javascript/CodeGenerator.java                                                 | 1789 +--
 rhino-1.7.14/src/org/mozilla/javascript/CompilerEnvirons.java                                              |    2 
 rhino-1.7.14/src/org/mozilla/javascript/ConsString.java                                                    |   73 
 rhino-1.7.14/src/org/mozilla/javascript/ConstProperties.java                                               |    2 
 rhino-1.7.14/src/org/mozilla/javascript/Constructable.java                                                 |   19 
 rhino-1.7.14/src/org/mozilla/javascript/Context.java                                                       | 2518 ++--
 rhino-1.7.14/src/org/mozilla/javascript/ContextAction.java                                                 |    7 
 rhino-1.7.14/src/org/mozilla/javascript/ContextFactory.java                                                |  448 
 rhino-1.7.14/src/org/mozilla/javascript/DToA.java                                                          |  170 
 rhino-1.7.14/src/org/mozilla/javascript/Decompiler.java                                                    | 1253 +-
 rhino-1.7.14/src/org/mozilla/javascript/DefaultErrorReporter.java                                          |   10 
 rhino-1.7.14/src/org/mozilla/javascript/DefiningClassLoader.java                                           |    2 
 rhino-1.7.14/src/org/mozilla/javascript/Delegator.java                                                     |  124 
 rhino-1.7.14/src/org/mozilla/javascript/ES6Generator.java                                                  |  460 
 rhino-1.7.14/src/org/mozilla/javascript/ES6Iterator.java                                                   |   57 
 rhino-1.7.14/src/org/mozilla/javascript/EcmaError.java                                                     |    2 
 rhino-1.7.14/src/org/mozilla/javascript/EmbeddedSlotMap.java                                               |  284 
 rhino-1.7.14/src/org/mozilla/javascript/EqualObjectGraphs.java                                             |  321 
 rhino-1.7.14/src/org/mozilla/javascript/Evaluator.java                                                     |    6 
 rhino-1.7.14/src/org/mozilla/javascript/EvaluatorException.java                                            |    2 
 rhino-1.7.14/src/org/mozilla/javascript/Function.java                                                      |   32 
 rhino-1.7.14/src/org/mozilla/javascript/FunctionObject.java                                                |  381 
 rhino-1.7.14/src/org/mozilla/javascript/HashSlotMap.java                                                   |   97 
 rhino-1.7.14/src/org/mozilla/javascript/Hashtable.java                                                     |  317 
 rhino-1.7.14/src/org/mozilla/javascript/IRFactory.java                                                     | 1348 +-
 rhino-1.7.14/src/org/mozilla/javascript/Icode.java                                                         |  480 
 rhino-1.7.14/src/org/mozilla/javascript/IdFunctionObject.java                                              |    9 
 rhino-1.7.14/src/org/mozilla/javascript/IdFunctionObjectES6.java                                           |    7 
 rhino-1.7.14/src/org/mozilla/javascript/IdScriptableObject.java                                            |  713 -
 rhino-1.7.14/src/org/mozilla/javascript/ImplementationVersion.java                                         |   60 
 rhino-1.7.14/src/org/mozilla/javascript/ImporterTopLevel.java                                              |  240 
 rhino-1.7.14/src/org/mozilla/javascript/InterfaceAdapter.java                                              |   87 
 rhino-1.7.14/src/org/mozilla/javascript/InterpretedFunction.java                                           |  111 
 rhino-1.7.14/src/org/mozilla/javascript/Interpreter.java                                                   | 4089 ++++---
 rhino-1.7.14/src/org/mozilla/javascript/InterpreterData.java                                               |   93 
 rhino-1.7.14/src/org/mozilla/javascript/IteratorLikeIterable.java                                          |  109 
 rhino-1.7.14/src/org/mozilla/javascript/JavaAdapter.java                                                   |   63 
 rhino-1.7.14/src/org/mozilla/javascript/JavaMembers.java                                                   |  552 -
 rhino-1.7.14/src/org/mozilla/javascript/JavaMembers_jdk11.java                                             |  118 
 rhino-1.7.14/src/org/mozilla/javascript/JavaScriptException.java                                           |   65 
 rhino-1.7.14/src/org/mozilla/javascript/JavaToJSONConverters.java                                          |   86 
 rhino-1.7.14/src/org/mozilla/javascript/Kit.java                                                           |  249 
 rhino-1.7.14/src/org/mozilla/javascript/LambdaConstructor.java                                             |  195 
 rhino-1.7.14/src/org/mozilla/javascript/LambdaFunction.java                                                |   73 
 rhino-1.7.14/src/org/mozilla/javascript/LambdaSlot.java                                                    |   65 
 rhino-1.7.14/src/org/mozilla/javascript/LazilyLoadedCtor.java                                              |   26 
 rhino-1.7.14/src/org/mozilla/javascript/LazyLoadSlot.java                                                  |   25 
 rhino-1.7.14/src/org/mozilla/javascript/MemberBox.java                                                     |  253 
 rhino-1.7.14/src/org/mozilla/javascript/NativeArray.java                                                   | 2427 ++--
 rhino-1.7.14/src/org/mozilla/javascript/NativeArrayIterator.java                                           |   27 
 rhino-1.7.14/src/org/mozilla/javascript/NativeBigInt.java                                                  |  223 
 rhino-1.7.14/src/org/mozilla/javascript/NativeBoolean.java                                                 |  131 
 rhino-1.7.14/src/org/mozilla/javascript/NativeCall.java                                                    |   10 
 rhino-1.7.14/src/org/mozilla/javascript/NativeCallSite.java                                                |  332 
 rhino-1.7.14/src/org/mozilla/javascript/NativeCollectionIterator.java                                      |   78 
 rhino-1.7.14/src/org/mozilla/javascript/NativeContinuation.java                                            |   88 
 rhino-1.7.14/src/org/mozilla/javascript/NativeDate.java                                                    | 2064 ++-
 rhino-1.7.14/src/org/mozilla/javascript/NativeError.java                                                   |  328 
 rhino-1.7.14/src/org/mozilla/javascript/NativeFunction.java                                                |   22 
 rhino-1.7.14/src/org/mozilla/javascript/NativeGenerator.java                                               |  184 
 rhino-1.7.14/src/org/mozilla/javascript/NativeGlobal.java                                                  |  552 -
 rhino-1.7.14/src/org/mozilla/javascript/NativeIterator.java                                                |  191 
 rhino-1.7.14/src/org/mozilla/javascript/NativeJSON.java                                                    |  466 
 rhino-1.7.14/src/org/mozilla/javascript/NativeJavaArray.java                                               |   30 
 rhino-1.7.14/src/org/mozilla/javascript/NativeJavaClass.java                                               |   71 
 rhino-1.7.14/src/org/mozilla/javascript/NativeJavaConstructor.java                                         |    2 
 rhino-1.7.14/src/org/mozilla/javascript/NativeJavaList.java                                                |  186 
 rhino-1.7.14/src/org/mozilla/javascript/NativeJavaMap.java                                                 |  197 
 rhino-1.7.14/src/org/mozilla/javascript/NativeJavaMethod.java                                              |  261 
 rhino-1.7.14/src/org/mozilla/javascript/NativeJavaObject.java                                              | 1069 +-
 rhino-1.7.14/src/org/mozilla/javascript/NativeJavaPackage.java                                             |   19 
 rhino-1.7.14/src/org/mozilla/javascript/NativeJavaTopPackage.java                                          |    9 
 rhino-1.7.14/src/org/mozilla/javascript/NativeMap.java                                                     |  352 
 rhino-1.7.14/src/org/mozilla/javascript/NativeMath.java                                                    |  731 -
 rhino-1.7.14/src/org/mozilla/javascript/NativeNumber.java                                                  |  501 
 rhino-1.7.14/src/org/mozilla/javascript/NativeObject.java                                                  | 1242 +-
 rhino-1.7.14/src/org/mozilla/javascript/NativePromise.java                                                 |  798 +
 rhino-1.7.14/src/org/mozilla/javascript/NativeScript.java                                                  |  199 
 rhino-1.7.14/src/org/mozilla/javascript/NativeSet.java                                                     |  312 
 rhino-1.7.14/src/org/mozilla/javascript/NativeString.java                                                  | 1509 +-
 rhino-1.7.14/src/org/mozilla/javascript/NativeStringIterator.java                                          |    4 
 rhino-1.7.14/src/org/mozilla/javascript/NativeSymbol.java                                                  |  233 
 rhino-1.7.14/src/org/mozilla/javascript/NativeWeakMap.java                                                 |  215 
 rhino-1.7.14/src/org/mozilla/javascript/NativeWeakSet.java                                                 |  184 
 rhino-1.7.14/src/org/mozilla/javascript/NativeWith.java                                                    |   72 
 rhino-1.7.14/src/org/mozilla/javascript/Node.java                                                          | 1024 -
 rhino-1.7.14/src/org/mozilla/javascript/NodeTransformer.java                                               |   28 
 rhino-1.7.14/src/org/mozilla/javascript/ObjArray.java                                                      |   36 
 rhino-1.7.14/src/org/mozilla/javascript/ObjToIntMap.java                                                   |  475 
 rhino-1.7.14/src/org/mozilla/javascript/Parser.java                                                        | 3140 ++---
 rhino-1.7.14/src/org/mozilla/javascript/PolicySecurityController.java                                      |    5 
 rhino-1.7.14/src/org/mozilla/javascript/Ref.java                                                           |    6 
 rhino-1.7.14/src/org/mozilla/javascript/RhinoException.java                                                |  229 
 rhino-1.7.14/src/org/mozilla/javascript/ScriptRuntime.java                                                 | 3800 +++----
 rhino-1.7.14/src/org/mozilla/javascript/ScriptRuntimeES6.java                                              |    4 
 rhino-1.7.14/src/org/mozilla/javascript/ScriptStackElement.java                                            |    2 
 rhino-1.7.14/src/org/mozilla/javascript/Scriptable.java                                                    |    2 
 rhino-1.7.14/src/org/mozilla/javascript/ScriptableObject.java                                              | 2717 +----
 rhino-1.7.14/src/org/mozilla/javascript/SecureCaller.java                                                  |    3 
 rhino-1.7.14/src/org/mozilla/javascript/SecurityController.java                                            |    7 
 rhino-1.7.14/src/org/mozilla/javascript/SecurityUtilities.java                                             |    3 
 rhino-1.7.14/src/org/mozilla/javascript/Slot.java                                                          |  119 
 rhino-1.7.14/src/org/mozilla/javascript/SlotMap.java                                                       |   63 
 rhino-1.7.14/src/org/mozilla/javascript/SlotMapContainer.java                                              |  108 
 rhino-1.7.14/src/org/mozilla/javascript/Sorting.java                                                       |   84 
 rhino-1.7.14/src/org/mozilla/javascript/SpecialRef.java                                                    |  124 
 rhino-1.7.14/src/org/mozilla/javascript/StackStyle.java                                                    |   24 
 rhino-1.7.14/src/org/mozilla/javascript/SymbolKey.java                                                     |    2 
 rhino-1.7.14/src/org/mozilla/javascript/ThreadSafeSlotMapContainer.java                                    |  156 
 rhino-1.7.14/src/org/mozilla/javascript/Token.java                                                         | 1047 +
 rhino-1.7.14/src/org/mozilla/javascript/TokenStream.java                                                   | 2426 ++--
 rhino-1.7.14/src/org/mozilla/javascript/TopLevel.java                                                      |  166 
 rhino-1.7.14/src/org/mozilla/javascript/UintMap.java                                                       |  465 
 rhino-1.7.14/src/org/mozilla/javascript/Undefined.java                                                     |  143 
 rhino-1.7.14/src/org/mozilla/javascript/UnhandledRejectionTracker.java                                     |   77 
 rhino-1.7.14/src/org/mozilla/javascript/UniqueTag.java                                                     |    2 
 rhino-1.7.14/src/org/mozilla/javascript/VMBridge.java                                                      |   60 
 rhino-1.7.14/src/org/mozilla/javascript/WrapFactory.java                                                   |  146 
 rhino-1.7.14/src/org/mozilla/javascript/WrappedException.java                                              |    6 
 rhino-1.7.14/src/org/mozilla/javascript/annotations/JSConstructor.java                                     |    6 
 rhino-1.7.14/src/org/mozilla/javascript/annotations/JSFunction.java                                        |    6 
 rhino-1.7.14/src/org/mozilla/javascript/annotations/JSGetter.java                                          |    6 
 rhino-1.7.14/src/org/mozilla/javascript/annotations/JSSetter.java                                          |    6 
 rhino-1.7.14/src/org/mozilla/javascript/annotations/JSStaticFunction.java                                  |    6 
 rhino-1.7.14/src/org/mozilla/javascript/ast/ArrayComprehension.java                                        |    6 
 rhino-1.7.14/src/org/mozilla/javascript/ast/ArrayComprehensionLoop.java                                    |    4 
 rhino-1.7.14/src/org/mozilla/javascript/ast/ArrayLiteral.java                                              |   10 
 rhino-1.7.14/src/org/mozilla/javascript/ast/AstNode.java                                                   |   55 
 rhino-1.7.14/src/org/mozilla/javascript/ast/AstRoot.java                                                   |   14 
 rhino-1.7.14/src/org/mozilla/javascript/ast/BigIntLiteral.java                                             |   82 
 rhino-1.7.14/src/org/mozilla/javascript/ast/Block.java                                                     |   12 
 rhino-1.7.14/src/org/mozilla/javascript/ast/BreakStatement.java                                            |    2 
 rhino-1.7.14/src/org/mozilla/javascript/ast/Comment.java                                                   |   24 
 rhino-1.7.14/src/org/mozilla/javascript/ast/ConditionalExpression.java                                     |    2 
 rhino-1.7.14/src/org/mozilla/javascript/ast/ContinueStatement.java                                         |    2 
 rhino-1.7.14/src/org/mozilla/javascript/ast/DoLoop.java                                                    |    5 
 rhino-1.7.14/src/org/mozilla/javascript/ast/EmptyStatement.java                                            |    2 
 rhino-1.7.14/src/org/mozilla/javascript/ast/ErrorCollector.java                                            |   15 
 rhino-1.7.14/src/org/mozilla/javascript/ast/ErrorNode.java                                                 |    2 
 rhino-1.7.14/src/org/mozilla/javascript/ast/ExpressionStatement.java                                       |    6 
 rhino-1.7.14/src/org/mozilla/javascript/ast/ForInLoop.java                                                 |    2 
 rhino-1.7.14/src/org/mozilla/javascript/ast/ForLoop.java                                                   |   16 
 rhino-1.7.14/src/org/mozilla/javascript/ast/FunctionCall.java                                              |    9 
 rhino-1.7.14/src/org/mozilla/javascript/ast/FunctionNode.java                                              |   22 
 rhino-1.7.14/src/org/mozilla/javascript/ast/GeneratorExpression.java                                       |    1 
 rhino-1.7.14/src/org/mozilla/javascript/ast/IfStatement.java                                               |   30 
 rhino-1.7.14/src/org/mozilla/javascript/ast/Label.java                                                     |    2 
 rhino-1.7.14/src/org/mozilla/javascript/ast/LabeledStatement.java                                          |    8 
 rhino-1.7.14/src/org/mozilla/javascript/ast/LetNode.java                                                   |    4 
 rhino-1.7.14/src/org/mozilla/javascript/ast/NewExpression.java                                             |    2 
 rhino-1.7.14/src/org/mozilla/javascript/ast/NumberLiteral.java                                             |    2 
 rhino-1.7.14/src/org/mozilla/javascript/ast/ObjectLiteral.java                                             |   10 
 rhino-1.7.14/src/org/mozilla/javascript/ast/ObjectProperty.java                                            |   61 
 rhino-1.7.14/src/org/mozilla/javascript/ast/ParenthesizedExpression.java                                   |    2 
 rhino-1.7.14/src/org/mozilla/javascript/ast/RegExpLiteral.java                                             |    2 
 rhino-1.7.14/src/org/mozilla/javascript/ast/ReturnStatement.java                                           |    2 
 rhino-1.7.14/src/org/mozilla/javascript/ast/Scope.java                                                     |   12 
 rhino-1.7.14/src/org/mozilla/javascript/ast/ScriptNode.java                                                |  105 
 rhino-1.7.14/src/org/mozilla/javascript/ast/StringLiteral.java                                             |    4 
 rhino-1.7.14/src/org/mozilla/javascript/ast/SwitchCase.java                                                |   15 
 rhino-1.7.14/src/org/mozilla/javascript/ast/SwitchStatement.java                                           |    6 
 rhino-1.7.14/src/org/mozilla/javascript/ast/TaggedTemplateLiteral.java                                     |   66 
 rhino-1.7.14/src/org/mozilla/javascript/ast/TemplateCharacters.java                                        |   82 
 rhino-1.7.14/src/org/mozilla/javascript/ast/TemplateLiteral.java                                           |  140 
 rhino-1.7.14/src/org/mozilla/javascript/ast/ThrowStatement.java                                            |    2 
 rhino-1.7.14/src/org/mozilla/javascript/ast/TryStatement.java                                              |    9 
 rhino-1.7.14/src/org/mozilla/javascript/ast/UnaryExpression.java                                           |   63 
 rhino-1.7.14/src/org/mozilla/javascript/ast/UpdateExpression.java                                          |  128 
 rhino-1.7.14/src/org/mozilla/javascript/ast/VariableDeclaration.java                                       |   11 
 rhino-1.7.14/src/org/mozilla/javascript/ast/VariableInitializer.java                                       |    4 
 rhino-1.7.14/src/org/mozilla/javascript/ast/WhileLoop.java                                                 |   10 
 rhino-1.7.14/src/org/mozilla/javascript/ast/WithStatement.java                                             |    8 
 rhino-1.7.14/src/org/mozilla/javascript/ast/XmlElemRef.java                                                |    5 
 rhino-1.7.14/src/org/mozilla/javascript/ast/XmlFragment.java                                               |    2 
 rhino-1.7.14/src/org/mozilla/javascript/ast/XmlLiteral.java                                                |    6 
 rhino-1.7.14/src/org/mozilla/javascript/ast/XmlMemberGet.java                                              |   18 
 rhino-1.7.14/src/org/mozilla/javascript/ast/XmlPropRef.java                                                |    6 
 rhino-1.7.14/src/org/mozilla/javascript/ast/XmlRef.java                                                    |    7 
 rhino-1.7.14/src/org/mozilla/javascript/ast/Yield.java                                                     |   12 
 rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/ModuleScope.java                                   |    8 
 rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/ModuleScript.java                                  |    4 
 rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/ModuleScriptProvider.java                          |    4 
 rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/Require.java                                       |    7 
 rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/RequireBuilder.java                                |    4 
 rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/CachingModuleScriptProviderBase.java      |   17 
 rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/DefaultUrlConnectionExpiryCalculator.java |    6 
 rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/ModuleSource.java                         |    4 
 rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/ModuleSourceProviderBase.java             |   12 
 rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/MultiModuleScriptProvider.java            |    1 
 rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/ParsedContentType.java                    |    4 
 rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/SoftCachingModuleScriptProvider.java      |   13 
 rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/StrongCachingModuleScriptProvider.java    |    8 
 rhino-1.7.14/src/org/mozilla/javascript/commonjs/module/provider/UrlModuleSourceProvider.java              |   49 
 rhino-1.7.14/src/org/mozilla/javascript/debug/DebuggableScript.java                                        |   10 
 rhino-1.7.14/src/org/mozilla/javascript/engine/BindingsObject.java                                         |   59 
 rhino-1.7.14/src/org/mozilla/javascript/engine/Builtins.java                                               |   59 
 rhino-1.7.14/src/org/mozilla/javascript/engine/RhinoCompiledScript.java                                    |   33 
 rhino-1.7.14/src/org/mozilla/javascript/engine/RhinoInvocationHandler.java                                 |   25 
 rhino-1.7.14/src/org/mozilla/javascript/engine/RhinoScriptEngine.java                                      |  341 
 rhino-1.7.14/src/org/mozilla/javascript/engine/RhinoScriptEngineFactory.java                               |  137 
 rhino-1.7.14/src/org/mozilla/javascript/jdk13/VMBridge_jdk13.java                                          |  154 
 rhino-1.7.14/src/org/mozilla/javascript/jdk15/VMBridge_jdk15.java                                          |   58 
 rhino-1.7.14/src/org/mozilla/javascript/jdk18/VMBridge_jdk18.java                                          |  143 
 rhino-1.7.14/src/org/mozilla/javascript/json/JsonParser.java                                               |   22 
 rhino-1.7.14/src/org/mozilla/javascript/optimizer/Block.java                                               |  391 
 rhino-1.7.14/src/org/mozilla/javascript/optimizer/BodyCodegen.java                                         | 4391 ++++++++
 rhino-1.7.14/src/org/mozilla/javascript/optimizer/ClassCompiler.java                                       |   13 
 rhino-1.7.14/src/org/mozilla/javascript/optimizer/Codegen.java                                             | 5322 +---------
 rhino-1.7.14/src/org/mozilla/javascript/optimizer/OptFunctionNode.java                                     |    4 
 rhino-1.7.14/src/org/mozilla/javascript/optimizer/OptRuntime.java                                          |  332 
 rhino-1.7.14/src/org/mozilla/javascript/optimizer/OptTransformer.java                                      |   33 
 rhino-1.7.14/src/org/mozilla/javascript/optimizer/Optimizer.java                                           |  318 
 rhino-1.7.14/src/org/mozilla/javascript/regexp/NativeRegExp.java                                           | 3117 ++---
 rhino-1.7.14/src/org/mozilla/javascript/regexp/NativeRegExpCallable.java                                   |   29 
 rhino-1.7.14/src/org/mozilla/javascript/regexp/NativeRegExpCtor.java                                       |  362 
 rhino-1.7.14/src/org/mozilla/javascript/regexp/NativeRegExpInstantiator.java                               |   25 
 rhino-1.7.14/src/org/mozilla/javascript/regexp/RegExpImpl.java                                             |   71 
 rhino-1.7.14/src/org/mozilla/javascript/regexp/SubString.java                                              |    2 
 rhino-1.7.14/src/org/mozilla/javascript/resources/Messages.properties                                      |   85 
 rhino-1.7.14/src/org/mozilla/javascript/resources/Messages_en.properties                                   |   10 
 rhino-1.7.14/src/org/mozilla/javascript/resources/Messages_fr.properties                                   |   28 
 rhino-1.7.14/src/org/mozilla/javascript/serialize/ScriptableInputStream.java                               |   10 
 rhino-1.7.14/src/org/mozilla/javascript/serialize/ScriptableOutputStream.java                              |   13 
 rhino-1.7.14/src/org/mozilla/javascript/typedarrays/ByteIo.java                                            |   36 
 rhino-1.7.14/src/org/mozilla/javascript/typedarrays/Conversions.java                                       |   49 
 rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeArrayBuffer.java                                 |  191 
 rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeArrayBufferView.java                             |  122 
 rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeDataView.java                                    |  627 -
 rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeFloat32Array.java                                |   17 
 rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeFloat64Array.java                                |   19 
 rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeInt16Array.java                                  |   17 
 rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeInt32Array.java                                  |   17 
 rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeInt8Array.java                                   |   11 
 rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeTypedArrayIterator.java                          |    4 
 rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeTypedArrayView.java                              |  569 -
 rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeUint16Array.java                                 |   17 
 rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeUint32Array.java                                 |   17 
 rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeUint8Array.java                                  |   11 
 rhino-1.7.14/src/org/mozilla/javascript/typedarrays/NativeUint8ClampedArray.java                           |   13 
 rhino-1.7.14/src/org/mozilla/javascript/v8dtoa/DoubleConversion.java                                       |    3 
 rhino-1.7.14/src/org/mozilla/javascript/v8dtoa/DoubleHelper.java                                           |    3 
 rhino-1.7.14/src/org/mozilla/javascript/xml/XMLLib.java                                                    |    8 
 rhino-1.7.14/src/org/mozilla/javascript/xml/XMLObject.java                                                 |   14 
 rhino-1.7.14/tools/ci/linux/release.sh                                                                     |    2 
 rhino-1.7.14/toolsrc/build.xml                                                                             |   49 
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/ToolErrorReporter.java                                   |   16 
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/debugger/Dim.java                                        |   31 
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/debugger/Main.java                                       |    5 
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/debugger/SwingGui.java                                   | 2246 +---
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/debugger/build.xml                                       |   96 
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/debugger/treetable/AbstractCellEditor.java               |    7 
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/debugger/treetable/JTreeTable.java                       |   30 
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/debugger/treetable/TreeTableModelAdapter.java            |    4 
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/idswitch/CodePrinter.java                                |  181 
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/idswitch/FileBody.java                                   |  159 
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/idswitch/IdValuePair.java                                |   26 
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/idswitch/Main.java                                       |  580 -
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/idswitch/README                                          |  154 
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/idswitch/SwitchGenerator.java                            |  459 
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/jsc/Main.java                                            |   20 
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/resources/Messages.properties                            |   56 
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/ConsoleTextArea.java                               |   20 
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/Environment.java                                   |    6 
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/Global.java                                        |  717 -
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/JavaPolicySecurity.java                            |   15 
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/Main.java                                          |  259 
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/SecurityProxy.java                                 |    4 
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/ShellConsole.java                                  |   10 
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/ShellContextFactory.java                           |    4 
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/ShellLine.java                                     |    4 
 rhino-1.7.14/toolsrc/org/mozilla/javascript/tools/shell/Timers.java                                        |  160 
 rhino-1.7.14/xmlimplsrc/build.xml                                                                          |   69 
 rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/Namespace.java                                      |   11 
 rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/QName.java                                          |   15 
 rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XML.java                                            |    6 
 rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLCtor.java                                        |    7 
 rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLLibImpl.java                                     |   19 
 rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLList.java                                        |   17 
 rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLName.java                                        |    8 
 rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLObjectImpl.java                                  |   17 
 rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XMLWithScope.java                                   |    5 
 rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlNode.java                                        |   28 
 rhino-1.7.14/xmlimplsrc/org/mozilla/javascript/xmlimpl/XmlProcessor.java                                   |  215 
 344 files changed, 46178 insertions(+), 38246 deletions(-)


More information about the pkg-java-maintainers mailing list