|
7 | 7 | #include "ser.h" |
8 | 8 | #include "setup.h" |
9 | 9 |
|
| 10 | +#include <workerd/jsg/exception-metadata.capnp.h> |
| 11 | + |
10 | 12 | #include <openssl/rand.h> |
11 | 13 |
|
| 14 | +#include <capnp/message.h> |
| 15 | +#include <capnp/serialize.h> |
12 | 16 | #include <kj/debug.h> |
13 | 17 |
|
14 | 18 | #include <cstdlib> |
@@ -454,6 +458,50 @@ void addExceptionDetail(Lock& js, kj::Exception& exception, v8::Local<v8::Value> |
454 | 458 | } |
455 | 459 | } |
456 | 460 |
|
| 461 | +void addJsExceptionMetadata(Lock& js, kj::Exception& exception, v8::Local<v8::Value> handle) { |
| 462 | + // Extract JavaScript error type and stack trace |
| 463 | + if (!handle->IsObject()) { |
| 464 | + return; // Not an error object, nothing to extract |
| 465 | + } |
| 466 | + |
| 467 | + auto isolate = js.v8Isolate; |
| 468 | + auto context = isolate->GetCurrentContext(); |
| 469 | + auto errorObj = handle.As<v8::Object>(); |
| 470 | + |
| 471 | + // Build Cap'n Proto message |
| 472 | + capnp::MallocMessageBuilder message; |
| 473 | + auto metadata = message.initRoot<JsExceptionMetadata>(); |
| 474 | + |
| 475 | + // Limit for user-controlled fields (4KB) |
| 476 | + constexpr size_t MAX_FIELD_SIZE = 4096; |
| 477 | + |
| 478 | + // Extract error name (e.g., "Error", "TypeError", "RangeError") |
| 479 | + auto nameProp = errorObj->Get(context, v8StrIntern(isolate, "name"_kj)); |
| 480 | + if (!nameProp.IsEmpty() && nameProp.ToLocalChecked()->IsString()) { |
| 481 | + auto errorType = kj::str(nameProp.ToLocalChecked().As<v8::String>()); |
| 482 | + // Truncate to 4KB if needed |
| 483 | + if (errorType.size() > MAX_FIELD_SIZE) { |
| 484 | + errorType = kj::str(errorType.slice(0, MAX_FIELD_SIZE)); |
| 485 | + } |
| 486 | + metadata.setErrorType(errorType); |
| 487 | + } |
| 488 | + |
| 489 | + // Extract stack trace string |
| 490 | + auto stackProp = errorObj->Get(context, v8StrIntern(isolate, "stack"_kj)); |
| 491 | + if (!stackProp.IsEmpty() && stackProp.ToLocalChecked()->IsString()) { |
| 492 | + auto stackTrace = kj::str(stackProp.ToLocalChecked().As<v8::String>()); |
| 493 | + // Truncate to 4KB if needed |
| 494 | + if (stackTrace.size() > MAX_FIELD_SIZE) { |
| 495 | + stackTrace = kj::str(stackTrace.slice(0, MAX_FIELD_SIZE)); |
| 496 | + } |
| 497 | + metadata.setStackTrace(stackTrace); |
| 498 | + } |
| 499 | + |
| 500 | + // Serialize to bytes using Cap'n Proto |
| 501 | + auto words = capnp::messageToFlatArray(message); |
| 502 | + exception.setDetail(JS_EXCEPTION_METADATA_DETAIL_ID, kj::heapArray(words.asBytes())); |
| 503 | +} |
| 504 | + |
457 | 505 | static kj::String typeErrorMessage(TypeErrorContext c, const char* expectedType) { |
458 | 506 | kj::String type; |
459 | 507 |
|
|
0 commit comments