History

Further exception work on V8

* Get rid of maybeEmitUncaughtException * Mostly get rid of makeError * Introduce exception hierarchy, change exceptions to shared_ptr * Simplify exception throwing code

Dale Glass committed 3 years ago

Commit 67e7a731 parent c0e62c510 files · +249 −260Browse files

Showing the changes to libraries/script-engine/src/v8/ScriptEngineV8.cpp (1 of 10 files). Show all

Unified · Split

libraries/script-engine/src/v8/ScriptEngineV8.cpp+45−172
@@ -71,20 +71,17 @@ bool ScriptEngineV8::IS_THREADSAFE_INVOCATION(const QThread* thread, const QStri · 70 unmodified lines
7171 return false;
7272 }
7373
74−// engine-aware JS Error copier and factory
75−V8ScriptValue ScriptEngineV8::makeError(const V8ScriptValue& _other, const QString& type) {
74+ScriptValue ScriptEngineV8::makeError(const ScriptValue& _other, const QString& type) {
7675 if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
77− v8::Locker locker(_v8Isolate);
78− v8::Isolate::Scope isolateScope(_v8Isolate);
79− v8::HandleScope handleScope(_v8Isolate);
80− v8::Context::Scope contextScope(getContext());
81− return V8ScriptValue(this, v8::Null(_v8Isolate));
76+ return nullValue();
8277 }
8378 v8::Locker locker(_v8Isolate);
8479 v8::Isolate::Scope isolateScope(_v8Isolate);
8580 v8::HandleScope handleScope(_v8Isolate);
8681 v8::Context::Scope contextScope(getContext());
87− return V8ScriptValue(this, v8::Null(_v8Isolate));
82+ return nullValue();
83+}
84+
8885 //V8TODO
8986 /*
9087 auto other = _other;
@@ -117,33 +114,8 @@ V8ScriptValue ScriptEngineV8::makeError(const V8ScriptValue& _other, const QStri · 26 unmodified lines
117114 err.setProperty(it.name(), it.value());
118115 }
119116 return err;*/
120−}
117+//}
121118
122−ScriptValue ScriptEngineV8::makeError(const ScriptValue& _other, const QString& type) {
123− if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
124− return nullValue();
125− }
126− v8::Locker locker(_v8Isolate);
127− v8::Isolate::Scope isolateScope(_v8Isolate);
128− v8::HandleScope handleScope(_v8Isolate);
129− v8::Context::Scope contextScope(getContext());
130− return nullValue();
131−
132− //V8TODO
133− //what does makeError actually do?
134− /*ScriptValueV8Wrapper* unwrapped = ScriptValueV8Wrapper::unwrap(_other);
135− V8ScriptValue other;
136− if (_other.isString()) {
137− other = QScriptEngine::newObject();
138− other.setProperty("message", _other.toString());
139− } else if (unwrapped) {
140− other = unwrapped->toV8Value();
141− } else {
142− other = QScriptEngine::newVariant(_other.toVariant());
143− }
144− V8ScriptValue result = makeError(other, type);
145− return ScriptValue(new ScriptValueV8Wrapper(this, std::move(result)));*/
146−}
147119
148120 // check syntax and when there are issues returns an actual "SyntaxError" with the details
149121 ScriptValue ScriptEngineV8::checkScriptSyntax(ScriptProgramPointer program) {
@@ -200,82 +172,14 @@ ScriptValue ScriptEngineV8::checkScriptSyntax(ScriptProgramPointer program) { · 50 unmodified lines
200172 return undefinedValue();
201173 }*/
202174
203−// this pulls from the best available information to create a detailed snapshot of the current exception
204−ScriptValue ScriptEngineV8::cloneUncaughtException(const QString& extraDetail) {
205− if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
206− return nullValue();
207− }
208− if (!hasUncaughtException()) {
209− return nullValue();
210− }
211− v8::Locker locker(_v8Isolate);
212− v8::Isolate::Scope isolateScope(_v8Isolate);
213− v8::HandleScope handleScope(_v8Isolate);
214− v8::Context::Scope contextScope(getContext());
215− return nullValue();
216− //V8TODO
217− /*
218− auto exception = uncaughtException();
219− // ensure the error object is engine-local
220− auto err = makeError(exception);
221−
222− // not sure why Qt does't offer uncaughtExceptionFileName -- but the line number
223− // on its own is often useless/wrong if arbitrarily married to a filename.
224− // when the error object already has this info, it seems to be the most reliable
225− auto fileName = exception.property("fileName").toString();
226− auto lineNumber = exception.property("lineNumber").toInt32();
227−
228− // the backtrace, on the other hand, seems most reliable taken from uncaughtExceptionBacktrace
229− auto backtrace = uncaughtExceptionBacktrace();
230− if (backtrace.isEmpty()) {
231− // fallback to the error object
232− backtrace = exception.property("stack").toString().split(ScriptManager::SCRIPT_BACKTRACE_SEP);
233− }
234− // the ad hoc "detail" property can be used now to embed additional clues
235− auto detail = exception.property("detail").toString();
236− if (detail.isEmpty()) {
237− detail = extraDetail;
238− } else if (!extraDetail.isEmpty()) {
239− detail += "(" + extraDetail + ")";
240− }
241− if (lineNumber <= 0) {
242− lineNumber = uncaughtExceptionLineNumber();
243− }
244− if (fileName.isEmpty()) {
245− // climb the stack frames looking for something useful to display
246− for (auto c = QScriptEngine::currentContext(); c && fileName.isEmpty(); c = c->parentContext()) {
247− V8ScriptContextInfo info{ c };
248− if (!info.fileName().isEmpty()) {
249− // take fileName:lineNumber as a pair
250− fileName = info.fileName();
251− lineNumber = info.lineNumber();
252− if (backtrace.isEmpty()) {
253− backtrace = c->backtrace();
254− }
255− break;
256− }
257− }
258− }
259− err.setProperty("fileName", fileNlintame);
260− err.setProperty("lineNumber", lineNumber);
261− err.setProperty("detail", detail);
262− err.setProperty("stack", backtrace.join(ScriptManager::SCRIPT_BACKTRACE_SEP));
263−
264−#ifdef DEBUG_JS_EXCEPTIONS
265− err.setProperty("_fileName", exception.property("fileName").toString());
266− err.setProperty("_stack", uncaughtExceptionBacktrace().join(SCRIPT_BACKTRACE_SEP));
267− err.setProperty("_lineNumber", uncaughtExceptionLineNumber());
268−#endif
269− return err;
270− */
271−}
272−
273175 bool ScriptEngineV8::raiseException(const V8ScriptValue& exception) {
274176 if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
275177 return false;
276178 }
277179 //V8TODO
278− _v8Isolate->ThrowException(makeError(exception).get());
180+ // _v8Isolate->ThrowException(makeError(exception).get());
181+
182+
279183 /*if (QScriptEngine::currentContext()) {
280184 // we have an active context / JS stack frame so throw the exception per usual
281185 QScriptEngine::currentContext()->throwValue(makeError(exception));
@@ -290,22 +194,6 @@ bool ScriptEngineV8::raiseException(const V8ScriptValue& exception) { · 8 unmodified lines
290194 return false;
291195 }
292196
293−bool ScriptEngineV8::maybeEmitUncaughtException(const QString& debugHint) {
294−
295− /*
296− if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
297− return false;
298− }
299− if (!isEvaluating() && hasUncaughtException()) {
300− emit exception(cloneUncaughtException(debugHint));
301− clearExceptions();
302− return true;
303− }
304−
305− */
306− return false;
307−}
308−
309197 // Lambda
310198 /*ScriptValue ScriptEngineV8::newLambdaFunction(std::function<V8ScriptValue(V8ScriptContext*, ScriptEngineV8*)> operation,
311199 const V8ScriptValue& data,
@@ -1019,12 +907,11 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure, · 707 unmodified lines
1019907 }
1020908
1021909 if (hasUncaughtException()) {
1022− auto err = cloneUncaughtException(__FUNCTION__);
1023910 #ifdef DEBUG_JS_EXCEPTIONS
1024911 qCWarning(shared) << __FUNCTION__ << "---------- hasCaught:" << err.toString() << result.toString();
1025912 err.setProperty("_result", result);
1026913 #endif
1027− result = err;
914+ result = nullValue();
1028915 } else {
1029916 result = ScriptValue(new ScriptValueV8Wrapper(this, V8ScriptValue(this, v8Result)));
1030917 }
@@ -1073,34 +960,9 @@ ScriptValue ScriptEngineV8::evaluate(const QString& sourceCode, const QString& f · 42 unmodified lines
1073960 v8::ScriptOrigin scriptOrigin(getIsolate(), v8::String::NewFromUtf8(getIsolate(), fileName.toStdString().c_str()).ToLocalChecked());
1074961 v8::Local<v8::Script> script;
1075962 if (!v8::Script::Compile(getContext(), v8::String::NewFromUtf8(getIsolate(), sourceCode.toStdString().c_str()).ToLocalChecked(), &scriptOrigin).ToLocal(&script)) {
1076− //V8TODO replace this with external function
1077− int errorColumnNumber = 0;
1078− int errorLineNumber = 0;
1079− QString errorMessage = "";
1080− QString errorBacktrace = "";
1081− //v8::String::Utf8Value utf8Value(getIsolate(), tryCatch.Exception());
1082− v8::String::Utf8Value utf8Value(getIsolate(), tryCatch.Message()->Get());
1083− errorMessage = QString(*utf8Value);
1084− v8::Local<v8::Message> exceptionMessage = tryCatch.Message();
1085− if (!exceptionMessage.IsEmpty()) {
1086− errorLineNumber = exceptionMessage->GetLineNumber(getContext()).FromJust();
1087− errorColumnNumber = exceptionMessage->GetStartColumn(getContext()).FromJust();
1088− v8::Local<v8::Value> backtraceV8String;
1089− if (tryCatch.StackTrace(getContext()).ToLocal(&backtraceV8String)) {
1090− if (backtraceV8String->IsString()) {
1091− if (v8::Local<v8::String>::Cast(backtraceV8String)->Length() > 0) {
1092− v8::String::Utf8Value backtraceUtf8Value(getIsolate(), backtraceV8String);
1093− errorBacktrace = *backtraceUtf8Value;
1094− }
1095− }
1096− }
1097− qCDebug(scriptengine) << "Compiling script \"" << fileName << "\" failed on line " << errorLineNumber << " column " << errorColumnNumber << " with message: \"" << errorMessage <<"\" backtrace: " << errorBacktrace;
1098− }
1099− auto err = makeError(newValue(errorMessage));
1100− raiseException(err);
1101− maybeEmitUncaughtException("compile");
963+ setUncaughtException(tryCatch, "Error while compiling script");
1102964 _evaluatingCounter--;
1103− return err;
965+ return nullValue();
1104966 }
1105967 //qCDebug(scriptengine) << "Script compilation succesful: " << fileName;
1106968
@@ -1145,6 +1007,12 @@ ScriptValue ScriptEngineV8::evaluate(const QString& sourceCode, const QString& f · 38 unmodified lines
11451007 return ScriptValue(new ScriptValueV8Wrapper(this, std::move(resultValue)));
11461008 }
11471009
1010+
1011+void ScriptEngineV8::setUncaughtEngineException(const QString &reason, const QString& info) {
1012+ auto ex = std::make_shared<ScriptEngineException>(reason, info);
1013+ setUncaughtException(ex);
1014+}
1015+
11481016 void ScriptEngineV8::setUncaughtException(const v8::TryCatch &tryCatch, const QString& info) {
11491017 if (!tryCatch.HasCaught()) {
11501018 qCWarning(scriptengine) << "setUncaughtException called without exception";
@@ -1152,8 +1020,8 @@ void ScriptEngineV8::setUncaughtException(const v8::TryCatch &tryCatch, const QS · 1 unmodified line
11521020 return;
11531021 }
11541022
1155− ScriptException ex;
1156− ex.additionalInfo = info;
1023+ auto ex = std::make_shared<ScriptException>();
1024+ ex->additionalInfo = info;
11571025
11581026 v8::Locker locker(_v8Isolate);
11591027 v8::Isolate::Scope isolateScope(_v8Isolate);
@@ -1166,30 +1034,35 @@ void ScriptEngineV8::setUncaughtException(const v8::TryCatch &tryCatch, const QS · 6 unmodified lines
11661034 //v8::String::Utf8Value utf8Value(getIsolate(), tryCatch.Exception());
11671035 v8::String::Utf8Value utf8Value(getIsolate(), tryCatch.Message()->Get());
11681036
1169− ex.errorMessage = QString(*utf8Value);
1037+ ex->errorMessage = QString(*utf8Value);
11701038
11711039 v8::Local<v8::Message> exceptionMessage = tryCatch.Message();
11721040 if (!exceptionMessage.IsEmpty()) {
1173− ex.errorLine = exceptionMessage->GetLineNumber(getContext()).FromJust();
1174− ex.errorColumn = exceptionMessage->GetStartColumn(getContext()).FromJust();
1041+ ex->errorLine = exceptionMessage->GetLineNumber(getContext()).FromJust();
1042+ ex->errorColumn = exceptionMessage->GetStartColumn(getContext()).FromJust();
11751043 v8::Local<v8::Value> backtraceV8String;
11761044 if (tryCatch.StackTrace(getContext()).ToLocal(&backtraceV8String)) {
11771045 if (backtraceV8String->IsString()) {
11781046 if (v8::Local<v8::String>::Cast(backtraceV8String)->Length() > 0) {
11791047 v8::String::Utf8Value backtraceUtf8Value(getIsolate(), backtraceV8String);
11801048 QString errorBacktrace = *backtraceUtf8Value;
1181− ex.backtrace = errorBacktrace.split("\n");
1049+ ex->backtrace = errorBacktrace.split("\n");
11821050
11831051 }
11841052 }
11851053 }
11861054 }
11871055
1188− qCDebug(scriptengine) << "Emitting exception:" << ex;
1189− _uncaughtException = ex;
1190− emit exception(ex);
1056+ setUncaughtException(ex);
11911057 }
11921058
1059+void ScriptEngineV8::setUncaughtException(std::shared_ptr<ScriptException> uncaughtException) {
1060+ qCDebug(scriptengine) << "Emitting exception:" << uncaughtException;
1061+ _uncaughtException = uncaughtException;
1062+
1063+ auto copy = uncaughtException->clone();
1064+ emit exception(copy);
1065+}
11931066
11941067
11951068 QString ScriptEngineV8::formatErrorMessageFromTryCatch(v8::TryCatch &tryCatch) {
@@ -1274,18 +1147,14 @@ Q_INVOKABLE ScriptValue ScriptEngineV8::evaluate(const ScriptProgramPointer& pro · 78 unmodified lines
12741147 v8::Context::Scope contextScope(getContext());
12751148 ScriptProgramV8Wrapper* unwrapped = ScriptProgramV8Wrapper::unwrap(program);
12761149 if (!unwrapped) {
1277− errorValue = makeError(newValue("could not unwrap program"));
1278− raiseException(errorValue);
1279− maybeEmitUncaughtException("compile");
1150+ setUncaughtEngineException("Could not unwrap program", "Compile error");
12801151 hasFailed = true;
12811152 }
12821153
12831154 if(!hasFailed) {
12841155 ScriptSyntaxCheckResultPointer syntaxCheck = unwrapped->checkSyntax();
12851156 if (syntaxCheck->state() == ScriptSyntaxCheckResult::Error) {
1286− errorValue = makeError(newValue(syntaxCheck->errorMessage()));
1287− raiseException(errorValue);
1288− maybeEmitUncaughtException("compile");
1157+ setUncaughtEngineException(syntaxCheck->errorMessage(), "Compile error");
12891158 hasFailed = true;
12901159 }
12911160 }
@@ -1307,8 +1176,7 @@ Q_INVOKABLE ScriptValue ScriptEngineV8::evaluate(const ScriptProgramPointer& pro · 15 unmodified lines
13071176 Q_ASSERT(tryCatchRun.HasCaught());
13081177 auto runError = tryCatchRun.Message();
13091178 errorValue = ScriptValue(new ScriptValueV8Wrapper(this, V8ScriptValue(this, runError->Get())));
1310− raiseException(errorValue);
1311− maybeEmitUncaughtException("evaluate");
1179+ raiseException(errorValue, "evaluation error");
13121180 hasFailed = true;
13131181 } else {
13141182 // V8TODO this is just to check if run will always return false for uncaught exception
@@ -1529,7 +1397,7 @@ void ScriptEngineV8::abortEvaluation() { · 214 unmodified lines
15291397 }
15301398
15311399 void ScriptEngineV8::clearExceptions() {
1532− _uncaughtException = ScriptException();
1400+ _uncaughtException.reset();
15331401 }
15341402
15351403 ScriptContext* ScriptEngineV8::currentContext() const {
@@ -1551,7 +1419,7 @@ ScriptContext* ScriptEngineV8::currentContext() const { · 15 unmodified lines
15511419 }
15521420
15531421 bool ScriptEngineV8::hasUncaughtException() const {
1554− return !_uncaughtException.isEmpty();
1422+ return _uncaughtException != nullptr;
15551423 }
15561424
15571425 bool ScriptEngineV8::isEvaluating() const {
@@ -1667,11 +1535,15 @@ void ScriptEngineV8::setThread(QThread* thread) { · 109 unmodified lines
16671535 }*/
16681536
16691537
1670−ScriptException ScriptEngineV8::uncaughtException() const {
1671− return _uncaughtException;
1538+std::shared_ptr<ScriptException> ScriptEngineV8::uncaughtException() const {
1539+ return _uncaughtException->clone();
1540+}
1541+
1542+bool ScriptEngineV8::raiseException(const QString& error, const QString &reason) {
1543+ return raiseException(ScriptValue(), reason);
16721544 }
16731545
1674−bool ScriptEngineV8::raiseException(const ScriptValue& exception) {
1546+bool ScriptEngineV8::raiseException(const ScriptValue& exception, const QString &reason) {
16751547 //V8TODO
16761548 //Q_ASSERT(false);
16771549 // qCritical() << "Script exception occurred: " << exception.toString();
@@ -1681,6 +1553,7 @@ bool ScriptEngineV8::raiseException(const ScriptValue& exception) { · 3 unmodified lines
16811553 // emit
16821554 //return raiseException(qException);
16831555
1556+ qCCritical(scriptengine) << "Raise exception for reason" << reason << "NOT IMPLEMENTED!";
16841557 return false;
16851558 }
16861559