mirror of
https://github.com/Samsung/escargot.git
synced 2026-06-22 10:01:50 +00:00
Compare commits
2 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b379fd475c | ||
|
|
c65aed1a97 |
7 changed files with 202 additions and 198 deletions
|
|
@ -113,12 +113,13 @@ public:
|
|||
PlatformBridge(PlatformRef* p)
|
||||
: m_platform(p)
|
||||
{
|
||||
m_platform->ref();
|
||||
}
|
||||
|
||||
virtual ~PlatformBridge()
|
||||
{
|
||||
// delete PlatformRef, so we don't care about PlatformRef's lifetime
|
||||
delete m_platform;
|
||||
// deref PlatformRef, so we don't care about PlatformRef's lifetime
|
||||
m_platform->deref();
|
||||
}
|
||||
|
||||
virtual void* onMallocArrayBufferObjectDataBuffer(size_t sizeInByte) override
|
||||
|
|
@ -247,12 +248,24 @@ void* PlatformRef::threadLocalCustomData()
|
|||
return ThreadLocal::customData();
|
||||
}
|
||||
|
||||
void PlatformRef::ref()
|
||||
{
|
||||
m_refCount++;
|
||||
}
|
||||
|
||||
void PlatformRef::deref()
|
||||
{
|
||||
if (m_refCount.fetch_sub(1) == 1) {
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
thread_local bool g_globalsInited;
|
||||
void Globals::initialize(PlatformRef* platform)
|
||||
{
|
||||
// initialize global value or context including thread-local variables
|
||||
// this function should be invoked once at the start of the program
|
||||
// argument `platform` will be deleted automatically when Globals::finalize called
|
||||
// this function should be invoked once at the start of the thread
|
||||
// argument `platform` will be deleted automatically when Globals::finalize is called and refCount is zero
|
||||
RELEASE_ASSERT(!g_globalsInited);
|
||||
Global::initialize(new PlatformBridge(platform));
|
||||
ThreadLocal::initialize();
|
||||
|
|
@ -262,34 +275,16 @@ void Globals::initialize(PlatformRef* platform)
|
|||
void Globals::finalize()
|
||||
{
|
||||
// finalize global value or context including thread-local variables
|
||||
// this function should be invoked once at the end of the program
|
||||
// this function should be invoked once at the end of the thread
|
||||
RELEASE_ASSERT(!!g_globalsInited);
|
||||
ThreadLocal::finalize();
|
||||
|
||||
// Global::finalize should be called at the end of program
|
||||
// Global::finalize should be called at the end of thread
|
||||
// because it holds Platform which could be used in other Object's finalizer
|
||||
Global::finalize();
|
||||
g_globalsInited = false;
|
||||
}
|
||||
|
||||
void Globals::initializeThread()
|
||||
{
|
||||
// initialize thread-local variables
|
||||
// this function should be invoked at the start of sub-thread
|
||||
RELEASE_ASSERT(!g_globalsInited);
|
||||
ThreadLocal::initialize();
|
||||
g_globalsInited = true;
|
||||
}
|
||||
|
||||
void Globals::finalizeThread()
|
||||
{
|
||||
// finalize thread-local variables
|
||||
// this function should be invoked once at the end of sub-thread
|
||||
RELEASE_ASSERT(!!g_globalsInited);
|
||||
ThreadLocal::finalize();
|
||||
g_globalsInited = false;
|
||||
}
|
||||
|
||||
bool Globals::supportsThreading()
|
||||
{
|
||||
#if defined(ENABLE_THREADING)
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <sstream>
|
||||
#include <atomic>
|
||||
|
||||
#if !defined(NDEBUG) && defined(__GLIBCXX__) && !defined(_GLIBCXX_DEBUG)
|
||||
#pragma message("You should define `_GLIBCXX_DEBUG` in {debug mode + libstdc++} because Escargot uses it")
|
||||
|
|
@ -126,16 +127,11 @@ ESCARGOT_REF_LIST(DECLARE_REF_CLASS);
|
|||
|
||||
class ESCARGOT_EXPORT Globals {
|
||||
public:
|
||||
// Escargot has thread-independent Globals.
|
||||
// Users should call initialize, finalize once in the main program
|
||||
// Escargot has thread-dependent Globals.
|
||||
// Users should call initialize, finalize once in each thread
|
||||
static void initialize(PlatformRef* platform);
|
||||
static void finalize();
|
||||
|
||||
// Globals also used for thread initialization
|
||||
// Users need to call initializeThread, finalizeThread function for each thread
|
||||
static void initializeThread();
|
||||
static void finalizeThread();
|
||||
|
||||
static bool supportsThreading();
|
||||
|
||||
static const char* version();
|
||||
|
|
@ -2157,6 +2153,18 @@ public:
|
|||
}
|
||||
|
||||
void* threadLocalCustomData();
|
||||
|
||||
protected:
|
||||
friend class PlatformBridge;
|
||||
PlatformRef()
|
||||
: m_refCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
void ref();
|
||||
void deref();
|
||||
|
||||
std::atomic_size_t m_refCount;
|
||||
};
|
||||
|
||||
class ESCARGOT_EXPORT WASMOperationsRef {
|
||||
|
|
|
|||
|
|
@ -28,8 +28,11 @@
|
|||
|
||||
namespace Escargot {
|
||||
|
||||
bool Global::inited;
|
||||
Platform* Global::g_platform;
|
||||
#if defined(ENABLE_THREADING)
|
||||
std::atomic_size_t g_initCount;
|
||||
#endif
|
||||
MAY_THREAD_LOCAL bool Global::inited;
|
||||
MAY_THREAD_LOCAL Platform* Global::g_platform;
|
||||
#if defined(ENABLE_ATOMICS_GLOBAL_LOCK)
|
||||
SpinLock Global::g_atomicsLock;
|
||||
#endif
|
||||
|
|
@ -40,50 +43,51 @@ std::vector<Global::Waiter*> Global::g_waiter;
|
|||
|
||||
void Global::initialize(Platform* platform)
|
||||
{
|
||||
// initialize should be invoked only once in the program
|
||||
static bool called_once = true;
|
||||
RELEASE_ASSERT(called_once && !inited);
|
||||
RELEASE_ASSERT(!inited);
|
||||
|
||||
ASSERT(!g_platform);
|
||||
g_platform = platform;
|
||||
|
||||
// initialize PointerValue tag values
|
||||
// tag values should be initialized once and not changed
|
||||
PointerValue::g_objectTag = Object().getVTag();
|
||||
PointerValue::g_prototypeObjectTag = PrototypeObject().getVTag();
|
||||
PointerValue::g_arrayObjectTag = ArrayObject().getVTag();
|
||||
PointerValue::g_arrayPrototypeObjectTag = ArrayPrototypeObject().getVTag();
|
||||
PointerValue::g_scriptFunctionObjectTag = ScriptFunctionObject().getVTag();
|
||||
PointerValue::g_objectRareDataTag = ObjectRareData(nullptr).getVTag();
|
||||
// tag values for ScriptSimpleFunctionObject
|
||||
#if defined(ENABLE_THREADING)
|
||||
if (g_initCount.fetch_add(1) == 0) {
|
||||
#endif
|
||||
// initialize PointerValue tag values
|
||||
// tag values should be initialized once and not changed
|
||||
PointerValue::g_objectTag = Object().getVTag();
|
||||
PointerValue::g_prototypeObjectTag = PrototypeObject().getVTag();
|
||||
PointerValue::g_arrayObjectTag = ArrayObject().getVTag();
|
||||
PointerValue::g_arrayPrototypeObjectTag = ArrayPrototypeObject().getVTag();
|
||||
PointerValue::g_scriptFunctionObjectTag = ScriptFunctionObject().getVTag();
|
||||
PointerValue::g_objectRareDataTag = ObjectRareData(nullptr).getVTag();
|
||||
// tag values for ScriptSimpleFunctionObject
|
||||
#define INIT_SCRIPTSIMPLEFUNCTION_TAGS(STRICT, CLEAR, isStrict, isClear, SIZE) \
|
||||
PointerValue::g_scriptSimpleFunctionObject##STRICT##CLEAR##SIZE##Tag = ScriptSimpleFunctionObject<isStrict, isClear, SIZE>().getVTag();
|
||||
|
||||
DECLARE_SCRIPTSIMPLEFUNCTION_LIST(INIT_SCRIPTSIMPLEFUNCTION_TAGS);
|
||||
DECLARE_SCRIPTSIMPLEFUNCTION_LIST(INIT_SCRIPTSIMPLEFUNCTION_TAGS);
|
||||
#undef INIT_SCRIPTSIMPLEFUNCTION_TAGS
|
||||
#if defined(ENABLE_THREADING)
|
||||
}
|
||||
#endif
|
||||
|
||||
called_once = false;
|
||||
inited = true;
|
||||
}
|
||||
|
||||
void Global::finalize()
|
||||
{
|
||||
// finalize should be invoked only once in the program
|
||||
static bool called_once = true;
|
||||
RELEASE_ASSERT(called_once && inited);
|
||||
|
||||
RELEASE_ASSERT(inited);
|
||||
|
||||
#if defined(ENABLE_THREADING)
|
||||
for (size_t i = 0; i < g_waiter.size(); i++) {
|
||||
delete g_waiter[i];
|
||||
if (g_initCount.fetch_sub(1) == 1) {
|
||||
for (size_t i = 0; i < g_waiter.size(); i++) {
|
||||
delete g_waiter[i];
|
||||
}
|
||||
std::vector<Waiter*>().swap(g_waiter);
|
||||
}
|
||||
std::vector<Waiter*>().swap(g_waiter);
|
||||
#endif
|
||||
|
||||
delete g_platform;
|
||||
g_platform = nullptr;
|
||||
|
||||
called_once = false;
|
||||
inited = false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,8 +38,8 @@ class Platform;
|
|||
|
||||
// Global is a global interface used by all threads
|
||||
class Global {
|
||||
static bool inited;
|
||||
static Platform* g_platform;
|
||||
static MAY_THREAD_LOCAL bool inited;
|
||||
static MAY_THREAD_LOCAL Platform* g_platform;
|
||||
#if defined(ENABLE_ATOMICS_GLOBAL_LOCK)
|
||||
static SpinLock g_atomicsLock;
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -59,6 +59,8 @@ public:
|
|||
virtual void customInfoLogger(const char* format, va_list arg) = 0;
|
||||
virtual void customErrorLogger(const char* format, va_list arg) = 0;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
};
|
||||
} // namespace Escargot
|
||||
|
||||
|
|
|
|||
|
|
@ -125,9 +125,7 @@ static void genericGCEventListener(GC_EventType evtType)
|
|||
|
||||
void ThreadLocal::initialize()
|
||||
{
|
||||
// initialize should be invoked only once in each thread
|
||||
static MAY_THREAD_LOCAL bool called_once = true;
|
||||
RELEASE_ASSERT(called_once && !inited);
|
||||
RELEASE_ASSERT(!inited);
|
||||
|
||||
// Heap is initialized for each thread
|
||||
Heap::initialize();
|
||||
|
|
@ -162,15 +160,12 @@ void ThreadLocal::initialize()
|
|||
// g_customData
|
||||
g_customData = Global::platform()->allocateThreadLocalCustomData();
|
||||
|
||||
called_once = false;
|
||||
inited = true;
|
||||
}
|
||||
|
||||
void ThreadLocal::finalize()
|
||||
{
|
||||
// finalize should be invoked only once in each thread
|
||||
static MAY_THREAD_LOCAL bool called_once = true;
|
||||
RELEASE_ASSERT(called_once && inited);
|
||||
RELEASE_ASSERT(inited);
|
||||
|
||||
// g_customData
|
||||
Global::platform()->deallocateThreadLocalCustomData();
|
||||
|
|
@ -208,7 +203,6 @@ void ThreadLocal::finalize()
|
|||
delete g_bumpPointerAllocator;
|
||||
g_bumpPointerAllocator = nullptr;
|
||||
|
||||
called_once = false;
|
||||
inited = false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -271,278 +271,6 @@ static ValueRef* builtinGc(ExecutionStateRef* state, ValueRef* thisValue, size_t
|
|||
return ValueRef::createUndefined();
|
||||
}
|
||||
|
||||
PersistentRefHolder<ContextRef> createEscargotContext(VMInstanceRef* instance, bool isMainThread = true);
|
||||
|
||||
#if defined(ESCARGOT_ENABLE_TEST)
|
||||
|
||||
static bool evalScript(ContextRef* context, StringRef* source, StringRef* srcName, bool shouldPrintScriptResult, bool isModule);
|
||||
|
||||
static ValueRef* builtinUneval(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
if (argc) {
|
||||
if (argv[0]->isSymbol()) {
|
||||
return argv[0]->asSymbol()->symbolDescriptiveString();
|
||||
}
|
||||
return argv[0]->toString(state);
|
||||
}
|
||||
return StringRef::emptyString();
|
||||
}
|
||||
|
||||
static ValueRef* builtinDrainJobQueue(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
ContextRef* context = state->context();
|
||||
while (context->vmInstance()->hasPendingJob()) {
|
||||
auto jobResult = context->vmInstance()->executePendingJob();
|
||||
if (jobResult.error) {
|
||||
return ValueRef::create(false);
|
||||
}
|
||||
}
|
||||
return ValueRef::create(true);
|
||||
}
|
||||
|
||||
static ValueRef* builtinAddPromiseReactions(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
if (argc >= 3) {
|
||||
PromiseObjectRef* promise = argv[0]->toObject(state)->asPromiseObject();
|
||||
promise->then(state, argv[1], argv[2]);
|
||||
} else {
|
||||
state->throwException(TypeErrorObjectRef::create(state, StringRef::emptyString()));
|
||||
}
|
||||
return ValueRef::createUndefined();
|
||||
}
|
||||
|
||||
static ValueRef* builtinCreateNewGlobalObject(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
return ContextRef::create(state->context()->vmInstance())->globalObject();
|
||||
}
|
||||
|
||||
static ValueRef* builtin262CreateRealm(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
auto newContext = createEscargotContext(state->context()->vmInstance(), false);
|
||||
return newContext->globalObject()->get(state, StringRef::createFromASCII("$262"));
|
||||
}
|
||||
|
||||
static ValueRef* builtin262DetachArrayBuffer(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
if (argv[0]->isArrayBufferObject()) {
|
||||
argv[0]->asArrayBufferObject()->detachArrayBuffer();
|
||||
}
|
||||
|
||||
return ValueRef::createUndefined();
|
||||
}
|
||||
|
||||
static ValueRef* builtin262EvalScript(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
StringRef* src = argv[0]->toString(state);
|
||||
auto script = state->context()->scriptParser()->initializeScript(src, StringRef::createFromASCII("$262.evalScript input"), false).fetchScriptThrowsExceptionIfParseError(state);
|
||||
return script->execute(state);
|
||||
}
|
||||
|
||||
static ValueRef* builtin262IsHTMLDDA(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
return ValueRef::createNull();
|
||||
}
|
||||
|
||||
struct WorkerThreadData {
|
||||
std::string message;
|
||||
bool running;
|
||||
volatile bool ended;
|
||||
|
||||
WorkerThreadData()
|
||||
: running(true)
|
||||
, ended(false)
|
||||
{
|
||||
}
|
||||
};
|
||||
std::mutex workerMutex;
|
||||
std::vector<std::pair<std::thread, WorkerThreadData>> workerThreads;
|
||||
std::vector<std::string> messagesFromWorkers;
|
||||
|
||||
static ValueRef* builtin262AgentStart(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
std::string script = argv[0]->toString(state)->toStdUTF8String();
|
||||
|
||||
std::thread worker([](std::string script) {
|
||||
Globals::initializeThread();
|
||||
|
||||
Memory::setGCFrequency(24);
|
||||
|
||||
PersistentRefHolder<VMInstanceRef> instance = VMInstanceRef::create();
|
||||
PersistentRefHolder<ContextRef> context = createEscargotContext(instance.get(), false);
|
||||
|
||||
evalScript(context.get(), StringRef::createFromUTF8(script.data(), script.size()),
|
||||
StringRef::createFromASCII("from main thread"), false, false);
|
||||
|
||||
while (true) {
|
||||
{
|
||||
bool running = false;
|
||||
std::lock_guard<std::mutex> guard(workerMutex);
|
||||
for (size_t i = 0; i < workerThreads.size(); i++) {
|
||||
if (workerThreads[i].first.get_id() == std::this_thread::get_id()) {
|
||||
running = workerThreads[i].second.running;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!running) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// readmessage if exists
|
||||
std::string message;
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(workerMutex);
|
||||
for (size_t i = 0; i < workerThreads.size(); i++) {
|
||||
if (workerThreads[i].first.get_id() == std::this_thread::get_id()) {
|
||||
message = std::move(workerThreads[i].second.message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (message.length()) {
|
||||
std::istringstream istream(message);
|
||||
ValueRef* val1 = SerializerRef::deserializeFrom(context.get(), istream);
|
||||
ValueRef* val2 = SerializerRef::deserializeFrom(context.get(), istream);
|
||||
|
||||
ValueRef* callback = (ValueRef*)context.get()->globalObject()->extraData();
|
||||
if (callback) {
|
||||
Evaluator::execute(context.get(), [](ExecutionStateRef* state, ValueRef* callback, ValueRef* v1, ValueRef* v2) -> ValueRef* {
|
||||
ValueRef* argv[2] = { v1, v2 };
|
||||
return callback->call(state, ValueRef::createUndefined(), 2, argv);
|
||||
},
|
||||
callback, val1, val2);
|
||||
}
|
||||
}
|
||||
|
||||
usleep(10 * 1000);
|
||||
}
|
||||
|
||||
context.release();
|
||||
instance.release();
|
||||
|
||||
Globals::finalizeThread();
|
||||
|
||||
std::lock_guard<std::mutex> guard(workerMutex);
|
||||
for (size_t i = 0; i < workerThreads.size(); i++) {
|
||||
if (workerThreads[i].first.get_id() == std::this_thread::get_id()) {
|
||||
workerThreads[i].second.ended = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
script);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(workerMutex);
|
||||
workerThreads.push_back(std::make_pair(std::move(worker), WorkerThreadData()));
|
||||
}
|
||||
|
||||
return ValueRef::createUndefined();
|
||||
}
|
||||
|
||||
static ValueRef* builtin262AgentBroadcast(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
std::ostringstream ostream;
|
||||
if (argc > 0) {
|
||||
SerializerRef::serializeInto(argv[0], ostream);
|
||||
} else {
|
||||
SerializerRef::serializeInto(ValueRef::createUndefined(), ostream);
|
||||
}
|
||||
if (argc > 1) {
|
||||
SerializerRef::serializeInto(argv[1], ostream);
|
||||
} else {
|
||||
SerializerRef::serializeInto(ValueRef::createUndefined(), ostream);
|
||||
}
|
||||
|
||||
|
||||
std::string message(ostream.str());
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(workerMutex);
|
||||
for (size_t i = 0; i < workerThreads.size(); i++) {
|
||||
workerThreads[i].second.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
bool thereIsNoMessage = true;
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(workerMutex);
|
||||
for (size_t i = 0; i < workerThreads.size(); i++) {
|
||||
if (workerThreads[i].second.message.size()) {
|
||||
thereIsNoMessage = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (thereIsNoMessage) {
|
||||
break;
|
||||
}
|
||||
|
||||
usleep(10 * 1000); // sleep 10ms
|
||||
}
|
||||
|
||||
|
||||
return ValueRef::createUndefined();
|
||||
}
|
||||
|
||||
static ValueRef* builtin262AgentReport(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
std::string message;
|
||||
if (argc > 0) {
|
||||
message = argv[0]->toString(state)->toStdUTF8String();
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(workerMutex);
|
||||
messagesFromWorkers.push_back(message);
|
||||
return ValueRef::createUndefined();
|
||||
}
|
||||
|
||||
static ValueRef* builtin262AgentLeaving(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(workerMutex);
|
||||
for (size_t i = 0; i < workerThreads.size(); i++) {
|
||||
if (workerThreads[i].first.get_id() == std::this_thread::get_id()) {
|
||||
workerThreads[i].second.running = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ValueRef::createUndefined();
|
||||
}
|
||||
|
||||
static ValueRef* builtin262AgentReceiveBroadcast(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
state->context()->globalObject()->setExtraData(argv[0]);
|
||||
return ValueRef::createUndefined();
|
||||
}
|
||||
|
||||
static ValueRef* builtin262AgentGetReport(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(workerMutex);
|
||||
if (messagesFromWorkers.size()) {
|
||||
std::string message = messagesFromWorkers.front();
|
||||
messagesFromWorkers.erase(messagesFromWorkers.begin());
|
||||
return StringRef::createFromUTF8(message.data(), message.size());
|
||||
} else {
|
||||
return ValueRef::createNull();
|
||||
}
|
||||
}
|
||||
|
||||
static ValueRef* builtin262AgentMonotonicNow(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return ValueRef::create((uint64_t)tv.tv_sec * 1000UL + tv.tv_usec / 1000UL);
|
||||
}
|
||||
|
||||
static ValueRef* builtin262AgentSleep(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
double m = argv[0]->toNumber(state);
|
||||
usleep(m * 1000.0);
|
||||
return ValueRef::createUndefined();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
class ShellPlatform : public PlatformRef {
|
||||
public:
|
||||
bool m_canBlock;
|
||||
|
|
@ -671,6 +399,279 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
ShellPlatform* g_platform;
|
||||
PersistentRefHolder<ContextRef> createEscargotContext(VMInstanceRef* instance, bool isMainThread = true);
|
||||
|
||||
#if defined(ESCARGOT_ENABLE_TEST)
|
||||
|
||||
static bool evalScript(ContextRef* context, StringRef* source, StringRef* srcName, bool shouldPrintScriptResult, bool isModule);
|
||||
|
||||
static ValueRef* builtinUneval(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
if (argc) {
|
||||
if (argv[0]->isSymbol()) {
|
||||
return argv[0]->asSymbol()->symbolDescriptiveString();
|
||||
}
|
||||
return argv[0]->toString(state);
|
||||
}
|
||||
return StringRef::emptyString();
|
||||
}
|
||||
|
||||
static ValueRef* builtinDrainJobQueue(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
ContextRef* context = state->context();
|
||||
while (context->vmInstance()->hasPendingJob()) {
|
||||
auto jobResult = context->vmInstance()->executePendingJob();
|
||||
if (jobResult.error) {
|
||||
return ValueRef::create(false);
|
||||
}
|
||||
}
|
||||
return ValueRef::create(true);
|
||||
}
|
||||
|
||||
static ValueRef* builtinAddPromiseReactions(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
if (argc >= 3) {
|
||||
PromiseObjectRef* promise = argv[0]->toObject(state)->asPromiseObject();
|
||||
promise->then(state, argv[1], argv[2]);
|
||||
} else {
|
||||
state->throwException(TypeErrorObjectRef::create(state, StringRef::emptyString()));
|
||||
}
|
||||
return ValueRef::createUndefined();
|
||||
}
|
||||
|
||||
static ValueRef* builtinCreateNewGlobalObject(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
return ContextRef::create(state->context()->vmInstance())->globalObject();
|
||||
}
|
||||
|
||||
static ValueRef* builtin262CreateRealm(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
auto newContext = createEscargotContext(state->context()->vmInstance(), false);
|
||||
return newContext->globalObject()->get(state, StringRef::createFromASCII("$262"));
|
||||
}
|
||||
|
||||
static ValueRef* builtin262DetachArrayBuffer(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
if (argv[0]->isArrayBufferObject()) {
|
||||
argv[0]->asArrayBufferObject()->detachArrayBuffer();
|
||||
}
|
||||
|
||||
return ValueRef::createUndefined();
|
||||
}
|
||||
|
||||
static ValueRef* builtin262EvalScript(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
StringRef* src = argv[0]->toString(state);
|
||||
auto script = state->context()->scriptParser()->initializeScript(src, StringRef::createFromASCII("$262.evalScript input"), false).fetchScriptThrowsExceptionIfParseError(state);
|
||||
return script->execute(state);
|
||||
}
|
||||
|
||||
static ValueRef* builtin262IsHTMLDDA(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
return ValueRef::createNull();
|
||||
}
|
||||
|
||||
struct WorkerThreadData {
|
||||
std::string message;
|
||||
bool running;
|
||||
volatile bool ended;
|
||||
|
||||
WorkerThreadData()
|
||||
: running(true)
|
||||
, ended(false)
|
||||
{
|
||||
}
|
||||
};
|
||||
std::mutex workerMutex;
|
||||
std::vector<std::pair<std::thread, WorkerThreadData>> workerThreads;
|
||||
std::vector<std::string> messagesFromWorkers;
|
||||
|
||||
static ValueRef* builtin262AgentStart(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
std::string script = argv[0]->toString(state)->toStdUTF8String();
|
||||
|
||||
std::thread worker([](std::string script) {
|
||||
Globals::initialize(g_platform);
|
||||
|
||||
Memory::setGCFrequency(24);
|
||||
|
||||
PersistentRefHolder<VMInstanceRef> instance = VMInstanceRef::create();
|
||||
PersistentRefHolder<ContextRef> context = createEscargotContext(instance.get(), false);
|
||||
|
||||
evalScript(context.get(), StringRef::createFromUTF8(script.data(), script.size()),
|
||||
StringRef::createFromASCII("from main thread"), false, false);
|
||||
|
||||
while (true) {
|
||||
{
|
||||
bool running = false;
|
||||
std::lock_guard<std::mutex> guard(workerMutex);
|
||||
for (size_t i = 0; i < workerThreads.size(); i++) {
|
||||
if (workerThreads[i].first.get_id() == std::this_thread::get_id()) {
|
||||
running = workerThreads[i].second.running;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!running) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// readmessage if exists
|
||||
std::string message;
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(workerMutex);
|
||||
for (size_t i = 0; i < workerThreads.size(); i++) {
|
||||
if (workerThreads[i].first.get_id() == std::this_thread::get_id()) {
|
||||
message = std::move(workerThreads[i].second.message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (message.length()) {
|
||||
std::istringstream istream(message);
|
||||
ValueRef* val1 = SerializerRef::deserializeFrom(context.get(), istream);
|
||||
ValueRef* val2 = SerializerRef::deserializeFrom(context.get(), istream);
|
||||
|
||||
ValueRef* callback = (ValueRef*)context.get()->globalObject()->extraData();
|
||||
if (callback) {
|
||||
Evaluator::execute(context.get(), [](ExecutionStateRef* state, ValueRef* callback, ValueRef* v1, ValueRef* v2) -> ValueRef* {
|
||||
ValueRef* argv[2] = { v1, v2 };
|
||||
return callback->call(state, ValueRef::createUndefined(), 2, argv);
|
||||
},
|
||||
callback, val1, val2);
|
||||
}
|
||||
}
|
||||
|
||||
usleep(10 * 1000);
|
||||
}
|
||||
|
||||
context.release();
|
||||
instance.release();
|
||||
|
||||
Globals::finalize();
|
||||
|
||||
std::lock_guard<std::mutex> guard(workerMutex);
|
||||
for (size_t i = 0; i < workerThreads.size(); i++) {
|
||||
if (workerThreads[i].first.get_id() == std::this_thread::get_id()) {
|
||||
workerThreads[i].second.ended = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
script);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(workerMutex);
|
||||
workerThreads.push_back(std::make_pair(std::move(worker), WorkerThreadData()));
|
||||
}
|
||||
|
||||
return ValueRef::createUndefined();
|
||||
}
|
||||
|
||||
static ValueRef* builtin262AgentBroadcast(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
std::ostringstream ostream;
|
||||
if (argc > 0) {
|
||||
SerializerRef::serializeInto(argv[0], ostream);
|
||||
} else {
|
||||
SerializerRef::serializeInto(ValueRef::createUndefined(), ostream);
|
||||
}
|
||||
if (argc > 1) {
|
||||
SerializerRef::serializeInto(argv[1], ostream);
|
||||
} else {
|
||||
SerializerRef::serializeInto(ValueRef::createUndefined(), ostream);
|
||||
}
|
||||
|
||||
|
||||
std::string message(ostream.str());
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(workerMutex);
|
||||
for (size_t i = 0; i < workerThreads.size(); i++) {
|
||||
workerThreads[i].second.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
bool thereIsNoMessage = true;
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(workerMutex);
|
||||
for (size_t i = 0; i < workerThreads.size(); i++) {
|
||||
if (workerThreads[i].second.message.size()) {
|
||||
thereIsNoMessage = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (thereIsNoMessage) {
|
||||
break;
|
||||
}
|
||||
|
||||
usleep(10 * 1000); // sleep 10ms
|
||||
}
|
||||
|
||||
|
||||
return ValueRef::createUndefined();
|
||||
}
|
||||
|
||||
static ValueRef* builtin262AgentReport(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
std::string message;
|
||||
if (argc > 0) {
|
||||
message = argv[0]->toString(state)->toStdUTF8String();
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(workerMutex);
|
||||
messagesFromWorkers.push_back(message);
|
||||
return ValueRef::createUndefined();
|
||||
}
|
||||
|
||||
static ValueRef* builtin262AgentLeaving(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(workerMutex);
|
||||
for (size_t i = 0; i < workerThreads.size(); i++) {
|
||||
if (workerThreads[i].first.get_id() == std::this_thread::get_id()) {
|
||||
workerThreads[i].second.running = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ValueRef::createUndefined();
|
||||
}
|
||||
|
||||
static ValueRef* builtin262AgentReceiveBroadcast(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
state->context()->globalObject()->setExtraData(argv[0]);
|
||||
return ValueRef::createUndefined();
|
||||
}
|
||||
|
||||
static ValueRef* builtin262AgentGetReport(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(workerMutex);
|
||||
if (messagesFromWorkers.size()) {
|
||||
std::string message = messagesFromWorkers.front();
|
||||
messagesFromWorkers.erase(messagesFromWorkers.begin());
|
||||
return StringRef::createFromUTF8(message.data(), message.size());
|
||||
} else {
|
||||
return ValueRef::createNull();
|
||||
}
|
||||
}
|
||||
|
||||
static ValueRef* builtin262AgentMonotonicNow(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return ValueRef::create((uint64_t)tv.tv_sec * 1000UL + tv.tv_usec / 1000UL);
|
||||
}
|
||||
|
||||
static ValueRef* builtin262AgentSleep(ExecutionStateRef* state, ValueRef* thisValue, size_t argc, ValueRef** argv, bool isConstructCall)
|
||||
{
|
||||
double m = argv[0]->toNumber(state);
|
||||
usleep(m * 1000.0);
|
||||
return ValueRef::createUndefined();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static bool evalScript(ContextRef* context, StringRef* source, StringRef* srcName, bool shouldPrintScriptResult, bool isModule)
|
||||
{
|
||||
if (stringEndsWith(srcName->toStdUTF8String(), "mjs")) {
|
||||
|
|
@ -937,8 +938,8 @@ int main(int argc, char* argv[])
|
|||
|
||||
bool wait_before_exit = false;
|
||||
|
||||
ShellPlatform* platform = new ShellPlatform();
|
||||
Globals::initialize(platform);
|
||||
g_platform = new ShellPlatform();
|
||||
Globals::initialize(g_platform);
|
||||
|
||||
Memory::setGCFrequency(24);
|
||||
|
||||
|
|
@ -966,7 +967,7 @@ int main(int argc, char* argv[])
|
|||
continue;
|
||||
}
|
||||
if (strcmp(argv[i], "--canblock-is-false") == 0) {
|
||||
platform->setCanBlock(false);
|
||||
g_platform->setCanBlock(false);
|
||||
continue;
|
||||
}
|
||||
if (strstr(argv[i], "--filename-as=") == argv[i]) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue