/** * Copyright (c) 2012-2016 André Bargull * Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms. * * <https://github.com/anba/es6draft> */ package com.github.anba.es6draft.runtime.types.builtins; import static com.github.anba.es6draft.runtime.internal.Errors.newTypeError; import com.github.anba.es6draft.runtime.DeclarativeEnvironmentRecord; import com.github.anba.es6draft.runtime.ExecutionContext; import com.github.anba.es6draft.runtime.LexicalEnvironment; import com.github.anba.es6draft.runtime.Realm; import com.github.anba.es6draft.runtime.internal.CompatibilityOption; import com.github.anba.es6draft.runtime.internal.Messages; import com.github.anba.es6draft.runtime.types.BuiltinSymbol; import com.github.anba.es6draft.runtime.types.Callable; import com.github.anba.es6draft.runtime.types.Intrinsics; import com.github.anba.es6draft.runtime.types.Property; import com.github.anba.es6draft.runtime.types.PropertyDescriptor; /** * <h1>9 Ordinary and Exotic Objects Behaviours</h1><br> * <h2>9.4 Built-in Exotic Object Internal Methods and Data Fields</h2> * <ul> * <li>9.4.4 Arguments Exotic Objects * </ul> */ public final class ArgumentsObject extends OrdinaryObject { /** [[ParameterMap]] */ private ParameterMap parameterMap = null; private boolean hasIndexedAccessors = false; /** * Constructs a new Arguments object. * * @param realm * the realm object */ public ArgumentsObject(Realm realm) { super(realm); } /** * [[ParameterMap]] * * @return the parameter map */ ParameterMap getParameterMap() { return parameterMap; } private boolean isMapped(long propertyKey) { return parameterMap != null && parameterMap.hasOwnProperty(propertyKey); } private static boolean isStrictFunction(Object v) { return v instanceof FunctionObject && ((FunctionObject) v).isStrict(); } @Override public String className() { return "Arguments"; } @Override public boolean hasIndexedAccessors() { return hasIndexedAccessors; } @Override Object getIndexed(long propertyKey) { if (isMapped(propertyKey)) { return parameterMap.get(propertyKey); } return super.getIndexed(propertyKey); } /** * 9.4.4.1 [[GetOwnProperty]] (P) */ @Override protected Property getProperty(ExecutionContext cx, long propertyKey) { /* step 1 (not applicable) */ /* step 2 */ Property desc = ordinaryGetOwnProperty(propertyKey); /* step 3 */ if (desc == null) { return desc; } /* step 4 */ ParameterMap map = this.parameterMap; /* steps 5-6 */ boolean isMapped = map != null && map.hasOwnProperty(propertyKey); /* step 7 */ if (isMapped) { // FIXME: spec issue - maybe add assertion: IsDataDescriptor(desc)? assert desc.isDataDescriptor(); PropertyDescriptor d = desc.toPropertyDescriptor(); d.setValue(map.get(propertyKey)); desc = d.toProperty(); } /* step 8 (not applicable) */ /* step 9 */ return desc; } /** * 9.4.4.1 [[GetOwnProperty]] (P) */ @Override protected Property getProperty(ExecutionContext cx, String propertyKey) { /* step 1 (not applicable) */ /* step 2 */ Property desc = ordinaryGetOwnProperty(propertyKey); /* step 3 */ if (desc == null) { return desc; } /* steps 4-7 (not applicable) */ /* step 8 */ if (desc.isDataDescriptor() && "caller".equals(propertyKey) && isStrictFunction(desc.getValue()) && cx.getRealm().isEnabled(CompatibilityOption.ArgumentsCaller)) { throw newTypeError(cx, Messages.Key.StrictModePoisonPill); } /* step 9 */ return desc; } /** * 9.4.4.2 [[DefineOwnProperty]] (P, Desc) */ @Override protected boolean defineProperty(ExecutionContext cx, long propertyKey, PropertyDescriptor desc) { /* step 1 (not applicable) */ /* step 2 */ ParameterMap map = this.parameterMap; /* step 3 */ boolean isMapped = map != null && map.hasOwnProperty(propertyKey); /* step 4 */ PropertyDescriptor newArgDesc = desc; /* step 5 */ if (isMapped && desc.isDataDescriptor()) { /* step 5.a */ if (!desc.hasValue() && desc.hasWritable() && !desc.isWritable()) { newArgDesc = desc.clone(); newArgDesc.setValue(map.get(propertyKey)); } } /* step 6 */ boolean allowed = ordinaryDefineOwnProperty(cx, propertyKey, newArgDesc); /* step 7 */ if (!allowed) { return false; } /* step 8 */ if (isMapped) { if (desc.isAccessorDescriptor()) { map.delete(propertyKey); } else { if (desc.hasValue()) { map.put(propertyKey, desc.getValue()); } if (desc.hasWritable() && !desc.isWritable()) { map.delete(propertyKey); } } } hasIndexedAccessors |= desc.isAccessorDescriptor(); /* step 9 */ return true; } /** * 9.4.4.3 [[Get]] (P, Receiver) */ @Override protected Object getValue(ExecutionContext cx, long propertyKey, Object receiver) { /* steps 1-2 */ ParameterMap map = this.parameterMap; /* steps 3-4 */ boolean isMapped = map != null && map.hasOwnProperty(propertyKey); /* step 5 */ if (!isMapped) { return super.getValue(cx, propertyKey, receiver); } /* step 6 */ return map.get(propertyKey); } /** * 9.4.4.4 [[Set]] ( P, V, Receiver) */ @Override protected boolean setValue(ExecutionContext cx, long propertyKey, Object value, Object receiver) { /* steps 1, 3.a */ ParameterMap map = this.parameterMap; /* steps 2-3 */ boolean isMapped; if (this != receiver) { /* step 2 */ isMapped = false; } else { /* step 3 */ isMapped = map != null && map.hasOwnProperty(propertyKey); } /* step 4 */ if (isMapped) { map.put(propertyKey, value); // Mapped parameter implies own, writable property on the arguments object. It is not necessary to update // the underlying property in this case! // 1. Ordinary [[Set]] calls [[DefineOwnProperty]]. // 2. [[DefineOwnProperty]] for argument objects calls OrdinaryDefineOwnProperty. // 3. OrdinaryDefineOwnProperty calls [[GetOwnProperty]]. // 4. [[GetOwnProperty]] for argument objects calls OrdinaryGetOwnProperty, but updates [[Value]] from map. // 5. OrdinaryDefineOwnProperty calls ValidateAndApplyPropertyDescriptor. // 6. ValidateAndApplyPropertyDescriptor returns in step 4 if no property attribute changes are detected. return true; } /* step 5 */ return super.setValue(cx, propertyKey, value, receiver); } @Override protected boolean setPropertyValue(ExecutionContext cx, long propertyKey, Object value, Property current) { assert !isMapped(propertyKey); return super.setPropertyValue(cx, propertyKey, value, current); } /** * 9.4.4.5 [[Delete]] (P) */ @Override protected boolean deleteProperty(ExecutionContext cx, long propertyKey) { /* step 1 */ ParameterMap map = this.parameterMap; /* steps 2-3 */ boolean isMapped = map != null && map.hasOwnProperty(propertyKey); /* steps 4-5 */ boolean result = super.deleteProperty(cx, propertyKey); /* step 6 */ if (result && isMapped) { map.delete(propertyKey); } /* step 7 */ return result; } /** * 9.4.4.6 CreateUnmappedArgumentsObject(argumentsList) * <p> * [Called from generated code] * * @param cx * the execution context * @param argumentsList * the function arguments * @return the strict mode arguments object */ public static ArgumentsObject CreateUnmappedArgumentsObject(ExecutionContext cx, Object[] argumentsList) { /* step 1 */ int len = argumentsList.length; /* steps 2-3 */ ArgumentsObject obj = new ArgumentsObject(cx.getRealm()); obj.setPrototype(cx.getIntrinsic(Intrinsics.ObjectPrototype)); /* step 4 */ obj.infallibleDefineOwnProperty("length", new Property(len, true, false, true)); /* steps 5-6 */ for (int index = 0; index < len; ++index) { obj.setIndexed(index, argumentsList[index]); } Callable thrower = cx.getRealm().getThrowTypeError(); /* step 7 */ obj.infallibleDefineOwnProperty(BuiltinSymbol.iterator.get(), new Property(cx.getIntrinsic(Intrinsics.ArrayProto_values), true, false, true)); /* step 8 */ obj.infallibleDefineOwnProperty("callee", new Property(thrower, thrower, false, false)); /* step 9 */ obj.infallibleDefineOwnProperty("caller", new Property(thrower, thrower, false, false)); /* step 10 (not applicable) */ /* step 11 */ return obj; } /** * 9.4.4.7 CreateMappedArgumentsObject ( func, formals, argumentsList, env ) * <p> * [Called from generated code] * * @param cx * the execution context * @param func * the function callee * @param argumentsList * the function arguments * @param env * the current lexical environment * @return the mapped arguments object */ public static ArgumentsObject CreateMappedArgumentsObject(ExecutionContext cx, FunctionObject func, Object[] argumentsList, LexicalEnvironment<? extends DeclarativeEnvironmentRecord> env) { ParameterMap map = ParameterMap.create(func, argumentsList.length, env); return CreateMappedArgumentsObject(cx, func, argumentsList, map); } /** * 9.4.4.7 CreateMappedArgumentsObject ( func, formals, argumentsList, env ) * <p> * [Called from generated code] * * @param cx * the execution context * @param func * the function callee * @param argumentsList * the function arguments * @return the mapped arguments object */ public static ArgumentsObject CreateMappedArgumentsObject(ExecutionContext cx, FunctionObject func, Object[] argumentsList) { return CreateMappedArgumentsObject(cx, func, argumentsList, (ParameterMap) null); } /** * 9.4.4.7 CreateMappedArgumentsObject ( func, formals, argumentsList, env ) * <p> * [Called from generated code] * * @param cx * the execution context * @param func * the function callee * @param argumentsList * the function arguments * @param map * the parameter map * @return the mapped arguments object */ static ArgumentsObject CreateMappedArgumentsObject(ExecutionContext cx, FunctionObject func, Object[] argumentsList, ParameterMap map) { /* step 1 (not applicable) */ /* step 2 */ int len = argumentsList.length; /* steps 3-11 */ ArgumentsObject obj = new ArgumentsObject(cx.getRealm()); obj.setPrototype(cx.getIntrinsic(Intrinsics.ObjectPrototype)); /* steps 12-13 (not applicable) */ /* steps 14-15 */ for (int index = 0; index < len; ++index) { obj.setIndexed(index, argumentsList[index]); } /* step 16 */ obj.infallibleDefineOwnProperty("length", new Property(len, true, false, true)); /* steps 17-21 */ obj.parameterMap = map; /* step 22 */ obj.infallibleDefineOwnProperty(BuiltinSymbol.iterator.get(), new Property(cx.getIntrinsic(Intrinsics.ArrayProto_values), true, false, true)); /* steps 23-24 */ obj.infallibleDefineOwnProperty("callee", new Property(func, true, false, true)); /* step 25 */ return obj; } }