/**
* 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.AbstractOperations.CreateListIterator;
import static com.github.anba.es6draft.runtime.AbstractOperations.ToString;
import static com.github.anba.es6draft.runtime.internal.Errors.newReferenceError;
import static com.github.anba.es6draft.runtime.modules.ModuleSemantics.GetModuleNamespace;
import static com.github.anba.es6draft.runtime.types.Undefined.UNDEFINED;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.github.anba.es6draft.compiler.CompilationException;
import com.github.anba.es6draft.parser.ParserException;
import com.github.anba.es6draft.runtime.EnvironmentRecord;
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.Errors;
import com.github.anba.es6draft.runtime.internal.Messages;
import com.github.anba.es6draft.runtime.modules.MalformedNameException;
import com.github.anba.es6draft.runtime.modules.ModuleExport;
import com.github.anba.es6draft.runtime.modules.ModuleRecord;
import com.github.anba.es6draft.runtime.modules.ResolutionException;
import com.github.anba.es6draft.runtime.types.BuiltinSymbol;
import com.github.anba.es6draft.runtime.types.Property;
import com.github.anba.es6draft.runtime.types.PropertyDescriptor;
import com.github.anba.es6draft.runtime.types.ScriptObject;
import com.github.anba.es6draft.runtime.types.Symbol;
import com.github.anba.es6draft.runtime.types.Type;
/**
* <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.6 Module Namespace Exotic Objects
* </ul>
*/
public final class ModuleNamespaceObject extends OrdinaryObject {
/** [[Module]] */
private final ModuleRecord module;
/** [[Exports]] */
private final Set<String> exports;
private List<String> sortedExports;
/**
* Constructs a new Module object.
*
* @param realm
* the realm object
* @param module
* the module record
* @param exports
* the list of exported bindings
*/
public ModuleNamespaceObject(Realm realm, ModuleRecord module, Set<String> exports) {
super(realm);
this.module = module;
this.exports = exports;
}
/**
* Returns the module record.
*
* @return the module record
*/
public ModuleRecord getModule() {
return module;
}
/**
* Returns the list of exported bindings.
*
* @return the list of exported bindings
*/
public Set<String> getExports() {
return exports;
}
private List<String> getSortedExports() {
if (sortedExports == null) {
ArrayList<String> sorted = new ArrayList<>(exports);
Collections.sort(sorted);
sortedExports = Collections.unmodifiableList(sorted);
}
return sortedExports;
}
@Override
public String toString() {
return String.format("%s, module=%s", super.toString(), module.getSourceCodeId());
}
@Override
public boolean hasSpecialIndexedProperties() {
return true;
}
/** 9.4.6.1 [[GetPrototypeOf]] ( ) */
@Override
public ScriptObject getPrototypeOf(ExecutionContext cx) {
return null;
}
/** 9.4.6.2 [[SetPrototypeOf]] (V) */
@Override
public boolean setPrototypeOf(ExecutionContext cx, ScriptObject prototype) {
return false;
}
/** 9.4.6.3 [[IsExtensible]] ( ) */
@Override
public boolean isExtensible(ExecutionContext cx) {
return false;
}
/** 9.4.6.4 [[PreventExtensions]] ( ) */
@Override
public boolean preventExtensions(ExecutionContext cx) {
return true;
}
@Override
protected boolean hasOwnProperty(ExecutionContext cx, long propertyKey) {
return hasOwnProperty(cx, ToString(propertyKey));
}
@Override
protected boolean hasOwnProperty(ExecutionContext cx, String propertyKey) {
if (!exports.contains(propertyKey)) {
return false;
}
getValue(cx, propertyKey, this);
return true;
}
@Override
protected boolean hasOwnProperty(ExecutionContext cx, Symbol propertyKey) {
return ordinaryHasOwnProperty(propertyKey);
}
/** 9.4.6.5 [[GetOwnProperty]] (P) */
@Override
protected Property getProperty(ExecutionContext cx, long propertyKey) {
/* step 1 (not applicable) */
/* steps 2-6 */
return getProperty(cx, ToString(propertyKey));
}
/** 9.4.6.5 [[GetOwnProperty]] (P) */
@Override
protected Property getProperty(ExecutionContext cx, String propertyKey) {
/* step 1 (not applicable) */
/* steps 2-3 */
if (!exports.contains(propertyKey)) {
return null;
}
/* steps 4-5 */
Object value = getValue(cx, propertyKey, this);
/* step 6 */
return new Property(value, true, true, false);
}
/** 9.4.6.5 [[GetOwnProperty]] (P) */
@Override
protected Property getProperty(ExecutionContext cx, Symbol propertyKey) {
/* step 1 */
/* step 2 (not applicable) */
return ordinaryGetOwnProperty(propertyKey);
}
/** 9.4.6.6 [[DefineOwnProperty]] (P, Desc) */
@Override
protected boolean defineProperty(ExecutionContext cx, long propertyKey, PropertyDescriptor desc) {
/* step 1 */
return false;
}
/** 9.4.6.6 [[DefineOwnProperty]] (P, Desc) */
@Override
protected boolean defineProperty(ExecutionContext cx, String propertyKey, PropertyDescriptor desc) {
/* step 1 */
return false;
}
/** 9.4.6.6 [[DefineOwnProperty]] (P, Desc) */
@Override
protected boolean defineProperty(ExecutionContext cx, Symbol propertyKey, PropertyDescriptor desc) {
/* step 1 */
return false;
}
/** 9.4.6.7 [[HasProperty]] (P) */
@Override
protected boolean has(ExecutionContext cx, long propertyKey) {
return has(cx, ToString(propertyKey));
}
/** 9.4.6.7 [[HasProperty]] (P) */
@Override
protected boolean has(ExecutionContext cx, String propertyKey) {
/* step 1 (not applicable) */
/* steps 2-4 */
return exports.contains(propertyKey);
}
/** 9.4.6.7 [[HasProperty]] (P) */
@Override
protected boolean has(ExecutionContext cx, Symbol propertyKey) {
/* step 1 */
/* steps 2-4 (not applicable) */
return ordinaryHasProperty(cx, propertyKey);
}
/** 9.4.6.8 [[Get]] (P, Receiver) */
@Override
protected Object getValue(ExecutionContext cx, long propertyKey, Object receiver) {
return getValue(cx, ToString(propertyKey), receiver);
}
/** 9.4.6.8 [[Get]] (P, Receiver) */
@Override
protected Object getValue(ExecutionContext cx, String propertyKey, Object receiver) {
/* step 1 (not applicable) */
/* step 2 (not applicable) */
/* step 3 */
Set<String> exports = this.exports;
/* step 4 */
if (!exports.contains(propertyKey)) {
return UNDEFINED;
}
/* step 5 */
ModuleRecord m = this.module;
/* steps 6-8 */
ModuleExport binding;
try {
/* steps 6, 8 */
binding = m.resolveExport(propertyKey, new HashMap<>(), new HashSet<>());
} catch (IOException e) {
/* step 7 */
throw Errors.newInternalError(cx, e, Messages.Key.ModulesIOException, e.getMessage());
} catch (ResolutionException | MalformedNameException e) {
/* step 7 */
throw e.toScriptException(cx);
} catch (ParserException | CompilationException e) {
/* step 7 */
throw e.toScriptException(cx);
}
/* step 8 */
assert binding != null && !binding.isAmbiguous();
/* step 9 */
ModuleRecord targetModule = binding.getModule();
/* step 10 */
assert targetModule != null;
/* step 11 */
LexicalEnvironment<?> targetEnv = targetModule.getEnvironment();
/* step 12 */
if (targetEnv == null) {
throw newReferenceError(cx, Messages.Key.UninitializedBinding, binding.getBindingName());
}
/* step ? (Extension: Export From) */
if (binding.isNameSpaceExport()) {
try {
return GetModuleNamespace(cx, targetModule);
} catch (IOException e) {
throw Errors.newInternalError(cx, Messages.Key.ModulesIOException, e.getMessage());
} catch (MalformedNameException | ResolutionException e) {
throw e.toScriptException(cx);
}
}
/* step 13 */
EnvironmentRecord targetEnvRec = targetEnv.getEnvRec();
/* step 14 */
return targetEnvRec.getBindingValue(binding.getBindingName(), true);
}
/** 9.4.6.8 [[Get]] (P, Receiver) */
@Override
protected Object getValue(ExecutionContext cx, Symbol propertyKey, Object receiver) {
/* step 1 (not applicable) */
/* step 2 */
/* steps 3-16 (not applicable) */
return super.getValue(cx, propertyKey, receiver);
}
/** 9.4.6.9 [[Set]] ( P, V, Receiver) */
@Override
protected boolean setValue(ExecutionContext cx, long propertyKey, Object value, Object receiver) {
/* step 1 */
return false;
}
/** 9.4.6.9 [[Set]] ( P, V, Receiver) */
@Override
protected boolean setValue(ExecutionContext cx, String propertyKey, Object value, Object receiver) {
/* step 1 */
return false;
}
/** 9.4.6.9 [[Set]] ( P, V, Receiver) */
@Override
protected boolean setValue(ExecutionContext cx, Symbol propertyKey, Object value, Object receiver) {
/* step 1 */
return false;
}
/** 9.4.6.10 [[Delete]] (P) */
@Override
protected boolean deleteProperty(ExecutionContext cx, long propertyKey) {
return deleteProperty(cx, ToString(propertyKey));
}
/** 9.4.6.10 [[Delete]] (P) */
@Override
protected boolean deleteProperty(ExecutionContext cx, String propertyKey) {
/* step 1 (not applicable) */
/* steps 2-4 */
return !exports.contains(propertyKey);
}
/** 9.4.6.10 [[Delete]] (P) */
@Override
protected boolean deleteProperty(ExecutionContext cx, Symbol propertyKey) {
return true;
}
/** 9.4.6.11 [[Enumerate]] () */
@Override
protected List<String> getEnumerableKeys(ExecutionContext cx) {
return getSortedExports();
}
@Override
protected Enumerability isEnumerableOwnProperty(String propertyKey) {
assert exports.contains(propertyKey) : String.format("'%s' is not an exported binding", propertyKey);
return Enumerability.Enumerable;
}
/** 9.4.6.12 [[OwnPropertyKeys]] ( ) */
@Override
protected List<Object> getOwnPropertyKeys(ExecutionContext cx) {
int totalSize = countProperties(true) + exports.size();
/* step 1 */
ArrayList<Object> exports = new ArrayList<>(totalSize);
exports.addAll(getSortedExports());
/* steps 2-3 */
appendSymbolProperties(exports);
/* step 4 */
return exports;
}
/**
* 9.4.6.13 ModuleNamespaceCreate (module, exports)
*
* @param cx
* the execution context
* @param module
* the module record
* @param exports
* the exported bindings
* @return the new module namespace object
*/
public static ModuleNamespaceObject ModuleNamespaceCreate(ExecutionContext cx, ModuleRecord module,
Set<String> exports) {
/* step 1 (not applicable) */
/* step 2 */
assert module.getNamespace() == null;
/* step 3 (not applicable) */
/* steps 4-7 */
ModuleNamespaceObject m = new ModuleNamespaceObject(cx.getRealm(), module, exports);
/* step 8 */
// 26.3.1 @@toStringTag
m.infallibleDefineOwnProperty(BuiltinSymbol.toStringTag.get(), new Property("Module", false, false, true));
// 26.3.2 [ @@iterator ] ( )
BuiltinFunction iterator;
if (cx.getRealm().isEnabled(CompatibilityOption.Enumerate)) {
iterator = new ModuleIteratorFunction(cx.getRealm());
} else {
iterator = new ModuleExportsIteratorFunction(cx.getRealm());
}
m.infallibleDefineOwnProperty(BuiltinSymbol.iterator.get(), new Property(iterator, true, false, true));
/* step 9 */
module.setNamespace(m);
/* step 10 */
return m;
}
/**
* 26.3.2 [ @@iterator ] ( )
*/
public static final class ModuleIteratorFunction extends BuiltinFunction {
public ModuleIteratorFunction(Realm realm) {
super(realm, "[Symbol.iterator]", 0);
createDefaultFunctionProperties();
}
private ModuleIteratorFunction(Realm realm, Void ignore) {
super(realm, "[Symbol.iterator]", 0);
}
@Override
public ModuleIteratorFunction clone() {
return new ModuleIteratorFunction(getRealm(), null);
}
@Override
public Object call(ExecutionContext callerContext, Object thisValue, Object... args) {
ExecutionContext calleeContext = calleeContext();
/* step 1 (not applicable) */
/* step 2 */
if (!Type.isObject(thisValue)) {
throw Errors.newTypeError(calleeContext, Messages.Key.NotObjectType);
}
/* step 3 */
return Type.objectValue(thisValue).enumerate(calleeContext);
}
}
/**
* 26.3.2 [ @@iterator ] ( )
*/
public static final class ModuleExportsIteratorFunction extends BuiltinFunction {
public ModuleExportsIteratorFunction(Realm realm) {
super(realm, "[Symbol.iterator]", 0);
createDefaultFunctionProperties();
}
private ModuleExportsIteratorFunction(Realm realm, Void ignore) {
super(realm, "[Symbol.iterator]", 0);
}
@Override
public ModuleIteratorFunction clone() {
return new ModuleIteratorFunction(getRealm(), null);
}
@Override
public Object call(ExecutionContext callerContext, Object thisValue, Object... args) {
ExecutionContext calleeContext = calleeContext();
/* step 1 (not applicable) */
/* step 2 */
if (!(thisValue instanceof ModuleNamespaceObject)) {
throw Errors.newTypeError(calleeContext, Messages.Key.IncompatibleObject);
}
ModuleNamespaceObject module = (ModuleNamespaceObject) thisValue;
/* steps 3-4 */
return CreateListIterator(calleeContext, module.getSortedExports());
}
}
}