267 lines · 11.3 KB · C++
RawDownload
////  ScriptContextV8Wrapper.cpp//  libraries/script-engine/src/v8////  Created by Heather Anderson on 5/22/21.//  Modified for V8 by dr Karol Suprynowicz on 2022/10/08//  Copyright 2021 Vircadia contributors.//  Copyright 2022-2023 Overte e.V.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html//  SPDX-License-Identifier: Apache-2.0// #include "ScriptContextV8Wrapper.h" #include "ScriptEngineV8.h"#include "ScriptValueV8Wrapper.h"#include "ScriptEngineLoggingV8.h" ScriptContextV8Wrapper::ScriptContextV8Wrapper(ScriptEngineV8* engine, const v8::Local<v8::Context> context, ScriptContextPointer parent) :    _functionCallbackInfo(nullptr), _propertyCallbackInfo(nullptr), _engine(engine),    _context(engine->getIsolate(), context), _parentContext(parent) {} ScriptContextV8Wrapper::ScriptContextV8Wrapper(ScriptEngineV8* engine, const v8::FunctionCallbackInfo<v8::Value> *functionCallbackInfo,                                               const v8::Local<v8::Context> context, ScriptContextPointer parent) :    _functionCallbackInfo(functionCallbackInfo), _propertyCallbackInfo(nullptr), _engine(engine),    _context(engine->getIsolate(), context), _parentContext(parent)  {} ScriptContextV8Wrapper::ScriptContextV8Wrapper(ScriptEngineV8* engine, const v8::PropertyCallbackInfo<v8::Value> *propertyCallbackInfo,                                               const v8::Local<v8::Context> context, ScriptContextPointer parent) :    _functionCallbackInfo(nullptr), _propertyCallbackInfo(propertyCallbackInfo), _engine(engine),    _context(engine->getIsolate(), context), _parentContext(parent)   {} ScriptContextV8Wrapper::~ScriptContextV8Wrapper() noexcept {    //V8TODO: what if destructor happens during shutdown and V8 isolate is already disposed of?    auto isolate = _engine->getIsolate();    Q_ASSERT(isolate->IsCurrent());    _context.Reset();} ScriptContextV8Wrapper* ScriptContextV8Wrapper::unwrap(ScriptContext* val) {    if (!val) {        return nullptr;    }     return dynamic_cast<ScriptContextV8Wrapper*>(val);} v8::Local<v8::Context> ScriptContextV8Wrapper::toV8Value() const {    v8::EscapableHandleScope handleScope(_engine->getIsolate());    return handleScope.Escape(_context.Get(_engine->getIsolate()));} int ScriptContextV8Wrapper::argumentCount() const {    auto isolate = _engine->getIsolate();    Q_ASSERT(isolate->IsCurrent());    v8::HandleScope handleScope(isolate);    v8::Context::Scope contextScope(_context.Get(isolate));    if (_functionCallbackInfo) {        return _functionCallbackInfo->Length();    } else if (_propertyCallbackInfo) {        return 0;    } else {        return Q_METAMETHOD_INVOKE_MAX_ARGS;    }} ScriptValue ScriptContextV8Wrapper::argument(int index) const {    if (_functionCallbackInfo) {        auto isolate = _engine->getIsolate();        Q_ASSERT(isolate->IsCurrent());        v8::HandleScope handleScope(isolate);        v8::Context::Scope contextScope(_context.Get(isolate));        v8::Local<v8::Value> result = (*_functionCallbackInfo)[index];        if (!result->IsUndefined()) {            return ScriptValue(new ScriptValueV8Wrapper(_engine, V8ScriptValue(_engine, result)));        } else {            return _engine->undefinedValue();        }    } else if (_propertyCallbackInfo) {        return _engine->undefinedValue();    } else {        return _engine->undefinedValue();    }} QStringList ScriptContextV8Wrapper::backtrace() const {    auto isolate = _engine->getIsolate();    Q_ASSERT(isolate->IsCurrent());    v8::HandleScope handleScope(isolate);    v8::Context::Scope contextScope(_context.Get(isolate));    v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(isolate, 40);    QStringList backTrace;    for (int i = 0; i < stackTrace->GetFrameCount(); i++) {        v8::Local<v8::StackFrame> stackFrame = stackTrace->GetFrame(isolate, i);        backTrace.append(QString(*v8::String::Utf8Value(isolate, stackFrame->GetScriptNameOrSourceURL())) +                        QString(" ") +                        QString(*v8::String::Utf8Value(isolate, stackFrame->GetFunctionName())) +                        QString(":") +                        QStringLiteral("%1").arg(stackFrame->GetLineNumber())        );    }    return backTrace;} int ScriptContextV8Wrapper::currentLineNumber() const {    auto isolate = _engine->getIsolate();    Q_ASSERT(isolate->IsCurrent());    v8::HandleScope handleScope(isolate);    v8::Context::Scope contextScope(_context.Get(isolate));    v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(isolate, 1);    if (stackTrace->GetFrameCount() > 0) {        v8::Local<v8::StackFrame> stackFrame = stackTrace->GetFrame(isolate, 0);        return stackFrame->GetLineNumber();    } else {        return -1;    }} QString ScriptContextV8Wrapper::currentFileName() const {    auto isolate = _engine->getIsolate();    Q_ASSERT(isolate->IsCurrent());    v8::HandleScope handleScope(isolate);    v8::Context::Scope contextScope(_context.Get(isolate));    v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(isolate, 1);    QStringList backTrace;    if (stackTrace->GetFrameCount() > 0) {        v8::Local<v8::StackFrame> stackFrame = stackTrace->GetFrame(isolate, 0);        return *v8::String::Utf8Value(isolate, stackFrame->GetScriptNameOrSourceURL());    } else {        return "";    }} ScriptValue ScriptContextV8Wrapper::callee() const {    Q_ASSERT(false);    //V8TODO    //Can this be done with CurrentStackTrace?    //V8ScriptValue result = _context->callee();    return _engine->undefinedValue();} ScriptEnginePointer ScriptContextV8Wrapper::engine() const {    return _engine->shared_from_this();} ScriptFunctionContextPointer ScriptContextV8Wrapper::functionContext() const {    auto isolate = _engine->getIsolate();    Q_ASSERT(isolate->IsCurrent());    v8::HandleScope handleScope(isolate);    v8::Context::Scope contextScope(_context.Get(isolate));    auto scriptFunctionContextPointer = std::make_shared<ScriptFunctionContextV8Wrapper>(_engine, _context.Get(_engine->getIsolate()));    return scriptFunctionContextPointer;} ScriptContextPointer ScriptContextV8Wrapper::parentContext() const {    return _parentContext;} ScriptValue ScriptContextV8Wrapper::thisObject() const {    if (_functionCallbackInfo) {        auto isolate = _engine->getIsolate();        Q_ASSERT(isolate->IsCurrent());        v8::HandleScope handleScope(isolate);        v8::Context::Scope contextScope(_context.Get(isolate));        v8::Local<v8::Value> result = _functionCallbackInfo->This();        return ScriptValue(new ScriptValueV8Wrapper(_engine, V8ScriptValue(_engine, result)));    } else if (_propertyCallbackInfo) {        auto isolate = _engine->getIsolate();        Q_ASSERT(isolate->IsCurrent());        v8::HandleScope handleScope(isolate);        v8::Context::Scope contextScope(_context.Get(isolate));        v8::Local<v8::Value> result = _propertyCallbackInfo->This();        return ScriptValue(new ScriptValueV8Wrapper(_engine, V8ScriptValue(_engine, result)));    } else {        return _engine->undefinedValue();    }} ScriptValue ScriptContextV8Wrapper::throwError(const QString& text) {    auto isolate = _engine->getIsolate();    // V8TODO: I have no idea how to do this yet when it happens on another thread    if(isolate->IsCurrent()) {        Q_ASSERT(isolate->IsCurrent());        v8::HandleScope handleScope(isolate);        v8::Context::Scope contextScope(_context.Get(isolate));        V8ScriptValue result(_engine,                             _engine->getIsolate()->ThrowError(                                 v8::String::NewFromUtf8(_engine->getIsolate(), text.toStdString().c_str()).ToLocalChecked()));        return ScriptValue(new ScriptValueV8Wrapper(_engine, std::move(result)));    } else {        qCWarning(scriptengine_v8) << "throwError on a different thread not implemented yet, error value: " << text;        return ScriptValue();    }} ScriptValue ScriptContextV8Wrapper::throwValue(const ScriptValue& value) {    auto isolate = _engine->getIsolate();    Q_ASSERT(isolate->IsCurrent());    v8::HandleScope handleScope(isolate);    v8::Context::Scope contextScope(_context.Get(isolate));    ScriptValueV8Wrapper* unwrapped = ScriptValueV8Wrapper::unwrap(value);    if (!unwrapped) {        return _engine->undefinedValue();    }    V8ScriptValue result(_engine, _engine->getIsolate()->ThrowException(unwrapped->toV8Value().constGet()));    return ScriptValue(new ScriptValueV8Wrapper(_engine, std::move(result)));} ScriptFunctionContextV8Wrapper::ScriptFunctionContextV8Wrapper(ScriptEngineV8* engine, const v8::Local<v8::Context> context) : _engine(engine) {    auto isolate = _engine->getIsolate();    Q_ASSERT(isolate->IsCurrent());    v8::HandleScope handleScope(isolate);    _context.Reset(engine->getIsolate(), context);} ScriptFunctionContextV8Wrapper::~ScriptFunctionContextV8Wrapper() {    //V8TODO: what if destructor happens during shutdown and V8 isolate is already disposed of?    auto isolate = _engine->getIsolate();    Q_ASSERT(isolate->IsCurrent());    _context.Reset();} QString ScriptFunctionContextV8Wrapper::fileName() const {    // It's not exactly like in QtScript, because there's no such context object in V8, let's return the current one for now    auto isolate = _engine->getIsolate();    Q_ASSERT(isolate->IsCurrent());    v8::HandleScope handleScope(isolate);    v8::Context::Scope contextScope(_context.Get(isolate));    v8::Local<v8::String> name = v8::StackTrace::CurrentScriptNameOrSourceURL(_engine->getIsolate());    v8::String::Utf8Value nameUTF(_engine->getIsolate(), name);    return QString(*nameUTF);} QString ScriptFunctionContextV8Wrapper::functionName() const {    // It's not exactly like in QtScript, because there's no such context object in V8, let's return the current one for now    auto isolate = _engine->getIsolate();    Q_ASSERT(isolate->IsCurrent());    v8::HandleScope handleScope(isolate);    v8::Context::Scope contextScope(_context.Get(isolate));    v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(_engine->getIsolate(), 1);    v8::Local<v8::StackFrame> stackFrame = stackTrace->GetFrame(_engine->getIsolate(), 0);    v8::Local<v8::String> name = stackFrame->GetFunctionName();    v8::String::Utf8Value nameUTF(_engine->getIsolate(), name);    return QString(*nameUTF);} ScriptFunctionContext::FunctionType ScriptFunctionContextV8Wrapper::functionType() const {    //V8TODO: This is only used in debugPrint. Should we remove it?    //return static_cast<ScriptFunctionContext::FunctionType>(_value.functionType());    return ScriptFunctionContext::FunctionType::ScriptFunction;} int ScriptFunctionContextV8Wrapper::lineNumber() const {    // It's not exactly like in QtScript, because there's no such context object in V8, let's return the current one for now    auto isolate = _engine->getIsolate();    Q_ASSERT(isolate->IsCurrent());    v8::HandleScope handleScope(isolate);    v8::Context::Scope contextScope(_context.Get(isolate));    v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(_engine->getIsolate(), 1);    v8::Local<v8::StackFrame> stackFrame = stackTrace->GetFrame(_engine->getIsolate(), 0);    return stackFrame->GetLineNumber();}