package wazevo

import (
	
	
	
	
	
	
	
	

	
	
	
	
	
	
	
	
	
)

var crc = crc32.MakeTable(crc32.Castagnoli)

// fileCacheKey returns a key for the file cache.
// In order to avoid collisions with the existing compiler, we do not use m.ID directly,
// but instead we rehash it with magic.
func fileCacheKey( *wasm.Module) ( filecache.Key) {
	 := sha256.New()
	.Write(.ID[:])
	.Write(magic)
	// Write the CPU features so that we can cache the compiled module for the same CPU.
	// This prevents the incompatible CPU features from being used.
	 := platform.CpuFeatures.Raw()
	// Reuse the `ret` buffer to write the first 8 bytes of the CPU features so that we can avoid the allocation.
	binary.LittleEndian.PutUint64([:8], )
	.Write([:8])
	// Finally, write the hash to the ret buffer.
	.Sum([:0])
	return
}

func ( *engine) ( *wasm.Module,  *compiledModule) ( *compiledModule,  error) {
	 = .addCompiledModuleToMemory(, )
	if !.IsHostModule && .fileCache != nil {
		 = .addCompiledModuleToCache(, )
	}
	return
}

func ( *engine) ( *wasm.Module,  []experimental.FunctionListener,  bool) ( *compiledModule,  bool,  error) {
	,  = .getCompiledModuleFromMemory(, true)
	if  {
		return
	}
	, ,  = .getCompiledModuleFromCache()
	if  {
		.parent = 
		.module = 
		.sharedFunctions = .sharedFunctions
		.ensureTermination = 
		.offsets = wazevoapi.NewModuleContextOffsetData(, len() > 0)
		if len() > 0 {
			.listeners = 
			.listenerBeforeTrampolines = make([]*byte, len(.TypeSection))
			.listenerAfterTrampolines = make([]*byte, len(.TypeSection))
			for  := range .TypeSection {
				 := &.TypeSection[]
				,  := .getListenerTrampolineForType()
				.listenerBeforeTrampolines[] = 
				.listenerAfterTrampolines[] = 
			}
		}
		.addCompiledModuleToMemory(, )
		 := ssa.NewBuilder()
		 := newMachine()
		 := backend.NewCompiler(context.Background(), , )
		.executables.compileEntryPreambles(, , )

		// Set the finalizer.
		.setFinalizer(.executables, executablesFinalizer)
	}
	return
}

func ( *engine) ( *wasm.Module,  *compiledModule) *compiledModule {
	.mux.Lock()
	defer .mux.Unlock()
	if ,  := .compiledModules[.ID];  {
		.refCount++
		return .compiledModule
	}
	.compiledModules[.ID] = &compiledModuleWithCount{compiledModule: , refCount: 1}
	if len(.executable) > 0 {
		.addCompiledModuleToSortedList()
	}
	return 
}

func ( *engine) ( *wasm.Module,  bool) ( *compiledModule,  bool) {
	.mux.Lock()
	defer .mux.Unlock()

	,  := .compiledModules[.ID]
	if  {
		 = .compiledModule
		if  {
			.refCount++
		}
	}
	return
}

func ( *engine) ( *wasm.Module,  *compiledModule) ( error) {
	if .fileCache == nil || .IsHostModule {
		return
	}
	 = .fileCache.Add(fileCacheKey(), serializeCompiledModule(.wazeroVersion, ))
	return
}

func ( *engine) ( *wasm.Module) ( *compiledModule,  bool,  error) {
	if .fileCache == nil || .IsHostModule {
		return
	}

	// Check if the entries exist in the external cache.
	var  io.ReadCloser
	, ,  = .fileCache.Get(fileCacheKey())
	if ! ||  != nil {
		return
	}

	// Otherwise, we hit the cache on external cache.
	// We retrieve *code structures from `cached`.
	var  bool
	// Note: cached.Close is ensured to be called in deserializeCodes.
	, ,  = deserializeCompiledModule(.wazeroVersion, )
	if  != nil {
		 = false
		return
	} else if  {
		return nil, false, .fileCache.Delete(fileCacheKey())
	}
	return
}

var magic = []byte{'W', 'A', 'Z', 'E', 'V', 'O'}

func serializeCompiledModule( string,  *compiledModule) io.Reader {
	 := bytes.NewBuffer(nil)
	// First 6 byte: WAZEVO header.
	.Write(magic)
	// Next 1 byte: length of version:
	.WriteByte(byte(len()))
	// Version of wazero.
	.WriteString()
	// Number of *code (== locally defined functions in the module): 4 bytes.
	.Write(u32.LeBytes(uint32(len(.functionOffsets))))
	for ,  := range .functionOffsets {
		// The offset of this function in the executable (8 bytes).
		.Write(u64.LeBytes(uint64()))
	}
	// The length of code segment (8 bytes).
	.Write(u64.LeBytes(uint64(len(.executable))))
	// Append the native code.
	.Write(.executable)
	// Append checksum.
	 := crc32.Checksum(.executable, crc)
	.Write(u32.LeBytes())
	if  := .sourceMap; len(.executableOffsets) > 0 {
		.WriteByte(1) // indicates that source map is present.
		 := len(.wasmBinaryOffsets)
		.Write(u64.LeBytes(uint64()))
		 := uintptr(unsafe.Pointer(&.executable[0]))
		for  := 0;  < ; ++ {
			.Write(u64.LeBytes(.wasmBinaryOffsets[]))
			// executableOffsets is absolute address, so we need to subtract executableAddr.
			.Write(u64.LeBytes(uint64(.executableOffsets[] - )))
		}
	} else {
		.WriteByte(0) // indicates that source map is not present.
	}
	return bytes.NewReader(.Bytes())
}

func deserializeCompiledModule( string,  io.ReadCloser) ( *compiledModule,  bool,  error) {
	defer .Close()
	 := len(magic) + 1 /* version size */ + len() + 4 /* number of functions */

	// Read the header before the native code.
	 := make([]byte, )
	,  := .Read()
	if  != nil {
		return nil, false, fmt.Errorf("compilationcache: error reading header: %v", )
	}

	if  !=  {
		return nil, false, fmt.Errorf("compilationcache: invalid header length: %d", )
	}

	if !bytes.Equal([:len(magic)], magic) {
		return nil, false, fmt.Errorf(
			"compilationcache: invalid magic number: got %s but want %s", magic, [:len(magic)])
	}

	// Check the version compatibility.
	 := int([len(magic)])

	,  := len(magic)+1, len(magic)+1+
	if  >= len() {
		 = true
		return
	} else if  := string([:]);  !=  {
		 = true
		return
	}

	 := binary.LittleEndian.Uint32([len()-4:])
	 = &compiledModule{functionOffsets: make([]int, ), executables: &executables{}}

	var  [8]byte
	for  := uint32(0);  < ; ++ {
		// Read the offset of each function in the executable.
		var  uint64
		if ,  = readUint64(, &);  != nil {
			 = fmt.Errorf("compilationcache: error reading func[%d] executable offset: %v", , )
			return
		}
		.functionOffsets[] = int()
	}

	,  := readUint64(, &)
	if  != nil {
		 = fmt.Errorf("compilationcache: error reading executable size: %v", )
		return
	}

	if  > 0 {
		,  := platform.MmapCodeSegment(int())
		if  != nil {
			 = fmt.Errorf("compilationcache: error mmapping executable (len=%d): %v", , )
			return nil, false, 
		}

		_,  = io.ReadFull(, )
		if  != nil {
			 = fmt.Errorf("compilationcache: error reading executable (len=%d): %v", , )
			return nil, false, 
		}

		 := crc32.Checksum(, crc)
		if _,  = io.ReadFull(, [:4]);  != nil {
			return nil, false, fmt.Errorf("compilationcache: could not read checksum: %v", )
		} else if  := binary.LittleEndian.Uint32([:4]);  !=  {
			return nil, false, fmt.Errorf("compilationcache: checksum mismatch (expected %d, got %d)", , )
		}

		if  = platform.MprotectCodeSegment();  != nil {
			return nil, false, 
		}
		.executable = 
	}

	if ,  := io.ReadFull(, [:1]);  != nil {
		return nil, false, fmt.Errorf("compilationcache: error reading source map presence: %v", )
	}

	if [0] == 1 {
		 := &.sourceMap
		,  := readUint64(, &)
		if  != nil {
			 = fmt.Errorf("compilationcache: error reading source map length: %v", )
			return nil, false, 
		}
		 := uintptr(unsafe.Pointer(&.executable[0]))
		for  := uint64(0);  < ; ++ {
			,  := readUint64(, &)
			if  != nil {
				 = fmt.Errorf("compilationcache: error reading source map[%d] wasm binary offset: %v", , )
				return nil, false, 
			}
			,  := readUint64(, &)
			if  != nil {
				 = fmt.Errorf("compilationcache: error reading source map[%d] executable offset: %v", , )
				return nil, false, 
			}
			.wasmBinaryOffsets = append(.wasmBinaryOffsets, )
			// executableOffsets is absolute address, so we need to add executableOffset.
			.executableOffsets = append(.executableOffsets, uintptr()+)
		}
	}
	return
}

// readUint64 strictly reads an uint64 in little-endian byte order, using the
// given array as a buffer. This returns io.EOF if less than 8 bytes were read.
func readUint64( io.Reader,  *[8]byte) (uint64, error) {
	 := [0:8]
	,  := .Read()
	if  != nil {
		return 0, 
	} else if  < 8 { // more strict than reader.Read
		return 0, io.EOF
	}

	// Read the u64 from the underlying buffer.
	 := binary.LittleEndian.Uint64()
	return , nil
}