diff -Nru rhino-1.7.7.1/build.gradle rhino-1.7.7.2/build.gradle --- rhino-1.7.7.1/build.gradle 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/build.gradle 2017-09-27 22:57:34.000000000 +0000 @@ -3,6 +3,7 @@ apply plugin: 'maven-publish' apply plugin: 'jacoco' apply plugin: 'distribution' +apply plugin: 'checkstyle' sourceCompatibility = 1.6 targetCompatibility = 1.6 @@ -43,6 +44,15 @@ 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') + } + } } test { @@ -58,8 +68,18 @@ testLogging.showStandardStreams = true } +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 +} + task testBenchmark(type: Test) { - include "**/benchmarks/*Benchmark*" + jacoco { + enabled = false + } + include "**/benchmarks/V8Benchmark*" systemProperty 'rhino.benchmark.report', "${buildDir.absolutePath}" systemProperty 'file.encoding', 'UTF-8' workingDir = file("testsrc/benchmarks") @@ -67,6 +87,15 @@ testLogging.showStandardStreams = true forkEvery = 1 } +testBenchmark.dependsOn sunSpiderBenchmark + +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 +} + + idea { module { @@ -181,24 +210,36 @@ } } +checkstyle { + configFile = file("${projectDir}/checkstyle.xml") + sourceSets = [project.sourceSets.main] +} + distributions { main { contents { - from(sourceSets.main.allSource) { + from(sourceSets.main.java) { exclude 'man' - into '/src' + into 'rhino' + project.version + '/src' + } + from(sourceSets.main.resources) { + exclude '**/*.java' + into 'rhino' + project.version + '/src' } from(javadoc.destinationDir) { - into '/docs' + into 'rhino' + project.version + '/docs' } from(jar.outputs.files) { - into '/lib' + into 'rhino' + project.version + '/lib' } from(sourceSets.main.allSource) { include 'man/*.1' + into 'rhino' + project.version } from(file(".")) { - include '*.txt' + include '*.txt', '*.md', 'build.gradle', 'build.properties', 'gradle.properties', + 'gradle/**', 'gradlew' + into 'rhino' + project.version } into "/" } diff -Nru rhino-1.7.7.1/build.properties rhino-1.7.7.2/build.properties --- rhino-1.7.7.1/build.properties 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/build.properties 2017-09-27 22:57:34.000000000 +0000 @@ -4,9 +4,9 @@ name: rhino Name: Rhino -version: 1.7.7.1 +version: 1.7.7.2 # See Context#getImplementationVersion() for format of this! -implementation.version: Rhino 1.7.7 ${implementation.date} +implementation.version: Rhino 1.7.7.2 ${implementation.date} build.dir: build rhino.jar: js.jar diff -Nru rhino-1.7.7.1/checkstyle.xml rhino-1.7.7.2/checkstyle.xml --- rhino-1.7.7.1/checkstyle.xml 1970-01-01 00:00:00.000000000 +0000 +++ rhino-1.7.7.2/checkstyle.xml 2017-09-27 22:57:34.000000000 +0000 @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru rhino-1.7.7.1/debian/changelog rhino-1.7.7.2/debian/changelog --- rhino-1.7.7.1/debian/changelog 2017-01-20 10:04:18.000000000 +0000 +++ rhino-1.7.7.2/debian/changelog 2021-02-08 10:39:29.000000000 +0000 @@ -1,3 +1,32 @@ +rhino (1.7.7.2-3) unstable; urgency=medium + + * Team upload. + * Backported the JSR 223 script engine to use Rhino as a replacement + for Nashorn with OpenJDK 17 + * Standards-Version updated to 4.5.1 + * Switch to debhelper level 13 + + -- Emmanuel Bourg Mon, 08 Feb 2021 11:39:29 +0100 + +rhino (1.7.7.2-2) UNRELEASED; urgency=medium + + * Team upload. + * Add patch to install JAR as 1.7.7.2 (not 1.7.7.1) + + -- tony mancill Wed, 25 Nov 2020 12:02:51 -0800 + +rhino (1.7.7.2-1) unstable; urgency=medium + + * Team upload. + * New upstream release + - Refreshed the patches + * Removed Damien Raude-Morvan from the uploaders (Closes: #889449) + * Track the releases > 1.7.7.1 + * Standards-Version updated to 4.5.0 + * Use salsa.debian.org Vcs-* URLs + + -- Emmanuel Bourg Thu, 03 Sep 2020 13:59:51 +0200 + rhino (1.7.7.1-1) unstable; urgency=medium * Team upload. diff -Nru rhino-1.7.7.1/debian/compat rhino-1.7.7.2/debian/compat --- rhino-1.7.7.1/debian/compat 2017-01-19 17:45:23.000000000 +0000 +++ rhino-1.7.7.2/debian/compat 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -10 diff -Nru rhino-1.7.7.1/debian/control rhino-1.7.7.2/debian/control --- rhino-1.7.7.1/debian/control 2017-01-19 17:45:23.000000000 +0000 +++ rhino-1.7.7.2/debian/control 2021-02-08 10:39:26.000000000 +0000 @@ -2,25 +2,27 @@ Section: interpreters Priority: optional Maintainer: Debian Java Maintainers -Uploaders: Marcus Better , - Damien Raude-Morvan , - Jakub Adam -Build-Depends: ant, - debhelper (>= 10), - default-jdk, - javahelper, - maven-repo-helper -Standards-Version: 3.9.8 -Vcs-Git: https://anonscm.debian.org/git/pkg-java/rhino.git -Vcs-Browser: https://anonscm.debian.org/cgit/pkg-java/rhino.git +Uploaders: + Marcus Better , + Jakub Adam +Build-Depends: + ant, + debhelper-compat (= 13), + default-jdk, + javahelper, + maven-repo-helper +Standards-Version: 4.5.1 +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/ Package: rhino Architecture: all -Depends: default-jre-headless (>= 1:1.6) | java6-runtime-headless, - libjline-java, - librhino-java (= ${source:Version}), - ${misc:Depends} +Depends: + default-jre-headless (>= 1:1.6) | java6-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 diff -Nru rhino-1.7.7.1/debian/librhino-java.links rhino-1.7.7.2/debian/librhino-java.links --- rhino-1.7.7.1/debian/librhino-java.links 2017-01-19 17:45:23.000000000 +0000 +++ rhino-1.7.7.2/debian/librhino-java.links 2020-09-03 11:36:47.000000000 +0000 @@ -1 +1 @@ -usr/share/java/js.jar usr/share/java/rhino.jar \ No newline at end of file +usr/share/java/js.jar usr/share/java/rhino.jar diff -Nru rhino-1.7.7.1/debian/patches/03_public_getSourcePositionFromStack.patch rhino-1.7.7.2/debian/patches/03_public_getSourcePositionFromStack.patch --- rhino-1.7.7.1/debian/patches/03_public_getSourcePositionFromStack.patch 2017-01-19 17:45:23.000000000 +0000 +++ rhino-1.7.7.2/debian/patches/03_public_getSourcePositionFromStack.patch 2020-09-03 11:38:39.000000000 +0000 @@ -7,7 +7,7 @@ --- a/src/org/mozilla/javascript/Context.java +++ b/src/org/mozilla/javascript/Context.java -@@ -2568,7 +2568,7 @@ +@@ -2592,7 +2592,7 @@ return (Evaluator)Kit.newInstanceOrNull(interpreterClass); } diff -Nru rhino-1.7.7.1/debian/patches/06_preserve-backward-compatibility.patch rhino-1.7.7.2/debian/patches/06_preserve-backward-compatibility.patch --- rhino-1.7.7.1/debian/patches/06_preserve-backward-compatibility.patch 2017-01-20 09:04:51.000000000 +0000 +++ rhino-1.7.7.2/debian/patches/06_preserve-backward-compatibility.patch 2020-09-03 11:38:43.000000000 +0000 @@ -51,7 +51,7 @@ } --- a/src/org/mozilla/javascript/ast/FunctionNode.java +++ b/src/org/mozilla/javascript/ast/FunctionNode.java -@@ -329,14 +329,26 @@ +@@ -330,14 +330,26 @@ return functionForm == Form.GETTER || functionForm == Form.SETTER || functionForm == Form.METHOD; } @@ -78,7 +78,7 @@ public boolean isNormalMethod() { return functionForm == Form.METHOD; } -@@ -345,10 +357,18 @@ +@@ -346,10 +358,18 @@ functionForm = Form.GETTER; } diff -Nru rhino-1.7.7.1/debian/patches/07_fix-context-implementation-version.patch rhino-1.7.7.2/debian/patches/07_fix-context-implementation-version.patch --- rhino-1.7.7.1/debian/patches/07_fix-context-implementation-version.patch 2017-01-20 10:02:17.000000000 +0000 +++ rhino-1.7.7.2/debian/patches/07_fix-context-implementation-version.patch 2020-09-03 11:38:45.000000000 +0000 @@ -4,7 +4,7 @@ Forwarded: https://github.com/mozilla/rhino/commit/f575445cbe5e245#commitcomment-20551124 --- a/src/org/mozilla/javascript/Context.java +++ b/src/org/mozilla/javascript/Context.java -@@ -704,9 +704,9 @@ +@@ -729,9 +729,9 @@ is = metaUrl.openStream(); Manifest mf = new Manifest(is); Attributes attrs = mf.getMainAttributes(); diff -Nru rhino-1.7.7.1/debian/patches/08_fix-jar-version-number.patch rhino-1.7.7.2/debian/patches/08_fix-jar-version-number.patch --- rhino-1.7.7.1/debian/patches/08_fix-jar-version-number.patch 1970-01-01 00:00:00.000000000 +0000 +++ rhino-1.7.7.2/debian/patches/08_fix-jar-version-number.patch 2020-12-16 15:45:16.000000000 +0000 @@ -0,0 +1,14 @@ +Description: update POM to match upstream version +Author: tony mancill + +--- a/maven/maven-pom.xml ++++ b/maven/maven-pom.xml +@@ -12,7 +12,7 @@ + org.mozilla + rhino + Mozilla Rhino +- 1.7.7.1 ++ 1.7.7.2 + + jar + diff -Nru rhino-1.7.7.1/debian/patches/script-engine.patch rhino-1.7.7.2/debian/patches/script-engine.patch --- rhino-1.7.7.1/debian/patches/script-engine.patch 1970-01-01 00:00:00.000000000 +0000 +++ rhino-1.7.7.2/debian/patches/script-engine.patch 2021-02-08 10:03:36.000000000 +0000 @@ -0,0 +1,1303 @@ +From f195514ffee1b759ba088883732e77b026b3a694 Mon Sep 17 00:00:00 2001 +From: Gregory Brail +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; ++ ++/** ++ *

++ * This class defines the following built-in functions for the RhinoScriptEngine. ++ *

++ *
    ++ *
  • print(arg, arg, ...): Write each argument, concatenated to the ScriptEngine's ++ * "standard output" as a string.
  • ++ *
++ */ ++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; ++ ++/** ++ *

++ * This is the implementation of the standard ScriptEngine interface for Rhino. ++ *

++ *

++ * 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. ++ *

++ *

++ * 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. ++ *

++ *

++ * The Rhino script engine includes some top-level built-in functions. See the Builtins class for ++ * more documentation. ++ *

++ *

++ * 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. ++ *

++ *
    ++ *
  • 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.
  • ++ *
  • 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.
  • ++ *
++ */ ++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 getInterface(Class 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 getInterface(Object thiz, Class 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; ++ ++/** ++ *

++ * 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. ++ *

++ *

++ * 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. ++ *

++ *

++ * 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. ++ *

++ */ ++public class RhinoScriptEngineFactory ++ implements ScriptEngineFactory { ++ ++ public static final String NAME = "rhino"; ++ public static final String LANGUAGE = "javascript"; ++ public static final List NAMES = ++ Arrays.asList("rhino", "Rhino", "javascript", "JavaScript"); ++ public static final List EXTENSIONS = ++ Collections.singletonList("js"); ++ public static final List 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 getExtensions() { ++ return EXTENSIONS; ++ } ++ ++ @Override ++ public List getMimeTypes() { ++ return MIME_TYPES; ++ } ++ ++ @Override ++ public List 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}" +- /> ++ > ++ ++ + + + diff -Nru rhino-1.7.7.1/debian/patches/series rhino-1.7.7.2/debian/patches/series --- rhino-1.7.7.1/debian/patches/series 2017-01-20 09:53:06.000000000 +0000 +++ rhino-1.7.7.2/debian/patches/series 2021-02-08 09:51:49.000000000 +0000 @@ -2,3 +2,6 @@ 05_modify-usage.patch 06_preserve-backward-compatibility.patch 07_fix-context-implementation-version.patch +08_fix-jar-version-number.patch +script-engine.patch + diff -Nru rhino-1.7.7.1/debian/rhino.manpages rhino-1.7.7.2/debian/rhino.manpages --- rhino-1.7.7.1/debian/rhino.manpages 2017-01-19 17:45:23.000000000 +0000 +++ rhino-1.7.7.2/debian/rhino.manpages 2020-09-03 11:36:47.000000000 +0000 @@ -1,3 +1,3 @@ -debian/rhino.1 -debian/rhino-jsc.1 debian/rhino-debugger.1 +debian/rhino-jsc.1 +debian/rhino.1 diff -Nru rhino-1.7.7.1/debian/rules rhino-1.7.7.2/debian/rules --- rhino-1.7.7.1/debian/rules 2017-01-19 17:45:23.000000000 +0000 +++ rhino-1.7.7.2/debian/rules 2021-02-08 10:39:26.000000000 +0000 @@ -7,7 +7,7 @@ dh $@ --with javahelper --with maven-repo-helper override_dh_auto_build: - dh_auto_build -- jar javadoc + dh_auto_build -- jar javadoc -Dsource-level=7 -Dtarget-jvm=7 override_dh_installchangelogs: dh_installchangelogs -- RELEASE-NOTES.md @@ -21,6 +21,3 @@ mv $(BUILDDIR)/javadoc $(BUILDDIR)/api dh_install -plibrhino-java-doc $(BUILDDIR)/api /usr/share/doc/rhino/ - -get-orig-source: - uscan --force-download diff -Nru rhino-1.7.7.1/debian/watch rhino-1.7.7.2/debian/watch --- rhino-1.7.7.1/debian/watch 2017-01-19 17:45:23.000000000 +0000 +++ rhino-1.7.7.2/debian/watch 2020-09-03 11:36:47.000000000 +0000 @@ -1,3 +1,3 @@ version=4 opts=mode=git,compression=xz,uversionmangle=s/-RC/~RC/;s/pre/~pre/;s/_/./g;s/14R/1.4R/;s/140R/1.4.0R/;s/150R/1.5.0R/ \ -https://github.com/mozilla/rhino refs/tags/Rhino([\d_.R]+)_RELEASE +https://github.com/mozilla/rhino refs/tags/Rhino([\d_.R]+)_(?:RELEASE|Release) diff -Nru rhino-1.7.7.1/.gitignore rhino-1.7.7.2/.gitignore --- rhino-1.7.7.1/.gitignore 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/.gitignore 2017-09-27 22:57:34.000000000 +0000 @@ -21,3 +21,4 @@ # eclipse .classpath .project +test262 diff -Nru rhino-1.7.7.1/.gitmodules rhino-1.7.7.2/.gitmodules --- rhino-1.7.7.1/.gitmodules 1970-01-01 00:00:00.000000000 +0000 +++ rhino-1.7.7.2/.gitmodules 2017-09-27 22:57:34.000000000 +0000 @@ -0,0 +1,3 @@ +[submodule "test262"] + path = test262 + url = https://github.com/tc39/test262.git diff -Nru rhino-1.7.7.1/gradle.properties rhino-1.7.7.2/gradle.properties --- rhino-1.7.7.1/gradle.properties 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/gradle.properties 2017-09-27 22:57:34.000000000 +0000 @@ -1,7 +1,7 @@ #Sun, 26 Apr 2015 10:43:55 +0300 rootProject.name=rhino group=org.mozilla -version=1.7.7.1 +version=1.7.7.2 buildDir=buildGradle mavenSnapshotRepo=https://oss.sonatype.org/content/repositories/snapshots mavenReleaseRepo=https://oss.sonatype.org/service/local/staging/deploy/maven diff -Nru rhino-1.7.7.1/maven/maven-snapshot-deploy.sh rhino-1.7.7.2/maven/maven-snapshot-deploy.sh --- rhino-1.7.7.1/maven/maven-snapshot-deploy.sh 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/maven/maven-snapshot-deploy.sh 2017-09-27 22:57:34.000000000 +0000 @@ -1,7 +1,15 @@ #!/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=../build/rhino1.7.7/js.jar \ + -Dfile=${deployFile} \ -DpomFile=maven-pom.xml \ -DrepositoryId=sonatype-nexus-snapshots \ -Durl=https://oss.sonatype.org/content/repositories/snapshots/ diff -Nru rhino-1.7.7.1/maven/maven-staging-deploy.sh rhino-1.7.7.2/maven/maven-staging-deploy.sh --- rhino-1.7.7.1/maven/maven-staging-deploy.sh 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/maven/maven-staging-deploy.sh 2017-09-27 22:57:34.000000000 +0000 @@ -7,9 +7,9 @@ pom=maven-pom.xml jsjar=../buildGradle/libs/rhino-${vers}.jar echo "Installing ${jsjar}" -srczip=../rhino${vers}-sources.zip +srczip=../buildGradle/libs/rhino-${vers}-sources.jar echo "Sources are ${srczip}" -doczip=../build/rhino${vers}/javadoc.zip +doczip=../buildGradle/libs/rhino-${vers}-javadoc.jar echo "Javadoc is ${doczip}" if [ ! -f $jsjar ] @@ -20,7 +20,7 @@ if [ ! -f $srczip ] then - echo "Missing rhino${vers}-sources.zip. Run \"ant source-zip\"." + echo "Missing rhino-${vers}-sources.zip. Run \"ant source-zip\"." exit 2 fi diff -Nru rhino-1.7.7.1/README.md rhino-1.7.7.2/README.md --- rhino-1.7.7.1/README.md 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/README.md 2017-09-27 22:57:34.000000000 +0000 @@ -15,6 +15,7 @@ Rhino 1.7.6April 15, 2015 Rhino 1.7.7June 17, 2015 Rhino 1.7.7.1February 2, 2016 +Rhino 1.7.7.2August 24, 2017 [Release Notes](./RELEASE-NOTES.md) for recent releases. @@ -38,37 +39,20 @@ ## Building -### Status of "master" branch - - - - - -
Java 6 - - -
Java 7 - - -
Java 8 - - -
- ### How to Build Rhino builds with `Gradle`. Here are some useful tasks: - - ./gradlew jar - +``` +./gradlew jar +``` Build and create `Rhino` jar in the `build/libs` directory. - - ./gradlew test - +``` +./gradlew test +``` Build and run all the tests. - - ./gradlew testBenchmark - +``` +./gradlew testBenchmark +``` Build and run benchmark tests. ## Releasing and publishing new version @@ -76,30 +60,30 @@ 1. Ensure all tests are passing 2. Remove `-SNAPSHOT` from version in `gradle.properties` in project root folder 3. Create file `gradle.properties` in `$HOME/.gradle` folder with following properties. Populate them with maven repo credentials and repo location. - ``` +``` mavenUser= mavenPassword= mavenSnapshotRepo= mavenReleaseRepo= - ``` +``` 4. Run `Gradle` task to publish artifacts to Maven Central. - ``` +``` ./gradlew publish - ``` +``` 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.1.jar - Rhino 1.7.7 2015 05 03 - js> print('Hello, World!'); - Hello, World! - js> - +``` +java -jar buildGradle/libs/rhino-1.7.7.2.jar +Rhino 1.7.7.2 2017 08 24 +js> print('Hello, World!'); +Hello, World! +js> +``` You can also embed it, as most people do. See below for more docs. ## Issues diff -Nru rhino-1.7.7.1/RELEASE-NOTES.md rhino-1.7.7.2/RELEASE-NOTES.md --- rhino-1.7.7.1/RELEASE-NOTES.md 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/RELEASE-NOTES.md 2017-09-27 22:57:34.000000000 +0000 @@ -1,3 +1,28 @@ +# Rhino 1.7.7.2 +## February 1, 2016 + +This release contains fixes for a few important bugs that have caught Rhino users out in the +field. + +* Do not throw a Java exception from array.prototype.sort() no matter how weird the user-supplied +comparator function is. This is a major difference between JavaScript and Java and has caused +us to avoid using "Arrays.sort" on JavaScript arrays. +* Fix incorrect offsets in the "DataView" class. + +It also includes several other fixes: + +* Always append a column number to V8-style stack traces. (Unfortunately it is always "0".) +* Support Object.is and Object.assign. +* Make the Symbol implementation match the spec (for VERSION_ES6 and up only). +* Avoid throwing internal Java exceptions for certain native objects in "toJSON". +* Allow subclassing of ContinuationPending. +* For VERSION_ES6 and up, sort properties in the spec-defined order (int property names +first). +* Fix stack overflow in string concatenation. +* Improve performance of ConsString.toString + +The next release is likely to be 1.7.8. + # Rhino 1.7.7.1 ## February 1, 2016 diff -Nru rhino-1.7.7.1/release-steps.txt rhino-1.7.7.2/release-steps.txt --- rhino-1.7.7.1/release-steps.txt 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/release-steps.txt 2017-09-27 22:57:34.000000000 +0000 @@ -1,10 +1,9 @@ Update version in: src/manifest build.properties + gradle.properties maven/maven-pom.xml -ant dist source-zip - Paste release notes into RELEASE_NOTES.md: git shortlog LAST_TAG.. | sed 's/^ /*/' @@ -15,6 +14,10 @@ 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. @@ -26,6 +29,7 @@ Update to new snapshot version in: src/manifest build.properties + gradle.properties maven/maven-pom.xml Update the Homebrew formula. Submit a PR for: diff -Nru rhino-1.7.7.1/src/manifest rhino-1.7.7.2/src/manifest --- rhino-1.7.7.1/src/manifest 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/manifest 2017-09-27 22:57:34.000000000 +0000 @@ -1,6 +1,6 @@ Manifest-Version: 1.0 Main-Class: org.mozilla.javascript.tools.shell.Main -Implementation-Version: 1.7.7.1 -Implementation-Title: Mozilla Rhino 1.7.7.1 +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.1/src/org/mozilla/classfile/ClassFileWriter.java rhino-1.7.7.2/src/org/mozilla/classfile/ClassFileWriter.java --- rhino-1.7.7.1/src/org/mozilla/classfile/ClassFileWriter.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/classfile/ClassFileWriter.java 2017-09-27 22:57:34.000000000 +0000 @@ -6,11 +6,13 @@ package org.mozilla.classfile; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import org.mozilla.javascript.ObjToIntMap; import org.mozilla.javascript.ObjArray; import org.mozilla.javascript.UintMap; -import java.io.*; import java.util.Arrays; /** @@ -1883,6 +1885,7 @@ case ByteCode.CASTORE: case ByteCode.SASTORE: pop(); + // fallthru case ByteCode.PUTFIELD: // pop; pop case ByteCode.IF_ICMPEQ: case ByteCode.IF_ICMPNE: @@ -1893,6 +1896,7 @@ case ByteCode.IF_ACMPEQ: case ByteCode.IF_ACMPNE: pop(); + // fallthru case ByteCode.IFEQ: // pop case ByteCode.IFNE: case ByteCode.IFLT: @@ -1934,6 +1938,7 @@ case ByteCode.DCMPL: case ByteCode.DCMPG: pop(); + // fallthru case ByteCode.INEG: // pop; push(INTEGER) case ByteCode.L2I: case ByteCode.F2I: @@ -1944,6 +1949,7 @@ case ByteCode.ARRAYLENGTH: case ByteCode.INSTANCEOF: pop(); + // fallthru case ByteCode.ICONST_M1: // push(INTEGER) case ByteCode.ICONST_0: case ByteCode.ICONST_1: @@ -1973,11 +1979,13 @@ case ByteCode.LOR: case ByteCode.LXOR: pop(); + // fallthru case ByteCode.LNEG: // pop; push(LONG) case ByteCode.I2L: case ByteCode.F2L: case ByteCode.D2L: pop(); + // fallthru case ByteCode.LCONST_0: // push(LONG) case ByteCode.LCONST_1: case ByteCode.LLOAD: @@ -1994,11 +2002,13 @@ case ByteCode.FDIV: case ByteCode.FREM: pop(); + // fallthru case ByteCode.FNEG: // pop; push(FLOAT) case ByteCode.I2F: case ByteCode.L2F: case ByteCode.D2F: pop(); + // fallthru case ByteCode.FCONST_0: // push(FLOAT) case ByteCode.FCONST_1: case ByteCode.FCONST_2: @@ -2016,11 +2026,13 @@ case ByteCode.DDIV: case ByteCode.DREM: pop(); + // fallthru case ByteCode.DNEG: // pop; push(DOUBLE) case ByteCode.I2D: case ByteCode.L2D: case ByteCode.F2D: pop(); + // fallthru case ByteCode.DCONST_0: // push(DOUBLE) case ByteCode.DCONST_1: case ByteCode.DLOAD: @@ -2187,6 +2199,7 @@ break; case ByteCode.GETFIELD: pop(); + // fallthru case ByteCode.GETSTATIC: index = getOperand(bci + 1, 2); FieldOrMethodRef f = (FieldOrMethodRef) @@ -2869,7 +2882,7 @@ case 'J' : case 'D' : --stackDiff; - // fall thru + // fallthru case 'B' : case 'S' : case 'C' : @@ -2904,9 +2917,9 @@ ++index; continue; case 'L': - // fall thru + // fallthru } - // fall thru + // fallthru case 'L' : { --stackDiff; ++count; @@ -2931,7 +2944,7 @@ case 'J' : case 'D' : ++stackDiff; - // fall thru + // fallthru case 'B' : case 'S' : case 'C' : @@ -2941,7 +2954,7 @@ case 'L' : case '[' : ++stackDiff; - // fall thru + // fallthru case 'V' : break; } diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/Arguments.java rhino-1.7.7.2/src/org/mozilla/javascript/Arguments.java --- rhino-1.7.7.1/src/org/mozilla/javascript/Arguments.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/Arguments.java 2017-09-27 22:57:34.000000000 +0000 @@ -42,6 +42,8 @@ } else { callerObj = NOT_FOUND; } + + defineProperty(SymbolKey.ITERATOR, iteratorMethod, ScriptableObject.DONTENUM); } @Override @@ -118,6 +120,10 @@ private boolean sharedWithActivation(int index) { + Context cx = Context.getContext(); + if (cx.isStrictMode()) { + return false; + } NativeFunction f = activation.function; int definedCount = f.getParamCount(); if (index < definedCount) { @@ -147,6 +153,12 @@ } @Override + public void put(String name, Scriptable start, Object value) + { + super.put(name, start, value); + } + + @Override public void delete(int index) { if (0 <= index && index < args.length) { @@ -187,6 +199,13 @@ break L0; } // #/generated# + Context cx = Context.getContext(); + if (cx.isStrictMode()) { + if (id == Id_callee || id == Id_caller) { + return super.findInstanceIdInfo(s); + } + } + if (id == 0) return super.findInstanceIdInfo(s); @@ -265,9 +284,9 @@ } @Override - Object[] getIds(boolean getAll) + Object[] getIds(boolean getNonEnumerable, boolean getSymbols) { - Object[] ids = super.getIds(getAll); + Object[] ids = super.getIds(getNonEnumerable, getSymbols); if (args.length != 0) { boolean[] present = new boolean[args.length]; int extraCount = args.length; @@ -283,7 +302,7 @@ } } } - if (!getAll) { // avoid adding args which were redefined to non-enumerable + 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; @@ -310,6 +329,9 @@ @Override protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { + if (id instanceof Scriptable) { + return super.getOwnPropertyDescriptor(cx, id); + } double d = ScriptRuntime.toNumber(id); int index = (int) d; if (d != index) { @@ -361,6 +383,52 @@ } } + // ECMAScript2015 + // 9.4.4.6 CreateUnmappedArgumentsObject(argumentsList) + // 8. Perform DefinePropertyOrThrow(obj, "caller", PropertyDescriptor {[[Get]]: %ThrowTypeError%, + // [[Set]]: %ThrowTypeError%, [[Enumerable]]: false, [[Configurable]]: false}). + // 9. Perform DefinePropertyOrThrow(obj, "callee", PropertyDescriptor {[[Get]]: %ThrowTypeError%, + // [[Set]]: %ThrowTypeError%, [[Enumerable]]: false, [[Configurable]]: false}). + void defineAttributesForStrictMode() { + Context cx = Context.getContext(); + if (!cx.isStrictMode()) { + return; + } + setGetterOrSetter("caller", 0, new ThrowTypeError("caller"), true); + setGetterOrSetter("caller", 0, new ThrowTypeError("caller"), false); + setGetterOrSetter("callee", 0, new ThrowTypeError("callee"), true); + setGetterOrSetter("callee", 0, new ThrowTypeError("callee"), false); + setAttributes("caller", DONTENUM | PERMANENT); + setAttributes("callee", DONTENUM | PERMANENT); + callerObj = null; + 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 class ThrowTypeError extends BaseFunction { + private String propertyName; + + ThrowTypeError(String propertyName) { + this.propertyName = propertyName; + } + + @Override + public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + throw ScriptRuntime.typeError1("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 diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/ArrowFunction.java rhino-1.7.7.2/src/org/mozilla/javascript/ArrowFunction.java --- rhino-1.7.7.1/src/org/mozilla/javascript/ArrowFunction.java 1970-01-01 00:00:00.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/ArrowFunction.java 2017-09-27 22:57:34.000000000 +0000 @@ -0,0 +1,75 @@ +/* -*- 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; + +/** + * The class for Arrow Function Definitions + * EcmaScript 6 Rev 14, March 8, 2013 Draft spec , 13.2 + */ +public class ArrowFunction extends BaseFunction { + + static final long serialVersionUID = -7377989503697220633L; + + private final Callable targetFunction; + private final Scriptable boundThis; + + public ArrowFunction(Context cx, Scriptable scope, Callable targetFunction, Scriptable boundThis) + { + this.targetFunction = targetFunction; + this.boundThis = boundThis; + + ScriptRuntime.setFunctionProtoAndParent(this, scope); + + Function thrower = ScriptRuntime.typeErrorThrower(); + 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.preventExtensions(); + + this.defineOwnProperty(cx, "caller", throwing, false); + this.defineOwnProperty(cx, "arguments", throwing, false); + } + + @Override + public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) + { + Scriptable callThis = boundThis != null ? boundThis : ScriptRuntime.getTopCallScope(cx); + return targetFunction.call(cx, scope, callThis, args); + } + + @Override + public Scriptable construct(Context cx, Scriptable scope, Object[] args) { + throw ScriptRuntime.typeError1("msg.not.ctor", decompile(0, 0)); + } + + @Override + public boolean hasInstance(Scriptable instance) { + if (targetFunction instanceof Function) { + return ((Function) targetFunction).hasInstance(instance); + } + throw ScriptRuntime.typeError0("msg.not.ctor"); + } + + @Override + public int getLength() { + if (targetFunction instanceof BaseFunction) { + return ((BaseFunction) targetFunction).getLength(); + } + return 0; + } + + @Override + String decompile(int indent, int flags) + { + if (targetFunction instanceof BaseFunction) { + return ((BaseFunction)targetFunction).decompile(indent, flags); + } + return super.decompile(indent, flags); + } +} diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/ast/ArrayComprehensionLoop.java rhino-1.7.7.2/src/org/mozilla/javascript/ast/ArrayComprehensionLoop.java --- rhino-1.7.7.1/src/org/mozilla/javascript/ast/ArrayComprehensionLoop.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/ast/ArrayComprehensionLoop.java 2017-09-27 22:57:34.000000000 +0000 @@ -53,7 +53,7 @@ + (isForEach()?"each ":"") + "(" + iterator.toSource(0) - + " in " + + (isForOf()?" of ":" in ") + iteratedObject.toSource(0) + ")"; } diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/ast/AstRoot.java rhino-1.7.7.2/src/org/mozilla/javascript/ast/AstRoot.java --- rhino-1.7.7.1/src/org/mozilla/javascript/ast/AstRoot.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/ast/AstRoot.java 2017-09-27 22:57:34.000000000 +0000 @@ -25,7 +25,6 @@ public class AstRoot extends ScriptNode { private SortedSet comments; - private boolean inStrictMode; { type = Token.SCRIPT; @@ -76,14 +75,6 @@ comment.setParent(this); } - public void setInStrictMode(boolean inStrictMode) { - this.inStrictMode = inStrictMode; - } - - public boolean isInStrictMode() { - return inStrictMode; - } - /** * Visits the comment nodes in the order they appear in the source code. * The comments are not visited by the {@link #visit} function - you must diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/ast/ForInLoop.java rhino-1.7.7.2/src/org/mozilla/javascript/ast/ForInLoop.java --- rhino-1.7.7.1/src/org/mozilla/javascript/ast/ForInLoop.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/ast/ForInLoop.java 2017-09-27 22:57:34.000000000 +0000 @@ -9,10 +9,12 @@ import org.mozilla.javascript.Token; /** - * For-in or for-each-in statement. Node type is {@link Token#FOR}.

+ * For-in or for-each-in or for-of statement. Node type is {@link Token#FOR}.

* *

for [each] ( LeftHandSideExpression in Expression ) Statement
*
for [each] ( var VariableDeclarationNoIn in Expression ) Statement
+ *
for ( LeftHandSideExpression of Expression ) Statement
+ *
for ( ForDeclaration of Expression ) Statement
*/ public class ForInLoop extends Loop { @@ -21,6 +23,7 @@ protected int inPosition = -1; protected int eachPosition = -1; protected boolean isForEach; + protected boolean isForOf; { type = Token.FOR; @@ -45,7 +48,7 @@ } /** - * Sets loop iterator expression: the part before the "in" keyword. + * Sets loop iterator expression: the part before the "in" or "of" keyword. * Also sets its parent to this node. * @throws IllegalArgumentException if {@code iterator} is {@code null} */ @@ -87,15 +90,29 @@ } /** - * Returns position of "in" keyword + * Returns whether the loop is a for-of loop + */ + public boolean isForOf() { + return isForOf; + } + + /** + * Sets whether the loop is a for-each loop + */ + public void setIsForOf(boolean isForOf) { + this.isForOf = isForOf; + } + + /** + * Returns position of "in" or "of" keyword */ public int getInPosition() { return inPosition; } /** - * Sets position of "in" keyword - * @param inPosition position of "in" keyword, + * Sets position of "in" or "of" keyword + * @param inPosition position of "in" or "of" keyword, * or -1 if not present (e.g. in presence of a syntax error) */ public void setInPosition(int inPosition) { @@ -128,7 +145,11 @@ } sb.append("("); sb.append(iterator.toSource(0)); - sb.append(" in "); + if (isForOf) { + sb.append(" of "); + } else { + sb.append(" in "); + } sb.append(iteratedObject.toSource(0)); sb.append(") "); if (body.getType() == Token.BLOCK) { diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/ast/FunctionNode.java rhino-1.7.7.2/src/org/mozilla/javascript/ast/FunctionNode.java --- rhino-1.7.7.1/src/org/mozilla/javascript/ast/FunctionNode.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/ast/FunctionNode.java 2017-09-27 22:57:34.000000000 +0000 @@ -63,6 +63,7 @@ public static final int FUNCTION_STATEMENT = 1; public static final int FUNCTION_EXPRESSION = 2; public static final int FUNCTION_EXPRESSION_STATEMENT = 3; + public static final int ARROW_FUNCTION = 4; public static enum Form { FUNCTION, GETTER, SETTER, METHOD } @@ -376,9 +377,12 @@ @Override public String toSource(int depth) { StringBuilder sb = new StringBuilder(); + boolean isArrow = functionType == ARROW_FUNCTION; if (!isMethod()) { sb.append(makeIndent(depth)); - sb.append("function"); + if (!isArrow) { + sb.append("function"); + } } if (functionName != null) { sb.append(" "); @@ -386,11 +390,18 @@ } if (params == null) { sb.append("() "); + } else if (isArrow && lp == -1) { + // no paren + printList(params, sb); + sb.append(" "); } else { sb.append("("); printList(params, sb); sb.append(") "); } + if (isArrow) { + sb.append("=> "); + } if (isExpressionClosure) { AstNode body = getBody(); if (body.getLastChild() instanceof ReturnStatement) { diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/ast/GeneratorExpressionLoop.java rhino-1.7.7.2/src/org/mozilla/javascript/ast/GeneratorExpressionLoop.java --- rhino-1.7.7.1/src/org/mozilla/javascript/ast/GeneratorExpressionLoop.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/ast/GeneratorExpressionLoop.java 2017-09-27 22:57:34.000000000 +0000 @@ -44,7 +44,7 @@ + (isForEach()?"each ":"") + "(" + iterator.toSource(0) - + " in " + + (isForOf()?" of ":" in ") + iteratedObject.toSource(0) + ")"; } diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/ast/ScriptNode.java rhino-1.7.7.2/src/org/mozilla/javascript/ast/ScriptNode.java --- rhino-1.7.7.1/src/org/mozilla/javascript/ast/ScriptNode.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/ast/ScriptNode.java 2017-09-27 22:57:34.000000000 +0000 @@ -36,6 +36,7 @@ private Object compilerData; private int tempNumber = 0; + private boolean inStrictMode; { // during parsing, a ScriptNode or FunctionNode's top scope is itself @@ -305,6 +306,14 @@ return "$" + tempNumber++; } + public void setInStrictMode(boolean inStrictMode) { + this.inStrictMode = inStrictMode; + } + + public boolean isInStrictMode() { + return inStrictMode; + } + @Override public void visit(NodeVisitor v) { if (v.visit(this)) { diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/BaseFunction.java rhino-1.7.7.2/src/org/mozilla/javascript/BaseFunction.java --- rhino-1.7.7.1/src/org/mozilla/javascript/BaseFunction.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/BaseFunction.java 2017-09-27 22:57:34.000000000 +0000 @@ -48,7 +48,7 @@ @Override public String getTypeOf() { - return avoidObjectDetection() ? "undefined" : "function"; + return avoidObjectDetection() ? "undefined" : "function"; } /** diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/CodeGenerator.java rhino-1.7.7.2/src/org/mozilla/javascript/CodeGenerator.java --- rhino-1.7.7.1/src/org/mozilla/javascript/CodeGenerator.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/CodeGenerator.java 2017-09-27 22:57:34.000000000 +0000 @@ -6,7 +6,6 @@ package org.mozilla.javascript; -import org.mozilla.javascript.ast.AstRoot; import org.mozilla.javascript.ast.ScriptNode; import org.mozilla.javascript.ast.Jump; import org.mozilla.javascript.ast.FunctionNode; @@ -71,10 +70,11 @@ } else { scriptOrFn = tree; } + itsData = new InterpreterData(compilerEnv.getLanguageVersion(), scriptOrFn.getSourceName(), encodedSource, - ((AstRoot)tree).isInStrictMode()); + scriptOrFn.isInStrictMode()); itsData.topLevel = true; if (returnFunction) { @@ -100,6 +100,9 @@ addIcode(Icode_GENERATOR); addUint16(theFunction.getBaseLineno() & 0xFFFF); } + if (theFunction.isInStrictMode()) { + itsData.isStrict = true; + } generateICodeFromTree(theFunction.getLastChild()); } @@ -270,8 +273,8 @@ case Token.EMPTY: case Token.WITH: updateLineNumber(node); + // fallthru case Token.SCRIPT: - // fall through while (child != null) { visitStatement(child, initialStackDepth); child = child.getNext(); @@ -475,6 +478,7 @@ 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); @@ -504,7 +508,8 @@ 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) { + if (fn.getFunctionType() != FunctionNode.FUNCTION_EXPRESSION && + fn.getFunctionType() != FunctionNode.ARROW_FUNCTION) { throw Kit.codeBug(); } addIndexOp(Icode_CLOSURE_EXPR, fnIndex); diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/commonjs/module/provider/UrlConnectionSecurityDomainProvider.java rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/provider/UrlConnectionSecurityDomainProvider.java --- rhino-1.7.7.1/src/org/mozilla/javascript/commonjs/module/provider/UrlConnectionSecurityDomainProvider.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/commonjs/module/provider/UrlConnectionSecurityDomainProvider.java 2017-09-27 22:57:34.000000000 +0000 @@ -12,7 +12,7 @@ * Interface for URL connection based security domain providers. Used by * {@link UrlModuleSourceProvider} to create Rhino security domain objects for * newly loaded scripts (see {@link Context#compileReader(java.io.Reader, - * String, int, Object)}) based on the properties obtainable through their URL + * String, int, Object)}, in {@link Context}) based on the properties obtainable through their URL * connection. * @author Attila Szegedi * @version $Id: UrlConnectionSecurityDomainProvider.java,v 1.3 2011/04/07 20:26:12 hannes%helma.at Exp $ diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/ConsString.java rhino-1.7.7.2/src/org/mozilla/javascript/ConsString.java --- rhino-1.7.7.1/src/org/mozilla/javascript/ConsString.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/ConsString.java 2017-09-27 22:57:34.000000000 +0000 @@ -7,6 +7,7 @@ package org.mozilla.javascript; import java.io.Serializable; +import java.util.ArrayList; /** *

This class represents a string composed of two components, each of which @@ -43,10 +44,6 @@ if (str2 instanceof ConsString) { depth += ((ConsString)str2).depth; } - // Don't let it grow too deep, can cause stack overflows - if (depth > 2000) { - flatten(); - } } // Replace with string representation when serializing @@ -62,7 +59,19 @@ private synchronized String flatten() { if (depth > 0) { StringBuilder b = new StringBuilder(length); - appendTo(b); + ArrayList buffer = new ArrayList(); + buffer.add(s2); + buffer.add(s1); + while(!buffer.isEmpty()) { + CharSequence next = buffer.remove(buffer.size() - 1); + if (next instanceof ConsString) { + ConsString casted = (ConsString) next; + buffer.add(casted.s2); + buffer.add(casted.s1); + } else { + b.append(next); + } + } s1 = b.toString(); s2 = ""; depth = 0; @@ -70,19 +79,6 @@ return (String)s1; } - private synchronized void appendTo(StringBuilder b) { - appendFragment(s1, b); - appendFragment(s2, b); - } - - private static void appendFragment(CharSequence s, StringBuilder b) { - if (s instanceof ConsString) { - ((ConsString)s).appendTo(b); - } else { - b.append(s); - } - } - public int length() { return length; } diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/ContextFactory.java rhino-1.7.7.2/src/org/mozilla/javascript/ContextFactory.java --- rhino-1.7.7.1/src/org/mozilla/javascript/ContextFactory.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/ContextFactory.java 2017-09-27 22:57:34.000000000 +0000 @@ -282,6 +282,12 @@ 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; } // It is a bug to call the method with unknown featureIndex throw new IllegalArgumentException(String.valueOf(featureIndex)); diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/Context.java rhino-1.7.7.2/src/org/mozilla/javascript/Context.java --- rhino-1.7.7.1/src/org/mozilla/javascript/Context.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/Context.java 2017-09-27 22:57:34.000000000 +0000 @@ -10,12 +10,21 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.io.*; +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.URI; import java.net.URL; -import java.util.*; +import java.util.Enumeration; +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; @@ -292,6 +301,22 @@ */ 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. + * @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 + */ + public static final int FEATURE_ENUMERATE_IDS_FIRST = 16; + public static final String languageVersionProperty = "language version"; public static final String errorReporterProperty = "error reporter"; @@ -1309,7 +1334,7 @@ // Annotate so we can check later to ensure no java code in // intervening frames isContinuationsTopCall = true; - return ScriptRuntime.doTopCall(function, this, scope, scope, args); + return ScriptRuntime.doTopCall(function, this, scope, scope, args, isTopLevelStrict); } /** @@ -1372,8 +1397,7 @@ * @return whether the source is ready for compilation * @since 1.4 Release 2 */ - public final boolean stringIsCompilableUnit(String source) - { + public final boolean stringIsCompilableUnit(String source) { boolean errorseen = false; CompilerEnvirons compilerEnv = new CompilerEnvirons(); compilerEnv.initFromContext(this); @@ -1389,10 +1413,7 @@ // Return false only if an error occurred as a result of reading past // the end of the file, i.e. if the source could be fixed by // appending more source. - if (errorseen && p.eof()) - return false; - else - return true; + return !(errorseen && p.eof()); } /** @@ -2483,6 +2504,9 @@ if (returnFunction) { p.calledByCompileFunction = true; } + if (isStrictMode()) { + p.setDefaultUseStrictDirective(true); + } AstRoot ast; if (sourceString != null) { ast = p.parse(sourceString, sourceName, lineno); @@ -2669,6 +2693,10 @@ activationNames.remove(name); } + public final boolean isStrictMode() { + return isTopLevelStrict || (currentActivationCall != null && currentActivationCall.isStrict); + } + private static String implementationVersion; private final ContextFactory factory; @@ -2738,4 +2766,6 @@ // Generate an observer count on compiled code public boolean generateObserverCount = false; + + boolean isTopLevelStrict; } diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/ContinuationPending.java rhino-1.7.7.2/src/org/mozilla/javascript/ContinuationPending.java --- rhino-1.7.7.1/src/org/mozilla/javascript/ContinuationPending.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/ContinuationPending.java 2017-09-27 22:57:34.000000000 +0000 @@ -26,9 +26,10 @@ * users of the API should get continuations created on their behalf by * calling {@link org.mozilla.javascript.Context#executeScriptWithContinuations(Script, Scriptable)} * and {@link org.mozilla.javascript.Context#callFunctionWithContinuations(Callable, Scriptable, Object[])} + * Creating subclasses allowed. * @param continuationState Internal Continuation object */ - ContinuationPending(NativeContinuation continuationState) { + protected ContinuationPending(NativeContinuation continuationState) { this.continuationState = continuationState; } @@ -43,6 +44,14 @@ } /** + * Set continuation object. Allows subclasses to modify the internal state. + * @param continuation object + */ + public void setContinuation(NativeContinuation continuation) { + this.continuationState = continuation; + } + + /** * @return internal continuation state */ NativeContinuation getContinuationState() { diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/Decompiler.java rhino-1.7.7.2/src/org/mozilla/javascript/Decompiler.java --- rhino-1.7.7.1/src/org/mozilla/javascript/Decompiler.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/Decompiler.java 2017-09-27 22:57:34.000000000 +0000 @@ -86,8 +86,10 @@ int markFunctionStart(int functionType) { int savedOffset = getCurrentOffset(); - addToken(Token.FUNCTION); - append((char)functionType); + if (functionType != FunctionNode.ARROW_FUNCTION) { + addToken(Token.FUNCTION); + append((char)functionType); + } return savedOffset; } @@ -792,6 +794,10 @@ 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: " + diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/DToA.java rhino-1.7.7.2/src/org/mozilla/javascript/DToA.java --- rhino-1.7.7.1/src/org/mozilla/javascript/DToA.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/DToA.java 2017-09-27 22:57:34.000000000 +0000 @@ -602,7 +602,7 @@ break; case 2: leftright = false; - /* no break */ + /* fallthru */ case 4: if (ndigits <= 0) ndigits = 1; @@ -610,7 +610,7 @@ break; case 3: leftright = false; - /* no break */ + /* fallthru */ case 5: i = ndigits + k + 1; ilim = i; @@ -1168,7 +1168,7 @@ case DTOSTR_EXPONENTIAL: // JS_ASSERT(precision > 0); minNDigits = precision; - /* Fall through */ + /* fallthru */ case DTOSTR_STANDARD_EXPONENTIAL: exponentialNotation = true; break; diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/ES6Iterator.java rhino-1.7.7.2/src/org/mozilla/javascript/ES6Iterator.java --- rhino-1.7.7.1/src/org/mozilla/javascript/ES6Iterator.java 1970-01-01 00:00:00.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/ES6Iterator.java 2017-09-27 22:57:34.000000000 +0000 @@ -0,0 +1,138 @@ +/* -*- 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 abstract class ES6Iterator extends IdScriptableObject { + + static void init(ScriptableObject scope, boolean sealed, IdScriptableObject prototype, String tag) { + if (scope != null) { + prototype.setParentScope(scope); + prototype.setPrototype(getObjectPrototype(scope)); + } + prototype.activatePrototypeMap(MAX_PROTOTYPE_ID); + if (sealed) { + prototype.sealObject(); + } + + // Need to access Iterator prototype when constructing + // Iterator instances, but don't have a iterator constructor + // to use to find the prototype. Use the "associateValue" + // approach instead. + if (scope != null) { + scope.associateValue(tag, prototype); + } + } + + protected boolean exhausted = false; + + ES6Iterator() {} + + ES6Iterator(Scriptable scope) { + // 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. + Scriptable top = ScriptableObject.getTopLevelScope(scope); + this.setParentScope(top); + IdScriptableObject prototype = (IdScriptableObject) + ScriptableObject.getTopScopeValue(top, getTag()); + setPrototype(prototype); + } + + @Override + protected void initPrototypeId(int id) + { + switch (id) { + case Id_next: + initPrototypeMethod(getTag(), id, NEXT_METHOD, 0); + return; + case Id_iterator: + initPrototypeMethod(getTag(), id, SymbolKey.ITERATOR, "[Symbol.iterator]", 0); + return; + case Id_toStringTag: + initPrototypeValue(Id_toStringTag, SymbolKey.TO_STRING_TAG, getClassName(), DONTENUM | READONLY); + return; + default: throw new IllegalArgumentException(String.valueOf(id)); + } + } + + @Override + public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) + { + if (!f.hasTag(getTag())) { + return super.execIdCall(f, cx, scope, thisObj, args); + } + int id = f.methodId(); + + if (!(thisObj instanceof ES6Iterator)) + throw incompatibleCallError(f); + + ES6Iterator iterator = (ES6Iterator) thisObj; + + switch (id) { + case Id_next: + return iterator.next(cx, scope); + case Id_iterator: + return iterator; + default: + throw new IllegalArgumentException(String.valueOf(id)); + } + } + + @Override + protected int findPrototypeId(Symbol k) { + if (SymbolKey.ITERATOR.equals(k)) { + return Id_iterator; + } else if (SymbolKey.TO_STRING_TAG.equals(k)) { + return Id_toStringTag; + } + return 0; + } + + @Override + protected int findPrototypeId(String s) { + if ("next".equals(s)) { + return Id_next; + } + return 0; + } + + abstract protected boolean isDone(Context cx, Scriptable scope); + + abstract protected Object nextValue(Context cx, Scriptable scope); + + protected Object next(Context cx, Scriptable scope) { + Object value = Undefined.instance; + boolean done = isDone(cx, scope) || this.exhausted; + if (!done) { + value = nextValue(cx, scope); + } else { + this.exhausted = true; + } + return makeIteratorResult(cx, scope, done, value); + } + + abstract protected String getTag(); + + // 25.1.1.3 The IteratorResult Interface + private Scriptable makeIteratorResult(Context cx, Scriptable scope, boolean done, Object value) { + 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; + + public static final String NEXT_METHOD = "next"; + public static final String DONE_PROPERTY = "done"; + public static final String VALUE_PROPERTY = "value"; +} diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/IdFunctionObjectES6.java rhino-1.7.7.2/src/org/mozilla/javascript/IdFunctionObjectES6.java --- rhino-1.7.7.1/src/org/mozilla/javascript/IdFunctionObjectES6.java 1970-01-01 00:00:00.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/IdFunctionObjectES6.java 2017-09-27 22:57:34.000000000 +0000 @@ -0,0 +1,44 @@ +package org.mozilla.javascript; + +public class IdFunctionObjectES6 extends IdFunctionObject{ + + 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 boolean myLength = true; + private boolean myName = true; + + @Override + protected int findInstanceIdInfo(String s) { + if (s.equals("length")) return instanceIdInfo(READONLY | DONTENUM, Id_length); + else if (s.equals("name")) return instanceIdInfo(READONLY | DONTENUM, Id_name); + return super.findInstanceIdInfo(s); + } + + @Override + protected Object getInstanceIdValue(int id) { + if (id == Id_length && !myLength) { + return NOT_FOUND; + } else if (id == Id_name && !myName) { + return NOT_FOUND; + } + return super.getInstanceIdValue(id); + } + + @Override + protected void setInstanceIdValue(int id, Object value) { + if (id == Id_length && value == NOT_FOUND) { + this.myLength = false; + return; + } else if (id == Id_name && value == NOT_FOUND) { + this.myName = false; + return; + } + super.setInstanceIdValue(id, value); + } +} diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/IdScriptableObject.java rhino-1.7.7.2/src/org/mozilla/javascript/IdScriptableObject.java --- rhino-1.7.7.1/src/org/mozilla/javascript/IdScriptableObject.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/IdScriptableObject.java 2017-09-27 22:57:34.000000000 +0000 @@ -6,7 +6,10 @@ package org.mozilla.javascript; -import java.io.*; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; /** Base class for native object implementation that uses IdFunctionObject to export its methods to script via .prototype object. @@ -87,6 +90,30 @@ 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(); + ScriptableObject.checkValidAttributes(attributes); + 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"); + } + constructor = (IdFunctionObject)value; + constructorAttrs = (short)attributes; + return; + } + + initSlot(id, "", value, attributes); + } + private void initSlot(int id, String name, Object value, int attributes) { @@ -136,6 +163,11 @@ return obj.findPrototypeId(name); } + final int findId(Symbol key) + { + return obj.findPrototypeId(key); + } + final boolean has(int id) { Object[] array = valueArray; @@ -188,7 +220,15 @@ { ensureId(id); int attr = attributeArray[id - 1]; - if ((attr & PERMANENT) == 0) { + // 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); + } + } else { int valueSlot = (id - 1) * SLOT_SPAN; synchronized (this) { valueArray[valueSlot] = NOT_FOUND; @@ -326,6 +366,28 @@ return super.has(name, start); } + + @Override + public boolean has(Symbol key, Scriptable start) + { + int info = findInstanceIdInfo(key); + if (info != 0) { + int attr = (info >>> 16); + if ((attr & PERMANENT) != 0) { + return true; + } + int id = (info & 0xFFFF); + return NOT_FOUND != getInstanceIdValue(id); + } + if (prototypeValues != null) { + int id = prototypeValues.findId(key); + if (id != 0) { + return prototypeValues.has(id); + } + } + return super.has(key, start); + } + @Override public Object get(String name, Scriptable start) { @@ -352,6 +414,29 @@ } @Override + public Object get(Symbol key, Scriptable start) + { + Object value = super.get(key, start); + if (value != NOT_FOUND) { + return value; + } + int info = findInstanceIdInfo(key); + if (info != 0) { + int id = (info & 0xFFFF); + value = getInstanceIdValue(id); + if (value != NOT_FOUND) return value; + } + if (prototypeValues != null) { + int id = prototypeValues.findId(key); + if (id != 0) { + value = prototypeValues.get(id); + if (value != NOT_FOUND) return value; + } + } + return NOT_FOUND; + } + + @Override public void put(String name, Scriptable start, Object value) { int info = findInstanceIdInfo(name); @@ -387,6 +472,39 @@ } @Override + 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"); + } + int attr = (info >>> 16); + if ((attr & READONLY) == 0) { + if (start == this) { + int id = (info & 0xFFFF); + setInstanceIdValue(id, value); + } + else { + ensureSymbolScriptable(start).put(key, start, value); + } + } + return; + } + if (prototypeValues != null) { + int id = prototypeValues.findId(key); + if (id != 0) { + if (start == this && isSealed()) { + throw Context.reportRuntimeError0("msg.modify.sealed"); + } + prototypeValues.set(id, start, value); + return; + } + } + super.put(key, start, value); + } + + @Override public void delete(String name) { int info = findInstanceIdInfo(name); @@ -394,7 +512,13 @@ // Let the super class to throw exceptions for sealed objects if (!isSealed()) { int attr = (info >>> 16); - if ((attr & PERMANENT) == 0) { + // non-configurable + if ((attr & PERMANENT) != 0) { + Context cx = Context.getContext(); + if (cx.isStrictMode()) { + throw ScriptRuntime.typeError1("msg.delete.property.with.configurable.false", name); + } + } else { int id = (info & 0xFFFF); setInstanceIdValue(id, NOT_FOUND); } @@ -414,6 +538,39 @@ } @Override + public void delete(Symbol key) + { + int info = findInstanceIdInfo(key); + if (info != 0) { + // Let the super class to throw exceptions for sealed objects + if (!isSealed()) { + int attr = (info >>> 16); + // non-configurable + if ((attr & PERMANENT) != 0) { + Context cx = Context.getContext(); + if (cx.isStrictMode()) { + throw ScriptRuntime.typeError0("msg.delete.property.with.configurable.false"); + } + } else { + int id = (info & 0xFFFF); + setInstanceIdValue(id, NOT_FOUND); + } + return; + } + } + if (prototypeValues != null) { + int id = prototypeValues.findId(key); + if (id != 0) { + if (!isSealed()) { + prototypeValues.delete(id); + } + return; + } + } + super.delete(key); + } + + @Override public int getAttributes(String name) { int info = findInstanceIdInfo(name); @@ -454,12 +611,12 @@ } @Override - Object[] getIds(boolean getAll) + Object[] getIds(boolean getNonEnumerable, boolean getSymbols) { - Object[] result = super.getIds(getAll); + Object[] result = super.getIds(getNonEnumerable, getSymbols); if (prototypeValues != null) { - result = prototypeValues.getNames(getAll, result); + result = prototypeValues.getNames(getNonEnumerable, result); } int maxInstanceId = getMaxInstanceId(); @@ -477,7 +634,7 @@ continue; } } - if (getAll || (attr & DONTENUM) == 0) { + if (getNonEnumerable || (attr & DONTENUM) == 0) { if (count == 0) { // Need extra room for no more then [1..id] names ids = new Object[id]; @@ -524,6 +681,16 @@ return 0; } + /** + * 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) + { + return 0; + } + /** Map id back to property name it defines. */ protected String getInstanceIdName(int id) @@ -611,12 +778,30 @@ } } - public final void initPrototypeMethod(Object tag, int id, String name, + 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) { Scriptable scope = ScriptableObject.getTopLevelScope(this); - IdFunctionObject f = newIdFunction(tag, id, name, arity, scope); - prototypeValues.initValue(id, name, f, DONTENUM); + 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) + { + 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) @@ -636,6 +821,12 @@ prototypeValues.initValue(id, name, value, attributes); } + public final void initPrototypeValue(int id, Symbol key, Object value, + int attributes) + { + prototypeValues.initValue(id, key, value, attributes); + } + protected void initPrototypeId(int id) { throw new IllegalStateException(String.valueOf(id)); @@ -646,6 +837,11 @@ throw new IllegalStateException(name); } + protected int findPrototypeId(Symbol key) + { + return 0; + } + protected void fillConstructorProperties(IdFunctionObject ctor) { } @@ -688,10 +884,15 @@ private IdFunctionObject newIdFunction(Object tag, int id, String name, int arity, Scriptable scope) { - IdFunctionObject f = new IdFunctionObject(this, tag, id, name, arity, - scope); - if (isSealed()) { f.sealObject(); } - return f; + IdFunctionObject function = null; + if (Context.getContext().getLanguageVersion() < Context.VERSION_ES6) { + function = new IdFunctionObject(this, tag, id, name, arity, scope); + } else { + function = new IdFunctionObjectES6(this, tag, id, name, arity, scope); + } + + if (isSealed()) { function.sealObject(); } + return function; } @Override @@ -749,8 +950,12 @@ @Override protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { ScriptableObject desc = super.getOwnPropertyDescriptor(cx, id); - if (desc == null && id instanceof String) { - desc = getBuiltInDescriptor((String) id); + if (desc == null) { + if (id instanceof String) { + desc = getBuiltInDescriptor((String) id); + } else if (ScriptRuntime.isSymbol(id)) { + desc = getBuiltInDescriptor(((NativeSymbol)id).getKey()); + } } return desc; } @@ -776,6 +981,26 @@ 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; + + 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); } } diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/InterpretedFunction.java rhino-1.7.7.2/src/org/mozilla/javascript/InterpretedFunction.java --- rhino-1.7.7.1/src/org/mozilla/javascript/InterpretedFunction.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/InterpretedFunction.java 2017-09-27 22:57:34.000000000 +0000 @@ -75,7 +75,7 @@ * Create function embedded in script or another function. */ static InterpretedFunction createFunction(Context cx, Scriptable scope, - InterpretedFunction parent, + InterpretedFunction parent, int index) { InterpretedFunction f = new InterpretedFunction(parent, index); @@ -104,7 +104,7 @@ Object[] args) { if (!ScriptRuntime.hasTopCall(cx)) { - return ScriptRuntime.doTopCall(this, cx, scope, thisObj, args); + return ScriptRuntime.doTopCall(this, cx, scope, thisObj, args, idata.isStrict); } return Interpreter.interpret(this, cx, scope, thisObj, args); } @@ -118,7 +118,7 @@ if (!ScriptRuntime.hasTopCall(cx)) { // It will go through "call" path. but they are equivalent return ScriptRuntime.doTopCall( - this, cx, scope, scope, ScriptRuntime.emptyArgs); + this, cx, scope, scope, ScriptRuntime.emptyArgs, idata.isStrict); } return Interpreter.interpret( this, cx, scope, scope, ScriptRuntime.emptyArgs); diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/InterpreterData.java rhino-1.7.7.2/src/org/mozilla/javascript/InterpreterData.java --- rhino-1.7.7.1/src/org/mozilla/javascript/InterpreterData.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/InterpreterData.java 2017-09-27 22:57:34.000000000 +0000 @@ -34,7 +34,7 @@ this.languageVersion = parent.languageVersion; this.itsSourceFile = parent.itsSourceFile; this.encodedSource = parent.encodedSource; - + this.isStrict = parent.isStrict; init(); } diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/Interpreter.java rhino-1.7.7.2/src/org/mozilla/javascript/Interpreter.java --- rhino-1.7.7.1/src/org/mozilla/javascript/Interpreter.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/Interpreter.java 2017-09-27 22:57:34.000000000 +0000 @@ -853,7 +853,7 @@ Scriptable scope, Object[] args) { if (!ScriptRuntime.hasTopCall(cx)) { - return ScriptRuntime.doTopCall(c, cx, scope, null, args); + return ScriptRuntime.doTopCall(c, cx, scope, null, args, cx.isTopLevelStrict); } Object arg; @@ -1648,7 +1648,8 @@ } case Token.ENUM_INIT_KEYS : case Token.ENUM_INIT_VALUES : - case Token.ENUM_INIT_ARRAY : { + 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; @@ -1657,6 +1658,8 @@ ? 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; @@ -1711,9 +1714,14 @@ stack[indexReg] = frame.scope; continue Loop; case Icode_CLOSURE_EXPR : - stack[++stackTop] = InterpretedFunction.createFunction(cx, frame.scope, - frame.fnOrScript, - indexReg); + 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); @@ -2750,8 +2758,11 @@ scope = fnOrScript.getParentScope(); if (useActivation) { - scope = ScriptRuntime.createFunctionActivation( - fnOrScript, scope, args); + 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; diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/IRFactory.java rhino-1.7.7.2/src/org/mozilla/javascript/IRFactory.java --- rhino-1.7.7.1/src/org/mozilla/javascript/IRFactory.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/IRFactory.java 2017-09-27 22:57:34.000000000 +0000 @@ -6,7 +6,67 @@ package org.mozilla.javascript; -import org.mozilla.javascript.ast.*; +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.Block; +import org.mozilla.javascript.ast.BreakStatement; +import org.mozilla.javascript.ast.CatchClause; +import org.mozilla.javascript.ast.ConditionalExpression; +import org.mozilla.javascript.ast.ContinueStatement; +import org.mozilla.javascript.ast.DestructuringForm; +import org.mozilla.javascript.ast.DoLoop; +import org.mozilla.javascript.ast.ElementGet; +import org.mozilla.javascript.ast.EmptyExpression; +import org.mozilla.javascript.ast.ExpressionStatement; +import org.mozilla.javascript.ast.ForInLoop; +import org.mozilla.javascript.ast.ForLoop; +import org.mozilla.javascript.ast.FunctionCall; +import org.mozilla.javascript.ast.FunctionNode; +import org.mozilla.javascript.ast.GeneratorExpression; +import org.mozilla.javascript.ast.GeneratorExpressionLoop; +import org.mozilla.javascript.ast.IfStatement; +import org.mozilla.javascript.ast.InfixExpression; +import org.mozilla.javascript.ast.Jump; +import org.mozilla.javascript.ast.Label; +import org.mozilla.javascript.ast.LabeledStatement; +import org.mozilla.javascript.ast.LetNode; +import org.mozilla.javascript.ast.Loop; +import org.mozilla.javascript.ast.Name; +import org.mozilla.javascript.ast.NewExpression; +import org.mozilla.javascript.ast.NumberLiteral; +import org.mozilla.javascript.ast.ObjectLiteral; +import org.mozilla.javascript.ast.ObjectProperty; +import org.mozilla.javascript.ast.ParenthesizedExpression; +import org.mozilla.javascript.ast.PropertyGet; +import org.mozilla.javascript.ast.RegExpLiteral; +import org.mozilla.javascript.ast.ReturnStatement; +import org.mozilla.javascript.ast.Scope; +import org.mozilla.javascript.ast.ScriptNode; +import org.mozilla.javascript.ast.StringLiteral; +import org.mozilla.javascript.ast.SwitchCase; +import org.mozilla.javascript.ast.SwitchStatement; +import org.mozilla.javascript.ast.Symbol; +import org.mozilla.javascript.ast.ThrowStatement; +import org.mozilla.javascript.ast.TryStatement; +import org.mozilla.javascript.ast.UnaryExpression; +import org.mozilla.javascript.ast.VariableDeclaration; +import org.mozilla.javascript.ast.VariableInitializer; +import org.mozilla.javascript.ast.WhileLoop; +import org.mozilla.javascript.ast.WithStatement; +import org.mozilla.javascript.ast.XmlDotQuery; +import org.mozilla.javascript.ast.XmlElemRef; +import org.mozilla.javascript.ast.XmlExpression; +import org.mozilla.javascript.ast.XmlFragment; +import org.mozilla.javascript.ast.XmlLiteral; +import org.mozilla.javascript.ast.XmlMemberGet; +import org.mozilla.javascript.ast.XmlPropRef; +import org.mozilla.javascript.ast.XmlRef; +import org.mozilla.javascript.ast.XmlString; +import org.mozilla.javascript.ast.Yield; import java.util.List; import java.util.ArrayList; @@ -270,7 +330,11 @@ defineSymbol(Token.LET, name, false); iterators[i] = init; - decompiler.addToken(Token.IN); + if (acl.isForOf()) { + decompiler.addName("of "); + } else { + decompiler.addToken(Token.IN); + } iteratedObjs[i] = transform(acl.getIteratedObject()); decompiler.addToken(Token.RP); } @@ -305,7 +369,8 @@ iterators[i], iteratedObjs[i], body, - acl.isForEach()); + acl.isForEach(), + acl.isForOf()); } } finally { for (int i = 0; i < pushed; i++) { @@ -468,14 +533,18 @@ declType = ((VariableDeclaration)iter).getType(); } Node lhs = transform(iter); - decompiler.addToken(Token.IN); + if (loop.isForOf()) { + decompiler.addName("of "); + } else { + decompiler.addToken(Token.IN); + } Node obj = transform(loop.getIteratedObject()); decompiler.addToken(Token.RP); decompiler.addEOL(Token.LC); Node body = transform(loop.getBody()); decompiler.addEOL(Token.RC); return createForIn(declType, loop, lhs, obj, body, - loop.isForEach()); + loop.isForEach(), loop.isForOf()); } finally { popScope(); } @@ -671,7 +740,11 @@ defineSymbol(Token.LET, name, false); iterators[i] = init; - decompiler.addToken(Token.IN); + if (acl.isForOf()) { + decompiler.addName("of "); + } else { + decompiler.addToken(Token.IN); + } iteratedObjs[i] = transform(acl.getIteratedObject()); decompiler.addToken(Token.RP); } @@ -703,7 +776,8 @@ iterators[i], iteratedObjs[i], body, - acl.isForEach()); + acl.isForEach(), + acl.isForOf()); } } finally { for (int i = 0; i < pushed; i++) { @@ -943,8 +1017,11 @@ private Node transformReturn(ReturnStatement node) { boolean expClosure = Boolean.TRUE.equals(node.getProp(Node.EXPRESSION_CLOSURE_PROP)); + boolean isArrow = Boolean.TRUE.equals(node.getProp(Node.ARROW_FUNCTION_PROP)); if (expClosure) { - decompiler.addName(" "); + if (!isArrow) { + decompiler.addName(" "); + } } else { decompiler.addToken(Token.RETURN); } @@ -1510,7 +1587,7 @@ * Generate IR for a for..in loop. */ private Node createForIn(int declType, Node loop, Node lhs, - Node obj, Node body, boolean isForEach) + Node obj, Node body, boolean isForEach, boolean isForOf) { int destructuring = -1; int destructuringLen = 0; @@ -1548,6 +1625,7 @@ 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); @@ -1562,8 +1640,9 @@ Node assign; if (destructuring != -1) { assign = createDestructuringAssignment(declType, lvalue, id); - if (!isForEach && (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) @@ -2272,7 +2351,11 @@ } else if (fn.getMemberExprNode() != null) { mexpr = transform(fn.getMemberExprNode()); } - decompiler.addToken(Token.LP); + boolean isArrow = fn.getFunctionType() == FunctionNode.ARROW_FUNCTION; + boolean noParen = isArrow && fn.getLp() == -1; + if (!noParen) { + decompiler.addToken(Token.LP); + } List params = fn.getParams(); for (int i = 0; i < params.size(); i++) { decompile(params.get(i)); @@ -2280,7 +2363,12 @@ decompiler.addToken(Token.COMMA); } } - decompiler.addToken(Token.RP); + if (!noParen) { + decompiler.addToken(Token.RP); + } + if (isArrow) { + decompiler.addToken(Token.ARROW); + } if (!fn.isExpressionClosure()) { decompiler.addEOL(Token.LC); } diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/LazilyLoadedCtor.java rhino-1.7.7.2/src/org/mozilla/javascript/LazilyLoadedCtor.java --- rhino-1.7.7.1/src/org/mozilla/javascript/LazilyLoadedCtor.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/LazilyLoadedCtor.java 2017-09-27 22:57:34.000000000 +0000 @@ -16,7 +16,7 @@ *

This improves startup time and average memory usage. */ public final class LazilyLoadedCtor implements java.io.Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; private static final int STATE_BEFORE_INIT = 0; private static final int STATE_INITIALIZING = 1; diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/NativeArrayIterator.java rhino-1.7.7.2/src/org/mozilla/javascript/NativeArrayIterator.java --- rhino-1.7.7.1/src/org/mozilla/javascript/NativeArrayIterator.java 1970-01-01 00:00:00.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/NativeArrayIterator.java 2017-09-27 22:57:34.000000000 +0000 @@ -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.javascript; + +public final class NativeArrayIterator extends ES6Iterator { + private static final long serialVersionUID = 1L; + private static final String ITERATOR_TAG = "ArrayIterator"; + + static void init(ScriptableObject scope, boolean sealed) { + ES6Iterator.init(scope, sealed, new NativeArrayIterator(), ITERATOR_TAG); + } + + /** + * Only for constructing the prototype object. + */ + private NativeArrayIterator() { + super(); + } + + public NativeArrayIterator(Scriptable scope, Scriptable arrayLike) { + super(scope); + this.index = 0; + this.arrayLike = arrayLike; + } + + @Override + public String getClassName() { + return "Array Iterator"; + } + + @Override + protected boolean isDone(Context cx, Scriptable scope) { + return index >= NativeArray.getLengthProperty(cx, arrayLike); + } + + @Override + protected Object nextValue(Context cx, Scriptable scope) { + Object value = arrayLike.get(index++, arrayLike); + if (value == ScriptableObject.NOT_FOUND) { + value = Undefined.instance; + } + return value; + } + + @Override + protected String getTag() { + return ITERATOR_TAG; + } + + private Scriptable arrayLike; + private int index; +} + diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/NativeArray.java rhino-1.7.7.2/src/org/mozilla/javascript/NativeArray.java --- rhino-1.7.7.1/src/org/mozilla/javascript/NativeArray.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/NativeArray.java 2017-09-27 22:57:34.000000000 +0000 @@ -12,11 +12,11 @@ import java.util.Collection; import java.util.Comparator; import java.util.Iterator; -import java.util.LinkedHashSet; import java.util.List; import java.util.ListIterator; import java.util.NoSuchElementException; -import java.util.Set; + +import static org.mozilla.javascript.ScriptRuntimeES6.requireObjectCoercible; /** * This class implements the Array native object. @@ -186,7 +186,12 @@ @Override protected void initPrototypeId(int id) { - String s; + if (id == SymbolId_iterator) { + initPrototypeMethod(ARRAY_TAG, id, SymbolKey.ITERATOR, "[Symbol.iterator]", 0); + return; + } + + String s, fnName = null; int arity; switch (id) { case Id_constructor: arity=1; s="constructor"; break; @@ -216,7 +221,8 @@ case Id_reduceRight: arity=1; s="reduceRight"; break; default: throw new IllegalArgumentException(String.valueOf(id)); } - initPrototypeMethod(ARRAY_TAG, id, s, arity); + + initPrototypeMethod(ARRAY_TAG, id, s, fnName, arity); } @Override @@ -327,12 +333,15 @@ case Id_some: case Id_find: case Id_findIndex: - return iterativeMethod(cx, id, scope, thisObj, args); + 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(String.valueOf(id)); + throw new IllegalArgumentException("Array.prototype has no method: " + f.getFunctionName()); } } @@ -470,9 +479,9 @@ } @Override - public Object[] getIds() + public Object[] getIds(boolean nonEnumerable, boolean getSymbols) { - Object[] superIds = super.getIds(); + Object[] superIds = super.getIds(nonEnumerable, getSymbols); if (dense == null) { return superIds; } int N = dense.length; long currentLength = length; @@ -501,15 +510,6 @@ return ids; } - @Override - public Object[] getAllIds() - { - Set allIds = new LinkedHashSet( - Arrays.asList(this.getIds())); - allIds.addAll(Arrays.asList(super.getAllIds())); - return allIds.toArray(); - } - public Integer[] getIndexIds() { Object[] ids = getIds(); java.util.List indices = new java.util.ArrayList(ids.length); @@ -993,51 +993,25 @@ .getValueFunctionAndThis(args[0], cx); final Scriptable funThis = ScriptRuntime.lastStoredScriptable(cx); final Object[] cmpBuf = new Object[2]; // Buffer for cmp arguments - comparator = new Comparator() { - public int compare(final Object x, final Object y) { - // sort undefined to end - if (x == NOT_FOUND) { - return y == NOT_FOUND ? 0 : 1; - } else if (y == NOT_FOUND) { - return -1; - } else if (x == Undefined.instance) { - return y == Undefined.instance ? 0 : 1; - } else if (y == Undefined.instance) { - return -1; - } - + comparator = new ElementComparator( + new Comparator() { + 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); + cmpBuf); final double d = ScriptRuntime.toNumber(ret); if (d < 0) { - return -1; + return -1; } else if (d > 0) { - return +1; + return +1; } return 0; // ??? double and 0??? - } - }; + } + }); } else { - comparator = new Comparator() { - public int compare(final Object x, final Object y) { - // sort undefined to end - if (x == NOT_FOUND) { - return y == NOT_FOUND ? 0 : 1; - } else if (y == NOT_FOUND) { - return -1; - } else if (x == Undefined.instance) { - return y == Undefined.instance ? 0 : 1; - } else if (y == Undefined.instance) { - return -1; - } - - final String a = ScriptRuntime.toString(x); - final String b = ScriptRuntime.toString(y); - return a.compareTo(b); - } - }; + comparator = DEFAULT_COMPARATOR; } long llength = getLengthProperty(cx, thisObj); @@ -1053,7 +1027,7 @@ working[i] = getRawElem(thisObj, i); } - Arrays.sort(working, comparator); + Sorting.hybridSort(working, comparator); // copy the working array back into thisObj for (int i = 0; i < length; ++i) { @@ -1216,8 +1190,8 @@ private static Object js_splice(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { - NativeArray na = null; - boolean denseMode = false; + NativeArray na = null; + boolean denseMode = false; if (thisObj instanceof NativeArray) { na = (NativeArray) thisObj; denseMode = na.denseOnly; @@ -1271,7 +1245,7 @@ */ result = getElem(cx, thisObj, begin); } else { - if (denseMode) { + if (denseMode) { int intLen = (int) (end - begin); Object[] copy = new Object[intLen]; System.arraycopy(na.dense, (int) begin, copy, 0, intLen); @@ -1287,10 +1261,10 @@ // Need to set length for sparse result array setLengthProperty(cx, resultArray, end - begin); result = resultArray; - } + } } } else { // (count == 0) - if (cx.getLanguageVersion() == Context.VERSION_1_2) { + if (cx.getLanguageVersion() == Context.VERSION_1_2) { /* Emulate C JS1.2; if no elements are removed, return undefined. */ result = Undefined.instance; } else { @@ -1593,14 +1567,24 @@ /** * Implements the methods "every", "filter", "forEach", "map", and "some". */ - private static Object iterativeMethod(Context cx, int id, Scriptable scope, + private static Object iterativeMethod(Context cx, IdFunctionObject idFunctionObject, Scriptable scope, Scriptable thisObj, Object[] args) { + int id = idFunctionObject.methodId(); + + if (Id_find == id || Id_findIndex == id) thisObj = requireObjectCoercible(cx, thisObj, idFunctionObject); + long length = getLengthProperty(cx, thisObj); Object callbackArg = args.length > 0 ? args[0] : Undefined.instance; if (callbackArg == null || !(callbackArg instanceof Function)) { throw ScriptRuntime.notFunctionError(callbackArg); - } else if ((id == Id_find || id == Id_findIndex) && !(callbackArg instanceof NativeFunction)) { + } + 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]]. + // so, no [[Call]] for RegExp-s throw ScriptRuntime.notFunctionError(callbackArg); } @@ -1613,10 +1597,6 @@ thisArg = ScriptRuntime.toObject(cx, scope, args[1]); } - if ((Id_find == id || Id_findIndex == id) && thisArg == thisObj) { - throw ScriptRuntime.typeError("Array.prototype method called on null or undefined"); - } - Scriptable array = null; if (id == Id_filter || id == Id_map) { int resultLength = id == Id_map ? (int) length : 0; @@ -1627,7 +1607,11 @@ Object[] innerArgs = new Object[3]; Object elem = getRawElem(thisObj, i); if (elem == Scriptable.NOT_FOUND) { - continue; + if (id == Id_find || id == Id_findIndex) { + elem = Undefined.instance; + } else { + continue; + } } innerArgs[0] = elem; innerArgs[1] = Long.valueOf(i); @@ -1653,9 +1637,9 @@ break; case Id_find: if (ScriptRuntime.toBoolean(result)) - return elem; + return elem; break; - case Id_findIndex: + case Id_findIndex: if (ScriptRuntime.toBoolean(result)) return ScriptRuntime.wrapNumber(i); break; @@ -1940,13 +1924,76 @@ throw new UnsupportedOperationException(); } + @Override + protected int findPrototypeId(Symbol k) + { + if (SymbolKey.ITERATOR.equals(k)) { + return SymbolId_iterator; + } + return 0; + } + + // Comparators for the js_sort method. Putting them here lets us unit-test them better. + + private static final Comparator STRING_COMPARATOR = new StringLikeComparator(); + private static final Comparator DEFAULT_COMPARATOR = new ElementComparator(); + + public static final class StringLikeComparator + implements Comparator { + + 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 { + + private final Comparator child; + + public ElementComparator() { + child = STRING_COMPARATOR; + } + + public ElementComparator(Comparator 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; + } + + if (y == NOT_FOUND) { + return -1; + } + if (y == Undefined.instance) { + return -1; + } + + return child.compare(x, y); + } + } + // #string_id_map# @Override protected int findPrototypeId(String s) { int id; -// #generated# Last update: 2015-02-24 17:45:09 PST +// #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); @@ -2022,9 +2069,9 @@ Id_findIndex = 23, Id_reduce = 24, Id_reduceRight = 25, + SymbolId_iterator = 26, - - MAX_PROTOTYPE_ID = 25; + MAX_PROTOTYPE_ID = SymbolId_iterator; // #/string_id_map# diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/NativeCall.java rhino-1.7.7.2/src/org/mozilla/javascript/NativeCall.java --- rhino-1.7.7.1/src/org/mozilla/javascript/NativeCall.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/NativeCall.java 2017-09-27 22:57:34.000000000 +0000 @@ -28,7 +28,7 @@ NativeCall() { } - NativeCall(NativeFunction function, Scriptable scope, Object[] args) + NativeCall(NativeFunction function, Scriptable scope, Object[] args, boolean isArrow, boolean isStrict) { this.function = function; @@ -36,6 +36,7 @@ // leave prototype null this.originalArgs = (args == null) ? ScriptRuntime.emptyArgs : args; + this.isStrict = isStrict; // initialize values of arguments int paramAndVarCount = function.getParamAndVarCount(); @@ -51,8 +52,9 @@ // initialize "arguments" property but only if it was not overridden by // the parameter with the same name - if (!super.has("arguments", this)) { - defineProperty("arguments", new Arguments(this), PERMANENT); + if (!super.has("arguments", this) && !isArrow) { + arguments = new Arguments(this); + defineProperty("arguments", arguments, PERMANENT); } if (paramAndVarCount != 0) { @@ -113,12 +115,20 @@ throw new IllegalArgumentException(String.valueOf(id)); } + public void defineAttributesForArguments() { + if (arguments != null) { + arguments.defineAttributesForStrictMode(); + } + } + private static final int Id_constructor = 1, MAX_PROTOTYPE_ID = 1; NativeFunction function; Object[] originalArgs; + boolean isStrict; + private Arguments arguments; transient NativeCall parentActivationCall; } diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/NativeDate.java rhino-1.7.7.2/src/org/mozilla/javascript/NativeDate.java --- rhino-1.7.7.1/src/org/mozilla/javascript/NativeDate.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/NativeDate.java 2017-09-27 22:57:34.000000000 +0000 @@ -16,6 +16,9 @@ * This class implements the Date native object. * See ECMA 15.9. * @author Mike McCabe + * + * 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 { @@ -429,7 +432,7 @@ private static int YearFromTime(double t) { - if (Double.isInfinite(t) || Double.isNaN(t)) { + if (Double.isInfinite(t) || Double.isNaN(t)) { return 0; } @@ -536,7 +539,7 @@ // d: date count from 1 March int mdays, mstart; - switch (d / 30) { // approx number of month since March + 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; @@ -1781,4 +1784,3 @@ private double date; } - diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/NativeGenerator.java rhino-1.7.7.2/src/org/mozilla/javascript/NativeGenerator.java --- rhino-1.7.7.1/src/org/mozilla/javascript/NativeGenerator.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/NativeGenerator.java 2017-09-27 22:57:34.000000000 +0000 @@ -72,27 +72,6 @@ return "Generator"; } - private static class CloseGeneratorAction implements ContextAction { - private NativeGenerator generator; - - CloseGeneratorAction(NativeGenerator generator) { - this.generator = generator; - } - - public Object run(Context cx) { - Scriptable scope = ScriptableObject.getTopLevelScope(generator); - Callable closeGenerator = new Callable() { - public Object call(Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) { - return ((NativeGenerator)thisObj).resume(cx, scope, - GENERATOR_CLOSE, new GeneratorClosedException()); - } - }; - return ScriptRuntime.doTopCall(closeGenerator, cx, scope, - generator, null); - } - } - @Override protected void initPrototypeId(int id) { String s; diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/NativeJavaObject.java rhino-1.7.7.2/src/org/mozilla/javascript/NativeJavaObject.java --- rhino-1.7.7.1/src/org/mozilla/javascript/NativeJavaObject.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/NativeJavaObject.java 2017-09-27 22:57:34.000000000 +0000 @@ -362,10 +362,14 @@ } } else if (to.isInterface()) { - if (fromObj instanceof NativeObject || fromObj instanceof NativeFunction) { + + 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) { @@ -803,8 +807,7 @@ else { Method meth; try { - meth = value.getClass().getMethod("doubleValue", - (Class [])null); + meth = value.getClass().getMethod("doubleValue", (Class [])null); } catch (NoSuchMethodException e) { meth = null; @@ -814,8 +817,7 @@ } if (meth != null) { try { - return ((Number)meth.invoke(value, - (Object [])null)).doubleValue(); + return ((Number)meth.invoke(value, (Object [])null)).doubleValue(); } catch (IllegalAccessException e) { // XXX: ignore, or error message? diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/NativeJSON.java rhino-1.7.7.2/src/org/mozilla/javascript/NativeJSON.java --- rhino-1.7.7.1/src/org/mozilla/javascript/NativeJSON.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/NativeJSON.java 2017-09-27 22:57:34.000000000 +0000 @@ -92,11 +92,11 @@ case Id_stringify: { Object value = null, replacer = null, space = null; switch (args.length) { - default: case 3: space = args[2]; - case 2: replacer = args[1]; - case 1: value = args[0]; - case 0: + /* fallthru */ case 2: replacer = args[1]; + /* fallthru */ case 1: value = args[0]; + /* fallthru */ case 0: + /* fallthru */ default: } return stringify(cx, scope, value, replacer, space); } @@ -276,7 +276,7 @@ value = getProperty(holder, ((Number) key).intValue()); } - if (value instanceof Scriptable) { + 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", diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/NativeObject.java rhino-1.7.7.2/src/org/mozilla/javascript/NativeObject.java --- rhino-1.7.7.1/src/org/mozilla/javascript/NativeObject.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/NativeObject.java 2017-09-27 22:57:34.000000000 +0000 @@ -6,13 +6,7 @@ package org.mozilla.javascript; -import java.util.AbstractCollection; -import java.util.AbstractSet; -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; +import java.util.*; /** * This class implements the Object native object. @@ -52,6 +46,8 @@ "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, @@ -72,6 +68,10 @@ "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); } @@ -152,36 +152,50 @@ return thisObj; case Id_hasOwnProperty: { - boolean result; - Object arg = args.length < 1 ? Undefined.instance : args[0]; - 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); + 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]; - String s = ScriptRuntime.toStringIdOrIndex(cx, arg); - if (s == null) { - int index = ScriptRuntime.lastIndexResult(cx); - result = thisObj.has(index, thisObj); + + if (arg instanceof Symbol) { + result = ((SymbolScriptable)thisObj).has((Symbol)arg, thisObj); if (result && thisObj instanceof ScriptableObject) { ScriptableObject so = (ScriptableObject)thisObj; - int attrs = so.getAttributes(index); + int attrs = so.getAttributes((Symbol)arg); 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); + 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); + } } } return ScriptRuntime.wrapBoolean(result); @@ -266,13 +280,13 @@ case ConstructorId_getPrototypeOf: { Object arg = args.length < 1 ? Undefined.instance : args[0]; - Scriptable obj = ensureScriptable(arg); + Scriptable obj = getCompatibleObject(cx, scope, arg); return obj.getPrototype(); } case ConstructorId_keys: { Object arg = args.length < 1 ? Undefined.instance : args[0]; - Scriptable obj = ensureScriptable(arg); + Scriptable obj = getCompatibleObject(cx, scope, arg); Object[] ids = obj.getIds(); for (int i = 0; i < ids.length; i++) { ids[i] = ScriptRuntime.toString(ids[i]); @@ -282,23 +296,38 @@ case ConstructorId_getOwnPropertyNames: { Object arg = args.length < 1 ? Undefined.instance : args[0]; - ScriptableObject obj = ensureScriptableObject(arg); - Object[] ids = obj.getAllIds(); + 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); } + 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 syms = new ArrayList(); + for (int i = 0; i < ids.length; i++) { + if (ids[i] instanceof Symbol) { + syms.add(ids[i]); + } + } + 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? - ScriptableObject obj = ensureScriptableObject(arg); + Scriptable s = getCompatibleObject(cx, scope, arg); + ScriptableObject obj = ensureScriptableObject(s); Object nameArg = args.length < 2 ? Undefined.instance : args[1]; - String name = ScriptRuntime.toString(nameArg); - Scriptable desc = obj.getOwnPropertyDescriptor(cx, name); + Scriptable desc = obj.getOwnPropertyDescriptor(cx, nameArg); return desc == null ? Undefined.instance : desc; } case ConstructorId_defineProperty: @@ -415,12 +444,58 @@ 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); + } + } + } + } + return t; + } + + 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 Scriptable getCompatibleObject(Context cx, Scriptable scope, Object arg) + { + if (cx.getLanguageVersion() >= Context.VERSION_ES6) { + Scriptable s = ScriptRuntime.toObject(cx, scope, arg); + return ensureScriptable(s); + } + return ensureScriptable(arg); + } + // methods implementing java.util.Map public boolean containsKey(Object key) { @@ -677,6 +752,9 @@ ConstructorId_isFrozen = -11, ConstructorId_seal = -12, ConstructorId_freeze = -13, + ConstructorId_getOwnPropertySymbols = -14, + ConstructorId_assign = -15, + ConstructorId_is = -16, Id_constructor = 1, Id_toString = 2, diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/NativeStringIterator.java rhino-1.7.7.2/src/org/mozilla/javascript/NativeStringIterator.java --- rhino-1.7.7.1/src/org/mozilla/javascript/NativeStringIterator.java 1970-01-01 00:00:00.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/NativeStringIterator.java 2017-09-27 22:57:34.000000000 +0000 @@ -0,0 +1,55 @@ +/* -*- 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 NativeStringIterator extends ES6Iterator { + private static final long serialVersionUID = 1L; + private static final String ITERATOR_TAG = "StringIterator"; + + static void init(ScriptableObject scope, boolean sealed) { + ES6Iterator.init(scope, sealed, new NativeStringIterator(), ITERATOR_TAG); + } + + /** + * Only for constructing the prototype object. + */ + private NativeStringIterator() { + super(); + } + + NativeStringIterator(Scriptable scope, Scriptable stringLike) { + super(scope); + this.index = 0; + this.string = ScriptRuntime.toString(stringLike); + } + + @Override + public String getClassName() { + return "String Iterator"; + } + + @Override + protected boolean isDone(Context cx, Scriptable scope) { + return index >= string.length(); + } + + @Override + protected Object nextValue(Context cx, Scriptable scope) { + int newIndex = string.offsetByCodePoints(index, 1); + Object value = string.substring(index, newIndex); + index = newIndex; + return value; + } + + @Override + protected String getTag() { + return ITERATOR_TAG; + } + + private String string; + private int index; +} diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/NativeString.java rhino-1.7.7.2/src/org/mozilla/javascript/NativeString.java --- rhino-1.7.7.1/src/org/mozilla/javascript/NativeString.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/NativeString.java 2017-09-27 22:57:34.000000000 +0000 @@ -12,7 +12,7 @@ import java.text.Normalizer; import static org.mozilla.javascript.ScriptRuntime.rangeError; -import static org.mozilla.javascript.ScriptRuntime.requireObjectCoercible; +import static org.mozilla.javascript.ScriptRuntimeES6.requireObjectCoercible; /** * This class implements the String native object. @@ -127,7 +127,12 @@ @Override protected void initPrototypeId(int id) { - String s; + if (id == SymbolId_iterator) { + initPrototypeMethod(STRING_TAG, id, SymbolKey.ITERATOR, "[Symbol.iterator]", 0); + return; + } + + String s, fnName = null; int arity; switch (id) { case Id_constructor: arity=1; s="constructor"; break; @@ -177,7 +182,7 @@ case Id_codePointAt: arity=1; s="codePointAt"; break; default: throw new IllegalArgumentException(String.valueOf(id)); } - initPrototypeMethod(STRING_TAG, id, s, arity); + initPrototypeMethod(STRING_TAG, id, s, fnName, arity); } @Override @@ -235,8 +240,17 @@ } case Id_constructor: { - CharSequence s = (args.length >= 1) - ? ScriptRuntime.toCharSequence(args[0]) : ""; + 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); @@ -276,8 +290,7 @@ case Id_includes: case Id_startsWith: case Id_endsWith: - String s = ScriptRuntime.toString(requireObjectCoercible(thisObj, f)); - + String s = 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()); } @@ -407,7 +420,7 @@ .toUpperCase(cx.getLocale()); } case Id_trim: { - String str = ScriptRuntime.toString(thisObj); + String str = ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f)); char[] chars = str.toCharArray(); int start = 0; @@ -447,7 +460,8 @@ return str.substring(start, end); } - case Id_normalize: { + case Id_normalize: + { String formStr = ScriptRuntime.toString(args, 0); Normalizer.Form form; @@ -457,7 +471,7 @@ 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(thisObj, f)), form); + return Normalizer.normalize(ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f)), form); } case Id_repeat: @@ -466,13 +480,17 @@ } case Id_codePointAt: { - String str = ScriptRuntime.toString(requireObjectCoercible(thisObj, f)); + 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); + } throw new IllegalArgumentException("String.prototype has no method: " + f.getFunctionName()); } @@ -538,6 +556,32 @@ super.put(index, start, value); } + @Override + public boolean has(int index, Scriptable start) { + if (0 <= index && index < string.length()) { + return true; + } + return super.has(index, start); + } + + @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)) { + Object[] sids = super.getIds(nonEnumerable, getSymbols); + Object[] a = new Object[sids.length + string.length()]; + int i; + for (i = 0; i < string.length(); i++) { + a[i] = Integer.valueOf(i); + } + System.arraycopy(sids, 0, a, i, sids.length); + return a; + } + return super.getIds(nonEnumerable, getSymbols); + } + /* * * See ECMA 15.5.4.6. Uses Java String.indexOf() @@ -718,7 +762,7 @@ private static String js_repeat(Context cx, Scriptable thisObj, IdFunctionObject f, Object[] args) { - String str = ScriptRuntime.toString(requireObjectCoercible(thisObj, f)); + String str = ScriptRuntime.toString(requireObjectCoercible(cx, thisObj, f)); double cnt = ScriptRuntime.toInteger(args, 0); if ((cnt < 0.0) || (cnt == Double.POSITIVE_INFINITY)) { @@ -751,13 +795,22 @@ return retval.toString(); } + @Override + protected int findPrototypeId(Symbol k) + { + if (SymbolKey.ITERATOR.equals(k)) { + return SymbolId_iterator; + } + return 0; + } + // #string_id_map# @Override protected int findPrototypeId(String s) { int id; -// #generated# Last update: 2015-05-06 14:41:38 PDT +// #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); @@ -883,7 +936,8 @@ Id_normalize = 43, Id_repeat = 44, Id_codePointAt = 45, - MAX_PROTOTYPE_ID = Id_codePointAt; + SymbolId_iterator = 46, + MAX_PROTOTYPE_ID = SymbolId_iterator; // #/string_id_map# diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/NativeSymbol.java rhino-1.7.7.2/src/org/mozilla/javascript/NativeSymbol.java --- rhino-1.7.7.1/src/org/mozilla/javascript/NativeSymbol.java 1970-01-01 00:00:00.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/NativeSymbol.java 2017-09-27 22:57:34.000000000 +0000 @@ -0,0 +1,345 @@ +/* -*- 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.HashMap; +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. + */ + +public class NativeSymbol + extends IdScriptableObject + implements Symbol +{ + private static final long serialVersionUID = -589539749749830003L; + + public static final String CLASS_NAME = "Symbol"; + public static final String TYPE_NAME = "symbol"; + + private static final Object GLOBAL_TABLE_KEY = new Object(); + private static final Object CONSTRUCTOR_SLOT = new Object(); + + private final SymbolKey key; + private final NativeSymbol symbolData; + + public static void init(Context cx, Scriptable scope, boolean sealed) { + NativeSymbol obj = new NativeSymbol(""); + ScriptableObject ctor = obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); + + cx.putThreadLocal(CONSTRUCTOR_SLOT, Boolean.TRUE); + try { + createStandardSymbol(cx, scope, ctor, "iterator", SymbolKey.ITERATOR); + 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, "isRegExp", SymbolKey.IS_REGEXP); + createStandardSymbol(cx, scope, ctor, "toPrimitive", SymbolKey.TO_PRIMITIVE); + createStandardSymbol(cx, scope, ctor, "match", SymbolKey.MATCH); + createStandardSymbol(cx, scope, ctor, "replace", SymbolKey.REPLACE); + 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); + } + } + + private NativeSymbol(String desc) { + this.key = new SymbolKey(desc); + this.symbolData = this; + } + + private NativeSymbol(SymbolKey key) { + this.key = key; + this.symbolData = this; + } + + public NativeSymbol(NativeSymbol s) { + this.key = s.key; + this.symbolData = s.symbolData; + } + + /** + * 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) + { + cx.putThreadLocal(CONSTRUCTOR_SLOT, Boolean.TRUE); + try { + return (NativeSymbol)cx.newObject(scope, CLASS_NAME, args); + } finally { + cx.removeThreadLocal(CONSTRUCTOR_SLOT); + } + } + + @Override + public String getClassName() { + return CLASS_NAME; + } + + @Override + protected void fillConstructorProperties(IdFunctionObject ctor) { + super.fillConstructorProperties(ctor); + addIdFunctionProperty(ctor, CLASS_NAME, ConstructorId_for, "for", 1); + 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 }); + 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; + } +// #/generated# + return id; + } + + @Override + protected int findPrototypeId(Symbol key) { + if (SymbolKey.TO_STRING_TAG.equals(key)) { + return SymbolId_toStringTag; + } else if (SymbolKey.TO_PRIMITIVE.equals(key)) { + return SymbolId_toPrimitive; + } + 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# + + + @Override + protected void initPrototypeId(int id) + { + String s = null; + int arity = -1; + 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; + } + } + + @Override + 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 { + // 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); + } + } + + private NativeSymbol getSelf(Object thisObj) { + try { + return (NativeSymbol)thisObj; + } catch (ClassCastException cce) { + throw ScriptRuntime.typeError1("msg.invalid.type", thisObj.getClass().getName()); + } + } + + private static NativeSymbol js_constructor(Object[] args) { + String desc; + if (args.length > 0) { + if (Undefined.instance.equals(args[0])) { + desc = ""; + } else { + desc = ScriptRuntime.toString(args[0]); + } + } else { + desc = ""; + } + + if (args.length > 1) { + return new NativeSymbol((SymbolKey) args[1]); + } + + return new NativeSymbol(desc); + } + + private Object js_valueOf() { + // In the case that "Object()" was called we actually have a different "internal slot" + return symbolData; + } + + private Object js_for(Context cx, Scriptable scope, Object[] args) { + String name = (args.length > 0 ? ScriptRuntime.toString(args[0]) : ScriptRuntime.toString(Undefined.instance)); + + Map table = getGlobalMap(); + NativeSymbol ret = table.get(name); + + if (ret == null) { + ret = construct(cx, scope, new Object[]{name}); + table.put(name, ret); + } + return ret; + } + + private Object js_keyFor(Context cx, Scriptable scope, Object[] args) { + Object s = (args.length > 0 ? args[0] : Undefined.instance); + if (!(s instanceof NativeSymbol)) { + throw ScriptRuntime.throwCustomError(cx, scope, "TypeError", "Not a Symbol"); + } + NativeSymbol sym = (NativeSymbol)s; + + Map table = getGlobalMap(); + for (Map.Entry e : table.entrySet()) { + if (e.getValue().key == sym.key) { + return e.getKey(); + } + } + return Undefined.instance; + } + + @Override + public String toString() { + return key.toString(); + } + + // Symbol objects have a special property that one cannot add properties. + + @Override + 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"); + } + } + + @Override + 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"); + } + } + + @Override + 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"); + } + } + + /** + * 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() + { + return (symbolData == this); + } + + @Override + public String getTypeOf() + { + return (isSymbol() ? TYPE_NAME : super.getTypeOf()); + } + + @Override + public int hashCode() { + return key.hashCode(); + } + + @Override + public boolean equals(Object x) { + return key.equals(x); + } + + SymbolKey getKey() { + return key; + } + + private Map getGlobalMap() { + ScriptableObject top = (ScriptableObject)getTopLevelScope(this); + Map map = (Map)top.getAssociatedValue(GLOBAL_TABLE_KEY); + if (map == null) { + map = new HashMap(); + top.associateValue(GLOBAL_TABLE_KEY, map); + } + return map; + } +} diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/Node.java rhino-1.7.7.2/src/org/mozilla/javascript/Node.java --- rhino-1.7.7.1/src/org/mozilla/javascript/Node.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/Node.java 2017-09-27 22:57:34.000000000 +0000 @@ -63,7 +63,8 @@ JSDOC_PROP = 24, EXPRESSION_CLOSURE_PROP = 25, // JS 1.8 expression closure pseudo-return DESTRUCTURING_SHORTHAND = 26, // JS 1.8 destructuring shorthand - LAST_PROP = 26; + ARROW_FUNCTION_PROP = 27, + LAST_PROP = 27; // values of ISNUMBER_PROP to specify // which of the children are Number types diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/NodeTransformer.java rhino-1.7.7.2/src/org/mozilla/javascript/NodeTransformer.java --- rhino-1.7.7.1/src/org/mozilla/javascript/NodeTransformer.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/NodeTransformer.java 2017-09-27 22:57:34.000000000 +0000 @@ -6,7 +6,6 @@ package org.mozilla.javascript; -import org.mozilla.javascript.ast.AstRoot; import org.mozilla.javascript.ast.FunctionNode; import org.mozilla.javascript.ast.Jump; import org.mozilla.javascript.ast.Scope; @@ -31,14 +30,20 @@ public final void transform(ScriptNode tree) { - transformCompilationUnit(tree); + transform(tree, false); + } + + public final void transform(ScriptNode tree, boolean inStrictMode) + { + inStrictMode = inStrictMode || tree.isInStrictMode(); + transformCompilationUnit(tree, inStrictMode); for (int i = 0; i != tree.getFunctionCount(); ++i) { FunctionNode fn = tree.getFunctionNode(i); - transform(fn); + transform(fn, inStrictMode); } } - private void transformCompilationUnit(ScriptNode tree) + private void transformCompilationUnit(ScriptNode tree, boolean inStrictMode) { loops = new ObjArray(); loopEnds = new ObjArray(); @@ -53,8 +58,6 @@ //uncomment to print tree before transformation if (Token.printTrees) System.out.println(tree.toStringTree(tree)); - boolean inStrictMode = tree instanceof AstRoot && - ((AstRoot)tree).isInStrictMode(); transformCompilationUnit_r(tree, tree, tree, createScopeObjects, inStrictMode); } @@ -320,27 +323,28 @@ * for the following constructs: typeof o.p, if (o.p), * if (!o.p), if (o.p == undefined), if (undefined == o.p) */ - Node child = node.getFirstChild(); - if (type == Token.IFNE) { - while (child.getType() == Token.NOT) { - child = child.getFirstChild(); - } - if (child.getType() == Token.EQ || - child.getType() == Token.NE) - { - Node first = child.getFirstChild(); - Node last = child.getLastChild(); - if (first.getType() == Token.NAME && - first.getString().equals("undefined")) - child = last; - else if (last.getType() == Token.NAME && - last.getString().equals("undefined")) - child = first; - } - } - if (child.getType() == Token.GETPROP) - child.setType(Token.GETPROPNOWARN); - break; + Node child = node.getFirstChild(); + if (type == Token.IFNE) { + while (child.getType() == Token.NOT) { + child = child.getFirstChild(); + } + if (child.getType() == Token.EQ || + child.getType() == Token.NE) { + Node first = child.getFirstChild(); + Node last = child.getLastChild(); + if (first.getType() == Token.NAME && + first.getString().equals("undefined")) { + child = last; + } else if (last.getType() == Token.NAME && + last.getString().equals("undefined")) { + child = first; + } + } + } + if (child.getType() == Token.GETPROP) { + child.setType(Token.GETPROPNOWARN); + } + break; } case Token.SETNAME: diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/ObjArray.java rhino-1.7.7.2/src/org/mozilla/javascript/ObjArray.java --- rhino-1.7.7.1/src/org/mozilla/javascript/ObjArray.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/ObjArray.java 2017-09-27 22:57:34.000000000 +0000 @@ -174,21 +174,21 @@ case 0: if (N == 0) { f0 = value; break; } tmp = f0; f0 = value; value = tmp; - case 1: + /* fallthru */ case 1: if (N == 1) { f1 = value; break; } tmp = f1; f1 = value; value = tmp; - case 2: + /* fallthru */ case 2: if (N == 2) { f2 = value; break; } tmp = f2; f2 = value; value = tmp; - case 3: + /* fallthru */ case 3: if (N == 3) { f3 = value; break; } tmp = f3; f3 = value; value = tmp; - case 4: + /* fallthru */ case 4: if (N == 4) { f4 = value; break; } tmp = f4; f4 = value; value = tmp; index = FIELDS_STORE_SIZE; - default: + /* fallthru */ 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; - case 1: + /* fallthru */ case 1: if (N == 1) { f1 = null; break; } f1 = f2; - case 2: + /* fallthru */ case 2: if (N == 2) { f2 = null; break; } f2 = f3; - case 3: + /* fallthru */ case 3: if (N == 3) { f3 = null; break; } f3 = f4; - case 4: + /* fallthru */ case 4: if (N == 4) { f4 = null; break; } f4 = data[0]; index = FIELDS_STORE_SIZE; - default: + /* fallthru */ 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); - case 5: array[offset + 4] = f4; - case 4: array[offset + 3] = f3; - case 3: array[offset + 2] = f2; - case 2: array[offset + 1] = f1; - case 1: array[offset + 0] = f0; - case 0: break; + /* 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; } } diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/optimizer/Codegen.java rhino-1.7.7.2/src/org/mozilla/javascript/optimizer/Codegen.java --- rhino-1.7.7.1/src/org/mozilla/javascript/optimizer/Codegen.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/optimizer/Codegen.java 2017-09-27 22:57:34.000000000 +0000 @@ -264,6 +264,7 @@ { boolean hasScript = (scriptOrFnNodes[0].getType() == Token.SCRIPT); boolean hasFunctions = (scriptOrFnNodes.length > 1 || !hasScript); + boolean isStrictMode = scriptOrFnNodes[0].isInStrictMode(); String sourceFile = null; if (compilerEnv.isGenerateDebugInfo()) { @@ -286,7 +287,7 @@ generateExecute(cfw); } - generateCallMethod(cfw); + generateCallMethod(cfw, isStrictMode); generateResumeGenerator(cfw); generateNativeFunctionOverrides(cfw, encodedSource); @@ -406,7 +407,7 @@ boolean hasGenerators = false; for (int i=0; i < scriptOrFnNodes.length; i++) { if (isGenerator(scriptOrFnNodes[i])) - hasGenerators = true; + hasGenerators = true; } // if there are no generators defined, we don't implement a @@ -465,7 +466,7 @@ cfw.stopMethod((short)6); } - private void generateCallMethod(ClassFileWriter cfw) + private void generateCallMethod(ClassFileWriter cfw, boolean isStrictMode) { cfw.startMethod("call", "(Lorg/mozilla/javascript/Context;" + @@ -492,6 +493,7 @@ cfw.addALoad(2); cfw.addALoad(3); cfw.addALoad(4); + cfw.addPush(isStrictMode); cfw.addInvoke(ByteCode.INVOKESTATIC, "org/mozilla/javascript/ScriptRuntime", "doTopCall", @@ -500,6 +502,7 @@ +"Lorg/mozilla/javascript/Scriptable;" +"Lorg/mozilla/javascript/Scriptable;" +"[Ljava/lang/Object;" + +"Z" +")Ljava/lang/Object;"); cfw.add(ByteCode.ARETURN); cfw.markLabel(nonTopCallLabel); @@ -1346,10 +1349,12 @@ 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); @@ -1617,15 +1622,22 @@ 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); - addScriptRuntimeInvoke("createFunctionActivation", + 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); @@ -1999,6 +2011,7 @@ 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); @@ -2006,6 +2019,8 @@ ? 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", @@ -2172,7 +2187,8 @@ OptFunctionNode ofn = OptFunctionNode.get(scriptOrFn, fnIndex); int t = ofn.fnode.getFunctionType(); - if (t != FunctionNode.FUNCTION_EXPRESSION) { + if (t != FunctionNode.FUNCTION_EXPRESSION && + t != FunctionNode.ARROW_FUNCTION) { throw Codegen.badTree(); } visitFunction(ofn, t); @@ -2545,7 +2561,7 @@ } else { cfw.addALoad(variableObjectLocal); - addScriptRuntimeInvoke( + addScriptRuntimeInvoke( "getObjectElem", "(Ljava/lang/Object;" +"Ljava/lang/Object;" @@ -2960,7 +2976,20 @@ cfw.addInvoke(ByteCode.INVOKESPECIAL, codegen.mainClassName, "", Codegen.FUNCTION_CONSTRUCTOR_SIGNATURE); - if (functionType == FunctionNode.FUNCTION_EXPRESSION) { + 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; @@ -4436,10 +4465,10 @@ } else { cfw.addALoad(reg); } + addObjectToDouble(); if (post) { - cfw.add(ByteCode.DUP); + cfw.add(ByteCode.DUP2); } - addObjectToDouble(); cfw.addPush(1.0); if ((incrDecrMask & Node.DECR_FLAG) == 0) { cfw.add(ByteCode.DADD); @@ -4451,6 +4480,9 @@ cfw.add(ByteCode.DUP); } cfw.addAStore(reg); + if (post) { + addDoubleWrap(); + } } break; case Token.NAME: diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/optimizer/OptRuntime.java rhino-1.7.7.2/src/org/mozilla/javascript/optimizer/OptRuntime.java --- rhino-1.7.7.1/src/org/mozilla/javascript/optimizer/OptRuntime.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/optimizer/OptRuntime.java 2017-09-27 22:57:34.000000000 +0000 @@ -133,6 +133,11 @@ ScriptRuntime.initFunction(cx, scope, fn, functionType, false); } + 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, diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/Parser.java rhino-1.7.7.2/src/org/mozilla/javascript/Parser.java --- rhino-1.7.7.1/src/org/mozilla/javascript/Parser.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/Parser.java 2017-09-27 22:57:34.000000000 +0000 @@ -6,7 +6,71 @@ package org.mozilla.javascript; -import org.mozilla.javascript.ast.*; // we use basically every class +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.Block; +import org.mozilla.javascript.ast.BreakStatement; +import org.mozilla.javascript.ast.CatchClause; +import org.mozilla.javascript.ast.Comment; +import org.mozilla.javascript.ast.ConditionalExpression; +import org.mozilla.javascript.ast.ContinueStatement; +import org.mozilla.javascript.ast.DestructuringForm; +import org.mozilla.javascript.ast.DoLoop; +import org.mozilla.javascript.ast.ElementGet; +import org.mozilla.javascript.ast.EmptyExpression; +import org.mozilla.javascript.ast.EmptyStatement; +import org.mozilla.javascript.ast.ErrorNode; +import org.mozilla.javascript.ast.ExpressionStatement; +import org.mozilla.javascript.ast.ForInLoop; +import org.mozilla.javascript.ast.ForLoop; +import org.mozilla.javascript.ast.FunctionCall; +import org.mozilla.javascript.ast.FunctionNode; +import org.mozilla.javascript.ast.GeneratorExpression; +import org.mozilla.javascript.ast.GeneratorExpressionLoop; +import org.mozilla.javascript.ast.IdeErrorReporter; +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; +import org.mozilla.javascript.ast.Loop; +import org.mozilla.javascript.ast.Name; +import org.mozilla.javascript.ast.NewExpression; +import org.mozilla.javascript.ast.NumberLiteral; +import org.mozilla.javascript.ast.ObjectLiteral; +import org.mozilla.javascript.ast.ObjectProperty; +import org.mozilla.javascript.ast.ParenthesizedExpression; +import org.mozilla.javascript.ast.PropertyGet; +import org.mozilla.javascript.ast.RegExpLiteral; +import org.mozilla.javascript.ast.ReturnStatement; +import org.mozilla.javascript.ast.Scope; +import org.mozilla.javascript.ast.ScriptNode; +import org.mozilla.javascript.ast.StringLiteral; +import org.mozilla.javascript.ast.SwitchCase; +import org.mozilla.javascript.ast.SwitchStatement; +import org.mozilla.javascript.ast.Symbol; +import org.mozilla.javascript.ast.ThrowStatement; +import org.mozilla.javascript.ast.TryStatement; +import org.mozilla.javascript.ast.UnaryExpression; +import org.mozilla.javascript.ast.VariableDeclaration; +import org.mozilla.javascript.ast.VariableInitializer; +import org.mozilla.javascript.ast.WhileLoop; +import org.mozilla.javascript.ast.WithStatement; +import org.mozilla.javascript.ast.XmlDotQuery; +import org.mozilla.javascript.ast.XmlElemRef; +import org.mozilla.javascript.ast.XmlExpression; +import org.mozilla.javascript.ast.XmlLiteral; +import org.mozilla.javascript.ast.XmlMemberGet; +import org.mozilla.javascript.ast.XmlPropRef; +import org.mozilla.javascript.ast.XmlRef; +import org.mozilla.javascript.ast.XmlString; +import org.mozilla.javascript.ast.Yield; import java.io.BufferedReader; import java.io.IOException; @@ -93,6 +157,8 @@ private String prevNameTokenString = ""; private int prevNameTokenLineno; + private boolean defaultUseStrictDirective; + // Exception to unwind private static class ParserException extends RuntimeException { @@ -253,7 +319,7 @@ void reportError(String messageId, String messageArg, int position, int length) { - addError(messageId, position, length); + addError(messageId, messageArg, position, length); if (!compilerEnv.recoverFromErrors()) { throw new ParserException(); @@ -549,8 +615,11 @@ boolean inDirectivePrologue = true; boolean savedStrictMode = inUseStrictDirective; - // TODO: eval code should get strict mode from invoking code - inUseStrictDirective = false; + + inUseStrictDirective = defaultUseStrictDirective; + if (inUseStrictDirective) { + root.setInStrictMode(true); + } try { for (;;) { @@ -621,17 +690,18 @@ return root; } - private AstNode parseFunctionBody() + private AstNode parseFunctionBody(int type, FunctionNode fnNode) throws IOException { boolean isExpressionClosure = false; if (!matchToken(Token.LC)) { - if (compilerEnv.getLanguageVersion() < Context.VERSION_1_8) { + if (compilerEnv.getLanguageVersion() < Context.VERSION_1_8 && type != FunctionNode.ARROW_FUNCTION) { reportError("msg.no.brace.body"); } else { isExpressionClosure = true; } } + boolean isArrow = type == FunctionNode.ARROW_FUNCTION; ++nestingOfFunction; int pos = ts.tokenBeg; Block pn = new Block(pos); // starts at LC position @@ -648,6 +718,9 @@ // expression closure flag is required on both nodes n.putProp(Node.EXPRESSION_CLOSURE_PROP, Boolean.TRUE); pn.putProp(Node.EXPRESSION_CLOSURE_PROP, Boolean.TRUE); + if (isArrow) { + n.putProp(Node.ARROW_FUNCTION_PROP, Boolean.TRUE); + } pn.addStatement(n); } else { bodyLoop: for (;;) { @@ -671,6 +744,10 @@ inDirectivePrologue = false; } else if (directive.equals("use strict")) { inUseStrictDirective = true; + fnNode.setInStrictMode(true); + if (!savedStrictMode) { + setRequiresActivation(); + } } } break; @@ -825,7 +902,7 @@ PerFunctionVariables savedVars = new PerFunctionVariables(fnNode); try { parseFunctionParams(fnNode); - fnNode.setBody(parseFunctionBody()); + fnNode.setBody(parseFunctionBody(type, fnNode)); fnNode.setEncodedSourceBounds(functionSourceStart, ts.tokenEnd); fnNode.setLength(ts.tokenEnd - functionSourceStart); @@ -869,6 +946,93 @@ return fnNode; } + 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 + + FunctionNode fnNode = new FunctionNode(functionSourceStart); + fnNode.setFunctionType(FunctionNode.ARROW_FUNCTION); + fnNode.setJsDocNode(getAndResetJsDoc()); + + // Would prefer not to call createDestructuringAssignment until codegen, + // but the symbol definitions have to happen now, before body is parsed. + Map destructuring = new HashMap(); + Set paramNames = new HashSet(); + + PerFunctionVariables savedVars = new PerFunctionVariables(fnNode); + try { + if (params instanceof ParenthesizedExpression) { + fnNode.setParens(0, params.getLength()); + AstNode p = ((ParenthesizedExpression)params).getExpression(); + if (!(p instanceof EmptyExpression)) { + arrowFunctionParams(fnNode, p, destructuring, paramNames); + } + } else { + arrowFunctionParams(fnNode, params, destructuring, paramNames); + } + + if (!destructuring.isEmpty()) { + Node destructuringNode = new Node(Token.COMMA); + // Add assignment helper for each destructuring parameter + for (Map.Entry 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); + } finally { + savedVars.restore(); + } + + if (fnNode.isGenerator()) { + reportError("msg.arrowfunction.generator"); + return makeErrorNode(); + } + + fnNode.setSourceName(sourceURI); + fnNode.setBaseLineno(baseLineno); + fnNode.setEndLineno(ts.lineno); + + return fnNode; + } + + private void arrowFunctionParams(FunctionNode fnNode, AstNode params, Map destructuring, Set paramNames) { + if (params instanceof ArrayLiteral || params instanceof ObjectLiteral) { + markDestructuring(params); + fnNode.addParam(params); + String pname = currentScriptOrFn.getNextTempName(); + 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); + } else if (params instanceof Name) { + fnNode.addParam(params); + 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); + paramNames.add(paramName); + } + } else { + reportError("msg.no.parm", params.getPosition(), params.getLength()); + fnNode.addParam(makeErrorNode()); + } + } + // This function does not match the closing RC: the caller matches // the RC so it can provide a suitable error message if not matched. // This means it's up to the caller to set the length of the node to @@ -1260,7 +1424,7 @@ if (currentToken != Token.FOR) codeBug(); consumeToken(); int forPos = ts.tokenBeg, lineno = ts.lineno; - boolean isForEach = false, isForIn = false; + 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' @@ -1285,11 +1449,15 @@ int tt = peekToken(); init = forLoopInit(tt); - if (matchToken(Token.IN)) { 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())) { + 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"); if (peekToken() == Token.SEMI) { @@ -1313,7 +1481,7 @@ if (mustMatchToken(Token.RP, "msg.no.paren.for.ctrl")) rp = ts.tokenBeg - forPos; - if (isForIn) { + if (isForIn || isForOf) { ForInLoop fis = new ForInLoop(forPos); if (init instanceof VariableDeclaration) { // check that there was only one variable given @@ -1321,11 +1489,15 @@ reportError("msg.mult.index"); } } + if (isForOf && isForEach) { + reportError("msg.invalid.for.each"); + } fis.setIterator(init); fis.setIteratedObject(cond); fis.setInPosition(inPos); fis.setIsForEach(isForEach); fis.setEachPosition(eachPos); + fis.setIsForOf(isForOf); pn = fis; } else { ForLoop fl = new ForLoop(forPos); @@ -2064,7 +2236,12 @@ return returnOrYield(tt, true); } AstNode pn = condExpr(); - tt = peekToken(); + boolean hasEOL = false; + tt = peekTokenOrEOL(); + if (tt == Token.EOL) { + hasEOL = true; + tt = peekToken(); + } if (Token.FIRST_ASSIGN <= tt && tt <= Token.LAST_ASSIGN) { consumeToken(); @@ -2085,6 +2262,9 @@ if (currentJsDocComment != null) { pn.setJsDocNode(getAndResetJsDoc()); } + } else if (!hasEOL && tt == Token.ARROW) { + consumeToken(); + pn = arrowFunction(pn); } return pn; } @@ -2602,7 +2782,7 @@ int maybeName = nextToken(); if (maybeName != Token.NAME && !(compilerEnv.isReservedKeywordAsIdentifier() - && TokenStream.isKeyword(ts.getString()))) { + && TokenStream.isKeyword(ts.getString(), compilerEnv.getLanguageVersion(), inUseStrictDirective))) { reportError("msg.no.name.after.dot"); } @@ -2639,6 +2819,13 @@ 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}) @@ -2798,40 +2985,54 @@ private AstNode primaryExpr() throws IOException { - int ttFlagged = nextFlaggedToken(); + 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.isNumberOctal()) { - reportError("msg.no.octal.strict"); + if (this.inUseStrictDirective && ts.isNumberOldOctal()) { + reportError("msg.no.old.octal.strict"); } - if (ts.isNumberOctal()) { + if (ts.isNumberBinary()) { + s = "0b"+s; + } + if (ts.isNumberOldOctal()) { s = "0"+s; } + if (ts.isNumberOctal()) { + s = "0o"+s; + } if (ts.isNumberHex()) { s = "0x"+s; } @@ -2841,10 +3042,12 @@ } 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; @@ -2857,26 +3060,35 @@ 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; } // should only be reachable in IDE/error-recovery mode + consumeToken(); return makeErrorNode(); } @@ -2899,6 +3111,10 @@ 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; @@ -3030,6 +3246,7 @@ if (nextToken() != Token.FOR) codeBug(); int pos = ts.tokenBeg; int eachPos = -1, lp = -1, rp = -1, inPos = -1; + boolean isForIn = false, isForOf = false; ArrayComprehensionLoop pn = new ArrayComprehensionLoop(pos); pushScope(pn); @@ -3067,8 +3284,23 @@ defineSymbol(Token.LET, ts.getString(), true); } - if (mustMatchToken(Token.IN, "msg.in.after.for.name")) + 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"); + } + inPos = ts.tokenBeg - pos; + isForOf = true; + break; + } + default: + reportError("msg.in.after.for.name"); + } AstNode obj = expr(); if (mustMatchToken(Token.RP, "msg.no.paren.for.ctrl")) rp = ts.tokenBeg - pos; @@ -3080,6 +3312,7 @@ pn.setEachPosition(eachPos); pn.setIsForEach(eachPos != -1); pn.setParens(lp, rp); + pn.setIsForOf(isForOf); return pn; } finally { popScope(); @@ -3326,7 +3559,7 @@ default: if (compilerEnv.isReservedKeywordAsIdentifier() - && TokenStream.isKeyword(ts.getString())) { + && TokenStream.isKeyword(ts.getString(), compilerEnv.getLanguageVersion(), inUseStrictDirective)) { // convert keyword to property name, e.g. ({if: 1}) pname = createNameNode(); break; @@ -3444,10 +3677,12 @@ return; } boolean activation = false; - if ("arguments".equals(name) - || (compilerEnv.getActivationNames() != null - && compilerEnv.getActivationNames().contains(name))) - { + 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 @@ -3864,11 +4099,11 @@ int nodeType = left.getType(); switch (nodeType) { case Token.NAME: + String name = ((Name) left).getIdentifier(); if (inUseStrictDirective && - "eval".equals(((Name) left).getIdentifier())) + ("eval".equals(name) || "arguments".equals(name))) { - reportError("msg.bad.id.strict", - ((Name) left).getIdentifier()); + reportError("msg.bad.id.strict", name); } left.setType(Token.BINDNAME); return new Node(Token.SETNAME, left, right); @@ -3946,4 +4181,12 @@ + ", ts.tokenBeg=" + ts.tokenBeg + ", currentToken=" + currentToken); } + + public void setDefaultUseStrictDirective(boolean useStrict) { + defaultUseStrictDirective = useStrict; + } + + public boolean inUseStrictDirective() { + return inUseStrictDirective; + } } diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/regexp/NativeRegExp.java rhino-1.7.7.2/src/org/mozilla/javascript/regexp/NativeRegExp.java --- rhino-1.7.7.1/src/org/mozilla/javascript/regexp/NativeRegExp.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/regexp/NativeRegExp.java 2017-09-27 22:57:34.000000000 +0000 @@ -157,7 +157,7 @@ @Override public String getTypeOf() { - return "object"; + return "object"; } public Object call(Context cx, Scriptable scope, Scriptable thisObj, @@ -606,7 +606,7 @@ break; case 'u': nDigits += 2; - // fall thru... + // fallthru case 'x': n = 0; for (i = 0; (i < nDigits) && (index < end); i++) { @@ -948,9 +948,7 @@ /* UnicodeEscapeSequence */ case 'u': nDigits += 2; - // fall thru... - /* HexEscapeSequence */ - case 'x': + /* fallthru */ case 'x': /* HexEscapeSequence */ { int n = 0; int i; @@ -1601,7 +1599,7 @@ break; case 'u': nDigits += 2; - // fall thru + // fallthru case 'x': n = 0; for (i = 0; (i < nDigits) && (src < end); i++) { @@ -2401,7 +2399,7 @@ gData.cp = i; gData.skipped = i - start; for (int j = 0; j < re.parenCount; j++) { - gData.parens[j] = -1l; + gData.parens[j] = -1L; } boolean result = executeREBytecode(gData, input, end); diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/regexp/RegExpImpl.java rhino-1.7.7.2/src/org/mozilla/javascript/regexp/RegExpImpl.java --- rhino-1.7.7.1/src/org/mozilla/javascript/regexp/RegExpImpl.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/regexp/RegExpImpl.java 2017-09-27 22:57:34.000000000 +0000 @@ -579,7 +579,7 @@ * that matched part of the delimiter in the new array, after the * split substring that was delimited. */ - if (re != null && matched[0] == true) { + if (re != null && matched[0]) { int size = parens[0].length; for (int num = 0; num < size; num++) { if (limited && len >= limit) diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/resources/Messages.properties rhino-1.7.7.2/src/org/mozilla/javascript/resources/Messages.properties --- rhino-1.7.7.1/src/org/mozilla/javascript/resources/Messages.properties 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/resources/Messages.properties 2017-09-27 22:57:34.000000000 +0000 @@ -26,6 +26,11 @@ # SomeJavaClassWhereUsed +params.omit.non.js.object.warning = true + +msg.non.js.object.warning =\ + RHINO USAGE WARNING: Missed Context.javaToJS() conversion: Rhino runtime detected object "{0}" of class "{1}" where it expected String, Number, Boolean or Scriptable instance. Please check your code for missing Context.javaToJS() call. + # Codegen msg.dup.parms =\ Duplicate parameter name "{0}". @@ -498,8 +503,8 @@ msg.destruct.assign.no.init =\ Missing = in destructuring declaration -msg.no.octal.strict =\ - Octal numbers prohibited in strict mode. +msg.no.old.octal.strict =\ + Old octal numbers prohibited in strict mode. msg.dup.obj.lit.prop.strict =\ Property "{0}" already defined in this object literal. @@ -526,6 +531,12 @@ msg.iterator.primitive =\ __iterator__ returned a primitive value +msg.not.iterable = \ + {0} is not iterable + +msg.invalid.for.each = \ + invalid for each loop + msg.assn.create.strict =\ Assignment to undeclared variable {0} @@ -701,6 +712,9 @@ msg.not.extensible =\ Cannot add properties to this object because extensible is false. +msg.delete.property.with.configurable.false =\ + Cannot delete "{0}" property because configurable is false. + # TokenStream msg.missing.exponent =\ missing exponent @@ -842,4 +856,30 @@ {0}.prototype.{1} method called on null or undefined msg.first.arg.not.regexp=\ - First argument to {0}.prototype.{1} must not be a regular expression \ No newline at end of file + First argument to {0}.prototype.{1} must not be a regular expression + +msg.arrowfunction.generator =\ + arrow function can not become generator + +# Arguments +msg.arguments.not.access.strict =\ + Cannot access "{0}" property of the arguments object in strict mode. + +# Symbol support +msg.object.not.symbolscriptable =\ + Object {0} does not support Symbol keys + +msg.no.assign.symbol.strict =\ + Symbol objects may not be assigned properties in strict mode + +msg.not.a.string =\ + The object is not a string + +msg.not.a.number =\ + The object is not 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 diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/ScriptableObject.java rhino-1.7.7.2/src/org/mozilla/javascript/ScriptableObject.java --- rhino-1.7.7.1/src/org/mozilla/javascript/ScriptableObject.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/ScriptableObject.java 2017-09-27 22:57:34.000000000 +0000 @@ -19,6 +19,8 @@ import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -44,7 +46,9 @@ * @author Norris Boyd */ -public abstract class ScriptableObject implements Scriptable, Serializable, +public abstract class ScriptableObject implements Scriptable, + SymbolScriptable, + Serializable, DebuggableObject, ConstProperties { @@ -148,7 +152,7 @@ private static class Slot implements Serializable { private static final long serialVersionUID = -6090581677123995491L; - String name; // This can change due to caching + Object name; // This can change due to caching int indexOrHash; private volatile short attributes; transient volatile boolean wasDeleted; @@ -156,7 +160,7 @@ transient Slot next; // next in hash table bucket transient volatile Slot orderedNext; // next in linked list - Slot(String name, int indexOrHash, int attributes) + Slot(Object name, int indexOrHash, int attributes) { this.name = name; this.indexOrHash = indexOrHash; @@ -174,6 +178,10 @@ 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) { @@ -230,7 +238,7 @@ Object getter; Object setter; - GetterSlot(String name, int indexOrHash, int attributes) + GetterSlot(Object name, int indexOrHash, int attributes) { super(name, indexOrHash, attributes); } @@ -242,6 +250,9 @@ 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; @@ -251,9 +262,11 @@ boolean setValue(Object value, Scriptable owner, Scriptable start) { if (setter == null) { if (getter != null) { - if (Context.getContext().hasFeature(Context.FEATURE_STRICT_MODE)) { + 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 @@ -414,7 +427,7 @@ * case it returns "undefined" */ public String getTypeOf() { - return avoidObjectDetection() ? "undefined" : "object"; + return avoidObjectDetection() ? "undefined" : "object"; } /** @@ -454,6 +467,14 @@ } /** + * A version of "has" that supports symbols. + */ + public boolean has(Symbol key, Scriptable start) + { + return null != getSlot(key, 0, SLOT_QUERY); + } + + /** * Returns the value of the named property or NOT_FOUND. * * If the property was created using defineProperty, the @@ -496,6 +517,18 @@ } /** + * Another version of Get that supports Symbol keyed properties. + */ + public Object get(Symbol key, Scriptable start) + { + Slot slot = getSlot(key, 0, SLOT_QUERY); + if (slot == null) { + return Scriptable.NOT_FOUND; + } + return slot.getValue(start); + } + + /** * Sets the value of the named property, creating it if need be. * * If the property was created using defineProperty, the @@ -549,6 +582,18 @@ } /** + * Implementation of put required by SymbolScriptable objects. + */ + 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); + } + + /** * Removes a named property from the object. * * If the property is not found, or it has the PERMANENT attribute, @@ -577,6 +622,15 @@ } /** + * Removes an object like the others, but using a Symbol as the key. + */ + public void delete(Symbol key) + { + checkNotSealed(key, 0); + removeSlot(key, 0); + } + + /** * Sets the value of the named const property, creating it if need be. * * If the property was created using defineProperty, the @@ -710,6 +764,12 @@ return findAttributeSlot(null, index, SLOT_QUERY).getAttributes(); } + public int getAttributes(Symbol sym) + { + return findAttributeSlot(sym, SLOT_QUERY).getAttributes(); + } + + /** * Set the attributes of a named property. * @@ -756,6 +816,15 @@ } /** + * 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, @@ -942,7 +1011,7 @@ * a String will have a String entry in the returned array. */ public Object[] getIds() { - return getIds(false); + return getIds(false, false); } /** @@ -957,7 +1026,7 @@ * a String will have a String entry in the returned array. */ public Object[] getAllIds() { - return getIds(true); + return getIds(true, false); } /** @@ -1615,6 +1684,17 @@ } /** + * A version of defineProperty that uses a Symbol key. + */ + 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 @@ -1830,7 +1910,7 @@ * @param props a map of property ids to property descriptors */ public void defineOwnProperties(Context cx, ScriptableObject props) { - Object[] ids = props.getIds(); + Object[] ids = props.getIds(false, true); ScriptableObject[] descs = new ScriptableObject[ids.length]; for (int i = 0, len = ids.length; i < len; ++i) { Object descObj = ScriptRuntime.getObjectElem(props, ids[i], cx); @@ -1874,8 +1954,7 @@ if (checkValid) { ScriptableObject current = slot == null ? null : slot.getPropertyDescriptor(cx, this); - String name = ScriptRuntime.toString(id); - checkPropertyChange(name, current, desc); + checkPropertyChange(id, current, desc); } boolean isAccessor = isAccessorDescriptor(desc); @@ -1939,7 +2018,7 @@ } } - protected void checkPropertyChange(String id, ScriptableObject current, + protected void checkPropertyChange(Object id, ScriptableObject current, ScriptableObject desc) { if (current == null) { // new property if (!isExtensible()) throw ScriptRuntime.typeError0("msg.not.extensible"); @@ -2079,6 +2158,12 @@ return (Scriptable) arg; } + protected static SymbolScriptable ensureSymbolScriptable(Object arg) { + if ( !(arg instanceof SymbolScriptable) ) + throw ScriptRuntime.typeError1("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)); @@ -2240,12 +2325,12 @@ return count < 0; } - private void checkNotSealed(String name, int index) + private void checkNotSealed(Object key, int index) { if (!isSealed()) return; - String str = (name != null) ? name : Integer.toString(index); + String str = (key != null) ? key.toString() : Integer.toString(index); throw Context.reportRuntimeError1("msg.modify.sealed", str); } @@ -2275,6 +2360,22 @@ } /** + * 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; + 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. *

@@ -2410,6 +2511,14 @@ } /** + * 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. *

* Searches for the named property in the prototype chain. If it is found, @@ -2433,6 +2542,17 @@ } /** + * 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; + ensureSymbolScriptable(base).put(key, obj, value); + } + + /** * Puts a named property in an object or in an object in its prototype chain. *

* Searches for the named property in the prototype chain. If it is found, @@ -2630,6 +2750,16 @@ return obj; } + private static Scriptable getBase(Scriptable obj, Symbol key) + { + do { + if (ensureSymbolScriptable(obj).has(key, obj)) + break; + obj = obj.getPrototype(); + } while(obj != null); + return obj; + } + /** * Get arbitrary application-specific value associated with this object. * @param key key object to select particular value. @@ -2697,32 +2827,38 @@ /** * - * @param name + * @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. */ - private boolean putImpl(String name, int index, Scriptable start, + 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(name, index, SLOT_QUERY); + slot = getSlot(key, index, SLOT_QUERY); if (slot == null) { return false; } } else if (!isExtensible) { - slot = getSlot(name, index, SLOT_QUERY); + slot = getSlot(key, index, SLOT_QUERY); if (slot == null) { return true; } } else { - if (count < 0) checkNotSealed(name, index); - slot = getSlot(name, index, SLOT_MODIFY); + if (count < 0) checkNotSealed(key, index); + slot = getSlot(key, index, SLOT_MODIFY); } return slot.setValue(value, this, start); } @@ -2743,6 +2879,12 @@ Object value, int constFlag) { assert (constFlag != EMPTY); + if (!isExtensible) { + Context cx = Context.getContext(); + if (cx.isStrictMode()) { + throw ScriptRuntime.typeError0("msg.not.extensible"); + } + } Slot slot; if (this != start) { slot = getSlot(name, index, SLOT_QUERY); @@ -2782,6 +2924,15 @@ return slot; } + private Slot findAttributeSlot(Symbol key, int accessType) + { + Slot slot = getSlot(key, 0, accessType); + if (slot == null) { + throw Context.reportRuntimeError1("msg.prop.not.found", key); + } + return slot; + } + private static Slot unwrapSlot(Slot slot) { return (slot instanceof RelinkedSlot) ? ((RelinkedSlot)slot).slot : slot; } @@ -2790,10 +2941,10 @@ * 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 name property name or null if slot holds spare array index. + * @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(String name, int index, int accessType) + private Slot getSlot(Object key, int index, int accessType) { // Check the hashtable without using synchronization Slot[] slotsLocalRef = slots; // Get stable local reference @@ -2801,17 +2952,17 @@ return null; } - int indexOrHash = (name != null ? name.hashCode() : index); + 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 sname = slot.name; + Object skey = slot.name; if (indexOrHash == slot.indexOrHash && - (sname == name || - (name != null && name.equals(sname)))) { + (skey == key || + (key != null && key.equals(skey)))) { break; } } @@ -2838,10 +2989,10 @@ // A new slot has to be inserted or the old has to be replaced // by GetterSlot. Time to synchronize. - return createSlot(name, indexOrHash, accessType); + return createSlot(key, indexOrHash, accessType); } - private synchronized Slot createSlot(String name, int indexOrHash, int accessType) { + private synchronized Slot createSlot(Object key, int indexOrHash, int accessType) { Slot[] slotsLocalRef = slots; int insertPos; if (count == 0) { @@ -2856,8 +3007,8 @@ Slot slot = prev; while (slot != null) { if (slot.indexOrHash == indexOrHash && - (slot.name == name || - (name != null && name.equals(slot.name)))) + (slot.name == key || + (key != null && key.equals(slot.name)))) { break; } @@ -2877,10 +3028,10 @@ if (accessType == SLOT_MODIFY_GETTER_SETTER && !(inner instanceof GetterSlot)) { - newSlot = new GetterSlot(name, indexOrHash, inner.getAttributes()); + newSlot = new GetterSlot(key, indexOrHash, inner.getAttributes()); } else if (accessType == SLOT_CONVERT_ACCESSOR_TO_DATA && (inner instanceof GetterSlot)) { - newSlot = new Slot(name, indexOrHash, inner.getAttributes()); + newSlot = new Slot(key, indexOrHash, inner.getAttributes()); } else if (accessType == SLOT_MODIFY_CONST) { return null; } else { @@ -2919,8 +3070,8 @@ } } Slot newSlot = (accessType == SLOT_MODIFY_GETTER_SETTER - ? new GetterSlot(name, indexOrHash, 0) - : new Slot(name, indexOrHash, 0)); + ? new GetterSlot(key, indexOrHash, 0) + : new Slot(key, indexOrHash, 0)); if (accessType == SLOT_MODIFY_CONST) newSlot.setAttributes(CONST); ++count; @@ -2935,8 +3086,8 @@ return newSlot; } - private synchronized void removeSlot(String name, int index) { - int indexOrHash = (name != null ? name.hashCode() : index); + private synchronized void removeSlot(Object key, int index) { + int indexOrHash = (key != null ? key.hashCode() : index); Slot[] slotsLocalRef = slots; if (count != 0) { @@ -2946,15 +3097,23 @@ Slot slot = prev; while (slot != null) { if (slot.indexOrHash == indexOrHash && - (slot.name == name || - (name != null && name.equals(slot.name)))) + (slot.name == key || + (key != null && key.equals(slot.name)))) { break; } prev = slot; slot = slot.next; } - if (slot != null && (slot.getAttributes() & PERMANENT) == 0) { + 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) { @@ -3039,7 +3198,7 @@ } } - Object[] getIds(boolean getAll) { + Object[] getIds(boolean getNonEnumerable, boolean getSymbols) { Slot[] s = slots; Object[] a; int externalLen = (externalData == null ? 0 : externalData.getArrayLength()); @@ -3066,8 +3225,10 @@ slot = slot.orderedNext; } while (slot != null) { - if (getAll || (slot.getAttributes() & DONTENUM) == 0) { + 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) { @@ -3089,6 +3250,13 @@ } 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)) { + // Move all the numeric IDs to the front in numeric order + Arrays.sort(result, KEY_COMPARATOR); + } + return result; } @@ -3171,6 +3339,9 @@ } protected Slot getSlot(Context cx, Object id, int accessType) { + if (id instanceof Symbol) { + return getSlot(id, 0, accessType); + } String name = ScriptRuntime.toStringIdOrIndex(cx, id); if (name == null) { return getSlot(null, ScriptRuntime.lastIndexResult(cx), accessType); @@ -3195,6 +3366,8 @@ Object value = null; if (key instanceof String) { value = get((String) key, this); + } else if (key instanceof Symbol) { + value = get((Symbol) key, this); } else if (key instanceof Number) { value = get(((Number) key).intValue(), this); } @@ -3207,4 +3380,39 @@ } } + private static final Comparator KEY_COMPARATOR = new KeyComparator(); + + /** + * 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 + { + @Override + 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 -1; + } + if (o2 instanceof Integer) { + return 1; + } + return 0; + } + } } diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/ScriptRuntimeES6.java rhino-1.7.7.2/src/org/mozilla/javascript/ScriptRuntimeES6.java --- rhino-1.7.7.1/src/org/mozilla/javascript/ScriptRuntimeES6.java 1970-01-01 00:00:00.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/ScriptRuntimeES6.java 2017-09-27 22:57:34.000000000 +0000 @@ -0,0 +1,17 @@ +/* -*- 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 class ScriptRuntimeES6 { + + public static Scriptable requireObjectCoercible(Context cx, Scriptable val, IdFunctionObject idFuncObj) { + if (val == null || Undefined.isUndefined(val)) { + throw ScriptRuntime.typeError2("msg.called.null.or.undefined", idFuncObj.getTag(), idFuncObj.getFunctionName()); + } + return val; + } +} diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/ScriptRuntime.java rhino-1.7.7.2/src/org/mozilla/javascript/ScriptRuntime.java --- rhino-1.7.7.1/src/org/mozilla/javascript/ScriptRuntime.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/ScriptRuntime.java 2017-09-27 22:57:34.000000000 +0000 @@ -7,7 +7,7 @@ package org.mozilla.javascript; import java.io.Serializable; -import java.lang.reflect.*; +import java.lang.reflect.Constructor; import java.text.MessageFormat; import java.util.Locale; import java.util.ResourceBundle; @@ -198,6 +198,9 @@ NativeIterator.init(scope, sealed); // Also initializes NativeGenerator + NativeArrayIterator.init(scope, sealed); + NativeStringIterator.init(scope, sealed); + boolean withXml = cx.hasFeature(Context.FEATURE_E4X) && cx.getE4xImplementationFactory() != null; @@ -253,7 +256,11 @@ "org.mozilla.javascript.typedarrays.NativeDataView", sealed, true); } - + + if (cx.getLanguageVersion() >= Context.VERSION_ES6) { + NativeSymbol.init(cx, scope, sealed); + } + if (scope instanceof TopLevel) { ((TopLevel)scope).cacheBuiltins(); } @@ -283,7 +290,7 @@ return s; } - + static String[] getTopPackageNames() { // Include "android" top package if running on Android return "Dalvik".equals(System.getProperty("java.vm.name")) ? @@ -331,21 +338,21 @@ */ static boolean isStrWhiteSpaceChar(int c) { - switch (c) { - case ' ': // - case '\n': // - case '\r': // - case '\t': // - case '\u00A0': // - case '\u000C': // - case '\u000B': // - case '\u2028': // - case '\u2029': // - case '\uFEFF': // - return true; - default: - return Character.getType(c) == Character.SPACE_SEPARATOR; - } + switch (c) { + case ' ': // + case '\n': // + case '\r': // + case '\t': // + case '\u00A0': // + case '\u000C': // + case '\u000B': // + case '\u2028': // + case '\u2029': // + case '\uFEFF': // + return true; + default: + return Character.getType(c) == Character.SPACE_SEPARATOR; + } } public static Boolean wrapBoolean(boolean b) @@ -396,7 +403,7 @@ } // ECMA extension val = ((Scriptable) val).getDefaultValue(BooleanClass); - if (val instanceof Scriptable) + if ((val instanceof Scriptable) && !isSymbol(val)) throw errorWithClassName("msg.primitive.expected", val); continue; } @@ -425,9 +432,11 @@ 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"); if (val instanceof Scriptable) { val = ((Scriptable) val).getDefaultValue(NumberClass); - if (val instanceof Scriptable) + if ((val instanceof Scriptable) && !isSymbol(val)) throw errorWithClassName("msg.primitive.expected", val); continue; } @@ -773,7 +782,7 @@ return (sb == null) ? s : sb.toString(); } - static boolean isValidIdentifierName(String s) + static boolean isValidIdentifierName(String s, Context cx, boolean isStrict) { int L = s.length(); if (L == 0) @@ -784,7 +793,7 @@ if (!Character.isJavaIdentifierPart(s.charAt(i))) return false; } - return !TokenStream.isKeyword(s); + return !TokenStream.isKeyword(s, cx.getLanguageVersion(), isStrict); } public static CharSequence toCharSequence(Object val) { @@ -804,7 +813,7 @@ if (val == null) { return "null"; } - if (val == Undefined.instance) { + if (val == Undefined.instance || val == Undefined.SCRIPTABLE_UNDEFINED) { return "undefined"; } if (val instanceof String) { @@ -818,9 +827,12 @@ // about Numbers? return numberToString(((Number)val).doubleValue(), 10); } + if (val instanceof Symbol) { + throw typeError0("msg.not.a.string"); + } if (val instanceof Scriptable) { val = ((Scriptable) val).getDefaultValue(StringClass); - if (val instanceof Scriptable) { + if ((val instanceof Scriptable) && !isSymbol(val)) { throw errorWithClassName("msg.primitive.expected", val); } continue; @@ -831,6 +843,10 @@ static String defaultObjectToString(Scriptable obj) { + if (obj == null) + return "[object Null]"; + if (Undefined.isUndefined(obj)) + return "[object Undefined]"; return "[object " + obj.getClassName() + ']'; } @@ -963,7 +979,7 @@ continue; // a property has been removed if (i > 0) result.append(", "); - if (ScriptRuntime.isValidIdentifierName(strId)) { + if (ScriptRuntime.isValidIdentifierName(strId, cx, cx.isStrictMode())) { result.append(strId); } else { result.append('\''); @@ -1048,6 +1064,11 @@ */ public static Scriptable toObject(Context cx, Scriptable scope, Object val) { + if (isSymbol(val)) { + NativeSymbol result = new NativeSymbol((NativeSymbol)val); + setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Symbol); + return result; + } if (val instanceof Scriptable) { return (Scriptable) val; } @@ -1440,7 +1461,7 @@ @Deprecated public static Object getObjectElem(Object obj, Object elem, Context cx) { - return getObjectElem(obj, elem, cx, getTopCallScope(cx)); + return getObjectElem(obj, elem, cx, getTopCallScope(cx)); } /** @@ -1463,6 +1484,8 @@ if (obj instanceof XMLObject) { result = ((XMLObject)obj).get(cx, elem); + } else if (isSymbol(elem)) { + result = ScriptableObject.getProperty(obj, (Symbol)elem); } else { String s = toStringIdOrIndex(cx, elem); if (s == null) { @@ -1622,6 +1645,8 @@ { if (obj instanceof XMLObject) { ((XMLObject)obj).put(cx, elem, value); + } else if (isSymbol(elem)) { + ScriptableObject.putProperty(obj, (Symbol)elem, value); } else { String s = toStringIdOrIndex(cx, elem); if (s == null) { @@ -1713,6 +1738,12 @@ public static boolean deleteObjectElem(Scriptable target, Object elem, Context cx) { + if (isSymbol(elem)) { + SymbolScriptable so = ScriptableObject.ensureSymbolScriptable(target); + Symbol s = (Symbol)elem; + so.delete(s); + return !so.has(s, target); + } String s = toStringIdOrIndex(cx, elem); if (s == null) { int index = lastIndexResult(cx); @@ -1729,12 +1760,16 @@ { boolean result; - String s = toStringIdOrIndex(cx, elem); - if (s == null) { - int index = lastIndexResult(cx); - result = ScriptableObject.hasProperty(target, index); + if (isSymbol(elem)) { + result = ScriptableObject.hasProperty(target, (Symbol)elem); } else { - result = ScriptableObject.hasProperty(target, s); + String s = toStringIdOrIndex(cx, elem); + if (s == null) { + int index = lastIndexResult(cx); + result = ScriptableObject.hasProperty(target, index); + } else { + result = ScriptableObject.hasProperty(target, s); + } } return result; @@ -2093,7 +2128,7 @@ ObjToIntMap used; Object currentId; int enumType; /* one of ENUM_INIT_KEYS, ENUM_INIT_VALUES, - ENUM_INIT_ARRAY */ + ENUM_INIT_ARRAY, ENUMERATE_VALUES_IN_ORDER */ // if true, integer ids will be returned as numbers rather than strings boolean enumNumbers; @@ -2142,6 +2177,7 @@ public static final int ENUMERATE_KEYS_NO_ITERATOR = 3; public static final int ENUMERATE_VALUES_NO_ITERATOR = 4; 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 @@ -2157,6 +2193,12 @@ { IdEnumeration x = new IdEnumeration(); x.obj = toObjectOrNull(cx, value, scope); + // "for of" loop + if (enumType == ENUMERATE_VALUES_IN_ORDER) { + x.enumType = enumType; + x.iterator = null; + return enumInitInOrder(cx, x); + } if (x.obj == null) { // null or undefined do not cause errors but rather lead to empty // "for in" loop @@ -2180,6 +2222,30 @@ return x; } + private static Object enumInitInOrder(Context cx, IdEnumeration x) { + if (!(x.obj instanceof ScriptableObject)) { + throw typeError1("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); + if (!(iterator instanceof Callable)) { + throw typeError1("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)); + } + x.iterator = (Scriptable) v; + return x; + } + public static void setEnumNumbers(Object enumObj, boolean enumNumbers) { ((IdEnumeration)enumObj).enumNumbers = enumNumbers; } @@ -2188,6 +2254,9 @@ { 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; @@ -2199,7 +2268,7 @@ return Boolean.TRUE; } catch (JavaScriptException e) { if (e.getValue() instanceof NativeIterator.StopIteration) { - return Boolean.FALSE; + return Boolean.FALSE; } throw e; } @@ -2217,8 +2286,10 @@ if (x.used != null && x.used.has(id)) { continue; } - if (id instanceof String) { - String strId = (String)id; + if (id instanceof Symbol) { + continue; + } else if (id instanceof String) { + String strId = (String) id; if (!x.obj.has(strId, x.obj)) continue; // must have been deleted x.currentId = strId; @@ -2233,6 +2304,25 @@ } } + 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); + } + Callable f = (Callable) v; + Context cx = Context.getContext(); + Scriptable scope = enumObj.iterator.getParentScope(); + 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)) { + return Boolean.FALSE; + } + enumObj.currentId = ScriptableObject.getProperty(iteratorResult, ES6Iterator.VALUE_PROPERTY); + return Boolean.TRUE; + } + public static Object enumId(Object enumObj, Context cx) { IdEnumeration x = (IdEnumeration)enumObj; @@ -2260,12 +2350,17 @@ Object result; - String s = toStringIdOrIndex(cx, x.currentId); - if (s == null) { - int index = lastIndexResult(cx); - result = x.obj.get(index, x.obj); + if (isSymbol(x.currentId)) { + SymbolScriptable so = ScriptableObject.ensureSymbolScriptable(x.obj); + result = so.get((Symbol)x.currentId, x.obj); } else { - result = x.obj.get(s, x.obj); + String s = toStringIdOrIndex(cx, x.currentId); + if (s == null) { + int index = lastIndexResult(cx); + result = x.obj.get(index, x.obj); + } else { + result = x.obj.get(s, x.obj); + } } return result; @@ -2353,18 +2448,31 @@ public static Callable getElemFunctionAndThis(Object obj, Object elem, Context cx, Scriptable scope) { - String str = toStringIdOrIndex(cx, elem); - if (str != null) { - return getPropFunctionAndThis(obj, str, cx, scope); - } - int index = lastIndexResult(cx); + Scriptable thisObj; + Object value; - Scriptable thisObj = toObjectOrNull(cx, obj, scope); - if (thisObj == null) { - throw undefCallError(obj, String.valueOf(index)); + if (isSymbol(elem)) { + thisObj = toObjectOrNull(cx, obj, scope); + if (thisObj == null) { + throw undefCallError(obj, String.valueOf(elem)); + } + value = ScriptableObject.getProperty(thisObj, (Symbol)elem); + + } else { + String str = toStringIdOrIndex(cx, elem); + if (str != null) { + return getPropFunctionAndThis(obj, str, 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); } - Object value = ScriptableObject.getProperty(thisObj, index); if (!(value instanceof Callable)) { throw notFunctionError(value, elem); } @@ -2561,11 +2669,14 @@ Scriptable callThis = null; if (L != 0) { - callThis = toObjectOrNull(cx, args[0], scope); + 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); + } } - if (callThis == null) { - // This covers the case of args[0] == (null|undefined) as well. - callThis = getTopCallScope(cx); + 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. } Object[] callArgs; @@ -2674,7 +2785,7 @@ if (value == Undefined.instance) return "undefined"; if (value instanceof ScriptableObject) - return ((ScriptableObject) value).getTypeOf(); + return ((ScriptableObject) value).getTypeOf(); if (value instanceof Scriptable) return (value instanceof Callable) ? "function" : "object"; if (value instanceof CharSequence) @@ -2728,6 +2839,9 @@ return test; } } + if ((val1 instanceof Symbol) || (val2 instanceof Symbol)) { + throw typeError0("msg.not.a.number"); + } if (val1 instanceof Scriptable) val1 = ((Scriptable) val1).getDefaultValue(null); if (val2 instanceof Scriptable) @@ -2947,7 +3061,7 @@ } Scriptable s = (Scriptable)val; Object result = s.getDefaultValue(typeHint); - if (result instanceof Scriptable) + if ((result instanceof Scriptable) && !isSymbol(result)) throw typeError0("msg.bad.default.value"); return result; } @@ -3035,6 +3149,38 @@ } } + /* + * Implement "SameValue" as in ECMA 7.2.9. This is not the same as "eq" because it handles + * signed zeroes and NaNs differently. + */ + public static boolean same(Object x, Object y) { + if (!typeof(x).equals(typeof(y))) { + return false; + } + if (x instanceof Number) { + if (isNaN(x) && isNaN(y)) { + return true; + } + return x.equals(y); + } + return eq(x, y); + } + + public static boolean isNaN(Object n) { + if (n == NaNobj) { + return true; + } + if (n instanceof Double) { + Double d = (Double)n; + return ((d == NaN) || Double.isNaN(d)); + } + if (n instanceof Float) { + Float f = (Float)n; + return ((f == NaN) || Float.isNaN(f)); + } + return false; + } + public static boolean isPrimitive(Object obj) { return obj == null || obj == Undefined.instance || (obj instanceof Number) || (obj instanceof String) || @@ -3051,7 +3197,9 @@ } else if (y instanceof CharSequence) { return x == toNumber(y); } else if (y instanceof Boolean) { - return x == (((Boolean)y).booleanValue() ? 1.0 : +0.0); + return x == (((Boolean) y).booleanValue() ? 1.0 : +0.0); + } else if (isSymbol(y)) { + return false; } else if (y instanceof Scriptable) { if (y instanceof ScriptableObject) { Object xval = wrapNumber(x); @@ -3079,7 +3227,9 @@ } else if (y instanceof Number) { return toNumber(x.toString()) == ((Number)y).doubleValue(); } else if (y instanceof Boolean) { - return toNumber(x.toString()) == (((Boolean)y).booleanValue() ? 1.0 : 0.0); + 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()); @@ -3105,7 +3255,9 @@ double d = ((Number)x).doubleValue(); return d == d; } - if (x == null || x == Undefined.instance) { + 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; return false; } else if (x instanceof Number) { if (y instanceof Number) { @@ -3195,6 +3347,9 @@ d1 = ((Number)val1).doubleValue(); d2 = ((Number)val2).doubleValue(); } else { + if ((val1 instanceof Symbol) || (val2 instanceof Symbol)) { + throw typeError0("msg.compare.symbol"); + } if (val1 instanceof Scriptable) val1 = ((Scriptable) val1).getDefaultValue(NumberClass); if (val2 instanceof Scriptable) @@ -3215,6 +3370,9 @@ d1 = ((Number)val1).doubleValue(); d2 = ((Number)val2).doubleValue(); } else { + if ((val1 instanceof Symbol) || (val2 instanceof Symbol)) { + throw typeError0("msg.compare.symbol"); + } if (val1 instanceof Scriptable) val1 = ((Scriptable) val1).getDefaultValue(NumberClass); if (val2 instanceof Scriptable) @@ -3266,10 +3424,20 @@ return scope; } + /** + * @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) { + 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(); if (cx.topCallScope != null) throw new IllegalStateException(); @@ -3277,6 +3445,8 @@ Object result; cx.topCallScope = ScriptableObject.getTopLevelScope(scope); cx.useDynamicScope = cx.hasFeature(Context.FEATURE_DYNAMIC_SCOPE); + boolean previousTopLevelStrict = cx.isTopLevelStrict; + cx.isTopLevelStrict = isTopLevelStrict; ContextFactory f = cx.getFactory(); try { result = f.doTopCall(callable, cx, scope, thisObj, args); @@ -3284,6 +3454,7 @@ cx.topCallScope = null; // Cleanup cached references cx.cachedXMLLib = null; + cx.isTopLevelStrict = previousTopLevelStrict; if (cx.currentActivationCall != null) { // Function should always call exitActivationFunction @@ -3321,7 +3492,7 @@ public static void addInstructionCount(Context cx, int instructionsToAdd) { - cx.instructionCount += instructionsToAdd; + cx.instructionCount += instructionsToAdd; if (cx.instructionCount > cx.instructionThreshold) { cx.observeInstructionCount(cx.instructionCount); @@ -3369,13 +3540,32 @@ } } + /** + * @deprecated Use {@link #createFunctionActivation(NativeFunction, Scriptable, Object[], boolean)} instead + */ + @Deprecated public static Scriptable createFunctionActivation(NativeFunction funObj, Scriptable scope, Object[] args) { - return new NativeCall(funObj, scope, args); + return createFunctionActivation(funObj, scope, args, false); } + 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) + { + return new NativeCall(funObj, scope, args, true, isStrict); + } public static void enterActivationFunction(Context cx, Scriptable scope) @@ -3385,6 +3575,7 @@ NativeCall call = (NativeCall)scope; call.parentActivationCall = cx.currentActivationCall; cx.currentActivationCall = call; + call.defineAttributesForArguments(); } public static void exitActivationFunction(Context cx) @@ -3740,7 +3931,7 @@ ++skip; continue; } - ScriptableObject.putProperty(array, i, objects[j]); + array.put(i, array, objects[j]); ++j; } return array; @@ -4064,12 +4255,13 @@ private static void warnAboutNonJSObject(Object nonJSObject) { - String message = -"RHINO USAGE WARNING: Missed Context.javaToJS() conversion:\n" -+"Rhino runtime detected object "+nonJSObject+" of class "+nonJSObject.getClass().getName()+" where it expected String, Number, Boolean or Scriptable instance. Please check your code for missing Context.javaToJS() call."; - Context.reportWarning(message); - // Just to be sure that it would be noticed - System.err.println(message); + final String omitParam = ScriptRuntime.getMessage0("params.omit.non.js.object.warning"); + if (!"true".equals(omitParam)) { + String message = ScriptRuntime.getMessage2("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) @@ -4229,6 +4421,15 @@ || sourceUrl.indexOf("(Function)") >= 0; } + /** + * 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); + } + private static RuntimeException errorWithClassName(String msg, Object val) { return Context.reportRuntimeError1(msg, val.getClass().getName()); @@ -4263,7 +4464,7 @@ int[] linep = { 0 }; String filename = Context.getSourcePositionFromStack(linep); final Scriptable error = cx.newObject(scope, constructorName, - new Object[] { message, filename, Integer.valueOf(linep[0]) }); + new Object[] { message, filename, Integer.valueOf(linep[0]) }); return new JavaScriptException(error, filename, linep[0]); } @@ -4271,12 +4472,4 @@ public static final String[] emptyStrings = new String[0]; - public static Scriptable requireObjectCoercible(Scriptable val, IdFunctionObject idFuncObj) { - Scriptable val1 = val.getParentScope() != null ? val : null; - if (val1 == null || val1 == Undefined.instance) - throw ScriptRuntime.typeError2("msg.called.null.or.undefined", idFuncObj.getTag(), idFuncObj.getFunctionName()); - - return val1; - } - } diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/ScriptStackElement.java rhino-1.7.7.2/src/org/mozilla/javascript/ScriptStackElement.java --- rhino-1.7.7.1/src/org/mozilla/javascript/ScriptStackElement.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/ScriptStackElement.java 2017-09-27 22:57:34.000000000 +0000 @@ -15,7 +15,7 @@ public final class ScriptStackElement implements Serializable { static final long serialVersionUID = -6416688260860477449L; - + public final String fileName; public final String functionName; public final int lineNumber; @@ -36,6 +36,7 @@ /** * Render stack element in Java-inspired style: * at fileName:lineNumber (functionName) + * * @param sb the StringBuilder to append to */ public void renderJavaStyle(StringBuilder sb) { @@ -51,6 +52,7 @@ /** * Render stack element in Mozilla/Firefox style: * functionName()@fileName:lineNumber + * * @param sb the StringBuilder to append to */ public void renderMozillaStyle(StringBuilder sb) { @@ -68,12 +70,14 @@ * at functionName (fileName:lineNumber:columnNumber) * or: * at fileName:lineNumber:columnNumber + * * @param sb the StringBuilder to append to */ public void renderV8Style(StringBuilder sb) { sb.append(" at "); - if ((functionName == null) || "anonymous".equals(functionName) || "undefined".equals(functionName)) { + if ((functionName == null) || "anonymous".equals(functionName) || "undefined" + .equals(functionName)) { // Anonymous functions in V8 don't have names in the stack trace appendV8Location(sb); @@ -84,11 +88,8 @@ } } - private void appendV8Location(StringBuilder sb) - { - sb.append(fileName); - if (lineNumber > -1) { - sb.append(':').append(lineNumber); - } + private void appendV8Location(StringBuilder sb) { + sb.append(fileName).append(':'); + sb.append(lineNumber > -1 ? lineNumber : 0).append(":0"); } } diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/Sorting.java rhino-1.7.7.2/src/org/mozilla/javascript/Sorting.java --- rhino-1.7.7.1/src/org/mozilla/javascript/Sorting.java 1970-01-01 00:00:00.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/Sorting.java 2017-09-27 22:57:34.000000000 +0000 @@ -0,0 +1,94 @@ +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 cmp) + { + insertionSort(a, 0, a.length - 1, cmp); + } + + public static void insertionSort(Object[] a, int start, int end, Comparator cmp) + { + int i = start; + while (i <= end) { + Object x = a[i]; + int j = i - 1; + while ((j >= start) && (cmp.compare(a[j], x) > 0)) { + a[j + 1] = a[j]; + j--; + } + a[j + 1] = x; + i++; + } + } + + /* + Hybrid sorting mechanism similar to Introsort by David Musser. Uses quicksort's + partitioning mechanism recursively until the resulting array is small or the + 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 cmp) + { + hybridSort(a, 0, a.length - 1, cmp, log2(a.length) * 2); + } + + private static void hybridSort(Object[] a, int start, int end, Comparator cmp, int maxdepth) + { + if (start < end) { + if ((maxdepth == 0) || ((end - start) <= SMALLSORT)) { + insertionSort(a, start, end, cmp); + } else { + int p = partition(a, start, end, cmp); + hybridSort(a, start, p, cmp, maxdepth - 1); + hybridSort(a, p + 1, end, cmp, maxdepth - 1); + } + } + } + + /* + Quicksort-style partitioning, using the Hoare partition scheme described on Wikipedia. + 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 cmp) { + Object pivot = a[median(start, end, start + ((end - start) / 2))]; + int i = start - 1; + int j = end + 1; + while (true) { + do { + i++; + } while (cmp.compare(a[i], pivot) < 0); + do { + j--; + } while (cmp.compare(a[j], pivot) > 0); + if (i >= j) { + return j; + } + swap(a, i, j); + } + } + + private static void swap(Object[] a, int l, int h) + { + Object tmp = a[l]; + a[l] = a[h]; + a[h] = tmp; + } + + private static int log2(int n) + { + return (int)(Math.log10(n) / Math.log10(2.0)); + } + + private static int median(int n1, int n2, int n3) + { + int[] a = {n1, n2, n3}; + Arrays.sort(a); + return a[1]; + } +} diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/Symbol.java rhino-1.7.7.2/src/org/mozilla/javascript/Symbol.java --- rhino-1.7.7.1/src/org/mozilla/javascript/Symbol.java 1970-01-01 00:00:00.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/Symbol.java 2017-09-27 22:57:34.000000000 +0000 @@ -0,0 +1,19 @@ +/* -*- 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 Symbol is a JavaScript objecy that obeys the special properties of the + * Symbol prototype. This interface lets us possibly support multiple + * implementations of Symbol. + * + * @since 1.7.8 + */ + +public interface Symbol +{ +} diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/SymbolKey.java rhino-1.7.7.2/src/org/mozilla/javascript/SymbolKey.java --- rhino-1.7.7.1/src/org/mozilla/javascript/SymbolKey.java 1970-01-01 00:00:00.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/SymbolKey.java 2017-09-27 22:57:34.000000000 +0000 @@ -0,0 +1,69 @@ +package org.mozilla.javascript; + +import java.io.Serializable; + +/** + * A SymbolKey is one of the implementations of Symbol. It is really there + * so that we can easily use pre-defined symbols as keys in native code. + * A SymbolKey has the special property that two NativeSymbol objects + * with the same key are equal. + */ + +public class SymbolKey + implements Symbol, Serializable +{ + static final long serialVersionUID = -6019782713330994754L; + + // These are common SymbolKeys that are equivalent to well-known symbols + // defined in ECMAScript. + public static final SymbolKey ITERATOR = new SymbolKey("Symbol.iterator"); + public static final SymbolKey TO_STRING_TAG = new SymbolKey("Symbol.toStringTag"); + public static final SymbolKey SPECIES = new SymbolKey("Symbol.species"); + public static final SymbolKey HAS_INSTANCE = new SymbolKey("Symbol.hasInstance"); + public static final SymbolKey IS_CONCAT_SPREADABLE = new SymbolKey("Symbol.isConcatSpreadable"); + public static final SymbolKey IS_REGEXP = new SymbolKey("Symbol.isRegExp"); + public static final SymbolKey TO_PRIMITIVE = new SymbolKey("Symbol.toPrimitive"); + public static final SymbolKey MATCH = new SymbolKey("Symbol.match"); + public static final SymbolKey REPLACE = new SymbolKey("Symbol.replace"); + public static final SymbolKey SEARCH = new SymbolKey("Symbol.search"); + public static final SymbolKey SPLIT = new SymbolKey("Symbol.split"); + public static final SymbolKey UNSCOPABLES = new SymbolKey("Symbol.unscopables"); + + private String name; + + public SymbolKey(String name) + { + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public int hashCode() + { + return System.identityHashCode(this); + } + + @Override + public boolean equals(Object o) + { + if (o instanceof SymbolKey) { + return o == this; + } + if (o instanceof NativeSymbol) { + return ((NativeSymbol) o).getKey() == this; + } + return false; + } + + @Override + public String toString() + { + if (name == null) { + return "Symbol()"; + } + return "Symbol(" + name + ')'; + } +} diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/SymbolScriptable.java rhino-1.7.7.2/src/org/mozilla/javascript/SymbolScriptable.java --- rhino-1.7.7.1/src/org/mozilla/javascript/SymbolScriptable.java 1970-01-01 00:00:00.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/SymbolScriptable.java 2017-09-27 22:57:34.000000000 +0000 @@ -0,0 +1,41 @@ +/* -*- 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 interface may be combined with any object that implements Scriptable + * to add support for properties keyed by Symbol objects (as opposed to + * String and number objects as in previous versions of JavaScript. + * It's separated into its own interface so that the addition of Symbol + * support does not break compatibility for existing code. + * + * @since 1.7.8 + */ + +public interface SymbolScriptable +{ + /** + * Return the value of the property with the specified key, or + * NOT_FOUND. + */ + Object get(Symbol key, Scriptable start); + + /** + * Return true if the specified property exists. + */ + boolean has(Symbol key, Scriptable start); + + /** + * Add a new property to to the object. + */ + void put(Symbol key, Scriptable start, Object value); + + /** + * Delete a property with the specified key. + */ + void delete(Symbol key); +} diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/Token.java rhino-1.7.7.2/src/org/mozilla/javascript/Token.java --- rhino-1.7.7.1/src/org/mozilla/javascript/Token.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/Token.java 2017-09-27 22:57:34.000000000 +0000 @@ -102,134 +102,136 @@ ENUM_INIT_KEYS = 58, ENUM_INIT_VALUES = 59, ENUM_INIT_ARRAY= 60, - ENUM_NEXT = 61, - ENUM_ID = 62, - THISFN = 63, - RETURN_RESULT = 64, // to return previously stored return result - ARRAYLIT = 65, // array literal - OBJECTLIT = 66, // object literal - GET_REF = 67, // *reference - SET_REF = 68, // *reference = something - DEL_REF = 69, // delete reference - REF_CALL = 70, // f(args) = something or f(args)++ - REF_SPECIAL = 71, // reference for special properties like __proto - YIELD = 72, // JS 1.7 yield pseudo keyword - STRICT_SETNAME = 73, + 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 = 74, // default xml namespace = - ESCXMLATTR = 75, - ESCXMLTEXT = 76, - REF_MEMBER = 77, // Reference for x.@y, x..y etc. - REF_NS_MEMBER = 78, // Reference for x.ns::y, x..ns::y etc. - REF_NAME = 79, // Reference for @y, @[y] etc. - REF_NS_NAME = 80; // Reference for ns::y, @ns::y@[y] etc. + DEFAULTNAMESPACE = 75, // default xml namespace = + ESCXMLATTR = 76, + ESCXMLTEXT = 77, + REF_MEMBER = 78, // Reference for x.@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 = 81, - SEMI = 82, // semicolon - LB = 83, // left and right brackets - RB = 84, - LC = 85, // left and right curlies (braces) - RC = 86, - LP = 87, // left and right parentheses - RP = 88, - COMMA = 89, // comma operator - - ASSIGN = 90, // simple assignment (=) - ASSIGN_BITOR = 91, // |= - ASSIGN_BITXOR = 92, // ^= - ASSIGN_BITAND = 93, // |= - ASSIGN_LSH = 94, // <<= - ASSIGN_RSH = 95, // >>= - ASSIGN_URSH = 96, // >>>= - ASSIGN_ADD = 97, // += - ASSIGN_SUB = 98, // -= - ASSIGN_MUL = 99, // *= - ASSIGN_DIV = 100, // /= - ASSIGN_MOD = 101; // %= + 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 = 102, // conditional (?:) - COLON = 103, - OR = 104, // logical or (||) - AND = 105, // logical and (&&) - INC = 106, // increment/decrement (++ --) - DEC = 107, - DOT = 108, // member operator (.) - FUNCTION = 109, // function keyword - EXPORT = 110, // export keyword - IMPORT = 111, // import keyword - IF = 112, // if keyword - ELSE = 113, // else keyword - SWITCH = 114, // switch keyword - CASE = 115, // case keyword - DEFAULT = 116, // default keyword - WHILE = 117, // while keyword - DO = 118, // do keyword - FOR = 119, // for keyword - BREAK = 120, // break keyword - CONTINUE = 121, // continue keyword - VAR = 122, // var keyword - WITH = 123, // with keyword - CATCH = 124, // catch keyword - FINALLY = 125, // finally keyword - VOID = 126, // void keyword - RESERVED = 127, // reserved keywords + 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 = 128, + EMPTY = 129, /* types used for the parse tree - these never get returned * by the scanner. */ - BLOCK = 129, // statement block - LABEL = 130, // label - TARGET = 131, - LOOP = 132, - EXPR_VOID = 133, // expression statement in functions - EXPR_RESULT = 134, // expression statement in scripts - JSR = 135, - SCRIPT = 136, // top-level node for entire script - TYPEOFNAME = 137, // for typeof(simple-name) - USE_STACK = 138, - SETPROP_OP = 139, // x.y op= something - SETELEM_OP = 140, // x[y] op= something - LOCAL_BLOCK = 141, - SET_REF_OP = 142, // *reference op= something + 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 = 143, // member operator (..) - COLONCOLON = 144, // namespace::name - XML = 145, // XML type - DOTQUERY = 146, // .() -- e.g., x.emps.emp.(name == "terry") - XMLATTR = 147, // @ - XMLEND = 148, + 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 = 149, - TO_DOUBLE = 150, + TO_OBJECT = 150, + TO_DOUBLE = 151, - GET = 151, // JS 1.5 get pseudo keyword - SET = 152, // JS 1.5 set pseudo keyword - LET = 153, // JS 1.7 let pseudo keyword - CONST = 154, - SETCONST = 155, - SETCONSTVAR = 156, - ARRAYCOMP = 157, // array comprehension - LETEXPR = 158, - WITHEXPR = 159, - DEBUGGER = 160, - COMMENT = 161, - GENEXPR = 162, - METHOD = 163, // ES6 MethodDefinition - LAST_TOKEN = 164; + 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; /** * Returns a name for the token. If Rhino is compiled with certain @@ -314,6 +316,7 @@ 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"; @@ -415,6 +418,7 @@ case COMMENT: return "COMMENT"; case GENEXPR: return "GENEXPR"; case METHOD: return "METHOD"; + case ARROW: return "ARROW"; } // Token without name diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/TokenStream.java rhino-1.7.7.2/src/org/mozilla/javascript/TokenStream.java --- rhino-1.7.7.1/src/org/mozilla/javascript/TokenStream.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/TokenStream.java 2017-09-27 22:57:34.000000000 +0000 @@ -74,12 +74,24 @@ return ""; } - static boolean isKeyword(String s) + static boolean isKeyword(String s, int version, boolean isStrict) { - return Token.EOF != stringToKeyword(s); + return Token.EOF != stringToKeyword(s, version, isStrict); } - private static int stringToKeyword(String name) + private static int stringToKeyword(String name, int version, boolean isStrict) + { + if (version < Context.VERSION_ES6) { + return stringToKeywordForJS(name); + } else { + 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 @@ -255,6 +267,161 @@ return id & 0xff; } + /** + * ECMAScript 6. + */ + private static int stringToKeywordForES(String name, boolean isStrict) + { +// #string_id_map# +// 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; + + 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 '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; } + return id & 0xff; + } + final String getSourceString() { return sourceString; } final int getLineno() { return lineno; } @@ -266,6 +433,8 @@ } 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; } @@ -378,7 +547,7 @@ // check if it's a keyword. // Return the corresponding token if it's a keyword - int result = stringToKeyword(str); + int result = stringToKeyword(str, parser.compilerEnv.getLanguageVersion(), parser.inUseStrictDirective()); if (result != Token.EOF) { if ((result == Token.LET || result == Token.YIELD) && parser.compilerEnv.getLanguageVersion() @@ -393,13 +562,13 @@ this.string = (String)allStrings.intern(str); if (result != Token.RESERVED) { return result; - } else if (!parser.compilerEnv. - isReservedKeywordAsIdentifier()) - { + } else if (parser.compilerEnv.getLanguageVersion() >= Context.VERSION_ES6) { + return result; + } else if (!parser.compilerEnv.isReservedKeywordAsIdentifier()) { return result; } } - } else if (isKeyword(str)) { + } 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); @@ -410,10 +579,10 @@ // is it a number? if (isDigit(c) || (c == '.' && isDigit(peekChar()))) { - isOctal = false; stringBufferTop = 0; int base = 10; - isHex = isOctal = false; + isHex = isOldOctal = isOctal = isBinary = false; + boolean es6 = parser.compilerEnv.getLanguageVersion() >= Context.VERSION_ES6; if (c == '0') { c = getChar(); @@ -421,36 +590,59 @@ base = 16; isHex = true; c = getChar(); - } else if (isDigit(c)) { + } else if (es6 && (c == 'o' || c == 'O')) { base = 8; isOctal = true; + c = getChar(); + } else if (es6 && (c == 'b' || c == 'B')) { + base = 2; + isBinary = true; + c = getChar(); + } else if (isDigit(c)) { + base = 8; + isOldOctal = true; } else { addToString('0'); } } + boolean isEmpty = true; if (base == 16) { while (0 <= Kit.xDigitToInt(c, 0)) { addToString(c); c = getChar(); + isEmpty = false; } } else { while ('0' <= c && c <= '9') { - /* - * 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. - */ if (base == 8 && c >= '8') { - parser.addWarning("msg.bad.octal.literal", - c == '8' ? "8" : "9"); - base = 10; + 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 { + parser.addError("msg.caught.nfe"); + return Token.ERROR; + } + } else if (base == 2 && c >= '2') { + parser.addError("msg.caught.nfe"); + return Token.ERROR; } addToString(c); c = getChar(); + isEmpty = false; } } + if (isEmpty && (isBinary || isOctal || isHex)) { + parser.addError("msg.caught.nfe"); + return Token.ERROR; + } boolean isInteger = true; @@ -670,6 +862,8 @@ } else { return Token.EQ; } + } else if (matchChar('>')) { + return Token.ARROW; } else { return Token.ASSIGN; } @@ -1613,6 +1807,8 @@ // code. private String string = ""; private double number; + private boolean isBinary; + private boolean isOldOctal; private boolean isOctal; private boolean isHex; diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/TopLevel.java rhino-1.7.7.2/src/org/mozilla/javascript/TopLevel.java --- rhino-1.7.7.1/src/org/mozilla/javascript/TopLevel.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/TopLevel.java 2017-09-27 22:57:34.000000000 +0000 @@ -58,7 +58,9 @@ /** The built-in RegExp type. */ RegExp, /** The built-in Error type. */ - Error + Error, + /** The built-in Symbol type. */ + Symbol } /** diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/typedarrays/NativeDataView.java rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeDataView.java --- rhino-1.7.7.1/src/org/mozilla/javascript/typedarrays/NativeDataView.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeDataView.java 2017-09-27 22:57:34.000000000 +0000 @@ -96,22 +96,22 @@ { checkOffset(args, 0); - int offset = ScriptRuntime.toInt32(args[0]); - rangeCheck(offset, bytes); + int pos = ScriptRuntime.toInt32(args[0]); + rangeCheck(pos, bytes); boolean littleEndian = (isArg(args, 1) && (bytes > 1) && ScriptRuntime.toBoolean(args[1])); switch (bytes) { case 1: - return (signed ? ByteIo.readInt8(arrayBuffer.buffer, offset) : - ByteIo.readUint8(arrayBuffer.buffer, offset)); + return (signed ? ByteIo.readInt8(arrayBuffer.buffer, offset + pos) : + ByteIo.readUint8(arrayBuffer.buffer, offset + pos)); case 2: - return (signed ? ByteIo.readInt16(arrayBuffer.buffer, offset, littleEndian) : - ByteIo.readUint16(arrayBuffer.buffer, offset, littleEndian)); + 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, littleEndian) : - ByteIo.readUint32(arrayBuffer.buffer, offset, littleEndian)); + return (signed ? ByteIo.readInt32(arrayBuffer.buffer, offset + pos, littleEndian) : + ByteIo.readUint32(arrayBuffer.buffer, offset + pos, littleEndian)); default: throw new AssertionError(); } @@ -121,17 +121,17 @@ { checkOffset(args, 0); - int offset = ScriptRuntime.toInt32(args[0]); - rangeCheck(offset, bytes); + int pos = ScriptRuntime.toInt32(args[0]); + rangeCheck(pos, bytes); boolean littleEndian = (isArg(args, 1) && (bytes > 1) && ScriptRuntime.toBoolean(args[1])); switch (bytes) { case 4: - return ByteIo.readFloat32(arrayBuffer.buffer, offset, littleEndian); + return ByteIo.readFloat32(arrayBuffer.buffer, offset + pos, littleEndian); case 8: - return ByteIo.readFloat64(arrayBuffer.buffer, offset, littleEndian); + return ByteIo.readFloat64(arrayBuffer.buffer, offset + pos, littleEndian); default: throw new AssertionError(); } @@ -142,8 +142,8 @@ checkOffset(args, 0); checkValue(args, 1); - int offset = ScriptRuntime.toInt32(args[0]); - rangeCheck(offset, bytes); + int pos = ScriptRuntime.toInt32(args[0]); + rangeCheck(pos, bytes); boolean littleEndian = (isArg(args, 2) && (bytes > 1) && ScriptRuntime.toBoolean(args[2])); @@ -151,23 +151,23 @@ switch (bytes) { case 1: if (signed) { - ByteIo.writeInt8(arrayBuffer.buffer, offset, Conversions.toInt8(args[1])); + ByteIo.writeInt8(arrayBuffer.buffer, offset + pos, Conversions.toInt8(args[1])); } else { - ByteIo.writeUint8(arrayBuffer.buffer, offset, Conversions.toUint8(args[1])); + ByteIo.writeUint8(arrayBuffer.buffer, offset + pos, Conversions.toUint8(args[1])); } break; case 2: if (signed) { - ByteIo.writeInt16(arrayBuffer.buffer, offset, Conversions.toInt16(args[1]), littleEndian); + ByteIo.writeInt16(arrayBuffer.buffer, offset + pos, Conversions.toInt16(args[1]), littleEndian); } else { - ByteIo.writeUint16(arrayBuffer.buffer, offset, Conversions.toUint16(args[1]), littleEndian); + ByteIo.writeUint16(arrayBuffer.buffer, offset + pos, Conversions.toUint16(args[1]), littleEndian); } break; case 4: if (signed) { - ByteIo.writeInt32(arrayBuffer.buffer, offset, Conversions.toInt32(args[1]), littleEndian); + ByteIo.writeInt32(arrayBuffer.buffer, offset + pos, Conversions.toInt32(args[1]), littleEndian); } else { - ByteIo.writeUint32(arrayBuffer.buffer, offset, Conversions.toUint32(args[1]), littleEndian); + ByteIo.writeUint32(arrayBuffer.buffer, offset + pos, Conversions.toUint32(args[1]), littleEndian); } break; default: @@ -180,8 +180,8 @@ checkOffset(args, 0); checkValue(args, 1); - int offset = ScriptRuntime.toInt32(args[0]); - rangeCheck(offset, bytes); + int pos = ScriptRuntime.toInt32(args[0]); + rangeCheck(pos, bytes); boolean littleEndian = (isArg(args, 2) && (bytes > 1) && ScriptRuntime.toBoolean(args[2])); @@ -189,10 +189,10 @@ switch (bytes) { case 4: - ByteIo.writeFloat32(arrayBuffer.buffer, offset, val, littleEndian); + ByteIo.writeFloat32(arrayBuffer.buffer, offset + pos, val, littleEndian); break; case 8: - ByteIo.writeFloat64(arrayBuffer.buffer, offset, val, littleEndian); + ByteIo.writeFloat64(arrayBuffer.buffer, offset + pos, val, littleEndian); break; default: throw new AssertionError(); diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/typedarrays/NativeTypedArrayView.java rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeTypedArrayView.java --- rhino-1.7.7.1/src/org/mozilla/javascript/typedarrays/NativeTypedArrayView.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/typedarrays/NativeTypedArrayView.java 2017-09-27 22:57:34.000000000 +0000 @@ -10,8 +10,11 @@ import org.mozilla.javascript.ExternalArrayData; import org.mozilla.javascript.IdFunctionObject; import org.mozilla.javascript.NativeArray; +import org.mozilla.javascript.NativeArrayIterator; 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; @@ -281,6 +284,9 @@ } else { throw ScriptRuntime.constructError("Error", "invalid arguments"); } + + case SymbolId_iterator: + return new NativeArrayIterator(scope, thisObj); } throw new IllegalArgumentException(String.valueOf(id)); } @@ -288,7 +294,12 @@ @Override protected void initPrototypeId(int id) { - String s; + if (id == SymbolId_iterator) { + initPrototypeMethod(getClassName(), id, SymbolKey.ITERATOR, "[Symbol.iterator]", 0); + return; + } + + String s, fnName = null; int arity; switch (id) { case Id_constructor: arity = 1; s = "constructor"; break; @@ -297,7 +308,16 @@ case Id_subarray: arity = 2; s = "subarray"; break; default: throw new IllegalArgumentException(String.valueOf(id)); } - initPrototypeMethod(getClassName(), id, s, arity); + initPrototypeMethod(getClassName(), id, s, fnName, arity); + } + + @Override + protected int findPrototypeId(Symbol k) + { + if (SymbolKey.ITERATOR.equals(k)) { + return SymbolId_iterator; + } + return 0; } // #string_id_map# @@ -306,7 +326,7 @@ protected int findPrototypeId(String s) { int id; -// #generated# Last update: 2014-12-04 18:21:01 PST +// #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) { @@ -328,10 +348,11 @@ Id_constructor = 1, Id_get = 2, Id_set = 3, - Id_subarray = 4; + Id_subarray = 4, + SymbolId_iterator = 5; protected static final int - MAX_PROTOTYPE_ID = Id_subarray; + MAX_PROTOTYPE_ID = SymbolId_iterator; // #/string_id_map# diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/Undefined.java rhino-1.7.7.2/src/org/mozilla/javascript/Undefined.java --- rhino-1.7.7.1/src/org/mozilla/javascript/Undefined.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/Undefined.java 2017-09-27 22:57:34.000000000 +0000 @@ -7,6 +7,9 @@ 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. @@ -25,4 +28,35 @@ { return instance; } + + @Override + public boolean equals(Object obj) { + return isUndefined(obj) || super.equals(obj); + } + + @Override + public int hashCode() { + // All instances of Undefined are equivalent! + return 0; + } + + public static final Scriptable SCRIPTABLE_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()); + } + }); + } + + public static boolean isUndefined(Object obj) + { + return Undefined.instance == obj || Undefined.SCRIPTABLE_UNDEFINED == obj; + } } diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/v8dtoa/FastDtoa.java rhino-1.7.7.2/src/org/mozilla/javascript/v8dtoa/FastDtoa.java --- rhino-1.7.7.1/src/org/mozilla/javascript/v8dtoa/FastDtoa.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/v8dtoa/FastDtoa.java 2017-09-27 22:57:34.000000000 +0000 @@ -355,7 +355,7 @@ // such that: too_low < buffer * 10^kappa < too_high // We use too_high for the digit_generation and stop as soon as possible. // If we stop early we effectively round down. - DiyFp one = new DiyFp(1l << -w.e(), w.e()); + DiyFp one = new DiyFp(1L << -w.e(), w.e()); // Division by one is a shift. int integrals = (int)((too_high.f() >>> -one.e()) & 0xffffffffL); // Modulo by one is an and. diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/VMBridge.java rhino-1.7.7.2/src/org/mozilla/javascript/VMBridge.java --- rhino-1.7.7.1/src/org/mozilla/javascript/VMBridge.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/VMBridge.java 2017-09-27 22:57:34.000000000 +0000 @@ -108,7 +108,7 @@ * {@link InterfaceAdapter#invoke(ContextFactory, Object, Scriptable, * Method, Object[])} * as implementation of interface methods associated with - * proxyHelper. + * proxyHelper. {@link Method} * * @param proxyHelper The result of the previous call to * {@link #getInterfaceProxyHelper(ContextFactory, Class[])}. diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/xml/XMLLib.java rhino-1.7.7.2/src/org/mozilla/javascript/xml/XMLLib.java --- rhino-1.7.7.1/src/org/mozilla/javascript/xml/XMLLib.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/xml/XMLLib.java 2017-09-27 22:57:34.000000000 +0000 @@ -12,27 +12,28 @@ { private static final Object XML_LIB_KEY = new Object(); - /** - An object which specifies an XMLLib implementation to be used at runtime. - - This interface should be considered experimental. It may be better - (and certainly more flexible) to write an interface that returns an - XMLLib object rather than a class name, for example. But that would - cause many more ripple effects in the code, all the way back to - {@link ScriptRuntime}. - */ - public static abstract class Factory { - public static Factory create(final String className) { - return new Factory() { - @Override - public String getImplementationClassName() { - return className; - } - }; - } + /** + * An object which specifies an XMLLib implementation to be used at runtime. + * + * This interface should be considered experimental. It may be better + * (and certainly more flexible) to write an interface that returns an + * XMLLib object rather than a class name, for example. But that would + * cause many more ripple effects in the code, all the way back to + * {@link ScriptRuntime}. + */ + public static abstract class Factory { + + public static Factory create(final String className) { + return new Factory() { + @Override + public String getImplementationClassName() { + return className; + } + }; + } - public abstract String getImplementationClassName(); - } + public abstract String getImplementationClassName(); + } public static XMLLib extractFromScopeOrNull(Scriptable scope) { @@ -104,7 +105,7 @@ } public void setIgnoreWhitespace(boolean b) { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException(); } public void setIgnoreProcessingInstructions(boolean b) { diff -Nru rhino-1.7.7.1/src/org/mozilla/javascript/xml/XMLObject.java rhino-1.7.7.2/src/org/mozilla/javascript/xml/XMLObject.java --- rhino-1.7.7.1/src/org/mozilla/javascript/xml/XMLObject.java 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/src/org/mozilla/javascript/xml/XMLObject.java 2017-09-27 22:57:34.000000000 +0000 @@ -106,6 +106,6 @@ @Override public String getTypeOf() { - return avoidObjectDetection() ? "undefined" : "xml"; + return avoidObjectDetection() ? "undefined" : "xml"; } } diff -Nru rhino-1.7.7.1/testsrc/benchmarks/caliper/fieldTests.js rhino-1.7.7.2/testsrc/benchmarks/caliper/fieldTests.js --- rhino-1.7.7.1/testsrc/benchmarks/caliper/fieldTests.js 1970-01-01 00:00:00.000000000 +0000 +++ rhino-1.7.7.2/testsrc/benchmarks/caliper/fieldTests.js 2017-09-27 22:57:34.000000000 +0000 @@ -0,0 +1,75 @@ +function createObject(iterations, strings, ints) { + var o; + for (var ct = 0; ct < iterations; ct++) { + o = {}; + var s = 0; + var i = 0; + + while ((s < strings.length) && (i < ints.length)) { + if (s < strings.length) { + o[strings[s]] = strings[s]; + s++; + } + if (i < ints.length) { + o[ints[i]] = ints[i]; + i++; + } + } + } + return o; +} + +function iterateObject(iterations, o) { + var x; + for (var ct = 0; ct < iterations; ct++) { + for (var k in o) { + x = o[k]; + } + } + return x; +} + +function iterateOwnKeysObject(iterations, o) { + var pn; + for (var ct = 0; ct < iterations; ct++) { + pn = Object.getOwnPropertyNames(o); + } + return pn; +} + +function accessObject(iterations, o, strings, ints) { + var s = 0; + var i = 0; + for (var ct = 0; ct < iterations; ct++) { + if (strings.length > 0) { + var x = o[strings[s]]; + s++; + if (s === strings.length) { + s = 0; + } + } + if (ints.length > 0) { + var x = o[ints[i]]; + i++; + if (i === ints.length) { + i = 0; + } + } + } +} + +function deleteObject(iterations, o, strings, ints) { + var s = 0; + var i = 0; + + for (var ct = 0; ct < iterations; ct++) { + if (s < strings.length) { + delete o[strings[s]]; + s++; + } + if (i < ints.length) { + delete o[ints[i]]; + i++; + } + } +} \ No newline at end of file diff -Nru rhino-1.7.7.1/testsrc/build.xml rhino-1.7.7.2/testsrc/build.xml --- rhino-1.7.7.1/testsrc/build.xml 2016-02-01 18:15:28.000000000 +0000 +++ rhino-1.7.7.2/testsrc/build.xml 2017-09-27 22:57:34.000000000 +0000 @@ -15,6 +15,7 @@ --> +