/**
* 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;
import static com.github.anba.es6draft.runtime.AbstractOperations.DefinePropertyOrThrow;
import static com.github.anba.es6draft.runtime.AbstractOperations.HasOwnProperty;
import static com.github.anba.es6draft.runtime.AbstractOperations.IsExtensible;
import static com.github.anba.es6draft.runtime.AbstractOperations.Set;
import static com.github.anba.es6draft.runtime.internal.Errors.newTypeError;
import static com.github.anba.es6draft.runtime.types.Undefined.UNDEFINED;
import java.util.HashSet;
import java.util.Set;
import com.github.anba.es6draft.runtime.internal.Messages;
import com.github.anba.es6draft.runtime.types.Property;
import com.github.anba.es6draft.runtime.types.PropertyDescriptor;
import com.github.anba.es6draft.runtime.types.ScriptObject;
/**
* <h1>8 Executable Code and Execution Contexts</h1><br>
* <h2>8.1 Lexical Environments</h2><br>
* <h3>8.1.1 Environment Records</h3>
* <ul>
* <li>8.1.1.4 Global Environment Records
* </ul>
*/
public final class GlobalEnvironmentRecord implements EnvironmentRecord {
private final ExecutionContext cx;
private final ScriptObject globalObject;
/** [[GlobalThisValue]] */
private final ScriptObject globalThisValue;
/** [[ObjectRecord]] */
private final ObjectEnvironmentRecord objectRec;
/** [[DeclarativeRecord]] */
private final DeclarativeEnvironmentRecord declRec;
/** [[VarNames]] */
private final HashSet<String> varNames = new HashSet<>();
public GlobalEnvironmentRecord(ExecutionContext cx, ScriptObject globalObject, ScriptObject thisValue) {
this.cx = cx;
this.globalObject = globalObject;
this.globalThisValue = thisValue;
this.objectRec = new ObjectEnvironmentRecord(cx, globalObject, false);
this.declRec = new DeclarativeEnvironmentRecord(cx, false);
}
@Override
public String toString() {
return String.format("%s: {%n\tobjectEnv=%s,%n\tdeclEnv=%s,%n\tvarNames=%s%n}", getClass().getSimpleName(),
objectRec, declRec, varNames);
}
@Override
public Set<String> bindingNames() {
HashSet<String> names = new HashSet<>();
names.addAll(declRec.bindingNames());
names.addAll(objectRec.bindingNames());
return names;
}
/**
* [[GlobalThisValue]]
*
* @return the global this value
*/
public ScriptObject getGlobalThisValue() {
return globalThisValue;
}
/**
* 8.1.1.4.1 HasBinding(N)
*/
@Override
public boolean hasBinding(String name) {
/* steps 1-2 (omitted) */
/* step 3 */
if (declRec.hasBinding(name)) {
return true;
}
/* step 4 (omitted) */
/* step 5 */
return objectRec.hasBinding(name);
}
/**
* 8.1.1.4.2 CreateMutableBinding (N, D)
*/
@Override
public void createMutableBinding(String name, boolean deletable) {
/* steps 1-2 (omitted) */
/* step 3 */
if (declRec.hasBinding(name)) {
throw newTypeError(cx, Messages.Key.VariableRedeclaration, name);
}
/* step 4 */
declRec.createMutableBinding(name, deletable);
}
/**
* 8.1.1.4.3 CreateImmutableBinding (N, S)
*/
@Override
public void createImmutableBinding(String name, boolean strict) {
/* steps 1-2 (omitted) */
/* step 3 */
if (declRec.hasBinding(name)) {
throw newTypeError(cx, Messages.Key.VariableRedeclaration, name);
}
/* step 4 */
declRec.createImmutableBinding(name, strict);
}
/**
* 8.1.1.4.4 InitializeBinding (N,V)
*/
@Override
public void initializeBinding(String name, Object value) {
/* steps 1-2 (omitted) */
/* step 3 */
if (declRec.hasBinding(name)) {
declRec.initializeBinding(name, value);
return;
}
/* step 4 (not applicable) */
/* step 5 (omitted) */
/* step 6 */
objectRec.initializeBinding(name, value);
}
/**
* 8.1.1.4.5 SetMutableBinding (N,V,S)
*/
@Override
public void setMutableBinding(String name, Object value, boolean strict) {
/* steps 1-2 (omitted) */
/* step 3 */
if (declRec.hasBinding(name)) {
declRec.setMutableBinding(name, value, strict);
return;
}
/* step 4 (omitted) */
/* step 5 */
objectRec.setMutableBinding(name, value, strict);
}
/**
* 8.1.1.4.6 GetBindingValue(N,S)
*/
@Override
public Object getBindingValue(String name, boolean strict) {
/* steps 1-2 (omitted) */
/* step 3 */
if (declRec.hasBinding(name)) {
return declRec.getBindingValue(name, strict);
}
/* step 4 (omitted) */
/* step 5 */
return objectRec.getBindingValue(name, strict);
}
/**
* 8.1.1.4.7 DeleteBinding (N)
*/
@Override
public boolean deleteBinding(String name) {
/* steps 1-2 (omitted) */
/* step 3 */
if (declRec.hasBinding(name)) {
return declRec.deleteBinding(name);
}
/* step 4 (omitted) */
/* steps 5-7 */
boolean existingProp = HasOwnProperty(cx, globalObject, name);
/* step 8 */
if (existingProp) {
/* steps 8.a-b */
boolean status = objectRec.deleteBinding(name);
/* step 8.c */
if (status) {
varNames.remove(name);
}
/* step 8.d */
return status;
}
/* step 9 */
return true;
}
/**
* 8.1.1.4.8 HasThisBinding ()
*/
@Override
public boolean hasThisBinding() {
/* step 1 */
return true;
}
/**
* 8.1.1.4.9 HasSuperBinding ()
*/
@Override
public boolean hasSuperBinding() {
/* step 1 */
return false;
}
/**
* 8.1.1.4.10 WithBaseObject()
*/
@Override
public ScriptObject withBaseObject() {
/* step 1 */
return null;
}
/**
* 8.1.1.4.11 GetThisBinding ()
*/
@Override
public ScriptObject getThisBinding(ExecutionContext cx) {
/* steps 1-2 */
return globalThisValue;
}
/**
* 8.1.1.4.12 HasVarDeclaration (N)
*
* @param name
* the binding name
* @return {@code true} if the variable binding is present
*/
public boolean hasVarDeclaration(String name) {
/* steps 1-4 */
return varNames.contains(name);
}
/**
* 8.1.1.4.13 HasLexicalDeclaration (N)
*
* @param name
* the binding name
* @return {@code true} if the lexical binding is present
*/
public boolean hasLexicalDeclaration(String name) {
/* steps 1-3 */
return declRec.hasBinding(name);
}
/**
* 8.1.1.4.14 HasRestrictedGlobalProperty (N)
*
* @param name
* the binding name
* @return {@code true} if the global binding is present and non-configurable
*/
public boolean hasRestrictedGlobalProperty(String name) {
/* steps 1-3 (omitted) */
/* steps 4-5 */
Property existingProp = globalObject.getOwnProperty(cx, name);
/* step 6 */
if (existingProp == null) {
return false;
}
/* step 7 */
if (existingProp.isConfigurable()) {
return false;
}
/* step 8 */
return true;
}
/**
* 8.1.1.4.15 CanDeclareGlobalVar (N)
*
* @param name
* the binding name
* @return {@code true} if the binding can be created
*/
public boolean canDeclareGlobalVar(String name) {
/* steps 1-3 (omitted) */
/* steps 4-5 */
boolean hasProperty = HasOwnProperty(cx, globalObject, name);
/* step 6 */
if (hasProperty) {
return true;
}
/* step 7 */
return IsExtensible(cx, globalObject);
}
/**
* 8.1.1.4.16 CanDeclareGlobalFunction (N)
*
* @param name
* the binding name
* @return {@code true} if the binding can be created
*/
public boolean canDeclareGlobalFunction(String name) {
/* steps 1-3 (omitted) */
/* steps 4-5 */
Property existingProp = globalObject.getOwnProperty(cx, name);
/* step 6 */
if (existingProp == null) {
return IsExtensible(cx, globalObject);
}
/* step 7 */
if (existingProp.isConfigurable()) {
return true;
}
/* step 8 */
if (existingProp.isDataDescriptor() && existingProp.isWritable() && existingProp.isEnumerable()) {
return true;
}
/* step 9 */
return false;
}
/**
* 8.1.1.4.17 CreateGlobalVarBinding (N, D)
*
* @param name
* the binding name
* @param deletable
* the deletable for the binding
*/
public void createGlobalVarBinding(String name, boolean deletable) {
/* steps 1-3 (omitted) */
/* steps 4-5 */
boolean hasProperty = HasOwnProperty(cx, globalObject, name);
/* steps 6-7 */
boolean extensible = IsExtensible(cx, globalObject);
/* step 8 */
if (!hasProperty && extensible) {
/* steps 8.a-b */
objectRec.createMutableBinding(name, deletable);
/* steps 8.c-d */
objectRec.initializeBinding(name, UNDEFINED);
}
/* steps 9-10 */
varNames.add(name);
/* step 11 (return) */
}
/**
* 8.1.1.4.18 CreateGlobalFunctionBinding (N, V, D)
*
* @param name
* the binding name
* @param value
* the function value
* @param deletable
* the deletable for the binding
*/
public void createGlobalFunctionBinding(String name, Object value, boolean deletable) {
/* steps 1-3 (omitted) */
/* steps 4-5 */
Property existingProp = globalObject.getOwnProperty(cx, name);
/* steps 6-7 */
PropertyDescriptor desc;
if (existingProp == null || existingProp.isConfigurable()) {
desc = new PropertyDescriptor(value, true, true, deletable);
} else {
desc = new PropertyDescriptor(value);
}
/* steps 8-9 */
DefinePropertyOrThrow(cx, globalObject, name, desc);
/* steps 10-12 */
Set(cx, globalObject, name, value, false);
/* steps 13-14 */
varNames.add(name);
/* step 15 (return) */
}
}