<commit_before>// Copyright 2013-2014 The gl-rs developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![experimental]

use registry::*;
use super::ty;
use std::io::Writer;

pub struct StaticGenerator<'a, W: 'a> {
    ns: Ns,
    writer: &'a mut W,
    registry: &'a Registry,
}

impl<'a, W: Writer> StaticGenerator<'a, W> {
    fn new<'a>(writer: &'a mut W, registry: &'a Registry, ns: Ns) -> StaticGenerator<'a, W> {
        StaticGenerator {
            ns: ns,
            writer: writer,
            registry: registry,
        }
    }

    #[allow(unused_must_use)]
    fn write_str(&mut self, s: &str) {
        self.writer.write(s.as_bytes());
    }

    fn write_line(&mut self, s: &str) {
        self.write_str(s);
        self.write_str("\n");
    }

    fn write_enum(&mut self, enm: &Enum) {
        self.write_line(super::gen_enum_item(enm, "types::").as_slice());
    }

    fn write_enums(&mut self) {
        for e in self.registry.enum_iter() {
            self.write_enum(e);
        }
    }

    fn write_header(&mut self) {
        self.write_line("mod __gl_imports {");
        self.write_line("    extern crate libc;");
        self.write_line("    pub use std::mem;");
        self.write_line("}");
    }

    fn write_type_aliases(&mut self) {
        self.write_line("#[stable]");
        self.write_line("pub mod types {");
        let aliases = super::gen_type_aliases(&self.ns);
        self.write_line(aliases.as_slice());
        self.write_line("}");
    }

    fn write_fns(&mut self) {
        let ns = self.ns;

        self.write_line("#[allow(non_snake_case)] #[allow(unused_variable)] #[allow(dead_code)]");
        self.write_line("extern \"system\" {");

        for c in self.registry.cmd_iter() {
            self.write_line(format!(
                "#[link_name=\"{symbol}\"]
                pub fn {name}({params}){return_suffix};",
                symbol = super::gen_symbol_name(&ns, c),
                name = c.proto.ident,
                params = super::gen_param_list(c, true),
                return_suffix = super::gen_return_suffix(c)
            ).as_slice());
        }

        self.write_line("}");
    }

    pub fn write(writer: &mut W, registry: &Registry, ns: Ns) {
        let mut gen = StaticGenerator::new(writer, registry, ns);

        // header with imports
        gen.write_header();
        gen.write_line("");

        // type aliases
        gen.write_type_aliases();
        gen.write_line("");

        // enums definitions
        gen.write_enums();
        gen.write_line("");

        // safe and unsafe OpenGl functions
        gen.write_fns();
        gen.write_line("");
    }
}
<commit_msg>Now using global functions in static generator<commit_after>// Copyright 2013-2014 The gl-rs developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![experimental]

use registry::*;
use super::ty;
use std::io::Writer;

pub struct StaticGenerator;

impl StaticGenerator {
    pub fn write<W: Writer>(writer: &mut W, registry: &Registry, ns: Ns) {
        writeln!(writer, "{}", write_header());
        writeln!(writer, "{}", write_type_aliases(&ns));
        writeln!(writer, "{}", write_enums(registry));
        writeln!(writer, "{}", write_fns(registry, &ns));
    }
}

fn write_enum(enm: &Enum) -> String {
    super::gen_enum_item(enm, "types::")
}

fn write_enums(registry: &Registry) -> String {
    registry.enum_iter().map(|e| write_enum(e)).collect::<Vec<String>>().connect("\n")
}

fn write_header() -> String {
    format!(
        "mod __gl_imports {{
            extern crate libc;
            pub use std::mem;
        }}"
    )
}

fn write_type_aliases(ns: &Ns) -> String {
    format!(
        "#[stable]
        pub mod types {{
            {}
        }}
        ",

        super::gen_type_aliases(ns)
    )
}

fn write_fns(registry: &Registry, ns: &Ns) -> String {
    format!(
        "#[allow(non_snake_case)]
        #[allow(unused_variable)]
        #[allow(dead_code)]

        extern \"system\" {{
            {symbols}
        }}",

        symbols = registry.cmd_iter().map(|c| {
            format!(
                "#[link_name=\"{symbol}\"]
                pub fn {name}({params}){return_suffix};",
                symbol = super::gen_symbol_name(ns, c),
                name = c.proto.ident,
                params = super::gen_param_list(c, true),
                return_suffix = super::gen_return_suffix(c)
            )
        }).collect::<Vec<String>>().connect("\n")
    )
}
<|endoftext|>


<commit_before>extern crate walkdir;

use std::borrow::Cow;
use std::env;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Command;
use walkdir::{DirEntry, WalkDir};

fn build_hermit(src_dir: &Path, target_dir_opt: Option<&Path>) {
	let manifest_path = src_dir.join("Cargo.toml");
	assert!(
		manifest_path.exists(),
		"kernel manifest path `{}` does not exist",
		manifest_path.display()
	);
	let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
	let profile = env::var("PROFILE").expect("PROFILE was not set");
	let mut cmd = Command::new("cargo");

	let kernel_triple = match target_arch.as_str() {
		"x86_64" => "x86_64-unknown-none-hermitkernel",
		"aarch64" => "aarch64-unknown-hermit",
		_ => panic!("Unsupported target arch: {}", target_arch),
	};

	cmd.arg("build")
		.arg("-Z")
		.arg("build-std=core,alloc")
		.arg("--target")
		.arg(kernel_triple)
		.arg("--manifest-path")
		.arg("Cargo.toml");

	cmd.current_dir(src_dir);

	cmd.env_remove("RUSTUP_TOOLCHAIN");
	if option_env!("RUSTC_WORKSPACE_WRAPPER")
		.unwrap_or_default()
		.ends_with("clippy-driver")
	{
		cmd.env("RUSTC_WORKSPACE_WRAPPER", "clippy-driver");
	}

	cmd.env("CARGO_TERM_COLOR", "always");

	if let Some(target_dir) = target_dir_opt {
		cmd.arg("--target-dir").arg(target_dir);
	}
	let target_dir = match target_dir_opt {
		Some(target_dir) => target_dir.to_path_buf(),
		None => src_dir.join("target"),
	};

	if profile == "release" {
		cmd.arg("--release");
	}

	// disable all default features
	cmd.arg("--no-default-features");

	#[cfg(feature = "aarch64-qemu-stdout")]
	{
		cmd.arg("--features");
		cmd.arg("aarch64-qemu-stdout");
	}

	// do we have to enable PCI support?
	#[cfg(feature = "pci")]
	{
		cmd.arg("--features");
		cmd.arg("pci");
	}

	// do we have to enable acpi support?
	#[cfg(feature = "acpi")]
	{
		cmd.arg("--features");
		cmd.arg("acpi");
	}

	// do we have to enable FSGSBASE support?
	#[cfg(feature = "fsgs_base")]
	{
		cmd.arg("--features");
		cmd.arg("fsgs_base");
	}

	// do we support multi-processor systems?
	#[cfg(feature = "smp")]
	{
		cmd.arg("--features");
		cmd.arg("smp");
	}

	// do we have to enable VGA support
	#[cfg(feature = "vga")]
	{
		cmd.arg("--features");
		cmd.arg("vga");
	}

	let mut rustflags = vec!["-Zmutable-noalias=no".to_string()];
	let outer_rustflags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap();

	#[cfg(feature = "instrument")]
	{
		rustflags.push("-Zinstrument-mcount".to_string());
		// Add outer rustflags to command
		rustflags.push(outer_rustflags);
	}

	#[cfg(not(feature = "instrument"))]
	{
		// If the `instrument` feature feature is not enabled,
		// filter it from outer rustflags before adding them to the command.
		if !outer_rustflags.is_empty() {
			let flags = outer_rustflags
				.split('\x1f')
				.filter(|&flag| !flag.contains("instrument-mcount"))
				.map(String::from);
			rustflags.extend(flags);
		}
	}

	cmd.env("CARGO_ENCODED_RUSTFLAGS", rustflags.join("\x1f"));

	let status = cmd.status().expect("failed to start kernel build");
	assert!(status.success());

	let lib_location = target_dir
		.join(kernel_triple)
		.join(&profile)
		.canonicalize()
		.unwrap();

	println!("Lib location: {}", lib_location.display());

	let lib = lib_location.join("libhermit.a");

	let mut symbols = vec!["rust_begin_unwind", "rust_oom"];

	if target_arch == "aarch64" {
		symbols.extend(include_str!("aarch64-duplicate-symbols").lines());
	}

	rename_symbols(symbols, &lib);

	println!("cargo:rustc-link-search=native={}", lib_location.display());
	println!("cargo:rustc-link-lib=static=hermit");

	// HERMIT_LOG_LEVEL_FILTER sets the log level filter at compile time
	println!("cargo:rerun-if-env-changed=HERMIT_LOG_LEVEL_FILTER");
}

/// Kernel and user space has its own versions of panic handler, oom handler, memcpy, memset, etc,
/// Consequently, we rename the functions in the libos to avoid collisions.
fn rename_symbols(symbols: impl IntoIterator<Item = impl AsRef<OsStr>>, lib: impl AsRef<Path>) {
	let args = symbols.into_iter().flat_map(|symbol| {
		let option = OsStr::new("--redefine-sym");
		let arg = [symbol.as_ref(), "=kernel-".as_ref(), symbol.as_ref()]
			.into_iter()
			.collect::<OsString>();
		[Cow::Borrowed(option), Cow::Owned(arg)]
	});

	let status = Command::new("rust-objcopy")
		.args(args)
		.arg(lib.as_ref())
		.status()
		.expect("Failed to execute rust-objcopy. Is cargo-binutils installed?");
	assert!(status.success(), "rust-objcopy was not successful");
}

#[cfg(all(not(feature = "rustc-dep-of-std"), not(feature = "with_submodule")))]
fn build() {
	#[cfg(windows)]
	let out_dir = env::temp_dir();
	#[cfg(not(windows))]
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let src_dir = out_dir.join("rusty-hermit");

	if !src_dir.as_path().exists() {
		let status = Command::new(env!("CARGO"))
			.current_dir(out_dir)
			.arg("download")
			.arg("--output")
			.arg(src_dir.clone().into_os_string())
			.arg("--extract")
			.arg("rusty-hermit")
			.status()
			.expect("failed to start kernel download");
		assert!(
			status.success(),
			"Unable to download rusty-hermit. Is cargo-download installed?"
		);
	}

	build_hermit(src_dir.as_ref(), None);
}

#[cfg(all(not(feature = "rustc-dep-of-std"), feature = "with_submodule"))]
fn build() {
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let target_dir = out_dir.join("target");
	let src_dir = env::current_dir()
		.unwrap()
		.parent()
		.unwrap()
		.join("libhermit-rs");

	build_hermit(src_dir.as_ref(), Some(target_dir.as_ref()));
	configure_cargo_rerun_if_changed(src_dir.as_ref());
}

#[cfg(all(not(feature = "rustc-dep-of-std"), feature = "with_submodule"))]
fn configure_cargo_rerun_if_changed(src_dir: &Path) {
	fn is_not_ignored(entry: &DirEntry) -> bool {
		// Ignore .git .vscode and target directories, but not .cargo or .github
		if entry.depth() == 1
			&& entry.path().is_dir()
			&& (entry.path().ends_with("target")
				|| entry.path().ends_with(".git")
				|| entry.path().ends_with(".vscode"))
		{
			return false;
		}
		true
	}

	WalkDir::new(src_dir)
		.into_iter()
		.filter_entry(is_not_ignored)
		.filter_map(|v| v.ok())
		.filter_map(|v| v.path().canonicalize().ok())
		.for_each(|x| println!("cargo:rerun-if-changed={}", x.display()));
}

#[cfg(not(feature = "rustc-dep-of-std"))]
fn create_constants() {
	let out_dir = env::var("OUT_DIR").expect("No out dir");
	let dest_path = Path::new(&out_dir).join("constants.rs");
	let mut f = File::create(&dest_path).expect("Could not create file");

	let ip = option_env!("HERMIT_IP");
	let ip = ip.map_or("10.0.5.3", |v| v);

	let gateway = option_env!("HERMIT_GATEWAY");
	let gateway = gateway.map_or("10.0.5.1", |v| v);

	let mask = option_env!("HERMIT_MASK");
	let mask = mask.map_or("255.255.255.0", |v| v);

	writeln!(&mut f, "const HERMIT_IP: &str = \"{}\";", ip).expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_IP");

	writeln!(&mut f, "const HERMIT_GATEWAY: &str = \"{}\";", gateway)
		.expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_GATEWAY");

	writeln!(&mut f, "const HERMIT_MASK: &str = \"{}\";", mask).expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_MASK");
}

fn main() {
	#[cfg(not(feature = "rustc-dep-of-std"))]
	create_constants();
	#[cfg(not(feature = "rustc-dep-of-std"))]
	build();
}
<commit_msg>build.rs: Fix fsgsbase feature forwarding<commit_after>extern crate walkdir;

use std::borrow::Cow;
use std::env;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Command;
use walkdir::{DirEntry, WalkDir};

fn build_hermit(src_dir: &Path, target_dir_opt: Option<&Path>) {
	let manifest_path = src_dir.join("Cargo.toml");
	assert!(
		manifest_path.exists(),
		"kernel manifest path `{}` does not exist",
		manifest_path.display()
	);
	let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
	let profile = env::var("PROFILE").expect("PROFILE was not set");
	let mut cmd = Command::new("cargo");

	let kernel_triple = match target_arch.as_str() {
		"x86_64" => "x86_64-unknown-none-hermitkernel",
		"aarch64" => "aarch64-unknown-hermit",
		_ => panic!("Unsupported target arch: {}", target_arch),
	};

	cmd.arg("build")
		.arg("-Z")
		.arg("build-std=core,alloc")
		.arg("--target")
		.arg(kernel_triple)
		.arg("--manifest-path")
		.arg("Cargo.toml");

	cmd.current_dir(src_dir);

	cmd.env_remove("RUSTUP_TOOLCHAIN");
	if option_env!("RUSTC_WORKSPACE_WRAPPER")
		.unwrap_or_default()
		.ends_with("clippy-driver")
	{
		cmd.env("RUSTC_WORKSPACE_WRAPPER", "clippy-driver");
	}

	cmd.env("CARGO_TERM_COLOR", "always");

	if let Some(target_dir) = target_dir_opt {
		cmd.arg("--target-dir").arg(target_dir);
	}
	let target_dir = match target_dir_opt {
		Some(target_dir) => target_dir.to_path_buf(),
		None => src_dir.join("target"),
	};

	if profile == "release" {
		cmd.arg("--release");
	}

	// disable all default features
	cmd.arg("--no-default-features");

	#[cfg(feature = "aarch64-qemu-stdout")]
	{
		cmd.arg("--features");
		cmd.arg("aarch64-qemu-stdout");
	}

	// do we have to enable PCI support?
	#[cfg(feature = "pci")]
	{
		cmd.arg("--features");
		cmd.arg("pci");
	}

	// do we have to enable acpi support?
	#[cfg(feature = "acpi")]
	{
		cmd.arg("--features");
		cmd.arg("acpi");
	}

	// do we have to enable FSGSBASE support?
	#[cfg(feature = "fsgsbase")]
	{
		cmd.arg("--features");
		cmd.arg("fsgsbase");
	}

	// do we support multi-processor systems?
	#[cfg(feature = "smp")]
	{
		cmd.arg("--features");
		cmd.arg("smp");
	}

	// do we have to enable VGA support
	#[cfg(feature = "vga")]
	{
		cmd.arg("--features");
		cmd.arg("vga");
	}

	let mut rustflags = vec!["-Zmutable-noalias=no".to_string()];
	let outer_rustflags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap();

	#[cfg(feature = "instrument")]
	{
		rustflags.push("-Zinstrument-mcount".to_string());
		// Add outer rustflags to command
		rustflags.push(outer_rustflags);
	}

	#[cfg(not(feature = "instrument"))]
	{
		// If the `instrument` feature feature is not enabled,
		// filter it from outer rustflags before adding them to the command.
		if !outer_rustflags.is_empty() {
			let flags = outer_rustflags
				.split('\x1f')
				.filter(|&flag| !flag.contains("instrument-mcount"))
				.map(String::from);
			rustflags.extend(flags);
		}
	}

	cmd.env("CARGO_ENCODED_RUSTFLAGS", rustflags.join("\x1f"));

	let status = cmd.status().expect("failed to start kernel build");
	assert!(status.success());

	let lib_location = target_dir
		.join(kernel_triple)
		.join(&profile)
		.canonicalize()
		.unwrap();

	println!("Lib location: {}", lib_location.display());

	let lib = lib_location.join("libhermit.a");

	let mut symbols = vec!["rust_begin_unwind", "rust_oom"];

	if target_arch == "aarch64" {
		symbols.extend(include_str!("aarch64-duplicate-symbols").lines());
	}

	rename_symbols(symbols, &lib);

	println!("cargo:rustc-link-search=native={}", lib_location.display());
	println!("cargo:rustc-link-lib=static=hermit");

	// HERMIT_LOG_LEVEL_FILTER sets the log level filter at compile time
	println!("cargo:rerun-if-env-changed=HERMIT_LOG_LEVEL_FILTER");
}

/// Kernel and user space has its own versions of panic handler, oom handler, memcpy, memset, etc,
/// Consequently, we rename the functions in the libos to avoid collisions.
fn rename_symbols(symbols: impl IntoIterator<Item = impl AsRef<OsStr>>, lib: impl AsRef<Path>) {
	let args = symbols.into_iter().flat_map(|symbol| {
		let option = OsStr::new("--redefine-sym");
		let arg = [symbol.as_ref(), "=kernel-".as_ref(), symbol.as_ref()]
			.into_iter()
			.collect::<OsString>();
		[Cow::Borrowed(option), Cow::Owned(arg)]
	});

	let status = Command::new("rust-objcopy")
		.args(args)
		.arg(lib.as_ref())
		.status()
		.expect("Failed to execute rust-objcopy. Is cargo-binutils installed?");
	assert!(status.success(), "rust-objcopy was not successful");
}

#[cfg(all(not(feature = "rustc-dep-of-std"), not(feature = "with_submodule")))]
fn build() {
	#[cfg(windows)]
	let out_dir = env::temp_dir();
	#[cfg(not(windows))]
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let src_dir = out_dir.join("rusty-hermit");

	if !src_dir.as_path().exists() {
		let status = Command::new(env!("CARGO"))
			.current_dir(out_dir)
			.arg("download")
			.arg("--output")
			.arg(src_dir.clone().into_os_string())
			.arg("--extract")
			.arg("rusty-hermit")
			.status()
			.expect("failed to start kernel download");
		assert!(
			status.success(),
			"Unable to download rusty-hermit. Is cargo-download installed?"
		);
	}

	build_hermit(src_dir.as_ref(), None);
}

#[cfg(all(not(feature = "rustc-dep-of-std"), feature = "with_submodule"))]
fn build() {
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let target_dir = out_dir.join("target");
	let src_dir = env::current_dir()
		.unwrap()
		.parent()
		.unwrap()
		.join("libhermit-rs");

	build_hermit(src_dir.as_ref(), Some(target_dir.as_ref()));
	configure_cargo_rerun_if_changed(src_dir.as_ref());
}

#[cfg(all(not(feature = "rustc-dep-of-std"), feature = "with_submodule"))]
fn configure_cargo_rerun_if_changed(src_dir: &Path) {
	fn is_not_ignored(entry: &DirEntry) -> bool {
		// Ignore .git .vscode and target directories, but not .cargo or .github
		if entry.depth() == 1
			&& entry.path().is_dir()
			&& (entry.path().ends_with("target")
				|| entry.path().ends_with(".git")
				|| entry.path().ends_with(".vscode"))
		{
			return false;
		}
		true
	}

	WalkDir::new(src_dir)
		.into_iter()
		.filter_entry(is_not_ignored)
		.filter_map(|v| v.ok())
		.filter_map(|v| v.path().canonicalize().ok())
		.for_each(|x| println!("cargo:rerun-if-changed={}", x.display()));
}

#[cfg(not(feature = "rustc-dep-of-std"))]
fn create_constants() {
	let out_dir = env::var("OUT_DIR").expect("No out dir");
	let dest_path = Path::new(&out_dir).join("constants.rs");
	let mut f = File::create(&dest_path).expect("Could not create file");

	let ip = option_env!("HERMIT_IP");
	let ip = ip.map_or("10.0.5.3", |v| v);

	let gateway = option_env!("HERMIT_GATEWAY");
	let gateway = gateway.map_or("10.0.5.1", |v| v);

	let mask = option_env!("HERMIT_MASK");
	let mask = mask.map_or("255.255.255.0", |v| v);

	writeln!(&mut f, "const HERMIT_IP: &str = \"{}\";", ip).expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_IP");

	writeln!(&mut f, "const HERMIT_GATEWAY: &str = \"{}\";", gateway)
		.expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_GATEWAY");

	writeln!(&mut f, "const HERMIT_MASK: &str = \"{}\";", mask).expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_MASK");
}

fn main() {
	#[cfg(not(feature = "rustc-dep-of-std"))]
	create_constants();
	#[cfg(not(feature = "rustc-dep-of-std"))]
	build();
}
<|endoftext|>


<commit_before>use std::env;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

use toml;

pub fn get_template_source(tpl_path: &Path) -> String {
    let mut path = template_dir();
    path.push(tpl_path);
    let mut f = match File::open(&path) {
        Err(_) => {
            let msg = format!("unable to open template file '{}'", &path.to_str().unwrap());
            panic!(msg)
        }
        Ok(f) => f,
    };
    let mut s = String::new();
    f.read_to_string(&mut s).unwrap();
    if s.ends_with('\n') {
        let _ = s.pop();
    }
    s
}

pub fn find_template_from_path(path: &str, start_at: Option<&Path>) -> PathBuf {
    let root = template_dir();
    if let Some(rel) = start_at {
        let mut fs_rel_path = root.clone();
        fs_rel_path.push(rel);
        fs_rel_path = fs_rel_path.with_file_name(path);
        if fs_rel_path.exists() {
            return fs_rel_path.strip_prefix(&root).unwrap().to_owned();
        }
    }

    let mut fs_abs_path = root.clone();
    let path = Path::new(path);
    fs_abs_path.push(Path::new(path));
    if fs_abs_path.exists() {
        path.to_owned()
    } else {
        panic!(format!(
            "template {:?} not found at {:?}",
            path.to_str().unwrap(),
            fs_abs_path
        ));
    }
}

pub fn template_dir() -> PathBuf {
    let mut path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
    path.push("templates");
    path
}

#[derive(Deserialize)]
struct Config {
    dirs: Option<Vec<String>>,
}

pub fn template_dirs() -> Vec<PathBuf> {
    let root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
    let askama = root.join("askama.toml");
    let default = vec![root.join("templates")];
    if askama.exists() {
        let mut contents = String::new();
        File::open(askama).unwrap().read_to_string(&mut contents).unwrap();
        let config: Config = toml::from_str(&contents).unwrap();
        if let Some(dirs) = config.dirs {
            let paths: Vec<PathBuf> = dirs.into_iter().map(|directory| {
                root.join(directory)
            }).collect();
            if paths.len() > 0 {
                paths
            } else {
                default
            }
        } else {
            default
        }
    } else {
        vec![root.join("templates")]
    }
}

#[cfg(test)]
mod tests {
    use super::Path;
    use super::{find_template_from_path, get_template_source};

    #[test]
    fn get_source() {
        assert_eq!(get_template_source(Path::new("sub/b.html")), "bar");
    }

    #[test]
    fn find_absolute() {
        let path = find_template_from_path("sub/b.html", Some(Path::new("a.html")));
        assert_eq!(path, Path::new("sub/b.html"));
    }

    #[test]
    #[should_panic]
    fn find_relative_nonexistent() {
        find_template_from_path("b.html", Some(Path::new("a.html")));
    }

    #[test]
    fn find_relative() {
        let path = find_template_from_path("c.html", Some(Path::new("sub/b.html")));
        assert_eq!(path, Path::new("sub/c.html"));
    }

    #[test]
    fn find_relative_sub() {
        let path = find_template_from_path("sub1/d.html", Some(Path::new("sub/b.html")));
        assert_eq!(path, Path::new("sub/sub1/d.html"));
    }
}
<commit_msg>Add complete support for multiple template dirs<commit_after>use std::env;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

use toml;

fn search_template_in_dirs<'a>(dirs: &'a [PathBuf], path: &Path) -> Option<&'a PathBuf> {
    dirs.iter().find(|dir| dir.join(path).exists())
}

pub fn get_template_source(tpl_path: &Path) -> String {
    let dirs = template_dirs();
    let path = search_template_in_dirs(&dirs, tpl_path)
        .map(|dir| dir.join(tpl_path))
        .expect(&format!(
            "template file '{}' does not exist",
            tpl_path.to_str().unwrap()
        ));
    let mut f = match File::open(&path) {
        Err(_) => {
            let msg = format!("unable to open template file '{}'", &path.to_str().unwrap());
            panic!(msg)
        }
        Ok(f) => f,
    };
    let mut s = String::new();
    f.read_to_string(&mut s).unwrap();
    if s.ends_with('\n') {
        let _ = s.pop();
    }
    s
}

pub fn find_template_from_path(path: &str, start_at: Option<&Path>) -> PathBuf {
    let dirs = template_dirs();
    if let Some(rel) = start_at {
        let root = search_template_in_dirs(&dirs, rel).expect(&format!(
            "unable to find previously available template file '{}'",
            rel.to_str().unwrap()
        ));
        let fs_rel_path = root.join(rel).with_file_name(path);
        if fs_rel_path.exists() {
            return fs_rel_path.strip_prefix(&root).unwrap().to_owned();
        }
    }

    let path = Path::new(path);
    search_template_in_dirs(&dirs, &path).expect(&format!(
        "template {:?} not found in directories {:?}",
        path.to_str().unwrap(),
        dirs
    ));
    path.to_owned()
}

static CONFIG_FILE_NAME: &str = "askama.toml";

#[derive(Deserialize)]
struct RawConfig {
    dirs: Option<Vec<String>>,
}

struct Config {
    dirs: Vec<PathBuf>,
}

impl RawConfig {
    fn from_file(filename: &PathBuf) -> Option<RawConfig> {
        if filename.exists() {
            let mut contents = String::new();
            File::open(filename)
                .unwrap()
                .read_to_string(&mut contents)
                .unwrap();
            Some(toml::from_str(&contents).unwrap())
        } else {
            None
        }
    }
}

impl Config {
    fn from_file() -> Config {
        let root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
        let filename = root.join(CONFIG_FILE_NAME);
        RawConfig::from_file(&filename).map_or_else(
            || Config {
                dirs: vec![root.join("templates")],
            },
            |config| Config {
                dirs: config
                    .dirs
                    .unwrap_or_else(|| Vec::new())
                    .into_iter()
                    .map(|directory| root.join(directory))
                    .collect(),
            },
        )
    }
}

pub fn template_dirs() -> Vec<PathBuf> {
    Config::from_file().dirs
}

#[cfg(test)]
mod tests {
    use super::Path;
    use super::{find_template_from_path, get_template_source};

    #[test]
    fn get_source() {
        assert_eq!(get_template_source(Path::new("sub/b.html")), "bar");
    }

    #[test]
    fn find_absolute() {
        let path = find_template_from_path("sub/b.html", Some(Path::new("a.html")));
        assert_eq!(path, Path::new("sub/b.html"));
    }

    #[test]
    #[should_panic]
    fn find_relative_nonexistent() {
        find_template_from_path("b.html", Some(Path::new("a.html")));
    }

    #[test]
    fn find_relative() {
        let path = find_template_from_path("c.html", Some(Path::new("sub/b.html")));
        assert_eq!(path, Path::new("sub/c.html"));
    }

    #[test]
    fn find_relative_sub() {
        let path = find_template_from_path("sub1/d.html", Some(Path::new("sub/b.html")));
        assert_eq!(path, Path::new("sub/sub1/d.html"));
    }
}
<|endoftext|>


<commit_before>extern crate byteorder;
extern crate core;
extern crate obj;

mod tga;
mod renderer;

use tga::*;
use std::fs::File;
use renderer::line;
use std::path::*;
use obj::*;
use std::rc::Rc;

const WHITE: TgaColor = TgaColor { rgba: [255, 255, 255, 255] };
const RED: TgaColor = TgaColor { rgba: [255, 0, 0, 255] };
const BLACK: TgaColor = TgaColor { rgba: [0, 0, 0, 255] };

const WIDTH: usize = 800;
const HEIGHT: usize = 800;

fn main() {
    let b = &BLACK;
    let w = &WHITE;

    let mut image = TgaImage::new(WIDTH, HEIGHT, b);

    let obj = head();
    let verts = obj.position();
    for o in obj.object_iter() {
        for g in o.group_iter() {
            let is = g.indices();
            println!("{:?}", is.len());
            for tups in is {
                let face = tups.iter().map(|&(i, _, _)| i).collect::<Vec<_>>();
                for j in 0..3 {
                    let v0 = verts[face[j]];
                    let v1 = verts[face[(j + 1) % 3]];
                    let f_width = WIDTH as f32;
                    let f_height = HEIGHT as f32;
                    let x0 = (v0[0] + 1f32) * f_width / 2f32;
                    let y0 = (v0[1] + 1f32) * f_height / 2f32;
                    let x1 = (v1[0] + 1f32) * f_width / 2f32;
                    let y1 = (v1[1] + 1f32) * f_height / 2f32;
                    line(x0 as i32, y0 as i32, x1 as i32, y1 as i32, &mut image, w);
                }
            }
        }
    }

    let mut f = File::create("foo.tga").unwrap();
    image.write(&mut f).unwrap();
}

fn head() -> Obj<Rc<Material>, SimplePolygon> {
    let f = load::<SimplePolygon>(Path::new("obj/african_head/african_head.obj"));
    f.unwrap()
}
<commit_msg>lesson1: lines<commit_after>extern crate byteorder;
extern crate core;
extern crate obj;

mod tga;
mod renderer;

use tga::*;
use std::fs::File;
use renderer::line;
use std::path::*;
use obj::*;
use std::rc::Rc;

const WHITE: TgaColor = TgaColor { rgba: [255, 255, 255, 255] };
const RED: TgaColor = TgaColor { rgba: [255, 0, 0, 255] };
const BLACK: TgaColor = TgaColor { rgba: [0, 0, 0, 255] };

const WIDTH: usize = 800;
const HEIGHT: usize = 800;

fn main() {
    let b = &BLACK;
    let w = &WHITE;

    let mut image = TgaImage::new(WIDTH, HEIGHT, b);

    let obj = head();
    let verts = obj.position();
    for o in obj.object_iter() {
        for g in o.group_iter() {
            for tups in g.indices() {
                let face = tups.iter().map(|&(i, _, _)| i).collect::<Vec<_>>();
                for j in 0..3 {
                    let v0 = verts[face[j]];
                    let v1 = verts[face[(j + 1) % 3]];
                    let f_width = WIDTH as f32;
                    let f_height = HEIGHT as f32;
                    let x0 = (v0[0] + 1f32) * f_width / 2f32;
                    let y0 = (v0[1] + 1f32) * f_height / 2f32;
                    let x1 = (v1[0] + 1f32) * f_width / 2f32;
                    let y1 = (v1[1] + 1f32) * f_height / 2f32;
                    line(x0 as i32, y0 as i32, x1 as i32, y1 as i32, &mut image, w);
                }
            }
        }
    }

    let mut f = File::create("foo.tga").unwrap();
    image.write(&mut f).unwrap();
}

fn head() -> Obj<Rc<Material>, SimplePolygon> {
    let f = load::<SimplePolygon>(Path::new("obj/african_head/african_head.obj"));
    f.unwrap()
}
<|endoftext|>


<commit_before>trait Foo<const N: usize> {
    fn do_x(&self) -> [u8; N];
}

struct Bar;

const T: usize = 42;

impl Foo<N = const 3> for Bar {
//~^ERROR expected lifetime, type, or constant, found keyword `const`
//~^^ERROR cannot constrain an associated constant to a value
//~^^^ERROR this trait takes 1 generic argument but 0 generic arguments
//~^^^^ERROR associated type bindings are not allowed here
    fn do_x(&self) -> [u8; 3] {
        [0u8; 3]
    }
}

fn main() {}
<commit_msg>Use less verbose syntax for error annotations<commit_after>trait Foo<const N: usize> {
    fn do_x(&self) -> [u8; N];
}

struct Bar;

const T: usize = 42;

impl Foo<N = const 3> for Bar {
//~^ ERROR expected lifetime, type, or constant, found keyword `const`
//~| ERROR cannot constrain an associated constant to a value
//~| ERROR this trait takes 1 generic argument but 0 generic arguments
//~| ERROR associated type bindings are not allowed here
    fn do_x(&self) -> [u8; 3] {
        [0u8; 3]
    }
}

fn main() {}
<|endoftext|>


<commit_before>use scene::{BoxedScene, Scene};
use event::{Event, RenderArgs};
use conrod::UiContext;
use opengl_graphics::{Gl, Texture};
use graphics::{Context, AddImage, Draw};
use conrod::{
    Background,
    Color,
    Colorable,
    Drawable,
    Label,
    Labelable,
    Positionable
};
use input::{Release, Keyboard, keyboard};
use uorustlibs::art::{ArtReader, TileOrStatic, Art};
use uorustlibs::color::{Color16, Color32};
use uorustlibs::color::Color as ColorTrait;
use image::{GenericImage, ImageBuf, Rgba};

use std::io::IoResult;

fn create_slice(reader: &mut ArtReader) -> Texture {
    let max_x = 18;
    let max_y = 13;
    let limit = max_x * max_y;
    let idx = 0;
    let start = limit * idx;
    let mut dest = ImageBuf::new(800, 600);

    for x in range(0, max_x) {
        for y in range(0, max_y) {
            let maybe_tile = reader.read((x % max_y) + (y * max_x));
            match maybe_tile {
                Ok(TileOrStatic::Tile(tile)) => {
                    let (width, height, data) = tile.to_32bit();
                    let buf = ImageBuf::from_fn(width, height, |x, y| {
                        let (r, g, b, a) = data[((x % height) + (y * width)) as uint].to_rgba();
                        Rgba(r, g, b, a)
                    });
                    dest.put_image(44 * x, 44 * y, &buf)
                },
                _ => ()
            }
        }
    }

    Texture::from_image(&dest)
}

pub struct TileScene {
    reader: IoResult<ArtReader>,
    texture: Texture
}

impl TileScene {
    pub fn new() -> BoxedScene {
        let mut reader = ArtReader::new(&Path::new("./assets/artidx.mul"), &Path::new("./assets/art.mul"));
        let texture = match reader {
            Ok(ref mut reader) => create_slice(reader),
            _ => panic!("Oh dear")
        };
        let mut scene = box TileScene {
            reader: reader,
            texture: texture
        };

        scene
    }

    fn render(&self, args: RenderArgs, uic: &mut UiContext, gl: &mut Gl) {
        uic.background().color(Color::black()).draw(gl);
        let c = Context::abs(args.width as f64, args.height as f64);
        c.image(&self.texture).draw(gl);
    }
}

impl Scene for TileScene {
    fn handle_event(&mut self, e: &Event, ui_context: &mut UiContext, gl: &mut Gl) -> Option<BoxedScene> {
        match *e {
            Event::Render(args) => {
                self.render(args, ui_context, gl);
            },
            Event::Input(Release(Keyboard(key))) => {
                match key {
                    keyboard::Left => {
                    },
                    keyboard::Right => {
                    },
                    _ => ()
                }
            },
            _ => ()
        };
        None
    }
}
<commit_msg>Paging through tiles<commit_after>use scene::{BoxedScene, Scene};
use event::{Event, RenderArgs};
use conrod::UiContext;
use opengl_graphics::{Gl, Texture};
use graphics::{Context, AddImage, Draw};
use conrod::{
    Background,
    Color,
    Colorable,
    Drawable,
    Label,
    Labelable,
    Positionable
};
use input::{Release, Keyboard, keyboard};
use uorustlibs::art::{ArtReader, TileOrStatic, Art};
use uorustlibs::color::{Color16, Color32};
use uorustlibs::color::Color as ColorTrait;
use image::{GenericImage, ImageBuf, Rgba,};
use image::imageops::overlay;

use std::io::IoResult;

pub struct TileScene {
    reader: IoResult<ArtReader>,
    index: u32,
    texture: Option<Texture>
}

impl TileScene {
    pub fn new() -> BoxedScene {
        let mut reader = ArtReader::new(&Path::new("./assets/artidx.mul"), &Path::new("./assets/art.mul"));
        let mut scene = box TileScene {
            reader: reader,
            index: 0,
            texture: None
        };
        scene.create_slice();

        scene
    }

    fn create_slice(&mut self) {
        match self.reader {
            Ok(ref mut reader) => {
                let max_x = 18;
                let max_y = 13;
                let limit = max_x * max_y;
                let start = limit * self.index;
                let mut dest = ImageBuf::new(800, 600);

                for x in range(0, max_x) {
                    for y in range(0, max_y) {
                        let maybe_tile = reader.read(start + x + (y * max_x));
                        match maybe_tile {
                            Ok(TileOrStatic::Tile(tile)) => {
                                let (width, height, data) = tile.to_32bit();
                                let buf = ImageBuf::from_fn(width, height, |x, y| {
                                    let (r, g, b, a) = data[((x % height) + (y * width)) as uint].to_rgba();
                                    Rgba(r, g, b, a)
                                });
                                overlay(&mut dest, &buf, 44 * x, 44 * y)
                            },
                            _ => ()
                        }
                    }
                }

                self.texture = Some(Texture::from_image(&dest))
            },
            Err(_) => {
                self.texture = None
            }
        }
    }

    fn render(&self, args: RenderArgs, uic: &mut UiContext, gl: &mut Gl) {
        uic.background().color(Color::black()).draw(gl);
        let c = Context::abs(args.width as f64, args.height as f64);
        match self.texture {
            Some(ref texture) => {
                c.image(texture).draw(gl);
            },
            None => ()
        }
    }
}

impl Scene for TileScene {
    fn handle_event(&mut self, e: &Event, ui_context: &mut UiContext, gl: &mut Gl) -> Option<BoxedScene> {
        match *e {
            Event::Render(args) => {
                self.render(args, ui_context, gl);
            },
            Event::Input(Release(Keyboard(key))) => {
                match key {
                    keyboard::Left => {
                        if self.index > 0 {
                            self.index -= 1;
                            self.create_slice();
                        }
                    },
                    keyboard::Right => {
                        self.index += 1;
                        self.create_slice();
                    },
                    _ => ()
                }
            },
            _ => ()
        };
        None
    }
}
<|endoftext|>


<commit_before>use crate::traits::*;
use rustc_hir::def_id::CrateNum;
use rustc_index::vec::IndexVec;
use rustc_middle::mir;
use rustc_middle::ty;
use rustc_session::config::DebugInfo;
use rustc_span::symbol::{kw, Symbol};
use rustc_span::{BytePos, Span};
use rustc_target::abi::{LayoutOf, Size};

use super::operand::OperandValue;
use super::place::PlaceRef;
use super::{FunctionCx, LocalRef};

pub struct FunctionDebugContext<D> {
    pub scopes: IndexVec<mir::SourceScope, DebugScope<D>>,
    pub defining_crate: CrateNum,
}

#[derive(Copy, Clone)]
pub enum VariableKind {
    ArgumentVariable(usize /*index*/),
    LocalVariable,
}

/// Like `mir::VarDebugInfo`, but within a `mir::Local`.
#[derive(Copy, Clone)]
pub struct PerLocalVarDebugInfo<'tcx, D> {
    pub name: Symbol,
    pub source_info: mir::SourceInfo,

    /// `DIVariable` returned by `create_dbg_var`.
    pub dbg_var: Option<D>,

    /// `.place.projection` from `mir::VarDebugInfo`.
    pub projection: &'tcx ty::List<mir::PlaceElem<'tcx>>,
}

#[derive(Clone, Copy, Debug)]
pub struct DebugScope<D> {
    pub scope_metadata: Option<D>,
    // Start and end offsets of the file to which this DIScope belongs.
    // These are used to quickly determine whether some span refers to the same file.
    pub file_start_pos: BytePos,
    pub file_end_pos: BytePos,
}

impl<D> DebugScope<D> {
    pub fn is_valid(&self) -> bool {
        self.scope_metadata.is_some()
    }
}

impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
    pub fn set_debug_loc(&self, bx: &mut Bx, source_info: mir::SourceInfo) {
        let (scope, span) = self.debug_loc(source_info);
        if let Some(scope) = scope {
            bx.set_source_location(scope, span);
        }
    }

    pub fn debug_loc(&self, source_info: mir::SourceInfo) -> (Option<Bx::DIScope>, Span) {
        // Bail out if debug info emission is not enabled.
        match self.debug_context {
            None => return (None, source_info.span),
            Some(_) => {}
        }

        // In order to have a good line stepping behavior in debugger, we overwrite debug
        // locations of macro expansions with that of the outermost expansion site
        // (unless the crate is being compiled with `-Z debug-macros`).
        if !source_info.span.from_expansion() || self.cx.sess().opts.debugging_opts.debug_macros {
            let scope = self.scope_metadata_for_loc(source_info.scope, source_info.span.lo());
            (scope, source_info.span)
        } else {
            // Walk up the macro expansion chain until we reach a non-expanded span.
            // We also stop at the function body level because no line stepping can occur
            // at the level above that.
            let span = rustc_span::hygiene::walk_chain(source_info.span, self.mir.span.ctxt());
            let scope = self.scope_metadata_for_loc(source_info.scope, span.lo());
            // Use span of the outermost expansion site, while keeping the original lexical scope.
            (scope, span)
        }
    }

    // DILocations inherit source file name from the parent DIScope.  Due to macro expansions
    // it may so happen that the current span belongs to a different file than the DIScope
    // corresponding to span's containing source scope.  If so, we need to create a DIScope
    // "extension" into that file.
    fn scope_metadata_for_loc(
        &self,
        scope_id: mir::SourceScope,
        pos: BytePos,
    ) -> Option<Bx::DIScope> {
        let debug_context = self.debug_context.as_ref()?;
        let scope_metadata = debug_context.scopes[scope_id].scope_metadata;
        if pos < debug_context.scopes[scope_id].file_start_pos
            || pos >= debug_context.scopes[scope_id].file_end_pos
        {
            let sm = self.cx.sess().source_map();
            let defining_crate = debug_context.defining_crate;
            Some(self.cx.extend_scope_to_file(
                scope_metadata.unwrap(),
                &sm.lookup_char_pos(pos).file,
                defining_crate,
            ))
        } else {
            scope_metadata
        }
    }

    /// Apply debuginfo and/or name, after creating the `alloca` for a local,
    /// or initializing the local with an operand (whichever applies).
    pub fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) {
        let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full;

        // FIXME(eddyb) maybe name the return place as `_0` or `return`?
        if local == mir::RETURN_PLACE && !self.mir.local_decls[mir::RETURN_PLACE].is_user_variable()
        {
            return;
        }

        let vars = match &self.per_local_var_debug_info {
            Some(per_local) => &per_local[local],
            None => return,
        };
        let whole_local_var = vars.iter().find(|var| var.projection.is_empty()).copied();
        let has_proj = || vars.iter().any(|var| !var.projection.is_empty());

        let fallback_var = if self.mir.local_kind(local) == mir::LocalKind::Arg {
            let arg_index = local.index() - 1;

            // Add debuginfo even to unnamed arguments.
            // FIXME(eddyb) is this really needed?
            if arg_index == 0 && has_proj() {
                // Hide closure environments from debuginfo.
                // FIXME(eddyb) shouldn't `ArgumentVariable` indices
                // be offset to account for the hidden environment?
                None
            } else if whole_local_var.is_some() {
                // No need to make up anything, there is a `mir::VarDebugInfo`
                // covering the whole local.
                // FIXME(eddyb) take `whole_local_var.source_info.scope` into
                // account, just in case it doesn't use `ArgumentVariable`
                // (after #67586 gets fixed).
                None
            } else {
                let name = kw::Invalid;
                let decl = &self.mir.local_decls[local];
                let (scope, span) = if full_debug_info {
                    self.debug_loc(decl.source_info)
                } else {
                    (None, decl.source_info.span)
                };
                let dbg_var = scope.map(|scope| {
                    // FIXME(eddyb) is this `+ 1` needed at all?
                    let kind = VariableKind::ArgumentVariable(arg_index + 1);

                    self.cx.create_dbg_var(
                        self.debug_context.as_ref().unwrap(),
                        name,
                        self.monomorphize(&decl.ty),
                        scope,
                        kind,
                        span,
                    )
                });

                Some(PerLocalVarDebugInfo {
                    name,
                    source_info: decl.source_info,
                    dbg_var,
                    projection: ty::List::empty(),
                })
            }
        } else {
            None
        };

        let local_ref = &self.locals[local];

        let name = if bx.sess().fewer_names() {
            None
        } else {
            Some(match whole_local_var.or(fallback_var) {
                Some(var) if var.name != kw::Invalid => var.name.to_string(),
                _ => format!("{:?}", local),
            })
        };

        if let Some(name) = &name {
            match local_ref {
                LocalRef::Place(place) | LocalRef::UnsizedPlace(place) => {
                    bx.set_var_name(place.llval, name);
                }
                LocalRef::Operand(Some(operand)) => match operand.val {
                    OperandValue::Ref(x, ..) | OperandValue::Immediate(x) => {
                        bx.set_var_name(x, name);
                    }
                    OperandValue::Pair(a, b) => {
                        // FIXME(eddyb) these are scalar components,
                        // maybe extract the high-level fields?
                        bx.set_var_name(a, &(name.clone() + ".0"));
                        bx.set_var_name(b, &(name.clone() + ".1"));
                    }
                },
                LocalRef::Operand(None) => {}
            }
        }

        if !full_debug_info || vars.is_empty() && fallback_var.is_none() {
            return;
        }

        let base = match local_ref {
            LocalRef::Operand(None) => return,

            LocalRef::Operand(Some(operand)) => {
                // "Spill" the value onto the stack, for debuginfo,
                // without forcing non-debuginfo uses of the local
                // to also load from the stack every single time.
                // FIXME(#68817) use `llvm.dbg.value` instead,
                // at least for the cases which LLVM handles correctly.
                let spill_slot = PlaceRef::alloca(bx, operand.layout);
                if let Some(name) = name {
                    bx.set_var_name(spill_slot.llval, &(name + ".dbg.spill"));
                }
                operand.val.store(bx, spill_slot);
                spill_slot
            }

            LocalRef::Place(place) => *place,

            // FIXME(eddyb) add debuginfo for unsized places too.
            LocalRef::UnsizedPlace(_) => return,
        };

        let vars = vars.iter().copied().chain(fallback_var);

        for var in vars {
            let mut layout = base.layout;
            let mut direct_offset = Size::ZERO;
            // FIXME(eddyb) use smallvec here.
            let mut indirect_offsets = vec![];

            for elem in &var.projection[..] {
                match *elem {
                    mir::ProjectionElem::Deref => {
                        indirect_offsets.push(Size::ZERO);
                        layout = bx.cx().layout_of(
                            layout
                                .ty
                                .builtin_deref(true)
                                .unwrap_or_else(|| {
                                    span_bug!(var.source_info.span, "cannot deref `{}`", layout.ty)
                                })
                                .ty,
                        );
                    }
                    mir::ProjectionElem::Field(field, _) => {
                        let i = field.index();
                        let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
                        *offset += layout.fields.offset(i);
                        layout = layout.field(bx.cx(), i);
                    }
                    mir::ProjectionElem::Downcast(_, variant) => {
                        layout = layout.for_variant(bx.cx(), variant);
                    }
                    _ => span_bug!(
                        var.source_info.span,
                        "unsupported var debuginfo place `{:?}`",
                        mir::Place { local, projection: var.projection },
                    ),
                }
            }

            let (scope, span) = self.debug_loc(var.source_info);
            if let Some(scope) = scope {
                if let Some(dbg_var) = var.dbg_var {
                    bx.dbg_var_addr(
                        dbg_var,
                        scope,
                        base.llval,
                        direct_offset,
                        &indirect_offsets,
                        span,
                    );
                }
            }
        }
    }

    pub fn debug_introduce_locals(&self, bx: &mut Bx) {
        if bx.sess().opts.debuginfo == DebugInfo::Full || !bx.sess().fewer_names() {
            for local in self.locals.indices() {
                self.debug_introduce_local(bx, local);
            }
        }
    }

    /// Partition all `VarDebugInfo` in `self.mir`, by their base `Local`.
    pub fn compute_per_local_var_debug_info(
        &self,
    ) -> Option<IndexVec<mir::Local, Vec<PerLocalVarDebugInfo<'tcx, Bx::DIVariable>>>> {
        let full_debug_info = self.cx.sess().opts.debuginfo == DebugInfo::Full;

        if !full_debug_info && self.cx.sess().fewer_names() {
            return None;
        }

        let mut per_local = IndexVec::from_elem(vec![], &self.mir.local_decls);
        for var in &self.mir.var_debug_info {
            let (scope, span) = if full_debug_info {
                self.debug_loc(var.source_info)
            } else {
                (None, var.source_info.span)
            };
            let dbg_var = scope.map(|scope| {
                let place = var.place;
                let var_ty = self.monomorphized_place_ty(place.as_ref());
                let var_kind = if self.mir.local_kind(place.local) == mir::LocalKind::Arg
                    && place.projection.is_empty()
                    && var.source_info.scope == mir::OUTERMOST_SOURCE_SCOPE
                {
                    let arg_index = place.local.index() - 1;

                    // FIXME(eddyb) shouldn't `ArgumentVariable` indices be
                    // offset in closures to account for the hidden environment?
                    // Also, is this `+ 1` needed at all?
                    VariableKind::ArgumentVariable(arg_index + 1)
                } else {
                    VariableKind::LocalVariable
                };
                self.cx.create_dbg_var(
                    self.debug_context.as_ref().unwrap(),
                    var.name,
                    var_ty,
                    scope,
                    var_kind,
                    span,
                )
            });

            per_local[var.place.local].push(PerLocalVarDebugInfo {
                name: var.name,
                source_info: var.source_info,
                dbg_var,
                projection: var.place.projection,
            });
        }
        Some(per_local)
    }
}
<commit_msg>Don't spill operands onto the stack in naked functions<commit_after>use crate::traits::*;
use rustc_hir::def_id::CrateNum;
use rustc_index::vec::IndexVec;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir;
use rustc_middle::ty;
use rustc_session::config::DebugInfo;
use rustc_span::symbol::{kw, Symbol};
use rustc_span::{BytePos, Span};
use rustc_target::abi::{LayoutOf, Size};

use super::operand::OperandValue;
use super::place::PlaceRef;
use super::{FunctionCx, LocalRef};

pub struct FunctionDebugContext<D> {
    pub scopes: IndexVec<mir::SourceScope, DebugScope<D>>,
    pub defining_crate: CrateNum,
}

#[derive(Copy, Clone)]
pub enum VariableKind {
    ArgumentVariable(usize /*index*/),
    LocalVariable,
}

/// Like `mir::VarDebugInfo`, but within a `mir::Local`.
#[derive(Copy, Clone)]
pub struct PerLocalVarDebugInfo<'tcx, D> {
    pub name: Symbol,
    pub source_info: mir::SourceInfo,

    /// `DIVariable` returned by `create_dbg_var`.
    pub dbg_var: Option<D>,

    /// `.place.projection` from `mir::VarDebugInfo`.
    pub projection: &'tcx ty::List<mir::PlaceElem<'tcx>>,
}

#[derive(Clone, Copy, Debug)]
pub struct DebugScope<D> {
    pub scope_metadata: Option<D>,
    // Start and end offsets of the file to which this DIScope belongs.
    // These are used to quickly determine whether some span refers to the same file.
    pub file_start_pos: BytePos,
    pub file_end_pos: BytePos,
}

impl<D> DebugScope<D> {
    pub fn is_valid(&self) -> bool {
        self.scope_metadata.is_some()
    }
}

impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
    pub fn set_debug_loc(&self, bx: &mut Bx, source_info: mir::SourceInfo) {
        let (scope, span) = self.debug_loc(source_info);
        if let Some(scope) = scope {
            bx.set_source_location(scope, span);
        }
    }

    pub fn debug_loc(&self, source_info: mir::SourceInfo) -> (Option<Bx::DIScope>, Span) {
        // Bail out if debug info emission is not enabled.
        match self.debug_context {
            None => return (None, source_info.span),
            Some(_) => {}
        }

        // In order to have a good line stepping behavior in debugger, we overwrite debug
        // locations of macro expansions with that of the outermost expansion site
        // (unless the crate is being compiled with `-Z debug-macros`).
        if !source_info.span.from_expansion() || self.cx.sess().opts.debugging_opts.debug_macros {
            let scope = self.scope_metadata_for_loc(source_info.scope, source_info.span.lo());
            (scope, source_info.span)
        } else {
            // Walk up the macro expansion chain until we reach a non-expanded span.
            // We also stop at the function body level because no line stepping can occur
            // at the level above that.
            let span = rustc_span::hygiene::walk_chain(source_info.span, self.mir.span.ctxt());
            let scope = self.scope_metadata_for_loc(source_info.scope, span.lo());
            // Use span of the outermost expansion site, while keeping the original lexical scope.
            (scope, span)
        }
    }

    // DILocations inherit source file name from the parent DIScope.  Due to macro expansions
    // it may so happen that the current span belongs to a different file than the DIScope
    // corresponding to span's containing source scope.  If so, we need to create a DIScope
    // "extension" into that file.
    fn scope_metadata_for_loc(
        &self,
        scope_id: mir::SourceScope,
        pos: BytePos,
    ) -> Option<Bx::DIScope> {
        let debug_context = self.debug_context.as_ref()?;
        let scope_metadata = debug_context.scopes[scope_id].scope_metadata;
        if pos < debug_context.scopes[scope_id].file_start_pos
            || pos >= debug_context.scopes[scope_id].file_end_pos
        {
            let sm = self.cx.sess().source_map();
            let defining_crate = debug_context.defining_crate;
            Some(self.cx.extend_scope_to_file(
                scope_metadata.unwrap(),
                &sm.lookup_char_pos(pos).file,
                defining_crate,
            ))
        } else {
            scope_metadata
        }
    }

    /// Apply debuginfo and/or name, after creating the `alloca` for a local,
    /// or initializing the local with an operand (whichever applies).
    pub fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) {
        let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full;

        // FIXME(eddyb) maybe name the return place as `_0` or `return`?
        if local == mir::RETURN_PLACE && !self.mir.local_decls[mir::RETURN_PLACE].is_user_variable()
        {
            return;
        }

        let vars = match &self.per_local_var_debug_info {
            Some(per_local) => &per_local[local],
            None => return,
        };
        let whole_local_var = vars.iter().find(|var| var.projection.is_empty()).copied();
        let has_proj = || vars.iter().any(|var| !var.projection.is_empty());

        let fallback_var = if self.mir.local_kind(local) == mir::LocalKind::Arg {
            let arg_index = local.index() - 1;

            // Add debuginfo even to unnamed arguments.
            // FIXME(eddyb) is this really needed?
            if arg_index == 0 && has_proj() {
                // Hide closure environments from debuginfo.
                // FIXME(eddyb) shouldn't `ArgumentVariable` indices
                // be offset to account for the hidden environment?
                None
            } else if whole_local_var.is_some() {
                // No need to make up anything, there is a `mir::VarDebugInfo`
                // covering the whole local.
                // FIXME(eddyb) take `whole_local_var.source_info.scope` into
                // account, just in case it doesn't use `ArgumentVariable`
                // (after #67586 gets fixed).
                None
            } else {
                let name = kw::Invalid;
                let decl = &self.mir.local_decls[local];
                let (scope, span) = if full_debug_info {
                    self.debug_loc(decl.source_info)
                } else {
                    (None, decl.source_info.span)
                };
                let dbg_var = scope.map(|scope| {
                    // FIXME(eddyb) is this `+ 1` needed at all?
                    let kind = VariableKind::ArgumentVariable(arg_index + 1);

                    self.cx.create_dbg_var(
                        self.debug_context.as_ref().unwrap(),
                        name,
                        self.monomorphize(&decl.ty),
                        scope,
                        kind,
                        span,
                    )
                });

                Some(PerLocalVarDebugInfo {
                    name,
                    source_info: decl.source_info,
                    dbg_var,
                    projection: ty::List::empty(),
                })
            }
        } else {
            None
        };

        let local_ref = &self.locals[local];

        let name = if bx.sess().fewer_names() {
            None
        } else {
            Some(match whole_local_var.or(fallback_var) {
                Some(var) if var.name != kw::Invalid => var.name.to_string(),
                _ => format!("{:?}", local),
            })
        };

        if let Some(name) = &name {
            match local_ref {
                LocalRef::Place(place) | LocalRef::UnsizedPlace(place) => {
                    bx.set_var_name(place.llval, name);
                }
                LocalRef::Operand(Some(operand)) => match operand.val {
                    OperandValue::Ref(x, ..) | OperandValue::Immediate(x) => {
                        bx.set_var_name(x, name);
                    }
                    OperandValue::Pair(a, b) => {
                        // FIXME(eddyb) these are scalar components,
                        // maybe extract the high-level fields?
                        bx.set_var_name(a, &(name.clone() + ".0"));
                        bx.set_var_name(b, &(name.clone() + ".1"));
                    }
                },
                LocalRef::Operand(None) => {}
            }
        }

        if !full_debug_info || vars.is_empty() && fallback_var.is_none() {
            return;
        }

        let base = match local_ref {
            LocalRef::Operand(None) => return,

            LocalRef::Operand(Some(operand)) => {
                // Don't spill operands onto the stack in naked functions.
                // See: https://github.com/rust-lang/rust/issues/42779
                let attrs = bx.tcx().codegen_fn_attrs(self.instance.def_id());
                if attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
                    return;
                }

                // "Spill" the value onto the stack, for debuginfo,
                // without forcing non-debuginfo uses of the local
                // to also load from the stack every single time.
                // FIXME(#68817) use `llvm.dbg.value` instead,
                // at least for the cases which LLVM handles correctly.
                let spill_slot = PlaceRef::alloca(bx, operand.layout);
                if let Some(name) = name {
                    bx.set_var_name(spill_slot.llval, &(name + ".dbg.spill"));
                }
                operand.val.store(bx, spill_slot);
                spill_slot
            }

            LocalRef::Place(place) => *place,

            // FIXME(eddyb) add debuginfo for unsized places too.
            LocalRef::UnsizedPlace(_) => return,
        };

        let vars = vars.iter().copied().chain(fallback_var);

        for var in vars {
            let mut layout = base.layout;
            let mut direct_offset = Size::ZERO;
            // FIXME(eddyb) use smallvec here.
            let mut indirect_offsets = vec![];

            for elem in &var.projection[..] {
                match *elem {
                    mir::ProjectionElem::Deref => {
                        indirect_offsets.push(Size::ZERO);
                        layout = bx.cx().layout_of(
                            layout
                                .ty
                                .builtin_deref(true)
                                .unwrap_or_else(|| {
                                    span_bug!(var.source_info.span, "cannot deref `{}`", layout.ty)
                                })
                                .ty,
                        );
                    }
                    mir::ProjectionElem::Field(field, _) => {
                        let i = field.index();
                        let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
                        *offset += layout.fields.offset(i);
                        layout = layout.field(bx.cx(), i);
                    }
                    mir::ProjectionElem::Downcast(_, variant) => {
                        layout = layout.for_variant(bx.cx(), variant);
                    }
                    _ => span_bug!(
                        var.source_info.span,
                        "unsupported var debuginfo place `{:?}`",
                        mir::Place { local, projection: var.projection },
                    ),
                }
            }

            let (scope, span) = self.debug_loc(var.source_info);
            if let Some(scope) = scope {
                if let Some(dbg_var) = var.dbg_var {
                    bx.dbg_var_addr(
                        dbg_var,
                        scope,
                        base.llval,
                        direct_offset,
                        &indirect_offsets,
                        span,
                    );
                }
            }
        }
    }

    pub fn debug_introduce_locals(&self, bx: &mut Bx) {
        if bx.sess().opts.debuginfo == DebugInfo::Full || !bx.sess().fewer_names() {
            for local in self.locals.indices() {
                self.debug_introduce_local(bx, local);
            }
        }
    }

    /// Partition all `VarDebugInfo` in `self.mir`, by their base `Local`.
    pub fn compute_per_local_var_debug_info(
        &self,
    ) -> Option<IndexVec<mir::Local, Vec<PerLocalVarDebugInfo<'tcx, Bx::DIVariable>>>> {
        let full_debug_info = self.cx.sess().opts.debuginfo == DebugInfo::Full;

        if !full_debug_info && self.cx.sess().fewer_names() {
            return None;
        }

        let mut per_local = IndexVec::from_elem(vec![], &self.mir.local_decls);
        for var in &self.mir.var_debug_info {
            let (scope, span) = if full_debug_info {
                self.debug_loc(var.source_info)
            } else {
                (None, var.source_info.span)
            };
            let dbg_var = scope.map(|scope| {
                let place = var.place;
                let var_ty = self.monomorphized_place_ty(place.as_ref());
                let var_kind = if self.mir.local_kind(place.local) == mir::LocalKind::Arg
                    && place.projection.is_empty()
                    && var.source_info.scope == mir::OUTERMOST_SOURCE_SCOPE
                {
                    let arg_index = place.local.index() - 1;

                    // FIXME(eddyb) shouldn't `ArgumentVariable` indices be
                    // offset in closures to account for the hidden environment?
                    // Also, is this `+ 1` needed at all?
                    VariableKind::ArgumentVariable(arg_index + 1)
                } else {
                    VariableKind::LocalVariable
                };
                self.cx.create_dbg_var(
                    self.debug_context.as_ref().unwrap(),
                    var.name,
                    var_ty,
                    scope,
                    var_kind,
                    span,
                )
            });

            per_local[var.place.local].push(PerLocalVarDebugInfo {
                name: var.name,
                source_info: var.source_info,
                dbg_var,
                projection: var.place.projection,
            });
        }
        Some(per_local)
    }
}
<|endoftext|>


<commit_before>use cpython::*;

fn test_thread_exit_py(py: Python) -> PyResult<()> {
    let m = py.import("sys")?;
    m.add(py, "exit_thread", py_fn!(py, exit_thread()))?;
    py.run(
        "
import sys, threading
# should not abort
threading.Thread(target=sys.exit_thread).start()
",
        None,
        None,
    )?;
    py.allow_threads(|| {
        std::thread::sleep(std::time::Duration::from_millis(100));
    });
    Ok(())
}

// This test requires Rust 1.40, namely
// https://github.com/rust-lang/rust/pull/65646.
//
// `pthread_exit` on Linux raises a C++ `__foced_unwind` exception to unwind
// the stack. That exception does not inherit from C++ `std::exception` and
// must be rethrown if caught, otherwise pthread will abort with
// `FATAL: exception not rethrown`. On the Rust land, `catch_unwind` before
// PR65646 will incorrectly silent that C++ exception without rethrowing,
// breaking this test.
//
// Also skip nightly compilers, which might include some unstable changes
// affecting the test.
#[rustversion::all(since(1.40), stable)]
#[test]
fn test_thread_exit() {
    let gil = Python::acquire_gil();
    let py = gil.python();
    test_thread_exit_py(py).unwrap();
}

fn exit_thread(py: Python) -> PyResult<String> {
    #[cfg(unix)]
    {
        py.allow_threads(|| {
            // Emulates PyThread_exit_thread. It can happen during Py_Finalize.
            unsafe { libc::pthread_exit(std::ptr::null_mut()) };
        })
    }
    #[cfg(not(unix))]
    {
        let _ = py;
        // pthread might not exist on this platform.
        Ok(String::new())
    }
}
<commit_msg>disable test_thread_exit on rustc >= 1.48<commit_after>use cpython::*;

#[allow(unused)]
fn test_thread_exit_py(py: Python) -> PyResult<()> {
    let m = py.import("sys")?;
    m.add(py, "exit_thread", py_fn!(py, exit_thread()))?;
    py.run(
        "
import sys, threading
# should not abort
threading.Thread(target=sys.exit_thread).start()
",
        None,
        None,
    )?;
    py.allow_threads(|| {
        std::thread::sleep(std::time::Duration::from_millis(100));
    });
    Ok(())
}

// This test requires Rust 1.40, namely
// https://github.com/rust-lang/rust/pull/65646.
//
// `pthread_exit` on Linux raises a C++ `__foced_unwind` exception to unwind
// the stack. That exception does not inherit from C++ `std::exception` and
// must be rethrown if caught, otherwise pthread will abort with
// `FATAL: exception not rethrown`. On the Rust land, `catch_unwind` before
// PR65646 will incorrectly silence that C++ exception without rethrowing,
// breaking this test.
//
// This test no longer works after Rust 1.48, as unwinding across ABI
// boundaries is still undefined until the "C-unwind" project is complete.
// See: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md
//
// Also skip nightly compilers, which might include some unstable changes
// affecting the test.
#[rustversion::all(since(1.40), before(1.48), stable)]
#[test]
fn test_thread_exit() {
    let gil = Python::acquire_gil();
    let py = gil.python();
    test_thread_exit_py(py).unwrap();
}

#[allow(unused)]
fn exit_thread(py: Python) -> PyResult<String> {
    #[cfg(unix)]
    {
        py.allow_threads(|| {
            // Emulates PyThread_exit_thread. It can happen during Py_Finalize.
            unsafe { libc::pthread_exit(std::ptr::null_mut()) };
        })
    }
    #[cfg(not(unix))]
    {
        let _ = py;
        // pthread might not exist on this platform.
        Ok(String::new())
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![allow(unsafe_code, unrooted_must_root)]

use dom::attr::AttrHelpers;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::InheritTypes::{CharacterDataCast, DocumentTypeCast};
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLScriptElementCast};
use dom::bindings::codegen::InheritTypes::{HTMLFormElementDerived, NodeCast};
use dom::bindings::codegen::InheritTypes::ProcessingInstructionCast;
use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootable, Root};
use dom::bindings::js::RootedReference;
use dom::bindings::trace::RootedVec;
use dom::characterdata::CharacterDataHelpers;
use dom::comment::Comment;
use dom::document::{Document, DocumentHelpers};
use dom::document::{DocumentSource, IsHTMLDocument};
use dom::documenttype::DocumentType;
use dom::element::{Element, AttributeHandlers, ElementHelpers, ElementCreator};
use dom::htmlscriptelement::HTMLScriptElement;
use dom::htmlscriptelement::HTMLScriptElementHelpers;
use dom::node::{Node, NodeHelpers, NodeTypeId};
use dom::node::{document_from_node, window_from_node};
use dom::processinginstruction::ProcessingInstruction;
use dom::processinginstruction::ProcessingInstructionHelpers;
use dom::servohtmlparser;
use dom::servohtmlparser::{ServoHTMLParser, FragmentContext};
use dom::text::Text;
use parse::Parser;

use encoding::all::UTF_8;
use encoding::types::{Encoding, DecoderTrap};

use net_traits::{ProgressMsg, LoadResponse};
use util::str::DOMString;
use util::task_state;
use util::task_state::IN_HTML_PARSER;
use std::borrow::Cow;
use std::old_io::{Writer, IoResult};
use url::Url;
use html5ever::Attribute;
use html5ever::serialize::{Serializable, Serializer, AttrRef};
use html5ever::serialize::TraversalScope;
use html5ever::serialize::TraversalScope::{IncludeNode, ChildrenOnly};
use html5ever::tree_builder::{TreeSink, QuirksMode, NodeOrText, AppendNode, AppendText};
use string_cache::QualName;

use hyper::header::ContentType;
use hyper::mime::{Mime, TopLevel, SubLevel};

pub enum HTMLInput {
    InputString(String),
    InputUrl(LoadResponse),
}

trait SinkHelpers {
    fn get_or_create(&self, child: NodeOrText<JS<Node>>) -> Temporary<Node>;
}

impl SinkHelpers for servohtmlparser::Sink {
    fn get_or_create(&self, child: NodeOrText<JS<Node>>) -> Temporary<Node> {
        match child {
            AppendNode(n) => Temporary::new(n),
            AppendText(t) => {
                let doc = self.document.root();
                let text = Text::new(t, doc.r());
                NodeCast::from_temporary(text)
            }
        }
    }
}

impl<'a> TreeSink for servohtmlparser::Sink {
    type Handle = JS<Node>;
    fn get_document(&mut self) -> JS<Node> {
        let doc = self.document.root();
        let node: JSRef<Node> = NodeCast::from_ref(doc.r());
        JS::from_rooted(node)
    }

    fn same_node(&self, x: JS<Node>, y: JS<Node>) -> bool {
        x == y
    }

    fn elem_name(&self, target: JS<Node>) -> QualName {
        let node: Root<Node> = target.root();
        let elem: JSRef<Element> = ElementCast::to_ref(node.r())
            .expect("tried to get name of non-Element in HTML parsing");
        QualName {
            ns: elem.namespace().clone(),
            local: elem.local_name().clone(),
        }
    }

    fn create_element(&mut self, name: QualName, attrs: Vec<Attribute>)
            -> JS<Node> {
        let doc = self.document.root();
        let elem = Element::create(name, None, doc.r(),
                                   ElementCreator::ParserCreated).root();

        for attr in attrs.into_iter() {
            elem.r().set_attribute_from_parser(attr.name, attr.value, None);
        }

        let node: JSRef<Node> = NodeCast::from_ref(elem.r());
        JS::from_rooted(node)
    }

    fn create_comment(&mut self, text: String) -> JS<Node> {
        let doc = self.document.root();
        let comment = Comment::new(text, doc.r());
        let node: Root<Node> = NodeCast::from_temporary(comment).root();
        JS::from_rooted(node.r())
    }

    fn append_before_sibling(&mut self,
            sibling: JS<Node>,
            new_node: NodeOrText<JS<Node>>) -> Result<(), NodeOrText<JS<Node>>> {
        // If there is no parent, return the node to the parser.
        let sibling: Root<Node> = sibling.root();
        let parent = match sibling.r().parent_node() {
            Some(p) => p.root(),
            None => return Err(new_node),
        };

        let child = self.get_or_create(new_node).root();
        assert!(parent.r().InsertBefore(child.r(), Some(sibling.r())).is_ok());
        Ok(())
    }

    fn parse_error(&mut self, msg: Cow<'static, str>) {
        debug!("Parse error: {}", msg);
    }

    fn set_quirks_mode(&mut self, mode: QuirksMode) {
        let doc = self.document.root();
        doc.r().set_quirks_mode(mode);
    }

    fn append(&mut self, parent: JS<Node>, child: NodeOrText<JS<Node>>) {
        let parent: Root<Node> = parent.root();
        let child = self.get_or_create(child).root();

        // FIXME(#3701): Use a simpler algorithm and merge adjacent text nodes
        assert!(parent.r().AppendChild(child.r()).is_ok());
    }

    fn append_doctype_to_document(&mut self, name: String, public_id: String, system_id: String) {
        let doc = self.document.root();
        let doc_node: JSRef<Node> = NodeCast::from_ref(doc.r());
        let doctype = DocumentType::new(name, Some(public_id), Some(system_id), doc.r());
        let node: Root<Node> = NodeCast::from_temporary(doctype).root();

        assert!(doc_node.AppendChild(node.r()).is_ok());
    }

    fn add_attrs_if_missing(&mut self, target: JS<Node>, attrs: Vec<Attribute>) {
        let node: Root<Node> = target.root();
        let elem: JSRef<Element> = ElementCast::to_ref(node.r())
            .expect("tried to set attrs on non-Element in HTML parsing");
        for attr in attrs.into_iter() {
            elem.set_attribute_from_parser(attr.name, attr.value, None);
        }
    }

    fn remove_from_parent(&mut self, _target: JS<Node>) {
        error!("remove_from_parent not implemented!");
    }

    fn mark_script_already_started(&mut self, node: JS<Node>) {
        let node: Root<Node> = node.root();
        let script: Option<JSRef<HTMLScriptElement>> = HTMLScriptElementCast::to_ref(node.r());
        script.map(|script| script.mark_already_started());
    }

    fn complete_script(&mut self, node: JS<Node>) {
        let node: Root<Node> = node.root();
        let script: Option<JSRef<HTMLScriptElement>> = HTMLScriptElementCast::to_ref(node.r());
        script.map(|script| script.prepare());
    }

    fn reparent_children(&mut self, _node: JS<Node>, _new_parent: JS<Node>) {
        panic!("unimplemented")
    }
}

impl<'a> Serializable for JSRef<'a, Node> {
    fn serialize<'wr, Wr: Writer>(&self, serializer: &mut Serializer<'wr, Wr>,
                                  traversal_scope: TraversalScope) -> IoResult<()> {
        let node = *self;
        match (traversal_scope, node.type_id()) {
            (_, NodeTypeId::Element(..)) => {
                let elem: JSRef<Element> = ElementCast::to_ref(node).unwrap();
                let name = QualName::new(elem.namespace().clone(),
                                         elem.local_name().clone());
                if traversal_scope == IncludeNode {
                    let attrs = elem.attrs().iter().map(|at| {
                        let attr = at.root();
                        let qname = QualName::new(attr.r().namespace().clone(),
                                                  attr.r().local_name().clone());
                        let value = attr.r().value().clone();
                        (qname, value)
                    }).collect::<Vec<_>>();
                    let attr_refs = attrs.iter().map(|&(ref qname, ref value)| {
                        let ar: AttrRef = (&qname, value.as_slice());
                        ar
                    });
                    try!(serializer.start_elem(name.clone(), attr_refs));
                }

                for handle in node.children() {
                    let handle = handle.root();
                    try!(handle.r().serialize(serializer, IncludeNode));
                }

                if traversal_scope == IncludeNode {
                    try!(serializer.end_elem(name.clone()));
                }
                Ok(())
            },

            (ChildrenOnly, NodeTypeId::Document) => {
                for handle in node.children() {
                    let handle = handle.root();
                    try!(handle.r().serialize(serializer, IncludeNode));
                }
                Ok(())
            },

            (ChildrenOnly, _) => Ok(()),

            (IncludeNode, NodeTypeId::DocumentType) => {
                let doctype: JSRef<DocumentType> = DocumentTypeCast::to_ref(node).unwrap();
                serializer.write_doctype(doctype.name().as_slice())
            },

            (IncludeNode, NodeTypeId::Text) => {
                let cdata = CharacterDataCast::to_ref(node).unwrap();
                serializer.write_text(&cdata.data())
            },

            (IncludeNode, NodeTypeId::Comment) => {
                let cdata = CharacterDataCast::to_ref(node).unwrap();
                serializer.write_comment(&cdata.data())
            },

            (IncludeNode, NodeTypeId::ProcessingInstruction) => {
                let pi: JSRef<ProcessingInstruction> = ProcessingInstructionCast::to_ref(node).unwrap();
                let data = CharacterDataCast::from_ref(pi).data();
                serializer.write_processing_instruction(&pi.target(), &data)
            },

            (IncludeNode, NodeTypeId::DocumentFragment) => Ok(()),

            (IncludeNode, NodeTypeId::Document) => panic!("Can't serialize Document node itself"),
        }
    }
}

pub fn parse_html(document: JSRef<Document>,
                  input: HTMLInput,
                  url: &Url,
                  fragment_context: Option<FragmentContext>) {
    let parser = match fragment_context {
        None => ServoHTMLParser::new(Some(url.clone()), document).root(),
        Some(fc) => ServoHTMLParser::new_for_fragment(Some(url.clone()), document, fc).root(),
    };
    let parser: JSRef<ServoHTMLParser> = parser.r();

    let nested_parse = task_state::get().contains(task_state::IN_HTML_PARSER);
    if !nested_parse {
        task_state::enter(IN_HTML_PARSER);
    }

    fn parse_progress(parser: &JSRef<ServoHTMLParser>, url: &Url, load_response: &LoadResponse) {
        for msg in load_response.progress_port.iter() {
            match msg {
                ProgressMsg::Payload(data) => {
                    // FIXME: use Vec<u8> (html5ever #34)
                    let data = UTF_8.decode(data.as_slice(), DecoderTrap::Replace).unwrap();
                    parser.parse_chunk(data);
                }
                ProgressMsg::Done(Err(err)) => {
                    debug!("Failed to load page URL {}, error: {}", url.serialize(), err);
                    // TODO(Savago): we should send a notification to callers #5463.
                    break;
                }
                ProgressMsg::Done(Ok(())) => break,
            }
        }
    };

    match input {
        HTMLInput::InputString(s) => {
            parser.parse_chunk(s);
        }
        HTMLInput::InputUrl(load_response) => {
            match load_response.metadata.content_type {
                Some(ContentType(Mime(TopLevel::Image, _, _))) => {
                    let page = format!("", url.serialize());
                    parser.parse_chunk(page);
                },
                Some(ContentType(Mime(TopLevel::Text, SubLevel::Plain, _))) => {
                    // FIXME: When servo/html5ever#109 is fixed remove <plaintext> usage and
                    // replace with fix from that issue.

                    // text/plain documents require setting the tokenizer into PLAINTEXT mode.
                    // This is done by using a <plaintext> element as the html5ever tokenizer
                    // provides no other way to change to that state.
                    // Spec for text/plain handling is:
                    // https://html.spec.whatwg.org/multipage/#read-text
                    let page = format!("
\u{000A}<plaintext>");
                    parser.parse_chunk(page);
                    parse_progress(&parser, url, &load_response);
                },
                _ => {
                    parse_progress(&parser, url, &load_response);
                }
            }
        }
    }

    parser.finish();

    if !nested_parse {
        task_state::exit(IN_HTML_PARSER);
    }

    debug!("finished parsing");
}

// https://html.spec.whatwg.org/multipage/#parsing-html-fragments
pub fn parse_html_fragment(context_node: JSRef<Node>,
                           input: DOMString,
                           output: &mut RootedVec<JS<Node>>) {
    let window = window_from_node(context_node).root();
    let context_document = document_from_node(context_node).root();
    let url = context_document.r().url();

    // Step 1.
    let document = Document::new(window.r(), Some(url.clone()),
                                 IsHTMLDocument::HTMLDocument,
                                 None, None,
                                 DocumentSource::FromParser).root();

    // Step 2.
    document.r().set_quirks_mode(context_document.r().quirks_mode());

    // Step 11.
    let form = context_node.inclusive_ancestors()
                           .map(|element| element.root())
                           .find(|element| element.r().is_htmlformelement());
    let fragment_context = FragmentContext {
        context_elem: context_node,
        form_elem: form.r(),
    };
    parse_html(document.r(), HTMLInput::InputString(input), &url, Some(fragment_context));

    // Step 14.
    let root_element = document.r().GetDocumentElement().expect("no document element").root();
    let root_node: JSRef<Node> = NodeCast::from_ref(root_element.r());
    for child in root_node.children() {
        output.push(JS::from_rooted(child));
    }
}
<commit_msg>Change parse_progress() take JSRef by value. [Issue#5738]<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![allow(unsafe_code, unrooted_must_root)]

use dom::attr::AttrHelpers;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::InheritTypes::{CharacterDataCast, DocumentTypeCast};
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLScriptElementCast};
use dom::bindings::codegen::InheritTypes::{HTMLFormElementDerived, NodeCast};
use dom::bindings::codegen::InheritTypes::ProcessingInstructionCast;
use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootable, Root};
use dom::bindings::js::RootedReference;
use dom::bindings::trace::RootedVec;
use dom::characterdata::CharacterDataHelpers;
use dom::comment::Comment;
use dom::document::{Document, DocumentHelpers};
use dom::document::{DocumentSource, IsHTMLDocument};
use dom::documenttype::DocumentType;
use dom::element::{Element, AttributeHandlers, ElementHelpers, ElementCreator};
use dom::htmlscriptelement::HTMLScriptElement;
use dom::htmlscriptelement::HTMLScriptElementHelpers;
use dom::node::{Node, NodeHelpers, NodeTypeId};
use dom::node::{document_from_node, window_from_node};
use dom::processinginstruction::ProcessingInstruction;
use dom::processinginstruction::ProcessingInstructionHelpers;
use dom::servohtmlparser;
use dom::servohtmlparser::{ServoHTMLParser, FragmentContext};
use dom::text::Text;
use parse::Parser;

use encoding::all::UTF_8;
use encoding::types::{Encoding, DecoderTrap};

use net_traits::{ProgressMsg, LoadResponse};
use util::str::DOMString;
use util::task_state;
use util::task_state::IN_HTML_PARSER;
use std::borrow::Cow;
use std::old_io::{Writer, IoResult};
use url::Url;
use html5ever::Attribute;
use html5ever::serialize::{Serializable, Serializer, AttrRef};
use html5ever::serialize::TraversalScope;
use html5ever::serialize::TraversalScope::{IncludeNode, ChildrenOnly};
use html5ever::tree_builder::{TreeSink, QuirksMode, NodeOrText, AppendNode, AppendText};
use string_cache::QualName;

use hyper::header::ContentType;
use hyper::mime::{Mime, TopLevel, SubLevel};

pub enum HTMLInput {
    InputString(String),
    InputUrl(LoadResponse),
}

trait SinkHelpers {
    fn get_or_create(&self, child: NodeOrText<JS<Node>>) -> Temporary<Node>;
}

impl SinkHelpers for servohtmlparser::Sink {
    fn get_or_create(&self, child: NodeOrText<JS<Node>>) -> Temporary<Node> {
        match child {
            AppendNode(n) => Temporary::new(n),
            AppendText(t) => {
                let doc = self.document.root();
                let text = Text::new(t, doc.r());
                NodeCast::from_temporary(text)
            }
        }
    }
}

impl<'a> TreeSink for servohtmlparser::Sink {
    type Handle = JS<Node>;
    fn get_document(&mut self) -> JS<Node> {
        let doc = self.document.root();
        let node: JSRef<Node> = NodeCast::from_ref(doc.r());
        JS::from_rooted(node)
    }

    fn same_node(&self, x: JS<Node>, y: JS<Node>) -> bool {
        x == y
    }

    fn elem_name(&self, target: JS<Node>) -> QualName {
        let node: Root<Node> = target.root();
        let elem: JSRef<Element> = ElementCast::to_ref(node.r())
            .expect("tried to get name of non-Element in HTML parsing");
        QualName {
            ns: elem.namespace().clone(),
            local: elem.local_name().clone(),
        }
    }

    fn create_element(&mut self, name: QualName, attrs: Vec<Attribute>)
            -> JS<Node> {
        let doc = self.document.root();
        let elem = Element::create(name, None, doc.r(),
                                   ElementCreator::ParserCreated).root();

        for attr in attrs.into_iter() {
            elem.r().set_attribute_from_parser(attr.name, attr.value, None);
        }

        let node: JSRef<Node> = NodeCast::from_ref(elem.r());
        JS::from_rooted(node)
    }

    fn create_comment(&mut self, text: String) -> JS<Node> {
        let doc = self.document.root();
        let comment = Comment::new(text, doc.r());
        let node: Root<Node> = NodeCast::from_temporary(comment).root();
        JS::from_rooted(node.r())
    }

    fn append_before_sibling(&mut self,
            sibling: JS<Node>,
            new_node: NodeOrText<JS<Node>>) -> Result<(), NodeOrText<JS<Node>>> {
        // If there is no parent, return the node to the parser.
        let sibling: Root<Node> = sibling.root();
        let parent = match sibling.r().parent_node() {
            Some(p) => p.root(),
            None => return Err(new_node),
        };

        let child = self.get_or_create(new_node).root();
        assert!(parent.r().InsertBefore(child.r(), Some(sibling.r())).is_ok());
        Ok(())
    }

    fn parse_error(&mut self, msg: Cow<'static, str>) {
        debug!("Parse error: {}", msg);
    }

    fn set_quirks_mode(&mut self, mode: QuirksMode) {
        let doc = self.document.root();
        doc.r().set_quirks_mode(mode);
    }

    fn append(&mut self, parent: JS<Node>, child: NodeOrText<JS<Node>>) {
        let parent: Root<Node> = parent.root();
        let child = self.get_or_create(child).root();

        // FIXME(#3701): Use a simpler algorithm and merge adjacent text nodes
        assert!(parent.r().AppendChild(child.r()).is_ok());
    }

    fn append_doctype_to_document(&mut self, name: String, public_id: String, system_id: String) {
        let doc = self.document.root();
        let doc_node: JSRef<Node> = NodeCast::from_ref(doc.r());
        let doctype = DocumentType::new(name, Some(public_id), Some(system_id), doc.r());
        let node: Root<Node> = NodeCast::from_temporary(doctype).root();

        assert!(doc_node.AppendChild(node.r()).is_ok());
    }

    fn add_attrs_if_missing(&mut self, target: JS<Node>, attrs: Vec<Attribute>) {
        let node: Root<Node> = target.root();
        let elem: JSRef<Element> = ElementCast::to_ref(node.r())
            .expect("tried to set attrs on non-Element in HTML parsing");
        for attr in attrs.into_iter() {
            elem.set_attribute_from_parser(attr.name, attr.value, None);
        }
    }

    fn remove_from_parent(&mut self, _target: JS<Node>) {
        error!("remove_from_parent not implemented!");
    }

    fn mark_script_already_started(&mut self, node: JS<Node>) {
        let node: Root<Node> = node.root();
        let script: Option<JSRef<HTMLScriptElement>> = HTMLScriptElementCast::to_ref(node.r());
        script.map(|script| script.mark_already_started());
    }

    fn complete_script(&mut self, node: JS<Node>) {
        let node: Root<Node> = node.root();
        let script: Option<JSRef<HTMLScriptElement>> = HTMLScriptElementCast::to_ref(node.r());
        script.map(|script| script.prepare());
    }

    fn reparent_children(&mut self, _node: JS<Node>, _new_parent: JS<Node>) {
        panic!("unimplemented")
    }
}

impl<'a> Serializable for JSRef<'a, Node> {
    fn serialize<'wr, Wr: Writer>(&self, serializer: &mut Serializer<'wr, Wr>,
                                  traversal_scope: TraversalScope) -> IoResult<()> {
        let node = *self;
        match (traversal_scope, node.type_id()) {
            (_, NodeTypeId::Element(..)) => {
                let elem: JSRef<Element> = ElementCast::to_ref(node).unwrap();
                let name = QualName::new(elem.namespace().clone(),
                                         elem.local_name().clone());
                if traversal_scope == IncludeNode {
                    let attrs = elem.attrs().iter().map(|at| {
                        let attr = at.root();
                        let qname = QualName::new(attr.r().namespace().clone(),
                                                  attr.r().local_name().clone());
                        let value = attr.r().value().clone();
                        (qname, value)
                    }).collect::<Vec<_>>();
                    let attr_refs = attrs.iter().map(|&(ref qname, ref value)| {
                        let ar: AttrRef = (&qname, value.as_slice());
                        ar
                    });
                    try!(serializer.start_elem(name.clone(), attr_refs));
                }

                for handle in node.children() {
                    let handle = handle.root();
                    try!(handle.r().serialize(serializer, IncludeNode));
                }

                if traversal_scope == IncludeNode {
                    try!(serializer.end_elem(name.clone()));
                }
                Ok(())
            },

            (ChildrenOnly, NodeTypeId::Document) => {
                for handle in node.children() {
                    let handle = handle.root();
                    try!(handle.r().serialize(serializer, IncludeNode));
                }
                Ok(())
            },

            (ChildrenOnly, _) => Ok(()),

            (IncludeNode, NodeTypeId::DocumentType) => {
                let doctype: JSRef<DocumentType> = DocumentTypeCast::to_ref(node).unwrap();
                serializer.write_doctype(doctype.name().as_slice())
            },

            (IncludeNode, NodeTypeId::Text) => {
                let cdata = CharacterDataCast::to_ref(node).unwrap();
                serializer.write_text(&cdata.data())
            },

            (IncludeNode, NodeTypeId::Comment) => {
                let cdata = CharacterDataCast::to_ref(node).unwrap();
                serializer.write_comment(&cdata.data())
            },

            (IncludeNode, NodeTypeId::ProcessingInstruction) => {
                let pi: JSRef<ProcessingInstruction> = ProcessingInstructionCast::to_ref(node).unwrap();
                let data = CharacterDataCast::from_ref(pi).data();
                serializer.write_processing_instruction(&pi.target(), &data)
            },

            (IncludeNode, NodeTypeId::DocumentFragment) => Ok(()),

            (IncludeNode, NodeTypeId::Document) => panic!("Can't serialize Document node itself"),
        }
    }
}

pub fn parse_html(document: JSRef<Document>,
                  input: HTMLInput,
                  url: &Url,
                  fragment_context: Option<FragmentContext>) {
    let parser = match fragment_context {
        None => ServoHTMLParser::new(Some(url.clone()), document).root(),
        Some(fc) => ServoHTMLParser::new_for_fragment(Some(url.clone()), document, fc).root(),
    };
    let parser: JSRef<ServoHTMLParser> = parser.r();

    let nested_parse = task_state::get().contains(task_state::IN_HTML_PARSER);
    if !nested_parse {
        task_state::enter(IN_HTML_PARSER);
    }

    fn parse_progress(parser: JSRef<ServoHTMLParser>, url: &Url, load_response: &LoadResponse) {
        for msg in load_response.progress_port.iter() {
            match msg {
                ProgressMsg::Payload(data) => {
                    // FIXME: use Vec<u8> (html5ever #34)
                    let data = UTF_8.decode(data.as_slice(), DecoderTrap::Replace).unwrap();
                    parser.parse_chunk(data);
                }
                ProgressMsg::Done(Err(err)) => {
                    debug!("Failed to load page URL {}, error: {}", url.serialize(), err);
                    // TODO(Savago): we should send a notification to callers #5463.
                    break;
                }
                ProgressMsg::Done(Ok(())) => break,
            }
        }
    };

    match input {
        HTMLInput::InputString(s) => {
            parser.parse_chunk(s);
        }
        HTMLInput::InputUrl(load_response) => {
            match load_response.metadata.content_type {
                Some(ContentType(Mime(TopLevel::Image, _, _))) => {
                    let page = format!("", url.serialize());
                    parser.parse_chunk(page);
                },
                Some(ContentType(Mime(TopLevel::Text, SubLevel::Plain, _))) => {
                    // FIXME: When servo/html5ever#109 is fixed remove <plaintext> usage and
                    // replace with fix from that issue.

                    // text/plain documents require setting the tokenizer into PLAINTEXT mode.
                    // This is done by using a <plaintext> element as the html5ever tokenizer
                    // provides no other way to change to that state.
                    // Spec for text/plain handling is:
                    // https://html.spec.whatwg.org/multipage/#read-text
                    let page = format!("
\u{000A}<plaintext>");
                    parser.parse_chunk(page);
                    parse_progress(parser, url, &load_response);
                },
                _ => {
                    parse_progress(parser, url, &load_response);
                }
            }
        }
    }

    parser.finish();

    if !nested_parse {
        task_state::exit(IN_HTML_PARSER);
    }

    debug!("finished parsing");
}

// https://html.spec.whatwg.org/multipage/#parsing-html-fragments
pub fn parse_html_fragment(context_node: JSRef<Node>,
                           input: DOMString,
                           output: &mut RootedVec<JS<Node>>) {
    let window = window_from_node(context_node).root();
    let context_document = document_from_node(context_node).root();
    let url = context_document.r().url();

    // Step 1.
    let document = Document::new(window.r(), Some(url.clone()),
                                 IsHTMLDocument::HTMLDocument,
                                 None, None,
                                 DocumentSource::FromParser).root();

    // Step 2.
    document.r().set_quirks_mode(context_document.r().quirks_mode());

    // Step 11.
    let form = context_node.inclusive_ancestors()
                           .map(|element| element.root())
                           .find(|element| element.r().is_htmlformelement());
    let fragment_context = FragmentContext {
        context_elem: context_node,
        form_elem: form.r(),
    };
    parse_html(document.r(), HTMLInput::InputString(input), &url, Some(fragment_context));

    // Step 14.
    let root_element = document.r().GetDocumentElement().expect("no document element").root();
    let root_node: JSRef<Node> = NodeCast::from_ref(root_element.r());
    for child in root_node.children() {
        output.push(JS::from_rooted(child));
    }
}
<|endoftext|>