33#include < android/looper.h>
44#include < pthread.h>
55
6+ #include < optional>
67#include < thread>
78
89#include " ArgConverter.h"
@@ -33,36 +34,57 @@ namespace tns {
3334namespace {
3435
3536/*
36- * The `name` and `message` the parent rebuilds the worker's error from, read
37- * off the value the worker threw. An object's `name` and `message` are taken
38- * when they are strings, so an Error or a DOMException keeps both; anything
39- * else becomes an Error whose message is the value's string form. Either
40- * property may be a getter, and one that throws leaves the default in place.
37+ * The `name` and `message` the parent rebuilds the worker's error from. They
38+ * stay UTF-16 on the way, so both arrive exactly as thrown, embedded NULs and
39+ * unpaired surrogates included.
4140 */
42- void DescribeThrownValue (Isolate* isolate, Local<Context> context, Local<Value> thrown,
43- std::string& name, std::string& message) {
41+ struct ThrownErrorText {
42+ std::u16string name = u" Error" ;
43+ std::u16string message;
44+ };
45+
46+ std::u16string ToUtf16 (Isolate* isolate, Local<String> value) {
47+ std::u16string result (value->Length (), u' \0 ' );
48+ value->WriteV2 (isolate, 0 , value->Length (), reinterpret_cast <uint16_t *>(result.data ()));
49+ return result;
50+ }
51+
52+ Local<String> FromUtf16 (Isolate* isolate, const std::u16string& value) {
53+ return String::NewFromTwoByte (isolate, reinterpret_cast <const uint16_t *>(value.data ()),
54+ NewStringType::kNormal , static_cast <int >(value.size ()))
55+ .ToLocalChecked ();
56+ }
57+
58+ /*
59+ * Reads the text off the value the worker threw. An object's `name` and
60+ * `message` are taken when they are strings, so an Error or a DOMException
61+ * keeps both; anything else becomes an Error whose message is the value's
62+ * string form. Either property may be a getter, and one that throws leaves the
63+ * default in place.
64+ */
65+ ThrownErrorText DescribeThrownValue (Isolate* isolate, Local<Context> context, Local<Value> thrown) {
4466 HandleScope handleScope (isolate);
4567 TryCatch tc (isolate);
46- name = " Error" ;
47- message.clear ();
68+ ThrownErrorText text;
4869 Local<String> detail;
4970 if (thrown->ToDetailString (context).ToLocal (&detail)) {
50- message = ArgConverter::ConvertToString ( detail);
71+ text. message = ToUtf16 (isolate, detail);
5172 }
5273 if (!thrown->IsObject () || thrown->IsFunction ()) {
53- return ;
74+ return text ;
5475 }
5576 auto object = thrown.As <Object>();
5677 Local<Value> value;
5778 if (object->Get (context, ArgConverter::ConvertToV8String (isolate, " name" )).ToLocal (&value) &&
5879 value->IsString ()) {
59- name = ArgConverter::ConvertToString ( value.As <String>());
80+ text. name = ToUtf16 (isolate, value.As <String>());
6081 }
6182 if (object->Get (context, ArgConverter::ConvertToV8String (isolate, " message" ))
6283 .ToLocal (&value) &&
6384 value->IsString ()) {
64- message = ArgConverter::ConvertToString ( value.As <String>());
85+ text. message = ToUtf16 (isolate, value.As <String>());
6586 }
87+ return text;
6688}
6789
6890/*
@@ -416,26 +438,32 @@ void WorkerWrapper::PassUncaughtExceptionFromWorkerToParent(const std::string& m
416438
417439 // Read here, on the worker's isolate. A report with no thrown value, such
418440 // as the heap-limit one made from inside a GC, touches no v8 handle.
419- std::string errorName = " Error" ;
420- std::string errorMessage = message;
441+ std::optional<ThrownErrorText> thrownText;
421442 if (!thrown.IsEmpty ()) {
422443 Isolate* workerIsolate = Isolate::GetCurrent ();
423- DescribeThrownValue (workerIsolate, workerIsolate->GetCurrentContext (), thrown, errorName,
424- errorMessage);
444+ thrownText = DescribeThrownValue (workerIsolate, workerIsolate->GetCurrentContext (), thrown);
425445 }
426446
427447 int workerId = workerId_;
428448 std::string threadName = threadName_;
429449 Isolate* parentIsolate = parentIsolate_;
430450
431451 parentTasks->PostInternal ([workerId, message, filename, stackTrace, lineno, threadName,
432- parentIsolate, errorName, errorMessage ]() {
452+ parentIsolate, thrownText ]() {
433453 v8::Locker locker (parentIsolate);
434454 Isolate::Scope isolate_scope (parentIsolate);
435455 HandleScope handle_scope (parentIsolate);
436456 auto context = Runtime::GetRuntime (parentIsolate)->GetContext ();
437457 Context::Scope context_scope (context);
438458
459+ // Without a thrown value, the error is an Error carrying the report's
460+ // message.
461+ Local<String> errorName = thrownText
462+ ? FromUtf16 (parentIsolate, thrownText->name )
463+ : ArgConverter::ConvertToV8String (parentIsolate, " Error" );
464+ Local<String> errorMessage = thrownText
465+ ? FromUtf16 (parentIsolate, thrownText->message )
466+ : ArgConverter::ConvertToV8String (parentIsolate, message);
439467 WorkerWrapper::FireErrorOnParentWorkerObject (workerId, message, stackTrace, filename,
440468 lineno, threadName, errorName,
441469 errorMessage);
@@ -446,8 +474,8 @@ void WorkerWrapper::FireErrorOnParentWorkerObject(int workerId, const std::strin
446474 const std::string& stackTrace,
447475 const std::string& filename, int lineno,
448476 const std::string& threadName,
449- const std::string& errorName,
450- const std::string& errorMessage) {
477+ Local<String> errorName,
478+ Local<String> errorMessage) {
451479 auto wrapper = WorkerWrapper::GetById (workerId);
452480 if (wrapper == nullptr ) {
453481 DEBUG_WRITE (" MAIN: no worker instance was found with workerId=%d." , workerId);
0 commit comments