/**
* 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.objects.reflect;
import static com.github.anba.es6draft.runtime.AbstractOperations.ToFlatString;
import static com.github.anba.es6draft.runtime.internal.Errors.newInternalError;
import static com.github.anba.es6draft.runtime.internal.Errors.newTypeError;
import static com.github.anba.es6draft.runtime.internal.Properties.createProperties;
import static com.github.anba.es6draft.runtime.modules.ModuleSemantics.GetModuleNamespace;
import static com.github.anba.es6draft.runtime.objects.promise.PromiseAbstractOperations.PromiseOf;
import static com.github.anba.es6draft.runtime.types.Undefined.UNDEFINED;
import java.io.IOException;
import java.util.Objects;
import com.github.anba.es6draft.compiler.CompilationException;
import com.github.anba.es6draft.parser.ParserException;
import com.github.anba.es6draft.runtime.ExecutionContext;
import com.github.anba.es6draft.runtime.Realm;
import com.github.anba.es6draft.runtime.internal.CompatibilityOption;
import com.github.anba.es6draft.runtime.internal.Initializable;
import com.github.anba.es6draft.runtime.internal.Messages;
import com.github.anba.es6draft.runtime.internal.Properties.Attributes;
import com.github.anba.es6draft.runtime.internal.Properties.CompatibilityExtension;
import com.github.anba.es6draft.runtime.internal.Properties.Function;
import com.github.anba.es6draft.runtime.internal.Properties.Prototype;
import com.github.anba.es6draft.runtime.internal.Properties.Value;
import com.github.anba.es6draft.runtime.internal.ScriptException;
import com.github.anba.es6draft.runtime.modules.Loader;
import com.github.anba.es6draft.runtime.modules.MalformedNameException;
import com.github.anba.es6draft.runtime.modules.ModuleLoader;
import com.github.anba.es6draft.runtime.modules.ModuleRecord;
import com.github.anba.es6draft.runtime.modules.ModuleSource;
import com.github.anba.es6draft.runtime.modules.ResolutionException;
import com.github.anba.es6draft.runtime.modules.SourceIdentifier;
import com.github.anba.es6draft.runtime.modules.loader.StringModuleSource;
import com.github.anba.es6draft.runtime.types.Intrinsics;
/**
* <h1>26 Reflection</h1><br>
* <h2>26.4 The System Object</h2>
*/
public final class SystemObject extends LoaderObject implements Initializable {
/**
* Constructs a new System object.
*
* @param realm
* the realm object
*/
public SystemObject(Realm realm) {
super(realm);
}
@Override
public void initialize(Realm realm) {
setLoader(new Loader(realm, this));
createProperties(realm, this, Properties.class);
createProperties(realm, this, AdditionalProperties.class);
if (realm.isEnabled(CompatibilityOption.Loader)) {
setPrototype(realm.getIntrinsic(Intrinsics.LoaderPrototype));
}
}
/**
* Properties of the System Object
*/
public enum Properties {
;
@Prototype
public static final Intrinsics __proto__ = Intrinsics.ObjectPrototype;
/**
* System.global
*
* @param cx
* the execution context
* @return the global object
*/
@Value(name = "global", attributes = @Attributes(writable = false, enumerable = false, configurable = true))
public static Object global(ExecutionContext cx) {
return cx.getRealm().getGlobalThis();
}
}
/**
* Properties of the System Object
*/
@CompatibilityExtension(CompatibilityOption.System)
public enum AdditionalProperties {
;
/**
* Abstract Operation: thisLoader(value)
*
* @param cx
* the execution context
* @param value
* the argument value
* @return the loader object
*/
private static LoaderObject thisLoader(ExecutionContext cx, Object value) {
if (value instanceof LoaderObject) {
return (LoaderObject) value;
}
throw newTypeError(cx, Messages.Key.IncompatibleObject);
}
private static SourceIdentifier normalize(ExecutionContext cx,
ExecutionContext callerContext, ModuleLoader moduleLoader, String unnormalizedName) {
// TODO: Compute referrerId from caller context?
SourceIdentifier referrerId = null;
try {
return moduleLoader.normalizeName(unnormalizedName, referrerId);
} catch (MalformedNameException e) {
throw e.toScriptException(cx);
}
}
private static ScriptException toScriptException(ExecutionContext cx, IOException e) {
return newInternalError(cx, e, Messages.Key.ModulesIOException, Objects.toString(e.getMessage(), ""));
}
/**
* System.define(moduleName, source)
*
* @param cx
* the execution context
* @param callerContext
* the caller execution context
* @param thisValue
* the function this-value
* @param moduleName
* the module name
* @param source
* the module source code
* @return a promise object or undefined
*/
@Function(name = "define", arity = 2)
public static Object define(ExecutionContext cx, ExecutionContext callerContext,
Object thisValue, Object moduleName, Object source) {
LoaderObject loader = thisLoader(cx, thisValue);
Realm realm = loader.getLoader().getRealm();
ModuleLoader moduleLoader = realm.getModuleLoader();
String unnormalizedName = ToFlatString(cx, moduleName);
String sourceCode = ToFlatString(cx, source);
SourceIdentifier identifier = normalize(cx, callerContext, moduleLoader,
unnormalizedName);
ModuleSource src = new StringModuleSource(identifier, sourceCode);
try {
ModuleRecord module = moduleLoader.define(identifier, src, realm);
module.instantiate();
module.evaluate();
return PromiseOf(cx, GetModuleNamespace(cx, module));
} catch (IOException e) {
return PromiseOf(cx, toScriptException(cx, e));
} catch (MalformedNameException | ResolutionException e) {
return PromiseOf(cx, e.toScriptException(cx));
} catch (ScriptException | ParserException | CompilationException e) {
return PromiseOf(cx, e.toScriptException(cx));
}
}
/**
* System.import(moduleName)
*
* @param cx
* the execution context
* @param callerContext
* the caller execution context
* @param thisValue
* the function this-value
* @param moduleName
* the module name
* @return a promise object or undefined
*/
@Function(name = "import", arity = 1)
public static Object _import(ExecutionContext cx, ExecutionContext callerContext,
Object thisValue, Object moduleName) {
LoaderObject loader = thisLoader(cx, thisValue);
Realm realm = loader.getLoader().getRealm();
ModuleLoader moduleLoader = realm.getModuleLoader();
String unnormalizedName = ToFlatString(cx, moduleName);
SourceIdentifier normalizedModuleName = normalize(cx, callerContext, moduleLoader,
unnormalizedName);
try {
ModuleRecord module = moduleLoader.resolve(normalizedModuleName, realm);
module.instantiate();
return PromiseOf(cx, GetModuleNamespace(cx, module));
} catch (IOException e) {
return PromiseOf(cx, toScriptException(cx, e));
} catch (MalformedNameException | ResolutionException e) {
return PromiseOf(cx, e.toScriptException(cx));
} catch (ScriptException | ParserException | CompilationException e) {
return PromiseOf(cx, e.toScriptException(cx));
}
}
/**
* System.load(moduleName)
*
* @param cx
* the execution context
* @param callerContext
* the caller execution context
* @param thisValue
* the function this-value
* @param moduleName
* the module name
* @return undefined
*/
@Function(name = "load", arity = 1)
public static Object load(ExecutionContext cx, ExecutionContext callerContext,
Object thisValue, Object moduleName) {
LoaderObject loader = thisLoader(cx, thisValue);
Realm realm = loader.getLoader().getRealm();
ModuleLoader moduleLoader = realm.getModuleLoader();
String unnormalizedName = ToFlatString(cx, moduleName);
SourceIdentifier normalizedModuleName = normalize(cx, callerContext, moduleLoader,
unnormalizedName);
try {
moduleLoader.load(normalizedModuleName);
} catch (IOException e) {
return PromiseOf(cx, toScriptException(cx, e));
} catch (MalformedNameException e) {
return PromiseOf(cx, e.toScriptException(cx));
} catch (ParserException | CompilationException e) {
return PromiseOf(cx, e.toScriptException(cx));
}
return PromiseOf(cx, UNDEFINED);
}
/**
* System.get(moduleName)
*
* @param cx
* the execution context
* @param callerContext
* the caller execution context
* @param thisValue
* the function this-value
* @param moduleName
* the module name
* @return the module or undefined
*/
@Function(name = "get", arity = 1)
public static Object get(ExecutionContext cx, ExecutionContext callerContext,
Object thisValue, Object moduleName) {
LoaderObject loader = thisLoader(cx, thisValue);
Realm realm = loader.getLoader().getRealm();
ModuleLoader moduleLoader = realm.getModuleLoader();
String unnormalizedName = ToFlatString(cx, moduleName);
SourceIdentifier normalizedModuleName = normalize(cx, callerContext, moduleLoader,
unnormalizedName);
ModuleRecord module = moduleLoader.get(normalizedModuleName, realm);
if (module == null) {
return UNDEFINED;
}
try {
module.instantiate();
module.evaluate();
return GetModuleNamespace(cx, module);
} catch (IOException e) {
throw toScriptException(cx, e);
} catch (MalformedNameException | ResolutionException e) {
throw e.toScriptException(cx);
} catch (ParserException | CompilationException e) {
throw e.toScriptException(cx);
}
}
/**
* System.normalize(moduleName)
*
* @param cx
* the execution context
* @param callerContext
* the caller execution context
* @param thisValue
* the function this-value
* @param moduleName
* the module name
* @return the normalized module name
*/
@Function(name = "normalize", arity = 1)
public static Object normalize(ExecutionContext cx, ExecutionContext callerContext,
Object thisValue, Object moduleName) {
LoaderObject loader = thisLoader(cx, thisValue);
Realm realm = loader.getLoader().getRealm();
ModuleLoader moduleLoader = realm.getModuleLoader();
String unnormalizedName = ToFlatString(cx, moduleName);
return normalize(cx, callerContext, moduleLoader, unnormalizedName).toString();
}
}
}