<commit_before>extern crate collections;
extern crate sync;
extern crate rand;
use collections::HashMap;
use std::io::{File, Open, Read, Write, TempDir};
use std::num::Zero;
use sync::{Arc, RWLock};
use sync::Future;
use rand::task_rng;
use rand::distributions::{IndependentSample, Range};
use rand::distributions::range::SampleRange;

struct BufferManager {
	size: uint,
	entries: HashMap<u64, BufferEntry>,
	directory: TempDir,
}

struct BufferEntry {
	frame: Arc<RWLock<BufferFrame>>,
}

struct BufferFrame {
	pageId: u64,
	data: Vec<u8>,
}

/* Destructor trait implementation */
impl Drop for BufferManager {
	fn drop(&mut self) {
		println!("Dropping");
		// TODO
	}
}

impl BufferManager {
	pub fn new(size: uint) -> BufferManager {
		let h = HashMap::with_capacity(size);
		let dir = match TempDir::new("buffermanager") {
			Some(temp_dir) => temp_dir,
			None => fail!("creation of temporary directory"),
		};
		BufferManager {size: size, entries: h, directory: dir}
	}

	fn openOrCreate(&self, pageId: u64) -> File {
		let page_path = self.directory.path();
		let file_path = page_path.join(pageId.to_str());
		match File::open_mode(&file_path, Open, Read) {
			Ok(f) => f,
			Err(_) => {
				match File::open_mode(&file_path, Open, Write) {
					Ok(mut f) => {
						match f.write([0_u8, ..4 * 1024]) {
							Ok(_) => (),
							Err(e) => fail!("writing page failed: {}", e),
						}
						match File::open_mode(&file_path, Open, Read) {
							Ok(f) => f,
							Err(e) => fail!("failed reading file: {}", e),
						}
					},
					Err(e) => fail!("Writing file failed: {}", e),
				}
			},
		}
	}

	fn loadPage(&mut self, pageId: u64) {
		if self.entries.len() == self.size {
			self.evictPage();
		}

		let mut file_handle = self.openOrCreate(pageId);
		//let content = match file_handle.read_exact(4*1024) {
		let content = match file_handle.read_exact(10) {
			Ok(c) => Vec::from_slice(c),
			Err(e) => fail!("Couldn't read from page: {}", e),
		};

		let frame = BufferFrame {data: content, pageId: pageId};
		let entry = BufferEntry {frame: Arc::new(RWLock::new(frame))};
		self.entries.insert(pageId, entry);
	}

	fn evictPage(&mut self) {
		let random_key = {
			let mut iter = self.entries.keys();
			sample(&mut iter).map(|v| v.clone())
		};

		match random_key {
			None => false,
			Some(key) => self.entries.remove(&key),
		};
	}

	pub fn fix_page(&mut self, pageId: u64) -> Option<Arc<RWLock<BufferFrame>>> {
		if !self.entries.contains_key(&pageId) {
			self.loadPage(pageId);
		}
		let entry = self.entries.get(&pageId);
		// Arcs can be cloned and they will all point to the same RWLock
		Some(entry.frame.clone())
	}

	pub fn unfix_page(&mut self, frame: Arc<RWLock<BufferFrame>>, is_dirty: bool) {
		if !is_dirty {
			return;
		}
		let frame = frame.read();
		println!("writing back {}", frame.pageId);
		self.writePage(frame.pageId, frame.get_data());
	}

	fn writePage(&mut self, pageId: u64, data: &[u8]) {
		let page_path = self.directory.path();
		let file_path = page_path.join(pageId.to_str());
		let mut handle = match File::open_mode(&file_path, Open, Write) {
			Ok(handle) => handle,
			Err(e) => fail!("Opening file for writing failed: {}", e),
		};

		match handle.write(data) {
			Ok(_) => (),
			Err(e) => fail!("Writing to file failed: {}", e),
		};
	}
}

/*
 * A buffer frame, the unit that gets the data
 */
impl BufferFrame {
	/*
	 * get_mut_data returns a borrowed reference to a mutable slice to the vector
	 * contents.
	 */
	pub fn get_mut_data<'a>(&'a mut self) -> &'a mut [u8] {
		self.data.as_mut_slice()
	}
	pub fn get_data<'a>(&'a self) -> &'a [u8] {
		self.data.as_slice()
	}
}

fn sample<'a, T, I:Iterator<T>>(from: &'a mut I) -> Option<T> {
	let from: ~[T] = from.collect();
	let l = from.len();
	if l == 0 {
		return None;
	}
	let index = randrange(l);
	Some(from[index])
}

fn randrange<X: SampleRange + Ord + Zero>(high: X) -> X {
	let between: Range<X> = Range::new(Zero::zero(), high);
	let mut rng = rand::task_rng();
	let res = between.ind_sample(&mut rng);
	res
}

#[test]
fn test_create() {
	let mut bm = BufferManager::new(16);
	let pageref = match bm.fix_page(42) {
		Some(p) => p,
		None => fail!("Getting page failed"),
	};
	{
		let mut page = pageref.write();
		let data = page.get_mut_data();
		data[0] = 42;
		//println!("data: {}", Vec::from_slice(data));
	}
	bm.unfix_page(pageref, true);
	fail!("always");
}

#[test]
fn test_threads() {
	use rand::random;
	use std::task::spawn;

	let pages_in_ram = 1;
	let pages_on_disk: u64 = 20;
	let thread_count = 3;
	let mut buffermanager = BufferManager::new(pages_in_ram);

	for i in range(0, pages_on_disk) {
		let bf = match buffermanager.fix_page(i) {
			Some(frame) => frame,
			None => fail!("Couldn't fix page {}", i),
		};
		{
			let mut lock = bf.write();
			lock.get_mut_data()[0] = 0;
		}
		buffermanager.unfix_page(bf, true);
	}
	let bm = Arc::new(RWLock::new(buffermanager));

	// start read/write threads
	let mut rw_threads: Vec<Future<int>> = Vec::new();
	for _ in range(0, thread_count) {
		let bm = bm.clone();
		rw_threads.push(Future::spawn(proc() {
			let is_write = random::<bool>();
			println!("Creating new {} task",
				if is_write {"write"} else {"read"});
			let page_number = randrange(pages_on_disk);
			let mut bm = bm.write();
			if is_write {
				let bf = match bm.fix_page(page_number) {
					Some(frame) => frame,
					None => fail!("Cound't fix page"),
				};
				{
					println!("Wrote to page");
					let mut lock = bf.write();
					let data = lock.get_mut_data();
					data[0] = data[0] + 1;
					println!("data: {}", Vec::from_slice(data));
				}
				bm.unfix_page(bf, is_write);
			} else {
				let bf = match bm.fix_page(page_number) {
					Some(frame) => frame,
					None => fail!("Cound't fix page"),
				};
				bm.unfix_page(bf, is_write);
			}
			// return whether we wrote (1) or read (0) as future
			if is_write {1} else {0}
		}));
	}

	// Rust does not have join, but we can wait on Futures which does the same
	let total_count = rw_threads.mut_iter().fold(0, |acc, val| acc + val.get());
	println!("total_count: {}", total_count);

	// TODO: add scan thread

	//let mut bm = BufferManager::new(pages_in_ram);
	let mut bm = bm.write();
	let mut total_count_on_disk = 0;
	for i in range(0, pages_on_disk) {
		let bf = match bm.fix_page(i) {
			Some(frame) => frame,
			None => fail!("Couldn't fix page")
		};
		let value = {
			let lock = bf.read();
			let data = lock.get_data();
			data[0]
		};
		bm.unfix_page(bf, false);
		total_count_on_disk += value;
	}
	println!("Total count on disk: {}", total_count_on_disk);

	fail!("always");
}
<commit_msg>Rust code style (naming)<commit_after>extern crate collections;
extern crate sync;
extern crate rand;
use collections::HashMap;
use std::io::{File, Open, Read, Write, TempDir};
use std::num::Zero;
use sync::{Arc, RWLock};
use sync::Future;
use rand::task_rng;
use rand::distributions::{IndependentSample, Range};
use rand::distributions::range::SampleRange;

struct BufferManager {
	size: uint,
	entries: HashMap<u64, BufferEntry>,
	directory: TempDir,
}

struct BufferEntry {
	frame: Arc<RWLock<BufferFrame>>,
}

struct BufferFrame {
	page_id: u64,
	data: Vec<u8>,
}

/* Destructor trait implementation */
impl Drop for BufferManager {
	fn drop(&mut self) {
		println!("Dropping");
		// TODO
	}
}

impl BufferManager {
	pub fn new(size: uint) -> BufferManager {
		let h = HashMap::with_capacity(size);
		let dir = match TempDir::new("buffermanager") {
			Some(temp_dir) => temp_dir,
			None => fail!("creation of temporary directory"),
		};
		BufferManager {size: size, entries: h, directory: dir}
	}

	fn open_or_create(&self, page_id: u64) -> File {
		let page_path = self.directory.path();
		let file_path = page_path.join(page_id.to_str());
		match File::open_mode(&file_path, Open, Read) {
			Ok(f) => f,
			Err(_) => {
				match File::open_mode(&file_path, Open, Write) {
					Ok(mut f) => {
						match f.write([0_u8, ..4 * 1024]) {
							Ok(_) => (),
							Err(e) => fail!("writing page failed: {}", e),
						}
						match File::open_mode(&file_path, Open, Read) {
							Ok(f) => f,
							Err(e) => fail!("failed reading file: {}", e),
						}
					},
					Err(e) => fail!("Writing file failed: {}", e),
				}
			},
		}
	}

	fn load_page(&mut self, page_id: u64) {
		if self.entries.len() == self.size {
			self.evict_page();
		}

		let mut file_handle = self.open_or_create(page_id);
		//let content = match file_handle.read_exact(4*1024) {
		let content = match file_handle.read_exact(10) {
			Ok(c) => Vec::from_slice(c),
			Err(e) => fail!("Couldn't read from page: {}", e),
		};

		let frame = BufferFrame {data: content, page_id: page_id};
		let entry = BufferEntry {frame: Arc::new(RWLock::new(frame))};
		self.entries.insert(page_id, entry);
	}

	fn evict_page(&mut self) {
		let random_key = {
			let mut iter = self.entries.keys();
			sample(&mut iter).map(|v| v.clone())
		};

		match random_key {
			None => false,
			Some(key) => self.entries.remove(&key),
		};
	}

	pub fn fix_page(&mut self, page_id: u64) -> Option<Arc<RWLock<BufferFrame>>> {
		if !self.entries.contains_key(&page_id) {
			self.load_page(page_id);
		}
		let entry = self.entries.get(&page_id);
		// Arcs can be cloned and they will all point to the same RWLock
		Some(entry.frame.clone())
	}

	pub fn unfix_page(&mut self, frame: Arc<RWLock<BufferFrame>>, is_dirty: bool) {
		if !is_dirty {
			return;
		}
		let frame = frame.read();
		println!("writing back {}", frame.page_id);
		self.write_page(frame.page_id, frame.get_data());
	}

	fn write_page(&mut self, page_id: u64, data: &[u8]) {
		let page_path = self.directory.path();
		let file_path = page_path.join(page_id.to_str());
		let mut handle = match File::open_mode(&file_path, Open, Write) {
			Ok(handle) => handle,
			Err(e) => fail!("Opening file for writing failed: {}", e),
		};

		match handle.write(data) {
			Ok(_) => (),
			Err(e) => fail!("Writing to file failed: {}", e),
		};
	}
}

/*
 * A buffer frame, the unit that gets the data
 */
impl BufferFrame {
	/*
	 * get_mut_data returns a borrowed reference to a mutable slice to the vector
	 * contents.
	 */
	pub fn get_mut_data<'a>(&'a mut self) -> &'a mut [u8] {
		self.data.as_mut_slice()
	}
	pub fn get_data<'a>(&'a self) -> &'a [u8] {
		self.data.as_slice()
	}
}

fn sample<'a, T, I:Iterator<T>>(from: &'a mut I) -> Option<T> {
	let from: ~[T] = from.collect();
	let l = from.len();
	if l == 0 {
		return None;
	}
	let index = randrange(l);
	Some(from[index])
}

fn randrange<X: SampleRange + Ord + Zero>(high: X) -> X {
	let between: Range<X> = Range::new(Zero::zero(), high);
	let mut rng = rand::task_rng();
	between.ind_sample(&mut rng)
}

#[test]
fn test_create() {
	let mut bm = BufferManager::new(16);
	let pageref = match bm.fix_page(42) {
		Some(p) => p,
		None => fail!("Getting page failed"),
	};
	{
		let mut page = pageref.write();
		let data = page.get_mut_data();
		data[0] = 42;
		//println!("data: {}", Vec::from_slice(data));
	}
	bm.unfix_page(pageref, true);
	fail!("always");
}

#[test]
fn test_threads() {
	use rand::random;
	use std::task::spawn;

	let pages_in_ram = 1;
	let pages_on_disk: u64 = 20;
	let thread_count = 3;
	let mut buffermanager = BufferManager::new(pages_in_ram);

	for i in range(0, pages_on_disk) {
		let bf = match buffermanager.fix_page(i) {
			Some(frame) => frame,
			None => fail!("Couldn't fix page {}", i),
		};
		{
			let mut lock = bf.write();
			lock.get_mut_data()[0] = 0;
		}
		buffermanager.unfix_page(bf, true);
	}
	let bm = Arc::new(RWLock::new(buffermanager));

	// start read/write threads
	let mut rw_threads: Vec<Future<int>> = Vec::new();
	for _ in range(0, thread_count) {
		let bm = bm.clone();
		rw_threads.push(Future::spawn(proc() {
			let is_write = random::<bool>();
			println!("Creating new {} task",
				if is_write {"write"} else {"read"});
			let page_number = randrange(pages_on_disk);
			let mut bm = bm.write();
			if is_write {
				let bf = match bm.fix_page(page_number) {
					Some(frame) => frame,
					None => fail!("Cound't fix page"),
				};
				{
					println!("Wrote to page");
					let mut lock = bf.write();
					let data = lock.get_mut_data();
					data[0] = data[0] + 1;
					println!("data: {}", Vec::from_slice(data));
				}
				bm.unfix_page(bf, is_write);
			} else {
				let bf = match bm.fix_page(page_number) {
					Some(frame) => frame,
					None => fail!("Cound't fix page"),
				};
				bm.unfix_page(bf, is_write);
			}
			// return whether we wrote (1) or read (0) as future
			if is_write {1} else {0}
		}));
	}

	// Rust does not have join, but we can wait on Futures which does the same
	let total_count = rw_threads.mut_iter().fold(0, |acc, val| acc + val.get());
	println!("total_count: {}", total_count);

	// TODO: add scan thread

	//let mut bm = BufferManager::new(pages_in_ram);
	let mut bm = bm.write();
	let mut total_count_on_disk = 0;
	for i in range(0, pages_on_disk) {
		let bf = match bm.fix_page(i) {
			Some(frame) => frame,
			None => fail!("Couldn't fix page")
		};
		let value = {
			let lock = bf.read();
			let data = lock.get_data();
			data[0]
		};
		bm.unfix_page(bf, false);
		total_count_on_disk += value;
	}
	println!("Total count on disk: {}", total_count_on_disk);

	fail!("always");
}
<|endoftext|>


<commit_before>//! A minimal implementation of SHA1 for rust.
//!
//! Example:
//!
//! ```rust
//! ```

/// `factorial` is  a function that returns a n! from n
///
/// # Arguments
///
/// * `n` - The non-negative number to calculate the factorial to
///
/// # Example
/// ```
/// // example
/// let n: u64 = 5u64;
/// let result: u64 = hello_world::factorial(n);
/// println!("{}! = {}", n, result); // prints "5! = 120"
/// ```
pub fn factorial(n : u64) -> u64 {
    match n {
      1 | 0 => 1,
      _ => n * factorial(n - 1),
    }
}

fn main(){
    let o: u64 = 5u64;
    let x = factorial(o);

    // this is rust line comment
    // see it in action
    // e.g. we format x as string with base 2 (binary)
    //   and use it's length to count bits in the number
    let y = format!("{:b}", x).len() as u64;
    // nad now we print it
    println!("factorial({}) = {} ({:b} / {})", o, x, x, y);

    let q = 15u64;
    if y < q {
        println!("x < {} = {}", q, x);
    }

    let u = if x < 5u64 { 0i } else { 1i };
    println!("u = {}", u);

    let u1 = if x < 5u64 { 0i; } else { 1i; };
    println!("u1 = {}", u1);
}
<commit_msg>fix formatting (breaking change)<commit_after>//! A minimal implementation of SHA1 for rust.
//!
//! Example:
//!
//! ```rust
//! ```

/// `factorial` is  a function that returns a n! from n
///
/// # Arguments
///
/// * `n` - The non-negative number to calculate the factorial to
///
/// # Example
/// ```
/// // example
/// let n: u64 = 5u64;
/// let result: u64 = hello_world::factorial(n);
/// println!("{}! = {}", n, result); // prints "5! = 120"
/// ```
pub fn factorial(n : u64) -> u64 {
    match n {
      1 | 0 => 1,
      _ => n * factorial(n - 1),
    }
}

fn main(){
    let o: u64 = 5u64;
    let x = factorial(o);

    // this is rust line comment
    // see it in action
    // e.g. we format x as string with base 2 (binary)
    //   and use it's length to count bits in the number
    let y = format!("{:b}", x).len() as u64;
    // nad now we print it
    println!("factorial({}) = {} ({:b} / {})", o, x, x, y);

    let q = 15u64;
    if y < q {
        println!("x < {} = {}", q, x);
    }

    let u = if x < 5u64 { 0i } else { 1i };
    println!("u = {}", u);

    let u1 = if x < 5u64 { 0i; } else { 1i; };
    println!("u1 = {:?}", u1);
}
<|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 graphql_syntax::{Identifier, OperationDefinition, VariableDefinition};
use interner::StringKey;
use lsp_types::{HoverContents, MarkedString};
use schema::{SDLSchema, Schema};
use schema_documentation::SchemaDocumentation;

use crate::{
    hover::{get_open_schema_explorer_command_link, GraphQLSchemaExplorerParams},
    resolution_path::{
        ArgumentPath, ArgumentRoot, ConstantArgPath, ConstantBooleanPath, ConstantEnumPath,
        ConstantFloatPath, ConstantIntPath, ConstantListPath, ConstantNullPath, ConstantObjPath,
        ConstantStringPath, ConstantValueParent, ConstantValuePath, ConstantValueRoot,
        DefaultValuePath, IdentParent, IdentPath, ListTypeAnnotationPath, NamedTypeAnnotationPath,
        NonNullTypeAnnotationPath, OperationDefinitionPath, OperationPath, ResolutionPath,
        SelectionPath, TypeAnnotationPath, VariableDefinitionPath, VariableIdentifierParent,
        VariableIdentifierPath,
    },
    LSPExtraDataProvider,
};

pub(crate) fn hover_with_node_resolution_path<'a>(
    path: ResolutionPath<'a>,
    schema: &SDLSchema,
    schema_name: StringKey,
    extra_data_provider: &dyn LSPExtraDataProvider,
    schema_documentation: &impl SchemaDocumentation,
) -> Option<HoverContents> {
    match path {
        // Show query stats on the operation definition name
        // and "query/subscription/mutation" token
        ResolutionPath::Ident(IdentPath {
            inner: _,
            parent:
                IdentParent::OperationDefinitionName(OperationDefinitionPath {
                    inner: operation_definition,
                    parent: _,
                }),
        }) => on_hover_operation(operation_definition, extra_data_provider),
        ResolutionPath::Operation(OperationPath {
            inner: _,
            parent:
                OperationDefinitionPath {
                    inner: operation_definition,
                    parent: _,
                },
        }) => on_hover_operation(operation_definition, extra_data_provider),
        // Explicity show no hover in other parts of the operation definition.
        // For example, the curly braces after the operation variables are included in
        // this match arm.
        ResolutionPath::OperationDefinition(_) => None,

        // Variables definition for both operations and fragments (?):
        // Name, Type and Default Value => info about variable and link to types
        // Directives handled later
        ResolutionPath::VariableIdentifier(VariableIdentifierPath {
            inner: _,
            parent:
                VariableIdentifierParent::VariableDefinition(VariableDefinitionPath {
                    inner: variable_definition,
                    parent: _,
                }),
        }) => Some(on_hover_variable_definition(
            variable_definition,
            schema_name,
        )),
        ResolutionPath::DefaultValue(DefaultValuePath {
            inner: _,
            parent:
                VariableDefinitionPath {
                    inner: variable_definition,
                    parent: _,
                },
        }) => Some(on_hover_variable_definition(
            variable_definition,
            schema_name,
        )),
        ResolutionPath::VariableDefinition(VariableDefinitionPath {
            inner: variable_definition,
            parent: _,
        }) => Some(on_hover_variable_definition(
            variable_definition,
            schema_name,
        )),
        ResolutionPath::NonNullTypeAnnotation(NonNullTypeAnnotationPath {
            inner: _,
            parent: non_null_annotation_parent,
        }) => Some(on_hover_variable_definition(
            non_null_annotation_parent
                .parent
                .find_variable_definition_path()
                .inner,
            schema_name,
        )),
        ResolutionPath::ListTypeAnnotation(ListTypeAnnotationPath {
            inner: _,
            parent: list_type_annotation_parent,
        }) => Some(on_hover_variable_definition(
            list_type_annotation_parent
                .parent
                .find_variable_definition_path()
                .inner,
            schema_name,
        )),
        ResolutionPath::Ident(IdentPath {
            inner: _,
            parent:
                IdentParent::NamedTypeAnnotation(NamedTypeAnnotationPath {
                    inner: _,
                    parent:
                        TypeAnnotationPath {
                            inner: _,
                            parent: type_annotation_parent,
                        },
                }),
        }) => Some(on_hover_variable_definition(
            type_annotation_parent.find_variable_definition_path().inner,
            schema_name,
        )),

        // Explicitly don't show hovers for VariableDefinitionList
        ResolutionPath::VariableDefinitionList(_) => None,

        // Constant values can either be rooted in variable definitions or arguments to
        // directives or fields. Handle those cases.
        ResolutionPath::ConstantInt(ConstantIntPath {
            inner: _,
            parent:
                ConstantValuePath {
                    inner: _,
                    parent: constant_value_parent,
                },
        }) => on_hover_constant_value(
            &constant_value_parent,
            schema,
            schema_name,
            schema_documentation,
        ),
        ResolutionPath::ConstantFloat(ConstantFloatPath {
            inner: _,
            parent:
                ConstantValuePath {
                    inner: _,
                    parent: constant_value_parent,
                },
        }) => on_hover_constant_value(
            &constant_value_parent,
            schema,
            schema_name,
            schema_documentation,
        ),
        ResolutionPath::ConstantString(ConstantStringPath {
            inner: _,
            parent:
                ConstantValuePath {
                    inner: _,
                    parent: constant_value_parent,
                },
        }) => on_hover_constant_value(
            &constant_value_parent,
            schema,
            schema_name,
            schema_documentation,
        ),
        ResolutionPath::ConstantBoolean(ConstantBooleanPath {
            inner: _,
            parent:
                ConstantValuePath {
                    inner: _,
                    parent: constant_value_parent,
                },
        }) => on_hover_constant_value(
            &constant_value_parent,
            schema,
            schema_name,
            schema_documentation,
        ),
        ResolutionPath::ConstantNull(ConstantNullPath {
            inner: _,
            parent:
                ConstantValuePath {
                    inner: _,
                    parent: constant_value_parent,
                },
        }) => on_hover_constant_value(
            &constant_value_parent,
            schema,
            schema_name,
            schema_documentation,
        ),
        ResolutionPath::ConstantEnum(ConstantEnumPath {
            inner: _,
            parent:
                ConstantValuePath {
                    inner: _,
                    parent: constant_value_parent,
                },
        }) => on_hover_constant_value(
            &constant_value_parent,
            schema,
            schema_name,
            schema_documentation,
        ),
        ResolutionPath::ConstantList(ConstantListPath {
            inner: _,
            parent: constant_value_path,
        }) => on_hover_constant_value(
            &constant_value_path.parent,
            schema,
            schema_name,
            schema_documentation,
        ),
        ResolutionPath::ConstantObj(ConstantObjPath {
            inner: _,
            parent: constant_value_path,
        }) => on_hover_constant_value(
            &constant_value_path.parent,
            schema,
            schema_name,
            schema_documentation,
        ),
        ResolutionPath::ConstantArg(ConstantArgPath {
            inner: _,
            parent: constant_obj_path,
        }) => on_hover_constant_value(
            &constant_obj_path.parent.parent,
            schema,
            schema_name,
            schema_documentation,
        ),

        _ => None,
    }
}

fn on_hover_operation(
    operation_definition: &OperationDefinition,
    extra_data_provider: &dyn LSPExtraDataProvider,
) -> Option<HoverContents> {
    let name = operation_definition.name.as_ref()?;
    let extra_data = extra_data_provider.fetch_query_stats(name.value.lookup());
    if !extra_data.is_empty() {
        Some(HoverContents::Array(
            extra_data
                .iter()
                .map(|str| MarkedString::String(str.to_string()))
                .collect::<_>(),
        ))
    } else {
        None
    }
}

fn on_hover_variable_definition(
    variable_definition: &VariableDefinition,
    schema_name: StringKey,
) -> HoverContents {
    let variable_identifier = &variable_definition.name;
    let variable_inner_type = variable_definition.type_.inner().name.value;
    let variable_type = &variable_definition.type_;
    let variable_default_value = variable_definition
        .default_value
        .as_ref()
        .map(|default_value| format!(" with default value `{}`", default_value.value))
        .unwrap_or_else(|| "".to_string());

    HoverContents::Scalar(MarkedString::String(format!(
        "`{}`: **{}**{}",
        variable_identifier,
        get_open_schema_explorer_command_link(
            &variable_type.to_string(),
            &GraphQLSchemaExplorerParams {
                path: vec![variable_inner_type.lookup()],
                schema_name: schema_name.lookup(),
                filter: None,
            }
        ),
        variable_default_value
    )))
}

fn on_hover_constant_value<'a>(
    constant_value_parent: &'a ConstantValueParent<'a>,
    schema: &SDLSchema,
    schema_name: StringKey,
    schema_documentation: &impl SchemaDocumentation,
) -> Option<HoverContents> {
    match constant_value_parent.find_constant_value_root() {
        ConstantValueRoot::VariableDefinition(variable_definition_path) => Some(
            on_hover_variable_definition(variable_definition_path.inner, schema_name),
        ),
        ConstantValueRoot::Argument(argument_path) => {
            on_hover_argument_path(argument_path, schema, schema_name, schema_documentation)
        }
    }
}

fn on_hover_argument_path<'a>(
    argument_path: &ArgumentPath<'a>,
    schema: &SDLSchema,
    schema_name: StringKey,
    schema_documentation: &impl SchemaDocumentation,
) -> Option<HoverContents> {
    let ArgumentPath {
        inner: argument,
        parent,
    } = argument_path;

    let argument_name = argument.name.value.lookup();
    let argument_value = argument.value.to_string();
    let argument_info =
        MarkedString::String(format!("Argument `{}: {}`", argument_name, argument_value));

    let field_hover_info = match parent.find_argument_root() {
        ArgumentRoot::LinkedField(linked_field_path) => get_scalar_or_linked_field_hover_content(
            &linked_field_path.inner.name,
            &linked_field_path.parent,
            schema,
            schema_name,
            schema_documentation,
        ),
        // TODO call on_hover_directive when that function is written
        ArgumentRoot::Directive(_) => Some(vec![]),
        ArgumentRoot::ScalarField(scalar_field_path) => get_scalar_or_linked_field_hover_content(
            &scalar_field_path.inner.name,
            &scalar_field_path.parent,
            schema,
            schema_name,
            schema_documentation,
        ),
    }?;

    let mut contents = vec![argument_info];
    contents.extend(field_hover_info.into_iter());

    Some(HoverContents::Array(contents))
}

fn get_scalar_or_linked_field_hover_content(
    field_name: &Identifier,
    field_selection_path: &SelectionPath<'_>,
    schema: &SDLSchema,
    schema_name: StringKey,
    schema_documentation: &impl SchemaDocumentation,
) -> Option<Vec<MarkedString>> {
    let parent_types = field_selection_path.parent.find_type_path(schema);
    let parent_type = parent_types.last()?;

    let mut type_path = parent_types
        .iter()
        .map(|parent_type| schema.get_type_name(*parent_type).lookup())
        .collect::<Vec<_>>();
    let parent_type_name = schema.get_type_name(*parent_type).lookup();

    let field = schema
        .named_field(*parent_type, field_name.value)
        .map(|id| schema.field(id))?;

    let rendered_type_string = schema.get_type_string(&field.type_);
    let field_type_name = schema.get_type_name(field.type_.inner()).lookup();

    let mut hover_contents: Vec<MarkedString> =
        vec![MarkedString::String(format!("Field: **{}**", field.name))];

    if let Some(field_description) =
        schema_documentation.get_field_description(parent_type_name, field.name.lookup())
    {
        hover_contents.push(MarkedString::String(field_description.to_string()));
    }

    type_path.push(field_type_name);

    hover_contents.push(MarkedString::String(format!(
        "Type: **{}**",
        get_open_schema_explorer_command_link(
            &rendered_type_string,
            &GraphQLSchemaExplorerParams {
                path: type_path,
                schema_name: schema_name.lookup(),
                filter: None,
            }
        )
    )));

    if let Some(type_description) = schema_documentation.get_type_description(field_type_name) {
        hover_contents.push(MarkedString::String(type_description.to_string()));
    }

    if !field.arguments.is_empty() {
        hover_contents.push(MarkedString::String(
            "This field accepts these arguments".to_string(),
        ));

        for arg in field.arguments.iter() {
            let arg_type_name = schema.get_type_name(arg.type_.inner()).lookup();
            hover_contents.push(MarkedString::from_markdown(format!(
                "{}: **{}**{}\n\n{}",
                arg.name,
                get_open_schema_explorer_command_link(
                    &schema.get_type_string(&arg.type_),
                    &GraphQLSchemaExplorerParams {
                        path: vec![field_type_name, arg_type_name],
                        schema_name: schema_name.lookup(),
                        filter: None,
                    }
                ),
                if let Some(default_value) = &arg.default_value {
                    format!(" = {}", default_value)
                } else {
                    "".to_string()
                },
                if let Some(description) = schema_documentation.get_field_argument_description(
                    parent_type_name,
                    field.name.lookup(),
                    arg.name.lookup(),
                ) {
                    description.to_string()
                } else {
                    "".to_string()
                }
            )));
        }
    }
    Some(hover_contents)
}
<commit_msg>Handle scalar and linked fields using Resolution Path<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 graphql_syntax::{Identifier, OperationDefinition, VariableDefinition};
use interner::StringKey;
use lsp_types::{HoverContents, MarkedString};
use schema::{SDLSchema, Schema};
use schema_documentation::SchemaDocumentation;

use crate::{
    hover::{get_open_schema_explorer_command_link, GraphQLSchemaExplorerParams},
    resolution_path::{
        ArgumentPath, ArgumentRoot, ConstantArgPath, ConstantBooleanPath, ConstantEnumPath,
        ConstantFloatPath, ConstantIntPath, ConstantListPath, ConstantNullPath, ConstantObjPath,
        ConstantStringPath, ConstantValueParent, ConstantValuePath, ConstantValueRoot,
        DefaultValuePath, IdentParent, IdentPath, LinkedFieldPath, ListTypeAnnotationPath,
        NamedTypeAnnotationPath, NonNullTypeAnnotationPath, OperationDefinitionPath, OperationPath,
        ResolutionPath, ScalarFieldPath, SelectionPath, TypeAnnotationPath, VariableDefinitionPath,
        VariableIdentifierParent, VariableIdentifierPath,
    },
    LSPExtraDataProvider,
};

pub(crate) fn hover_with_node_resolution_path<'a>(
    path: ResolutionPath<'a>,
    schema: &SDLSchema,
    schema_name: StringKey,
    extra_data_provider: &dyn LSPExtraDataProvider,
    schema_documentation: &impl SchemaDocumentation,
) -> Option<HoverContents> {
    match path {
        // Show query stats on the operation definition name
        // and "query/subscription/mutation" token
        ResolutionPath::Ident(IdentPath {
            inner: _,
            parent:
                IdentParent::OperationDefinitionName(OperationDefinitionPath {
                    inner: operation_definition,
                    parent: _,
                }),
        }) => on_hover_operation(operation_definition, extra_data_provider),
        ResolutionPath::Operation(OperationPath {
            inner: _,
            parent:
                OperationDefinitionPath {
                    inner: operation_definition,
                    parent: _,
                },
        }) => on_hover_operation(operation_definition, extra_data_provider),
        // Explicity show no hover in other parts of the operation definition.
        // For example, the curly braces after the operation variables are included in
        // this match arm.
        ResolutionPath::OperationDefinition(_) => None,

        // Variables definition for both operations and fragments (?):
        // Name, Type and Default Value => info about variable and link to types
        // Directives handled later
        ResolutionPath::VariableIdentifier(VariableIdentifierPath {
            inner: _,
            parent:
                VariableIdentifierParent::VariableDefinition(VariableDefinitionPath {
                    inner: variable_definition,
                    parent: _,
                }),
        }) => Some(on_hover_variable_definition(
            variable_definition,
            schema_name,
        )),
        ResolutionPath::DefaultValue(DefaultValuePath {
            inner: _,
            parent:
                VariableDefinitionPath {
                    inner: variable_definition,
                    parent: _,
                },
        }) => Some(on_hover_variable_definition(
            variable_definition,
            schema_name,
        )),
        ResolutionPath::VariableDefinition(VariableDefinitionPath {
            inner: variable_definition,
            parent: _,
        }) => Some(on_hover_variable_definition(
            variable_definition,
            schema_name,
        )),
        ResolutionPath::NonNullTypeAnnotation(NonNullTypeAnnotationPath {
            inner: _,
            parent: non_null_annotation_parent,
        }) => Some(on_hover_variable_definition(
            non_null_annotation_parent
                .parent
                .find_variable_definition_path()
                .inner,
            schema_name,
        )),
        ResolutionPath::ListTypeAnnotation(ListTypeAnnotationPath {
            inner: _,
            parent: list_type_annotation_parent,
        }) => Some(on_hover_variable_definition(
            list_type_annotation_parent
                .parent
                .find_variable_definition_path()
                .inner,
            schema_name,
        )),
        ResolutionPath::Ident(IdentPath {
            inner: _,
            parent:
                IdentParent::NamedTypeAnnotation(NamedTypeAnnotationPath {
                    inner: _,
                    parent:
                        TypeAnnotationPath {
                            inner: _,
                            parent: type_annotation_parent,
                        },
                }),
        }) => Some(on_hover_variable_definition(
            type_annotation_parent.find_variable_definition_path().inner,
            schema_name,
        )),

        // Explicitly don't show hovers for VariableDefinitionList
        ResolutionPath::VariableDefinitionList(_) => None,

        // Constant values can either be rooted in variable definitions or arguments to
        // directives or fields. Handle those cases.
        ResolutionPath::ConstantInt(ConstantIntPath {
            inner: _,
            parent:
                ConstantValuePath {
                    inner: _,
                    parent: constant_value_parent,
                },
        }) => on_hover_constant_value(
            &constant_value_parent,
            schema,
            schema_name,
            schema_documentation,
        ),
        ResolutionPath::ConstantFloat(ConstantFloatPath {
            inner: _,
            parent:
                ConstantValuePath {
                    inner: _,
                    parent: constant_value_parent,
                },
        }) => on_hover_constant_value(
            &constant_value_parent,
            schema,
            schema_name,
            schema_documentation,
        ),
        ResolutionPath::ConstantString(ConstantStringPath {
            inner: _,
            parent:
                ConstantValuePath {
                    inner: _,
                    parent: constant_value_parent,
                },
        }) => on_hover_constant_value(
            &constant_value_parent,
            schema,
            schema_name,
            schema_documentation,
        ),
        ResolutionPath::ConstantBoolean(ConstantBooleanPath {
            inner: _,
            parent:
                ConstantValuePath {
                    inner: _,
                    parent: constant_value_parent,
                },
        }) => on_hover_constant_value(
            &constant_value_parent,
            schema,
            schema_name,
            schema_documentation,
        ),
        ResolutionPath::ConstantNull(ConstantNullPath {
            inner: _,
            parent:
                ConstantValuePath {
                    inner: _,
                    parent: constant_value_parent,
                },
        }) => on_hover_constant_value(
            &constant_value_parent,
            schema,
            schema_name,
            schema_documentation,
        ),
        ResolutionPath::ConstantEnum(ConstantEnumPath {
            inner: _,
            parent:
                ConstantValuePath {
                    inner: _,
                    parent: constant_value_parent,
                },
        }) => on_hover_constant_value(
            &constant_value_parent,
            schema,
            schema_name,
            schema_documentation,
        ),
        ResolutionPath::ConstantList(ConstantListPath {
            inner: _,
            parent: constant_value_path,
        }) => on_hover_constant_value(
            &constant_value_path.parent,
            schema,
            schema_name,
            schema_documentation,
        ),
        ResolutionPath::ConstantObj(ConstantObjPath {
            inner: _,
            parent: constant_value_path,
        }) => on_hover_constant_value(
            &constant_value_path.parent,
            schema,
            schema_name,
            schema_documentation,
        ),
        ResolutionPath::ConstantArg(ConstantArgPath {
            inner: _,
            parent: constant_obj_path,
        }) => on_hover_constant_value(
            &constant_obj_path.parent.parent,
            schema,
            schema_name,
            schema_documentation,
        ),

        // Scalar and linked fields
        ResolutionPath::ScalarField(ScalarFieldPath {
            inner: scalar_field,
            parent: selection_path,
        }) => on_hover_scalar_or_linked_field(
            &scalar_field.name,
            &selection_path,
            schema,
            schema_name,
            schema_documentation,
        ),
        ResolutionPath::Ident(IdentPath {
            inner: _,
            parent:
                IdentParent::ScalarFieldAlias(ScalarFieldPath {
                    inner: scalar_field,
                    parent: selection_path,
                }),
        }) => on_hover_scalar_or_linked_field(
            &scalar_field.name,
            &selection_path,
            schema,
            schema_name,
            schema_documentation,
        ),
        ResolutionPath::Ident(IdentPath {
            inner: _,
            parent:
                IdentParent::ScalarFieldName(ScalarFieldPath {
                    inner: scalar_field,
                    parent: selection_path,
                }),
        }) => on_hover_scalar_or_linked_field(
            &scalar_field.name,
            &selection_path,
            schema,
            schema_name,
            schema_documentation,
        ),
        ResolutionPath::LinkedField(LinkedFieldPath {
            inner: scalar_field,
            parent: selection_path,
        }) => on_hover_scalar_or_linked_field(
            &scalar_field.name,
            &selection_path,
            schema,
            schema_name,
            schema_documentation,
        ),
        ResolutionPath::Ident(IdentPath {
            inner: _,
            parent:
                IdentParent::LinkedFieldAlias(LinkedFieldPath {
                    inner: scalar_field,
                    parent: selection_path,
                }),
        }) => on_hover_scalar_or_linked_field(
            &scalar_field.name,
            &selection_path,
            schema,
            schema_name,
            schema_documentation,
        ),
        ResolutionPath::Ident(IdentPath {
            inner: _,
            parent:
                IdentParent::LinkedFieldName(LinkedFieldPath {
                    inner: scalar_field,
                    parent: selection_path,
                }),
        }) => on_hover_scalar_or_linked_field(
            &scalar_field.name,
            &selection_path,
            schema,
            schema_name,
            schema_documentation,
        ),

        _ => None,
    }
}

fn on_hover_operation(
    operation_definition: &OperationDefinition,
    extra_data_provider: &dyn LSPExtraDataProvider,
) -> Option<HoverContents> {
    let name = operation_definition.name.as_ref()?;
    let extra_data = extra_data_provider.fetch_query_stats(name.value.lookup());
    if !extra_data.is_empty() {
        Some(HoverContents::Array(
            extra_data
                .iter()
                .map(|str| MarkedString::String(str.to_string()))
                .collect::<_>(),
        ))
    } else {
        None
    }
}

fn on_hover_variable_definition(
    variable_definition: &VariableDefinition,
    schema_name: StringKey,
) -> HoverContents {
    let variable_identifier = &variable_definition.name;
    let variable_inner_type = variable_definition.type_.inner().name.value;
    let variable_type = &variable_definition.type_;
    let variable_default_value = variable_definition
        .default_value
        .as_ref()
        .map(|default_value| format!(" with default value `{}`", default_value.value))
        .unwrap_or_else(|| "".to_string());

    HoverContents::Scalar(MarkedString::String(format!(
        "`{}`: **{}**{}",
        variable_identifier,
        get_open_schema_explorer_command_link(
            &variable_type.to_string(),
            &GraphQLSchemaExplorerParams {
                path: vec![variable_inner_type.lookup()],
                schema_name: schema_name.lookup(),
                filter: None,
            }
        ),
        variable_default_value
    )))
}

fn on_hover_constant_value<'a>(
    constant_value_parent: &'a ConstantValueParent<'a>,
    schema: &SDLSchema,
    schema_name: StringKey,
    schema_documentation: &impl SchemaDocumentation,
) -> Option<HoverContents> {
    match constant_value_parent.find_constant_value_root() {
        ConstantValueRoot::VariableDefinition(variable_definition_path) => Some(
            on_hover_variable_definition(variable_definition_path.inner, schema_name),
        ),
        ConstantValueRoot::Argument(argument_path) => {
            on_hover_argument_path(argument_path, schema, schema_name, schema_documentation)
        }
    }
}

fn on_hover_argument_path<'a>(
    argument_path: &ArgumentPath<'a>,
    schema: &SDLSchema,
    schema_name: StringKey,
    schema_documentation: &impl SchemaDocumentation,
) -> Option<HoverContents> {
    let ArgumentPath {
        inner: argument,
        parent,
    } = argument_path;

    let argument_name = argument.name.value.lookup();
    let argument_value = argument.value.to_string();
    let argument_info =
        MarkedString::String(format!("Argument `{}: {}`", argument_name, argument_value));

    let field_hover_info = match parent.find_argument_root() {
        ArgumentRoot::LinkedField(linked_field_path) => get_scalar_or_linked_field_hover_content(
            &linked_field_path.inner.name,
            &linked_field_path.parent,
            schema,
            schema_name,
            schema_documentation,
        ),
        // TODO call on_hover_directive when that function is written
        ArgumentRoot::Directive(_) => Some(vec![]),
        ArgumentRoot::ScalarField(scalar_field_path) => get_scalar_or_linked_field_hover_content(
            &scalar_field_path.inner.name,
            &scalar_field_path.parent,
            schema,
            schema_name,
            schema_documentation,
        ),
    }?;

    let mut contents = vec![argument_info];
    contents.extend(field_hover_info.into_iter());

    Some(HoverContents::Array(contents))
}

fn on_hover_scalar_or_linked_field(
    field_name: &Identifier,
    field_selection_path: &SelectionPath<'_>,
    schema: &SDLSchema,
    schema_name: StringKey,
    schema_documentation: &impl SchemaDocumentation,
) -> Option<HoverContents> {
    let content = get_scalar_or_linked_field_hover_content(
        field_name,
        field_selection_path,
        schema,
        schema_name,
        schema_documentation,
    )?;
    Some(HoverContents::Array(content))
}

fn get_scalar_or_linked_field_hover_content(
    field_name: &Identifier,
    field_selection_path: &SelectionPath<'_>,
    schema: &SDLSchema,
    schema_name: StringKey,
    schema_documentation: &impl SchemaDocumentation,
) -> Option<Vec<MarkedString>> {
    let parent_types = field_selection_path.parent.find_type_path(schema);
    let parent_type = parent_types.last()?;

    let mut type_path = parent_types
        .iter()
        .map(|parent_type| schema.get_type_name(*parent_type).lookup())
        .collect::<Vec<_>>();
    let parent_type_name = schema.get_type_name(*parent_type).lookup();

    let field = schema
        .named_field(*parent_type, field_name.value)
        .map(|id| schema.field(id))?;

    let rendered_type_string = schema.get_type_string(&field.type_);
    let field_type_name = schema.get_type_name(field.type_.inner()).lookup();

    let mut hover_contents: Vec<MarkedString> =
        vec![MarkedString::String(format!("Field: **{}**", field.name))];

    if let Some(field_description) =
        schema_documentation.get_field_description(parent_type_name, field.name.lookup())
    {
        hover_contents.push(MarkedString::String(field_description.to_string()));
    }

    type_path.push(field_type_name);

    hover_contents.push(MarkedString::String(format!(
        "Type: **{}**",
        get_open_schema_explorer_command_link(
            &rendered_type_string,
            &GraphQLSchemaExplorerParams {
                path: type_path,
                schema_name: schema_name.lookup(),
                filter: None,
            }
        )
    )));

    if let Some(type_description) = schema_documentation.get_type_description(field_type_name) {
        hover_contents.push(MarkedString::String(type_description.to_string()));
    }

    if !field.arguments.is_empty() {
        hover_contents.push(MarkedString::String(
            "This field accepts these arguments".to_string(),
        ));

        for arg in field.arguments.iter() {
            let arg_type_name = schema.get_type_name(arg.type_.inner()).lookup();
            hover_contents.push(MarkedString::from_markdown(format!(
                "{}: **{}**{}\n\n{}",
                arg.name,
                get_open_schema_explorer_command_link(
                    &schema.get_type_string(&arg.type_),
                    &GraphQLSchemaExplorerParams {
                        path: vec![field_type_name, arg_type_name],
                        schema_name: schema_name.lookup(),
                        filter: None,
                    }
                ),
                if let Some(default_value) = &arg.default_value {
                    format!(" = {}", default_value)
                } else {
                    "".to_string()
                },
                if let Some(description) = schema_documentation.get_field_argument_description(
                    parent_type_name,
                    field.name.lookup(),
                    arg.name.lookup(),
                ) {
                    description.to_string()
                } else {
                    "".to_string()
                }
            )));
        }
    }
    Some(hover_contents)
}
<|endoftext|>


<commit_before>extern crate combine;
extern crate combine_language;
extern crate mnemosyne as core;

use combine::*;
use combine_language::{ LanguageEnv
                      , LanguageDef
                      , Identifier
                      };
use combine::primitives::{ Stream
                         , Positioner
                         , SourcePosition
                         };

use core::semantic::*;
use core::semantic::annotations::UnscopedState;
use core::semantic::ast::*;
use core::position::*;

type ParseFn<'a, I, T> = fn (&LanguageEnv<'a, I>, State<I>)
                            -> ParseResult<T, I>;

type U = UnscopedState;

/// Wraps a parsing function with a language definition environment.
///
/// TODO: this could probably push identifiers to the symbol table here?
#[derive(Copy)]
struct MnParser<'a: 'b, 'b, I, T>
where I: Stream<Item=char>
    , I::Range: 'b
    , I: 'b {
        env: &'b LanguageEnv<'a, I>
      , parser: ParseFn<'a, I, T>
}

impl<'a, 'b, I, T> Clone for MnParser<'a, 'b, I, T>
where I: Stream<Item=char>
    , I::Range: 'b
    , I: 'b
    , 'a: 'b {

    fn clone(&self) -> Self {
        MnParser { env: self.env , parser: self.parser }
    }
}

impl<'a, 'b, I, T> Parser for MnParser<'a, 'b, I, T>
where I: Stream<Item=char>
    , I::Range: 'b
    , I: 'b
    , 'a: 'b {

    type Input = I;
    type Output = T;

    fn parse_state(&mut self, input: State<I>) -> ParseResult<T, I> {
        (self.parser)(self.env, input)
    }

}

struct MnEnv<'a, I>
where I: Stream<Item = char>
    , I::Item: Positioner<Position = SourcePosition> {
    env: LanguageEnv<'a, I>
}

impl <'a, I> std::ops::Deref for MnEnv<'a, I>
where I: Stream<Item=char> {
    type Target = LanguageEnv<'a, I>;
    fn deref(&self) -> &LanguageEnv<'a, I> { &self.env }
}

impl<'a, 'b, I> MnEnv<'a, I>
where I: Stream<Item=char>
    , I::Item: Positioner<Position = SourcePosition>
    , I::Range: 'b {

        /// Wrap a function into a MnParser with this environment
        fn parser<T>(&'b self, parser: ParseFn<'a, I, T>)
                    -> MnParser<'a, 'b, I, T> {
            MnParser { env: self, parser: parser }
        }

        fn parse_def(&self, input: State<I>) -> ParseResult<Form<'a, U>, I> {
            unimplemented!()
        }

        fn parse_expr(&self, input: State<I>) -> ParseResult<Expr<'a, U>, I> {
            unimplemented!()
        }

        fn expr(&'b self) -> MnParser<'a, 'b, I, Expr<'a, U>> {
            unimplemented!()
        }

        fn def(&'b self) -> MnParser<'a, 'b, I, Form<'a, U>> {
            unimplemented!()
        }

}
pub fn parse_module<N: ?Sized>(code: &str) -> Result<Vec<N>, ParseError<&str>>
where N: ASTNode + Sized {
    let alpha_ext = "+-*/<=>!?:$%_~^";
    let ops = "+-*/|=<>";
    let env = LanguageEnv::new(LanguageDef {
        ident: Identifier {
            start: letter().or(satisfy(|c| alpha_ext.contains(c)))
          , rest: alpha_num().or(satisfy(|c| alpha_ext.contains(c)))
          , reserved: [ "and"               , "begin"
                      , "case"              , "cond"
                      , "data"              , "define"
                      , "defn"              , "delay"
                      , "do"                , "else"
                      , "if"                , "lambda"
                      , "let"               , "let*"
                      , "letrec"            , "or"
                      , "quasiquote"        , "quote"
                      , "set!"              , "unquote"
                      , "unquote-splicing"
                      ].iter().map(|x| (*x).into()).collect()
        }
      , op: Identifier {
            start: satisfy(|c| ops.contains(c))
          , rest:  satisfy(|c| ops.contains(c))
          , reserved: [ "=>", "->", "\\", "|"].iter().map(|x| (*x).into())
                                                     .collect()
        }
      , comment_line: string(";").map(|_| ())
      , comment_start: string("#|").map(|_| ())
      , comment_end: string("|#").map(|_| ())
    });
    let env = MnEnv { env: env };

    env.white_space()
       .parse(code);

    unimplemented!()
}
<commit_msg>Start on impementing the expr parser<commit_after>extern crate combine;
extern crate combine_language;
extern crate mnemosyne as core;

use combine::*;
use combine_language::{ LanguageEnv
                      , LanguageDef
                      , Identifier
                      };
use combine::primitives::{ Stream
                         , Positioner
                         , SourcePosition
                         };

use core::semantic::*;
use core::semantic::annotations::{ Annotated
                                 , UnscopedState
                                 };
use core::semantic::ast::*;
use core::position::*;

type ParseFn<'a, I, T> = fn (&MnEnv<'a, I>, State<I>) -> ParseResult<T, I>;

type U = UnscopedState;

/// Wraps a parsing function with a language definition environment.
///
/// TODO: this could probably push identifiers to the symbol table here?
#[derive(Copy)]
struct MnParser<'a: 'b, 'b, I, T>
where I: Stream<Item=char>
    , I::Range: 'b
    , I: 'b {
        env: &'b MnEnv<'a, I>
      , parser: ParseFn<'a, I, T>
}

impl<'a, 'b, I, T> Clone for MnParser<'a, 'b, I, T>
where I: Stream<Item=char>
    , I::Range: 'b
    , I: 'b
    , 'a: 'b {

    fn clone(&self) -> Self {
        MnParser { env: self.env , parser: self.parser }
    }
}

impl<'a, 'b, I, T> Parser for MnParser<'a, 'b, I, T>
where I: Stream<Item=char>
    , I::Range: 'b
    , I: 'b
    , 'a: 'b {

    type Input = I;
    type Output = T;

    fn parse_state(&mut self, input: State<I>) -> ParseResult<T, I> {
        (self.parser)(self.env, input)
    }

}

struct MnEnv<'a, I>
where I: Stream<Item = char>
    , I::Item: Positioner<Position = SourcePosition> {
    env: LanguageEnv<'a, I>
}

impl <'a, I> std::ops::Deref for MnEnv<'a, I>
where I: Stream<Item=char> {
    type Target = LanguageEnv<'a, I>;
    fn deref(&self) -> &LanguageEnv<'a, I> { &self.env }
}

impl<'a, 'b, I> MnEnv<'a, I>
where I: Stream<Item=char>
    , I::Item: Positioner<Position = SourcePosition>
    , I::Range: 'b {

        /// Wrap a function into a MnParser with this environment
        fn parser<T>(&'b self, parser: ParseFn<'a, I, T>)
                    -> MnParser<'a, 'b, I, T> {
            MnParser { env: self, parser: parser }
        }

        fn parse_def(&self, input: State<I>) -> ParseResult<Form<'a, U>, I> {
            unimplemented!()
        }

        fn parse_expr(&self, input: State<I>) -> ParseResult<Expr<'a, U>, I> {
            let pos = Position::from(input.position.clone());
            self.env
                .parens(choice([
                        self.parser(MnEnv::parse_def)
                    ]))
                .map(|f| Annotated::new(f, pos) )
                .parse_state(input)
        }

        fn expr(&'b self) -> MnParser<'a, 'b, I, Expr<'a, U>> {
            self.parser(MnEnv::parse_expr)
        }

        fn def(&'b self) -> MnParser<'a, 'b, I, Form<'a, U>> {
            unimplemented!()
        }

}
pub fn parse_module<N: ?Sized>(code: &str) -> Result<Vec<N>, ParseError<&str>>
where N: ASTNode + Sized {
    let alpha_ext = "+-*/<=>!?:$%_~^";
    let ops = "+-*/|=<>";
    let env = LanguageEnv::new(LanguageDef {
        ident: Identifier {
            start: letter().or(satisfy(|c| alpha_ext.contains(c)))
          , rest: alpha_num().or(satisfy(|c| alpha_ext.contains(c)))
          , reserved: [ "and"               , "begin"
                      , "case"              , "cond"
                      , "data"              , "define"
                      , "defn"              , "delay"
                      , "do"                , "else"
                      , "if"                , "lambda"
                      , "let"               , "let*"
                      , "letrec"            , "or"
                      , "quasiquote"        , "quote"
                      , "set!"              , "unquote"
                      , "unquote-splicing"
                      ].iter().map(|x| (*x).into()).collect()
        }
      , op: Identifier {
            start: satisfy(|c| ops.contains(c))
          , rest:  satisfy(|c| ops.contains(c))
          , reserved: [ "=>", "->", "\\", "|"].iter().map(|x| (*x).into())
                                                     .collect()
        }
      , comment_line: string(";").map(|_| ())
      , comment_start: string("#|").map(|_| ())
      , comment_end: string("|#").map(|_| ())
    });
    let env = MnEnv { env: env };

    env.white_space()
       .parse(code);

    unimplemented!()
}
<|endoftext|>


<commit_before>//! VM Runtime
use utils::bigint::{U256, M256};
use utils::gas::Gas;
use super::commit::{AccountState, BlockhashState};
use super::errors::{RequireError, MachineError, CommitError, EvalError, PCError};
use super::{Stack, Context, BlockHeader, Patch, PC, Memory, AccountCommitment, Log};

use self::check::{check_opcode, extra_check_opcode};
use self::run::run_opcode;
use self::cost::{gas_refund, gas_stipend, gas_cost, memory_cost, memory_gas, code_deposit_gas};
use self::utils::copy_into_memory;

mod cost;
mod run;
mod check;
mod utils;
mod precompiled;

/// A VM state without PC.
pub struct State<M> {
    pub memory: M,
    pub stack: Stack,

    pub context: Context,
    pub block: BlockHeader,
    pub patch: &'static Patch,

    pub out: Vec<u8>,

    pub memory_cost: Gas,
    pub used_gas: Gas,
    pub refunded_gas: Gas,

    pub account_state: AccountState,
    pub blockhash_state: BlockhashState,
    pub logs: Vec<Log>,

    pub depth: usize,
}

impl<M> State<M> {
    pub fn memory_gas(&self) -> Gas {
        memory_gas(self.memory_cost)
    }

    pub fn available_gas(&self) -> Gas {
        self.context.gas_limit - self.memory_gas() - self.used_gas
    }
}

/// A VM state with PC.
pub struct Machine<M> {
    state: State<M>,
    pc: PC,
    status: MachineStatus,
}

#[derive(Debug, Clone)]
/// Represents the current runtime status.
pub enum MachineStatus {
    /// This runtime is actively running or has just been started.
    Running,
    /// This runtime has exited successfully. Calling `step` on this
    /// runtime again would panic.
    ExitedOk,
    /// This runtime has exited with errors. Calling `step` on this
    /// runtime again would panic.
    ExitedErr(MachineError),
    /// This runtime requires execution of a sub runtime, which is a
    /// ContractCreation instruction.
    InvokeCreate(Context),
    /// This runtime requires execution of a sub runtime, which is a
    /// MessageCall instruction.
    InvokeCall(Context, (M256, M256)),
}

#[derive(Debug, Clone)]
/// Used for `check` for additional checks related to the runtime.
pub enum ControlCheck {
    Jump(M256),
}

#[derive(Debug, Clone)]
/// Used for `step` for additional operations related to the runtime.
pub enum Control {
    Stop,
    Jump(M256),
    InvokeCreate(Context),
    InvokeCall(Context, (M256, M256)),
}

impl<M: Memory + Default> Machine<M> {
    /// Create a new runtime.
    pub fn new(context: Context, block: BlockHeader, patch: &'static Patch, depth: usize) -> Self {
        Self::with_states(context, block, patch, depth,
                          AccountState::default(), BlockhashState::default())
    }

    pub fn with_states(context: Context, block: BlockHeader, patch: &'static Patch,
                       depth: usize, account_state: AccountState,
                       blockhash_state: BlockhashState) -> Self {
        Machine {
            pc: PC::new(context.code.as_slice()),
            status: MachineStatus::Running,
            state: State {
                memory: M::default(),
                stack: Stack::default(),

                context,
                block,
                patch,

                out: Vec::new(),

                memory_cost: Gas::zero(),
                used_gas: Gas::zero(),
                refunded_gas: Gas::zero(),

                account_state,
                blockhash_state,
                logs: Vec::new(),

                depth,
            },
        }
    }

    /// Derive this runtime to create a sub runtime. This will not
    /// modify the current runtime, and it will have a chance to
    /// review whether it wants to accept the result of this sub
    /// runtime afterwards.
    pub fn derive(&self, context: Context) -> Self {
        Machine {
            pc: PC::new(context.code.as_slice()),
            status: MachineStatus::Running,
            state: State {
                memory: M::default(),
                stack: Stack::default(),

                context: context,
                block: self.state.block.clone(),
                patch: self.state.patch.clone(),

                out: Vec::new(),

                memory_cost: Gas::zero(),
                used_gas: Gas::zero(),
                refunded_gas: Gas::zero(),

                account_state: self.state.account_state.clone(),
                blockhash_state: self.state.blockhash_state.clone(),
                logs: self.state.logs.clone(),

                depth: self.state.depth + 1,
            },
        }
    }

    /// Commit a new account into this runtime.
    pub fn commit_account(&mut self, commitment: AccountCommitment) -> Result<(), CommitError> {
        self.state.account_state.commit(commitment)
    }

    /// Commit a new blockhash into this runtime.
    pub fn commit_blockhash(&mut self, number: M256, hash: M256) -> Result<(), CommitError> {
        self.state.blockhash_state.commit(number, hash)
    }

    pub fn code_deposit(&mut self) -> Result<(), RequireError> {
        match self.status() {
            MachineStatus::ExitedOk | MachineStatus::ExitedErr(_) => (),
            _ => panic!(),
        }

        let deposit_cost = code_deposit_gas(self.state.out.len());
        if deposit_cost > self.state.available_gas() {
            self.status = MachineStatus::ExitedErr(MachineError::EmptyGas);
        } else {
            self.state.used_gas = self.state.used_gas + deposit_cost;
            self.state.account_state.create(self.state.context.address, U256::zero(),
                                            self.state.out.as_slice());
        }
        Ok(())
    }

    pub fn finalize(&mut self, real_used_gas: Gas) -> Result<(), RequireError> {
        println!("finalizing ...");

        let gas_dec = real_used_gas * self.state.context.gas_price;
        println!("gas_dec: 0x{:x}", gas_dec);
        self.state.account_state.decrease_balance(self.state.context.caller, gas_dec.into());

        match self.status() {
            MachineStatus::ExitedOk => (),
            MachineStatus::ExitedErr(_) => return Ok(()),
            _ => panic!(),
        }

        self.state.account_state.decrease_balance(self.state.context.caller,
                                                  self.state.context.value);
        self.state.account_state.increase_balance(self.state.context.address,
                                                  self.state.context.value);

        Ok(())
    }

    #[allow(unused_variables)]
    /// Apply a sub runtime into the current runtime. This sub runtime
    /// should have been created by the current runtime's `derive`
    /// function. Depending whether the current runtime is invoking a
    /// ContractCreation or MessageCall instruction, it will apply
    /// various states back.
    pub fn apply_sub(&mut self, sub: Machine<M>) {
        use std::mem::swap;
        let mut status = MachineStatus::Running;
        swap(&mut status, &mut self.status);
        match status {
            MachineStatus::InvokeCreate(_) => {
                self.apply_create(sub);
            },
            MachineStatus::InvokeCall(_, (out_start, out_len)) => {
                self.apply_call(sub, out_start, out_len);
            },
            _ => panic!(),
        }
    }

    fn apply_create(&mut self, sub: Machine<M>) {
        if self.state.available_gas() < sub.state.used_gas {
            panic!();
        }

        match sub.status() {
            MachineStatus::ExitedOk => {
                self.state.account_state = sub.state.account_state;
                self.state.blockhash_state = sub.state.blockhash_state;
                self.state.logs = sub.state.logs;
                self.state.used_gas = self.state.used_gas + sub.state.used_gas;
                self.state.refunded_gas = self.state.refunded_gas + sub.state.refunded_gas;
                if self.state.available_gas() >= code_deposit_gas(sub.state.out.len()) {
                    self.state.account_state.decrease_balance(self.state.context.address,
                                                              sub.state.context.value);
                    self.state.account_state.create(sub.state.context.address,
                                                    sub.state.context.value,
                                                    sub.state.out.as_slice());
                }

            },
            MachineStatus::ExitedErr(_) => {
                // self.state.used_gas = self.state.used_gas + sub.state.used_gas;
                // self.state.stack.pop().unwrap();
                // self.state.stack.push(M256::zero()).unwrap();
            },
            _ => panic!(),
        }
    }

    fn apply_call(&mut self, sub: Machine<M>, out_start: M256, out_len: M256) {
        if self.state.available_gas() < sub.state.used_gas {
            panic!();
        }

        match sub.status() {
            MachineStatus::ExitedOk => {
                self.state.account_state = sub.state.account_state;
                self.state.blockhash_state = sub.state.blockhash_state;
                self.state.logs = sub.state.logs;
                self.state.used_gas = self.state.used_gas + sub.state.used_gas;
                self.state.refunded_gas = self.state.refunded_gas + sub.state.refunded_gas;
                self.state.account_state.decrease_balance(self.state.context.address,
                                                          sub.state.context.value);
                self.state.account_state.increase_balance(sub.state.context.address,
                                                          sub.state.context.value);
                copy_into_memory(&mut self.state.memory, sub.state.out.as_slice(),
                                 out_start, M256::zero(), out_len);
            },
            MachineStatus::ExitedErr(_) => {
                // self.state.used_gas = self.state.used_gas + sub.state.used_gas;
                self.state.stack.pop().unwrap();
                self.state.stack.push(M256::from(1u64)).unwrap();
            },
            _ => panic!(),
        }
    }

    /// Check the next instruction about whether it will return
    /// errors.
    pub fn check(&self) -> Result<(), EvalError> {
        let instruction = self.pc.peek()?;
        check_opcode(instruction, &self.state).and_then(|v| {
            match v {
                None => Ok(()),
                Some(ControlCheck::Jump(dest)) => {
                    if dest <= M256::from(usize::max_value()) && self.pc.is_valid(dest.into()) {
                        Ok(())
                    } else {
                        Err(EvalError::Machine(MachineError::PC(PCError::BadJumpDest)))
                    }
                }
            }
        })
    }

    /// Step an instruction in the PC. The eval result is refected by
    /// the runtime status, and it will only return an error if
    /// there're accounts or blockhashes to be committed to this
    /// runtime for it to run. In that case, the state of the current
    /// runtime will not be affected.
    pub fn step(&mut self) -> Result<(), RequireError> {
        match &self.status {
            &MachineStatus::Running => (),
            _ => panic!(),
        }

        if self.step_precompiled() {
            return Ok(());
        }

        if self.state.depth >= 2 {
            self.status = MachineStatus::ExitedErr(MachineError::CallstackOverflow);
            return Ok(());
        }

        if self.pc.is_end() {
            self.status = MachineStatus::ExitedOk;
            return Ok(());
        }

        match self.check() {
            Ok(()) => (),
            Err(EvalError::Machine(error)) => {
                self.status = MachineStatus::ExitedErr(error);
                return Ok(());
            },
            Err(EvalError::Require(error)) => {
                return Err(error);
            },
        };

        let instruction = self.pc.peek().unwrap();
        let position = self.pc.position();
        let memory_cost = memory_cost(instruction, &self.state);
        let memory_gas = memory_gas(memory_cost);
        let gas_cost = gas_cost(instruction, &self.state);
        let gas_stipend = gas_stipend(instruction, &self.state);
        let gas_refund = gas_refund(instruction, &self.state);
        let after_gas = self.state.context.gas_limit - memory_gas - self.state.used_gas - gas_cost + gas_stipend;

        match extra_check_opcode(instruction, &self.state, gas_stipend, after_gas) {
            Ok(()) => (),
            Err(EvalError::Machine(error)) => {
                self.status = MachineStatus::ExitedErr(error);
                return Ok(());
            },
            Err(EvalError::Require(error)) => {
                return Err(error);
            },
        }

        if self.state.context.gas_limit < memory_gas + self.state.used_gas + gas_cost - gas_stipend {
            self.status = MachineStatus::ExitedErr(MachineError::EmptyGas);
            return Ok(());
        }

        let instruction = self.pc.read().unwrap();
        let result = run_opcode((instruction, position),
                                &mut self.state, gas_stipend, after_gas);

        self.state.used_gas = self.state.used_gas + gas_cost - gas_stipend;
        self.state.memory_cost = memory_cost;
        self.state.refunded_gas = self.state.refunded_gas + gas_refund;

        match result {
            None => Ok(()),
            Some(Control::Jump(dest)) => {
                self.pc.jump(dest.into()).unwrap();
                Ok(())
            },
            Some(Control::InvokeCall(context, (from, len))) => {
                self.status = MachineStatus::InvokeCall(context, (from, len));
                Ok(())
            },
            Some(Control::InvokeCreate(context)) => {
                self.status = MachineStatus::InvokeCreate(context);
                Ok(())
            },
            Some(Control::Stop) => {
                self.status = MachineStatus::ExitedOk;
                Ok(())
            },
        }
    }

    /// Get the runtime state.
    pub fn state(&self) -> &State<M> {
        &self.state
    }

    /// Get the current runtime status.
    pub fn status(&self) -> MachineStatus {
        self.status.clone()
    }

    /// If the machine is exited with EmptyGas, return gas_limit,
    /// otherwise return the sum of memory gas and used gas.
    pub fn real_used_gas(&self) -> Gas {
        match self.status() {
            MachineStatus::ExitedErr(MachineError::EmptyGas) =>
                self.state.context.gas_limit,
            _ => self.state.memory_gas() + self.state.used_gas,
        }
    }
}
<commit_msg>Problem: unused debugging message<commit_after>//! VM Runtime
use utils::bigint::{U256, M256};
use utils::gas::Gas;
use super::commit::{AccountState, BlockhashState};
use super::errors::{RequireError, MachineError, CommitError, EvalError, PCError};
use super::{Stack, Context, BlockHeader, Patch, PC, Memory, AccountCommitment, Log};

use self::check::{check_opcode, extra_check_opcode};
use self::run::run_opcode;
use self::cost::{gas_refund, gas_stipend, gas_cost, memory_cost, memory_gas, code_deposit_gas};
use self::utils::copy_into_memory;

mod cost;
mod run;
mod check;
mod utils;
mod precompiled;

/// A VM state without PC.
pub struct State<M> {
    pub memory: M,
    pub stack: Stack,

    pub context: Context,
    pub block: BlockHeader,
    pub patch: &'static Patch,

    pub out: Vec<u8>,

    pub memory_cost: Gas,
    pub used_gas: Gas,
    pub refunded_gas: Gas,

    pub account_state: AccountState,
    pub blockhash_state: BlockhashState,
    pub logs: Vec<Log>,

    pub depth: usize,
}

impl<M> State<M> {
    pub fn memory_gas(&self) -> Gas {
        memory_gas(self.memory_cost)
    }

    pub fn available_gas(&self) -> Gas {
        self.context.gas_limit - self.memory_gas() - self.used_gas
    }
}

/// A VM state with PC.
pub struct Machine<M> {
    state: State<M>,
    pc: PC,
    status: MachineStatus,
}

#[derive(Debug, Clone)]
/// Represents the current runtime status.
pub enum MachineStatus {
    /// This runtime is actively running or has just been started.
    Running,
    /// This runtime has exited successfully. Calling `step` on this
    /// runtime again would panic.
    ExitedOk,
    /// This runtime has exited with errors. Calling `step` on this
    /// runtime again would panic.
    ExitedErr(MachineError),
    /// This runtime requires execution of a sub runtime, which is a
    /// ContractCreation instruction.
    InvokeCreate(Context),
    /// This runtime requires execution of a sub runtime, which is a
    /// MessageCall instruction.
    InvokeCall(Context, (M256, M256)),
}

#[derive(Debug, Clone)]
/// Used for `check` for additional checks related to the runtime.
pub enum ControlCheck {
    Jump(M256),
}

#[derive(Debug, Clone)]
/// Used for `step` for additional operations related to the runtime.
pub enum Control {
    Stop,
    Jump(M256),
    InvokeCreate(Context),
    InvokeCall(Context, (M256, M256)),
}

impl<M: Memory + Default> Machine<M> {
    /// Create a new runtime.
    pub fn new(context: Context, block: BlockHeader, patch: &'static Patch, depth: usize) -> Self {
        Self::with_states(context, block, patch, depth,
                          AccountState::default(), BlockhashState::default())
    }

    pub fn with_states(context: Context, block: BlockHeader, patch: &'static Patch,
                       depth: usize, account_state: AccountState,
                       blockhash_state: BlockhashState) -> Self {
        Machine {
            pc: PC::new(context.code.as_slice()),
            status: MachineStatus::Running,
            state: State {
                memory: M::default(),
                stack: Stack::default(),

                context,
                block,
                patch,

                out: Vec::new(),

                memory_cost: Gas::zero(),
                used_gas: Gas::zero(),
                refunded_gas: Gas::zero(),

                account_state,
                blockhash_state,
                logs: Vec::new(),

                depth,
            },
        }
    }

    /// Derive this runtime to create a sub runtime. This will not
    /// modify the current runtime, and it will have a chance to
    /// review whether it wants to accept the result of this sub
    /// runtime afterwards.
    pub fn derive(&self, context: Context) -> Self {
        Machine {
            pc: PC::new(context.code.as_slice()),
            status: MachineStatus::Running,
            state: State {
                memory: M::default(),
                stack: Stack::default(),

                context: context,
                block: self.state.block.clone(),
                patch: self.state.patch.clone(),

                out: Vec::new(),

                memory_cost: Gas::zero(),
                used_gas: Gas::zero(),
                refunded_gas: Gas::zero(),

                account_state: self.state.account_state.clone(),
                blockhash_state: self.state.blockhash_state.clone(),
                logs: self.state.logs.clone(),

                depth: self.state.depth + 1,
            },
        }
    }

    /// Commit a new account into this runtime.
    pub fn commit_account(&mut self, commitment: AccountCommitment) -> Result<(), CommitError> {
        self.state.account_state.commit(commitment)
    }

    /// Commit a new blockhash into this runtime.
    pub fn commit_blockhash(&mut self, number: M256, hash: M256) -> Result<(), CommitError> {
        self.state.blockhash_state.commit(number, hash)
    }

    pub fn code_deposit(&mut self) -> Result<(), RequireError> {
        match self.status() {
            MachineStatus::ExitedOk | MachineStatus::ExitedErr(_) => (),
            _ => panic!(),
        }

        let deposit_cost = code_deposit_gas(self.state.out.len());
        if deposit_cost > self.state.available_gas() {
            self.status = MachineStatus::ExitedErr(MachineError::EmptyGas);
        } else {
            self.state.used_gas = self.state.used_gas + deposit_cost;
            self.state.account_state.create(self.state.context.address, U256::zero(),
                                            self.state.out.as_slice());
        }
        Ok(())
    }

    pub fn finalize(&mut self, real_used_gas: Gas) -> Result<(), RequireError> {
        let gas_dec = real_used_gas * self.state.context.gas_price;
        self.state.account_state.decrease_balance(self.state.context.caller, gas_dec.into());

        match self.status() {
            MachineStatus::ExitedOk => (),
            MachineStatus::ExitedErr(_) => return Ok(()),
            _ => panic!(),
        }

        self.state.account_state.decrease_balance(self.state.context.caller,
                                                  self.state.context.value);
        self.state.account_state.increase_balance(self.state.context.address,
                                                  self.state.context.value);

        Ok(())
    }

    #[allow(unused_variables)]
    /// Apply a sub runtime into the current runtime. This sub runtime
    /// should have been created by the current runtime's `derive`
    /// function. Depending whether the current runtime is invoking a
    /// ContractCreation or MessageCall instruction, it will apply
    /// various states back.
    pub fn apply_sub(&mut self, sub: Machine<M>) {
        use std::mem::swap;
        let mut status = MachineStatus::Running;
        swap(&mut status, &mut self.status);
        match status {
            MachineStatus::InvokeCreate(_) => {
                self.apply_create(sub);
            },
            MachineStatus::InvokeCall(_, (out_start, out_len)) => {
                self.apply_call(sub, out_start, out_len);
            },
            _ => panic!(),
        }
    }

    fn apply_create(&mut self, sub: Machine<M>) {
        if self.state.available_gas() < sub.state.used_gas {
            panic!();
        }

        match sub.status() {
            MachineStatus::ExitedOk => {
                self.state.account_state = sub.state.account_state;
                self.state.blockhash_state = sub.state.blockhash_state;
                self.state.logs = sub.state.logs;
                self.state.used_gas = self.state.used_gas + sub.state.used_gas;
                self.state.refunded_gas = self.state.refunded_gas + sub.state.refunded_gas;
                if self.state.available_gas() >= code_deposit_gas(sub.state.out.len()) {
                    self.state.account_state.decrease_balance(self.state.context.address,
                                                              sub.state.context.value);
                    self.state.account_state.create(sub.state.context.address,
                                                    sub.state.context.value,
                                                    sub.state.out.as_slice());
                }

            },
            MachineStatus::ExitedErr(_) => {
                // self.state.used_gas = self.state.used_gas + sub.state.used_gas;
                // self.state.stack.pop().unwrap();
                // self.state.stack.push(M256::zero()).unwrap();
            },
            _ => panic!(),
        }
    }

    fn apply_call(&mut self, sub: Machine<M>, out_start: M256, out_len: M256) {
        if self.state.available_gas() < sub.state.used_gas {
            panic!();
        }

        match sub.status() {
            MachineStatus::ExitedOk => {
                self.state.account_state = sub.state.account_state;
                self.state.blockhash_state = sub.state.blockhash_state;
                self.state.logs = sub.state.logs;
                self.state.used_gas = self.state.used_gas + sub.state.used_gas;
                self.state.refunded_gas = self.state.refunded_gas + sub.state.refunded_gas;
                self.state.account_state.decrease_balance(self.state.context.address,
                                                          sub.state.context.value);
                self.state.account_state.increase_balance(sub.state.context.address,
                                                          sub.state.context.value);
                copy_into_memory(&mut self.state.memory, sub.state.out.as_slice(),
                                 out_start, M256::zero(), out_len);
            },
            MachineStatus::ExitedErr(_) => {
                // self.state.used_gas = self.state.used_gas + sub.state.used_gas;
                self.state.stack.pop().unwrap();
                self.state.stack.push(M256::from(1u64)).unwrap();
            },
            _ => panic!(),
        }
    }

    /// Check the next instruction about whether it will return
    /// errors.
    pub fn check(&self) -> Result<(), EvalError> {
        let instruction = self.pc.peek()?;
        check_opcode(instruction, &self.state).and_then(|v| {
            match v {
                None => Ok(()),
                Some(ControlCheck::Jump(dest)) => {
                    if dest <= M256::from(usize::max_value()) && self.pc.is_valid(dest.into()) {
                        Ok(())
                    } else {
                        Err(EvalError::Machine(MachineError::PC(PCError::BadJumpDest)))
                    }
                }
            }
        })
    }

    /// Step an instruction in the PC. The eval result is refected by
    /// the runtime status, and it will only return an error if
    /// there're accounts or blockhashes to be committed to this
    /// runtime for it to run. In that case, the state of the current
    /// runtime will not be affected.
    pub fn step(&mut self) -> Result<(), RequireError> {
        match &self.status {
            &MachineStatus::Running => (),
            _ => panic!(),
        }

        if self.step_precompiled() {
            return Ok(());
        }

        if self.state.depth >= 2 {
            self.status = MachineStatus::ExitedErr(MachineError::CallstackOverflow);
            return Ok(());
        }

        if self.pc.is_end() {
            self.status = MachineStatus::ExitedOk;
            return Ok(());
        }

        match self.check() {
            Ok(()) => (),
            Err(EvalError::Machine(error)) => {
                self.status = MachineStatus::ExitedErr(error);
                return Ok(());
            },
            Err(EvalError::Require(error)) => {
                return Err(error);
            },
        };

        let instruction = self.pc.peek().unwrap();
        let position = self.pc.position();
        let memory_cost = memory_cost(instruction, &self.state);
        let memory_gas = memory_gas(memory_cost);
        let gas_cost = gas_cost(instruction, &self.state);
        let gas_stipend = gas_stipend(instruction, &self.state);
        let gas_refund = gas_refund(instruction, &self.state);
        let after_gas = self.state.context.gas_limit - memory_gas - self.state.used_gas - gas_cost + gas_stipend;

        match extra_check_opcode(instruction, &self.state, gas_stipend, after_gas) {
            Ok(()) => (),
            Err(EvalError::Machine(error)) => {
                self.status = MachineStatus::ExitedErr(error);
                return Ok(());
            },
            Err(EvalError::Require(error)) => {
                return Err(error);
            },
        }

        if self.state.context.gas_limit < memory_gas + self.state.used_gas + gas_cost - gas_stipend {
            self.status = MachineStatus::ExitedErr(MachineError::EmptyGas);
            return Ok(());
        }

        let instruction = self.pc.read().unwrap();
        let result = run_opcode((instruction, position),
                                &mut self.state, gas_stipend, after_gas);

        self.state.used_gas = self.state.used_gas + gas_cost - gas_stipend;
        self.state.memory_cost = memory_cost;
        self.state.refunded_gas = self.state.refunded_gas + gas_refund;

        match result {
            None => Ok(()),
            Some(Control::Jump(dest)) => {
                self.pc.jump(dest.into()).unwrap();
                Ok(())
            },
            Some(Control::InvokeCall(context, (from, len))) => {
                self.status = MachineStatus::InvokeCall(context, (from, len));
                Ok(())
            },
            Some(Control::InvokeCreate(context)) => {
                self.status = MachineStatus::InvokeCreate(context);
                Ok(())
            },
            Some(Control::Stop) => {
                self.status = MachineStatus::ExitedOk;
                Ok(())
            },
        }
    }

    /// Get the runtime state.
    pub fn state(&self) -> &State<M> {
        &self.state
    }

    /// Get the current runtime status.
    pub fn status(&self) -> MachineStatus {
        self.status.clone()
    }

    /// If the machine is exited with EmptyGas, return gas_limit,
    /// otherwise return the sum of memory gas and used gas.
    pub fn real_used_gas(&self) -> Gas {
        match self.status() {
            MachineStatus::ExitedErr(MachineError::EmptyGas) =>
                self.state.context.gas_limit,
            _ => self.state.memory_gas() + self.state.used_gas,
        }
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::io;
use std::process::Command;
use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions};

use Arch::*;

#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
pub enum Arch {
    Armv7,
    Armv7s,
    Arm64,
    I386,
    X86_64
}

impl Arch {
    pub fn to_string(self) -> &'static str {
        match self {
            Armv7 => "armv7",
            Armv7s => "armv7s",
            Arm64 => "arm64",
            I386 => "i386",
            X86_64 => "x86_64"
        }
    }
}

pub fn get_sdk_root(sdk_name: &str) -> Result<String, String> {
    if let Some(sdkroot) = env::var("SDKROOT").ok() {
        let sdkroot_path = Path::new(sdkroot);
        if sdkroot_path.is_absolute() && sdkroot_path != Path::new("/") && sdkroot_path.exists() {
            return Ok(sdkroot);
        }
    }
    let res = Command::new("xcrun")
                      .arg("--show-sdk-path")
                      .arg("-sdk")
                      .arg(sdk_name)
                      .output()
                      .and_then(|output| {
                          if output.status.success() {
                              Ok(String::from_utf8(output.stdout).unwrap())
                          } else {
                              let error = String::from_utf8(output.stderr);
                              let error = format!("process exit with error: {}",
                                                  error.unwrap());
                              Err(io::Error::new(io::ErrorKind::Other,
                                                 &error[..]))
                          }
                      });

    match res {
        Ok(output) => Ok(output.trim().to_string()),
        Err(e) => Err(format!("failed to get {} SDK path: {}", sdk_name, e))
    }
}

fn build_pre_link_args(arch: Arch) -> Result<LinkArgs, String> {
    let sdk_name = match arch {
        Armv7 | Armv7s | Arm64 => "iphoneos",
        I386 | X86_64 => "iphonesimulator"
    };

    let arch_name = arch.to_string();

    let sdk_root = get_sdk_root(sdk_name)?;

    let mut args = LinkArgs::new();
    args.insert(LinkerFlavor::Gcc,
                vec!["-arch".to_string(),
                     arch_name.to_string(),
                     "-isysroot".to_string(),
                     sdk_root.clone(),
                     "-Wl,-syslibroot".to_string(),
                     sdk_root]);

    Ok(args)
}

fn target_cpu(arch: Arch) -> String {
    match arch {
        Armv7 => "cortex-a8", // iOS7 is supported on iPhone 4 and higher
        Armv7s => "cortex-a9",
        Arm64 => "cyclone",
        I386 => "yonah",
        X86_64 => "core2",
    }.to_string()
}

pub fn opts(arch: Arch) -> Result<TargetOptions, String> {
    let pre_link_args = build_pre_link_args(arch)?;
    Ok(TargetOptions {
        cpu: target_cpu(arch),
        dynamic_linking: false,
        executables: true,
        pre_link_args,
        has_elf_tls: false,
        eliminate_frame_pointer: false,
        .. super::apple_base::opts()
    })
}
<commit_msg>fix check<commit_after>use std::env;
use std::io;
use std::path::Path;
use std::process::Command;
use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions};

use Arch::*;

#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
pub enum Arch {
    Armv7,
    Armv7s,
    Arm64,
    I386,
    X86_64
}

impl Arch {
    pub fn to_string(self) -> &'static str {
        match self {
            Armv7 => "armv7",
            Armv7s => "armv7s",
            Arm64 => "arm64",
            I386 => "i386",
            X86_64 => "x86_64"
        }
    }
}

pub fn get_sdk_root(sdk_name: &str) -> Result<String, String> {
    if let Some(sdkroot) = env::var("SDKROOT").ok() {
        let sdkroot_path = Path::new(&sdkroot);
        if sdkroot_path.is_absolute() && sdkroot_path != Path::new("/") && sdkroot_path.exists() {
            return Ok(sdkroot);
        }
    }
    let res = Command::new("xcrun")
                      .arg("--show-sdk-path")
                      .arg("-sdk")
                      .arg(sdk_name)
                      .output()
                      .and_then(|output| {
                          if output.status.success() {
                              Ok(String::from_utf8(output.stdout).unwrap())
                          } else {
                              let error = String::from_utf8(output.stderr);
                              let error = format!("process exit with error: {}",
                                                  error.unwrap());
                              Err(io::Error::new(io::ErrorKind::Other,
                                                 &error[..]))
                          }
                      });

    match res {
        Ok(output) => Ok(output.trim().to_string()),
        Err(e) => Err(format!("failed to get {} SDK path: {}", sdk_name, e))
    }
}

fn build_pre_link_args(arch: Arch) -> Result<LinkArgs, String> {
    let sdk_name = match arch {
        Armv7 | Armv7s | Arm64 => "iphoneos",
        I386 | X86_64 => "iphonesimulator"
    };

    let arch_name = arch.to_string();

    let sdk_root = get_sdk_root(sdk_name)?;

    let mut args = LinkArgs::new();
    args.insert(LinkerFlavor::Gcc,
                vec!["-arch".to_string(),
                     arch_name.to_string(),
                     "-isysroot".to_string(),
                     sdk_root.clone(),
                     "-Wl,-syslibroot".to_string(),
                     sdk_root]);

    Ok(args)
}

fn target_cpu(arch: Arch) -> String {
    match arch {
        Armv7 => "cortex-a8", // iOS7 is supported on iPhone 4 and higher
        Armv7s => "cortex-a9",
        Arm64 => "cyclone",
        I386 => "yonah",
        X86_64 => "core2",
    }.to_string()
}

pub fn opts(arch: Arch) -> Result<TargetOptions, String> {
    let pre_link_args = build_pre_link_args(arch)?;
    Ok(TargetOptions {
        cpu: target_cpu(arch),
        dynamic_linking: false,
        executables: true,
        pre_link_args,
        has_elf_tls: false,
        eliminate_frame_pointer: false,
        .. super::apple_base::opts()
    })
}
<|endoftext|>