| 
									
										
										
										
											2024-10-28 10:55:48 +00:00
										 |  |  | package wasm | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/tetratelabs/wazero/api" | 
					
						
							|  |  |  | 	"github.com/tetratelabs/wazero/experimental" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type snapshotskey struct{} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-25 16:38:19 +02:00
										 |  |  | type snapshotctx struct { | 
					
						
							|  |  |  | 	context.Context | 
					
						
							|  |  |  | 	snaps *snapshots | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (ctx snapshotctx) Value(key any) any { | 
					
						
							|  |  |  | 	if _, ok := key.(snapshotskey); ok { | 
					
						
							|  |  |  | 		return ctx.snaps | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return ctx.Context.Value(key) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ringsz uint = 8 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type snapshots struct { | 
					
						
							|  |  |  | 	r [ringsz]struct { | 
					
						
							|  |  |  | 		eptr uint32 | 
					
						
							|  |  |  | 		snap experimental.Snapshot | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	n uint | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s *snapshots) get(envptr uint32) experimental.Snapshot { | 
					
						
							|  |  |  | 	start := (s.n % ringsz) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i := start; i != ^uint(0); i-- { | 
					
						
							|  |  |  | 		if s.r[i].eptr == envptr { | 
					
						
							|  |  |  | 			snap := s.r[i].snap | 
					
						
							|  |  |  | 			s.r[i].eptr = 0 | 
					
						
							|  |  |  | 			s.r[i].snap = nil | 
					
						
							|  |  |  | 			s.n = i - 1 | 
					
						
							|  |  |  | 			return snap | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i := ringsz - 1; i > start; i-- { | 
					
						
							|  |  |  | 		if s.r[i].eptr == envptr { | 
					
						
							|  |  |  | 			snap := s.r[i].snap | 
					
						
							|  |  |  | 			s.r[i].eptr = 0 | 
					
						
							|  |  |  | 			s.r[i].snap = nil | 
					
						
							|  |  |  | 			s.n = i - 1 | 
					
						
							|  |  |  | 			return snap | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	panic("snapshot not found") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s *snapshots) set(envptr uint32, snapshot experimental.Snapshot) { | 
					
						
							|  |  |  | 	start := (s.n % ringsz) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i := start; i < ringsz; i++ { | 
					
						
							|  |  |  | 		switch s.r[i].eptr { | 
					
						
							|  |  |  | 		case 0, envptr: | 
					
						
							|  |  |  | 			s.r[i].eptr = envptr | 
					
						
							|  |  |  | 			s.r[i].snap = snapshot | 
					
						
							|  |  |  | 			s.n = i | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i := uint(0); i < start; i++ { | 
					
						
							|  |  |  | 		switch s.r[i].eptr { | 
					
						
							|  |  |  | 		case 0, envptr: | 
					
						
							|  |  |  | 			s.r[i].eptr = envptr | 
					
						
							|  |  |  | 			s.r[i].snap = snapshot | 
					
						
							|  |  |  | 			s.n = i | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	panic("snapshots full") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-28 10:55:48 +00:00
										 |  |  | // withSetjmpLongjmp updates the context to contain wazero/experimental.Snapshotter{} support, | 
					
						
							|  |  |  | // and embeds the necessary snapshots map required for later calls to Setjmp() / Longjmp(). | 
					
						
							|  |  |  | func withSetjmpLongjmp(ctx context.Context) context.Context { | 
					
						
							| 
									
										
										
										
											2025-09-25 16:38:19 +02:00
										 |  |  | 	return snapshotctx{Context: experimental.WithSnapshotter(ctx), snaps: new(snapshots)} | 
					
						
							| 
									
										
										
										
											2024-10-28 10:55:48 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-25 16:38:19 +02:00
										 |  |  | func getSnapshots(ctx context.Context) *snapshots { | 
					
						
							|  |  |  | 	v, _ := ctx.Value(snapshotskey{}).(*snapshots) | 
					
						
							| 
									
										
										
										
											2024-10-28 10:55:48 +00:00
										 |  |  | 	return v | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // setjmp implements the C function: setjmp(env jmp_buf) | 
					
						
							| 
									
										
										
										
											2025-09-25 16:38:19 +02:00
										 |  |  | func setjmp(ctx context.Context, _ api.Module, stack []uint64) { | 
					
						
							| 
									
										
										
										
											2024-10-28 10:55:48 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Input arguments. | 
					
						
							|  |  |  | 	envptr := api.DecodeU32(stack[0]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Take snapshot of current execution environment. | 
					
						
							|  |  |  | 	snapshotter := experimental.GetSnapshotter(ctx) | 
					
						
							|  |  |  | 	snapshot := snapshotter.Snapshot() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Get stored snapshots map. | 
					
						
							|  |  |  | 	snapshots := getSnapshots(ctx) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Set latest snapshot in map. | 
					
						
							| 
									
										
										
										
											2025-09-25 16:38:19 +02:00
										 |  |  | 	snapshots.set(envptr, snapshot) | 
					
						
							| 
									
										
										
										
											2024-10-28 10:55:48 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Set return. | 
					
						
							|  |  |  | 	stack[0] = 0 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // longjmp implements the C function: int longjmp(env jmp_buf, value int) | 
					
						
							| 
									
										
										
										
											2025-09-25 16:38:19 +02:00
										 |  |  | func longjmp(ctx context.Context, _ api.Module, stack []uint64) { | 
					
						
							| 
									
										
										
										
											2024-10-28 10:55:48 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Input arguments. | 
					
						
							|  |  |  | 	envptr := api.DecodeU32(stack[0]) | 
					
						
							|  |  |  | 	// val := stack[1] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Get stored snapshots map. | 
					
						
							|  |  |  | 	snapshots := getSnapshots(ctx) | 
					
						
							|  |  |  | 	if snapshots == nil { | 
					
						
							|  |  |  | 		panic("setjmp / longjmp not supported") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Get snapshot stored in map. | 
					
						
							| 
									
										
										
										
											2025-09-25 16:38:19 +02:00
										 |  |  | 	snapshot := snapshots.get(envptr) | 
					
						
							| 
									
										
										
										
											2024-10-28 10:55:48 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Set return. | 
					
						
							|  |  |  | 	stack[0] = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Restore execution and | 
					
						
							|  |  |  | 	// return passed value arg. | 
					
						
							|  |  |  | 	snapshot.Restore(stack[1:]) | 
					
						
							|  |  |  | } |