|
8 | 8 |
|
9 | 9 | import java.util.ArrayList; |
10 | 10 | import java.util.List; |
| 11 | +import java.util.Objects; |
11 | 12 | import org.mozilla.javascript.ScriptRuntime.StringIdOrIndex; |
12 | 13 |
|
13 | 14 | /** |
@@ -213,23 +214,101 @@ private static Object deleteProperty( |
213 | 214 | return false; |
214 | 215 | } |
215 | 216 |
|
| 217 | + /* |
| 218 | + * https://tc39.es/ecma262/#sec-reflect.get |
| 219 | + * 1. If target is not an Object, throw a TypeError exception. |
| 220 | + * 2. Let key be ? ToPropertyKey(propertyKey). |
| 221 | + * 3. If receiver is not present, then |
| 222 | + * a. Set receiver to target. |
| 223 | + * 4. Return ? target.[[Get]](key, receiver). |
| 224 | + */ |
216 | 225 | private static Object get(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { |
217 | | - ScriptableObject target = checkTarget(args); |
218 | | - |
219 | | - if (args.length > 1) { |
220 | | - if (ScriptRuntime.isSymbol(args[1])) { |
221 | | - Object prop = ScriptableObject.getProperty(target, (Symbol) args[1]); |
222 | | - return prop == Scriptable.NOT_FOUND ? Undefined.SCRIPTABLE_UNDEFINED : prop; |
| 226 | + final ScriptableObject target = checkTarget(args); |
| 227 | + final Object propertyKey = args.length > 1 ? args[1] : Undefined.instance; |
| 228 | + final Object receiver = args.length > 2 ? args[2] : target; |
| 229 | + |
| 230 | + // If target is a proxy, delegate to the proxy handler |
| 231 | + if (target instanceof NativeProxy) { |
| 232 | + final NativeProxy proxy = (NativeProxy) target; |
| 233 | + final Function trap = proxy.getTrap("get"); |
| 234 | + if (trap != null) { |
| 235 | + final ScriptableObject proxyTarget = proxy.getTargetThrowIfRevoked(); |
| 236 | + final Object[] trapArgs = {proxyTarget, propertyKey, receiver}; |
| 237 | + final Object trapResult = proxy.callTrap(trap, trapArgs); |
| 238 | + |
| 239 | + // checks for non-configurable properties |
| 240 | + // https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver steps 9 |
| 241 | + final DescriptorInfo targetDesc = proxyTarget.getOwnPropertyDescriptor(cx, propertyKey); |
| 242 | + if (targetDesc != null && targetDesc.isConfigurable(false)) { |
| 243 | + if (targetDesc.isDataDescriptor() && targetDesc.isWritable(false)) { |
| 244 | + if (!Objects.equals(trapResult, targetDesc.value)) { |
| 245 | + throw ScriptRuntime.typeError( |
| 246 | + "proxy must report the same value for the non-writable," |
| 247 | + + " non-configurable property '\"" + propertyKey + "\"'"); |
| 248 | + } |
| 249 | + } |
| 250 | + if (targetDesc.isAccessorDescriptor() |
| 251 | + && (targetDesc.getter == null |
| 252 | + || targetDesc.getter == Scriptable.NOT_FOUND |
| 253 | + || Undefined.isUndefined(targetDesc.getter))) { |
| 254 | + if (!Undefined.isUndefined(trapResult)) { |
| 255 | + throw ScriptRuntime.typeError( |
| 256 | + "proxy must report the same value for the non-writable," |
| 257 | + + " non-configurable property '\"" + propertyKey + "\"'"); |
| 258 | + } |
| 259 | + } |
| 260 | + } |
| 261 | + return trapResult; |
223 | 262 | } |
224 | | - if (args[1] instanceof Number) { |
225 | | - Object prop = ScriptableObject.getProperty(target, ScriptRuntime.toIndex(args[1])); |
226 | | - return prop == Scriptable.NOT_FOUND ? Undefined.SCRIPTABLE_UNDEFINED : prop; |
| 263 | + } |
| 264 | + |
| 265 | + return internalGet(cx, target, propertyKey, receiver); |
| 266 | + } |
| 267 | + |
| 268 | + /* |
| 269 | + * https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-get-p-receiver |
| 270 | + * 1. Let desc be ? O.[[GetOwnProperty]](P). |
| 271 | + * 2. If desc is undefined, then |
| 272 | + * a. Let parent be ? O.[[GetPrototypeOf]](). |
| 273 | + * b. If parent is null, return undefined. |
| 274 | + * c. Return ? parent.[[Get]](P, Receiver). |
| 275 | + * 3. If IsDataDescriptor(desc) is true, return desc.[[Value]]. |
| 276 | + * 4. Assert: IsAccessorDescriptor(desc) is true. |
| 277 | + * 5. Let getter be desc.[[Get]]. |
| 278 | + * 6. If getter is undefined, return undefined. |
| 279 | + * 7. Return ? Call(getter, Receiver). |
| 280 | + */ |
| 281 | + private static Object internalGet( |
| 282 | + Context cx, ScriptableObject target, Object propertyKey, Object receiver) { |
| 283 | + final DescriptorInfo desc = target.getOwnPropertyDescriptor(cx, propertyKey); |
| 284 | + if (desc == null) { |
| 285 | + final Scriptable parent = target.getPrototype(); |
| 286 | + if (parent == null) { |
| 287 | + return Undefined.SCRIPTABLE_UNDEFINED; |
227 | 288 | } |
| 289 | + return internalGet(cx, ScriptableObject.ensureScriptableObject(parent), propertyKey, receiver); |
| 290 | + } |
228 | 291 |
|
229 | | - Object prop = ScriptableObject.getProperty(target, ScriptRuntime.toString(args[1])); |
230 | | - return prop == Scriptable.NOT_FOUND ? Undefined.SCRIPTABLE_UNDEFINED : prop; |
| 292 | + if (desc.isDataDescriptor()) { |
| 293 | + return desc.value == Scriptable.NOT_FOUND |
| 294 | + ? Undefined.SCRIPTABLE_UNDEFINED |
| 295 | + : desc.value; |
231 | 296 | } |
232 | | - return Undefined.SCRIPTABLE_UNDEFINED; |
| 297 | + |
| 298 | + final Object getter = desc.getter; |
| 299 | + if (getter == null || getter == Scriptable.NOT_FOUND || Undefined.isUndefined(getter)) { |
| 300 | + return Undefined.SCRIPTABLE_UNDEFINED; |
| 301 | + } |
| 302 | + |
| 303 | + final Scriptable receiverForCall; |
| 304 | + if (receiver == null || Undefined.isUndefined(receiver)) { |
| 305 | + receiverForCall = cx.isStrictMode() |
| 306 | + ? null |
| 307 | + : ScriptableObject.getTopLevelScope(target); |
| 308 | + } else { |
| 309 | + receiverForCall = ScriptableObject.ensureScriptable(receiver); |
| 310 | + } |
| 311 | + return ((Function) getter).call(cx, target, receiverForCall, ScriptRuntime.emptyArgs); |
233 | 312 | } |
234 | 313 |
|
235 | 314 | private static Scriptable getOwnPropertyDescriptor( |
|
0 commit comments