/**
* 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 java.util.BitSet;
import com.github.anba.es6draft.runtime.DeclarativeEnvironmentRecord;
import com.github.anba.es6draft.runtime.LexicalEnvironment;
import com.github.anba.es6draft.runtime.internal.RuntimeInfo;
/**
* <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 Exotic Arguments Objects
* </ul>
*/
final class ParameterMap {
private static final long MAX_LENGTH = 0x7FFF_FFFF;
private final LexicalEnvironment<? extends DeclarativeEnvironmentRecord> env;
private final String[] parameters;
private final int length;
private BitSet unmapped; // lazily instantiated
private ParameterMap(LexicalEnvironment<? extends DeclarativeEnvironmentRecord> env, String[] parameterNames,
int length) {
this.env = env;
this.parameters = parameterNames;
this.length = length;
}
private boolean isUnmapped(int index) {
return unmapped != null && unmapped.get(index);
}
private void setUnmapped(int index) {
if (unmapped == null) {
unmapped = new BitSet();
}
unmapped.set(index);
}
/**
* Returns a non-negative integer if {@code p} is a valid argument index, otherwise <code>-1</code>.
*
* @param p
* the property key
* @return the integer index or {@code -1}
*/
static int toArgumentIndex(long p) {
return 0 <= p && p < MAX_LENGTH ? (int) p : -1;
}
/**
* 9.4.4.7 CreateMappedArgumentsObject ( func, formals, argumentsList, env )
* <p>
* Returns a new {@link ParameterMap} if there are any mapped arguments, otherwise <code>null</code>.
*
* @param func
* the function object
* @param len
* the actual number of function arguments
* @param env
* the current lexical environment
* @return a new parameter map if mapped parameters are present, otherwise {@code null}
*/
static ParameterMap create(FunctionObject func, int len,
LexicalEnvironment<? extends DeclarativeEnvironmentRecord> env) {
assert func.getCode().is(RuntimeInfo.FunctionFlags.MappedArguments);
// Directly return null if no arguments were passed.
if (len == 0) {
return null;
}
/* steps 17-20 */
String[] parameterNames = func.getCode().parameters();
for (int index = Math.min(len, parameterNames.length) - 1; index >= 0; --index) {
if (parameterNames[index] != null) {
// Found a mapped argument.
return new ParameterMap(env, parameterNames, index + 1);
}
}
// No mapped arguments found.
return null;
}
/**
* Tests whether {@code propertyKey} is an array index for a mapped argument.
*
* @param propertyKey
* the property key
* @return {@code true} if the property key is mapped
*/
boolean hasOwnProperty(long propertyKey) {
int index = toArgumentIndex(propertyKey);
if (0 <= index && index < length && !isUnmapped(index)) {
return parameters[index] != null;
}
return false;
}
/**
* 9.4.4.7.1 MakeArgGetter (name, env)
*
* @param propertyKey
* the property key
* @return the mapped argument
*/
Object get(long propertyKey) {
int index = toArgumentIndex(propertyKey);
assert (0 <= index && index < length && parameters[index] != null);
assert !isUnmapped(index);
String name = parameters[index];
return env.getEnvRec().getBindingValue(name, false);
}
/**
* 9.4.4.7.2 MakeArgSetter (name, env)
*
* @param propertyKey
* the property key
* @param value
* the new value for the mapped argument
*/
void put(long propertyKey, Object value) {
int index = toArgumentIndex(propertyKey);
assert (0 <= index && index < length && parameters[index] != null);
assert !isUnmapped(index);
String name = parameters[index];
env.getEnvRec().setMutableBinding(name, value, false);
}
/**
* Removes the mapping for {@code arguments[propertyKey]}.
*
* @param propertyKey
* the property key
*/
void delete(long propertyKey) {
int index = toArgumentIndex(propertyKey);
assert (0 <= index && index < length && parameters[index] != null);
assert !isUnmapped(index);
setUnmapped(index);
}
}