<commit_before>use lucet_runtime::{lucet_hostcall_terminate, vmctx::Vmctx};
use std::cell::Ref;
use wasi_common::wasi::wasi_snapshot_preview1::WasiSnapshotPreview1;
use wasi_common::WasiCtx;
use wiggle::{GuestError, GuestPtr};

lucet_wasi_generate::bindings!({
    ctx: LucetWasiCtx,
    constructor: { LucetWasiCtx { vmctx } }
});

pub mod types {
    pub use wasi_common::wasi::types::*;
}

pub fn export_wasi_funcs() {
    hostcalls::init()
}

pub struct LucetWasiCtx<'a> {
    vmctx: &'a Vmctx,
}

impl<'a> LucetWasiCtx<'a> {
    pub fn wasi(&self) -> Ref<WasiCtx> {
        self.vmctx.get_embed_ctx()
    }
}

impl<'a> types::GuestErrorConversion for LucetWasiCtx<'a> {
    fn into_errno(&self, _e: GuestError) -> types::Errno {
        // TODO log error
        types::Errno::Inval
    }
}

impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for LucetWasiCtx<'a> {
    fn args_get<'b>(
        &self,
        argv: &GuestPtr<'b, GuestPtr<'b, u8>>,
        argv_buf: &GuestPtr<'b, u8>,
    ) -> Result<(), types::Errno> {
        self.wasi().args_get(argv, argv_buf)
    }

    fn args_sizes_get(&self) -> Result<(types::Size, types::Size), types::Errno> {
        self.wasi().args_sizes_get()
    }

    fn environ_get<'b>(
        &self,
        environ: &GuestPtr<'b, GuestPtr<'b, u8>>,
        environ_buf: &GuestPtr<'b, u8>,
    ) -> Result<(), types::Errno> {
        self.wasi().environ_get(environ, environ_buf)
    }

    fn environ_sizes_get(&self) -> Result<(types::Size, types::Size), types::Errno> {
        self.wasi().environ_sizes_get()
    }

    fn clock_res_get(&self, id: types::Clockid) -> Result<types::Timestamp, types::Errno> {
        self.wasi().clock_res_get(id)
    }

    fn clock_time_get(
        &self,
        id: types::Clockid,
        precision: types::Timestamp,
    ) -> Result<types::Timestamp, types::Errno> {
        self.wasi().clock_time_get(id, precision)
    }

    fn fd_advise(
        &self,
        fd: types::Fd,
        offset: types::Filesize,
        len: types::Filesize,
        advice: types::Advice,
    ) -> Result<(), types::Errno> {
        self.wasi().fd_advise(fd, offset, len, advice)
    }

    fn fd_allocate(
        &self,
        fd: types::Fd,
        offset: types::Filesize,
        len: types::Filesize,
    ) -> Result<(), types::Errno> {
        self.wasi().fd_allocate(fd, offset, len)
    }

    fn fd_close(&self, fd: types::Fd) -> Result<(), types::Errno> {
        self.wasi().fd_close(fd)
    }

    fn fd_datasync(&self, fd: types::Fd) -> Result<(), types::Errno> {
        self.wasi().fd_datasync(fd)
    }

    fn fd_fdstat_get(&self, fd: types::Fd) -> Result<types::Fdstat, types::Errno> {
        self.wasi().fd_fdstat_get(fd)
    }

    fn fd_fdstat_set_flags(
        &self,
        fd: types::Fd,
        flags: types::Fdflags,
    ) -> Result<(), types::Errno> {
        self.wasi().fd_fdstat_set_flags(fd, flags)
    }

    fn fd_fdstat_set_rights(
        &self,
        fd: types::Fd,
        fs_rights_base: types::Rights,
        fs_rights_inheriting: types::Rights,
    ) -> Result<(), types::Errno> {
        self.wasi()
            .fd_fdstat_set_rights(fd, fs_rights_base, fs_rights_inheriting)
    }

    fn fd_filestat_get(&self, fd: types::Fd) -> Result<types::Filestat, types::Errno> {
        self.wasi().fd_filestat_get(fd)
    }

    fn fd_filestat_set_size(
        &self,
        fd: types::Fd,
        size: types::Filesize,
    ) -> Result<(), types::Errno> {
        self.wasi().fd_filestat_set_size(fd, size)
    }

    fn fd_filestat_set_times(
        &self,
        fd: types::Fd,
        atim: types::Timestamp,
        mtim: types::Timestamp,
        fst_flags: types::Fstflags,
    ) -> Result<(), types::Errno> {
        self.wasi().fd_filestat_set_times(fd, atim, mtim, fst_flags)
    }

    fn fd_pread(
        &self,
        fd: types::Fd,
        iovs: &types::IovecArray<'_>,
        offset: types::Filesize,
    ) -> Result<types::Size, types::Errno> {
        self.wasi().fd_pread(fd, iovs, offset)
    }

    fn fd_prestat_get(&self, fd: types::Fd) -> Result<types::Prestat, types::Errno> {
        self.wasi().fd_prestat_get(fd)
    }

    fn fd_prestat_dir_name(
        &self,
        fd: types::Fd,
        path: &GuestPtr<u8>,
        path_len: types::Size,
    ) -> Result<(), types::Errno> {
        self.wasi().fd_prestat_dir_name(fd, path, path_len)
    }

    fn fd_pwrite(
        &self,
        fd: types::Fd,
        ciovs: &types::CiovecArray<'_>,
        offset: types::Filesize,
    ) -> Result<types::Size, types::Errno> {
        self.wasi().fd_pwrite(fd, ciovs, offset)
    }

    fn fd_read(
        &self,
        fd: types::Fd,
        iovs: &types::IovecArray<'_>,
    ) -> Result<types::Size, types::Errno> {
        self.wasi().fd_read(fd, iovs)
    }

    fn fd_readdir(
        &self,
        fd: types::Fd,
        buf: &GuestPtr<u8>,
        buf_len: types::Size,
        cookie: types::Dircookie,
    ) -> Result<types::Size, types::Errno> {
        self.wasi().fd_readdir(fd, buf, buf_len, cookie)
    }

    fn fd_renumber(&self, from: types::Fd, to: types::Fd) -> Result<(), types::Errno> {
        self.wasi().fd_renumber(from, to)
    }

    fn fd_seek(
        &self,
        fd: types::Fd,
        offset: types::Filedelta,
        whence: types::Whence,
    ) -> Result<types::Filesize, types::Errno> {
        self.wasi().fd_seek(fd, offset, whence)
    }

    fn fd_sync(&self, fd: types::Fd) -> Result<(), types::Errno> {
        self.wasi().fd_sync(fd)
    }

    fn fd_tell(&self, fd: types::Fd) -> Result<types::Filesize, types::Errno> {
        self.wasi().fd_tell(fd)
    }

    fn fd_write(
        &self,
        fd: types::Fd,
        ciovs: &types::CiovecArray<'_>,
    ) -> Result<types::Size, types::Errno> {
        self.wasi().fd_write(fd, ciovs)
    }

    fn path_create_directory(
        &self,
        dirfd: types::Fd,
        path: &GuestPtr<'_, str>,
    ) -> Result<(), types::Errno> {
        self.wasi().path_create_directory(dirfd, path)
    }

    fn path_filestat_get(
        &self,
        dirfd: types::Fd,
        flags: types::Lookupflags,
        path: &GuestPtr<'_, str>,
    ) -> Result<types::Filestat, types::Errno> {
        self.wasi().path_filestat_get(dirfd, flags, path)
    }

    fn path_filestat_set_times(
        &self,
        dirfd: types::Fd,
        flags: types::Lookupflags,
        path: &GuestPtr<'_, str>,
        atim: types::Timestamp,
        mtim: types::Timestamp,
        fst_flags: types::Fstflags,
    ) -> Result<(), types::Errno> {
        self.wasi()
            .path_filestat_set_times(dirfd, flags, path, atim, mtim, fst_flags)
    }

    fn path_link(
        &self,
        old_fd: types::Fd,
        old_flags: types::Lookupflags,
        old_path: &GuestPtr<'_, str>,
        new_fd: types::Fd,
        new_path: &GuestPtr<'_, str>,
    ) -> Result<(), types::Errno> {
        self.wasi()
            .path_link(old_fd, old_flags, old_path, new_fd, new_path)
    }

    fn path_open(
        &self,
        dirfd: types::Fd,
        dirflags: types::Lookupflags,
        path: &GuestPtr<'_, str>,
        oflags: types::Oflags,
        fs_rights_base: types::Rights,
        fs_rights_inheriting: types::Rights,
        fdflags: types::Fdflags,
    ) -> Result<types::Fd, types::Errno> {
        self.wasi().path_open(
            dirfd,
            dirflags,
            path,
            oflags,
            fs_rights_base,
            fs_rights_inheriting,
            fdflags,
        )
    }

    fn path_readlink(
        &self,
        dirfd: types::Fd,
        path: &GuestPtr<'_, str>,
        buf: &GuestPtr<u8>,
        buf_len: types::Size,
    ) -> Result<types::Size, types::Errno> {
        self.wasi().path_readlink(dirfd, path, buf, buf_len)
    }

    fn path_remove_directory(
        &self,
        dirfd: types::Fd,
        path: &GuestPtr<'_, str>,
    ) -> Result<(), types::Errno> {
        self.wasi().path_remove_directory(dirfd, path)
    }

    fn path_rename(
        &self,
        old_fd: types::Fd,
        old_path: &GuestPtr<'_, str>,
        new_fd: types::Fd,
        new_path: &GuestPtr<'_, str>,
    ) -> Result<(), types::Errno> {
        self.wasi().path_rename(old_fd, old_path, new_fd, new_path)
    }

    fn path_symlink(
        &self,
        old_path: &GuestPtr<'_, str>,
        dirfd: types::Fd,
        new_path: &GuestPtr<'_, str>,
    ) -> Result<(), types::Errno> {
        self.wasi().path_symlink(old_path, dirfd, new_path)
    }

    fn path_unlink_file(
        &self,
        dirfd: types::Fd,
        path: &GuestPtr<'_, str>,
    ) -> Result<(), types::Errno> {
        self.wasi().path_unlink_file(dirfd, path)
    }

    fn poll_oneoff(
        &self,
        in_: &GuestPtr<types::Subscription>,
        out: &GuestPtr<types::Event>,
        nsubscriptions: types::Size,
    ) -> Result<types::Size, types::Errno> {
        self.wasi().poll_oneoff(in_, out, nsubscriptions)
    }

    fn proc_exit(&self, rval: types::Exitcode) -> Result<(), ()> {
        lucet_hostcall_terminate!(rval)
    }

    fn proc_raise(&self, _sig: types::Signal) -> Result<(), types::Errno> {
        Err(types::Errno::Inval)
    }

    fn sched_yield(&self) -> Result<(), types::Errno> {
        Ok(())
    }

    fn random_get(&self, buf: &GuestPtr<u8>, buf_len: types::Size) -> Result<(), types::Errno> {
        self.wasi().random_get(buf, buf_len)
    }

    fn sock_recv(
        &self,
        _fd: types::Fd,
        _ri_data: &types::IovecArray<'_>,
        _ri_flags: types::Riflags,
    ) -> Result<(types::Size, types::Roflags), types::Errno> {
        Err(types::Errno::Inval)
    }

    fn sock_send(
        &self,
        _fd: types::Fd,
        _si_data: &types::CiovecArray<'_>,
        _si_flags: types::Siflags,
    ) -> Result<types::Size, types::Errno> {
        Err(types::Errno::Inval)
    }

    fn sock_shutdown(&self, _fd: types::Fd, _how: types::Sdflags) -> Result<(), types::Errno> {
        Err(types::Errno::Inval)
    }
}
<commit_msg>lucet-wasi: comments on how bindings macro args work<commit_after>use lucet_runtime::{lucet_hostcall_terminate, vmctx::Vmctx};
use std::cell::Ref;
use wasi_common::wasi::wasi_snapshot_preview1::WasiSnapshotPreview1;
use wasi_common::WasiCtx;
use wiggle::{GuestError, GuestPtr};

lucet_wasi_generate::bindings!({
    // The context type, which we will implement the GuestErrorConversion and
    // WasiSnapshotPreview1 traits.
    ctx: LucetWasiCtx,
    // Describe how to construct the context type. The expression inside the first set
    // of braces will be used each time LucetWasiCtx needs to be constructed.
    // `vmctx: &mut Vmctx` is a free variable at the construction site.
    constructor: { LucetWasiCtx { vmctx } }
});

pub mod types {
    pub use wasi_common::wasi::types::*;
}

pub fn export_wasi_funcs() {
    hostcalls::init()
}

pub struct LucetWasiCtx<'a> {
    vmctx: &'a Vmctx,
}

impl<'a> LucetWasiCtx<'a> {
    pub fn wasi(&self) -> Ref<WasiCtx> {
        self.vmctx.get_embed_ctx()
    }
}

impl<'a> types::GuestErrorConversion for LucetWasiCtx<'a> {
    fn into_errno(&self, _e: GuestError) -> types::Errno {
        // TODO log error
        types::Errno::Inval
    }
}

impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for LucetWasiCtx<'a> {
    fn args_get<'b>(
        &self,
        argv: &GuestPtr<'b, GuestPtr<'b, u8>>,
        argv_buf: &GuestPtr<'b, u8>,
    ) -> Result<(), types::Errno> {
        self.wasi().args_get(argv, argv_buf)
    }

    fn args_sizes_get(&self) -> Result<(types::Size, types::Size), types::Errno> {
        self.wasi().args_sizes_get()
    }

    fn environ_get<'b>(
        &self,
        environ: &GuestPtr<'b, GuestPtr<'b, u8>>,
        environ_buf: &GuestPtr<'b, u8>,
    ) -> Result<(), types::Errno> {
        self.wasi().environ_get(environ, environ_buf)
    }

    fn environ_sizes_get(&self) -> Result<(types::Size, types::Size), types::Errno> {
        self.wasi().environ_sizes_get()
    }

    fn clock_res_get(&self, id: types::Clockid) -> Result<types::Timestamp, types::Errno> {
        self.wasi().clock_res_get(id)
    }

    fn clock_time_get(
        &self,
        id: types::Clockid,
        precision: types::Timestamp,
    ) -> Result<types::Timestamp, types::Errno> {
        self.wasi().clock_time_get(id, precision)
    }

    fn fd_advise(
        &self,
        fd: types::Fd,
        offset: types::Filesize,
        len: types::Filesize,
        advice: types::Advice,
    ) -> Result<(), types::Errno> {
        self.wasi().fd_advise(fd, offset, len, advice)
    }

    fn fd_allocate(
        &self,
        fd: types::Fd,
        offset: types::Filesize,
        len: types::Filesize,
    ) -> Result<(), types::Errno> {
        self.wasi().fd_allocate(fd, offset, len)
    }

    fn fd_close(&self, fd: types::Fd) -> Result<(), types::Errno> {
        self.wasi().fd_close(fd)
    }

    fn fd_datasync(&self, fd: types::Fd) -> Result<(), types::Errno> {
        self.wasi().fd_datasync(fd)
    }

    fn fd_fdstat_get(&self, fd: types::Fd) -> Result<types::Fdstat, types::Errno> {
        self.wasi().fd_fdstat_get(fd)
    }

    fn fd_fdstat_set_flags(
        &self,
        fd: types::Fd,
        flags: types::Fdflags,
    ) -> Result<(), types::Errno> {
        self.wasi().fd_fdstat_set_flags(fd, flags)
    }

    fn fd_fdstat_set_rights(
        &self,
        fd: types::Fd,
        fs_rights_base: types::Rights,
        fs_rights_inheriting: types::Rights,
    ) -> Result<(), types::Errno> {
        self.wasi()
            .fd_fdstat_set_rights(fd, fs_rights_base, fs_rights_inheriting)
    }

    fn fd_filestat_get(&self, fd: types::Fd) -> Result<types::Filestat, types::Errno> {
        self.wasi().fd_filestat_get(fd)
    }

    fn fd_filestat_set_size(
        &self,
        fd: types::Fd,
        size: types::Filesize,
    ) -> Result<(), types::Errno> {
        self.wasi().fd_filestat_set_size(fd, size)
    }

    fn fd_filestat_set_times(
        &self,
        fd: types::Fd,
        atim: types::Timestamp,
        mtim: types::Timestamp,
        fst_flags: types::Fstflags,
    ) -> Result<(), types::Errno> {
        self.wasi().fd_filestat_set_times(fd, atim, mtim, fst_flags)
    }

    fn fd_pread(
        &self,
        fd: types::Fd,
        iovs: &types::IovecArray<'_>,
        offset: types::Filesize,
    ) -> Result<types::Size, types::Errno> {
        self.wasi().fd_pread(fd, iovs, offset)
    }

    fn fd_prestat_get(&self, fd: types::Fd) -> Result<types::Prestat, types::Errno> {
        self.wasi().fd_prestat_get(fd)
    }

    fn fd_prestat_dir_name(
        &self,
        fd: types::Fd,
        path: &GuestPtr<u8>,
        path_len: types::Size,
    ) -> Result<(), types::Errno> {
        self.wasi().fd_prestat_dir_name(fd, path, path_len)
    }

    fn fd_pwrite(
        &self,
        fd: types::Fd,
        ciovs: &types::CiovecArray<'_>,
        offset: types::Filesize,
    ) -> Result<types::Size, types::Errno> {
        self.wasi().fd_pwrite(fd, ciovs, offset)
    }

    fn fd_read(
        &self,
        fd: types::Fd,
        iovs: &types::IovecArray<'_>,
    ) -> Result<types::Size, types::Errno> {
        self.wasi().fd_read(fd, iovs)
    }

    fn fd_readdir(
        &self,
        fd: types::Fd,
        buf: &GuestPtr<u8>,
        buf_len: types::Size,
        cookie: types::Dircookie,
    ) -> Result<types::Size, types::Errno> {
        self.wasi().fd_readdir(fd, buf, buf_len, cookie)
    }

    fn fd_renumber(&self, from: types::Fd, to: types::Fd) -> Result<(), types::Errno> {
        self.wasi().fd_renumber(from, to)
    }

    fn fd_seek(
        &self,
        fd: types::Fd,
        offset: types::Filedelta,
        whence: types::Whence,
    ) -> Result<types::Filesize, types::Errno> {
        self.wasi().fd_seek(fd, offset, whence)
    }

    fn fd_sync(&self, fd: types::Fd) -> Result<(), types::Errno> {
        self.wasi().fd_sync(fd)
    }

    fn fd_tell(&self, fd: types::Fd) -> Result<types::Filesize, types::Errno> {
        self.wasi().fd_tell(fd)
    }

    fn fd_write(
        &self,
        fd: types::Fd,
        ciovs: &types::CiovecArray<'_>,
    ) -> Result<types::Size, types::Errno> {
        self.wasi().fd_write(fd, ciovs)
    }

    fn path_create_directory(
        &self,
        dirfd: types::Fd,
        path: &GuestPtr<'_, str>,
    ) -> Result<(), types::Errno> {
        self.wasi().path_create_directory(dirfd, path)
    }

    fn path_filestat_get(
        &self,
        dirfd: types::Fd,
        flags: types::Lookupflags,
        path: &GuestPtr<'_, str>,
    ) -> Result<types::Filestat, types::Errno> {
        self.wasi().path_filestat_get(dirfd, flags, path)
    }

    fn path_filestat_set_times(
        &self,
        dirfd: types::Fd,
        flags: types::Lookupflags,
        path: &GuestPtr<'_, str>,
        atim: types::Timestamp,
        mtim: types::Timestamp,
        fst_flags: types::Fstflags,
    ) -> Result<(), types::Errno> {
        self.wasi()
            .path_filestat_set_times(dirfd, flags, path, atim, mtim, fst_flags)
    }

    fn path_link(
        &self,
        old_fd: types::Fd,
        old_flags: types::Lookupflags,
        old_path: &GuestPtr<'_, str>,
        new_fd: types::Fd,
        new_path: &GuestPtr<'_, str>,
    ) -> Result<(), types::Errno> {
        self.wasi()
            .path_link(old_fd, old_flags, old_path, new_fd, new_path)
    }

    fn path_open(
        &self,
        dirfd: types::Fd,
        dirflags: types::Lookupflags,
        path: &GuestPtr<'_, str>,
        oflags: types::Oflags,
        fs_rights_base: types::Rights,
        fs_rights_inheriting: types::Rights,
        fdflags: types::Fdflags,
    ) -> Result<types::Fd, types::Errno> {
        self.wasi().path_open(
            dirfd,
            dirflags,
            path,
            oflags,
            fs_rights_base,
            fs_rights_inheriting,
            fdflags,
        )
    }

    fn path_readlink(
        &self,
        dirfd: types::Fd,
        path: &GuestPtr<'_, str>,
        buf: &GuestPtr<u8>,
        buf_len: types::Size,
    ) -> Result<types::Size, types::Errno> {
        self.wasi().path_readlink(dirfd, path, buf, buf_len)
    }

    fn path_remove_directory(
        &self,
        dirfd: types::Fd,
        path: &GuestPtr<'_, str>,
    ) -> Result<(), types::Errno> {
        self.wasi().path_remove_directory(dirfd, path)
    }

    fn path_rename(
        &self,
        old_fd: types::Fd,
        old_path: &GuestPtr<'_, str>,
        new_fd: types::Fd,
        new_path: &GuestPtr<'_, str>,
    ) -> Result<(), types::Errno> {
        self.wasi().path_rename(old_fd, old_path, new_fd, new_path)
    }

    fn path_symlink(
        &self,
        old_path: &GuestPtr<'_, str>,
        dirfd: types::Fd,
        new_path: &GuestPtr<'_, str>,
    ) -> Result<(), types::Errno> {
        self.wasi().path_symlink(old_path, dirfd, new_path)
    }

    fn path_unlink_file(
        &self,
        dirfd: types::Fd,
        path: &GuestPtr<'_, str>,
    ) -> Result<(), types::Errno> {
        self.wasi().path_unlink_file(dirfd, path)
    }

    fn poll_oneoff(
        &self,
        in_: &GuestPtr<types::Subscription>,
        out: &GuestPtr<types::Event>,
        nsubscriptions: types::Size,
    ) -> Result<types::Size, types::Errno> {
        self.wasi().poll_oneoff(in_, out, nsubscriptions)
    }

    fn proc_exit(&self, rval: types::Exitcode) -> Result<(), ()> {
        lucet_hostcall_terminate!(rval)
    }

    fn proc_raise(&self, _sig: types::Signal) -> Result<(), types::Errno> {
        Err(types::Errno::Inval)
    }

    fn sched_yield(&self) -> Result<(), types::Errno> {
        Ok(())
    }

    fn random_get(&self, buf: &GuestPtr<u8>, buf_len: types::Size) -> Result<(), types::Errno> {
        self.wasi().random_get(buf, buf_len)
    }

    fn sock_recv(
        &self,
        _fd: types::Fd,
        _ri_data: &types::IovecArray<'_>,
        _ri_flags: types::Riflags,
    ) -> Result<(types::Size, types::Roflags), types::Errno> {
        Err(types::Errno::Inval)
    }

    fn sock_send(
        &self,
        _fd: types::Fd,
        _si_data: &types::CiovecArray<'_>,
        _si_flags: types::Siflags,
    ) -> Result<types::Size, types::Errno> {
        Err(types::Errno::Inval)
    }

    fn sock_shutdown(&self, _fd: types::Fd, _how: types::Sdflags) -> Result<(), types::Errno> {
        Err(types::Errno::Inval)
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use base::{warn, Event};
use std::time::{SystemTime, UNIX_EPOCH};

use crate::BusDevice;

// Register offsets
// Data register
const RTCDR: u64 = 0x0;
// Match register
const RTCMR: u64 = 0x4;
// Interrupt status register
const RTCSTAT: u64 = 0x8;
// Interrupt clear register
const RTCEOI: u64 = 0x8;
// Counter load register
const RTCLR: u64 = 0xC;
// Counter register
const RTCCR: u64 = 0x10;

// A single 4K page is mapped for this device
pub const PL030_AMBA_IOMEM_SIZE: u64 = 0x1000;

// AMBA id registers are at the end of the allocated memory space
const AMBA_ID_OFFSET: u64 = PL030_AMBA_IOMEM_SIZE - 0x20;
const AMBA_MASK_OFFSET: u64 = PL030_AMBA_IOMEM_SIZE - 0x28;

// This is the AMBA id for this device
pub const PL030_AMBA_ID: u32 = 0x00041030;
pub const PL030_AMBA_MASK: u32 = 0x000FFFFF;

/// An emulated ARM pl030 RTC
pub struct Pl030 {
    // Event to be used to interrupt the guest for an alarm event
    alarm_evt: Event,

    // This is the delta we subtract from current time to get the
    // counter value
    counter_delta_time: u32,

    // This is the value that triggers an alarm interrupt when it
    // matches with the rtc time
    match_value: u32,

    // status flag to keep track of whether the interrupt is cleared
    // or not
    interrupt_active: bool,
}

fn get_epoch_time() -> u32 {
    let epoch_time = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .expect("SystemTime::duration_since failed");
    epoch_time.as_secs() as u32
}

impl Pl030 {
    /// Constructs a Pl030 device
    pub fn new(evt: Event) -> Pl030 {
        Pl030 {
            alarm_evt: evt,
            counter_delta_time: get_epoch_time(),
            match_value: 0,
            interrupt_active: false,
        }
    }
}

impl BusDevice for Pl030 {
    fn debug_label(&self) -> String {
        "Pl030".to_owned()
    }

    fn write(&mut self, offset: u64, data: &[u8]) {
        if data.len() != 4 {
            warn!("bad write size: {} for pl030", data.len());
            return;
        }

        let reg_val: u32 = (data[0] as u32) << 24
            | (data[1] as u32) << 16
            | (data[2] as u32) << 8
            | (data[3] as u32);
        match offset {
            RTCDR => {
                warn!("invalid write to read-only RTCDR register");
            }
            RTCMR => {
                self.match_value = reg_val;
                // TODO(sonnyrao): here we need to set up a timer for
                // when host time equals the value written here and
                // fire the interrupt
                warn!("Not implemented: VM tried to set an RTC alarm");
            }
            RTCEOI => {
                if reg_val == 0 {
                    self.interrupt_active = false;
                } else {
                    self.alarm_evt.write(1).unwrap();
                    self.interrupt_active = true;
                }
            }
            RTCLR => {
                // TODO(sonnyrao): if we ever need to let the VM set it's own time
                // then we'll need to keep track of the delta between
                // the rtc time it sets and the host's rtc time and
                // record that here
                warn!("Not implemented: VM tried to set the RTC");
            }
            RTCCR => {
                self.counter_delta_time = get_epoch_time();
            }
            o => panic!("pl030: bad write offset {}", o),
        }
    }

    fn read(&mut self, offset: u64, data: &mut [u8]) {
        if data.len() != 4 {
            warn!("bad read size: {} for pl030", data.len());
            return;
        }

        let reg_content: u32 = match offset {
            RTCDR => get_epoch_time(),
            RTCMR => self.match_value,
            RTCSTAT => self.interrupt_active as u32,
            RTCLR => {
                warn!("invalid read of RTCLR register");
                0
            }
            RTCCR => get_epoch_time() - self.counter_delta_time,
            AMBA_ID_OFFSET => PL030_AMBA_ID,
            AMBA_MASK_OFFSET => PL030_AMBA_MASK,

            o => panic!("pl030: bad read offset {}", o),
        };
        data[0] = reg_content as u8;
        data[1] = (reg_content >> 8) as u8;
        data[2] = (reg_content >> 16) as u8;
        data[3] = (reg_content >> 24) as u8;
    }
}
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_interrupt_status_register() {
        let event = Event::new().unwrap();
        let mut device = Pl030::new(event.try_clone().unwrap());
        let mut register = [0, 0, 0, 0];

        // set interrupt
        device.write(RTCEOI, &[1, 0, 0, 0]);
        device.read(RTCSTAT, &mut register);
        assert_eq!(register, [1, 0, 0, 0]);
        assert_eq!(event.read().unwrap(), 1);

        // clear interrupt
        device.write(RTCEOI, &[0, 0, 0, 0]);
        device.read(RTCSTAT, &mut register);
        assert_eq!(register, [0, 0, 0, 0]);
    }

    #[test]
    fn test_match_register() {
        let mut device = Pl030::new(Event::new().unwrap());
        let mut register = [0, 0, 0, 0];

        device.write(RTCMR, &[1, 2, 3, 4]);
        device.read(RTCMR, &mut register);
        assert_eq!(register, [4, 3, 2, 1]);
    }
}
<commit_msg>pl030: Fix write endian conversion<commit_after>// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use base::{warn, Event};
use std::convert::TryFrom;
use std::time::{SystemTime, UNIX_EPOCH};

use crate::BusDevice;

// Register offsets
// Data register
const RTCDR: u64 = 0x0;
// Match register
const RTCMR: u64 = 0x4;
// Interrupt status register
const RTCSTAT: u64 = 0x8;
// Interrupt clear register
const RTCEOI: u64 = 0x8;
// Counter load register
const RTCLR: u64 = 0xC;
// Counter register
const RTCCR: u64 = 0x10;

// A single 4K page is mapped for this device
pub const PL030_AMBA_IOMEM_SIZE: u64 = 0x1000;

// AMBA id registers are at the end of the allocated memory space
const AMBA_ID_OFFSET: u64 = PL030_AMBA_IOMEM_SIZE - 0x20;
const AMBA_MASK_OFFSET: u64 = PL030_AMBA_IOMEM_SIZE - 0x28;

// This is the AMBA id for this device
pub const PL030_AMBA_ID: u32 = 0x00041030;
pub const PL030_AMBA_MASK: u32 = 0x000FFFFF;

/// An emulated ARM pl030 RTC
pub struct Pl030 {
    // Event to be used to interrupt the guest for an alarm event
    alarm_evt: Event,

    // This is the delta we subtract from current time to get the
    // counter value
    counter_delta_time: u32,

    // This is the value that triggers an alarm interrupt when it
    // matches with the rtc time
    match_value: u32,

    // status flag to keep track of whether the interrupt is cleared
    // or not
    interrupt_active: bool,
}

fn get_epoch_time() -> u32 {
    let epoch_time = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .expect("SystemTime::duration_since failed");
    epoch_time.as_secs() as u32
}

impl Pl030 {
    /// Constructs a Pl030 device
    pub fn new(evt: Event) -> Pl030 {
        Pl030 {
            alarm_evt: evt,
            counter_delta_time: get_epoch_time(),
            match_value: 0,
            interrupt_active: false,
        }
    }
}

impl BusDevice for Pl030 {
    fn debug_label(&self) -> String {
        "Pl030".to_owned()
    }

    fn write(&mut self, offset: u64, data: &[u8]) {
        let data_array = match <&[u8; 4]>::try_from(data) {
            Ok(array) => array,
            _ => {
                warn!("bad write size: {} for pl030", data.len());
                return;
            }
        };

        let reg_val = u32::from_ne_bytes(*data_array);
        match offset {
            RTCDR => {
                warn!("invalid write to read-only RTCDR register");
            }
            RTCMR => {
                self.match_value = reg_val;
                // TODO(sonnyrao): here we need to set up a timer for
                // when host time equals the value written here and
                // fire the interrupt
                warn!("Not implemented: VM tried to set an RTC alarm");
            }
            RTCEOI => {
                if reg_val == 0 {
                    self.interrupt_active = false;
                } else {
                    self.alarm_evt.write(1).unwrap();
                    self.interrupt_active = true;
                }
            }
            RTCLR => {
                // TODO(sonnyrao): if we ever need to let the VM set it's own time
                // then we'll need to keep track of the delta between
                // the rtc time it sets and the host's rtc time and
                // record that here
                warn!("Not implemented: VM tried to set the RTC");
            }
            RTCCR => {
                self.counter_delta_time = get_epoch_time();
            }
            o => panic!("pl030: bad write offset {}", o),
        }
    }

    fn read(&mut self, offset: u64, data: &mut [u8]) {
        let data_array = match <&mut [u8; 4]>::try_from(data) {
            Ok(array) => array,
            _ => {
                warn!("bad write size for pl030");
                return;
            }
        };

        let reg_content: u32 = match offset {
            RTCDR => get_epoch_time(),
            RTCMR => self.match_value,
            RTCSTAT => self.interrupt_active as u32,
            RTCLR => {
                warn!("invalid read of RTCLR register");
                0
            }
            RTCCR => get_epoch_time() - self.counter_delta_time,
            AMBA_ID_OFFSET => PL030_AMBA_ID,
            AMBA_MASK_OFFSET => PL030_AMBA_MASK,

            o => panic!("pl030: bad read offset {}", o),
        };
        *data_array = reg_content.to_ne_bytes();
    }
}
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_interrupt_status_register() {
        let event = Event::new().unwrap();
        let mut device = Pl030::new(event.try_clone().unwrap());
        let mut register = [0, 0, 0, 0];

        // set interrupt
        device.write(RTCEOI, &[1, 0, 0, 0]);
        device.read(RTCSTAT, &mut register);
        assert_eq!(register, [1, 0, 0, 0]);
        assert_eq!(event.read().unwrap(), 1);

        // clear interrupt
        device.write(RTCEOI, &[0, 0, 0, 0]);
        device.read(RTCSTAT, &mut register);
        assert_eq!(register, [0, 0, 0, 0]);
    }

    #[test]
    fn test_match_register() {
        let mut device = Pl030::new(Event::new().unwrap());
        let mut register = [0, 0, 0, 0];

        device.write(RTCMR, &[1, 2, 3, 4]);
        device.read(RTCMR, &mut register);
        assert_eq!(register, [1, 2, 3, 4]);
    }
}
<|endoftext|>


<commit_before>#![feature(asm, core_intrinsics, lang_items, naked_functions)]
#![no_std]

mod gpio;
mod watchdog;

use core::intrinsics::{volatile_store, volatile_load};

const GPFSEL2: u32 = 0x3F20_0008;
const GPSET0: u32 =  0x3F20_001C;
const GPCLR0: u32 =  0x3F20_0028;

const GPIO20: u32 = 1 << 20;
const GPIO21: u32 = 1 << 21;
const GPIO22: u32 = 1 << 22;

const IRQ_BASIC_ENABLE: u32 =  0x3F00_B218;

const ARM_TIMER_LOD: u32 = 0x3F00_B400;
const ARM_TIMER_CTL: u32 = 0x3F00_B408;
const ARM_TIMER_CLI: u32 = 0x3F00_B40C;
const ARM_TIMER_RLD: u32 = 0x3F00_B418;
const ARM_TIMER_DIV: u32 = 0x3F00_B41C;

const SHORT_TIMEOUT: u32 = 500_000;

static mut STATE_COUNTER: u32 = 0;

extern {
    fn enable_irq();
}

#[naked]
#[no_mangle]
pub extern fn rust_main() {
    gpio::write_register(ARM_TIMER_LOD, SHORT_TIMEOUT - 1);
    gpio::write_register(ARM_TIMER_RLD, SHORT_TIMEOUT - 1);
    // // Set the timer pre-divider to 0xF9. System clock freq (~250MHz) / 0xF9 = ~1 million ticks/sec
    gpio::write_register(ARM_TIMER_DIV, 0x0000_00F9);
    gpio::write_register(ARM_TIMER_CLI, 0);
    gpio::write_register(ARM_TIMER_CTL, 0x003E_00A2);

    gpio::write_register(IRQ_BASIC_ENABLE, 0x1);

    initialize_leds();

    unsafe { enable_irq(); }

    watchdog::start(0x000F_FFFF);

    loop {
        if watchdog::remaining_time() < 0x67697 {
            gpio::write_register(GPSET0, GPIO22);
            break;
        }
    }

    loop {}
}

#[naked]
#[no_mangle]
pub extern fn rust_irq_handler() {
    let state_counter = unsafe { volatile_load::<u32>(&STATE_COUNTER as *const u32 as *mut u32) };

    if state_counter & 0x1 == 0 {
        gpio::write_register(GPSET0, GPIO20);
        gpio::write_register(GPCLR0, GPIO21);
    } else {
        gpio::write_register(GPCLR0, GPIO20);
        gpio::write_register(GPSET0, GPIO21);
    }

    unsafe {
        volatile_store(&STATE_COUNTER as *const u32 as *mut u32, state_counter.wrapping_add(1));
    };

    gpio::write_register(ARM_TIMER_CLI, 0);
}

fn initialize_leds() {
    let mut gpfsel2_val = gpio::read_register(GPFSEL2);
    gpfsel2_val &= !0x1FF; // mask out other GPIO pins
    gpfsel2_val |= 0x49; // 0b01001001
    gpio::write_register(GPFSEL2, gpfsel2_val);
}

#[no_mangle]
pub unsafe fn __aeabi_unwind_cpp_pr0() {
    loop {}
}

#[lang = "eh_personality"]
extern fn eh_personality() {}

#[lang = "panic_fmt"]
extern fn panic_fmt() -> ! {
    loop {}
}
<commit_msg>Remove 'naked' annotations<commit_after>#![feature(asm, core_intrinsics, lang_items, naked_functions)]
#![no_std]

mod gpio;
mod watchdog;

use core::intrinsics::{volatile_store, volatile_load};

const GPFSEL2: u32 = 0x3F20_0008;
const GPSET0: u32 =  0x3F20_001C;
const GPCLR0: u32 =  0x3F20_0028;

const GPIO20: u32 = 1 << 20;
const GPIO21: u32 = 1 << 21;
const GPIO22: u32 = 1 << 22;

const IRQ_BASIC_ENABLE: u32 =  0x3F00_B218;

const ARM_TIMER_LOD: u32 = 0x3F00_B400;
const ARM_TIMER_CTL: u32 = 0x3F00_B408;
const ARM_TIMER_CLI: u32 = 0x3F00_B40C;
const ARM_TIMER_RLD: u32 = 0x3F00_B418;
const ARM_TIMER_DIV: u32 = 0x3F00_B41C;

const SHORT_TIMEOUT: u32 = 500_000;

static mut STATE_COUNTER: u32 = 0;

extern {
    fn enable_irq();
}

#[no_mangle]
pub extern fn rust_main() {
    gpio::write_register(ARM_TIMER_LOD, SHORT_TIMEOUT - 1);
    gpio::write_register(ARM_TIMER_RLD, SHORT_TIMEOUT - 1);
    // // Set the timer pre-divider to 0xF9. System clock freq (~250MHz) / 0xF9 = ~1 million ticks/sec
    gpio::write_register(ARM_TIMER_DIV, 0x0000_00F9);
    gpio::write_register(ARM_TIMER_CLI, 0);
    gpio::write_register(ARM_TIMER_CTL, 0x003E_00A2);

    gpio::write_register(IRQ_BASIC_ENABLE, 0x1);

    initialize_leds();

    unsafe { enable_irq(); }

    watchdog::start(0x000F_FFFF);

    loop {
        if watchdog::remaining_time() < 0x67697 {
            gpio::write_register(GPSET0, GPIO22);
            break;
        }
    }

    loop {}
}

#[no_mangle]
pub extern fn rust_irq_handler() {
    let state_counter = unsafe { volatile_load::<u32>(&STATE_COUNTER as *const u32 as *mut u32) };

    if state_counter & 0x1 == 0 {
        gpio::write_register(GPSET0, GPIO20);
        gpio::write_register(GPCLR0, GPIO21);
    } else {
        gpio::write_register(GPCLR0, GPIO20);
        gpio::write_register(GPSET0, GPIO21);
    }

    unsafe {
        volatile_store(&STATE_COUNTER as *const u32 as *mut u32, state_counter.wrapping_add(1));
    }

    gpio::write_register(ARM_TIMER_CLI, 0);
}

fn initialize_leds() {
    let mut gpfsel2_val = gpio::read_register(GPFSEL2);
    gpfsel2_val &= !0x1FF; // mask out other GPIO pins
    gpfsel2_val |= 0x49; // 0b01001001
    gpio::write_register(GPFSEL2, gpfsel2_val);
}

#[no_mangle]
pub unsafe fn __aeabi_unwind_cpp_pr0() {
    loop {}
}

#[lang = "eh_personality"]
extern fn eh_personality() {}

#[lang = "panic_fmt"]
extern fn panic_fmt() -> ! {
    loop {}
}
<|endoftext|>


<commit_before>// use to create a .so/.dll
#![crate_type = "dylib"]

extern crate libc;
extern crate collections;
use libc::types::common::c95::c_void;
use std::mem::transmute;
use std::collections::HashMap;
use collections::hash::sip::SipHasher;

#[no_mangle]
pub extern fn create_hash() -> *mut c_void {
    // SipHasher at the opposite of RandomSipHasher does not require
    // rust runtime to work, enabling use to use HashMap without runtime
    // too
    let hasher = SipHasher::new();
    let mut english_to_french = box HashMap::with_hasher(hasher);
    english_to_french.insert("one".to_string(), "un".to_string());
    english_to_french.insert("two".to_string(), "deux".to_string());

    return unsafe { transmute(english_to_french) };
}

#[no_mangle]
pub extern fn print_hash(english_to_french_ptr: *mut c_void) {
    let english_to_french : Box<HashMap<String, String>> = unsafe { transmute(english_to_french_ptr) };
    for (english, french) in english_to_french.iter() {
        println!(
            "in french '{}' is '{}'",
            *english,
            *french
        );
    }

}
<commit_msg>removed use of transmute and C void pointer in rust code of the HashMap example<commit_after>// we need this one to be able to change the default hasher
// in the type declaration of our hashmap
#![feature(default_type_params)]
// use to create a .so/.dll
#![crate_type = "dylib"]

extern crate collections;
use std::collections::HashMap;
use collections::hash::sip::SipHasher;

#[no_mangle]
pub extern fn create_hash() -> Box<HashMap<String, String, SipHasher>> {
    // SipHasher at the opposite of RandomSipHasher does not require
    // rust runtime to work, enabling use to use HashMap without runtime
    // too
    let hasher = SipHasher::new();
    let mut english_to_french = box HashMap::with_hasher(hasher);
    english_to_french.insert("one".to_string(), "un".to_string());
    english_to_french.insert("two".to_string(), "deux".to_string());

    english_to_french
}

#[no_mangle]
pub extern fn print_hash(english_to_french: Box<HashMap<String, String, SipHasher>>) {
    for (english, french) in english_to_french.iter() {
        println!(
            "in french '{}' is '{}'",
            *english,
            *french
        );
    }

}
<|endoftext|>


<commit_before>// vim: tw=80
use super::*;
use std::collections::HashMap;
use syn::parse::{Parse, ParseStream};

/// A single automock attribute
// This enum is very short-lived, so it's fine not to box it.
#[allow(clippy::large_enum_variant)]
enum Attr {
    Mod(ItemMod),
    Type(TraitItemType),
}

impl Parse for Attr {
    fn parse(input: ParseStream) -> parse::Result<Self> {
        let lookahead = input.lookahead1();
        if lookahead.peek(Token![mod]) {
            input.parse().map(Attr::Mod)
        } else if lookahead.peek(Token![type]) {
            input.parse().map(Attr::Type)
        } else {
            Err(lookahead.error())
        }
    }
}

/// automock attributes
#[derive(Debug, Default)]
pub(crate) struct Attrs {
    pub attrs: HashMap<Ident, Type>,
    pub modname: Option<Ident>
}

impl Attrs {
    fn get_path(&self, path: &Path) -> Option<Type> {
        if path.leading_colon.is_none() & (path.segments.len() == 2) {
            if path.segments.first().unwrap().ident == "Self" {
                let ident = &path.segments.last().unwrap().ident;
                self.attrs.get(ident).cloned()
            } else {
                None
            }
        } else {
            None
        }
    }

    fn substitute_path_segment(&self, seg: &mut PathSegment) {
        match &mut seg.arguments {
            PathArguments::None => /* nothing to do */(),
            PathArguments::Parenthesized(p) => {
                compile_error(p.span(),
                    "Mockall does not support mocking Fn objects");
            },
            PathArguments::AngleBracketed(abga) => {
                for arg in abga.args.iter_mut() {
                    match arg {
                        GenericArgument::Type(ty) => {
                            self.substitute_type(ty)
                        },
                        GenericArgument::Binding(binding) => {
                            self.substitute_type(&mut binding.ty);
                        },
                        _ => {
                            /*
                             * Nothing to do, as long as lifetimes can't be
                             * associated types
                             */
                        }
                    }
                }
            },
        }
    }

    /// Recursively substitute types in the input
    fn substitute_type(&self, ty: &mut Type) {
        match ty {
            Type::Slice(s) => {
                self.substitute_type(s.elem.as_mut())
            },
            Type::Array(a) => {
                self.substitute_type(a.elem.as_mut())
            },
            Type::Ptr(p) => {
                self.substitute_type(p.elem.as_mut())
            },
            Type::Reference(r) => {
                self.substitute_type(r.elem.as_mut())
            },
            Type::BareFn(bfn) => {
                for fn_arg in bfn.inputs.iter_mut() {
                    self.substitute_type(&mut fn_arg.ty);
                }
                if let ReturnType::Type(_, ref mut ty) = &mut bfn.output {
                    self.substitute_type(ty);
                }
            },
            Type::Tuple(tuple) => {
                for elem in tuple.elems.iter_mut() {
                    self.substitute_type(elem)
                }
            }
            Type::Path(path) => {
                if let Some(ref qself) = path.qself {
                    let qp = if let Type::Path(p) = qself.ty.as_ref() {
                        &p.path
                    } else {
                        panic!("QSelf's type isn't a path?")
                    };
                    let qident = &qp.segments.first().unwrap().ident;
                    if qself.position != 1
                        || qp.segments.len() != 1
                        || path.path.segments.len() != 2
                        || qident != "Self" {
                        compile_error(path.span(),
                            "QSelf is a work in progress");
                    }
                    let last_seg = path.path.segments.pop().unwrap();
                    let to_sub = &last_seg.value().ident;
                    let _ident = path.path.segments.pop().unwrap().value();
                    // TODO: check that the ident is the name of this type
                    let new_type = self.attrs.get(to_sub)
                        .expect("Unknown type substitution for QSelf");
                    *ty = new_type.clone();
                } else if let Some(newty) = self.get_path(&path.path) {
                    *ty = newty;
                } else {
                    for seg in path.path.segments.iter_mut() {
                        self.substitute_path_segment(seg);
                    }
                }
            },
            Type::TraitObject(to) => {
                for bound in to.bounds.iter_mut() {
                    self.substitute_type_param_bound(bound);
                }
            },
            Type::ImplTrait(it) => {
                for bound in it.bounds.iter_mut() {
                    self.substitute_type_param_bound(bound);
                }
            },
            Type::Paren(p) => {
                self.substitute_type(p.elem.as_mut())
            },
            Type::Group(g) => {
                self.substitute_type(g.elem.as_mut())
            },
            Type::Macro(_) | Type::Verbatim(_) => {
                compile_error(ty.span(),
                    "mockall_derive does not support this type when using associated types");
            },
            Type::Infer(_) | Type::Never(_) => {
                /* Nothing to do */
            },
            _ => compile_error(ty.span(), "Unsupported type"),
        }
    }

    fn substitute_type_param_bound(&self, bound: &mut TypeParamBound) {
        if let TypeParamBound::Trait(t) = bound {
            match self.get_path(&t.path) {
                None => {
                    for seg in t.path.segments.iter_mut() {
                        self.substitute_path_segment(seg);
                    }
                },
                Some(Type::Path(type_path)) => {
                    t.path = type_path.path;
                },
                Some(_) => {
                    compile_error(t.path.span(),
                        "Can only substitute paths for trait bounds");
                }
            }
        }
    }

    pub(crate) fn substitute_trait(&self, item: &ItemTrait) -> ItemTrait {
        let mut output = item.clone();
        for trait_item in output.items.iter_mut() {
            match trait_item {
                TraitItem::Type(tity) => {
                    if let Some(ty) = self.attrs.get(&tity.ident) {
                        let span = tity.span();
                        tity.default = Some((Token![=](span), ty.clone()));
                        // Concrete associated types aren't allowed to have
                        // bounds
                        tity.bounds = Punctuated::new();
                    } else {
                        compile_error(tity.span(),
                            "Default value not given for associated type");
                    }
                },
                TraitItem::Method(method) => {
                    let sig = &mut method.sig;
                    for fn_arg in sig.inputs.iter_mut() {
                        if let FnArg::Typed(arg) = fn_arg {
                            self.substitute_type(&mut arg.ty);
                        }
                    }
                    if let ReturnType::Type(_, ref mut ty) = &mut sig.output {
                        self.substitute_type(ty);
                    }
                },
                _ => {
                    // Nothing to do
                }
            }
        }
        output
    }
}

impl Parse for Attrs {
    fn parse(input: ParseStream) -> parse::Result<Self> {
        let mut attrs = HashMap::new();
        let mut modname = None;
        while !input.is_empty() {
            let attr: Attr = input.parse()?;
            match attr {
                Attr::Mod(item_mod) => {
                    if let Some((br, _)) = item_mod.content {
                        compile_error(br.span,
                            "mod name attributes must have the form \"mod my_name;\"");
                    }
                    modname = Some(item_mod.ident.clone());
                },
                Attr::Type(trait_item_type) => {
                    let ident = trait_item_type.ident.clone();
                    if let Some((_, ty)) = trait_item_type.default {
                        attrs.insert(ident, ty.clone());
                    } else {
                        compile_error(trait_item_type.span(),
                          "automock type attributes must have a default value");
                    }
                }
            }
        }
        Ok(Attrs{attrs, modname})
    }
}

/// Unit tests for `Attrs`.
#[cfg(test)]
mod t {
    use super::super::*;
    use pretty_assertions::assert_eq;

    fn check_substitute_type(attrs: TokenStream, input: TokenStream,
        expected: TokenStream)
    {
        let _self: super::Attrs = parse2(attrs).unwrap();
        let mut in_ty: Type = parse2(input).unwrap();
        let expect_ty: Type = parse2(expected).unwrap();
        _self.substitute_type(&mut in_ty);
        assert_eq!(in_ty, expect_ty);
    }

    #[test]
    fn qself() {
        check_substitute_type(quote!(type T = u32;),
                              quote!(<Self as Foo>::T),
                              quote!(u32));
    }
}
<commit_msg>Print an appropriate error when using QSelf in an unsupported way<commit_after>// vim: tw=80
use super::*;
use std::collections::HashMap;
use syn::parse::{Parse, ParseStream};

/// A single automock attribute
// This enum is very short-lived, so it's fine not to box it.
#[allow(clippy::large_enum_variant)]
enum Attr {
    Mod(ItemMod),
    Type(TraitItemType),
}

impl Parse for Attr {
    fn parse(input: ParseStream) -> parse::Result<Self> {
        let lookahead = input.lookahead1();
        if lookahead.peek(Token![mod]) {
            input.parse().map(Attr::Mod)
        } else if lookahead.peek(Token![type]) {
            input.parse().map(Attr::Type)
        } else {
            Err(lookahead.error())
        }
    }
}

/// automock attributes
#[derive(Debug, Default)]
pub(crate) struct Attrs {
    pub attrs: HashMap<Ident, Type>,
    pub modname: Option<Ident>
}

impl Attrs {
    fn get_path(&self, path: &Path) -> Option<Type> {
        if path.leading_colon.is_none() & (path.segments.len() == 2) {
            if path.segments.first().unwrap().ident == "Self" {
                let ident = &path.segments.last().unwrap().ident;
                self.attrs.get(ident).cloned()
            } else {
                None
            }
        } else {
            None
        }
    }

    fn substitute_path_segment(&self, seg: &mut PathSegment, traitname: &Ident)
    {
        match &mut seg.arguments {
            PathArguments::None => /* nothing to do */(),
            PathArguments::Parenthesized(p) => {
                compile_error(p.span(),
                    "Mockall does not support mocking Fn objects");
            },
            PathArguments::AngleBracketed(abga) => {
                for arg in abga.args.iter_mut() {
                    match arg {
                        GenericArgument::Type(ty) => {
                            self.substitute_type(ty, traitname)
                        },
                        GenericArgument::Binding(binding) => {
                            self.substitute_type(&mut binding.ty, traitname);
                        },
                        _ => {
                            /*
                             * Nothing to do, as long as lifetimes can't be
                             * associated types
                             */
                        }
                    }
                }
            },
        }
    }

    /// Recursively substitute types in the input
    fn substitute_type(&self, ty: &mut Type, traitname: &Ident) {
        match ty {
            Type::Slice(s) => {
                self.substitute_type(s.elem.as_mut(), traitname)
            },
            Type::Array(a) => {
                self.substitute_type(a.elem.as_mut(), traitname)
            },
            Type::Ptr(p) => {
                self.substitute_type(p.elem.as_mut(), traitname)
            },
            Type::Reference(r) => {
                self.substitute_type(r.elem.as_mut(), traitname)
            },
            Type::BareFn(bfn) => {
                for fn_arg in bfn.inputs.iter_mut() {
                    self.substitute_type(&mut fn_arg.ty, traitname);
                }
                if let ReturnType::Type(_, ref mut ty) = &mut bfn.output {
                    self.substitute_type(ty, traitname);
                }
            },
            Type::Tuple(tuple) => {
                for elem in tuple.elems.iter_mut() {
                    self.substitute_type(elem, traitname)
                }
            }
            Type::Path(path) => {
                if let Some(ref qself) = path.qself {
                    let qp = if let Type::Path(p) = qself.ty.as_ref() {
                        &p.path
                    } else {
                        panic!("QSelf's type isn't a path?")
                    };
                    let qident = &qp.segments.first().unwrap().ident;
                    if qself.position != 1
                        || qp.segments.len() != 1
                        || path.path.segments.len() != 2
                        || qident != "Self" {
                        compile_error(path.span(),
                            "QSelf is a work in progress");
                    }
                    let last_seg = path.path.segments.pop().unwrap();
                    let to_sub = &last_seg.value().ident;
                    let penultimate_seg = path.path.segments.pop().unwrap();
                    let qident = &penultimate_seg.value().ident;
                    if qident != traitname {
                        compile_error(qident.span(),
                            "Mockall does not support QSelf substitutions except for the trait being mocked");
                    }
                    let new_type = self.attrs.get(to_sub)
                        .expect("Unknown type substitution for QSelf");
                    *ty = new_type.clone();
                } else if let Some(newty) = self.get_path(&path.path) {
                    *ty = newty;
                } else {
                    for seg in path.path.segments.iter_mut() {
                        self.substitute_path_segment(seg, &traitname);
                    }
                }
            },
            Type::TraitObject(to) => {
                for bound in to.bounds.iter_mut() {
                    self.substitute_type_param_bound(bound, traitname);
                }
            },
            Type::ImplTrait(it) => {
                for bound in it.bounds.iter_mut() {
                    self.substitute_type_param_bound(bound, traitname);
                }
            },
            Type::Paren(p) => {
                self.substitute_type(p.elem.as_mut(), traitname)
            },
            Type::Group(g) => {
                self.substitute_type(g.elem.as_mut(), traitname)
            },
            Type::Macro(_) | Type::Verbatim(_) => {
                compile_error(ty.span(),
                    "mockall_derive does not support this type when using associated types");
            },
            Type::Infer(_) | Type::Never(_) => {
                /* Nothing to do */
            },
            _ => compile_error(ty.span(), "Unsupported type"),
        }
    }

    fn substitute_type_param_bound(&self,
                                   bound: &mut TypeParamBound,
                                   traitname: &Ident)
    {
        if let TypeParamBound::Trait(t) = bound {
            match self.get_path(&t.path) {
                None => {
                    for seg in t.path.segments.iter_mut() {
                        self.substitute_path_segment(seg, &traitname);
                    }
                },
                Some(Type::Path(type_path)) => {
                    t.path = type_path.path;
                },
                Some(_) => {
                    compile_error(t.path.span(),
                        "Can only substitute paths for trait bounds");
                }
            }
        }
    }

    pub(crate) fn substitute_trait(&self, item: &ItemTrait) -> ItemTrait {
        let mut output = item.clone();
        for trait_item in output.items.iter_mut() {
            match trait_item {
                TraitItem::Type(tity) => {
                    if let Some(ty) = self.attrs.get(&tity.ident) {
                        let span = tity.span();
                        tity.default = Some((Token![=](span), ty.clone()));
                        // Concrete associated types aren't allowed to have
                        // bounds
                        tity.bounds = Punctuated::new();
                    } else {
                        compile_error(tity.span(),
                            "Default value not given for associated type");
                    }
                },
                TraitItem::Method(method) => {
                    let sig = &mut method.sig;
                    for fn_arg in sig.inputs.iter_mut() {
                        if let FnArg::Typed(arg) = fn_arg {
                            self.substitute_type(&mut arg.ty, &item.ident);
                        }
                    }
                    if let ReturnType::Type(_, ref mut ty) = &mut sig.output {
                        self.substitute_type(ty, &item.ident);
                    }
                },
                _ => {
                    // Nothing to do
                }
            }
        }
        output
    }
}

impl Parse for Attrs {
    fn parse(input: ParseStream) -> parse::Result<Self> {
        let mut attrs = HashMap::new();
        let mut modname = None;
        while !input.is_empty() {
            let attr: Attr = input.parse()?;
            match attr {
                Attr::Mod(item_mod) => {
                    if let Some((br, _)) = item_mod.content {
                        compile_error(br.span,
                            "mod name attributes must have the form \"mod my_name;\"");
                    }
                    modname = Some(item_mod.ident.clone());
                },
                Attr::Type(trait_item_type) => {
                    let ident = trait_item_type.ident.clone();
                    if let Some((_, ty)) = trait_item_type.default {
                        attrs.insert(ident, ty.clone());
                    } else {
                        compile_error(trait_item_type.span(),
                          "automock type attributes must have a default value");
                    }
                }
            }
        }
        Ok(Attrs{attrs, modname})
    }
}

/// Unit tests for `Attrs`.
#[cfg(test)]
mod t {
    use super::super::*;
    use pretty_assertions::assert_eq;

    fn check_substitute_type(
        attrs: TokenStream,
        input: TokenStream,
        traitname: Ident,
        expected: TokenStream)
    {
        let _self: super::Attrs = parse2(attrs).unwrap();
        let mut in_ty: Type = parse2(input).unwrap();
        let expect_ty: Type = parse2(expected).unwrap();
        _self.substitute_type(&mut in_ty, &traitname);
        assert_eq!(in_ty, expect_ty);
    }

    #[test]
    fn qself() {
        check_substitute_type(quote!(type T = u32;),
                              quote!(<Self as Foo>::T),
                              format_ident!("Foo"),
                              quote!(u32));
    }

    #[test]
    #[should_panic(expected = "Mockall does not support QSelf substitutions except for the trait being mocked")]
    fn qself_other() {
        check_substitute_type(quote!(type T = u32;),
                              quote!(<Self as AsRef>::T),
                              format_ident!("Foo"),
                              quote!(u32));
    }
}
<|endoftext|>


<commit_before>use datum::*;
use primitive::*;
use numeric::LNumeric;
use core::hashmap::linear::LinearMap;
use core::num::Zero::zero;
use core::num::One::one;

struct Runtime {
    stdin: @io::Reader,
    stdout: @io::Writer,
    stderr: @io::Writer,
    global: LinearMap<~str, @LDatum>,
    syntax: LinearMap<~str, PrimSyntax>,
}

#[deriving(Eq)]
enum RuntimeError {
    UnboundVariable(~str),
    NotCallable,
    NotList,
    ArgNumError,
    TypeError,
    DivideByZeroError,
    NilEval,
}

impl ToStr for RuntimeError {
    fn to_str(&self) -> ~str {
        err_to_str(self)
    }
}

priv fn err_to_str(&err: &RuntimeError) -> ~str {
    match err {
        UnboundVariable(name) => ~"unbound variable: " + copy name,
        NotCallable => ~"not callable",
        NotList => ~"not list",
        ArgNumError => ~"bad number of arguments",
        TypeError => ~"type error",
        DivideByZeroError => ~"divide by zero",
        NilEval => ~"() cannot be evaluated",
    }
}

fn load_prelude() -> LinearMap<~str, @LDatum> {
    let mut map = LinearMap::new();
    for vec::each(prelude()) |&pair| {
        let (key, func) = pair;
        map.insert(key, @LPrim(func));
    }
    map
}

fn load_prelude_macro() -> LinearMap<~str, PrimSyntax> {
    let mut map = LinearMap::new();
    for vec::each(syntax_prelude()) |&pair| {
        let (key, syntax) = pair;
        map.insert(key, syntax);
    }
    map
}

priv fn call_prim1(args: &[@LDatum],
                op: &fn(@LDatum) -> Result<@LDatum, RuntimeError>)
    -> Result<@LDatum, RuntimeError>
{
    if args.len() == 1 {
        op(args[0])
    } else {
        Err(ArgNumError)
    }
}

priv fn call_num_prim2(args: &[@LDatum],
                    op: &fn(&LNumeric, &LNumeric) -> Result<@LDatum, RuntimeError>)
    -> Result<@LDatum, RuntimeError>
{
    if args.len() == 2 {
        match *args[0] {
            LNum(lhs) => match *args[1] {
                LNum(rhs) => op(&lhs, &rhs),
                _ => Err(TypeError),
            },
            _ => Err(TypeError),
        }
    } else {
        Err(ArgNumError)
    }
}

priv fn call_num_foldl(args: &[@LDatum],
                    a0: LNumeric,
                    op: &fn(&LNumeric, &LNumeric) -> Result<LNumeric, RuntimeError>)
    -> Result<@LDatum, RuntimeError>
{
    let mut res = a0;
    let mut err = false;
    do args.each |&arg| {
        match *arg {
            LNum(a) => {
                match op(&res, &a) {
                    Ok(n) => {
                        res = n;
                        err = false;
                    },
                    _ => {
                        err = true;
                    }
                }
            },
            _ => {
                err = true;
            }
        }
        !err
    }
    if err {
        Err(TypeError)
    } else {
        Ok(@LNum(res))
    }
}

priv impl Runtime {
    fn get_syntax(&self, val: @LDatum) -> Option<PrimSyntax> {
        match copy *val {
            LIdent(name) => match self.syntax.find(&name) {
                Some(syn) => Some(*syn),
                None => None,
            },
            _ => None,
        }
    }

    fn run_syntax(&mut self, syn: PrimSyntax, args: &[@LDatum]) -> Result<@LDatum, RuntimeError> {
        match syn {
            SynIf => if args.len() == 3 {
                    do result::chain(self.eval(args[0])) |cond| {
                        match *cond {
                            LBool(true) => self.eval(args[1]),
                            LBool(false) => self.eval(args[2]),
                            _ => Err(TypeError),
                        }
                    }
                } else {
                    Err(ArgNumError)
                },
        }
    }

    fn call_prim(&mut self, f: PFunc, args: &[@LDatum]) -> Result<@LDatum, RuntimeError> {
        match f {
            PEval => do call_prim1(args) |arg| {
                self.eval(arg)
            },
            PAdd => do call_num_foldl(args, zero()) |&lhs, &rhs| { Ok(lhs + rhs) },
            PSub => do call_num_foldl(args, one()) |&lhs, &rhs| { Ok(lhs - rhs) },
            PMul => do call_num_foldl(args, one()) |&lhs, &rhs| { Ok(lhs * rhs) },
            PDiv => do call_num_foldl(args, one()) |&lhs, &rhs| {
                if rhs.is_zero() {
                    Err(DivideByZeroError)
                } else {
                    Ok(lhs / rhs)
                }
            },
            PCar => do call_prim1(args) |arg| {
                match *arg {
                    LCons(h, _) => Ok(h),
                    _ => Err(TypeError),
                }
            },
            PCdr => do call_prim1(args) |arg| {
                match *arg {
                    LCons(_, t) => Ok(t),
                    _ => Err(TypeError),
                }
            },
        }
    }
}

pub impl Runtime {
    fn new_std() -> Runtime {
        Runtime {
            stdin: io::stdin(),
            stdout: io::stdout(),
            stderr: io::stderr(),
            global: load_prelude(),
            syntax: load_prelude_macro(),
        }
    }

    fn eval(&mut self, val: @LDatum) -> Result<@LDatum, RuntimeError> {
        match copy *val {
            LIdent(name) =>
                match self.global.find(&name) {
                    Some(&datum) => Ok(datum),
                    None => Err(UnboundVariable(name)),
                },
            LCons(fexpr, aexpr) =>
                match aexpr.to_list() {
                    None => Err(NotList),
                    Some(aexprs) => {
                        match self.get_syntax(fexpr) {
                            Some(syntax) =>
                                self.run_syntax(syntax, aexprs),
                            None =>
                                match self.eval(fexpr) {
                                    Ok(@LPrim(f)) => {
                                        match result::map_vec(aexprs, |&expr| self.eval(expr)) {
                                            Ok(args) => self.call_prim(f, args),
                                            Err(e) => Err(e),
                                        }
                                    }
                                    Ok(_) => Err(NotCallable),
                                    Err(e) => Err(e),
                                },
                        }
                    },
                },
            LQuote(val) => Ok(val),
            LNil => Err(NilEval),
            _ => Ok(val),
        }
    }

}
<commit_msg>change some argument ownership<commit_after>use datum::*;
use primitive::*;
use numeric::LNumeric;
use core::hashmap::linear::LinearMap;
use core::num::Zero::zero;
use core::num::One::one;

struct Runtime {
    stdin: @io::Reader,
    stdout: @io::Writer,
    stderr: @io::Writer,
    global: LinearMap<~str, @LDatum>,
    syntax: LinearMap<~str, PrimSyntax>,
}

#[deriving(Eq)]
enum RuntimeError {
    UnboundVariable(~str),
    NotCallable,
    NotList,
    ArgNumError,
    TypeError,
    DivideByZeroError,
    NilEval,
}

impl ToStr for RuntimeError {
    fn to_str(&self) -> ~str {
        err_to_str(self)
    }
}

priv fn err_to_str(&err: &RuntimeError) -> ~str {
    match err {
        UnboundVariable(name) => ~"unbound variable: " + copy name,
        NotCallable => ~"not callable",
        NotList => ~"not list",
        ArgNumError => ~"bad number of arguments",
        TypeError => ~"type error",
        DivideByZeroError => ~"divide by zero",
        NilEval => ~"() cannot be evaluated",
    }
}

fn load_prelude() -> LinearMap<~str, @LDatum> {
    let mut map = LinearMap::new();
    for vec::each(prelude()) |&pair| {
        let (key, func) = pair;
        map.insert(key, @LPrim(func));
    }
    map
}

fn load_prelude_macro() -> LinearMap<~str, PrimSyntax> {
    let mut map = LinearMap::new();
    for vec::each(syntax_prelude()) |&pair| {
        let (key, syntax) = pair;
        map.insert(key, syntax);
    }
    map
}

priv fn call_prim1(args: ~[@LDatum],
                op: &fn(@LDatum) -> Result<@LDatum, RuntimeError>)
    -> Result<@LDatum, RuntimeError>
{
    if args.len() == 1 {
        op(args[0])
    } else {
        Err(ArgNumError)
    }
}

priv fn call_num_prim2(args: ~[@LDatum],
                    op: &fn(&LNumeric, &LNumeric) -> Result<@LDatum, RuntimeError>)
    -> Result<@LDatum, RuntimeError>
{
    if args.len() == 2 {
        match *args[0] {
            LNum(lhs) => match *args[1] {
                LNum(rhs) => op(&lhs, &rhs),
                _ => Err(TypeError),
            },
            _ => Err(TypeError),
        }
    } else {
        Err(ArgNumError)
    }
}

priv fn call_num_foldl(args: ~[@LDatum],
                    a0: LNumeric,
                    op: &fn(&LNumeric, &LNumeric) -> Result<LNumeric, RuntimeError>)
    -> Result<@LDatum, RuntimeError>
{
    let mut res = a0;
    let mut err = false;
    do args.each |&arg| {
        match *arg {
            LNum(a) => {
                match op(&res, &a) {
                    Ok(n) => {
                        res = n;
                        err = false;
                    },
                    _ => {
                        err = true;
                    }
                }
            },
            _ => {
                err = true;
            }
        }
        !err
    }
    if err {
        Err(TypeError)
    } else {
        Ok(@LNum(res))
    }
}

priv impl Runtime {
    fn get_syntax(&self, val: @LDatum) -> Option<PrimSyntax> {
        match copy *val {
            LIdent(name) => match self.syntax.find(&name) {
                Some(syn) => Some(*syn),
                None => None,
            },
            _ => None,
        }
    }

    fn run_syntax(&mut self, syn: PrimSyntax, args: ~[@LDatum]) -> Result<@LDatum, RuntimeError> {
        match syn {
            SynIf => if args.len() == 3 {
                    do result::chain(self.eval(args[0])) |cond| {
                        match *cond {
                            LBool(true) => self.eval(args[1]),
                            LBool(false) => self.eval(args[2]),
                            _ => Err(TypeError),
                        }
                    }
                } else {
                    Err(ArgNumError)
                },
        }
    }

    fn call_prim(&mut self, f: PFunc, args: ~[@LDatum]) -> Result<@LDatum, RuntimeError> {
        match f {
            PEval => do call_prim1(args) |arg| {
                self.eval(arg)
            },
            PAdd => do call_num_foldl(args, zero()) |&lhs, &rhs| { Ok(lhs + rhs) },
            PSub => do call_num_foldl(args, one()) |&lhs, &rhs| { Ok(lhs - rhs) },
            PMul => do call_num_foldl(args, one()) |&lhs, &rhs| { Ok(lhs * rhs) },
            PDiv => do call_num_foldl(args, one()) |&lhs, &rhs| {
                if rhs.is_zero() {
                    Err(DivideByZeroError)
                } else {
                    Ok(lhs / rhs)
                }
            },
            PCar => do call_prim1(args) |arg| {
                match *arg {
                    LCons(h, _) => Ok(h),
                    _ => Err(TypeError),
                }
            },
            PCdr => do call_prim1(args) |arg| {
                match *arg {
                    LCons(_, t) => Ok(t),
                    _ => Err(TypeError),
                }
            },
        }
    }
}

pub impl Runtime {
    fn new_std() -> Runtime {
        Runtime {
            stdin: io::stdin(),
            stdout: io::stdout(),
            stderr: io::stderr(),
            global: load_prelude(),
            syntax: load_prelude_macro(),
        }
    }

    fn eval(&mut self, val: @LDatum) -> Result<@LDatum, RuntimeError> {
        match copy *val {
            LIdent(name) =>
                match self.global.find(&name) {
                    Some(&datum) => Ok(datum),
                    None => Err(UnboundVariable(name)),
                },
            LCons(fexpr, aexpr) =>
                match aexpr.to_list() {
                    None => Err(NotList),
                    Some(aexprs) => {
                        match self.get_syntax(fexpr) {
                            Some(syntax) =>
                                self.run_syntax(syntax, aexprs),
                            None =>
                                match self.eval(fexpr) {
                                    Ok(@LPrim(f)) => {
                                        match result::map_vec(aexprs, |&expr| self.eval(expr)) {
                                            Ok(args) => self.call_prim(f, args),
                                            Err(e) => Err(e),
                                        }
                                    }
                                    Ok(_) => Err(NotCallable),
                                    Err(e) => Err(e),
                                },
                        }
                    },
                },
            LQuote(val) => Ok(val),
            LNil => Err(NilEval),
            _ => Ok(val),
        }
    }

}
<|endoftext|>


<commit_before>use crate::cpp_code_generator;
use crate::cpp_code_generator::generate_cpp_type_size_requester;
use crate::processor::ProcessingStep;
use crate::processor::ProcessorData;
use crate::rust_code_generator;
use crate::versions;
use pathdiff::diff_paths;
use ritual_common::errors::{err_msg, Result};
use ritual_common::file_utils::copy_file;
use ritual_common::file_utils::copy_recursively;
use ritual_common::file_utils::create_dir;
use ritual_common::file_utils::create_dir_all;
use ritual_common::file_utils::create_file;
use ritual_common::file_utils::path_to_str;
use ritual_common::file_utils::read_dir;
use ritual_common::file_utils::repo_crate_local_path;
use ritual_common::file_utils::save_json;
use ritual_common::file_utils::save_toml;
use ritual_common::toml;
use ritual_common::utils::run_command;
use ritual_common::utils::MapIfOk;
use ritual_common::BuildScriptData;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;

/// Merges `a` and `b` recursively. `b` take precedence over `a`.
fn recursive_merge_toml(a: toml::Value, b: toml::Value) -> toml::Value {
    if a.same_type(&b) {
        if let toml::Value::Array(mut a_array) = a {
            if let toml::Value::Array(mut b_array) = b {
                a_array.append(&mut b_array);
                toml::Value::Array(a_array)
            } else {
                unreachable!()
            }
        } else if let toml::Value::Table(mut a_table) = a {
            if let toml::Value::Table(b_table) = b {
                for (key, value) in b_table {
                    if let Some(old_value) = a_table.remove(&key) {
                        a_table.insert(key, recursive_merge_toml(old_value, value));
                    } else {
                        a_table.insert(key, value);
                    }
                }
                toml::Value::Table(a_table)
            } else {
                unreachable!()
            }
        } else {
            b
        }
    } else {
        b
    }
}

/// Generates `Cargo.toml` file and skeleton of the crate.
/// If a crate template was supplied, files from it are
/// copied to the output location.
fn generate_crate_template(data: &mut ProcessorData) -> Result<()> {
    let output_path = data
        .workspace
        .crate_path(data.config.crate_properties().name())?;

    let template_build_rs_path =
        data.config
            .crate_template_path()
            .as_ref()
            .and_then(|crate_template_path| {
                let template_build_rs_path = crate_template_path.join("build.rs");
                if template_build_rs_path.exists() {
                    Some(template_build_rs_path)
                } else {
                    None
                }
            });
    let output_build_rs_path = output_path.join("build.rs");
    if let Some(ref template_build_rs_path) = template_build_rs_path {
        copy_file(template_build_rs_path, output_build_rs_path)?;
    } else {
        let mut rustfmt_file = create_file(&output_build_rs_path)?;
        rustfmt_file.write(include_str!("../templates/crate/build.rs"))?;
    }
    let cargo_toml_data = {
        let package = toml::value::Value::Table({
            let mut table = toml::value::Table::new();
            table.insert(
                "name".to_string(),
                toml::Value::String(data.config.crate_properties().name().into()),
            );
            table.insert(
                "version".to_string(),
                toml::Value::String(data.config.crate_properties().version().into()),
            );
            table.insert(
                "build".to_string(),
                toml::Value::String("build.rs".to_string()),
            );
            table.insert(
                "edition".to_string(),
                toml::Value::String("2018".to_string()),
            );
            table
        });
        let dep_value = |version: &str, local_path: Option<PathBuf>| -> Result<toml::Value> {
            Ok(
                if local_path.is_none() || !data.workspace.config().write_dependencies_local_paths {
                    toml::Value::String(version.to_string())
                } else {
                    toml::Value::Table({
                        let mut value = toml::value::Table::new();
                        value.insert(
                            "version".to_string(),
                            toml::Value::String(version.to_string()),
                        );
                        value.insert(
                            "path".to_string(),
                            toml::Value::String(
                                path_to_str(&local_path.expect("checked above"))?.to_string(),
                            ),
                        );
                        value
                    })
                },
            )
        };
        let dependencies = toml::Value::Table({
            let mut table = toml::value::Table::new();
            if !data
                .config
                .crate_properties()
                .should_remove_default_dependencies()
            {
                table.insert(
                    "cpp_utils".to_string(),
                    dep_value(
                        versions::CPP_UTILS_VERSION,
                        if data.workspace.config().write_dependencies_local_paths {
                            Some(repo_crate_local_path("cpp_utils")?)
                        } else {
                            None
                        },
                    )?,
                );
                for dep in data.dep_databases {
                    let relative_path =
                        diff_paths(&data.workspace.crate_path(&dep.crate_name)?, &output_path)
                            .ok_or_else(|| {
                                err_msg("failed to get relative path to the dependency")
                            })?;

                    table.insert(
                        dep.crate_name.clone(),
                        dep_value(&dep.crate_version, Some(relative_path))?,
                    );
                }
            }
            for dep in data.config.crate_properties().dependencies() {
                table.insert(
                    dep.name().to_string(),
                    dep_value(dep.version(), dep.local_path().cloned())?,
                );
            }
            table
        });
        let build_dependencies = toml::Value::Table({
            let mut table = toml::value::Table::new();
            if !data
                .config
                .crate_properties()
                .should_remove_default_build_dependencies()
            {
                table.insert(
                    "ritual_build".to_string(),
                    dep_value(
                        versions::RITUAL_BUILD_VERSION,
                        if data.workspace.config().write_dependencies_local_paths {
                            Some(repo_crate_local_path("ritual_build")?)
                        } else {
                            None
                        },
                    )?,
                );
            }
            for dep in data.config.crate_properties().build_dependencies() {
                table.insert(
                    dep.name().to_string(),
                    dep_value(dep.version(), dep.local_path().cloned())?,
                );
            }
            table
        });
        let mut table = toml::value::Table::new();
        table.insert("package".to_string(), package);
        table.insert("dependencies".to_string(), dependencies);
        table.insert("build-dependencies".to_string(), build_dependencies);
        recursive_merge_toml(
            toml::Value::Table(table),
            toml::Value::Table(data.config.crate_properties().custom_fields().clone()),
        )
    };
    save_toml(output_path.join("Cargo.toml"), &cargo_toml_data)?;

    if let Some(ref template_path) = data.config.crate_template_path() {
        for item in read_dir(template_path)? {
            let item = item?;
            copy_recursively(&item.path(), &output_path.join(item.file_name()))?;
        }
    }
    if !output_path.join("src").exists() {
        create_dir_all(output_path.join("src"))?;
    }
    Ok(())
}

/// Generates main files and directories of the library.
fn generate_c_lib_template(
    lib_name: &str,
    lib_path: &Path,
    global_header_name: &str,
    include_directives: &[PathBuf],
) -> Result<()> {
    let name_upper = lib_name.to_uppercase();
    let cmakelists_path = lib_path.join("CMakeLists.txt");
    let mut cmakelists_file = create_file(&cmakelists_path)?;

    cmakelists_file.write(format!(
        include_str!("../templates/c_lib/CMakeLists.txt"),
        lib_name_lowercase = lib_name,
        lib_name_uppercase = name_upper
    ))?;

    let include_directives_code = include_directives
        .map_if_ok(|d| -> Result<_> { Ok(format!("#include \"{}\"", path_to_str(d)?)) })?
        .join("\n");

    let global_header_path = lib_path.join(&global_header_name);
    let mut global_header_file = create_file(&global_header_path)?;
    global_header_file.write(format!(
        include_str!("../templates/c_lib/global.h"),
        include_directives_code = include_directives_code
    ))?;
    Ok(())
}

fn run(data: &mut ProcessorData) -> Result<()> {
    generate_crate_template(data)?;

    let output_path = data
        .workspace
        .crate_path(data.config.crate_properties().name())?;

    let c_lib_path = output_path.join("c_lib");
    if !c_lib_path.exists() {
        create_dir(&c_lib_path)?;
    }
    let c_lib_name = format!("{}_c", data.config.crate_properties().name());
    let global_header_name = format!("{}_global.h", c_lib_name);
    generate_c_lib_template(
        &c_lib_name,
        &c_lib_path,
        &global_header_name,
        data.config.include_directives(),
    )?;

    cpp_code_generator::generate_cpp_file(
        &data.current_database.cpp_items,
        &c_lib_path.join("file1.cpp"),
        &global_header_name,
    )?;

    let file = create_file(c_lib_path.join("sized_types.cxx"))?;
    generate_cpp_type_size_requester(
        &data.current_database.rust_database,
        data.config.include_directives(),
        file,
    )?;

    rust_code_generator::generate(
        data.config.crate_properties().name(),
        &data.current_database.rust_database,
        &output_path.join("src"),
    )?;

    run_command(Command::new("cargo").arg("fmt").current_dir(&output_path))?;
    run_command(
        Command::new("rustfmt")
            .arg("src/ffi.in.rs")
            .current_dir(&output_path),
    )?;

    save_json(
        output_path.join("build_script_data.json"),
        &BuildScriptData {
            cpp_build_config: data.config.cpp_build_config().clone(),
            cpp_wrapper_lib_name: c_lib_name,
            cpp_lib_version: data.config.cpp_lib_version().map(|s| s.to_string()),
        },
    )?;
    Ok(())
}

pub fn crate_writer_step() -> ProcessingStep {
    // TODO: set dependencies
    ProcessingStep::new_const("crate_writer", run)
}
<commit_msg>clean up target dir before writing crate template<commit_after>use crate::cpp_code_generator;
use crate::cpp_code_generator::generate_cpp_type_size_requester;
use crate::processor::ProcessingStep;
use crate::processor::ProcessorData;
use crate::rust_code_generator;
use crate::versions;
use pathdiff::diff_paths;
use ritual_common::errors::{err_msg, Result};
use ritual_common::file_utils::copy_file;
use ritual_common::file_utils::copy_recursively;
use ritual_common::file_utils::create_dir;
use ritual_common::file_utils::create_dir_all;
use ritual_common::file_utils::create_file;
use ritual_common::file_utils::path_to_str;
use ritual_common::file_utils::read_dir;
use ritual_common::file_utils::remove_dir_all;
use ritual_common::file_utils::repo_crate_local_path;
use ritual_common::file_utils::save_json;
use ritual_common::file_utils::save_toml;
use ritual_common::toml;
use ritual_common::utils::run_command;
use ritual_common::utils::MapIfOk;
use ritual_common::BuildScriptData;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;

/// Merges `a` and `b` recursively. `b` take precedence over `a`.
fn recursive_merge_toml(a: toml::Value, b: toml::Value) -> toml::Value {
    if a.same_type(&b) {
        if let toml::Value::Array(mut a_array) = a {
            if let toml::Value::Array(mut b_array) = b {
                a_array.append(&mut b_array);
                toml::Value::Array(a_array)
            } else {
                unreachable!()
            }
        } else if let toml::Value::Table(mut a_table) = a {
            if let toml::Value::Table(b_table) = b {
                for (key, value) in b_table {
                    if let Some(old_value) = a_table.remove(&key) {
                        a_table.insert(key, recursive_merge_toml(old_value, value));
                    } else {
                        a_table.insert(key, value);
                    }
                }
                toml::Value::Table(a_table)
            } else {
                unreachable!()
            }
        } else {
            b
        }
    } else {
        b
    }
}

/// Generates `Cargo.toml` file and skeleton of the crate.
/// If a crate template was supplied, files from it are
/// copied to the output location.
fn generate_crate_template(data: &mut ProcessorData) -> Result<()> {
    let output_path = data
        .workspace
        .crate_path(data.config.crate_properties().name())?;

    let template_build_rs_path =
        data.config
            .crate_template_path()
            .as_ref()
            .and_then(|crate_template_path| {
                let template_build_rs_path = crate_template_path.join("build.rs");
                if template_build_rs_path.exists() {
                    Some(template_build_rs_path)
                } else {
                    None
                }
            });
    let output_build_rs_path = output_path.join("build.rs");
    if let Some(ref template_build_rs_path) = template_build_rs_path {
        copy_file(template_build_rs_path, output_build_rs_path)?;
    } else {
        let mut rustfmt_file = create_file(&output_build_rs_path)?;
        rustfmt_file.write(include_str!("../templates/crate/build.rs"))?;
    }
    let cargo_toml_data = {
        let package = toml::value::Value::Table({
            let mut table = toml::value::Table::new();
            table.insert(
                "name".to_string(),
                toml::Value::String(data.config.crate_properties().name().into()),
            );
            table.insert(
                "version".to_string(),
                toml::Value::String(data.config.crate_properties().version().into()),
            );
            table.insert(
                "build".to_string(),
                toml::Value::String("build.rs".to_string()),
            );
            table.insert(
                "edition".to_string(),
                toml::Value::String("2018".to_string()),
            );
            table
        });
        let dep_value = |version: &str, local_path: Option<PathBuf>| -> Result<toml::Value> {
            Ok(
                if local_path.is_none() || !data.workspace.config().write_dependencies_local_paths {
                    toml::Value::String(version.to_string())
                } else {
                    toml::Value::Table({
                        let mut value = toml::value::Table::new();
                        value.insert(
                            "version".to_string(),
                            toml::Value::String(version.to_string()),
                        );
                        value.insert(
                            "path".to_string(),
                            toml::Value::String(
                                path_to_str(&local_path.expect("checked above"))?.to_string(),
                            ),
                        );
                        value
                    })
                },
            )
        };
        let dependencies = toml::Value::Table({
            let mut table = toml::value::Table::new();
            if !data
                .config
                .crate_properties()
                .should_remove_default_dependencies()
            {
                table.insert(
                    "cpp_utils".to_string(),
                    dep_value(
                        versions::CPP_UTILS_VERSION,
                        if data.workspace.config().write_dependencies_local_paths {
                            Some(repo_crate_local_path("cpp_utils")?)
                        } else {
                            None
                        },
                    )?,
                );
                for dep in data.dep_databases {
                    let relative_path =
                        diff_paths(&data.workspace.crate_path(&dep.crate_name)?, &output_path)
                            .ok_or_else(|| {
                                err_msg("failed to get relative path to the dependency")
                            })?;

                    table.insert(
                        dep.crate_name.clone(),
                        dep_value(&dep.crate_version, Some(relative_path))?,
                    );
                }
            }
            for dep in data.config.crate_properties().dependencies() {
                table.insert(
                    dep.name().to_string(),
                    dep_value(dep.version(), dep.local_path().cloned())?,
                );
            }
            table
        });
        let build_dependencies = toml::Value::Table({
            let mut table = toml::value::Table::new();
            if !data
                .config
                .crate_properties()
                .should_remove_default_build_dependencies()
            {
                table.insert(
                    "ritual_build".to_string(),
                    dep_value(
                        versions::RITUAL_BUILD_VERSION,
                        if data.workspace.config().write_dependencies_local_paths {
                            Some(repo_crate_local_path("ritual_build")?)
                        } else {
                            None
                        },
                    )?,
                );
            }
            for dep in data.config.crate_properties().build_dependencies() {
                table.insert(
                    dep.name().to_string(),
                    dep_value(dep.version(), dep.local_path().cloned())?,
                );
            }
            table
        });
        let mut table = toml::value::Table::new();
        table.insert("package".to_string(), package);
        table.insert("dependencies".to_string(), dependencies);
        table.insert("build-dependencies".to_string(), build_dependencies);
        recursive_merge_toml(
            toml::Value::Table(table),
            toml::Value::Table(data.config.crate_properties().custom_fields().clone()),
        )
    };
    save_toml(output_path.join("Cargo.toml"), &cargo_toml_data)?;

    if let Some(ref template_path) = data.config.crate_template_path() {
        for item in read_dir(template_path)? {
            let item = item?;
            let target = output_path.join(item.file_name());
            remove_dir_all(&target)?;
            copy_recursively(&item.path(), &target)?;
        }
    }
    if !output_path.join("src").exists() {
        create_dir_all(output_path.join("src"))?;
    }
    Ok(())
}

/// Generates main files and directories of the library.
fn generate_c_lib_template(
    lib_name: &str,
    lib_path: &Path,
    global_header_name: &str,
    include_directives: &[PathBuf],
) -> Result<()> {
    let name_upper = lib_name.to_uppercase();
    let cmakelists_path = lib_path.join("CMakeLists.txt");
    let mut cmakelists_file = create_file(&cmakelists_path)?;

    cmakelists_file.write(format!(
        include_str!("../templates/c_lib/CMakeLists.txt"),
        lib_name_lowercase = lib_name,
        lib_name_uppercase = name_upper
    ))?;

    let include_directives_code = include_directives
        .map_if_ok(|d| -> Result<_> { Ok(format!("#include \"{}\"", path_to_str(d)?)) })?
        .join("\n");

    let global_header_path = lib_path.join(&global_header_name);
    let mut global_header_file = create_file(&global_header_path)?;
    global_header_file.write(format!(
        include_str!("../templates/c_lib/global.h"),
        include_directives_code = include_directives_code
    ))?;
    Ok(())
}

fn run(data: &mut ProcessorData) -> Result<()> {
    generate_crate_template(data)?;

    let output_path = data
        .workspace
        .crate_path(data.config.crate_properties().name())?;

    let c_lib_path = output_path.join("c_lib");
    if !c_lib_path.exists() {
        create_dir(&c_lib_path)?;
    }
    let c_lib_name = format!("{}_c", data.config.crate_properties().name());
    let global_header_name = format!("{}_global.h", c_lib_name);
    generate_c_lib_template(
        &c_lib_name,
        &c_lib_path,
        &global_header_name,
        data.config.include_directives(),
    )?;

    cpp_code_generator::generate_cpp_file(
        &data.current_database.cpp_items,
        &c_lib_path.join("file1.cpp"),
        &global_header_name,
    )?;

    let file = create_file(c_lib_path.join("sized_types.cxx"))?;
    generate_cpp_type_size_requester(
        &data.current_database.rust_database,
        data.config.include_directives(),
        file,
    )?;

    rust_code_generator::generate(
        data.config.crate_properties().name(),
        &data.current_database.rust_database,
        &output_path.join("src"),
    )?;

    run_command(Command::new("cargo").arg("fmt").current_dir(&output_path))?;
    run_command(
        Command::new("rustfmt")
            .arg("src/ffi.in.rs")
            .current_dir(&output_path),
    )?;

    save_json(
        output_path.join("build_script_data.json"),
        &BuildScriptData {
            cpp_build_config: data.config.cpp_build_config().clone(),
            cpp_wrapper_lib_name: c_lib_name,
            cpp_lib_version: data.config.cpp_lib_version().map(|s| s.to_string()),
        },
    )?;
    Ok(())
}

pub fn crate_writer_step() -> ProcessingStep {
    // TODO: set dependencies
    ProcessingStep::new_const("crate_writer", run)
}
<|endoftext|>