/**
* 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.Realm.InitializeHostDefinedRealm;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayDeque;
import com.github.anba.es6draft.compiler.CompilationException;
import com.github.anba.es6draft.parser.ParserException;
import com.github.anba.es6draft.runtime.internal.CompatibilityOption;
import com.github.anba.es6draft.runtime.internal.Messages;
import com.github.anba.es6draft.runtime.internal.RuntimeContext;
import com.github.anba.es6draft.runtime.internal.ScriptLoader;
import com.github.anba.es6draft.runtime.internal.TaskSource;
import com.github.anba.es6draft.runtime.internal.UnhandledRejectionException;
import com.github.anba.es6draft.runtime.modules.ModuleLoader;
/**
* <h1>8 Executable Code and Execution Contexts</h1>
* <ul>
* <li>8.2 Code Realms
* <li>8.4 Tasks and Task Queues
* </ul>
*/
public final class World {
private final RuntimeContext context;
private final ModuleLoader moduleLoader;
private final ScriptLoader scriptLoader;
private final Messages messages;
private final GlobalSymbolRegistry symbolRegistry = new GlobalSymbolRegistry();
private final ArrayDeque<Task> scriptTasks = new ArrayDeque<>();
private final ArrayDeque<Task> promiseTasks = new ArrayDeque<>();
private final ArrayDeque<Object> unhandledRejections = new ArrayDeque<>();
private static final TaskSource EMPTY_TASK_SOURCE = new TaskSource() {
@Override
public Task nextTask() {
return null;
}
@Override
public Task awaitTask() {
throw new IllegalStateException();
}
};
/**
* Creates a new {@link World} object.
*
* @param context
* the runtime context
*/
public World(RuntimeContext context) {
this.context = context;
this.scriptLoader = new ScriptLoader(context);
this.moduleLoader = context.getModuleLoader().apply(context, scriptLoader);
this.messages = Messages.create(context.getLocale());
}
/**
* Returns the runtime context.
*
* @return the runtime context
*/
public RuntimeContext getContext() {
return context;
}
/**
* Returns the module loader.
*
* @return the module loader
*/
public ModuleLoader getModuleLoader() {
return moduleLoader;
}
/**
* Returns the script loader.
*
* @return the script loader
*/
public ScriptLoader getScriptLoader() {
return scriptLoader;
}
/**
* Checks whether there are any pending tasks.
*
* @return {@code true} if there are any pending tasks
*/
public boolean hasPendingTasks() {
return !(scriptTasks.isEmpty() && promiseTasks.isEmpty());
}
/**
* 8.4.1 EnqueueTask ( queueName, task, arguments)
* <p>
* Enqueues {@code task} to the queue of pending script-tasks.
*
* @param task
* the new script task
*/
public void enqueueScriptTask(Task task) {
scriptTasks.offer(task);
}
/**
* 8.4.1 EnqueueTask ( queueName, task, arguments)
* <p>
* Enqueues {@code task} to the queue of pending promise-tasks.
*
* @param task
* the new promise task
*/
public void enqueuePromiseTask(Task task) {
promiseTasks.offer(task);
}
/**
* Enqueue a promise rejection reason to the global rejection list.
*
* @param reason
* the promise rejection reason
*/
public void enqueueUnhandledPromiseRejection(Object reason) {
unhandledRejections.offer(reason);
}
/**
* Executes the queue of pending tasks.
*/
public void runEventLoop() {
try {
runEventLoop(EMPTY_TASK_SOURCE);
} catch (InterruptedException e) {
// The empty task source never throws InterruptedException.
throw new AssertionError(e);
}
}
/**
* Executes the queue of pending tasks.
*
* @param taskSource
* the task source
* @throws InterruptedException
* if interrupted while waiting
*/
public void runEventLoop(TaskSource taskSource) throws InterruptedException {
ArrayDeque<Task> scriptTasks = this.scriptTasks, promiseTasks = this.promiseTasks;
ArrayDeque<Object> unhandledRejections = this.unhandledRejections;
for (;;) {
while (!(scriptTasks.isEmpty() && promiseTasks.isEmpty())) {
executeTasks(scriptTasks);
executeTasks(promiseTasks);
}
if (!unhandledRejections.isEmpty()) {
throw new UnhandledRejectionException(unhandledRejections.poll());
}
Task task = taskSource.nextTask();
if (task == null) {
break;
}
enqueueScriptTask(task);
}
}
/**
* Executes the queue of pending tasks.
*
* @param tasks
* the tasks to be executed
*/
private void executeTasks(ArrayDeque<Task> tasks) {
// Execute all pending tasks until the queue is empty
for (Task task; (task = tasks.poll()) != null;) {
task.execute();
}
}
/**
* Returns the localised message for {@code key}.
*
* @param key
* the message key
* @return the localised message
*/
public String message(Messages.Key key) {
return messages.getMessage(key);
}
/**
* Returns the localised message for {@code key}.
*
* @param key
* the message key
* @param args
* the message arguments
* @return the localised message
*/
public String message(Messages.Key key, String... args) {
return messages.getMessage(key, args);
}
/**
* Tests whether the requested compatibility option is enabled for this instance.
*
* @param option
* the compatibility option
* @return {@code true} if the compatibility option is enabled
*/
public boolean isEnabled(CompatibilityOption option) {
return context.getOptions().contains(option);
}
/**
* Returns the global symbol registry.
*
* @return the global symbol registry
*/
public GlobalSymbolRegistry getSymbolRegistry() {
return symbolRegistry;
}
/**
* Creates a new {@link Realm} object.
*
* @return the new realm object
*/
public Realm newRealm() {
return new Realm(this);
}
/**
* Creates a new, initialized {@link Realm} object.
*
* @return the new realm object
* @throws IOException
* if there was any I/O error
* @throws URISyntaxException
* the URL is not a valid URI
* @throws ParserException
* if the source contains any syntax errors
* @throws CompilationException
* if the parsed source could not be compiled
*/
public Realm newInitializedRealm() throws ParserException, CompilationException, IOException, URISyntaxException {
Realm realm = new Realm(this);
InitializeHostDefinedRealm(realm);
return realm;
}
}