From a8478b28bb3c1dddd4ba3696a637f48471221a0a Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Fri, 20 May 2022 18:06:52 +0200 Subject: [PATCH] fix scope for defineOwnProperties --- .../AbstractEcmaObjectOperations.java | 16 ++++--- src/org/mozilla/javascript/Arguments.java | 16 +++---- src/org/mozilla/javascript/ArrowFunction.java | 4 +- src/org/mozilla/javascript/BoundFunction.java | 4 +- .../javascript/IdScriptableObject.java | 12 ++--- src/org/mozilla/javascript/NativeArray.java | 8 ++-- src/org/mozilla/javascript/NativeMap.java | 2 +- src/org/mozilla/javascript/NativeObject.java | 18 +++---- src/org/mozilla/javascript/NativePromise.java | 2 +- src/org/mozilla/javascript/NativeSet.java | 2 +- src/org/mozilla/javascript/NativeString.java | 4 +- src/org/mozilla/javascript/ScriptRuntime.java | 4 +- .../mozilla/javascript/ScriptableObject.java | 48 +++++++++++++++---- .../tests/es6/NativeObjectTest.java | 16 +++++++ 14 files changed, 103 insertions(+), 53 deletions(-) diff --git a/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java b/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java index 717d81fa00..b0119c62d0 100644 --- a/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java +++ b/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java @@ -54,19 +54,21 @@ static boolean hasOwnProperty(Context cx, Object o, Object property) { * Implementation of Abstract Object operation testIntegrityLevel as defined by EcmaScript * * @param cx + * @param scope the current scope. * @param o * @param level * @return boolean * @see TestIntegrityLevel */ - static boolean testIntegrityLevel(Context cx, Object o, INTEGRITY_LEVEL level) { + static boolean testIntegrityLevel( + Context cx, Scriptable scope, Object o, INTEGRITY_LEVEL level) { ScriptableObject obj = ScriptableObject.ensureScriptableObject(o); if (obj.isExtensible()) return false; for (Object name : obj.getIds(true, true)) { - ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, name); + ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, scope, name); if (Boolean.TRUE.equals(desc.get("configurable"))) return false; if (level == INTEGRITY_LEVEL.FROZEN @@ -81,13 +83,15 @@ static boolean testIntegrityLevel(Context cx, Object o, INTEGRITY_LEVEL level) { * Implementation of Abstract Object operation setIntegrityLevel as defined by EcmaScript * * @param cx + * @param scope the current scope. * @param o * @param level * @return boolean * @see SetIntegrityLevel */ - static boolean setIntegrityLevel(Context cx, Object o, INTEGRITY_LEVEL level) { + static boolean setIntegrityLevel( + Context cx, Scriptable scope, Object o, INTEGRITY_LEVEL level) { /* 1. Assert: Type(O) is Object. 2. Assert: level is either sealed or frozen. @@ -128,13 +132,13 @@ static boolean setIntegrityLevel(Context cx, Object o, INTEGRITY_LEVEL level) { obj.preventExtensions(); for (Object key : obj.getIds(true, true)) { - ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, key); + ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, scope, key); if (level == INTEGRITY_LEVEL.SEALED) { if (Boolean.TRUE.equals(desc.get("configurable"))) { desc.put("configurable", desc, Boolean.FALSE); - obj.defineOwnProperty(cx, key, desc, false); + obj.defineOwnProperty(cx, scope, key, desc, false); } } else { if (ScriptableObject.isDataDescriptor(desc) @@ -144,7 +148,7 @@ static boolean setIntegrityLevel(Context cx, Object o, INTEGRITY_LEVEL level) { if (Boolean.TRUE.equals(desc.get("configurable"))) { desc.put("configurable", desc, Boolean.FALSE); } - obj.defineOwnProperty(cx, key, desc, false); + obj.defineOwnProperty(cx, scope, key, desc, false); } } diff --git a/src/org/mozilla/javascript/Arguments.java b/src/org/mozilla/javascript/Arguments.java index a8bc9250f6..06ea55500c 100644 --- a/src/org/mozilla/javascript/Arguments.java +++ b/src/org/mozilla/javascript/Arguments.java @@ -322,37 +322,35 @@ Object[] getIds(boolean getNonEnumerable, boolean getSymbols) { } @Override - protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { + protected ScriptableObject getOwnPropertyDescriptor(Context cx, Scriptable scope, Object id) { if (ScriptRuntime.isSymbol(id) || id instanceof Scriptable) { - return super.getOwnPropertyDescriptor(cx, id); + return super.getOwnPropertyDescriptor(cx, scope, id); } double d = ScriptRuntime.toNumber(id); int index = (int) d; if (d != index) { - return super.getOwnPropertyDescriptor(cx, id); + return super.getOwnPropertyDescriptor(cx, scope, id); } Object value = arg(index); if (value == NOT_FOUND) { - return super.getOwnPropertyDescriptor(cx, id); + return super.getOwnPropertyDescriptor(cx, scope, id); } if (sharedWithActivation(index)) { value = getFromActivation(index); } if (super.has(index, this)) { // the descriptor has been redefined - ScriptableObject desc = super.getOwnPropertyDescriptor(cx, id); + ScriptableObject desc = super.getOwnPropertyDescriptor(cx, scope, id); desc.put("value", desc, value); return desc; } - Scriptable scope = getParentScope(); - if (scope == null) scope = this; return buildDataDescriptor(scope, value, EMPTY); } @Override protected void defineOwnProperty( - Context cx, Object id, ScriptableObject desc, boolean checkValid) { - super.defineOwnProperty(cx, id, desc, checkValid); + Context cx, Scriptable scope, Object id, ScriptableObject desc, boolean checkValid) { + super.defineOwnProperty(cx, scope, id, desc, checkValid); if (ScriptRuntime.isSymbol(id)) { return; } diff --git a/src/org/mozilla/javascript/ArrowFunction.java b/src/org/mozilla/javascript/ArrowFunction.java index fef2797947..e2986d1387 100644 --- a/src/org/mozilla/javascript/ArrowFunction.java +++ b/src/org/mozilla/javascript/ArrowFunction.java @@ -29,8 +29,8 @@ public ArrowFunction( throwing.put("configurable", throwing, Boolean.FALSE); throwing.preventExtensions(); - this.defineOwnProperty(cx, "caller", throwing, false); - this.defineOwnProperty(cx, "arguments", throwing, false); + this.defineOwnProperty(cx, scope, "caller", throwing, false); + this.defineOwnProperty(cx, scope, "arguments", throwing, false); } @Override diff --git a/src/org/mozilla/javascript/BoundFunction.java b/src/org/mozilla/javascript/BoundFunction.java index d12d989acb..6e60d89869 100644 --- a/src/org/mozilla/javascript/BoundFunction.java +++ b/src/org/mozilla/javascript/BoundFunction.java @@ -45,8 +45,8 @@ public BoundFunction( throwing.put("configurable", throwing, Boolean.FALSE); throwing.preventExtensions(); - this.defineOwnProperty(cx, "caller", throwing, false); - this.defineOwnProperty(cx, "arguments", throwing, false); + this.defineOwnProperty(cx, scope, "caller", throwing, false); + this.defineOwnProperty(cx, scope, "arguments", throwing, false); } @Override diff --git a/src/org/mozilla/javascript/IdScriptableObject.java b/src/org/mozilla/javascript/IdScriptableObject.java index bc7f963780..4ff6af1371 100644 --- a/src/org/mozilla/javascript/IdScriptableObject.java +++ b/src/org/mozilla/javascript/IdScriptableObject.java @@ -853,7 +853,7 @@ private IdFunctionObject newIdFunction( @Override protected void defineOwnProperty( - Context cx, Object key, ScriptableObject desc, boolean checkValid) { + Context cx, Scriptable scope, Object key, ScriptableObject desc, boolean checkValid) { if (key instanceof String) { String name = (String) key; int info = findInstanceIdInfo(name); @@ -863,7 +863,7 @@ protected void defineOwnProperty( delete(id); // it will be replaced with a slot } else { checkPropertyDefinition(desc); - ScriptableObject current = getOwnPropertyDescriptor(cx, key); + ScriptableObject current = getOwnPropertyDescriptor(cx, scope, key); checkPropertyChange(name, current, desc); int attr = (info >>> 16); Object value = getProperty(desc, "value"); @@ -884,7 +884,7 @@ protected void defineOwnProperty( prototypeValues.delete(id); // it will be replaced with a slot } else { checkPropertyDefinition(desc); - ScriptableObject current = getOwnPropertyDescriptor(cx, key); + ScriptableObject current = getOwnPropertyDescriptor(cx, scope, key); checkPropertyChange(name, current, desc); int attr = prototypeValues.getAttributes(id); Object value = getProperty(desc, "value"); @@ -909,12 +909,12 @@ protected void defineOwnProperty( } } } - super.defineOwnProperty(cx, key, desc, checkValid); + super.defineOwnProperty(cx, scope, key, desc, checkValid); } @Override - protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { - ScriptableObject desc = super.getOwnPropertyDescriptor(cx, id); + protected ScriptableObject getOwnPropertyDescriptor(Context cx, Scriptable scope, Object id) { + ScriptableObject desc = super.getOwnPropertyDescriptor(cx, scope, id); if (desc == null) { if (id instanceof String) { desc = getBuiltInDescriptor((String) id); diff --git a/src/org/mozilla/javascript/NativeArray.java b/src/org/mozilla/javascript/NativeArray.java index 28143dd02f..c33da07730 100644 --- a/src/org/mozilla/javascript/NativeArray.java +++ b/src/org/mozilla/javascript/NativeArray.java @@ -668,7 +668,7 @@ public int getAttributes(int index) { } @Override - protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { + protected ScriptableObject getOwnPropertyDescriptor(Context cx, Scriptable scope, Object id) { if (dense != null) { int index = toDenseIndex(id); if (0 <= index && index < dense.length && dense[index] != NOT_FOUND) { @@ -676,12 +676,12 @@ protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { return defaultIndexPropertyDescriptor(value); } } - return super.getOwnPropertyDescriptor(cx, id); + return super.getOwnPropertyDescriptor(cx, scope, id); } @Override protected void defineOwnProperty( - Context cx, Object id, ScriptableObject desc, boolean checkValid) { + Context cx, Scriptable scope, Object id, ScriptableObject desc, boolean checkValid) { long index = toArrayIndex(id); if (index >= length) { length = index + 1; @@ -705,7 +705,7 @@ protected void defineOwnProperty( } } - super.defineOwnProperty(cx, id, desc, checkValid); + super.defineOwnProperty(cx, scope, id, desc, checkValid); if (id instanceof String && ((String) id).equals("length")) { lengthAttr = diff --git a/src/org/mozilla/javascript/NativeMap.java b/src/org/mozilla/javascript/NativeMap.java index 0e254d7656..b16535f7f6 100644 --- a/src/org/mozilla/javascript/NativeMap.java +++ b/src/org/mozilla/javascript/NativeMap.java @@ -25,7 +25,7 @@ static void init(Context cx, Scriptable scope, boolean sealed) { desc.put("enumerable", desc, Boolean.FALSE); desc.put("configurable", desc, Boolean.TRUE); desc.put("get", desc, obj.get(NativeSet.GETSIZE, obj)); - obj.defineOwnProperty(cx, "size", desc); + obj.defineOwnProperty(cx, scope, "size", desc); if (sealed) { obj.sealObject(); diff --git a/src/org/mozilla/javascript/NativeObject.java b/src/org/mozilla/javascript/NativeObject.java index ea713ab6f3..2b928f0aca 100644 --- a/src/org/mozilla/javascript/NativeObject.java +++ b/src/org/mozilla/javascript/NativeObject.java @@ -510,7 +510,7 @@ public Object execIdCall( Scriptable s = getCompatibleObject(cx, scope, arg); ScriptableObject obj = ensureScriptableObject(s); Object nameArg = args.length < 2 ? Undefined.instance : args[1]; - Scriptable desc = obj.getOwnPropertyDescriptor(cx, nameArg); + Scriptable desc = obj.getOwnPropertyDescriptor(cx, scope, nameArg); return desc == null ? Undefined.instance : desc; } case ConstructorId_getOwnPropertyDescriptors: @@ -521,7 +521,7 @@ public Object execIdCall( ScriptableObject descs = (ScriptableObject) cx.newObject(scope); for (Object key : obj.getIds(true, true)) { - Scriptable desc = obj.getOwnPropertyDescriptor(cx, key); + Scriptable desc = obj.getOwnPropertyDescriptor(cx, scope, key); if (desc == null) { continue; } else if (key instanceof Symbol) { @@ -541,7 +541,7 @@ public Object execIdCall( Object name = args.length < 2 ? Undefined.instance : args[1]; Object descArg = args.length < 3 ? Undefined.instance : args[2]; ScriptableObject desc = ensureScriptableObject(descArg); - obj.defineOwnProperty(cx, name, desc); + obj.defineOwnProperty(cx, scope, name, desc); return obj; } case ConstructorId_isExtensible: @@ -573,7 +573,7 @@ public Object execIdCall( ScriptableObject obj = ensureScriptableObject(arg); Object propsObj = args.length < 2 ? Undefined.instance : args[1]; Scriptable props = Context.toObject(propsObj, scope); - obj.defineOwnProperties(cx, ensureScriptableObject(props)); + obj.defineOwnProperties(cx, scope, ensureScriptableObject(props)); return obj; } case ConstructorId_create: @@ -587,7 +587,7 @@ public Object execIdCall( if (args.length > 1 && !Undefined.isUndefined(args[1])) { Scriptable props = Context.toObject(args[1], scope); - newObject.defineOwnProperties(cx, ensureScriptableObject(props)); + newObject.defineOwnProperties(cx, scope, ensureScriptableObject(props)); } return newObject; @@ -601,7 +601,7 @@ public Object execIdCall( } return AbstractEcmaObjectOperations.testIntegrityLevel( - cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.SEALED); + cx, scope, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.SEALED); } case ConstructorId_isFrozen: { @@ -612,7 +612,7 @@ public Object execIdCall( } return AbstractEcmaObjectOperations.testIntegrityLevel( - cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.FROZEN); + cx, scope, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.FROZEN); } case ConstructorId_seal: { @@ -623,7 +623,7 @@ public Object execIdCall( } AbstractEcmaObjectOperations.setIntegrityLevel( - cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.SEALED); + cx, scope, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.SEALED); return arg; } @@ -636,7 +636,7 @@ public Object execIdCall( } AbstractEcmaObjectOperations.setIntegrityLevel( - cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.FROZEN); + cx, scope, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.FROZEN); return arg; } diff --git a/src/org/mozilla/javascript/NativePromise.java b/src/org/mozilla/javascript/NativePromise.java index e92260c8d6..3725313264 100644 --- a/src/org/mozilla/javascript/NativePromise.java +++ b/src/org/mozilla/javascript/NativePromise.java @@ -61,7 +61,7 @@ public static void init(Context cx, Scriptable scope, boolean sealed) { 0, (Context lcx, Scriptable lscope, Scriptable thisObj, Object[] args) -> constructor)); - constructor.defineOwnProperty(cx, SymbolKey.SPECIES, speciesDescriptor, false); + constructor.defineOwnProperty(cx, scope, SymbolKey.SPECIES, speciesDescriptor, false); constructor.definePrototypeMethod( scope, diff --git a/src/org/mozilla/javascript/NativeSet.java b/src/org/mozilla/javascript/NativeSet.java index d462f656ca..6d97aa2fa4 100644 --- a/src/org/mozilla/javascript/NativeSet.java +++ b/src/org/mozilla/javascript/NativeSet.java @@ -27,7 +27,7 @@ static void init(Context cx, Scriptable scope, boolean sealed) { desc.put("enumerable", desc, Boolean.FALSE); desc.put("configurable", desc, Boolean.TRUE); desc.put("get", desc, obj.get(GETSIZE, obj)); - obj.defineOwnProperty(cx, "size", desc); + obj.defineOwnProperty(cx, scope, "size", desc); if (sealed) { obj.sealObject(); diff --git a/src/org/mozilla/javascript/NativeString.java b/src/org/mozilla/javascript/NativeString.java index 09206889cc..686d8f1faf 100644 --- a/src/org/mozilla/javascript/NativeString.java +++ b/src/org/mozilla/javascript/NativeString.java @@ -837,7 +837,7 @@ protected Object[] getIds(boolean nonEnumerable, boolean getSymbols) { } @Override - protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { + protected ScriptableObject getOwnPropertyDescriptor(Context cx, Scriptable scope, Object id) { if (!(id instanceof Symbol) && (cx != null) && (cx.getLanguageVersion() >= Context.VERSION_ES6)) { @@ -847,7 +847,7 @@ protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { return defaultIndexPropertyDescriptor(value); } } - return super.getOwnPropertyDescriptor(cx, id); + return super.getOwnPropertyDescriptor(cx, scope, id); } private ScriptableObject defaultIndexPropertyDescriptor(Object value) { diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 423b2a9c89..d583f3bb08 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -4739,9 +4739,9 @@ public static Scriptable getTemplateLiteralCallSite( } AbstractEcmaObjectOperations.setIntegrityLevel( - cx, rawObj, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.FROZEN); + cx, scope, rawObj, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.FROZEN); AbstractEcmaObjectOperations.setIntegrityLevel( - cx, siteObj, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.FROZEN); + cx, scope, siteObj, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.FROZEN); strings[index] = siteObj; diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 906e12864b..5f22735d90 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -1528,9 +1528,10 @@ public void defineProperty( * Defines one or more properties on this object. * * @param cx the current Context + * @param scope the current scope. * @param props a map of property ids to property descriptors */ - public void defineOwnProperties(Context cx, ScriptableObject props) { + public void defineOwnProperties(Context cx, Scriptable scope, ScriptableObject props) { Object[] ids = props.getIds(false, true); ScriptableObject[] descs = new ScriptableObject[ids.length]; for (int i = 0, len = ids.length; i < len; ++i) { @@ -1540,20 +1541,40 @@ public void defineOwnProperties(Context cx, ScriptableObject props) { descs[i] = desc; } for (int i = 0, len = ids.length; i < len; ++i) { - defineOwnProperty(cx, ids[i], descs[i]); + defineOwnProperty(cx, scope, ids[i], descs[i]); } } + /** + * @deprecated Use {@link #defineOwnProperties(Context, Scriptable, ScriptableObject)} instead + */ + @Deprecated + public void defineOwnProperties(Context cx, ScriptableObject props) { + Scriptable scope = getParentScope(); + defineOwnProperties(cx, (scope == null ? this : scope), props); + } + /** * Defines a property on an object. * * @param cx the current Context + * @param scope the current scope. * @param id the name/index of the property * @param desc the new property descriptor, as described in 8.6.1 */ - public void defineOwnProperty(Context cx, Object id, ScriptableObject desc) { + public void defineOwnProperty(Context cx, Scriptable scope, Object id, ScriptableObject desc) { checkPropertyDefinition(desc); - defineOwnProperty(cx, id, desc, true); + defineOwnProperty(cx, scope, id, desc, true); + } + + /** + * @deprecated Use {@link #defineOwnProperty(Context, Scriptable, Object, ScriptableObject)} + * instead + */ + @Deprecated + public void defineOwnProperty(Context cx, Object id, ScriptableObject desc) { + Scriptable scope = getParentScope(); + defineOwnProperty(cx, (scope == null ? this : scope), id, desc); } /** @@ -1562,12 +1583,13 @@ public void defineOwnProperty(Context cx, Object id, ScriptableObject desc) { *

Based on [[DefineOwnProperty]] from 8.12.10 of the spec. * * @param cx the current Context + * @param scope the current scope. * @param id the name/index of the property * @param desc the new property descriptor, as described in 8.6.1 * @param checkValid whether to perform validity checks */ protected void defineOwnProperty( - Context cx, Object id, ScriptableObject desc, boolean checkValid) { + Context cx, Scriptable scope, Object id, ScriptableObject desc, boolean checkValid) { Object key = null; int index = 0; @@ -1637,6 +1659,17 @@ protected void defineOwnProperty( } } + /** + * @deprecated Use {@link #defineOwnProperty(Context, Scriptable, Object, ScriptableObject, + * boolean)} instead + */ + @Deprecated + protected void defineOwnProperty( + Context cx, Object id, ScriptableObject desc, boolean checkValid) { + Scriptable scope = getParentScope(); + defineOwnProperty(cx, (scope == null ? this : scope), id, desc, checkValid); + } + /** * Define a property on this object that is implemented using lambda functions. If a property * with the same name already exists, then it will be replaced. This property will appear to the @@ -2648,11 +2681,10 @@ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundE } } - protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { + protected ScriptableObject getOwnPropertyDescriptor(Context cx, Scriptable scope, Object id) { Slot slot = querySlot(cx, id); if (slot == null) return null; - Scriptable scope = getParentScope(); - return slot.getPropertyDescriptor(cx, (scope == null ? this : scope)); + return slot.getPropertyDescriptor(cx, scope); } protected Slot querySlot(Context cx, Object id) { diff --git a/testsrc/org/mozilla/javascript/tests/es6/NativeObjectTest.java b/testsrc/org/mozilla/javascript/tests/es6/NativeObjectTest.java index 016557328f..a996073613 100644 --- a/testsrc/org/mozilla/javascript/tests/es6/NativeObjectTest.java +++ b/testsrc/org/mozilla/javascript/tests/es6/NativeObjectTest.java @@ -18,6 +18,7 @@ /** Test for NativeObject. */ public class NativeObjectTest { + @Test public void testAssignPropertyGetter() { evaluateAndAssert( @@ -277,4 +278,19 @@ private void evaluateAndAssert(final String script, final Object expected) { }); } } + + @Test + public void issue943() { + evaluateAndAssert( + "var foo = function e() {}\n" + + "var fooProto = foo.prototype;\n" + + // + "var fooProtoDesc = + // Object.getOwnPropertyDescriptor(fooProto);\n" + // + "var descProp = fooProtoDesc['constructor'];\n" + + + "var descProp = Object.getOwnPropertyDescriptor(fooProto, 'constructor');" + + "descProp.hasOwnProperty('value');\n", + true); + } }