fix(code-runtime): capture worker error intrinsic
This commit is contained in:
@@ -10,6 +10,7 @@ import type { DoneMessage, ReplyMessage, WorkerBootData, WorkerToHost } from './
|
|||||||
import { jsonStringBytesUpTo, jsonValueBytesUpTo, truncateJsonStringBytes } from './output-json.ts'
|
import { jsonStringBytesUpTo, jsonValueBytesUpTo, truncateJsonStringBytes } from './output-json.ts'
|
||||||
import { decodeWorkerJson, encodeWorkerJson, snapshotCodeJsonValue } from './worker-json.ts'
|
import { decodeWorkerJson, encodeWorkerJson, snapshotCodeJsonValue } from './worker-json.ts'
|
||||||
|
|
||||||
|
const CapturedError = Error
|
||||||
const capturedObjectCreate = Object.create
|
const capturedObjectCreate = Object.create
|
||||||
const capturedObjectDefineProperty = Object.defineProperty
|
const capturedObjectDefineProperty = Object.defineProperty
|
||||||
|
|
||||||
@@ -77,7 +78,7 @@ export class LogBuffer {
|
|||||||
if (prefix.length > 0) {
|
if (prefix.length > 0) {
|
||||||
const prefixBytes = jsonStringBytesUpTo(prefix, availableBytes)
|
const prefixBytes = jsonStringBytesUpTo(prefix, availableBytes)
|
||||||
/* v8 ignore next -- truncateJsonStringBytes guarantees the returned prefix fits. */
|
/* v8 ignore next -- truncateJsonStringBytes guarantees the returned prefix fits. */
|
||||||
if (prefixBytes === undefined) throw new Error('worker output ledger produced an oversized log prefix')
|
if (prefixBytes === undefined) throw new CapturedError('worker output ledger produced an oversized log prefix')
|
||||||
this.bytes += prefixBytes + separatorBytes
|
this.bytes += prefixBytes + separatorBytes
|
||||||
this.entries += 1
|
this.entries += 1
|
||||||
this.sink(prefix)
|
this.sink(prefix)
|
||||||
@@ -219,7 +220,7 @@ export function prepareException(
|
|||||||
): Omit<DoneMessage, 'type'> {
|
): Omit<DoneMessage, 'type'> {
|
||||||
let message: string
|
let message: string
|
||||||
try {
|
try {
|
||||||
const detail: unknown = error instanceof Error ? error.stack ?? error.message : error
|
const detail: unknown = error instanceof CapturedError ? error.stack ?? error.message : error
|
||||||
message = typeof detail === 'string' ? detail : String(detail)
|
message = typeof detail === 'string' ? detail : String(detail)
|
||||||
} catch {
|
} catch {
|
||||||
message = 'program threw an unrenderable value'
|
message = 'program threw an unrenderable value'
|
||||||
@@ -244,7 +245,7 @@ export type BindingErrorConstructor = new (memberName: string, message: string)
|
|||||||
function makeBindingErrorClass(
|
function makeBindingErrorClass(
|
||||||
descriptor: { name: string; memberNameProperty: string },
|
descriptor: { name: string; memberNameProperty: string },
|
||||||
): BindingErrorConstructor {
|
): BindingErrorConstructor {
|
||||||
return class BindingCallError extends Error {
|
return class BindingCallError extends CapturedError {
|
||||||
constructor(memberName: string, message: string) {
|
constructor(memberName: string, message: string) {
|
||||||
super(message)
|
super(message)
|
||||||
defineBindingErrorField(this, 'name', descriptor.name)
|
defineBindingErrorField(this, 'name', descriptor.name)
|
||||||
@@ -255,7 +256,7 @@ function makeBindingErrorClass(
|
|||||||
|
|
||||||
/** Create the namespace-specific rejection for one failed binding call. */
|
/** Create the namespace-specific rejection for one failed binding call. */
|
||||||
function bindingFailure(errorClass: BindingErrorConstructor | undefined, memberName: string, message: string): Error {
|
function bindingFailure(errorClass: BindingErrorConstructor | undefined, memberName: string, message: string): Error {
|
||||||
return errorClass ? new errorClass(memberName, message) : new Error(message)
|
return errorClass ? new errorClass(memberName, message) : new CapturedError(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -289,10 +290,10 @@ export function wireReplies(port: BootstrapPort, pending: Map<number, PendingCal
|
|||||||
pending.delete(message.id)
|
pending.delete(message.id)
|
||||||
if (message.ok) {
|
if (message.ok) {
|
||||||
const value = decodeWorkerJson(message.value)
|
const value = decodeWorkerJson(message.value)
|
||||||
if (value === undefined) entry.reject(new Error('binding resolution must be lossless JSON'))
|
if (value === undefined) entry.reject(new CapturedError('binding resolution must be lossless JSON'))
|
||||||
else entry.resolve(value)
|
else entry.resolve(value)
|
||||||
} else {
|
} else {
|
||||||
entry.reject(new Error(message.message))
|
entry.reject(new CapturedError(message.message))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -346,7 +347,7 @@ export function makeNamespaces(
|
|||||||
port.postMessage({ type: 'call', id, global, name, args: encodeWorkerJson(detached) })
|
port.postMessage({ type: 'call', id, global, name, args: encodeWorkerJson(detached) })
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
pending.delete(id)
|
pending.delete(id)
|
||||||
const message = `binding arguments must be structured-cloneable: ${error instanceof Error ? error.message : String(error)}`
|
const message = `binding arguments must be structured-cloneable: ${error instanceof CapturedError ? error.message : String(error)}`
|
||||||
reject(bindingFailure(errorClass, name, message))
|
reject(bindingFailure(errorClass, name, message))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -391,7 +392,7 @@ export async function runWorkerMain(
|
|||||||
errorClassParameters.push(namespace.errorClass.name)
|
errorClassParameters.push(namespace.errorClass.name)
|
||||||
const errorClass = errorClasses.get(namespace.global)
|
const errorClass = errorClasses.get(namespace.global)
|
||||||
/* v8 ignore next -- makeBindingErrorClasses covers every declaration in the same data. */
|
/* v8 ignore next -- makeBindingErrorClasses covers every declaration in the same data. */
|
||||||
if (!errorClass) throw new Error(`missing binding error class for ${namespace.global}`)
|
if (!errorClass) throw new CapturedError(`missing binding error class for ${namespace.global}`)
|
||||||
errorClassValues.push(errorClass)
|
errorClassValues.push(errorClass)
|
||||||
}
|
}
|
||||||
const consoleShim = makeConsoleShim(logs)
|
const consoleShim = makeConsoleShim(logs)
|
||||||
|
|||||||
@@ -679,7 +679,7 @@ describe('WorkerCodeRuntime — hostile programs (real workers)', () => {
|
|||||||
Function.prototype.toString = () => 'mutated';
|
Function.prototype.toString = () => 'mutated';
|
||||||
objectPrototype.get = () => undefined;
|
objectPrototype.get = () => undefined;
|
||||||
objectPrototype.constructor = arrayPrototype.constructor = null;
|
objectPrototype.constructor = arrayPrototype.constructor = null;
|
||||||
globalThis.Array = globalThis.Buffer = globalThis.Function = globalThis.Number = globalThis.Object = globalThis.Reflect = globalThis.Set = globalThis.String = undefined;
|
globalThis.Array = globalThis.Buffer = globalThis.Error = globalThis.Function = globalThis.Number = globalThis.Object = globalThis.Reflect = globalThis.Set = globalThis.String = undefined;
|
||||||
const echoed = await tools.echo({ request: ['€', 1] });
|
const echoed = await tools.echo({ request: ['€', 1] });
|
||||||
let failure;
|
let failure;
|
||||||
try { await tools.fail({}) } catch (error) {
|
try { await tools.fail({}) } catch (error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user