/**
* 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.repl.global;
import static com.github.anba.es6draft.runtime.internal.Errors.newTypeError;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.locks.ReentrantLock;
import com.github.anba.es6draft.Script;
import com.github.anba.es6draft.repl.loader.NodeModuleLoader;
import com.github.anba.es6draft.runtime.ExecutionContext;
import com.github.anba.es6draft.runtime.Realm;
import com.github.anba.es6draft.runtime.World;
import com.github.anba.es6draft.runtime.internal.Messages;
import com.github.anba.es6draft.runtime.internal.Properties.Function;
import com.github.anba.es6draft.runtime.internal.RuntimeContext;
import com.github.anba.es6draft.runtime.internal.Source;
import com.github.anba.es6draft.runtime.modules.MalformedNameException;
import com.github.anba.es6draft.runtime.modules.ModuleLoader;
import com.github.anba.es6draft.runtime.modules.ResolutionException;
import com.github.anba.es6draft.runtime.objects.atomics.SharedArrayBufferObject;
import com.github.anba.es6draft.runtime.types.Intrinsics;
import com.github.anba.es6draft.runtime.types.Type;
/**
*
*/
public final class AtomicsTestFunctions {
private final ReentrantLock sharedLock = new ReentrantLock();
private ByteBuffer sharedBufferData;
private long sharedBufferLength;
/**
* shell-function: {@code getSharedArrayBuffer()}
*
* @param cx
* the execution context
* @return the shared array buffer
*/
@Function(name = "getSharedArrayBuffer", arity = 0)
public SharedArrayBufferObject getSharedArrayBuffer(ExecutionContext cx) {
sharedLock.lock();
try {
if (sharedBufferData == null) {
return null;
}
return new SharedArrayBufferObject(cx.getRealm(), sharedBufferData, sharedBufferLength,
cx.getIntrinsic(Intrinsics.SharedArrayBufferPrototype));
} finally {
sharedLock.unlock();
}
}
/**
* shell-function: {@code setSharedArrayBuffer(buffer)}
*
* @param cx
* the execution context
* @param buffer
* the shared array buffer
*/
@Function(name = "setSharedArrayBuffer", arity = 1)
public void setSharedArrayBuffer(ExecutionContext cx, Object buffer) {
sharedLock.lock();
try {
if (Type.isUndefinedOrNull(buffer)) {
sharedBufferData = null;
sharedBufferLength = 0;
} else if (buffer instanceof SharedArrayBufferObject) {
SharedArrayBufferObject sharedBuffer = (SharedArrayBufferObject) buffer;
sharedBufferData = sharedBuffer.getData();
sharedBufferLength = sharedBuffer.getByteLength();
} else {
throw newTypeError(cx, Messages.Key.IncompatibleObject);
}
} finally {
sharedLock.unlock();
}
}
/**
* shell-function: {@code evalInWorker(sourceString)}
*
* @param cx
* the execution context
* @param caller
* the caller execution context
* @param sourceString
* the script source code
* @return {@code true} if a new script worker was started, otherwise returns {@code false}
*/
@Function(name = "evalInWorker", arity = 1)
public boolean evalInWorker(ExecutionContext cx, ExecutionContext caller, String sourceString) {
Source baseSource = Objects.requireNonNull(cx.getRealm().sourceInfo(caller));
try {
// TODO: Initialize extensions (console.jsm, window timers?).
CompletableFuture.supplyAsync(() -> {
// Set 'executor' to null so it doesn't get shared with the current runtime context.
/* @formatter:off */
RuntimeContext context = new RuntimeContext.Builder(cx.getRuntimeContext())
.setExecutor(null)
.build();
/* @formatter:on */
World world = new World(context);
Realm realm;
try {
realm = world.newInitializedRealm();
} catch (IOException | URISyntaxException e) {
throw new CompletionException(e);
}
// Bind test functions to this instance.
realm.createGlobalProperties(this, AtomicsTestFunctions.class);
// TODO: Add proper abstraction.
ModuleLoader moduleLoader = world.getModuleLoader();
if (moduleLoader instanceof NodeModuleLoader) {
try {
((NodeModuleLoader) moduleLoader).initialize(realm);
} catch (IOException | URISyntaxException | MalformedNameException | ResolutionException e) {
throw new CompletionException(e);
}
}
// Evaluate the script source code and then run pending jobs.
Source source = new Source(baseSource, "evalInWorker-script", 1);
Script script = realm.getScriptLoader().script(source, sourceString);
Object result = script.evaluate(realm);
world.runEventLoop();
return result;
} , cx.getRuntimeContext().getWorkerExecutor()).whenComplete((r, e) -> {
if (e instanceof CompletionException) {
Throwable cause = ((CompletionException) e).getCause();
cx.getRuntimeContext().getWorkerErrorReporter().accept(cx, (cause != null ? cause : e));
} else if (e != null) {
cx.getRuntimeContext().getWorkerErrorReporter().accept(cx, e);
}
});
return true;
} catch (RejectedExecutionException e) {
return false;
}
}
}