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