I am currently investigating following issue in Nashorn js scripting engine and I'd appreciate your input. Following code snippet fails execution (code in Kotlin):
fun main() {
val brokenScript = """
function fn() {
const object1 = { "name": "Pepa" };
print(object1.name);
const descriptor1 = Object.getOwnPropertyDescriptor(object1, 'name');
print(descriptor1.configurable);
print(eval("3+1"));
}
fn();
""".trimIndent()
NashornScriptEngineFactory()
.getScriptEngine("--no-deprecation-warning", "--language=es6", "--log=compiler:finest,fields,recompile:fine")
.let { (it as Compilable).compile(brokenScript) }
.also { script -> script.eval() }
}
Error:
Exception in thread "main" javax.script.ScriptException: ReferenceError: "descriptor1" is not defined in <eval> at line number 8
at jdk.scripting.nashorn/jdk.nashorn.api.scripting.NashornScriptEngine.throwAsScriptException(NashornScriptEngine.java:477)
at jdk.scripting.nashorn/jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:461)
at jdk.scripting.nashorn/jdk.nashorn.api.scripting.NashornScriptEngine$3.eval(NashornScriptEngine.java:517)
at java.scripting/javax.script.CompiledScript.eval(CompiledScript.java:103)
at MainKt.main(Main.kt:19)
at MainKt.main(Main.kt)
Caused by: <eval>:8 ReferenceError: "descriptor1" is not defined
at jdk.scripting.nashorn/jdk.nashorn.internal.runtime.ECMAErrors.error(ECMAErrors.java:57)
at jdk.scripting.nashorn/jdk.nashorn.internal.runtime.ECMAErrors.referenceError(ECMAErrors.java:319)
at jdk.scripting.nashorn/jdk.nashorn.internal.runtime.ECMAErrors.referenceError(ECMAErrors.java:291)
at jdk.scripting.nashorn/jdk.nashorn.internal.runtime.ECMAErrors.referenceError(ECMAErrors.java:278)
at jdk.scripting.nashorn/jdk.nashorn.internal.runtime.AccessorProperty.checkUndeclared(AccessorProperty.java:571)
...
Observations:
- issue is influenced by eval function - ommiting it from script helps - also need to be nested in function - works fine at top level
- issue is influenced by Object.getOwnPropertyDescriptor function - ommiting it from script helps
- issue is influenced by scoping - changing variable declaration from scoped (const/let) to unscoped var helps
- output log contains
caught RewriteException [pp=3, type=object, value='Pepa')]
Current hypothesis:
Object.getOwnPropertyDescriptor function raises UnwarrantedOptimismException that forces the function to recompile. Scope of recompiled script is corrupted. Usually the scope is manipulated by eval function usage so it can declare variables - maybe this is resolved incorrectly during the runtime recompilation. Accessing scoped variables fails.
What outcome I hope for?(in order of preference)
- it works just as when executed in node - obviously
- issue is identified at compile time without turning off es6 features
- disabling eval function prevents the issue - I tried this by removing eval from Bindings object but script is corrupted at compilation phase and modified Bindings are passed later to
script.eval(bindings)- so the script still fails before reaching eval line - (this is actually what I try to avoid) bypassing issue by turning off es6 features (removing --language=es6 - this forces the script to fail at compile time due to usage of scoped variables (const/let)
(I am aware that nashorn is deprecated and will be removed.)