From 05ab4ffdf3eb1e8e603856a0a44577715dcb2cc3 Mon Sep 17 00:00:00 2001 From: Andrea Bergia Date: Tue, 31 Oct 2023 02:15:32 +0100 Subject: [PATCH] Allow updating of `name` of a function, as required by the standard (#1398) Make the attributes of the "name" properties of functions match current ECMAScript specs. See https://github.com/mozilla/rhino/issues/1297 for more details --- src/org/mozilla/javascript/Arguments.java | 1 + src/org/mozilla/javascript/BaseFunction.java | 14 +++++++-- .../javascript/IdScriptableObject.java | 5 +-- .../tests/es6/Issue1297FunctionNameTest.java | 31 +++++++++++++++++++ testsrc/test262.properties | 21 +++++-------- 5 files changed, 53 insertions(+), 19 deletions(-) create mode 100644 testsrc/org/mozilla/javascript/tests/es6/Issue1297FunctionNameTest.java diff --git a/src/org/mozilla/javascript/Arguments.java b/src/org/mozilla/javascript/Arguments.java index a8bc9250f6..b0300b22e6 100644 --- a/src/org/mozilla/javascript/Arguments.java +++ b/src/org/mozilla/javascript/Arguments.java @@ -424,6 +424,7 @@ private static class ThrowTypeError extends BaseFunction { ThrowTypeError(String propertyName) { this.propertyName = propertyName; + super.setInstanceIdAttributes(BaseFunction.Id_name, PERMANENT | READONLY | DONTENUM); } @Override diff --git a/src/org/mozilla/javascript/BaseFunction.java b/src/org/mozilla/javascript/BaseFunction.java index 0acfa10ff4..1fc6abaae8 100644 --- a/src/org/mozilla/javascript/BaseFunction.java +++ b/src/org/mozilla/javascript/BaseFunction.java @@ -100,7 +100,7 @@ public boolean hasInstance(Scriptable instance) { throw ScriptRuntime.typeErrorById("msg.instanceof.bad.prototype", getFunctionName()); } - private static final int Id_length = 1, + protected static final int Id_length = 1, Id_arity = 2, Id_name = 3, Id_prototype = 4, @@ -169,7 +169,9 @@ protected Object getInstanceIdValue(int id) { case Id_arity: return arityPropertyAttributes >= 0 ? getArity() : NOT_FOUND; case Id_name: - return namePropertyAttributes >= 0 ? getFunctionName() : NOT_FOUND; + return namePropertyAttributes >= 0 + ? (nameValue != null ? nameValue : getFunctionName()) + : NOT_FOUND; case Id_prototype: return getPrototypeProperty(); case Id_arguments: @@ -200,6 +202,11 @@ protected void setInstanceIdValue(int id, Object value) { case Id_name: if (value == NOT_FOUND) { namePropertyAttributes = -1; + nameValue = null; + } else if (value instanceof CharSequence) { + nameValue = ScriptRuntime.toString(value); + } else { + nameValue = ""; } return; case Id_arity: @@ -650,6 +657,7 @@ protected int findPrototypeId(String s) { private Object prototypeProperty; private Object argumentsObj = NOT_FOUND; + private String nameValue = null; private boolean isGeneratorFunction = false; // For function object instances, attributes are @@ -658,6 +666,6 @@ protected int findPrototypeId(String s) { private int prototypePropertyAttributes = PERMANENT | DONTENUM; private int argumentsAttributes = PERMANENT | DONTENUM; private int arityPropertyAttributes = PERMANENT | READONLY | DONTENUM; - private int namePropertyAttributes = PERMANENT | READONLY | DONTENUM; + private int namePropertyAttributes = READONLY | DONTENUM; private int lengthPropertyAttributes = PERMANENT | READONLY | DONTENUM; } diff --git a/src/org/mozilla/javascript/IdScriptableObject.java b/src/org/mozilla/javascript/IdScriptableObject.java index ba46b01940..650dd7f6ba 100644 --- a/src/org/mozilla/javascript/IdScriptableObject.java +++ b/src/org/mozilla/javascript/IdScriptableObject.java @@ -867,13 +867,14 @@ protected void defineOwnProperty( checkPropertyChange(name, current, desc); int attr = (info >>> 16); Object value = getProperty(desc, "value"); - if (value != NOT_FOUND && (attr & READONLY) == 0) { + if (value != NOT_FOUND && ((attr & READONLY) == 0 || (attr & PERMANENT) == 0)) { Object currentValue = getInstanceIdValue(id); if (!sameValue(value, currentValue)) { setInstanceIdValue(id, value); } } - setAttributes(name, applyDescriptorToAttributeBitset(attr, desc)); + attr = applyDescriptorToAttributeBitset(attr, desc); + setAttributes(name, attr); return; } } diff --git a/testsrc/org/mozilla/javascript/tests/es6/Issue1297FunctionNameTest.java b/testsrc/org/mozilla/javascript/tests/es6/Issue1297FunctionNameTest.java new file mode 100644 index 0000000000..7ea898064d --- /dev/null +++ b/testsrc/org/mozilla/javascript/tests/es6/Issue1297FunctionNameTest.java @@ -0,0 +1,31 @@ +/* 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.tests.es6; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.tests.Utils; + +/** Test that we can redefine a function's name. */ +public class Issue1297FunctionNameTest { + private static final String source = + "'use strict';" + + "function X() {};\n" + + "Object.defineProperty(X, 'name', {value: 'y', configurable: true, writable: true});" + + "X.name"; + + @Test + public void canSetFunctionName() { + Utils.runWithAllOptimizationLevels( + cx -> { + Scriptable scope = cx.initStandardObjects(null); + Object result = cx.evaluateString(scope, source, "test", 1, null); + assertEquals("y", result); + return null; + }); + } +} diff --git a/testsrc/test262.properties b/testsrc/test262.properties index 57d2fba45b..83fde87afb 100644 --- a/testsrc/test262.properties +++ b/testsrc/test262.properties @@ -449,12 +449,10 @@ built-ins/Date 39/707 (5.52%) S15.1.3.2_A2.4_T1.js S15.1.3.2_A5.2.js -built-ins/encodeURI 2/30 (6.67%) - name.js +built-ins/encodeURI 1/30 (3.33%) S15.1.3.3_A5.2.js -built-ins/encodeURIComponent 2/30 (6.67%) - name.js +built-ins/encodeURIComponent 1/30 (3.33%) S15.1.3.4_A5.2.js built-ins/Error 5/42 (11.9%) @@ -464,9 +462,8 @@ built-ins/Error 5/42 (11.9%) prototype/S15.11.4_A2.js proto-from-ctor-realm.js {unsupported: [Reflect]} -built-ins/eval 3/9 (33.33%) +built-ins/eval 2/9 (22.22%) length-non-configurable.js - name.js private-identifiers-not-empty.js {unsupported: [class-fields-private]} built-ins/Function 186/505 (36.83%) @@ -674,9 +671,8 @@ built-ins/global 0/29 (0.0%) built-ins/Infinity 0/6 (0.0%) -built-ins/isFinite 8/16 (50.0%) +built-ins/isFinite 7/16 (43.75%) length.js - name.js toprimitive-call-abrupt.js toprimitive-get-abrupt.js toprimitive-not-callable-throws.js @@ -684,9 +680,8 @@ built-ins/isFinite 8/16 (50.0%) toprimitive-result-is-symbol-throws.js toprimitive-valid-result.js -built-ins/isNaN 8/16 (50.0%) +built-ins/isNaN 7/16 (43.75%) length.js - name.js toprimitive-call-abrupt.js toprimitive-get-abrupt.js toprimitive-not-callable-throws.js @@ -901,13 +896,11 @@ built-ins/Object 127/3150 (4.03%) proto-from-ctor-realm.js {unsupported: [Reflect]} subclass-object-arg.js {unsupported: [Reflect.construct, Reflect, class]} -built-ins/parseFloat 3/58 (5.17%) - name.js +built-ins/parseFloat 2/58 (3.45%) S15.1.2.3_A2_T10_U180E.js {unsupported: [u180e]} S15.1.2.3_A7.2.js -built-ins/parseInt 3/60 (5.0%) - name.js +built-ins/parseInt 2/60 (3.33%) S15.1.2.2_A2_T10_U180E.js {unsupported: [u180e]} S15.1.2.2_A9.2.js