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

use core::prelude::*;
use core::str;
use core::io;

use driver::session::{OptLevel, No, Less, Aggressive};
use driver::session::{Session};
use lib::llvm::{PassRef, ModuleRef,PassManagerRef,TargetDataRef};
use lib::llvm::llvm;
use lib;

pub struct PassManager {
    priv llpm: PassManagerRef
}

impl Drop for PassManager {
    fn finalize(&self) {
        unsafe {
            llvm::LLVMDisposePassManager(self.llpm);
        }
    }
}

impl PassManager {
    pub fn new(td: TargetDataRef) -> PassManager {
        unsafe {
            let pm = PassManager {
                llpm: llvm::LLVMCreatePassManager()
            };
            llvm::LLVMAddTargetData(td, pm.llpm);

            return pm;
        }
    }

    pub fn add_pass(&mut self, pass:PassRef) {
        unsafe {
            llvm::LLVMAddPass(self.llpm, pass);
        }
    }

    pub fn add_pass_from_name(&mut self, name:&str) {
        let pass = create_pass(name).unwrap();
        self.add_pass(pass);
    }

    pub fn run(&self, md:ModuleRef) -> bool {
        unsafe {
            llvm::LLVMRunPassManager(self.llpm, md) == lib::llvm::True
        }
    }
}

pub fn create_standard_passes(level:OptLevel) -> ~[~str] {
    let mut passes = ~[~"strip-dead-prototypes"];

    if level == No {
        passes.push(~"always-inline");
        return passes;
    }

    passes.push(~"targetlibinfo");

    passes.push(~"tbaa");
    passes.push(~"basicaa");

    passes.push(~"early-cse");

    passes.push(~"globalopt");
    passes.push(~"ipsccp");
    passes.push(~"deadargelim");
    passes.push(~"instcombine");
    passes.push(~"simplifycfg");

    passes.push(~"prune-eh");

    if level == Aggressive {
        passes.push(~"mergefunc");
    }

    passes.push(~"inline");

    passes.push(~"functionattrs");

    if level == Aggressive {
        passes.push(~"argpromotion");
    }

    passes.push(~"early-cse");
    passes.push(~"simplify-libcalls");
    passes.push(~"jump-threading");
    passes.push(~"correlated-propagation");
    passes.push(~"simplifycfg");
    passes.push(~"instcombine");

    passes.push(~"tailcallelim");
    passes.push(~"simplifycfg");
    passes.push(~"reassociate");
    passes.push(~"loop-rotate");
    passes.push(~"licm");

    passes.push(~"lcssa");
    passes.push(~"loop-unswitch");

    passes.push(~"instcombine");
    passes.push(~"indvars");
    passes.push(~"loop-idiom");
    passes.push(~"loop-deletion");

    if level == Aggressive {
        passes.push(~"loop-vectorize");
    }

    passes.push(~"loop-unroll");

    if level != Less {
        passes.push(~"gvn");
    }

    passes.push(~"memcpyopt");
    passes.push(~"sccp");

    passes.push(~"instcombine");
    passes.push(~"jump-threading");
    passes.push(~"correlated-propagation");
    passes.push(~"dse");

    passes.push(~"bb-vectorize");
    passes.push(~"instcombine");
    passes.push(~"early-cse");

    passes.push(~"loop-unroll");

    passes.push(~"adce");
    passes.push(~"simplifycfg");
    passes.push(~"instsimplify");

    if level != Less {
        passes.push(~"globaldce");
        passes.push(~"constmerge");
    }

    return passes;
}

pub fn populate_pass_manager(sess: Session, pm: &mut PassManager, pass_list:&[~str]) {
    for pass_list.each |&nm| {
        match create_pass(nm) {
            Some(p) => pm.add_pass(p),
            None    => sess.warn(fmt!("Unknown pass %s", nm))
        }
    }
}

pub fn create_pass(name:&str) -> Option<PassRef> {
    do str::as_c_str(name) |s| {
        unsafe {
            let p = llvm::LLVMCreatePass(s);
            if p.is_null() {
                None
            } else {
                Some(p)
            }
        }
    }
}

pub fn list_passes() {
    io::println("\nAvailable Passes:");

    io::println("\nAnalysis Passes:");
    for analysis_passes.each |&(name, desc)| {
        io::println(fmt!("    %-30s -- %s", name, desc));
    }
    io::println("\nTransformation Passes:");
    for transform_passes.each |&(name, desc)| {
        io::println(fmt!("    %-30s -- %s", name, desc));
    }
    io::println("\nUtility Passes:");
    for utility_passes.each |&(name, desc)| {
        io::println(fmt!("    %-30s -- %s", name, desc));
    }
}

/** Analysis Passes */
pub static analysis_passes : &'static [(&'static str, &'static str)] = &'static [
    ("aa-eval",                         "Exhausive Alias Analysis Precision Evaluator"),
    ("asan",                            "AddressSanitizer"),
    ("basicaa",                         "Basic Alias Analysis"),
    ("basiccg",                         "Basic CallGraph Construction"),
    ("block-freq",                      "Block Frequency Analysis"),
    ("cost-model",                      "Cost Model Analysis"),
    ("count-aa",                        "Count Alias Analysis Query Responses"),
    ("da",                              "Dependence Analysis"),
    ("debug-aa",                        "AA Use Debugger"),
    ("domfrontier",                     "Dominance Frontier Construction"),
    ("domtree",                         "Dominator Tree Construction"),
    ("globalsmodref-aa",                "Simple mod/ref analysis for globals"),
    ("instcount",                       "Count the various types of Instructions"),
    ("intervals",                       "Interval Partition Construction"),
    ("iv-users",                        "Induction Variable Users"),
    ("lazy-value-info",                 "Lazy Value Information Analysis"),
    ("libcall-aa",                      "LibCall Alias Analysis"),
    ("lint",                            "Statically lint-check LLVM IR"),
    ("loops",                           "Natural Loop Information"),
    ("memdep",                          "Memory Dependence Analysis"),
    ("module-debuginfo",                "Decodes module-level debug info"),
    ("profile-estimator",               "Estimate profiling information"),
    ("profile-loader",                  "Load profile information from llvmprof.out"),
    ("profile-verifier",                "Verify profiling information"),
    ("regions",                         "Detect single entry single exit regions"),
    ("scalar-evolution",                "Scalar Evolution Analysis"),
    ("scev-aa",                         "Scalar Evolution-based Alias Analysis"),
    ("tbaa",                            "Type-Based Alias Analysis"),
    ("tsan",                            "ThreadSanitizer"),
];

/** Transformation Passes */
pub static transform_passes : &'static [(&'static str, &'static str)] = &'static [
    ("adce",                            "Aggressive Dead Code Elimination"),
    ("always-inline",                   "Inliner for #[inline(always)] functions"),
    ("argpromotion",                    "Promote 'by reference' arguments to scalars"),
    ("bb-vectorize",                    "Basic-Block Vectorization"),
    ("block-placement",                 "Profile Guided Basic Block Placement"),
    ("bounds-checking",                 "Run-time bounds checking"),
    ("break-crit-edges",                "Break critical edges in CFG"),
    ("codegenprepare",                  "Optimize for code generation"),
    ("constmerge",                      "Merge Duplicate Global Constants"),
    ("constprop",                       "Simple constant propagation"),
    ("correlated-propagation",          "Value Propagation"),
    ("da",                              "Data Layout"),
    ("dce",                             "Dead Code Elimination"),
    ("deadargelim",                     "Dead Argument Elimination"),
    ("die",                             "Dead Instruction Elimination"),
    ("dse",                             "Dead Store Elimination"),
    ("early-cse",                       "Early CSE"),
    ("functionattrs",                   "Deduce function attributes"),
    ("globaldce",                       "Dead Global Elimination"),
    ("globalopt",                       "Global Variable Optimizer"),
    ("gvn",                             "Global Value Numbering"),
    ("indvars",                         "Canonicalize Induction Variables"),
    ("inline",                          "Function Integration/Inlining"),
    ("insert-edge-profiling",           "Insert instrumentation for edge profiling"),
    ("insert-gcov-profiling",           "Insert instrumentation for GCOV profiling"),
    ("insert-optimal-edge-profiling",   "Insert optimal instrumentation for edge profiling"),
    ("instcombine",                     "Combine redundant instructions"),
    ("instsimplify",                    "Remove redundant instructions"),
    ("ipconstprop",                     "Interprocedural constant propagation"),
    ("ipsccp",                          "Interprocedural Sparse Conditional Constant Propagation"),
    ("jump-threading",                  "Jump Threading"),
    ("lcssa",                           "Loop-Closed SSA Form Pass"),
    ("licm",                            "Loop Invariant Code Motion"),
    ("loop-deletion",                   "Delete dead loops"),
    ("loop-extract",                    "Extract loops into new functions"),
    ("loop-extract-single",             "Extract at most one loop into a new function"),
    ("loop-idiom",                      "Recognise loop idioms"),
    ("loop-instsimplify",               "Simplify instructions in loops"),
    ("loop-reduce",                     "Loop Strength Reduction"),
    ("loop-rotate",                     "Rotate Loops"),
    ("loop-simplify",                   "Canonicalize natural loops"),
    ("loop-unroll",                     "Unroll loops"),
    ("loop-unswitch",                   "Unswitch loops"),
    ("loop-vectorize",                  "Loop Vectorization"),
    ("lower-expect",                    "Lower 'expect' Intrinsics"),
    ("mem2reg",                         "Promote Memory to Register"),
    ("memcpyopt",                       "MemCpy Optimization"),
    ("mergefunc",                       "Merge Functions"),
    ("mergereturn",                     "Unify function exit nodes"),
    ("partial-inliner",                 "Partial Inliner"),
    ("prune-eh",                        "Remove unused exception handling info"),
    ("reassociate",                     "Reassociate expressions"),
    ("reg2mem",                         "Demote all values to stack slots"),
    ("scalarrepl",                      "Scalar Replacement of Aggregates (DT)"),
    ("scalarrepl-ssa",                  "Scalar Replacement of Aggregates (SSAUp)"),
    ("sccp",                            "Sparse Conditional Constant Propagation"),
    ("simplify-libcalls",               "Simplify well-known library calls"),
    ("simplifycfg",                     "Simplify the CFG"),
    ("sink",                            "Code sinking"),
    ("strip",                           "Strip all symbols from a module"),
    ("strip-dead-debug-info",           "Strip debug info for unused symbols"),
    ("strip-dead-prototypes",           "Strip Unused Function Prototypes"),
    ("strip-debug-declare",             "Strip all llvm.dbg.declare intrinsics"),
    ("strip-nondebug",                  "Strip all symbols, except dbg symbols, from a module"),
    ("sroa",                            "Scalar Replacement of Aggregates"),
    ("tailcallelim",                    "Tail Call Elimination"),
];

/** Utility Passes */
static utility_passes : &'static [(&'static str, &'static str)] = &'static [
    ("instnamer",                       "Assign names to anonymous instructions"),
    ("verify",                          "Module Verifier"),
];

#[test]
fn passes_exist() {
    let mut failed = ~[];
    unsafe { llvm::LLVMInitializePasses(); }
    for analysis_passes.each() |&(name,_)| {
        if !create_pass(name).is_some() {
            failed.push(name);
        }
    }
    for transform_passes.each() |&(name,_)| {
        if !create_pass(name).is_some() {
            failed.push(name);
        }
    }
    for utility_passes.each() |&(name,_)| {
        if !create_pass(name).is_some() {
            failed.push(name);
        }
    }

    if failed.len() > 0 {
        io::println("Some passes don't exist:");
        for failed.each |&n| {
            io::println(fmt!("    %s", n));
        }
        fail!();
    }
}
<commit_msg>auto merge of #6859 : thestinger/rust/optimize, r=thestinger<commit_after>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use core::prelude::*;
use core::str;
use core::io;

use driver::session::{OptLevel, No, Less, Aggressive};
use driver::session::{Session};
use lib::llvm::{PassRef, ModuleRef,PassManagerRef,TargetDataRef};
use lib::llvm::llvm;
use lib;

pub struct PassManager {
    priv llpm: PassManagerRef
}

impl Drop for PassManager {
    fn finalize(&self) {
        unsafe {
            llvm::LLVMDisposePassManager(self.llpm);
        }
    }
}

impl PassManager {
    pub fn new(td: TargetDataRef) -> PassManager {
        unsafe {
            let pm = PassManager {
                llpm: llvm::LLVMCreatePassManager()
            };
            llvm::LLVMAddTargetData(td, pm.llpm);

            return pm;
        }
    }

    pub fn add_pass(&mut self, pass:PassRef) {
        unsafe {
            llvm::LLVMAddPass(self.llpm, pass);
        }
    }

    pub fn add_pass_from_name(&mut self, name:&str) {
        let pass = create_pass(name).unwrap();
        self.add_pass(pass);
    }

    pub fn run(&self, md:ModuleRef) -> bool {
        unsafe {
            llvm::LLVMRunPassManager(self.llpm, md) == lib::llvm::True
        }
    }
}

pub fn create_standard_passes(level:OptLevel) -> ~[~str] {
    let mut passes = ~[~"strip-dead-prototypes"];

    if level == No {
        passes.push(~"always-inline");
        return passes;
    }

    passes.push(~"targetlibinfo");

    passes.push(~"tbaa");
    passes.push(~"basicaa");

    passes.push(~"early-cse");

    passes.push(~"globalopt");
    passes.push(~"ipsccp");
    passes.push(~"deadargelim");
    passes.push(~"instcombine");
    passes.push(~"simplifycfg");

    passes.push(~"prune-eh");

    if level == Aggressive {
        passes.push(~"mergefunc");
    }

    passes.push(~"inline");

    passes.push(~"functionattrs");

    if level == Aggressive {
        passes.push(~"argpromotion");
    }

    passes.push(~"early-cse");
    passes.push(~"simplify-libcalls");
    passes.push(~"jump-threading");
    passes.push(~"correlated-propagation");
    passes.push(~"simplifycfg");
    passes.push(~"instcombine");

    passes.push(~"tailcallelim");
    passes.push(~"simplifycfg");
    passes.push(~"reassociate");
    passes.push(~"loop-rotate");
    passes.push(~"licm");

    passes.push(~"lcssa");
    passes.push(~"loop-unswitch");

    passes.push(~"instcombine");
    passes.push(~"indvars");
    passes.push(~"loop-idiom");
    passes.push(~"loop-deletion");

    if level == Aggressive {
        passes.push(~"loop-vectorize");
    }

    passes.push(~"loop-unroll");

    if level != Less {
        passes.push(~"gvn");
    }

    passes.push(~"memcpyopt");
    passes.push(~"sccp");

    passes.push(~"instcombine");
    passes.push(~"jump-threading");
    passes.push(~"correlated-propagation");
    passes.push(~"dse");

    passes.push(~"instcombine");
    passes.push(~"early-cse");

    passes.push(~"loop-unroll");

    passes.push(~"adce");
    passes.push(~"simplifycfg");
    passes.push(~"instsimplify");

    if level != Less {
        passes.push(~"globaldce");
        passes.push(~"constmerge");
    }

    return passes;
}

pub fn populate_pass_manager(sess: Session, pm: &mut PassManager, pass_list:&[~str]) {
    for pass_list.each |&nm| {
        match create_pass(nm) {
            Some(p) => pm.add_pass(p),
            None    => sess.warn(fmt!("Unknown pass %s", nm))
        }
    }
}

pub fn create_pass(name:&str) -> Option<PassRef> {
    do str::as_c_str(name) |s| {
        unsafe {
            let p = llvm::LLVMCreatePass(s);
            if p.is_null() {
                None
            } else {
                Some(p)
            }
        }
    }
}

pub fn list_passes() {
    io::println("\nAvailable Passes:");

    io::println("\nAnalysis Passes:");
    for analysis_passes.each |&(name, desc)| {
        io::println(fmt!("    %-30s -- %s", name, desc));
    }
    io::println("\nTransformation Passes:");
    for transform_passes.each |&(name, desc)| {
        io::println(fmt!("    %-30s -- %s", name, desc));
    }
    io::println("\nUtility Passes:");
    for utility_passes.each |&(name, desc)| {
        io::println(fmt!("    %-30s -- %s", name, desc));
    }
}

/** Analysis Passes */
pub static analysis_passes : &'static [(&'static str, &'static str)] = &'static [
    ("aa-eval",                         "Exhausive Alias Analysis Precision Evaluator"),
    ("asan",                            "AddressSanitizer"),
    ("basicaa",                         "Basic Alias Analysis"),
    ("basiccg",                         "Basic CallGraph Construction"),
    ("block-freq",                      "Block Frequency Analysis"),
    ("cost-model",                      "Cost Model Analysis"),
    ("count-aa",                        "Count Alias Analysis Query Responses"),
    ("da",                              "Dependence Analysis"),
    ("debug-aa",                        "AA Use Debugger"),
    ("domfrontier",                     "Dominance Frontier Construction"),
    ("domtree",                         "Dominator Tree Construction"),
    ("globalsmodref-aa",                "Simple mod/ref analysis for globals"),
    ("instcount",                       "Count the various types of Instructions"),
    ("intervals",                       "Interval Partition Construction"),
    ("iv-users",                        "Induction Variable Users"),
    ("lazy-value-info",                 "Lazy Value Information Analysis"),
    ("libcall-aa",                      "LibCall Alias Analysis"),
    ("lint",                            "Statically lint-check LLVM IR"),
    ("loops",                           "Natural Loop Information"),
    ("memdep",                          "Memory Dependence Analysis"),
    ("module-debuginfo",                "Decodes module-level debug info"),
    ("profile-estimator",               "Estimate profiling information"),
    ("profile-loader",                  "Load profile information from llvmprof.out"),
    ("profile-verifier",                "Verify profiling information"),
    ("regions",                         "Detect single entry single exit regions"),
    ("scalar-evolution",                "Scalar Evolution Analysis"),
    ("scev-aa",                         "Scalar Evolution-based Alias Analysis"),
    ("tbaa",                            "Type-Based Alias Analysis"),
    ("tsan",                            "ThreadSanitizer"),
];

/** Transformation Passes */
pub static transform_passes : &'static [(&'static str, &'static str)] = &'static [
    ("adce",                            "Aggressive Dead Code Elimination"),
    ("always-inline",                   "Inliner for #[inline(always)] functions"),
    ("argpromotion",                    "Promote 'by reference' arguments to scalars"),
    ("bb-vectorize",                    "Basic-Block Vectorization"),
    ("block-placement",                 "Profile Guided Basic Block Placement"),
    ("bounds-checking",                 "Run-time bounds checking"),
    ("break-crit-edges",                "Break critical edges in CFG"),
    ("codegenprepare",                  "Optimize for code generation"),
    ("constmerge",                      "Merge Duplicate Global Constants"),
    ("constprop",                       "Simple constant propagation"),
    ("correlated-propagation",          "Value Propagation"),
    ("da",                              "Data Layout"),
    ("dce",                             "Dead Code Elimination"),
    ("deadargelim",                     "Dead Argument Elimination"),
    ("die",                             "Dead Instruction Elimination"),
    ("dse",                             "Dead Store Elimination"),
    ("early-cse",                       "Early CSE"),
    ("functionattrs",                   "Deduce function attributes"),
    ("globaldce",                       "Dead Global Elimination"),
    ("globalopt",                       "Global Variable Optimizer"),
    ("gvn",                             "Global Value Numbering"),
    ("indvars",                         "Canonicalize Induction Variables"),
    ("inline",                          "Function Integration/Inlining"),
    ("insert-edge-profiling",           "Insert instrumentation for edge profiling"),
    ("insert-gcov-profiling",           "Insert instrumentation for GCOV profiling"),
    ("insert-optimal-edge-profiling",   "Insert optimal instrumentation for edge profiling"),
    ("instcombine",                     "Combine redundant instructions"),
    ("instsimplify",                    "Remove redundant instructions"),
    ("ipconstprop",                     "Interprocedural constant propagation"),
    ("ipsccp",                          "Interprocedural Sparse Conditional Constant Propagation"),
    ("jump-threading",                  "Jump Threading"),
    ("lcssa",                           "Loop-Closed SSA Form Pass"),
    ("licm",                            "Loop Invariant Code Motion"),
    ("loop-deletion",                   "Delete dead loops"),
    ("loop-extract",                    "Extract loops into new functions"),
    ("loop-extract-single",             "Extract at most one loop into a new function"),
    ("loop-idiom",                      "Recognise loop idioms"),
    ("loop-instsimplify",               "Simplify instructions in loops"),
    ("loop-reduce",                     "Loop Strength Reduction"),
    ("loop-rotate",                     "Rotate Loops"),
    ("loop-simplify",                   "Canonicalize natural loops"),
    ("loop-unroll",                     "Unroll loops"),
    ("loop-unswitch",                   "Unswitch loops"),
    ("loop-vectorize",                  "Loop Vectorization"),
    ("lower-expect",                    "Lower 'expect' Intrinsics"),
    ("mem2reg",                         "Promote Memory to Register"),
    ("memcpyopt",                       "MemCpy Optimization"),
    ("mergefunc",                       "Merge Functions"),
    ("mergereturn",                     "Unify function exit nodes"),
    ("partial-inliner",                 "Partial Inliner"),
    ("prune-eh",                        "Remove unused exception handling info"),
    ("reassociate",                     "Reassociate expressions"),
    ("reg2mem",                         "Demote all values to stack slots"),
    ("scalarrepl",                      "Scalar Replacement of Aggregates (DT)"),
    ("scalarrepl-ssa",                  "Scalar Replacement of Aggregates (SSAUp)"),
    ("sccp",                            "Sparse Conditional Constant Propagation"),
    ("simplify-libcalls",               "Simplify well-known library calls"),
    ("simplifycfg",                     "Simplify the CFG"),
    ("sink",                            "Code sinking"),
    ("strip",                           "Strip all symbols from a module"),
    ("strip-dead-debug-info",           "Strip debug info for unused symbols"),
    ("strip-dead-prototypes",           "Strip Unused Function Prototypes"),
    ("strip-debug-declare",             "Strip all llvm.dbg.declare intrinsics"),
    ("strip-nondebug",                  "Strip all symbols, except dbg symbols, from a module"),
    ("sroa",                            "Scalar Replacement of Aggregates"),
    ("tailcallelim",                    "Tail Call Elimination"),
];

/** Utility Passes */
static utility_passes : &'static [(&'static str, &'static str)] = &'static [
    ("instnamer",                       "Assign names to anonymous instructions"),
    ("verify",                          "Module Verifier"),
];

#[test]
fn passes_exist() {
    let mut failed = ~[];
    unsafe { llvm::LLVMInitializePasses(); }
    for analysis_passes.each() |&(name,_)| {
        if !create_pass(name).is_some() {
            failed.push(name);
        }
    }
    for transform_passes.each() |&(name,_)| {
        if !create_pass(name).is_some() {
            failed.push(name);
        }
    }
    for utility_passes.each() |&(name,_)| {
        if !create_pass(name).is_some() {
            failed.push(name);
        }
    }

    if failed.len() > 0 {
        io::println("Some passes don't exist:");
        for failed.each |&n| {
            io::println(fmt!("    %s", n));
        }
        fail!();
    }
}
<|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/. */

use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding;
use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods;
use dom::bindings::codegen::Bindings::ErrorEventBinding::ErrorEventMethods;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::InheritTypes::DedicatedWorkerGlobalScopeDerived;
use dom::bindings::codegen::InheritTypes::{EventTargetCast, WorkerGlobalScopeCast};
use dom::bindings::error::{ErrorResult, report_pending_exception};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{RootCollection, Root};
use dom::bindings::refcounted::LiveDOMReferences;
use dom::bindings::structuredclone::StructuredCloneData;
use dom::bindings::utils::Reflectable;
use dom::errorevent::ErrorEvent;
use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId};
use dom::messageevent::MessageEvent;
use dom::worker::{TrustedWorkerAddress, WorkerMessageHandler, WorkerEventHandler, WorkerErrorHandler};
use dom::workerglobalscope::{WorkerGlobalScope, WorkerGlobalScopeHelpers};
use dom::workerglobalscope::WorkerGlobalScopeTypeId;
use script_task::{ScriptTask, ScriptChan, ScriptMsg, TimerSource, ScriptPort};
use script_task::StackRootTLS;

use msg::constellation_msg::PipelineId;

use devtools_traits::DevtoolsControlChan;

use net_traits::{load_whole_resource, ResourceTask};
use profile_traits::mem::{self, Reporter, ReportsChan};
use util::task::spawn_named;
use util::task_state;
use util::task_state::{SCRIPT, IN_WORKER};

use js::jsapi::{JSContext, RootedValue, HandleValue};
use js::jsapi::{JSAutoRequest, JSAutoCompartment};
use js::jsval::UndefinedValue;
use js::rust::Runtime;
use url::Url;

use rand::random;
use std::rc::Rc;
use std::sync::mpsc::{Sender, Receiver, channel};

/// A ScriptChan that can be cloned freely and will silently send a TrustedWorkerAddress with
/// every message. While this SendableWorkerScriptChan is alive, the associated Worker object
/// will remain alive.
#[derive(JSTraceable, Clone)]
pub struct SendableWorkerScriptChan {
    sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
    worker: TrustedWorkerAddress,
}

impl ScriptChan for SendableWorkerScriptChan {
    fn send(&self, msg: ScriptMsg) -> Result<(), ()> {
        return self.sender.send((self.worker.clone(), msg)).map_err(|_| ());
    }

    fn clone(&self) -> Box<ScriptChan + Send> {
        box SendableWorkerScriptChan {
            sender: self.sender.clone(),
            worker: self.worker.clone(),
        }
    }
}

impl Reporter for SendableWorkerScriptChan {
    // Just injects an appropriate event into the worker task's queue.
    fn collect_reports(&self, reports_chan: ReportsChan) -> bool {
        self.send(ScriptMsg::CollectReports(reports_chan)).is_ok()
    }
}

/// Set the `worker` field of a related DedicatedWorkerGlobalScope object to a particular
/// value for the duration of this object's lifetime. This ensures that the related Worker
/// object only lives as long as necessary (ie. while events are being executed), while
/// providing a reference that can be cloned freely.
struct AutoWorkerReset<'a> {
    workerscope: &'a DedicatedWorkerGlobalScope,
    old_worker: Option<TrustedWorkerAddress>,
}

impl<'a> AutoWorkerReset<'a> {
    fn new(workerscope: &'a DedicatedWorkerGlobalScope, worker: TrustedWorkerAddress) -> AutoWorkerReset<'a> {
        let reset = AutoWorkerReset {
            workerscope: workerscope,
            old_worker: workerscope.worker.borrow().clone()
        };
        *workerscope.worker.borrow_mut() = Some(worker);
        reset
    }
}

impl<'a> Drop for AutoWorkerReset<'a> {
    fn drop(&mut self) {
        *self.workerscope.worker.borrow_mut() = self.old_worker.clone();
    }
}

// https://html.spec.whatwg.org/multipage/#dedicatedworkerglobalscope
#[dom_struct]
pub struct DedicatedWorkerGlobalScope {
    workerglobalscope: WorkerGlobalScope,
    id: PipelineId,
    receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>,
    own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
    worker: DOMRefCell<Option<TrustedWorkerAddress>>,
    /// Sender to the parent thread.
    parent_sender: Box<ScriptChan+Send>,
}

impl DedicatedWorkerGlobalScope {
    fn new_inherited(worker_url: Url,
                     id: PipelineId,
                     mem_profiler_chan: mem::ProfilerChan,
                     devtools_chan: Option<DevtoolsControlChan>,
                     runtime: Rc<Runtime>,
                     resource_task: ResourceTask,
                     parent_sender: Box<ScriptChan+Send>,
                     own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
                     receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>)
                     -> DedicatedWorkerGlobalScope {
        DedicatedWorkerGlobalScope {
            workerglobalscope: WorkerGlobalScope::new_inherited(
                WorkerGlobalScopeTypeId::DedicatedGlobalScope, worker_url,
                runtime, resource_task, mem_profiler_chan, devtools_chan),
            id: id,
            receiver: receiver,
            own_sender: own_sender,
            parent_sender: parent_sender,
            worker: DOMRefCell::new(None),
        }
    }

    pub fn new(worker_url: Url,
               id: PipelineId,
               mem_profiler_chan: mem::ProfilerChan,
               devtools_chan: Option<DevtoolsControlChan>,
               runtime: Rc<Runtime>,
               resource_task: ResourceTask,
               parent_sender: Box<ScriptChan+Send>,
               own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
               receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>)
               -> Root<DedicatedWorkerGlobalScope> {
        let scope = box DedicatedWorkerGlobalScope::new_inherited(
            worker_url, id, mem_profiler_chan, devtools_chan, runtime.clone(), resource_task,
            parent_sender, own_sender, receiver);
        DedicatedWorkerGlobalScopeBinding::Wrap(runtime.cx(), scope)
    }
}

impl DedicatedWorkerGlobalScope {
    pub fn run_worker_scope(worker_url: Url,
                            id: PipelineId,
                            mem_profiler_chan: mem::ProfilerChan,
                            devtools_chan: Option<DevtoolsControlChan>,
                            worker: TrustedWorkerAddress,
                            resource_task: ResourceTask,
                            parent_sender: Box<ScriptChan+Send>,
                            own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
                            receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>) {
        let serialized_worker_url = worker_url.serialize();
        spawn_named(format!("WebWorker for {}", serialized_worker_url), move || {
            task_state::initialize(SCRIPT | IN_WORKER);

            let roots = RootCollection::new();
            let _stack_roots_tls = StackRootTLS::new(&roots);

            let (url, source) = match load_whole_resource(&resource_task, worker_url) {
                Err(_) => {
                    println!("error loading script {}", serialized_worker_url);
                    parent_sender.send(ScriptMsg::RunnableMsg(
                        box WorkerEventHandler::new(worker))).unwrap();
                    return;
                }
                Ok((metadata, bytes)) => {
                    (metadata.final_url, String::from_utf8(bytes).unwrap())
                }
            };

            let runtime = Rc::new(ScriptTask::new_rt_and_cx());
            let serialized_url = url.serialize();
            let global = DedicatedWorkerGlobalScope::new(
                url, id, mem_profiler_chan.clone(), devtools_chan, runtime.clone(), resource_task,
                parent_sender, own_sender, receiver);
            // FIXME(njn): workers currently don't have a unique ID suitable for using in reporter
            // registration (#6631), so we instead use a random number and cross our fingers.
            let reporter_name = format!("worker-reporter-{}", random::<u64>());
            println!("reporter_name = {}", reporter_name);

            {
                let _ar = AutoWorkerReset::new(global.r(), worker);

                match runtime.evaluate_script(
                    global.r().reflector().get_jsobject(), source, serialized_url, 1) {
                    Ok(_) => (),
                    Err(_) => {
                        // TODO: An error needs to be dispatched to the parent.
                        // https://github.com/servo/servo/issues/6422
                        println!("evaluate_script failed");
                        let _ar = JSAutoRequest::new(runtime.cx());
                        report_pending_exception(runtime.cx(), global.r().reflector().get_jsobject().get());
                    }
                }

                // Register this task as a memory reporter. This needs to be done within the
                // scope of `_ar` otherwise script_chan_as_reporter() will panic.
                let reporter = global.script_chan_as_reporter();
                let msg = mem::ProfilerMsg::RegisterReporter(reporter_name.clone(), reporter);
                mem_profiler_chan.send(msg);
            }

            loop {
                match global.r().receiver.recv() {
                    Ok((linked_worker, msg)) => {
                        let _ar = AutoWorkerReset::new(global.r(), linked_worker);
                        global.r().handle_event(msg);
                    }
                    Err(_) => break,
                }
            }

            // Unregister this task as a memory reporter.
            let msg = mem::ProfilerMsg::UnregisterReporter(reporter_name);
            mem_profiler_chan.send(msg);
        });
    }
}

pub trait DedicatedWorkerGlobalScopeHelpers {
    fn script_chan(self) -> Box<ScriptChan+Send>;
    fn script_chan_as_reporter(self) -> Box<Reporter+Send>;
    fn pipeline(self) -> PipelineId;
    fn new_script_pair(self) -> (Box<ScriptChan+Send>, Box<ScriptPort+Send>);
    fn process_event(self, msg: ScriptMsg);
}

impl<'a> DedicatedWorkerGlobalScopeHelpers for &'a DedicatedWorkerGlobalScope {
    fn script_chan(self) -> Box<ScriptChan+Send> {
        box SendableWorkerScriptChan {
            sender: self.own_sender.clone(),
            worker: self.worker.borrow().as_ref().unwrap().clone(),
        }
    }

    fn script_chan_as_reporter(self) -> Box<Reporter+Send> {
        box SendableWorkerScriptChan {
            sender: self.own_sender.clone(),
            worker: self.worker.borrow().as_ref().unwrap().clone(),
        }
    }

    fn pipeline(self) -> PipelineId {
        self.id
    }

    fn new_script_pair(self) -> (Box<ScriptChan+Send>, Box<ScriptPort+Send>) {
        let (tx, rx) = channel();
        let chan = box SendableWorkerScriptChan {
            sender: tx,
            worker: self.worker.borrow().as_ref().unwrap().clone(),
        };
        (chan, box rx)
    }

    fn process_event(self, msg: ScriptMsg) {
        self.handle_event(msg);
    }
}

trait PrivateDedicatedWorkerGlobalScopeHelpers {
    fn handle_event(self, msg: ScriptMsg);
    fn dispatch_error_to_worker(self, &ErrorEvent);
}

impl<'a> PrivateDedicatedWorkerGlobalScopeHelpers for &'a DedicatedWorkerGlobalScope {
    fn handle_event(self, msg: ScriptMsg) {
        match msg {
            ScriptMsg::DOMMessage(data) => {
                let scope = WorkerGlobalScopeCast::from_ref(self);
                let target = EventTargetCast::from_ref(self);
                let _ar = JSAutoRequest::new(scope.get_cx());
                let _ac = JSAutoCompartment::new(scope.get_cx(), scope.reflector().get_jsobject().get());
                let mut message = RootedValue::new(scope.get_cx(), UndefinedValue());
                data.read(GlobalRef::Worker(scope), message.handle_mut());
                MessageEvent::dispatch_jsval(target, GlobalRef::Worker(scope), message.handle());
            },
            ScriptMsg::RunnableMsg(runnable) => {
                runnable.handler()
            },
            ScriptMsg::RefcountCleanup(addr) => {
                LiveDOMReferences::cleanup(addr);
            }
            ScriptMsg::FireTimer(TimerSource::FromWorker, timer_id) => {
                let scope = WorkerGlobalScopeCast::from_ref(self);
                scope.handle_fire_timer(timer_id);
            }
            ScriptMsg::CollectReports(reports_chan) => {
                let scope = WorkerGlobalScopeCast::from_ref(self);
                let cx = scope.get_cx();
                let path_seg = format!("url({})", scope.get_url());
                let reports = ScriptTask::get_reports(cx, path_seg);
                reports_chan.send(reports);
            }
            _ => panic!("Unexpected message"),
        }
    }

    fn dispatch_error_to_worker(self, errorevent: &ErrorEvent) {
        let msg = errorevent.Message();
        let file_name = errorevent.Filename();
        let line_num = errorevent.Lineno();
        let col_num = errorevent.Colno();
        let worker = self.worker.borrow().as_ref().unwrap().clone();
        self.parent_sender.send(ScriptMsg::RunnableMsg(
            box WorkerErrorHandler::new(worker, msg, file_name, line_num, col_num))).unwrap();
 }
}

impl<'a> DedicatedWorkerGlobalScopeMethods for &'a DedicatedWorkerGlobalScope {
    // https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
    fn PostMessage(self, cx: *mut JSContext, message: HandleValue) -> ErrorResult {
        let data = try!(StructuredCloneData::write(cx, message));
        let worker = self.worker.borrow().as_ref().unwrap().clone();
        self.parent_sender.send(ScriptMsg::RunnableMsg(
            box WorkerMessageHandler::new(worker, data))).unwrap();
        Ok(())
    }

    event_handler!(message, GetOnmessage, SetOnmessage);
}

impl DedicatedWorkerGlobalScopeDerived for EventTarget {
    fn is_dedicatedworkerglobalscope(&self) -> bool {
        match *self.type_id() {
            EventTargetTypeId::WorkerGlobalScope(WorkerGlobalScopeTypeId::DedicatedGlobalScope) => true,
            _ => false
        }
    }
}
<commit_msg>Remove leftover debug println.<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/. */

use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding;
use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods;
use dom::bindings::codegen::Bindings::ErrorEventBinding::ErrorEventMethods;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::InheritTypes::DedicatedWorkerGlobalScopeDerived;
use dom::bindings::codegen::InheritTypes::{EventTargetCast, WorkerGlobalScopeCast};
use dom::bindings::error::{ErrorResult, report_pending_exception};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{RootCollection, Root};
use dom::bindings::refcounted::LiveDOMReferences;
use dom::bindings::structuredclone::StructuredCloneData;
use dom::bindings::utils::Reflectable;
use dom::errorevent::ErrorEvent;
use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId};
use dom::messageevent::MessageEvent;
use dom::worker::{TrustedWorkerAddress, WorkerMessageHandler, WorkerEventHandler, WorkerErrorHandler};
use dom::workerglobalscope::{WorkerGlobalScope, WorkerGlobalScopeHelpers};
use dom::workerglobalscope::WorkerGlobalScopeTypeId;
use script_task::{ScriptTask, ScriptChan, ScriptMsg, TimerSource, ScriptPort};
use script_task::StackRootTLS;

use msg::constellation_msg::PipelineId;

use devtools_traits::DevtoolsControlChan;

use net_traits::{load_whole_resource, ResourceTask};
use profile_traits::mem::{self, Reporter, ReportsChan};
use util::task::spawn_named;
use util::task_state;
use util::task_state::{SCRIPT, IN_WORKER};

use js::jsapi::{JSContext, RootedValue, HandleValue};
use js::jsapi::{JSAutoRequest, JSAutoCompartment};
use js::jsval::UndefinedValue;
use js::rust::Runtime;
use url::Url;

use rand::random;
use std::rc::Rc;
use std::sync::mpsc::{Sender, Receiver, channel};

/// A ScriptChan that can be cloned freely and will silently send a TrustedWorkerAddress with
/// every message. While this SendableWorkerScriptChan is alive, the associated Worker object
/// will remain alive.
#[derive(JSTraceable, Clone)]
pub struct SendableWorkerScriptChan {
    sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
    worker: TrustedWorkerAddress,
}

impl ScriptChan for SendableWorkerScriptChan {
    fn send(&self, msg: ScriptMsg) -> Result<(), ()> {
        return self.sender.send((self.worker.clone(), msg)).map_err(|_| ());
    }

    fn clone(&self) -> Box<ScriptChan + Send> {
        box SendableWorkerScriptChan {
            sender: self.sender.clone(),
            worker: self.worker.clone(),
        }
    }
}

impl Reporter for SendableWorkerScriptChan {
    // Just injects an appropriate event into the worker task's queue.
    fn collect_reports(&self, reports_chan: ReportsChan) -> bool {
        self.send(ScriptMsg::CollectReports(reports_chan)).is_ok()
    }
}

/// Set the `worker` field of a related DedicatedWorkerGlobalScope object to a particular
/// value for the duration of this object's lifetime. This ensures that the related Worker
/// object only lives as long as necessary (ie. while events are being executed), while
/// providing a reference that can be cloned freely.
struct AutoWorkerReset<'a> {
    workerscope: &'a DedicatedWorkerGlobalScope,
    old_worker: Option<TrustedWorkerAddress>,
}

impl<'a> AutoWorkerReset<'a> {
    fn new(workerscope: &'a DedicatedWorkerGlobalScope, worker: TrustedWorkerAddress) -> AutoWorkerReset<'a> {
        let reset = AutoWorkerReset {
            workerscope: workerscope,
            old_worker: workerscope.worker.borrow().clone()
        };
        *workerscope.worker.borrow_mut() = Some(worker);
        reset
    }
}

impl<'a> Drop for AutoWorkerReset<'a> {
    fn drop(&mut self) {
        *self.workerscope.worker.borrow_mut() = self.old_worker.clone();
    }
}

// https://html.spec.whatwg.org/multipage/#dedicatedworkerglobalscope
#[dom_struct]
pub struct DedicatedWorkerGlobalScope {
    workerglobalscope: WorkerGlobalScope,
    id: PipelineId,
    receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>,
    own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
    worker: DOMRefCell<Option<TrustedWorkerAddress>>,
    /// Sender to the parent thread.
    parent_sender: Box<ScriptChan+Send>,
}

impl DedicatedWorkerGlobalScope {
    fn new_inherited(worker_url: Url,
                     id: PipelineId,
                     mem_profiler_chan: mem::ProfilerChan,
                     devtools_chan: Option<DevtoolsControlChan>,
                     runtime: Rc<Runtime>,
                     resource_task: ResourceTask,
                     parent_sender: Box<ScriptChan+Send>,
                     own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
                     receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>)
                     -> DedicatedWorkerGlobalScope {
        DedicatedWorkerGlobalScope {
            workerglobalscope: WorkerGlobalScope::new_inherited(
                WorkerGlobalScopeTypeId::DedicatedGlobalScope, worker_url,
                runtime, resource_task, mem_profiler_chan, devtools_chan),
            id: id,
            receiver: receiver,
            own_sender: own_sender,
            parent_sender: parent_sender,
            worker: DOMRefCell::new(None),
        }
    }

    pub fn new(worker_url: Url,
               id: PipelineId,
               mem_profiler_chan: mem::ProfilerChan,
               devtools_chan: Option<DevtoolsControlChan>,
               runtime: Rc<Runtime>,
               resource_task: ResourceTask,
               parent_sender: Box<ScriptChan+Send>,
               own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
               receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>)
               -> Root<DedicatedWorkerGlobalScope> {
        let scope = box DedicatedWorkerGlobalScope::new_inherited(
            worker_url, id, mem_profiler_chan, devtools_chan, runtime.clone(), resource_task,
            parent_sender, own_sender, receiver);
        DedicatedWorkerGlobalScopeBinding::Wrap(runtime.cx(), scope)
    }
}

impl DedicatedWorkerGlobalScope {
    pub fn run_worker_scope(worker_url: Url,
                            id: PipelineId,
                            mem_profiler_chan: mem::ProfilerChan,
                            devtools_chan: Option<DevtoolsControlChan>,
                            worker: TrustedWorkerAddress,
                            resource_task: ResourceTask,
                            parent_sender: Box<ScriptChan+Send>,
                            own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
                            receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>) {
        let serialized_worker_url = worker_url.serialize();
        spawn_named(format!("WebWorker for {}", serialized_worker_url), move || {
            task_state::initialize(SCRIPT | IN_WORKER);

            let roots = RootCollection::new();
            let _stack_roots_tls = StackRootTLS::new(&roots);

            let (url, source) = match load_whole_resource(&resource_task, worker_url) {
                Err(_) => {
                    println!("error loading script {}", serialized_worker_url);
                    parent_sender.send(ScriptMsg::RunnableMsg(
                        box WorkerEventHandler::new(worker))).unwrap();
                    return;
                }
                Ok((metadata, bytes)) => {
                    (metadata.final_url, String::from_utf8(bytes).unwrap())
                }
            };

            let runtime = Rc::new(ScriptTask::new_rt_and_cx());
            let serialized_url = url.serialize();
            let global = DedicatedWorkerGlobalScope::new(
                url, id, mem_profiler_chan.clone(), devtools_chan, runtime.clone(), resource_task,
                parent_sender, own_sender, receiver);
            // FIXME(njn): workers currently don't have a unique ID suitable for using in reporter
            // registration (#6631), so we instead use a random number and cross our fingers.
            let reporter_name = format!("worker-reporter-{}", random::<u64>());

            {
                let _ar = AutoWorkerReset::new(global.r(), worker);

                match runtime.evaluate_script(
                    global.r().reflector().get_jsobject(), source, serialized_url, 1) {
                    Ok(_) => (),
                    Err(_) => {
                        // TODO: An error needs to be dispatched to the parent.
                        // https://github.com/servo/servo/issues/6422
                        println!("evaluate_script failed");
                        let _ar = JSAutoRequest::new(runtime.cx());
                        report_pending_exception(runtime.cx(), global.r().reflector().get_jsobject().get());
                    }
                }

                // Register this task as a memory reporter. This needs to be done within the
                // scope of `_ar` otherwise script_chan_as_reporter() will panic.
                let reporter = global.script_chan_as_reporter();
                let msg = mem::ProfilerMsg::RegisterReporter(reporter_name.clone(), reporter);
                mem_profiler_chan.send(msg);
            }

            loop {
                match global.r().receiver.recv() {
                    Ok((linked_worker, msg)) => {
                        let _ar = AutoWorkerReset::new(global.r(), linked_worker);
                        global.r().handle_event(msg);
                    }
                    Err(_) => break,
                }
            }

            // Unregister this task as a memory reporter.
            let msg = mem::ProfilerMsg::UnregisterReporter(reporter_name);
            mem_profiler_chan.send(msg);
        });
    }
}

pub trait DedicatedWorkerGlobalScopeHelpers {
    fn script_chan(self) -> Box<ScriptChan+Send>;
    fn script_chan_as_reporter(self) -> Box<Reporter+Send>;
    fn pipeline(self) -> PipelineId;
    fn new_script_pair(self) -> (Box<ScriptChan+Send>, Box<ScriptPort+Send>);
    fn process_event(self, msg: ScriptMsg);
}

impl<'a> DedicatedWorkerGlobalScopeHelpers for &'a DedicatedWorkerGlobalScope {
    fn script_chan(self) -> Box<ScriptChan+Send> {
        box SendableWorkerScriptChan {
            sender: self.own_sender.clone(),
            worker: self.worker.borrow().as_ref().unwrap().clone(),
        }
    }

    fn script_chan_as_reporter(self) -> Box<Reporter+Send> {
        box SendableWorkerScriptChan {
            sender: self.own_sender.clone(),
            worker: self.worker.borrow().as_ref().unwrap().clone(),
        }
    }

    fn pipeline(self) -> PipelineId {
        self.id
    }

    fn new_script_pair(self) -> (Box<ScriptChan+Send>, Box<ScriptPort+Send>) {
        let (tx, rx) = channel();
        let chan = box SendableWorkerScriptChan {
            sender: tx,
            worker: self.worker.borrow().as_ref().unwrap().clone(),
        };
        (chan, box rx)
    }

    fn process_event(self, msg: ScriptMsg) {
        self.handle_event(msg);
    }
}

trait PrivateDedicatedWorkerGlobalScopeHelpers {
    fn handle_event(self, msg: ScriptMsg);
    fn dispatch_error_to_worker(self, &ErrorEvent);
}

impl<'a> PrivateDedicatedWorkerGlobalScopeHelpers for &'a DedicatedWorkerGlobalScope {
    fn handle_event(self, msg: ScriptMsg) {
        match msg {
            ScriptMsg::DOMMessage(data) => {
                let scope = WorkerGlobalScopeCast::from_ref(self);
                let target = EventTargetCast::from_ref(self);
                let _ar = JSAutoRequest::new(scope.get_cx());
                let _ac = JSAutoCompartment::new(scope.get_cx(), scope.reflector().get_jsobject().get());
                let mut message = RootedValue::new(scope.get_cx(), UndefinedValue());
                data.read(GlobalRef::Worker(scope), message.handle_mut());
                MessageEvent::dispatch_jsval(target, GlobalRef::Worker(scope), message.handle());
            },
            ScriptMsg::RunnableMsg(runnable) => {
                runnable.handler()
            },
            ScriptMsg::RefcountCleanup(addr) => {
                LiveDOMReferences::cleanup(addr);
            }
            ScriptMsg::FireTimer(TimerSource::FromWorker, timer_id) => {
                let scope = WorkerGlobalScopeCast::from_ref(self);
                scope.handle_fire_timer(timer_id);
            }
            ScriptMsg::CollectReports(reports_chan) => {
                let scope = WorkerGlobalScopeCast::from_ref(self);
                let cx = scope.get_cx();
                let path_seg = format!("url({})", scope.get_url());
                let reports = ScriptTask::get_reports(cx, path_seg);
                reports_chan.send(reports);
            }
            _ => panic!("Unexpected message"),
        }
    }

    fn dispatch_error_to_worker(self, errorevent: &ErrorEvent) {
        let msg = errorevent.Message();
        let file_name = errorevent.Filename();
        let line_num = errorevent.Lineno();
        let col_num = errorevent.Colno();
        let worker = self.worker.borrow().as_ref().unwrap().clone();
        self.parent_sender.send(ScriptMsg::RunnableMsg(
            box WorkerErrorHandler::new(worker, msg, file_name, line_num, col_num))).unwrap();
 }
}

impl<'a> DedicatedWorkerGlobalScopeMethods for &'a DedicatedWorkerGlobalScope {
    // https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
    fn PostMessage(self, cx: *mut JSContext, message: HandleValue) -> ErrorResult {
        let data = try!(StructuredCloneData::write(cx, message));
        let worker = self.worker.borrow().as_ref().unwrap().clone();
        self.parent_sender.send(ScriptMsg::RunnableMsg(
            box WorkerMessageHandler::new(worker, data))).unwrap();
        Ok(())
    }

    event_handler!(message, GetOnmessage, SetOnmessage);
}

impl DedicatedWorkerGlobalScopeDerived for EventTarget {
    fn is_dedicatedworkerglobalscope(&self) -> bool {
        match *self.type_id() {
            EventTargetTypeId::WorkerGlobalScope(WorkerGlobalScopeTypeId::DedicatedGlobalScope) => true,
            _ => false
        }
    }
}
<|endoftext|>


<commit_before>#![warn(clippy::needless_range_loop)]

fn calc_idx(i: usize) -> usize {
    (i + i + 20) % 4
}

fn main() {
    let ns = vec![2, 3, 5, 7];

    for i in 3..10 {
        println!("{}", ns[i]);
    }

    for i in 3..10 {
        println!("{}", ns[i % 4]);
    }

    for i in 3..10 {
        println!("{}", ns[i % ns.len()]);
    }

    for i in 3..10 {
        println!("{}", ns[calc_idx(i)]);
    }

    for i in 3..10 {
        println!("{}", ns[calc_idx(i) % 4]);
    }

    let mut ms = vec![1, 2, 3, 4, 5, 6];
    for i in 0..ms.len() {
        ms[i] *= 2;
    }
    assert_eq!(ms, vec![2, 4, 6, 8, 10, 12]);

    let mut ms = vec![1, 2, 3, 4, 5, 6];
    for i in 0..ms.len() {
        let x = &mut ms[i];
        *x *= 2;
    }
    assert_eq!(ms, vec![2, 4, 6, 8, 10, 12]);

    let g = vec![1, 2, 3, 4, 5, 6];
    let glen = g.len();
    for i in 0..glen {
        let x: u32 = g[i + 1..].iter().sum();
        println!("{}", g[i] + x);
    }
    assert_eq!(g, vec![20, 18, 15, 11, 6, 0]);

    let mut g = vec![1, 2, 3, 4, 5, 6];
    let glen = g.len();
    for i in 0..glen {
        g[i] = g[i + 1..].iter().sum();
    }
    assert_eq!(g, vec![20, 18, 15, 11, 6, 0]);

    let x = 5;
    let mut vec = vec![0; 9];

    for i in x..x + 4 {
        vec[i] += 1;
    }

    let x = 5;
    let mut vec = vec![0; 10];

    for i in x..=x + 4 {
        vec[i] += 1;
    }

    let arr = [1, 2, 3];

    for i in 0..3 {
        println!("{}", arr[i]);
    }

    for i in 0..2 {
        println!("{}", arr[i]);
    }

    for i in 1..3 {
        println!("{}", arr[i]);
    }
}
<commit_msg>Add test for `needless_range_loop` issue<commit_after>#![warn(clippy::needless_range_loop)]

fn calc_idx(i: usize) -> usize {
    (i + i + 20) % 4
}

fn main() {
    let ns = vec![2, 3, 5, 7];

    for i in 3..10 {
        println!("{}", ns[i]);
    }

    for i in 3..10 {
        println!("{}", ns[i % 4]);
    }

    for i in 3..10 {
        println!("{}", ns[i % ns.len()]);
    }

    for i in 3..10 {
        println!("{}", ns[calc_idx(i)]);
    }

    for i in 3..10 {
        println!("{}", ns[calc_idx(i) % 4]);
    }

    let mut ms = vec![1, 2, 3, 4, 5, 6];
    for i in 0..ms.len() {
        ms[i] *= 2;
    }
    assert_eq!(ms, vec![2, 4, 6, 8, 10, 12]);

    let mut ms = vec![1, 2, 3, 4, 5, 6];
    for i in 0..ms.len() {
        let x = &mut ms[i];
        *x *= 2;
    }
    assert_eq!(ms, vec![2, 4, 6, 8, 10, 12]);

    let g = vec![1, 2, 3, 4, 5, 6];
    let glen = g.len();
    for i in 0..glen {
        let x: u32 = g[i + 1..].iter().sum();
        println!("{}", g[i] + x);
    }
    assert_eq!(g, vec![20, 18, 15, 11, 6, 0]);

    let mut g = vec![1, 2, 3, 4, 5, 6];
    let glen = g.len();
    for i in 0..glen {
        g[i] = g[i + 1..].iter().sum();
    }
    assert_eq!(g, vec![20, 18, 15, 11, 6, 0]);

    let x = 5;
    let mut vec = vec![0; 9];

    for i in x..x + 4 {
        vec[i] += 1;
    }

    let x = 5;
    let mut vec = vec![0; 10];

    for i in x..=x + 4 {
        vec[i] += 1;
    }

    let arr = [1, 2, 3];

    for i in 0..3 {
        println!("{}", arr[i]);
    }

    for i in 0..2 {
        println!("{}", arr[i]);
    }

    for i in 1..3 {
        println!("{}", arr[i]);
    }
}

mod issue2277 {
    pub fn example(list: &[[f64; 3]]) {
        let mut x: [f64; 3] = [10.; 3];

        for i in 0..3 {
            x[i] = list.iter().map(|item| item[i]).sum::<f64>();
        }
    }
}
<|endoftext|>


<commit_before>use gluon::base::pos::{self, BytePos, Line, Span};
use gluon::base::source;

use languageserver_types::{Position, Range};

use rpc::ServerError;

pub fn location_to_position(line: &str, loc: &pos::Location) -> Result<Position, ServerError<()>> {
    let mut next_character = 0;
    let found = line.char_indices()
        .enumerate()
        .inspect(|&(character, _)| next_character = character + 1)
        .find(|&(_, (i, _))| i == loc.column.to_usize())
        .map(|(i, _)| i as u64);

    let character = found
        .or_else(|| {
            if line.len() == loc.column.to_usize() {
                Some(next_character as u64)
            } else {
                None
            }
        })
        .ok_or_else(|| ServerError::from(format!("{} is not a valid location", loc)))?;

    Ok(Position {
        line: loc.line.to_usize() as u64,
        character,
    })
}
pub fn span_to_range(
    source: &source::Source,
    span: &Span<pos::Location>,
) -> Result<Range, ServerError<()>> {
    let (start_line, end_line) = source
        .line(Line::from(span.start.line))
        .and_then(|(_, start_line)| {
            source
                .line(Line::from(span.end.line))
                .map(|(_, end_line)| (start_line, end_line))
        })
        .ok_or_else(|| {
            ServerError::from(format!("{}:{} is not a valid span", span.start, span.end))
        })?;
    Ok(Range {
        start: location_to_position(start_line, &span.start)?,
        end: location_to_position(end_line, &span.end)?,
    })
}

pub fn byte_pos_to_location(
    source: &source::Source,
    pos: BytePos,
) -> Result<pos::Location, ServerError<()>> {
    Ok(source
        .location(pos)
        .ok_or_else(|| ServerError::from(&"Unable to translate index to location"))?)
}

pub fn byte_pos_to_position(
    source: &source::Source,
    pos: BytePos,
) -> Result<Position, ServerError<()>> {
    let (line, location) = source
        .line_at_byte(pos)
        .and_then(|(_, line)| source.location(pos).map(|location| (line, location)))
        .ok_or_else(|| ServerError::from(&"Unable to translate index to location"))?;
    location_to_position(line, &location)
}

pub fn byte_span_to_range(
    source: &source::Source,
    span: Span<BytePos>,
) -> Result<Range, ServerError<()>> {
    Ok(Range {
        start: byte_pos_to_position(source, span.start)?,
        end: byte_pos_to_position(source, span.end)?,
    })
}

pub fn character_to_line_offset(line: &str, character: u64) -> Option<BytePos> {
    let mut next_character = 0;
    let found = line.char_indices()
        .enumerate()
        .inspect(|&(i, _)| next_character = i + 1)
        .find(|&(i, (_, _))| i as u64 == character)
        .map(|(_, (byte_offset, _))| byte_offset);

    found
        .or_else(|| {
            // Handle positions after the last character on the line
            if next_character as u64 == character {
                Some(line.len())
            } else {
                None
            }
        })
        .map(BytePos::from)
}

pub fn position_to_byte_pos(
    source: &source::Source,
    position: &Position,
) -> Result<BytePos, ServerError<()>> {
    source
        .line(Line::from(position.line as usize))
        .and_then(|(line_pos, line_str)| {
            character_to_line_offset(line_str, position.character)
                .map(|byte_offset| line_pos + byte_offset)
        })
        .ok_or_else(|| {
            ServerError {
                message: format!(
                    "Position ({}, {}) is out of range",
                    position.line,
                    position.character
                ),
                data: None,
            }
        })
}

pub fn range_to_byte_span(
    source: &source::Source,
    range: &Range,
) -> Result<Span<BytePos>, ServerError<()>> {
    Ok(Span::new(
        position_to_byte_pos(source, &range.start)?,
        position_to_byte_pos(source, &range.end)?,
    ))
}

#[cfg(test)]
mod tests {
    use super::*;

    // The protocol specifies that each `character` in position is a UTF-16 character.
    // This means that `å` and `ä` here counts as 1 while `𐐀` counts as 2.
    const UNICODE: &str = "åä t𐐀b";

    #[test]
    fn unicode_get_byte_pos() {
        let source = source::Source::new(UNICODE);

        let result = position_to_byte_pos(
            &source,
            &Position {
                line: 0,
                character: 3,
            },
        );
        assert_eq!(result, Ok(BytePos::from(5)));
    }

    #[test]
    fn unicode_get_position() {
        let source = source::Source::new(UNICODE);

        let result = byte_pos_to_position(&source, BytePos::from(5));
        assert_eq!(
            result,
            Ok(Position {
                line: 0,
                character: 3,
            },)
        );
    }
}
<commit_msg>fix: Correctly handle positions as utf-16 character offsets<commit_after>use gluon::base::pos::{self, BytePos, Line, Span};
use gluon::base::source;

use languageserver_types::{Position, Range};

use rpc::ServerError;

pub fn location_to_position(line: &str, loc: &pos::Location) -> Result<Position, ServerError<()>> {
    let column = loc.column.to_usize();
    if column <= line.len() && line.is_char_boundary(column) {
        let character = line[..column].encode_utf16().count() as u64;
        Ok(Position {
            line: loc.line.to_usize() as u64,
            character,
        })
    } else {
        Err(ServerError::from(format!(
            "{} is not a valid location",
            loc
        )))
    }
}
pub fn span_to_range(
    source: &source::Source,
    span: &Span<pos::Location>,
) -> Result<Range, ServerError<()>> {
    let (start_line, end_line) = source
        .line(Line::from(span.start.line))
        .and_then(|(_, start_line)| {
            source
                .line(Line::from(span.end.line))
                .map(|(_, end_line)| (start_line, end_line))
        })
        .ok_or_else(|| {
            ServerError::from(format!("{}:{} is not a valid span", span.start, span.end))
        })?;
    Ok(Range {
        start: location_to_position(start_line, &span.start)?,
        end: location_to_position(end_line, &span.end)?,
    })
}

pub fn byte_pos_to_location(
    source: &source::Source,
    pos: BytePos,
) -> Result<pos::Location, ServerError<()>> {
    Ok(source
        .location(pos)
        .ok_or_else(|| ServerError::from(&"Unable to translate index to location"))?)
}

pub fn byte_pos_to_position(
    source: &source::Source,
    pos: BytePos,
) -> Result<Position, ServerError<()>> {
    let (line, location) = source
        .line_at_byte(pos)
        .and_then(|(_, line)| source.location(pos).map(|location| (line, location)))
        .ok_or_else(|| ServerError::from(&"Unable to translate index to location"))?;
    location_to_position(line, &location)
}

pub fn byte_span_to_range(
    source: &source::Source,
    span: Span<BytePos>,
) -> Result<Range, ServerError<()>> {
    Ok(Range {
        start: byte_pos_to_position(source, span.start)?,
        end: byte_pos_to_position(source, span.end)?,
    })
}

pub fn character_to_line_offset(line: &str, character: u64) -> Option<BytePos> {
    let mut character_offset = 0;
    let mut found = None;

    let mut chars = line.chars();
    while let Some(c) = chars.next() {
        if character_offset == character {
            found = Some(line.len() - chars.as_str().len() - c.len_utf8());
            break;
        }
        character_offset += c.len_utf16() as u64;
    }

    found
        .or_else(|| {
            // Handle positions after the last character on the line
            if character_offset == character {
                Some(line.len())
            } else {
                None
            }
        })
        .map(BytePos::from)
}

pub fn position_to_byte_pos(
    source: &source::Source,
    position: &Position,
) -> Result<BytePos, ServerError<()>> {
    source
        .line(Line::from(position.line as usize))
        .and_then(|(line_pos, line_str)| {
            character_to_line_offset(line_str, position.character)
                .map(|byte_offset| line_pos + byte_offset)
        })
        .ok_or_else(|| {
            ServerError {
                message: format!(
                    "Position ({}, {}) is out of range",
                    position.line,
                    position.character
                ),
                data: None,
            }
        })
}

pub fn range_to_byte_span(
    source: &source::Source,
    range: &Range,
) -> Result<Span<BytePos>, ServerError<()>> {
    Ok(Span::new(
        position_to_byte_pos(source, &range.start)?,
        position_to_byte_pos(source, &range.end)?,
    ))
}

#[cfg(test)]
mod tests {
    use super::*;

    // The protocol specifies that each `character` in position is a UTF-16 character.
    // This means that `å` and `ä` here counts as 1 while `𐐀` counts as 2.
    const UNICODE: &str = "åä t𐐀b";

    #[test]
    fn unicode_get_byte_pos() {
        let source = source::Source::new(UNICODE);

        let result = position_to_byte_pos(
            &source,
            &Position {
                line: 0,
                character: 3,
            },
        );
        assert_eq!(result, Ok(BytePos::from(5)));

        let result = position_to_byte_pos(
            &source,
            &Position {
                line: 0,
                character: 6,
            },
        );
        assert_eq!(result, Ok(BytePos::from(10)));
    }

    #[test]
    fn unicode_get_position() {
        let source = source::Source::new(UNICODE);

        let result = byte_pos_to_position(&source, BytePos::from(5));
        assert_eq!(
            result,
            Ok(Position {
                line: 0,
                character: 3,
            },)
        );

        let result = byte_pos_to_position(&source, BytePos::from(10));
        assert_eq!(
            result,
            Ok(Position {
                line: 0,
                character: 6,
            },)
        );
    }
}
<|endoftext|>


<commit_before>use super::super::{AuditLogEntryId, UserId};
use serde::de::{self, Deserialize, Deserializer, MapAccess, Visitor};
use std::fmt;
use std::collections::HashMap;

trait FromNum {
    fn from_num(num: i32) -> Result<Self, String> where Self: Sized;
}

/// Determines to what entity an action was used on.
#[derive(Debug)]
pub enum Target {
    Guild,
    Channel,
    User,
    Role,
    Invite,
    Webhook,
    Emoji,
}

impl FromNum for Target {
    fn from_num(num: i32) -> Result<Self, String> {
        use self::Target::*;

        Ok(match num {
            10 => Guild,
            20 => Channel,
            30 => User,
            40 => Role,
            50 => Invite,
            60 => Webhook,
            70 => Emoji,
            _ => return Err(format!("Unexpected target number: {}", num)),
        })
    }
}

/// Determines the action that was done on a target.
#[derive(Debug)]
pub enum Action {
    GuildUpdate,
    Channel(ActionChannel),
    ChannelOverwrite(ActionChannelOverwrite),
    Member(ActionMember),
    Role(ActionRole),
    Invite(ActionInvite),
    Webhook(ActionWebhook),
    Emoji(ActionEmoji),
}

impl FromNum for Action {
    fn from_num(num: i32) -> Result<Self, String> {
        Ok(match num {
            1 => Action::GuildUpdate,
            10...13 => Action::Channel(ActionChannel::from_num(num)?),
            13...15 => Action::ChannelOverwrite(ActionChannelOverwrite::from_num(num)?),
            20...25 => Action::Member(ActionMember::from_num(num)?),
            30...32 => Action::Role(ActionRole::from_num(num)?),
            40...42 => Action::Invite(ActionInvite::from_num(num)?),
            50...52 => Action::Webhook(ActionWebhook::from_num(num)?),
            60...62 => Action::Emoji(ActionEmoji::from_num(num)?),
            _ => return Err(format!("Unexpected action number: {}", num)),
        })
    }
}

#[derive(Debug)]
pub enum ActionChannel {
    Create,
    Update,
    Delete,
}

impl FromNum for ActionChannel {
    fn from_num(num: i32) -> Result<Self, String> {
        use self::ActionChannel::*;

        Ok(match num {
            10 => Create,
            11 => Update,
            12 => Delete,
            _ => return Err(format!("Unexpected action channel num: {}", num)),
        })
    }
}

#[derive(Debug)]
pub enum ActionChannelOverwrite {
    Create,
    Update,
    Delete,
}

impl FromNum for ActionChannelOverwrite {
    fn from_num(num: i32) -> Result<Self, String> {
        use self::ActionChannelOverwrite::*;

        Ok(match num {
            13 => Create,
            14 => Update,
            15 => Delete,
            _ => return Err(format!("Unexpected action channel overwrite num: {}", num)),
        })
    }
}

#[derive(Debug)]
pub enum ActionMember {
    Kick,
    Prune,
    BanAdd,
    BanRemove,
    Update,
    RoleUpdate,
}

impl FromNum for ActionMember {
    fn from_num(num: i32) -> Result<Self, String> {
        use self::ActionMember::*;

        Ok(match num {
            20 => Kick,
            21 => Prune,
            22 => BanAdd,
            23 => BanRemove,
            24 => Update,
            25 => RoleUpdate,
            _ => return Err(format!("Unexpected action member num: {}", num)),
        })
    }
}

#[derive(Debug)]
pub enum ActionRole {
    Create,
    Update,
    Delete,
}

impl FromNum for ActionRole {
    fn from_num(num: i32) -> Result<Self, String> {
        use self::ActionRole::*;

        Ok(match num {
            30 => Create,
            31 => Update,
            32 => Delete,
            _ => return Err(format!("Unexpected action role num: {}", num)),
        })
    }
}

#[derive(Debug)]
pub enum ActionInvite {
    Create,
    Update,
    Delete,
}

impl FromNum for ActionInvite {
    fn from_num(num: i32) -> Result<Self, String> {
        use self::ActionInvite::*;

        Ok(match num {
            40 => Create,
            41 => Update,
            42 => Delete,
            _ => return Err(format!("Unexpected action invite num: {}", num)),
        })
    }
}

#[derive(Debug)]
pub enum ActionWebhook {
    Create,
    Update,
    Delete,
}

impl FromNum for ActionWebhook {
    fn from_num(num: i32) -> Result<Self, String> {
        use self::ActionWebhook::*;

        Ok(match num {
            50 => Create,
            51 => Update,
            52 => Delete,
            _ => return Err(format!("Unexpected action webhook num: {}", num)),
        })
    }
}

#[derive(Debug)]
pub enum ActionEmoji {
    Create,
    Delete,
    Update,
}

impl FromNum for ActionEmoji {
    fn from_num(num: i32) -> Result<Self, String> {
        use self::ActionEmoji::*;

        Ok(match num {
            60 => Create,
            61 => Update,
            62 => Delete,
            _ => return Err(format!("Unexpected action emoji num: {}", num)),
        })
    }
}

#[derive(Debug, Deserialize)]
pub struct Change {
    #[serde(rename = "key")]
    pub name: String,
    #[serde(rename = "old_value")]
    pub old: String,
    #[serde(rename = "new_value")]
    pub new: String,
}

#[derive(Debug)]
pub struct AuditLogs {
    pub entries: HashMap<AuditLogEntryId, AuditLogEntry>,
}

#[derive(Debug, Deserialize)]
pub struct AuditLogEntry {
    /// Determines to what entity an [`action`] was used on.
    ///
    /// [`action`]: #structfield.action
    #[serde(deserialize_with = "deserialize_target", rename = "target_type")]
    pub target: Target,
    /// Determines what action was done on a [`target`]
    ///
    /// [`target`]: #structfield.target
    #[serde(deserialize_with = "deserialize_action", rename = "action_type")]
    pub action: Action,
    /// What was the reasoning by doing an action on a target? If there was one.
    pub reason: Option<String>,
    /// The user that did this action on a target.
    pub user_id: UserId,
    /// What changes were made.
    pub changes: Vec<Change>,
    /// The id of this entry.
    pub id: AuditLogEntryId,
}

fn deserialize_target<'de, D: Deserializer<'de>>(de: D) -> Result<Target, D::Error> {
    struct TargetVisitor;

    impl<'de> Visitor<'de> for TargetVisitor {
        type Value = Target;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("an integer between 0 to 70")
        }

        fn visit_i32<E: de::Error>(self, value: i32) -> Result<Target, E> {
            Target::from_num(value).map_err(E::custom)
        }
    }

    de.deserialize_i32(TargetVisitor)
}

fn deserialize_action<'de, D: Deserializer<'de>>(de: D) -> Result<Action, D::Error> {
    struct ActionVisitor;

    impl<'de> Visitor<'de> for ActionVisitor {
        type Value = Action;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("an integer between 1 to 62")
        }

        fn visit_i32<E: de::Error>(self, value: i32) -> Result<Action, E> {
            Action::from_num(value).map_err(E::custom)
        }
    }

    de.deserialize_i32(ActionVisitor)
}

impl<'de> Deserialize<'de> for AuditLogs {
    fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
        #[derive(Deserialize)]
        #[serde(field_identifier)]
        enum Field {
            #[serde(rename = "audit_log_entries")]
            Entries,
        }

        struct EntriesVisitor;

        impl<'de> Visitor<'de> for EntriesVisitor {
            type Value = AuditLogs;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("audit log entries")
            }

            fn visit_map<V: MapAccess<'de>>(self, mut map: V) -> Result<AuditLogs, V::Error> {
                let audit_log_entries = loop {
                    if let Some(Field::Entries) = map.next_key()? {
                        break map.next_value::<Vec<AuditLogEntry>>()?;
                    }
                };

                Ok(AuditLogs {
                    entries: audit_log_entries
                        .into_iter()
                        .map(|entry| (entry.id, entry))
                        .collect(),
                })
            }
        }

        const FIELD: &'static [&'static str] = &["audit_log_entries"];
        de.deserialize_struct("AuditLogs", FIELD, EntriesVisitor)
    }
}
<commit_msg>Change to c-like enums and transmute<commit_after>use super::super::{AuditLogEntryId, UserId};
use serde::de::{self, Deserialize, Deserializer, MapAccess, Visitor};
use std::fmt;
use std::collections::HashMap;
use std::mem::transmute;

/// Determines to what entity an action was used on.
#[derive(Debug)]
#[repr(i32)]
pub enum Target {
    Guild = 10,
    Channel = 20,
    User = 30,
    Role = 40,
    Invite = 50,
    Webhook = 60,
    Emoji = 70,
}

/// Determines the action that was done on a target.
#[derive(Debug)]
pub enum Action {
    GuildUpdate,
    Channel(ActionChannel),
    ChannelOverwrite(ActionChannelOverwrite),
    Member(ActionMember),
    Role(ActionRole),
    Invite(ActionInvite),
    Webhook(ActionWebhook),
    Emoji(ActionEmoji),
}

#[derive(Debug)]
#[repr(i32)]
pub enum ActionChannel {
    Create = 10,
    Update = 11,
    Delete = 12,
}

#[derive(Debug)]
#[repr(i32)]
pub enum ActionChannelOverwrite {
    Create = 13,
    Update = 14,
    Delete = 15,
}

#[derive(Debug)]
#[repr(i32)]
pub enum ActionMember {
    Kick = 20,
    Prune = 21,
    BanAdd = 22,
    BanRemove = 23,
    Update = 24,
    RoleUpdate = 25,
}

#[derive(Debug)]
#[repr(i32)]
pub enum ActionRole {
    Create = 30,
    Update = 31,
    Delete = 32,
}

#[derive(Debug)]
#[repr(i32)]
pub enum ActionInvite {
    Create = 40,
    Update = 41,
    Delete = 42,
}

#[derive(Debug)]
#[repr(i32)]
pub enum ActionWebhook {
    Create = 50,
    Update = 51,
    Delete = 52,
}

#[derive(Debug)]
#[repr(i32)]
pub enum ActionEmoji {
    Create = 60,
    Delete = 61,
    Update = 62,
}

#[derive(Debug, Deserialize)]
pub struct Change {
    #[serde(rename = "key")]
    pub name: String,
    #[serde(rename = "old_value")]
    pub old: String,
    #[serde(rename = "new_value")]
    pub new: String,
}

#[derive(Debug)]
pub struct AuditLogs {
    pub entries: HashMap<AuditLogEntryId, AuditLogEntry>,
}

#[derive(Debug, Deserialize)]
pub struct AuditLogEntry {
    /// Determines to what entity an [`action`] was used on.
    ///
    /// [`action`]: #structfield.action
    #[serde(deserialize_with = "deserialize_target", rename = "target_type")]
    pub target: Target,
    /// Determines what action was done on a [`target`]
    ///
    /// [`target`]: #structfield.target
    #[serde(deserialize_with = "deserialize_action", rename = "action_type")]
    pub action: Action,
    /// What was the reasoning by doing an action on a target? If there was one.
    pub reason: Option<String>,
    /// The user that did this action on a target.
    pub user_id: UserId,
    /// What changes were made.
    pub changes: Vec<Change>,
    /// The id of this entry.
    pub id: AuditLogEntryId,
}

fn deserialize_target<'de, D: Deserializer<'de>>(de: D) -> Result<Target, D::Error> {
    struct TargetVisitor;

    impl<'de> Visitor<'de> for TargetVisitor {
        type Value = Target;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("an integer between 0 to 70")
        }

        fn visit_i32<E: de::Error>(self, value: i32) -> Result<Target, E> {
            Ok(match value {
                10...70 => unsafe { transmute(value) },
                _ => return Err(E::custom(format!("unexpected target number: {}", value))),
            })
        }
    }

    de.deserialize_i32(TargetVisitor)
}

fn deserialize_action<'de, D: Deserializer<'de>>(de: D) -> Result<Action, D::Error> {
    struct ActionVisitor;

    impl<'de> Visitor<'de> for ActionVisitor {
        type Value = Action;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("an integer between 1 to 62")
        }

        fn visit_i32<E: de::Error>(self, value: i32) -> Result<Action, E> {
            Ok(match value {
                1 => Action::GuildUpdate,
                10...13 => Action::Channel(unsafe { transmute(value) }),
                13...15 => Action::ChannelOverwrite(unsafe { transmute(value) }),
                20...25 => Action::Member(unsafe { transmute(value) }),
                30...32 => Action::Role(unsafe { transmute(value) }),
                40...42 => Action::Invite(unsafe { transmute(value) }),
                50...52 => Action::Webhook(unsafe { transmute(value) }),
                60...62 => Action::Emoji(unsafe { transmute(value) }),
                _ => return Err(E::custom(format!("Unexpected action number: {}", value))),
            })
        }
    }

    de.deserialize_i32(ActionVisitor)
}

impl<'de> Deserialize<'de> for AuditLogs {
    fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
        #[derive(Deserialize)]
        #[serde(field_identifier)]
        enum Field {
            #[serde(rename = "audit_log_entries")]
            Entries,
        }

        struct EntriesVisitor;

        impl<'de> Visitor<'de> for EntriesVisitor {
            type Value = AuditLogs;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("audit log entries")
            }

            fn visit_map<V: MapAccess<'de>>(self, mut map: V) -> Result<AuditLogs, V::Error> {
                let audit_log_entries = loop {
                    if let Some(Field::Entries) = map.next_key()? {
                        break map.next_value::<Vec<AuditLogEntry>>()?;
                    }
                };

                Ok(AuditLogs {
                    entries: audit_log_entries
                        .into_iter()
                        .map(|entry| (entry.id, entry))
                        .collect(),
                })
            }
        }

        const FIELD: &'static [&'static str] = &["audit_log_entries"];
        de.deserialize_struct("AuditLogs", FIELD, EntriesVisitor)
    }
}
<|endoftext|>