<commit_before>use mem::Addressable;
use super::cpu::CPU;

// General information on 65xx: http://en.wikipedia.org/wiki/MOS_Technology_6510
// Useful emulator information: http://emudocs.org/?page=CPU%2065xx
// Web simulator and much info: http://e-tradition.net/bytes/6502/
// Good reference and overview: http://www.obelisk.demon.co.uk/index.html
// Processor bugs and caveats : http://www.textfiles.com/apple/6502.bugs.txt

/// Hard-coded address where to look for the address to jump to on nonmaskable interrupt
static NMI_VECTOR:			u16 = 0xfffa;
/// Hard-coded address where to look for the address to jump to on reset
static RESET_VECTOR:		u16 = 0xfffc;
/// Hard-coded address where to look for the address to jump to on interrupt
static IRQ_VECTOR:			u16 = 0xfffe;

/// The MOS6502 processor
pub struct Mos6502 {
	priv pc: u16,						// Program Counter
	priv ac: u8,						// Accumulator
	priv x: u8,							// X register
	priv y: u8,							// Y register
	priv sr: u8,						// Status Register (NV-BDIZC: Negative, oVerflow, 1, Break, Decimal, Interrupt, Zero, Carry)
	priv sp: u8,						// Stack Pointer
}

/// The MOS6502 status flags
pub enum StatusFlag {
	CarryFlag				= 0,
	ZeroFlag				= 1,
	InterruptDisableFlag	= 2,
	DecimalFlag				= 3,
	BreakFlag				= 4,
	OverflowFlag			= 6,
	NegativeFlag			= 7,
}

impl Mos6502 {
	/// Create a new MOS6502 processor
	pub fn new () -> Mos6502 {
		Mos6502 { pc: 0, ac: 0, x: 0, y: 0, sr: 0, sp: 0 }
	}

	/// Get the memory contents at the current PC and advance the PC
	fn next<M: Addressable<u16>, T: Primitive> (&mut self, mem: &M) -> T {
		let val = mem.get_le(self.pc);
		self.pc += Primitive::bytes(None::<T>) as u16;
		val
	}

	/// Get the given status flag
	fn get_flag (&self, flag: StatusFlag) -> bool {
		(self.sr & (1 << flag as u8)) != 0
	}

	/// Set the given status flag to the given state
	fn set_flag (&mut self, flag: StatusFlag, state: bool) {
		if state {
			self.sr |= (1 << flag as u8);
		} else {
			self.sr &= !(1 << flag as u8);
		}
	}

	/// Set ZeroFlag and NegativeFlag based on the given value
	fn set_zn (&mut self, value: u8) -> u8 {
		self.set_flag(ZeroFlag, value == 0);
		self.set_flag(NegativeFlag, (value as i8) < 0);
		value
	}
}

impl CPU<u16> for Mos6502 {
	/// Reset the CPU
	fn reset<M: Addressable<u16>> (&mut self, mem: &M) {
		// On reset, the interrupt-disable flag is set (and the decimal flag is cleared in the CMOS version 65c02).
		// The other bits and all registers (including the stack pointer are unspecified and might contain random values.
		// Execution begins at the address pointed to by the reset vector at address $FFFC.
		self.pc = mem.get_le(RESET_VECTOR);
		self.sr = 0x24;
		debug!("mos65xx: Reset! Start at (${:04X}) -> ${:04X}", RESET_VECTOR, self.pc);
	}

	/// Do one step (execute the next instruction). Returns the number of
	/// cycles the instruction needed
	fn step<M: Addressable<u16>> (&mut self, mem: &mut M) -> uint {
		// TODO...
		0
	}
}

#[cfg(test)]
mod test {
	use mem::test::TestMemory;
	use super::Mos6502;
	use super::{CarryFlag, ZeroFlag, OverflowFlag, NegativeFlag};

	#[test]
	fn initial_state () {
		let cpu = Mos6502::new();
		assert_eq!(cpu.pc, 0x0000);
	}

	#[test]
	fn state_after_reset () {
		let mut cpu = Mos6502::new();
		let mem: TestMemory<u16> = TestMemory::new();
		cpu.reset(&mem);
		assert_eq!(cpu.pc, 0xfcfb);
		assert_eq!(cpu.sr, 0x24);
	}

	#[test]
	fn fetch_memory_contents_and_advance_pc () {
		let mut cpu = Mos6502::new();
		let mem: TestMemory<u16> = TestMemory::new();
		let val: u8 = cpu.next(&mem); assert_eq!(val, 0x00);
		let val: u8 = cpu.next(&mem); assert_eq!(val, 0x01);
		let val: u16 = cpu.next(&mem); assert_eq!(val, 0x0302);
		let val: u16 = cpu.next(&mem); assert_eq!(val, 0x0504);
	}

	#[test]
	fn status_flags () {
		let mut cpu = Mos6502::new();
		cpu.sr = 0xaa;
		assert!(!cpu.get_flag(CarryFlag));
		assert!(cpu.get_flag(ZeroFlag));
		assert!(!cpu.get_flag(OverflowFlag));
		assert!(cpu.get_flag(NegativeFlag));
	}

	#[test]
	fn set_status_flag () {
		let mut cpu = Mos6502::new();
		cpu.sr = 0xaa;
		cpu.set_flag(CarryFlag, true);
		cpu.set_flag(ZeroFlag, false);
		cpu.set_flag(OverflowFlag, true);
		cpu.set_flag(NegativeFlag, false);
		assert_eq!(cpu.sr, 0x69);
	}

	#[test]
	fn zero_and_negative_values () {
		let mut cpu = Mos6502::new();
		cpu.set_zn(0);
		assert!(cpu.get_flag(ZeroFlag)); assert!(!cpu.get_flag(NegativeFlag));
		cpu.set_zn(42);
		assert!(!cpu.get_flag(ZeroFlag)); assert!(!cpu.get_flag(NegativeFlag));
		cpu.set_zn(142);
		assert!(!cpu.get_flag(ZeroFlag)); assert!(cpu.get_flag(NegativeFlag));
	}
}
<commit_msg>Clean up 6502 tests<commit_after>use mem::Addressable;
use super::cpu::CPU;

// General information on 65xx: http://en.wikipedia.org/wiki/MOS_Technology_6510
// Useful emulator information: http://emudocs.org/?page=CPU%2065xx
// Web simulator and much info: http://e-tradition.net/bytes/6502/
// Good reference and overview: http://www.obelisk.demon.co.uk/index.html
// Processor bugs and caveats : http://www.textfiles.com/apple/6502.bugs.txt

/// Hard-coded address where to look for the address to jump to on nonmaskable interrupt
static NMI_VECTOR:			u16 = 0xfffa;
/// Hard-coded address where to look for the address to jump to on reset
static RESET_VECTOR:		u16 = 0xfffc;
/// Hard-coded address where to look for the address to jump to on interrupt
static IRQ_VECTOR:			u16 = 0xfffe;

/// The MOS6502 processor
pub struct Mos6502 {
	priv pc: u16,						// Program Counter
	priv ac: u8,						// Accumulator
	priv x: u8,							// X register
	priv y: u8,							// Y register
	priv sr: u8,						// Status Register (NV-BDIZC: Negative, oVerflow, 1, Break, Decimal, Interrupt, Zero, Carry)
	priv sp: u8,						// Stack Pointer
}

/// The MOS6502 status flags
pub enum StatusFlag {
	CarryFlag				= 0,
	ZeroFlag				= 1,
	InterruptDisableFlag	= 2,
	DecimalFlag				= 3,
	BreakFlag				= 4,
	OverflowFlag			= 6,
	NegativeFlag			= 7,
}

impl Mos6502 {
	/// Create a new MOS6502 processor
	pub fn new () -> Mos6502 {
		Mos6502 { pc: 0, ac: 0, x: 0, y: 0, sr: 0, sp: 0 }
	}

	/// Get the memory contents at the current PC and advance the PC
	fn next<M: Addressable<u16>, T: Primitive> (&mut self, mem: &M) -> T {
		let val = mem.get_le(self.pc);
		self.pc += Primitive::bytes(None::<T>) as u16;
		val
	}

	/// Get the given status flag
	fn get_flag (&self, flag: StatusFlag) -> bool {
		(self.sr & (1 << flag as u8)) != 0
	}

	/// Set the given status flag to the given state
	fn set_flag (&mut self, flag: StatusFlag, state: bool) {
		if state {
			self.sr |= (1 << flag as u8);
		} else {
			self.sr &= !(1 << flag as u8);
		}
	}

	/// Set ZeroFlag and NegativeFlag based on the given value
	fn set_zn (&mut self, value: u8) -> u8 {
		self.set_flag(ZeroFlag, value == 0);
		self.set_flag(NegativeFlag, (value as i8) < 0);
		value
	}
}

impl CPU<u16> for Mos6502 {
	/// Reset the CPU
	fn reset<M: Addressable<u16>> (&mut self, mem: &M) {
		// On reset, the interrupt-disable flag is set (and the decimal flag is cleared in the CMOS version 65c02).
		// The other bits and all registers (including the stack pointer are unspecified and might contain random values.
		// Execution begins at the address pointed to by the reset vector at address $FFFC.
		self.pc = mem.get_le(RESET_VECTOR);
		self.sr = 0x24;
		debug!("mos65xx: Reset! Start at (${:04X}) -> ${:04X}", RESET_VECTOR, self.pc);
	}

	/// Do one step (execute the next instruction). Returns the number of
	/// cycles the instruction needed
	fn step<M: Addressable<u16>> (&mut self, mem: &mut M) -> uint {
		// TODO...
		0
	}
}

#[cfg(test)]
mod test {
	use mem::test::TestMemory;
	use super::Mos6502;
	use super::{CarryFlag, ZeroFlag, OverflowFlag, NegativeFlag};

	#[test]
	fn initial_state () {
		let cpu = Mos6502::new();
		assert_eq!(cpu.pc, 0x0000);
	}

	#[test]
	fn state_after_reset () {
		let mut cpu = Mos6502::new();
		let mem: TestMemory<u16> = TestMemory::new();
		cpu.reset(&mem);
		assert_eq!(cpu.pc, 0xfcfb);
		assert_eq!(cpu.sr, 0x24);
	}

	#[test]
	fn fetch_memory_contents_and_advance_pc () {
		let mut cpu = Mos6502::new();
		let mem: TestMemory<u16> = TestMemory::new();
		let val: u8 = cpu.next(&mem); assert_eq!(val, 0x00);
		let val: u8 = cpu.next(&mem); assert_eq!(val, 0x01);
		let val: u16 = cpu.next(&mem); assert_eq!(val, 0x0302);
		let val: u16 = cpu.next(&mem); assert_eq!(val, 0x0504);
	}

	#[test]
	fn status_flags () {
		let mut cpu = Mos6502::new();
		cpu.sr = 0xaa;
		assert!(!cpu.get_flag(CarryFlag));
		assert!(cpu.get_flag(ZeroFlag));
		assert!(!cpu.get_flag(OverflowFlag));
		assert!(cpu.get_flag(NegativeFlag));
		cpu.set_flag(CarryFlag, true);
		cpu.set_flag(ZeroFlag, false);
		cpu.set_flag(OverflowFlag, true);
		cpu.set_flag(NegativeFlag, false);
		assert_eq!(cpu.sr, 0x69);
	}

	#[test]
	fn zero_and_negative_values () {
		let mut cpu = Mos6502::new();
		cpu.set_zn(0);
		assert!(cpu.get_flag(ZeroFlag));
		assert!(!cpu.get_flag(NegativeFlag));
		cpu.set_zn(42);
		assert!(!cpu.get_flag(ZeroFlag));
		assert!(!cpu.get_flag(NegativeFlag));
		cpu.set_zn(142);
		assert!(!cpu.get_flag(ZeroFlag));
		assert!(cpu.get_flag(NegativeFlag));
	}
}
<|endoftext|>


<commit_before>// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Rust stack-limit management
//!
//! Currently Rust uses a segmented-stack-like scheme in order to detect stack
//! overflow for rust threads. In this scheme, the prologue of all functions are
//! preceded with a check to see whether the current stack limits are being
//! exceeded.
//!
//! This module provides the functionality necessary in order to manage these
//! stack limits (which are stored in platform-specific locations). The
//! functions here are used at the borders of the thread lifetime in order to
//! manage these limits.
//!
//! This function is an unstable module because this scheme for stack overflow
//! detection is not guaranteed to continue in the future. Usage of this module
//! is discouraged unless absolutely necessary.

// iOS related notes
//
// It is possible to implement it using idea from
// http://www.opensource.apple.com/source/Libc/Libc-825.40.1/pthreads/pthread_machdep.h
//
// In short: _pthread_{get,set}_specific_direct allows extremely fast
// access, exactly what is required for segmented stack
// There is a pool of reserved slots for Apple internal use (0..119)
// First dynamic allocated pthread key starts with 257 (on iOS7)
// So using slot 149 should be pretty safe ASSUMING space is reserved
// for every key < first dynamic key
//
// There is also an opportunity to steal keys reserved for Garbage Collection
// ranges 80..89 and 110..119, especially considering the fact Garbage Collection
// never supposed to work on iOS. But as everybody knows it - there is a chance
// that those slots will be re-used, like it happened with key 95 (moved from
// JavaScriptCore to CoreText)
//
// Unfortunately Apple rejected patch to LLVM which generated
// corresponding prolog, decision was taken to disable segmented
// stack support on iOS.

pub const RED_ZONE: usize = 20 * 1024;

/// This function is invoked from rust's current __morestack function. Segmented
/// stacks are currently not enabled as segmented stacks, but rather one giant
/// stack segment. This means that whenever we run out of stack, we want to
/// truly consider it to be stack overflow rather than allocating a new stack.
#[cfg(not(test))] // in testing, use the original libstd's version
#[lang = "stack_exhausted"]
extern fn stack_exhausted() {
    use intrinsics;

    unsafe {
        // We're calling this function because the stack just ran out. We need
        // to call some other rust functions, but if we invoke the functions
        // right now it'll just trigger this handler being called again. In
        // order to alleviate this, we move the stack limit to be inside of the
        // red zone that was allocated for exactly this reason.
        let limit = get_sp_limit();
        record_sp_limit(limit - RED_ZONE / 2);

        // This probably isn't the best course of action. Ideally one would want
        // to unwind the stack here instead of just aborting the entire process.
        // This is a tricky problem, however. There's a few things which need to
        // be considered:
        //
        //  1. We're here because of a stack overflow, yet unwinding will run
        //     destructors and hence arbitrary code. What if that code overflows
        //     the stack? One possibility is to use the above allocation of an
        //     extra 10k to hope that we don't hit the limit, and if we do then
        //     abort the whole program. Not the best, but kind of hard to deal
        //     with unless we want to switch stacks.
        //
        //  2. LLVM will optimize functions based on whether they can unwind or
        //     not. It will flag functions with 'nounwind' if it believes that
        //     the function cannot trigger unwinding, but if we do unwind on
        //     stack overflow then it means that we could unwind in any function
        //     anywhere. We would have to make sure that LLVM only places the
        //     nounwind flag on functions which don't call any other functions.
        //
        //  3. The function that overflowed may have owned arguments. These
        //     arguments need to have their destructors run, but we haven't even
        //     begun executing the function yet, so unwinding will not run the
        //     any landing pads for these functions. If this is ignored, then
        //     the arguments will just be leaked.
        //
        // Exactly what to do here is a very delicate topic, and is possibly
        // still up in the air for what exactly to do. Some relevant issues:
        //
        //  #3555 - out-of-stack failure leaks arguments
        //  #3695 - should there be a stack limit?
        //  #9855 - possible strategies which could be taken
        //  #9854 - unwinding on windows through __morestack has never worked
        //  #2361 - possible implementation of not using landing pads

        ::rt::util::report_overflow();

        intrinsics::abort();
    }
}

// Windows maintains a record of upper and lower stack bounds in the Thread Information
// Block (TIB), and some syscalls do check that addresses which are supposed to be in
// the stack, indeed lie between these two values.
// (See https://github.com/rust-lang/rust/issues/3445#issuecomment-26114839)
//
// When using Rust-managed stacks (libgreen), we must maintain these values accordingly.
// For OS-managed stacks (libnative), we let the OS manage them for us.
//
// On all other platforms both variants behave identically.

#[inline(always)]
pub unsafe fn record_os_managed_stack_bounds(stack_lo: usize, _stack_hi: usize) {
    record_sp_limit(stack_lo + RED_ZONE);
}

/// Records the current limit of the stack as specified by `end`.
///
/// This is stored in an OS-dependent location, likely inside of the thread
/// local storage. The location that the limit is stored is a pre-ordained
/// location because it's where LLVM has emitted code to check.
///
/// Note that this cannot be called under normal circumstances. This function is
/// changing the stack limit, so upon returning any further function calls will
/// possibly be triggering the morestack logic if you're not careful.
///
/// Also note that this and all of the inside functions are all flagged as
/// "inline(always)" because they're messing around with the stack limits.  This
/// would be unfortunate for the functions themselves to trigger a morestack
/// invocation (if they were an actual function call).
#[inline(always)]
pub unsafe fn record_sp_limit(limit: usize) {
    return target_record_sp_limit(limit);

    // x86-64
    #[cfg(all(target_arch = "x86_64",
              any(target_os = "macos", target_os = "ios")))]
    #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        asm!("movq $$0x60+90*8, %rsi
              movq $0, %gs:(%rsi)" :: "r"(limit) : "rsi" : "volatile")
    }
    #[cfg(all(target_arch = "x86_64", target_os = "linux"))] #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        asm!("movq $0, %fs:112" :: "r"(limit) :: "volatile")
    }
    #[cfg(all(target_arch = "x86_64", target_os = "windows"))] #[inline(always)]
    unsafe fn target_record_sp_limit(_: usize) {
    }
    #[cfg(all(target_arch = "x86_64", target_os = "freebsd"))] #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        asm!("movq $0, %fs:24" :: "r"(limit) :: "volatile")
    }
    #[cfg(all(target_arch = "x86_64", target_os = "dragonfly"))]
    #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        asm!("movq $0, %fs:32" :: "r"(limit) :: "volatile")
    }

    // x86
    #[cfg(all(target_arch = "x86",
              any(target_os = "macos", target_os = "ios")))]
    #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        asm!("movl $$0x48+90*4, %eax
              movl $0, %gs:(%eax)" :: "r"(limit) : "eax" : "volatile")
    }
    #[cfg(all(target_arch = "x86",
              any(target_os = "linux", target_os = "freebsd")))]
    #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        asm!("movl $0, %gs:48" :: "r"(limit) :: "volatile")
    }
    #[cfg(all(target_arch = "x86", target_os = "windows"))] #[inline(always)]
    unsafe fn target_record_sp_limit(_: usize) {
    }

    // mips, arm - Some brave soul can port these to inline asm, but it's over
    //             my head personally
    #[cfg(any(target_arch = "mips",
              target_arch = "mipsel",
              all(target_arch = "arm", not(target_os = "ios"))))]
    #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        use libc::c_void;
        return record_sp_limit(limit as *const c_void);
        extern {
            fn record_sp_limit(limit: *const c_void);
        }
    }

    // aarch64 - FIXME(AARCH64): missing...
    // powerpc - FIXME(POWERPC): missing...
    // arm-ios - iOS segmented stack is disabled for now, see related notes
    // openbsd - segmented stack is disabled
    #[cfg(any(target_arch = "aarch64",
              target_arch = "powerpc",
              all(target_arch = "arm", target_os = "ios"),
              target_os = "bitrig",
              target_os = "openbsd"))]
    unsafe fn target_record_sp_limit(_: usize) {
    }
}

/// The counterpart of the function above, this function will fetch the current
/// stack limit stored in TLS.
///
/// Note that all of these functions are meant to be exact counterparts of their
/// brethren above, except that the operands are reversed.
///
/// As with the setter, this function does not have a __morestack header and can
/// therefore be called in a "we're out of stack" situation.
#[inline(always)]
pub unsafe fn get_sp_limit() -> usize {
    return target_get_sp_limit();

    // x86-64
    #[cfg(all(target_arch = "x86_64",
              any(target_os = "macos", target_os = "ios")))]
    #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        let limit;
        asm!("movq $$0x60+90*8, %rsi
              movq %gs:(%rsi), $0" : "=r"(limit) :: "rsi" : "volatile");
        return limit;
    }
    #[cfg(all(target_arch = "x86_64", target_os = "linux"))] #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        let limit;
        asm!("movq %fs:112, $0" : "=r"(limit) ::: "volatile");
        return limit;
    }
    #[cfg(all(target_arch = "x86_64", target_os = "windows"))] #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        return 1024;
    }
    #[cfg(all(target_arch = "x86_64", target_os = "freebsd"))] #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        let limit;
        asm!("movq %fs:24, $0" : "=r"(limit) ::: "volatile");
        return limit;
    }
    #[cfg(all(target_arch = "x86_64", target_os = "dragonfly"))]
    #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        let limit;
        asm!("movq %fs:32, $0" : "=r"(limit) ::: "volatile");
        return limit;
    }

    // x86
    #[cfg(all(target_arch = "x86",
              any(target_os = "macos", target_os = "ios")))]
    #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        let limit;
        asm!("movl $$0x48+90*4, %eax
              movl %gs:(%eax), $0" : "=r"(limit) :: "eax" : "volatile");
        return limit;
    }
    #[cfg(all(target_arch = "x86",
              any(target_os = "linux", target_os = "freebsd")))]
    #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        let limit;
        asm!("movl %gs:48, $0" : "=r"(limit) ::: "volatile");
        return limit;
    }
    #[cfg(all(target_arch = "x86", target_os = "windows"))] #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        return 1024;
    }

    // mips, arm - Some brave soul can port these to inline asm, but it's over
    //             my head personally
    #[cfg(any(target_arch = "mips",
              target_arch = "mipsel",
              all(target_arch = "arm", not(target_os = "ios"))))]
    #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        use libc::c_void;
        return get_sp_limit() as usize;
        extern {
            fn get_sp_limit() -> *const c_void;
        }
    }

    // aarch64 - FIXME(AARCH64): missing...
    // powerpc - FIXME(POWERPC): missing...
    // arm-ios - iOS doesn't support segmented stacks yet.
    // openbsd - OpenBSD doesn't support segmented stacks.
    //
    // This function might be called by runtime though
    // so it is unsafe to unreachable, let's return a fixed constant.
    #[cfg(any(target_arch = "aarch64",
              target_arch = "powerpc",
              all(target_arch = "arm", target_os = "ios"),
              target_os = "bitrig",
              target_os = "openbsd"))]
    #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        1024
    }
}
<commit_msg>Rollup merge of #25974 - richo:stack-msg, r=alexcrichton<commit_after>// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Rust stack-limit management
//!
//! Currently Rust uses a segmented-stack-like scheme in order to detect stack
//! overflow for rust threads. In this scheme, the prologue of all functions are
//! preceded with a check to see whether the current stack limits are being
//! exceeded.
//!
//! This module provides the functionality necessary in order to manage these
//! stack limits (which are stored in platform-specific locations). The
//! functions here are used at the borders of the thread lifetime in order to
//! manage these limits.
//!
//! This function is an unstable module because this scheme for stack overflow
//! detection is not guaranteed to continue in the future. Usage of this module
//! is discouraged unless absolutely necessary.

// iOS related notes
//
// It is possible to implement it using idea from
// http://www.opensource.apple.com/source/Libc/Libc-825.40.1/pthreads/pthread_machdep.h
//
// In short: _pthread_{get,set}_specific_direct allows extremely fast
// access, exactly what is required for segmented stack
// There is a pool of reserved slots for Apple internal use (0..119)
// First dynamic allocated pthread key starts with 257 (on iOS7)
// So using slot 149 should be pretty safe ASSUMING space is reserved
// for every key < first dynamic key
//
// There is also an opportunity to steal keys reserved for Garbage Collection
// ranges 80..89 and 110..119, especially considering the fact Garbage Collection
// never supposed to work on iOS. But as everybody knows it - there is a chance
// that those slots will be re-used, like it happened with key 95 (moved from
// JavaScriptCore to CoreText)
//
// Unfortunately Apple rejected patch to LLVM which generated
// corresponding prolog, decision was taken to disable segmented
// stack support on iOS.

pub const RED_ZONE: usize = 20 * 1024;

/// This function is invoked from rust's current __morestack function. Segmented
/// stacks are currently not enabled as segmented stacks, but rather one giant
/// stack segment. This means that whenever we run out of stack, we want to
/// truly consider it to be stack overflow rather than allocating a new stack.
#[cfg(not(test))] // in testing, use the original libstd's version
#[lang = "stack_exhausted"]
extern fn stack_exhausted() {
    use intrinsics;

    unsafe {
        // We're calling this function because the stack just ran out. We need
        // to call some other rust functions, but if we invoke the functions
        // right now it'll just trigger this handler being called again. In
        // order to alleviate this, we move the stack limit to be inside of the
        // red zone that was allocated for exactly this reason.
        let limit = get_sp_limit();
        record_sp_limit(limit - RED_ZONE / 2);

        // This probably isn't the best course of action. Ideally one would want
        // to unwind the stack here instead of just aborting the entire process.
        // This is a tricky problem, however. There's a few things which need to
        // be considered:
        //
        //  1. We're here because of a stack overflow, yet unwinding will run
        //     destructors and hence arbitrary code. What if that code overflows
        //     the stack? One possibility is to use the above allocation of an
        //     extra 10k to hope that we don't hit the limit, and if we do then
        //     abort the whole program. Not the best, but kind of hard to deal
        //     with unless we want to switch stacks.
        //
        //  2. LLVM will optimize functions based on whether they can unwind or
        //     not. It will flag functions with 'nounwind' if it believes that
        //     the function cannot trigger unwinding, but if we do unwind on
        //     stack overflow then it means that we could unwind in any function
        //     anywhere. We would have to make sure that LLVM only places the
        //     nounwind flag on functions which don't call any other functions.
        //
        //  3. The function that overflowed may have owned arguments. These
        //     arguments need to have their destructors run, but we haven't even
        //     begun executing the function yet, so unwinding will not run the
        //     any landing pads for these functions. If this is ignored, then
        //     the arguments will just be leaked.
        //
        // Exactly what to do here is a very delicate topic, and is possibly
        // still up in the air for what exactly to do. Some relevant issues:
        //
        //  #3555 - out-of-stack failure leaks arguments
        //  #3695 - should there be a stack limit?
        //  #9855 - possible strategies which could be taken
        //  #9854 - unwinding on windows through __morestack has never worked
        //  #2361 - possible implementation of not using landing pads

        ::rt::util::report_overflow();

        intrinsics::abort();
    }
}

// Windows maintains a record of upper and lower stack bounds in the Thread Information
// Block (TIB), and some syscalls do check that addresses which are supposed to be in
// the stack, indeed lie between these two values.
// (See https://github.com/rust-lang/rust/issues/3445#issuecomment-26114839)
//
// When using Rust-managed stacks (libgreen), we must maintain these values accordingly.
// For OS-managed stacks (libnative), we let the OS manage them for us.
//
// On all other platforms both variants behave identically.

#[inline(always)]
pub unsafe fn record_os_managed_stack_bounds(stack_lo: usize, _stack_hi: usize) {
    record_sp_limit(stack_lo + RED_ZONE);
}

/// Records the current limit of the stack as specified by `end`.
///
/// This is stored in an OS-dependent location, likely inside of the thread
/// local storage. The location that the limit is stored is a pre-ordained
/// location because it's where LLVM has emitted code to check.
///
/// Note that this cannot be called under normal circumstances. This function is
/// changing the stack limit, so upon returning any further function calls will
/// possibly be triggering the morestack logic if you're not careful.
///
/// Also note that this and all of the inside functions are all flagged as
/// "inline(always)" because they're messing around with the stack limits.  This
/// would be unfortunate for the functions themselves to trigger a morestack
/// invocation (if they were an actual function call).
#[inline(always)]
pub unsafe fn record_sp_limit(limit: usize) {
    return target_record_sp_limit(limit);

    #[cfg(all(target_arch = "x86_64",
              any(target_os = "macos", target_os = "ios")))]
    #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        asm!("movq $$0x60+90*8, %rsi
              movq $0, %gs:(%rsi)" :: "r"(limit) : "rsi" : "volatile")
    }
    #[cfg(all(target_arch = "x86_64", target_os = "linux"))] #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        asm!("movq $0, %fs:112" :: "r"(limit) :: "volatile")
    }
    #[cfg(all(target_arch = "x86_64", target_os = "windows"))] #[inline(always)]
    unsafe fn target_record_sp_limit(_: usize) {
    }
    #[cfg(all(target_arch = "x86_64", target_os = "freebsd"))] #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        asm!("movq $0, %fs:24" :: "r"(limit) :: "volatile")
    }
    #[cfg(all(target_arch = "x86_64", target_os = "dragonfly"))]
    #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        asm!("movq $0, %fs:32" :: "r"(limit) :: "volatile")
    }

    #[cfg(all(target_arch = "x86",
              any(target_os = "macos", target_os = "ios")))]
    #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        asm!("movl $$0x48+90*4, %eax
              movl $0, %gs:(%eax)" :: "r"(limit) : "eax" : "volatile")
    }
    #[cfg(all(target_arch = "x86",
              any(target_os = "linux", target_os = "freebsd")))]
    #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        asm!("movl $0, %gs:48" :: "r"(limit) :: "volatile")
    }
    #[cfg(all(target_arch = "x86", target_os = "windows"))] #[inline(always)]
    unsafe fn target_record_sp_limit(_: usize) {
    }

    // mips, arm - The implementations are a bit big for inline asm!
    //             They can be found in src/rt/arch/$target_arch/record_sp.S
    #[cfg(any(target_arch = "mips",
              target_arch = "mipsel",
              all(target_arch = "arm", not(target_os = "ios"))))]
    #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        use libc::c_void;
        return record_sp_limit(limit as *const c_void);
        extern {
            fn record_sp_limit(limit: *const c_void);
        }
    }

    // aarch64 - FIXME(AARCH64): missing...
    // powerpc - FIXME(POWERPC): missing...
    // arm-ios - iOS segmented stack is disabled for now, see related notes
    // openbsd - segmented stack is disabled
    #[cfg(any(target_arch = "aarch64",
              target_arch = "powerpc",
              all(target_arch = "arm", target_os = "ios"),
              target_os = "bitrig",
              target_os = "openbsd"))]
    unsafe fn target_record_sp_limit(_: usize) {
    }
}

/// The counterpart of the function above, this function will fetch the current
/// stack limit stored in TLS.
///
/// Note that all of these functions are meant to be exact counterparts of their
/// brethren above, except that the operands are reversed.
///
/// As with the setter, this function does not have a __morestack header and can
/// therefore be called in a "we're out of stack" situation.
#[inline(always)]
pub unsafe fn get_sp_limit() -> usize {
    return target_get_sp_limit();

    #[cfg(all(target_arch = "x86_64",
              any(target_os = "macos", target_os = "ios")))]
    #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        let limit;
        asm!("movq $$0x60+90*8, %rsi
              movq %gs:(%rsi), $0" : "=r"(limit) :: "rsi" : "volatile");
        return limit;
    }
    #[cfg(all(target_arch = "x86_64", target_os = "linux"))] #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        let limit;
        asm!("movq %fs:112, $0" : "=r"(limit) ::: "volatile");
        return limit;
    }
    #[cfg(all(target_arch = "x86_64", target_os = "windows"))] #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        return 1024;
    }
    #[cfg(all(target_arch = "x86_64", target_os = "freebsd"))] #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        let limit;
        asm!("movq %fs:24, $0" : "=r"(limit) ::: "volatile");
        return limit;
    }
    #[cfg(all(target_arch = "x86_64", target_os = "dragonfly"))]
    #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        let limit;
        asm!("movq %fs:32, $0" : "=r"(limit) ::: "volatile");
        return limit;
    }

    #[cfg(all(target_arch = "x86",
              any(target_os = "macos", target_os = "ios")))]
    #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        let limit;
        asm!("movl $$0x48+90*4, %eax
              movl %gs:(%eax), $0" : "=r"(limit) :: "eax" : "volatile");
        return limit;
    }
    #[cfg(all(target_arch = "x86",
              any(target_os = "linux", target_os = "freebsd")))]
    #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        let limit;
        asm!("movl %gs:48, $0" : "=r"(limit) ::: "volatile");
        return limit;
    }
    #[cfg(all(target_arch = "x86", target_os = "windows"))] #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        return 1024;
    }

    // mips, arm - The implementations are a bit big for inline asm!
    //             They can be found in src/rt/arch/$target_arch/record_sp.S
    #[cfg(any(target_arch = "mips",
              target_arch = "mipsel",
              all(target_arch = "arm", not(target_os = "ios"))))]
    #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        use libc::c_void;
        return get_sp_limit() as usize;
        extern {
            fn get_sp_limit() -> *const c_void;
        }
    }

    // aarch64 - FIXME(AARCH64): missing...
    // powerpc - FIXME(POWERPC): missing...
    // arm-ios - iOS doesn't support segmented stacks yet.
    // openbsd - OpenBSD doesn't support segmented stacks.
    //
    // This function might be called by runtime though
    // so it is unsafe to unreachable, let's return a fixed constant.
    #[cfg(any(target_arch = "aarch64",
              target_arch = "powerpc",
              all(target_arch = "arm", target_os = "ios"),
              target_os = "bitrig",
              target_os = "openbsd"))]
    #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        1024
    }
}
<|endoftext|>


<commit_before>extern crate image;

use self::image::ImageBuffer;

use std::fs::File;
use std::path::PathBuf;
use std::cmp;

pub fn prime_filter_to_spiral_png(x_size: usize, from_vec: Vec<bool>, path: String){
    let imgbuf = make_spiral_buffer(x_size, from_vec);
    let mut w_path = PathBuf::from(path);

    w_path.push(format!("ulam_spiral{}", x_size));
    w_path.set_extension("png");

    let ref mut four = File::create(w_path).unwrap();

    let _ = image::ImageLuma8(imgbuf).save(four, image::PNG);
}
fn point_on_spiral_to_array_index_end(spiral_size: usize, x: usize, y: usize)->usize{
    let ss_minus_one = spiral_size-1;
    match (x, y) {
        (x, y) if y == 0 => x,
        (x, y) if x == 0 => ss_minus_one*4 - y,
        (x, y) if y == spiral_size-1 => ss_minus_one*3 - x,
        (x, y) if x == spiral_size-1 => ss_minus_one + y,
        (x, y) => {
            let skip_loop_num = cmp::min(cmp::min(x, y), cmp::min(ss_minus_one-x, ss_minus_one-y));
            (2*spiral_size - skip_loop_num)*skip_loop_num + point_on_spiral_to_array_index_end(
                    spiral_size-2*skip_loop_num, x-skip_loop_num, y-skip_loop_num)
        },
    }

}
fn point_on_spiral_to_array_index(spiral_size: usize, x: usize, y: usize)->usize{
    spiral_size*spiral_size - point_on_spiral_to_array_index_end(spiral_size, x, y)
}
fn make_spiral_buffer(x_size: usize, from_vec: Vec<bool>)->
                    ImageBuffer<image::Luma<u8>, Vec<u8>>{
    assert_eq!(from_vec.len(), x_size*x_size+1);
    let img = ImageBuffer::from_fn(x_size as u32, x_size as u32, |x, y| {
        match from_vec[point_on_spiral_to_array_index(x_size, x as usize, y as usize)] {
            true  => image::Luma([0u8]),
            false => image::Luma([255u8]),
        }
    });
    img
}
<commit_msg>Revert "Fixed recursion stack problem."<commit_after>extern crate image;

use self::image::ImageBuffer;

use std::fs::File;
use std::path::PathBuf;

pub fn prime_filter_to_spiral_png(x_size: usize, from_vec: Vec<bool>, path: String){
    let imgbuf = make_spiral_buffer(x_size, from_vec);
    let mut w_path = PathBuf::from(path);

    w_path.push(format!("ulam_spiral{}", x_size));
    w_path.set_extension("png");

    let ref mut four = File::create(w_path).unwrap();

    let _ = image::ImageLuma8(imgbuf).save(four, image::PNG);
}
fn point_on_spiral_to_array_index_end(spiral_size: usize, x: usize, y: usize)->usize{
    let ss_minus_one = spiral_size-1;
    match (x, y) {
        (x, y) if y == 0 => x,
        (x, y) if x == 0 => ss_minus_one*4 - y,
        (x, y) if y == spiral_size-1 => ss_minus_one*3 - x,
        (x, y) if x == spiral_size-1 => ss_minus_one + y,
        (x, y) => ss_minus_one*4 + point_on_spiral_to_array_index_end(spiral_size-2, x-1, y-1),
    }

}
fn point_on_spiral_to_array_index(spiral_size: usize, x: usize, y: usize)->usize{
    spiral_size*spiral_size - point_on_spiral_to_array_index_end(spiral_size, x, y)
}
fn make_spiral_buffer(x_size: usize, from_vec: Vec<bool>)->
                    ImageBuffer<image::Luma<u8>, Vec<u8>>{
    assert_eq!(from_vec.len(), x_size*x_size+1);
    let img = ImageBuffer::from_fn(x_size as u32, x_size as u32, |x, y| {
        match from_vec[point_on_spiral_to_array_index(x_size, x as usize, y as usize)] {
            true  => image::Luma([0u8]),
            false => image::Luma([255u8]),
        }
    });
    img
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::config::Config;
use crate::errors::{Error, Result};
use crate::graphql_asts::GraphQLAsts;
use crate::red_to_green::RedToGreen;
use crate::watchman::FileSource;
use crate::{
    build_project::{
        build_project, build_raw_program, build_schema, commit_project, BuildProjectFailure,
    },
    errors::BuildProjectError,
};
use crate::{
    compiler_state::{ArtifactMapKind, CompilerState, ProjectName},
    watchman::FileSourceSubscriptionNextChange,
};
use common::{Diagnostic, PerfLogEvent, PerfLogger};
use futures::future::join_all;
use graphql_ir::Program;
use log::{debug, error, info};
use rayon::prelude::*;
use schema::SDLSchema;
use std::{collections::HashMap, collections::HashSet, sync::atomic::Ordering, sync::Arc};
use tokio::{sync::Notify, task};

pub struct Compiler<TPerfLogger>
where
    TPerfLogger: PerfLogger + 'static,
{
    config: Arc<Config>,
    perf_logger: Arc<TPerfLogger>,
}

impl<TPerfLogger: PerfLogger> Compiler<TPerfLogger> {
    pub fn new(config: Arc<Config>, perf_logger: Arc<TPerfLogger>) -> Self {
        Self {
            config,
            perf_logger,
        }
    }

    pub async fn compile(&self) -> Result<CompilerState> {
        let setup_event = self.perf_logger.create_event("compiler_setup");
        let file_source = FileSource::connect(&self.config, &setup_event).await?;
        let mut compiler_state = file_source
            .query(&setup_event, self.perf_logger.as_ref())
            .await?;

        self.build_projects(&mut compiler_state, &setup_event)
            .await?;

        self.perf_logger.complete_event(setup_event);
        Ok(compiler_state)
    }

    pub fn build_schemas(
        &self,
        compiler_state: &CompilerState,
        setup_event: &impl PerfLogEvent,
    ) -> (HashMap<ProjectName, Arc<SDLSchema>>, Vec<Diagnostic>) {
        let mut errors: Vec<Diagnostic> = vec![];
        let timer = setup_event.start("build_schemas");
        let mut schemas = HashMap::default();
        for project_config in self.config.enabled_projects() {
            match build_schema(compiler_state, project_config) {
                Ok(schema) => {
                    schemas.insert(project_config.name, schema);
                }
                Err(diagnostics) => {
                    for err in diagnostics {
                        errors.push(err);
                    }
                }
            };
        }
        setup_event.stop(timer);

        (schemas, errors)
    }

    pub fn build_raw_programs(
        &self,
        compiler_state: &CompilerState,
        schemas: &HashMap<ProjectName, Arc<SDLSchema>>,
        affected_projects: Option<HashSet<&ProjectName>>, // for watch-mode to filter-out unchanged projects
        setup_event: &impl PerfLogEvent,
    ) -> Result<(HashMap<ProjectName, Program>, Vec<BuildProjectError>)> {
        let graphql_asts = setup_event.time("parse_sources_time", || {
            GraphQLAsts::from_graphql_sources_map(
                &compiler_state.graphql_sources,
                &compiler_state.get_dirty_definitions(&self.config),
            )
        })?;

        let programs: Vec<(ProjectName, _)> = self
            .config
            .par_enabled_projects()
            .filter(|project_config| {
                if let Some(affected_projects) = &affected_projects {
                    affected_projects.contains(&project_config.name)
                } else {
                    true
                }
            })
            .map(|project_config| {
                let project_name = project_config.name;
                if let Some(schema) = schemas.get(&project_name) {
                    (
                        project_config.name,
                        build_raw_program(
                            project_config,
                            &graphql_asts,
                            Arc::clone(schema),
                            setup_event,
                        ),
                    )
                } else {
                    (
                        project_name,
                        Err(BuildProjectError::SchemaNotFoundForProject { project_name }),
                    )
                }
            })
            .collect();

        let mut errors: Vec<BuildProjectError> = vec![];
        let mut program_map: HashMap<ProjectName, Program> = Default::default();

        for (program_name, program_result) in programs {
            match program_result {
                Ok(program) => {
                    program_map.insert(program_name, program);
                }
                Err(error) => {
                    errors.push(error);
                }
            };
        }

        Ok((program_map, errors))
    }

    pub async fn watch(&self) -> Result<()> {
        let setup_event = self.perf_logger.create_event("compiler_setup");

        let file_source = FileSource::connect(&self.config, &setup_event).await?;

        let (mut compiler_state, mut subscription) = file_source
            .subscribe(&setup_event, self.perf_logger.as_ref())
            .await?;

        let mut red_to_green = RedToGreen::new();

        if self
            .build_projects(&mut compiler_state, &setup_event)
            .await
            .is_err()
        {
            // build_projects should have logged already
            red_to_green.log_error()
        } else {
            info!("Compilation completed.");
        }
        self.perf_logger.complete_event(setup_event);
        info!("Waiting for changes...");

        let pending_file_source_changes = compiler_state.pending_file_source_changes.clone();
        let source_control_update_in_progress =
            compiler_state.source_control_update_in_progress.clone();
        let notify_sender = Arc::new(Notify::new());
        let notify_receiver = notify_sender.clone();
        task::spawn(async move {
            loop {
                let next_change = subscription.next_change().await;
                match next_change {
                    Ok(FileSourceSubscriptionNextChange::Result(file_source_changes)) => {
                        source_control_update_in_progress.store(false, Ordering::Relaxed);
                        pending_file_source_changes
                            .write()
                            .unwrap()
                            .push(file_source_changes);
                        notify_sender.notify_one();
                    }
                    Ok(FileSourceSubscriptionNextChange::SourceControlUpdate) => {
                        source_control_update_in_progress.store(true, Ordering::Relaxed);
                    }
                    Ok(FileSourceSubscriptionNextChange::None) => {}
                    Err(err) => {
                        error!("Watchman subscription error: {}", err);
                    }
                }
            }
        });

        loop {
            notify_receiver.notified().await;
            // Single change to file sometimes produces 2 watchman change events for the same file
            // wait for 50ms in case there is a subsequent request
            tokio::time::sleep(std::time::Duration::from_millis(50)).await;
            if compiler_state.has_pending_file_source_changes()
                && !compiler_state.is_source_control_update_in_progress()
            {
                let incremental_build_event =
                    self.perf_logger.create_event("incremental_build_event");
                let incremental_build_time =
                    incremental_build_event.start("incremental_build_time");

                let had_new_changes = compiler_state.merge_file_source_changes(
                    &self.config,
                    &incremental_build_event,
                    self.perf_logger.as_ref(),
                    false,
                )?;

                if had_new_changes {
                    info!("Change detected, start compiling...");
                    if self
                        .build_projects(&mut compiler_state, &incremental_build_event)
                        .await
                        .is_err()
                    {
                        // build_projects should have logged already
                        red_to_green.log_error()
                    } else {
                        info!("Compilation completed.");
                        red_to_green.clear_error_and_log(self.perf_logger.as_ref());
                    }
                    incremental_build_event.stop(incremental_build_time);
                    info!("Waiting for changes...");
                } else {
                    incremental_build_event.stop(incremental_build_time);
                }
                self.perf_logger.complete_event(incremental_build_event);
                // We probably don't want the messages queue to grow indefinitely
                // and we need to flush then, as the check/build is completed
                self.perf_logger.flush();
            }
        }
    }

    async fn build_projects(
        &self,
        compiler_state: &mut CompilerState,
        setup_event: &impl PerfLogEvent,
    ) -> Result<()> {
        self.config.status_reporter.build_starts();
        let build_projects_time = setup_event.start("build_projects_time");
        let result = build_projects(
            Arc::clone(&self.config),
            Arc::clone(&self.perf_logger),
            setup_event,
            compiler_state,
        )
        .await;
        setup_event.stop(build_projects_time);
        let result = setup_event.time("post_build_projects_time", || match result {
            Ok(()) => {
                compiler_state.complete_compilation();
                self.config.artifact_writer.finalize()?;
                if let Some(post_artifacts_write) = &self.config.post_artifacts_write {
                    if let Err(error) = post_artifacts_write(&self.config) {
                        let error = Error::PostArtifactsError { error };
                        Err(error)
                    } else {
                        Ok(())
                    }
                } else {
                    Ok(())
                }
            }
            Err(error) => Err(error),
        });
        self.config.status_reporter.build_finishes(&result);
        result
    }
}

async fn build_projects<TPerfLogger: PerfLogger + 'static>(
    config: Arc<Config>,
    perf_logger: Arc<TPerfLogger>,
    setup_event: &impl PerfLogEvent,
    compiler_state: &mut CompilerState,
) -> Result<()> {
    let mut graphql_asts = setup_event.time("parse_sources_time", || {
        GraphQLAsts::from_graphql_sources_map(
            &compiler_state.graphql_sources,
            &compiler_state.get_dirty_definitions(&config),
        )
    })?;

    if compiler_state.should_cancel_current_build() {
        debug!("Build is cancelled: updates in source code/or new file changes are pending.");
        return Err(Error::Cancelled);
    }

    let build_results: Vec<_> = config
        .par_enabled_projects()
        .filter(|project_config| {
            if let Some(base) = project_config.base {
                if compiler_state.project_has_pending_changes(base) {
                    return true;
                }
            }
            compiler_state.project_has_pending_changes(project_config.name)
        })
        .map(|project_config| {
            build_project(
                &config,
                project_config,
                compiler_state,
                &graphql_asts,
                Arc::clone(&perf_logger),
            )
        })
        .collect();
    let mut results = Vec::new();
    let mut errors = Vec::new();
    for result in build_results {
        match result {
            Ok(result) => results.push(result),
            Err(error) => match error {
                BuildProjectFailure::Error(error) => errors.push(error),
                BuildProjectFailure::Cancelled => {
                    return Err(Error::Cancelled);
                }
            },
        }
    }

    if errors.is_empty() {
        if compiler_state.should_cancel_current_build() {
            debug!("Build is cancelled: updates in source code/or new file changes are pending.");
            return Err(Error::Cancelled);
        }

        let mut handles = Vec::new();
        for (project_name, schema, programs, artifacts) in results {
            let config = Arc::clone(&config);
            let perf_logger = Arc::clone(&perf_logger);
            let artifact_map = compiler_state
                .artifacts
                .get(&project_name)
                .cloned()
                .unwrap_or_else(|| Arc::new(ArtifactMapKind::Unconnected(Default::default())));
            let removed_definition_names = graphql_asts
                .remove(&project_name)
                .expect("Expect GraphQLAsts to exist.")
                .removed_definition_names;
            let dirty_artifact_paths = compiler_state
                .dirty_artifact_paths
                .get(&project_name)
                .cloned()
                .unwrap_or_default();
            handles.push(task::spawn(async move {
                let project_config = &config.projects[&project_name];
                Ok((
                    project_name,
                    commit_project(
                        &config,
                        project_config,
                        perf_logger,
                        &schema,
                        programs,
                        artifacts,
                        artifact_map,
                        removed_definition_names,
                        dirty_artifact_paths,
                    )
                    .await?,
                    schema,
                ))
            }));
        }
        for commit_result in join_all(handles).await {
            let commit_result: std::result::Result<std::result::Result<_, _>, _> = commit_result;
            let inner_result = commit_result.map_err(|e| Error::JoinError {
                error: e.to_string(),
            })?;
            match inner_result {
                Ok((project_name, next_artifact_map, schema)) => {
                    let next_artifact_map = Arc::new(ArtifactMapKind::Mapping(next_artifact_map));
                    compiler_state
                        .artifacts
                        .insert(project_name, next_artifact_map);
                    compiler_state.schema_cache.insert(project_name, schema);
                }
                Err(error) => {
                    errors.push(error);
                }
            }
        }
    }

    if errors.is_empty() {
        Ok(())
    } else {
        Err(Error::BuildProjectsErrors { errors })
    }
}
<commit_msg>listen to watchman events before starting the compiler<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::config::Config;
use crate::errors::{Error, Result};
use crate::graphql_asts::GraphQLAsts;
use crate::red_to_green::RedToGreen;
use crate::watchman::FileSource;
use crate::{
    build_project::{
        build_project, build_raw_program, build_schema, commit_project, BuildProjectFailure,
    },
    errors::BuildProjectError,
};
use crate::{
    compiler_state::{ArtifactMapKind, CompilerState, ProjectName},
    watchman::FileSourceSubscriptionNextChange,
};
use common::{Diagnostic, PerfLogEvent, PerfLogger};
use futures::future::join_all;
use graphql_ir::Program;
use log::{debug, error, info};
use rayon::prelude::*;
use schema::SDLSchema;
use std::{collections::HashMap, collections::HashSet, sync::atomic::Ordering, sync::Arc};
use tokio::{sync::Notify, task};

pub struct Compiler<TPerfLogger>
where
    TPerfLogger: PerfLogger + 'static,
{
    config: Arc<Config>,
    perf_logger: Arc<TPerfLogger>,
}

impl<TPerfLogger: PerfLogger> Compiler<TPerfLogger> {
    pub fn new(config: Arc<Config>, perf_logger: Arc<TPerfLogger>) -> Self {
        Self {
            config,
            perf_logger,
        }
    }

    pub async fn compile(&self) -> Result<CompilerState> {
        let setup_event = self.perf_logger.create_event("compiler_setup");
        let file_source = FileSource::connect(&self.config, &setup_event).await?;
        let mut compiler_state = file_source
            .query(&setup_event, self.perf_logger.as_ref())
            .await?;

        self.build_projects(&mut compiler_state, &setup_event)
            .await?;

        self.perf_logger.complete_event(setup_event);
        Ok(compiler_state)
    }

    pub fn build_schemas(
        &self,
        compiler_state: &CompilerState,
        setup_event: &impl PerfLogEvent,
    ) -> (HashMap<ProjectName, Arc<SDLSchema>>, Vec<Diagnostic>) {
        let mut errors: Vec<Diagnostic> = vec![];
        let timer = setup_event.start("build_schemas");
        let mut schemas = HashMap::default();
        for project_config in self.config.enabled_projects() {
            match build_schema(compiler_state, project_config) {
                Ok(schema) => {
                    schemas.insert(project_config.name, schema);
                }
                Err(diagnostics) => {
                    for err in diagnostics {
                        errors.push(err);
                    }
                }
            };
        }
        setup_event.stop(timer);

        (schemas, errors)
    }

    pub fn build_raw_programs(
        &self,
        compiler_state: &CompilerState,
        schemas: &HashMap<ProjectName, Arc<SDLSchema>>,
        affected_projects: Option<HashSet<&ProjectName>>, // for watch-mode to filter-out unchanged projects
        setup_event: &impl PerfLogEvent,
    ) -> Result<(HashMap<ProjectName, Program>, Vec<BuildProjectError>)> {
        let graphql_asts = setup_event.time("parse_sources_time", || {
            GraphQLAsts::from_graphql_sources_map(
                &compiler_state.graphql_sources,
                &compiler_state.get_dirty_definitions(&self.config),
            )
        })?;

        let programs: Vec<(ProjectName, _)> = self
            .config
            .par_enabled_projects()
            .filter(|project_config| {
                if let Some(affected_projects) = &affected_projects {
                    affected_projects.contains(&project_config.name)
                } else {
                    true
                }
            })
            .map(|project_config| {
                let project_name = project_config.name;
                if let Some(schema) = schemas.get(&project_name) {
                    (
                        project_config.name,
                        build_raw_program(
                            project_config,
                            &graphql_asts,
                            Arc::clone(schema),
                            setup_event,
                        ),
                    )
                } else {
                    (
                        project_name,
                        Err(BuildProjectError::SchemaNotFoundForProject { project_name }),
                    )
                }
            })
            .collect();

        let mut errors: Vec<BuildProjectError> = vec![];
        let mut program_map: HashMap<ProjectName, Program> = Default::default();

        for (program_name, program_result) in programs {
            match program_result {
                Ok(program) => {
                    program_map.insert(program_name, program);
                }
                Err(error) => {
                    errors.push(error);
                }
            };
        }

        Ok((program_map, errors))
    }

    pub async fn watch(&self) -> Result<()> {
        let setup_event = self.perf_logger.create_event("compiler_setup");

        let file_source = FileSource::connect(&self.config, &setup_event).await?;

        let (mut compiler_state, mut subscription) = file_source
            .subscribe(&setup_event, self.perf_logger.as_ref())
            .await?;

        let pending_file_source_changes = compiler_state.pending_file_source_changes.clone();
        let source_control_update_in_progress =
            compiler_state.source_control_update_in_progress.clone();
        let notify_sender = Arc::new(Notify::new());
        let notify_receiver = notify_sender.clone();

        // First, set up watchman subscription
        task::spawn(async move {
            loop {
                let next_change = subscription.next_change().await;
                match next_change {
                    Ok(FileSourceSubscriptionNextChange::Result(file_source_changes)) => {
                        source_control_update_in_progress.store(false, Ordering::Relaxed);
                        pending_file_source_changes
                            .write()
                            .unwrap()
                            .push(file_source_changes);
                        notify_sender.notify_one();
                    }
                    Ok(FileSourceSubscriptionNextChange::SourceControlUpdate) => {
                        source_control_update_in_progress.store(true, Ordering::Relaxed);
                    }
                    Ok(FileSourceSubscriptionNextChange::None) => {}
                    Err(err) => {
                        error!("Watchman subscription error: {}", err);
                    }
                }
            }
        });

        let mut red_to_green = RedToGreen::new();
        if self
            .build_projects(&mut compiler_state, &setup_event)
            .await
            .is_err()
        {
            // build_projects should have logged already
            red_to_green.log_error()
        } else {
            info!("Compilation completed.");
        }
        self.perf_logger.complete_event(setup_event);

        info!("Waiting for changes...");
        loop {
            notify_receiver.notified().await;
            // Single change to file sometimes produces 2 watchman change events for the same file
            // wait for 50ms in case there is a subsequent request
            tokio::time::sleep(std::time::Duration::from_millis(50)).await;

            if compiler_state.has_pending_file_source_changes()
                && !compiler_state.is_source_control_update_in_progress()
            {
                let incremental_build_event =
                    self.perf_logger.create_event("incremental_build_event");
                let incremental_build_time =
                    incremental_build_event.start("incremental_build_time");

                let had_new_changes = compiler_state.merge_file_source_changes(
                    &self.config,
                    &incremental_build_event,
                    self.perf_logger.as_ref(),
                    false,
                )?;

                if had_new_changes {
                    info!("Change detected, start compiling...");
                    if self
                        .build_projects(&mut compiler_state, &incremental_build_event)
                        .await
                        .is_err()
                    {
                        // build_projects should have logged already
                        red_to_green.log_error()
                    } else {
                        info!("Compilation completed.");
                        red_to_green.clear_error_and_log(self.perf_logger.as_ref());
                    }
                    incremental_build_event.stop(incremental_build_time);
                    info!("Waiting for changes...");
                } else {
                    incremental_build_event.stop(incremental_build_time);
                }
                self.perf_logger.complete_event(incremental_build_event);
                // We probably don't want the messages queue to grow indefinitely
                // and we need to flush then, as the check/build is completed
                self.perf_logger.flush();
            }
        }
    }

    async fn build_projects(
        &self,
        compiler_state: &mut CompilerState,
        setup_event: &impl PerfLogEvent,
    ) -> Result<()> {
        self.config.status_reporter.build_starts();
        let build_projects_time = setup_event.start("build_projects_time");
        let result = build_projects(
            Arc::clone(&self.config),
            Arc::clone(&self.perf_logger),
            setup_event,
            compiler_state,
        )
        .await;
        setup_event.stop(build_projects_time);
        let result = setup_event.time("post_build_projects_time", || match result {
            Ok(()) => {
                compiler_state.complete_compilation();
                self.config.artifact_writer.finalize()?;
                if let Some(post_artifacts_write) = &self.config.post_artifacts_write {
                    if let Err(error) = post_artifacts_write(&self.config) {
                        let error = Error::PostArtifactsError { error };
                        Err(error)
                    } else {
                        Ok(())
                    }
                } else {
                    Ok(())
                }
            }
            Err(error) => Err(error),
        });
        self.config.status_reporter.build_finishes(&result);
        result
    }
}

async fn build_projects<TPerfLogger: PerfLogger + 'static>(
    config: Arc<Config>,
    perf_logger: Arc<TPerfLogger>,
    setup_event: &impl PerfLogEvent,
    compiler_state: &mut CompilerState,
) -> Result<()> {
    let mut graphql_asts = setup_event.time("parse_sources_time", || {
        GraphQLAsts::from_graphql_sources_map(
            &compiler_state.graphql_sources,
            &compiler_state.get_dirty_definitions(&config),
        )
    })?;

    if compiler_state.should_cancel_current_build() {
        debug!("Build is cancelled: updates in source code/or new file changes are pending.");
        return Err(Error::Cancelled);
    }

    let build_results: Vec<_> = config
        .par_enabled_projects()
        .filter(|project_config| {
            if let Some(base) = project_config.base {
                if compiler_state.project_has_pending_changes(base) {
                    return true;
                }
            }
            compiler_state.project_has_pending_changes(project_config.name)
        })
        .map(|project_config| {
            build_project(
                &config,
                project_config,
                compiler_state,
                &graphql_asts,
                Arc::clone(&perf_logger),
            )
        })
        .collect();
    let mut results = Vec::new();
    let mut errors = Vec::new();
    for result in build_results {
        match result {
            Ok(result) => results.push(result),
            Err(error) => match error {
                BuildProjectFailure::Error(error) => errors.push(error),
                BuildProjectFailure::Cancelled => {
                    return Err(Error::Cancelled);
                }
            },
        }
    }

    if errors.is_empty() {
        if compiler_state.should_cancel_current_build() {
            debug!("Build is cancelled: updates in source code/or new file changes are pending.");
            return Err(Error::Cancelled);
        }

        let mut handles = Vec::new();
        for (project_name, schema, programs, artifacts) in results {
            let config = Arc::clone(&config);
            let perf_logger = Arc::clone(&perf_logger);
            let artifact_map = compiler_state
                .artifacts
                .get(&project_name)
                .cloned()
                .unwrap_or_else(|| Arc::new(ArtifactMapKind::Unconnected(Default::default())));
            let removed_definition_names = graphql_asts
                .remove(&project_name)
                .expect("Expect GraphQLAsts to exist.")
                .removed_definition_names;
            let dirty_artifact_paths = compiler_state
                .dirty_artifact_paths
                .get(&project_name)
                .cloned()
                .unwrap_or_default();
            handles.push(task::spawn(async move {
                let project_config = &config.projects[&project_name];
                Ok((
                    project_name,
                    commit_project(
                        &config,
                        project_config,
                        perf_logger,
                        &schema,
                        programs,
                        artifacts,
                        artifact_map,
                        removed_definition_names,
                        dirty_artifact_paths,
                    )
                    .await?,
                    schema,
                ))
            }));
        }
        for commit_result in join_all(handles).await {
            let commit_result: std::result::Result<std::result::Result<_, _>, _> = commit_result;
            let inner_result = commit_result.map_err(|e| Error::JoinError {
                error: e.to_string(),
            })?;
            match inner_result {
                Ok((project_name, next_artifact_map, schema)) => {
                    let next_artifact_map = Arc::new(ArtifactMapKind::Mapping(next_artifact_map));
                    compiler_state
                        .artifacts
                        .insert(project_name, next_artifact_map);
                    compiler_state.schema_cache.insert(project_name, schema);
                }
                Err(error) => {
                    errors.push(error);
                }
            }
        }
    }

    if errors.is_empty() {
        Ok(())
    } else {
        Err(Error::BuildProjectsErrors { errors })
    }
}
<|endoftext|>


<commit_before>use futures::executor::block_on;
use futures::future::{Future, FutureExt};
use futures::stream::{self, StreamExt, TryStreamExt};
use futures::io::{AsyncBufReadExt, Cursor};
use futures::task::Poll;
use futures_test::io::AsyncReadTestExt;
use futures_test::task::noop_context;

#[test]
fn read_line() {
    let mut buf = Cursor::new(b"12");
    let mut v = String::new();
    assert_eq!(block_on(buf.read_line(&mut v)).unwrap(), 2);
    assert_eq!(v, "12");

    let mut buf = Cursor::new(b"12\n\n");
    let mut v = String::new();
    assert_eq!(block_on(buf.read_line(&mut v)).unwrap(), 3);
    assert_eq!(v, "12\n");
    v.clear();
    assert_eq!(block_on(buf.read_line(&mut v)).unwrap(), 1);
    assert_eq!(v, "\n");
    v.clear();
    assert_eq!(block_on(buf.read_line(&mut v)).unwrap(), 0);
    assert_eq!(v, "");
}

fn run<F: Future + Unpin>(mut f: F) -> F::Output {
    let mut cx = noop_context();
    loop {
        if let Poll::Ready(x) = f.poll_unpin(&mut cx) {
            return x;
        }
    }
}

#[test]
fn maybe_pending() {
    let mut buf = b"12".interleave_pending();
    let mut v = String::new();
    assert_eq!(run(buf.read_line(&mut v)).unwrap(), 2);
    assert_eq!(v, "12");

    let mut buf = stream::iter(vec![&b"12"[..], &b"\n\n"[..]])
        .map(Ok)
        .into_async_read()
        .interleave_pending();
    let mut v = String::new();
    assert_eq!(run(buf.read_line(&mut v)).unwrap(), 3);
    assert_eq!(v, "12\n");
    v.clear();
    assert_eq!(run(buf.read_line(&mut v)).unwrap(), 1);
    assert_eq!(v, "\n");
    v.clear();
    assert_eq!(run(buf.read_line(&mut v)).unwrap(), 0);
    assert_eq!(v, "");
    v.clear();
    assert_eq!(run(buf.read_line(&mut v)).unwrap(), 0);
    assert_eq!(v, "");
}
<commit_msg>futures: tests/io_read_line.rs: Don't break w/o features<commit_after>#[cfg(feature = "executor")]
#[test]
fn read_line() {
    use futures::executor::block_on;
    use futures::io::{AsyncBufReadExt, Cursor};

    let mut buf = Cursor::new(b"12");
    let mut v = String::new();
    assert_eq!(block_on(buf.read_line(&mut v)).unwrap(), 2);
    assert_eq!(v, "12");

    let mut buf = Cursor::new(b"12\n\n");
    let mut v = String::new();
    assert_eq!(block_on(buf.read_line(&mut v)).unwrap(), 3);
    assert_eq!(v, "12\n");
    v.clear();
    assert_eq!(block_on(buf.read_line(&mut v)).unwrap(), 1);
    assert_eq!(v, "\n");
    v.clear();
    assert_eq!(block_on(buf.read_line(&mut v)).unwrap(), 0);
    assert_eq!(v, "");
}

#[cfg(feature = "std")]
#[test]
fn maybe_pending() {
    use futures::future::Future;

    fn run<F: Future + Unpin>(mut f: F) -> F::Output {
        use futures::future::FutureExt;
        use futures::task::Poll;
        use futures_test::task::noop_context;

        let mut cx = noop_context();
        loop {
            if let Poll::Ready(x) = f.poll_unpin(&mut cx) {
                return x;
            }
        }
    }

    use futures::stream::{self, StreamExt, TryStreamExt};
    use futures::io::AsyncBufReadExt;
    use futures_test::io::AsyncReadTestExt;

    let mut buf = b"12".interleave_pending();
    let mut v = String::new();
    assert_eq!(run(buf.read_line(&mut v)).unwrap(), 2);
    assert_eq!(v, "12");

    let mut buf = stream::iter(vec![&b"12"[..], &b"\n\n"[..]])
        .map(Ok)
        .into_async_read()
        .interleave_pending();
    let mut v = String::new();
    assert_eq!(run(buf.read_line(&mut v)).unwrap(), 3);
    assert_eq!(v, "12\n");
    v.clear();
    assert_eq!(run(buf.read_line(&mut v)).unwrap(), 1);
    assert_eq!(v, "\n");
    v.clear();
    assert_eq!(run(buf.read_line(&mut v)).unwrap(), 0);
    assert_eq!(v, "");
    v.clear();
    assert_eq!(run(buf.read_line(&mut v)).unwrap(), 0);
    assert_eq!(v, "");
}
<|endoftext|>


<commit_before>//! Abstract Syntax Tree, layered on top of untyped `SyntaxNode`s

mod generated;
mod traits;
mod tokens;
mod extensions;
mod expr_extensions;
pub mod edit;
pub mod make;

use std::marker::PhantomData;

use crate::{
    syntax_node::{SyntaxNode, SyntaxNodeChildren, SyntaxToken},
    SmolStr, SyntaxKind,
};

pub use self::{
    expr_extensions::{ArrayExprKind, BinOp, Effect, ElseBranch, LiteralKind, PrefixOp, RangeOp},
    extensions::{
        AttrKind, FieldKind, NameOrNameRef, PathSegmentKind, SelfParamKind, SlicePatComponents,
        StructKind, TypeBoundKind, VisibilityKind,
    },
    generated::{nodes::*, tokens::*},
    tokens::*,
    traits::*,
};

/// The main trait to go from untyped `SyntaxNode`  to a typed ast. The
/// conversion itself has zero runtime cost: ast and syntax nodes have exactly
/// the same representation: a pointer to the tree root and a pointer to the
/// node itself.
pub trait AstNode {
    fn can_cast(kind: SyntaxKind) -> bool
    where
        Self: Sized;

    fn cast(syntax: SyntaxNode) -> Option<Self>
    where
        Self: Sized;

    fn syntax(&self) -> &SyntaxNode;
}

/// Like `AstNode`, but wraps tokens rather than interior nodes.
pub trait AstToken {
    fn can_cast(token: SyntaxKind) -> bool
    where
        Self: Sized;

    fn cast(syntax: SyntaxToken) -> Option<Self>
    where
        Self: Sized;

    fn syntax(&self) -> &SyntaxToken;

    fn text(&self) -> &SmolStr {
        self.syntax().text()
    }
}

/// An iterator over `SyntaxNode` children of a particular AST type.
#[derive(Debug, Clone)]
pub struct AstChildren<N> {
    inner: SyntaxNodeChildren,
    ph: PhantomData<N>,
}

impl<N> AstChildren<N> {
    fn new(parent: &SyntaxNode) -> Self {
        AstChildren { inner: parent.children(), ph: PhantomData }
    }
}

impl<N: AstNode> Iterator for AstChildren<N> {
    type Item = N;
    fn next(&mut self) -> Option<N> {
        self.inner.by_ref().find_map(N::cast)
    }
}

mod support {
    use super::{AstChildren, AstNode, SyntaxKind, SyntaxNode, SyntaxToken};

    pub(super) fn child<N: AstNode>(parent: &SyntaxNode) -> Option<N> {
        parent.children().find_map(N::cast)
    }

    pub(super) fn children<N: AstNode>(parent: &SyntaxNode) -> AstChildren<N> {
        AstChildren::new(parent)
    }

    pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option<SyntaxToken> {
        parent.children_with_tokens().filter_map(|it| it.into_token()).find(|it| it.kind() == kind)
    }
}

#[test]
fn assert_ast_is_object_safe() {
    fn _f(_: &dyn AstNode, _: &dyn NameOwner) {}
}

#[test]
fn test_doc_comment_none() {
    let file = SourceFile::parse(
        r#"
        // non-doc
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert!(module.doc_comment_text().is_none());
}

#[test]
fn test_doc_comment_of_items() {
    let file = SourceFile::parse(
        r#"
        //! doc
        // non-doc
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!("doc", module.doc_comment_text().unwrap());
}

#[test]
fn test_doc_comment_of_statics() {
    let file = SourceFile::parse(
        r#"
        /// Number of levels
        static LEVELS: i32 = 0;
        "#,
    )
    .ok()
    .unwrap();
    let st = file.syntax().descendants().find_map(StaticDef::cast).unwrap();
    assert_eq!("Number of levels", st.doc_comment_text().unwrap());
}

#[test]
fn test_doc_comment_preserves_indents() {
    let file = SourceFile::parse(
        r#"
        /// doc1
        /// ```
        /// fn foo() {
        ///     // ...
        /// }
        /// ```
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!("doc1\n```\nfn foo() {\n    // ...\n}\n```", module.doc_comment_text().unwrap());
}

#[test]
fn test_doc_comment_preserves_newlines() {
    let file = SourceFile::parse(
        r#"
        /// this
        /// is
        /// mod
        /// foo
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!("this\nis\nmod\nfoo", module.doc_comment_text().unwrap());
}

#[test]
fn test_doc_comment_single_line_block_strips_suffix() {
    let file = SourceFile::parse(
        r#"
        /** this is mod foo*/
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!("this is mod foo", module.doc_comment_text().unwrap());
}

#[test]
fn test_doc_comment_single_line_block_strips_suffix_whitespace() {
    let file = SourceFile::parse(
        r#"
        /** this is mod foo */
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!("this is mod foo ", module.doc_comment_text().unwrap());
}

#[test]
fn test_doc_comment_multi_line_block_strips_suffix() {
    let file = SourceFile::parse(
        r#"
        /**
        this
        is
        mod foo
        */
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!(
        "        this\n        is\n        mod foo\n        ",
        module.doc_comment_text().unwrap()
    );
}

#[test]
fn test_comments_preserve_trailing_whitespace() {
    let file = SourceFile::parse(
        "\n/// Representation of a Realm.   \n/// In the specification these are called Realm Records.\nstruct Realm {}",
    )
    .ok()
    .unwrap();
    let def = file.syntax().descendants().find_map(StructDef::cast).unwrap();
    assert_eq!(
        "Representation of a Realm.   \nIn the specification these are called Realm Records.",
        def.doc_comment_text().unwrap()
    );
}

#[test]
fn test_four_slash_line_comment() {
    let file = SourceFile::parse(
        r#"
        //// too many slashes to be a doc comment
        /// doc comment
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!("doc comment", module.doc_comment_text().unwrap());
}

#[test]
fn test_where_predicates() {
    fn assert_bound(text: &str, bound: Option<TypeBound>) {
        assert_eq!(text, bound.unwrap().syntax().text().to_string());
    }

    let file = SourceFile::parse(
        r#"
fn foo()
where
   T: Clone + Copy + Debug + 'static,
   'a: 'b + 'c,
   Iterator::Item: 'a + Debug,
   Iterator::Item: Debug + 'a,
   <T as Iterator>::Item: Debug + 'a,
   for<'a> F: Fn(&'a str)
{}
        "#,
    )
    .ok()
    .unwrap();
    let where_clause = file.syntax().descendants().find_map(WhereClause::cast).unwrap();

    let mut predicates = where_clause.predicates();

    let pred = predicates.next().unwrap();
    let mut bounds = pred.type_bound_list().unwrap().bounds();

    assert_eq!("T", pred.type_ref().unwrap().syntax().text().to_string());
    assert_bound("Clone", bounds.next());
    assert_bound("Copy", bounds.next());
    assert_bound("Debug", bounds.next());
    assert_bound("'static", bounds.next());

    let pred = predicates.next().unwrap();
    let mut bounds = pred.type_bound_list().unwrap().bounds();

    assert_eq!("'a", pred.lifetime_token().unwrap().text());

    assert_bound("'b", bounds.next());
    assert_bound("'c", bounds.next());

    let pred = predicates.next().unwrap();
    let mut bounds = pred.type_bound_list().unwrap().bounds();

    assert_eq!("Iterator::Item", pred.type_ref().unwrap().syntax().text().to_string());
    assert_bound("'a", bounds.next());

    let pred = predicates.next().unwrap();
    let mut bounds = pred.type_bound_list().unwrap().bounds();

    assert_eq!("Iterator::Item", pred.type_ref().unwrap().syntax().text().to_string());
    assert_bound("Debug", bounds.next());
    assert_bound("'a", bounds.next());

    let pred = predicates.next().unwrap();
    let mut bounds = pred.type_bound_list().unwrap().bounds();

    assert_eq!("<T as Iterator>::Item", pred.type_ref().unwrap().syntax().text().to_string());
    assert_bound("Debug", bounds.next());
    assert_bound("'a", bounds.next());

    let pred = predicates.next().unwrap();
    let mut bounds = pred.type_bound_list().unwrap().bounds();

    assert_eq!("for<'a> F", pred.type_ref().unwrap().syntax().text().to_string());
    assert_bound("Fn(&'a str)", bounds.next());
}
<commit_msg>Simplify<commit_after>//! Abstract Syntax Tree, layered on top of untyped `SyntaxNode`s

mod generated;
mod traits;
mod tokens;
mod extensions;
mod expr_extensions;
pub mod edit;
pub mod make;

use std::marker::PhantomData;

use crate::{
    syntax_node::{SyntaxNode, SyntaxNodeChildren, SyntaxToken},
    SmolStr, SyntaxKind,
};

pub use self::{
    expr_extensions::{ArrayExprKind, BinOp, Effect, ElseBranch, LiteralKind, PrefixOp, RangeOp},
    extensions::{
        AttrKind, FieldKind, NameOrNameRef, PathSegmentKind, SelfParamKind, SlicePatComponents,
        StructKind, TypeBoundKind, VisibilityKind,
    },
    generated::{nodes::*, tokens::*},
    tokens::*,
    traits::*,
};

/// The main trait to go from untyped `SyntaxNode`  to a typed ast. The
/// conversion itself has zero runtime cost: ast and syntax nodes have exactly
/// the same representation: a pointer to the tree root and a pointer to the
/// node itself.
pub trait AstNode {
    fn can_cast(kind: SyntaxKind) -> bool
    where
        Self: Sized;

    fn cast(syntax: SyntaxNode) -> Option<Self>
    where
        Self: Sized;

    fn syntax(&self) -> &SyntaxNode;
}

/// Like `AstNode`, but wraps tokens rather than interior nodes.
pub trait AstToken {
    fn can_cast(token: SyntaxKind) -> bool
    where
        Self: Sized;

    fn cast(syntax: SyntaxToken) -> Option<Self>
    where
        Self: Sized;

    fn syntax(&self) -> &SyntaxToken;

    fn text(&self) -> &SmolStr {
        self.syntax().text()
    }
}

/// An iterator over `SyntaxNode` children of a particular AST type.
#[derive(Debug, Clone)]
pub struct AstChildren<N> {
    inner: SyntaxNodeChildren,
    ph: PhantomData<N>,
}

impl<N> AstChildren<N> {
    fn new(parent: &SyntaxNode) -> Self {
        AstChildren { inner: parent.children(), ph: PhantomData }
    }
}

impl<N: AstNode> Iterator for AstChildren<N> {
    type Item = N;
    fn next(&mut self) -> Option<N> {
        self.inner.find_map(N::cast)
    }
}

mod support {
    use super::{AstChildren, AstNode, SyntaxKind, SyntaxNode, SyntaxToken};

    pub(super) fn child<N: AstNode>(parent: &SyntaxNode) -> Option<N> {
        parent.children().find_map(N::cast)
    }

    pub(super) fn children<N: AstNode>(parent: &SyntaxNode) -> AstChildren<N> {
        AstChildren::new(parent)
    }

    pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option<SyntaxToken> {
        parent.children_with_tokens().filter_map(|it| it.into_token()).find(|it| it.kind() == kind)
    }
}

#[test]
fn assert_ast_is_object_safe() {
    fn _f(_: &dyn AstNode, _: &dyn NameOwner) {}
}

#[test]
fn test_doc_comment_none() {
    let file = SourceFile::parse(
        r#"
        // non-doc
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert!(module.doc_comment_text().is_none());
}

#[test]
fn test_doc_comment_of_items() {
    let file = SourceFile::parse(
        r#"
        //! doc
        // non-doc
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!("doc", module.doc_comment_text().unwrap());
}

#[test]
fn test_doc_comment_of_statics() {
    let file = SourceFile::parse(
        r#"
        /// Number of levels
        static LEVELS: i32 = 0;
        "#,
    )
    .ok()
    .unwrap();
    let st = file.syntax().descendants().find_map(StaticDef::cast).unwrap();
    assert_eq!("Number of levels", st.doc_comment_text().unwrap());
}

#[test]
fn test_doc_comment_preserves_indents() {
    let file = SourceFile::parse(
        r#"
        /// doc1
        /// ```
        /// fn foo() {
        ///     // ...
        /// }
        /// ```
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!("doc1\n```\nfn foo() {\n    // ...\n}\n```", module.doc_comment_text().unwrap());
}

#[test]
fn test_doc_comment_preserves_newlines() {
    let file = SourceFile::parse(
        r#"
        /// this
        /// is
        /// mod
        /// foo
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!("this\nis\nmod\nfoo", module.doc_comment_text().unwrap());
}

#[test]
fn test_doc_comment_single_line_block_strips_suffix() {
    let file = SourceFile::parse(
        r#"
        /** this is mod foo*/
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!("this is mod foo", module.doc_comment_text().unwrap());
}

#[test]
fn test_doc_comment_single_line_block_strips_suffix_whitespace() {
    let file = SourceFile::parse(
        r#"
        /** this is mod foo */
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!("this is mod foo ", module.doc_comment_text().unwrap());
}

#[test]
fn test_doc_comment_multi_line_block_strips_suffix() {
    let file = SourceFile::parse(
        r#"
        /**
        this
        is
        mod foo
        */
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!(
        "        this\n        is\n        mod foo\n        ",
        module.doc_comment_text().unwrap()
    );
}

#[test]
fn test_comments_preserve_trailing_whitespace() {
    let file = SourceFile::parse(
        "\n/// Representation of a Realm.   \n/// In the specification these are called Realm Records.\nstruct Realm {}",
    )
    .ok()
    .unwrap();
    let def = file.syntax().descendants().find_map(StructDef::cast).unwrap();
    assert_eq!(
        "Representation of a Realm.   \nIn the specification these are called Realm Records.",
        def.doc_comment_text().unwrap()
    );
}

#[test]
fn test_four_slash_line_comment() {
    let file = SourceFile::parse(
        r#"
        //// too many slashes to be a doc comment
        /// doc comment
        mod foo {}
        "#,
    )
    .ok()
    .unwrap();
    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
    assert_eq!("doc comment", module.doc_comment_text().unwrap());
}

#[test]
fn test_where_predicates() {
    fn assert_bound(text: &str, bound: Option<TypeBound>) {
        assert_eq!(text, bound.unwrap().syntax().text().to_string());
    }

    let file = SourceFile::parse(
        r#"
fn foo()
where
   T: Clone + Copy + Debug + 'static,
   'a: 'b + 'c,
   Iterator::Item: 'a + Debug,
   Iterator::Item: Debug + 'a,
   <T as Iterator>::Item: Debug + 'a,
   for<'a> F: Fn(&'a str)
{}
        "#,
    )
    .ok()
    .unwrap();
    let where_clause = file.syntax().descendants().find_map(WhereClause::cast).unwrap();

    let mut predicates = where_clause.predicates();

    let pred = predicates.next().unwrap();
    let mut bounds = pred.type_bound_list().unwrap().bounds();

    assert_eq!("T", pred.type_ref().unwrap().syntax().text().to_string());
    assert_bound("Clone", bounds.next());
    assert_bound("Copy", bounds.next());
    assert_bound("Debug", bounds.next());
    assert_bound("'static", bounds.next());

    let pred = predicates.next().unwrap();
    let mut bounds = pred.type_bound_list().unwrap().bounds();

    assert_eq!("'a", pred.lifetime_token().unwrap().text());

    assert_bound("'b", bounds.next());
    assert_bound("'c", bounds.next());

    let pred = predicates.next().unwrap();
    let mut bounds = pred.type_bound_list().unwrap().bounds();

    assert_eq!("Iterator::Item", pred.type_ref().unwrap().syntax().text().to_string());
    assert_bound("'a", bounds.next());

    let pred = predicates.next().unwrap();
    let mut bounds = pred.type_bound_list().unwrap().bounds();

    assert_eq!("Iterator::Item", pred.type_ref().unwrap().syntax().text().to_string());
    assert_bound("Debug", bounds.next());
    assert_bound("'a", bounds.next());

    let pred = predicates.next().unwrap();
    let mut bounds = pred.type_bound_list().unwrap().bounds();

    assert_eq!("<T as Iterator>::Item", pred.type_ref().unwrap().syntax().text().to_string());
    assert_bound("Debug", bounds.next());
    assert_bound("'a", bounds.next());

    let pred = predicates.next().unwrap();
    let mut bounds = pred.type_bound_list().unwrap().bounds();

    assert_eq!("for<'a> F", pred.type_ref().unwrap().syntax().text().to_string());
    assert_bound("Fn(&'a str)", bounds.next());
}
<|endoftext|>


<commit_before>#[derive(Debug)]
pub struct ChessPosition {
    x: i32,
    y: i32,
}

impl ChessPosition {
    pub fn new(m: i32, n: i32) -> Result<ChessPosition, &'static str> {
        if m < 0 || m > 7 {
            return Result::Err("Invalid value for m");
        }

        if n < 0 || n > 7 {
            return Result::Err("Invalid value for n");
        }

        Result::Ok(ChessPosition{x:m, y:n})
    }
}

#[derive(Debug)]
pub struct Queen {
    pos: ChessPosition,
}

impl Queen {
    pub fn new(x: ChessPosition) -> Queen {
        Queen{pos: x}
    }

    pub fn can_attack(&self, q: &Queen) -> bool {
        if self.pos.x == q.pos.x {
            return true;
        }

        if self.pos.y == q.pos.y {
            return true;
        }

        let xdelta = (self.pos.x - q.pos.x).abs();
        let ydelta = (self.pos.y - q.pos.y).abs();

        xdelta == ydelta
    }
}<commit_msg>Simplify can_attack function<commit_after>#[derive(Debug)]
pub struct ChessPosition {
    x: i32,
    y: i32,
}

impl ChessPosition {
    pub fn new(m: i32, n: i32) -> Result<ChessPosition, &'static str> {
        if m < 0 || m > 7 {
            return Result::Err("Invalid value for m");
        }

        if n < 0 || n > 7 {
            return Result::Err("Invalid value for n");
        }

        Result::Ok(ChessPosition{x:m, y:n})
    }
}

#[derive(Debug)]
pub struct Queen {
    pos: ChessPosition,
}

impl Queen {
    pub fn new(x: ChessPosition) -> Queen {
        Queen{pos: x}
    }

    pub fn can_attack(&self, q: &Queen) -> bool {
        let xdelta = self.pos.x - q.pos.x;
        let ydelta = self.pos.y - q.pos.y;

        xdelta == 0 || ydelta == 0 || xdelta.abs() == ydelta.abs()
    }
}<|endoftext|>