diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 8aa001a50a..fc1bfc094f 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -44,7 +44,9 @@ using ScriptProgramPointer = std::shared_ptr; Q_DECLARE_METATYPE(ScriptEnginePointer); template -inline ScriptValue scriptValueFromValue(ScriptEngine* engine, const T& t); +inline ScriptValue + +scriptValueFromValue(ScriptEngine* engine, const T& t); template inline T scriptvalue_cast(const ScriptValue& value); diff --git a/libraries/script-engine/src/ScriptValue.cpp b/libraries/script-engine/src/ScriptValue.cpp index 39d3c73beb..2c8f9e2581 100644 --- a/libraries/script-engine/src/ScriptValue.cpp +++ b/libraries/script-engine/src/ScriptValue.cpp @@ -45,6 +45,7 @@ public: const ScriptValue::ResolveFlags& mode = ScriptValue::ResolvePrototype) const override; virtual ScriptValue property(quint32 arrayIndex, const ScriptValue::ResolveFlags& mode = ScriptValue::ResolvePrototype) const override; + virtual ScriptValue prototype() const override; virtual void setData(const ScriptValue& val) override; virtual bool hasProperty(const QString &name) const override; virtual void setProperty(const QString& name, @@ -55,7 +56,7 @@ public: const ScriptValue::PropertyFlags& flags = ScriptValue::KeepExistingFlags) override; virtual void setPrototype(const ScriptValue& prototype) override; virtual bool strictlyEquals(const ScriptValue& other) const override; - virtual inline QList getPropertyNames() const override; + virtual QList getPropertyNames() const override; virtual bool toBool() const override; virtual qint32 toInt32() const override; @@ -177,6 +178,10 @@ ScriptValue ScriptValueProxyNull::property(quint32 arrayIndex, const ScriptValue return ScriptValue(); } +ScriptValue ScriptValueProxyNull::prototype() const { + return ScriptValue(); +} + void ScriptValueProxyNull::setData(const ScriptValue& val) { Q_ASSERT(false); qCWarning(scriptengine_script, "ScriptValue::setData called on empty value"); diff --git a/libraries/script-engine/src/ScriptValue.h b/libraries/script-engine/src/ScriptValue.h index 38059c4d06..1560c99a3e 100644 --- a/libraries/script-engine/src/ScriptValue.h +++ b/libraries/script-engine/src/ScriptValue.h @@ -90,6 +90,7 @@ public: inline ScriptValueIteratorPointer newIterator() const; inline ScriptValue property(const QString& name, const ResolveFlags& mode = ResolvePrototype) const; inline ScriptValue property(quint32 arrayIndex, const ResolveFlags& mode = ResolvePrototype) const; + inline ScriptValue prototype() const; inline void setData(const ScriptValue& val); inline bool hasProperty(const QString &name) const; inline void setProperty(const QString& name, @@ -154,6 +155,7 @@ public: const ScriptValue::ResolveFlags& mode = ScriptValue::ResolvePrototype) const = 0; virtual ScriptValue property(quint32 arrayIndex, const ScriptValue::ResolveFlags& mode = ScriptValue::ResolvePrototype) const = 0; + virtual ScriptValue prototype() const = 0; virtual void setData(const ScriptValue& val) = 0; virtual bool hasProperty(const QString &name) const = 0; virtual void setProperty(const QString& name, @@ -318,6 +320,11 @@ ScriptValue ScriptValue::property(quint32 arrayIndex, const ResolveFlags& mode) return _proxy->property(arrayIndex, mode); } +ScriptValue ScriptValue::prototype() const { + Q_ASSERT(_proxy != nullptr); + return _proxy->prototype(); +} + void ScriptValue::setData(const ScriptValue& val) { Q_ASSERT(_proxy != nullptr); return _proxy->setData(val); diff --git a/libraries/script-engine/src/v8/ScriptEngineV8.cpp b/libraries/script-engine/src/v8/ScriptEngineV8.cpp index 669d1a92bb..63af8f8ef6 100644 --- a/libraries/script-engine/src/v8/ScriptEngineV8.cpp +++ b/libraries/script-engine/src/v8/ScriptEngineV8.cpp @@ -46,6 +46,7 @@ #include "../ScriptEngineLogging.h" #include "../ScriptProgram.h" +#include "../ScriptEngineCast.h" #include "../ScriptValue.h" #include "ScriptContextV8Wrapper.h" @@ -53,6 +54,7 @@ #include "ScriptProgramV8Wrapper.h" #include "ScriptValueV8Wrapper.h" #include "ScriptEngineLoggingV8.h" +#include "ScriptValueIteratorV8Wrapper.h" static const int MAX_DEBUG_VALUE_LENGTH { 80 }; @@ -77,46 +79,40 @@ ScriptValue ScriptEngineV8::makeError(const ScriptValue& _other, const QString& if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) { return nullValue(); } - v8::Locker locker(_v8Isolate); - v8::Isolate::Scope isolateScope(_v8Isolate); - v8::HandleScope handleScope(_v8Isolate); - v8::Context::Scope contextScope(getContext()); return nullValue(); -} - //V8TODO: do not remove until ScriptEngineV8::makeError is implemented - /* auto other = _other; - if (_other.constGet()->IsString()) { - other = QScriptEngine::newObject(); + if (_other.isString()) { + other = newObject(); other.setProperty("message", _other.toString()); } - auto proto = QScriptEngine::globalObject().property(type); + auto proto = globalObject().property(type); if (!proto.isFunction()) { - proto = QScriptEngine::globalObject().property(other.prototype().property("constructor").property("name").toString()); + proto = globalObject().property(other.prototype().property("constructor").property("name").toString()); } if (!proto.isFunction()) { #ifdef DEBUG_JS_EXCEPTIONS qCDebug(shared) << "BaseScriptEngine::makeError -- couldn't find constructor for" << type << " -- using Error instead"; #endif - proto = QScriptEngine::globalObject().property("Error"); + proto = globalObject().property("Error"); } - if (other.engine() != this) { + if (other.engine().get() != this) { // JS Objects are parented to a specific script engine instance // -- this effectively ~clones it locally by routing through a QVariant and back - other = QScriptEngine::toScriptValue(other.toVariant()); + other = toScriptValue(other.toVariant()); } // ~ var err = new Error(other.message) - auto err = proto.construct(V8ScriptValueList({ other.property("message") })); + auto err = proto.construct(ScriptValueList({ other.property("message") })); // transfer over any existing properties - V8ScriptValueIterator it(other); - while (it.hasNext()) { - it.next(); - err.setProperty(it.name(), it.value()); + auto it = other.newIterator(); + while (it->hasNext()) { + it->next(); + err.setProperty(it->name(), it->value()); } - return err;*/ -//} + return err; +} + // check syntax and when there are issues returns an actual "SyntaxError" with the details diff --git a/libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp b/libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp index 0397dbc272..7ce54acbc8 100644 --- a/libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp +++ b/libraries/script-engine/src/v8/ScriptValueV8Wrapper.cpp @@ -153,7 +153,8 @@ ScriptValue ScriptValueV8Wrapper::construct(const ScriptValueList& args) { } v8::Local v8Function = v8::Local::Cast(_value.get()); - //V8TODO: I'm not sure if this is correct, maybe use CallAsContructor instead? + // V8TODO: I'm not sure if this is correct, maybe use CallAsConstructor instead? + // Maybe it's CallAsConstructor for function and NewInstance for class? auto maybeResult = v8Function->NewInstance(_engine->getContext(), args.length(), v8Args); v8::Local result; if (maybeResult.ToLocal(&result)) { @@ -319,6 +320,24 @@ ScriptValue ScriptValueV8Wrapper::property(quint32 arrayIndex, const ScriptValue return _engine->undefinedValue(); } +ScriptValue ScriptValueV8Wrapper::prototype() const { + auto isolate = _engine->getIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + v8::Context::Scope contextScope(_engine->getContext()); + + auto value = _value.constGet(); + if (!value->IsObject()) { + return _engine->undefinedValue(); + } + auto object = v8::Local::Cast(value); + auto prototype = object->GetPrototype(); + + V8ScriptValue result(_engine, prototype); + return ScriptValue(new ScriptValueV8Wrapper(_engine, result)); +} + void ScriptValueV8Wrapper::setData(const ScriptValue& value) { auto isolate = _engine->getIsolate(); v8::Locker locker(isolate); @@ -612,13 +631,26 @@ bool ScriptValueV8Wrapper::isBool() const { } bool ScriptValueV8Wrapper::isError() const { - //auto isolate = _engine->getIsolate(); - // Q_ASSERT(isolate->IsCurrent()); - // v8::HandleScope handleScope(isolate); - // v8::Context::Scope contextScope(_engine->getContext()); - //V8TODO + auto isolate = _engine->getIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + auto context = _engine->getContext(); + v8::Context::Scope contextScope(_engine->getContext()); + v8::Local error; + if (!context->Global()->Get(context, v8::String::NewFromUtf8(isolate, "Error").ToLocalChecked()).ToLocal(&error)) { + Q_ASSERT(false); + } + if (!error->IsObject()) { + Q_ASSERT(false); + } + auto errorObj = v8::Local::Cast(error); + if (_value.constGet()->InstanceOf(context, errorObj).FromMaybe(false)) { + qDebug() << "ScriptValueV8Wrapper::isError : true"; + return true; + } + qDebug() << "ScriptValueV8Wrapper::isError : false"; return false; - //return _value.constGet()->IsError(); } bool ScriptValueV8Wrapper::isFunction() const { diff --git a/libraries/script-engine/src/v8/ScriptValueV8Wrapper.h b/libraries/script-engine/src/v8/ScriptValueV8Wrapper.h index 6d5ca4cff9..3c4f7ff427 100644 --- a/libraries/script-engine/src/v8/ScriptValueV8Wrapper.h +++ b/libraries/script-engine/src/v8/ScriptValueV8Wrapper.h @@ -57,6 +57,7 @@ public: // ScriptValue implementation const ScriptValue::ResolveFlags& mode = ScriptValue::ResolvePrototype) const override; virtual ScriptValue property(quint32 arrayIndex, const ScriptValue::ResolveFlags& mode = ScriptValue::ResolvePrototype) const override; + virtual ScriptValue prototype() const override; virtual void setData(const ScriptValue& val) override; virtual bool hasProperty(const QString &name) const override;