v8_api/
lib.rs

1extern crate clang;
2#[macro_use]
3extern crate log;
4
5use std::env;
6use std::fmt;
7use std::path;
8
9/// A description of the V8 API.
10#[derive(Debug)]
11pub struct Api {
12    /// The classes that the API consists of.
13    pub classes: Vec<Class>,
14}
15
16/// A C++ class,
17#[derive(Debug)]
18pub struct Class {
19    /// The simple name of the class (without the `v8::` prefix).
20    pub name: String,
21    /// The methods of this class, in declaration order.
22    pub methods: Vec<Method>,
23}
24
25/// A C++ method
26#[derive(Debug)]
27pub struct Method {
28    /// Whether the method is static.
29    pub is_static: bool,
30    /// The name of the method.
31    pub name: String,
32    /// A mangled version of the method that makes it unique among all
33    /// of the methods of its class.
34    pub mangled_name: String,
35    /// The arguments that the method takes, in declaration order.
36    pub args: Vec<Arg>,
37    /// The return type of the method.
38    pub ret_type: RetType,
39}
40
41/// The return type of a method.
42#[derive(Debug)]
43pub enum RetType {
44    /// The type is directly returned.  For primitives `T`, this means
45    /// just `T` (e.g. `int`).  For references to `T`, this means
46    /// `Local<T>` (e.g. `Local<String>`).  For pointers to `T`, this
47    /// means a non-null pointer `T *`.
48    Direct(Type),
49    /// The type might be absent.  For primitives `T`, this means
50    /// `Maybe<T>` (e.g. `Maybe<int>`).  For references to `T`, this
51    /// means `MaybeLocal<T>`.  For pointers to `T`, this means a
52    /// nullable pointer `T *`.
53    Maybe(Type),
54}
55
56/// A method argument.
57#[derive(Debug)]
58pub struct Arg {
59    /// The argument name.
60    pub name: String,
61    /// The type of the argument.
62    pub arg_type: Type,
63}
64
65/// The types used in V8.
66#[derive(Debug, Eq, PartialEq)]
67pub enum Type {
68    /// The `void` type.
69    Void,
70    /// The `bool` type.
71    Bool,
72
73    /// The `unsigned char` type.
74    UChar,
75    /// The `const unsigned char` type.
76    ConstUChar,
77    /// The `char` type.
78    Char,
79    /// The `const char` type.
80    ConstChar,
81    /// The `unsigned int` type.
82    UInt,
83    /// The `int` type.
84    Int,
85    /// The `unsigned long` type.
86    ULong,
87    /// The `long` type.
88    Long,
89
90    /// The `uint8_t` type.
91    U8,
92    /// The `int8_t` type.
93    I8,
94    /// The `uint16_t` type.
95    U16,
96    /// The `int16_t` type.
97    I16,
98    /// The `uint32_t` type.
99    U32,
100    /// The `int32_t` type.
101    I32,
102    /// The `uint64_t` type.
103    U64,
104    /// The `int64_t` type.
105    I64,
106    /// The `double` type.
107    F64,
108
109    /// The `size_t` type.
110    USize,
111
112    /// A class with the specified name, without the `v8::` prefix.
113    Class(String),
114    /// An enum with the specified name, without the `v8::` prefix.
115    Enum(String),
116    /// A callback function pointer name, without the `v8::` prefix.
117    Callback(String),
118    /// An argument to a callback
119    CallbackLValue(String),
120
121    /// A reference to the specified type, meaning a `Local<T>` or
122    /// `MaybeLocal<T>`.
123    Ref(Box<Type>),
124    /// A pointer to the specified type, i.e. `T *`.
125    Ptr(Box<Type>),
126    /// An array of the specified type, i.e. `T[]`.
127    Arr(Box<Type>),
128}
129
130/// A method mangle rule.
131struct MethodMangle {
132    /// The exact name of the method to mangle.
133    name: &'static str,
134    /// A unique part of the method signature.
135    signature: &'static str,
136    /// The mangled name of the method.
137    mangle: &'static str,
138}
139
140/// Classes that we should not return because they are special.
141#[cfg_attr(rustfmt, rustfmt_skip)]
142const SPECIAL_CLASSES: &'static [&'static str] = &[
143    // v8.h
144    // Allocation stuff
145    "Isolate", // Because part of RustContext (so chicken and egg methods)
146    "HandleScope", // Because stack local
147    "EscapableHandleScope", // Because stack local
148    "SealHandleScope", // Because stack local
149    "TryCatch", // Because stack local
150
151    // Annoying classes
152    "ScriptOrigin", // Because it's sent around by-value
153    "PersistentHandleVisitor", // Only used on Isolate?
154    "EmbedderHeapTracer", // Can't be heap allocated?
155    "ValueSerializer", // Weird API and only on modern V8's
156    "ValueDeserializer", // Weird API and only on modern V8's
157    "ExtensionConfiguration", // Weird API
158    "Module", // Too experimental
159    "SnapshotCreator", // Snapshots not supported
160
161    // v8-platform.h
162    // These are all pre-requisites for creating an Isolate so
163    // hand-roll them
164    "Platform",
165    "Task",
166    "IdleTask",
167];
168
169/// Methods that we should not return because they are special.
170#[cfg_attr(rustfmt, rustfmt_skip)]
171const SPECIAL_METHODS: &'static [(&'static str, &'static str)] = &[
172    ("Script", "Compile"), // Because ScriptOrigin param
173    ("Message", "GetScriptOrigin"), // Because ScriptOrigin
174    ("String", "WriteUtf8"), // Because annoying-to-map signature
175    ("Object", "SetAlignedPointerInInternalFields"), // Because annoying-to-map signature
176    ("Object", "CallAsFunction"), // Because annoying-to-map signature
177    ("Object", "CallAsConstructor"), // Because annoying-to-map signature
178    ("Object", "NewInstance"), // Because annoying-to-map signature
179    ("Object", "Call"), // Because annoying-to-map signature
180    ("Function", "New"), // Because annoying-to-map signature
181    ("Function", "GetScriptOrigin"), // Because ScriptOrigin
182    ("Function", "NewInstance"), // Because annoying-to-map signature
183    ("Function", "Call"), // Because annoying-to-map signature
184    ("Template", "SetNativeDataProperty"), // Because annoying-to-map signature
185    ("Template", "SetLazyDataProperty"), // Because annoying-to-map signature
186    ("FunctionTemplate", "New"), // Because annoying-to-map signature
187    ("FunctionTemplate", "NewWithCache"), // Because annoying-to-map signature
188    ("ObjectTemplate", "SetAccessor"), // Because annoying-to-map signature
189    ("ObjectTemplate", "SetNamedPropertyHandler"), // Because annoying-to-map signature
190    ("ObjectTemplate", "SetIndexedPropertyHandler"), // Because annoying-to-map signature
191    ("ObjectTemplate", "SetCallAsFunctionHandler"), // Because annoying-to-map signature
192    ("ObjectTemplate", "SetAccessCheckCallback"), // Because annoying-to-map signature
193    ("ObjectTemplate", "SetAccessCheckCallbackAndHandler"), // Because annoying-to-map signature
194    ("Value", "IsFloat32x4"), // Too experimental
195    ("WasmCompiledModule", "Serialize"), // Breaks on ARM/Mac
196    ("WasmCompiledModule", "Deserialize"), // Breaks on ARM/Mac
197    ("ConvertableToTraceFormat", "AppendAsTraceFormat"), // Breaks on ARM
198    ("V8", "CreateSnapshotDataBlob"), // Because annoying-to-map signature
199    ("V8", "WarmUpSnapshotDataBlob"), // Because annoying-to-map signature
200    ("V8", "Initialize"), // V8::Initialize takes no context
201    ("V8", "Dispose"), // V8::Dispose takes no context
202    ("V8", "InitializePlatform"), // V8::InitializePlatform takes no context
203    ("V8", "ShutdownPlatform"), // V8::ShutdownPlatform takes no context
204];
205
206/// Default mangle rules.
207#[cfg_attr(rustfmt, rustfmt_skip)]
208const METHOD_MANGLES: &'static [MethodMangle] = &[
209    MethodMangle { name: "Set", signature: "index", mangle: "Set_Index"},
210    MethodMangle { name: "Set", signature: "key", mangle: "Set_Key"},
211    MethodMangle { name: "CreateDataProperty", signature: "index", mangle: "CreateDataProperty_Index"},
212    MethodMangle { name: "CreateDataProperty", signature: "key", mangle: "CreateDataProperty_Key"},
213    MethodMangle { name: "Get", signature: "index", mangle: "Get_Index"},
214    MethodMangle { name: "Get", signature: "key", mangle: "Get_Key"},
215    MethodMangle { name: "Has", signature: "index", mangle: "Has_Index"},
216    MethodMangle { name: "Has", signature: "key", mangle: "Has_Key"},
217    MethodMangle { name: "Delete", signature: "index", mangle: "Delete_Index"},
218    MethodMangle { name: "Delete", signature: "key", mangle: "Delete_Key"},
219    MethodMangle { name: "HasOwnProperty", signature: "index", mangle: "HasOwnProperty_Index"},
220    MethodMangle { name: "HasOwnProperty", signature: "key", mangle: "HasOwnProperty_Key"},
221    MethodMangle { name: "GetPropertyNames", signature: "mode", mangle: "GetPropertyNames_Filter"},
222    MethodMangle { name: "GetOwnPropertyNames", signature: "filter", mangle: "GetOwnPropertyNames_Filter"},
223    MethodMangle { name: "InitializeExternalStartupData", signature: "natives_blob", mangle: "InitializeExternalStartupData_Blobs"},
224    MethodMangle { name: "InitializeExternalStartupData", signature: "directory_path", mangle: "InitializeExternalStartupData_Directory"},
225    MethodMangle { name: "New", signature: "shared_array_buffer", mangle: "New_Shared"},
226    MethodMangle { name: "New", signature: "array_buffer", mangle: "New_Owned"},
227    MethodMangle { name: "New", signature: "mode", mangle: "New_Mode"},
228    MethodMangle { name: "Set", signature: "char", mangle: "Set_Raw"},
229    MethodMangle { name: "SetNativeDataProperty", signature: "v8::Name", mangle: "SetNativeDataProperty_Name"},
230    MethodMangle { name: "SetAccessor", signature: "v8::Name", mangle: "SetAccessor_Name"},
231    MethodMangle { name: "SetHandler", signature: "v8::Name", mangle: "SetHandler_Name"},
232];
233
234/// Reads the V8 API from the given file path pointing to a `v8.h`
235/// file (or a file that includes `v8.h`), using the specified extra
236/// includes if necessary.
237///
238/// # Panics
239///
240/// Since this library is supposed to be used in a build script,
241/// panics if anything goes wrong whatsoever.
242pub fn read<P1, P2>(file_path: P1, extra_includes: &[P2]) -> Api
243where
244    P1: AsRef<path::Path>,
245    P2: AsRef<path::Path>,
246{
247    let clang = clang::Clang::new().unwrap();
248    let index = clang::Index::new(&clang, false, true);
249
250    let mut args = vec![
251        "-x".to_owned(),
252        "c++".to_owned(),
253        "--std=c++11".to_owned(),
254        "-DV8_DEPRECATION_WARNINGS".to_owned(),
255        "-DV8_IMMINENT_DEPRECATION_WARNINGS".to_owned(),
256    ];
257
258    if cfg!(all(windows, target_env = "msvc")) {
259        args.push("-fms-compatibility-version=19".to_owned());
260    }
261
262    if let Ok(target) = env::var("TARGET") {
263        args.push("-target".to_owned());
264        args.push(target.to_owned());
265    }
266
267    for include in extra_includes {
268        println!("-I{:?}", include.as_ref());
269        if let Some(include_str) = include.as_ref().to_str() {
270            args.push(format!("-I{}", include_str));
271        }
272    }
273
274    let translation_unit = index
275        .parser(file_path.as_ref())
276        .arguments(&args)
277        .parse()
278        .unwrap();
279
280    build_api(&translation_unit.get_entity())
281}
282
283fn build_api(entity: &clang::Entity) -> Api {
284    let namespaces = entity
285        .get_children()
286        .into_iter()
287        .filter(|e| e.get_name().map(|n| n == "v8").unwrap_or(false));
288    let classes = namespaces
289        .flat_map(|n| build_classes(&n).into_iter())
290        .collect();
291    Api { classes: classes }
292}
293
294fn build_classes(entity: &clang::Entity) -> Vec<Class> {
295    entity
296        .get_children()
297        .into_iter()
298        // Is a class
299        .filter(|e| e.get_kind() == clang::EntityKind::ClassDecl)
300        // Is not just a declaration
301        .filter(|e| !e.get_children().is_empty())
302        // Is not nameless or special
303        .filter(|e| {
304            e.get_name()
305                .map(|ref n| !SPECIAL_CLASSES.contains(&n.as_str()))
306                .unwrap_or(false)
307        })
308        .map(|e| build_class(&e))
309        .collect::<Vec<_>>()
310}
311
312fn build_class(entity: &clang::Entity) -> Class {
313    let name = entity.get_name().unwrap();
314    Class {
315        methods: entity
316            .get_children()
317            .into_iter()
318            // Is a method
319            .filter(|e| e.get_kind() == clang::EntityKind::Method)
320            // Is not deprecated
321            .filter(|e| e.get_availability() == clang::Availability::Available)
322            // Is public
323            .filter(|e| e.get_accessibility() == Some(clang::Accessibility::Public))
324            // Is not an operator or special
325            .filter(|e| {
326                e.get_name()
327                    .map(|ref n| {
328                        !n.starts_with("operator")
329                            && !SPECIAL_METHODS.iter().any(|m| m.0 == name && m.1 == n)
330                    })
331                    .unwrap_or(false)
332            })
333            .flat_map(|e| {
334                build_method(&e).map_err(|err| {
335                    warn!(
336                        "Could not translate method {}",
337                        e.get_display_name()
338                            .unwrap_or_else(|| "(unnamed)".to_owned())
339                    );
340                    err
341                })
342            })
343            .collect(),
344        name: name,
345    }
346}
347
348fn build_method(entity: &clang::Entity) -> Result<Method, ()> {
349    let display_name = r#try!(entity.get_display_name().ok_or(()));
350    let name = r#try!(entity.get_name().ok_or(()));
351    let args = r#try!(entity.get_arguments().ok_or(()));
352    let args: Vec<Arg> = r#try!(args.iter().map(|e| build_arg(&e)).collect());
353
354    let method_type = r#try!(entity.get_type().ok_or(()));
355    let method_type_display_name = method_type.get_display_name();
356    let ret_type = r#try!(method_type.get_result_type().ok_or(()));
357    let ret_type = r#try!(build_ret_type(&ret_type));
358
359    let mangled_name = METHOD_MANGLES
360        .iter()
361        .find(|m| {
362            m.name == name
363                && (args.iter().any(|a| a.name == m.signature)
364                    || display_name.contains(m.signature)
365                    || method_type_display_name.contains(m.signature))
366        })
367        .map(|m| m.mangle.to_owned());
368
369    Ok(Method {
370        is_static: entity.is_static_method(),
371        mangled_name: mangled_name.unwrap_or_else(|| name.clone()),
372        name: name,
373        args: args,
374        ret_type: ret_type,
375    })
376}
377
378fn build_arg(entity: &clang::Entity) -> Result<Arg, ()> {
379    Ok(Arg {
380        name: r#try!(entity.get_name().ok_or(())),
381        arg_type: r#try!(build_type(&entity.get_type().unwrap())),
382    })
383}
384
385fn build_ret_type(typ: &clang::Type) -> Result<RetType, ()> {
386    if typ.get_kind() == clang::TypeKind::Unexposed {
387        let name = typ.get_display_name();
388
389        if name.starts_with("MaybeLocal<") {
390            let ref_type = r#try!(build_type(&get_first_tpl_arg(typ)));
391            Ok(RetType::Maybe(Type::Ref(Box::new(ref_type))))
392        } else if name.starts_with("Maybe<") {
393            let ref_type = r#try!(build_type(&get_first_tpl_arg(typ)));
394            Ok(RetType::Maybe(ref_type))
395        } else {
396            Ok(RetType::Direct(r#try!(build_type(typ))))
397        }
398    } else {
399        Ok(RetType::Direct(r#try!(build_type(typ))))
400    }
401}
402
403fn build_type(typ: &clang::Type) -> Result<Type, ()> {
404    match typ.get_kind() {
405        clang::TypeKind::Void => Ok(Type::Void),
406        clang::TypeKind::Bool => Ok(Type::Bool),
407        clang::TypeKind::CharS => {
408            if typ.is_const_qualified() {
409                Ok(Type::ConstChar)
410            } else {
411                Ok(Type::Char)
412            }
413        }
414        clang::TypeKind::CharU => {
415            if typ.is_const_qualified() {
416                Ok(Type::ConstUChar)
417            } else {
418                Ok(Type::UChar)
419            }
420        }
421        clang::TypeKind::UInt => Ok(Type::UInt),
422        clang::TypeKind::Int => Ok(Type::Int),
423        clang::TypeKind::ULong => Ok(Type::ULong),
424        clang::TypeKind::Long => Ok(Type::Long),
425        clang::TypeKind::Double => Ok(Type::F64),
426        clang::TypeKind::LongLong => Ok(Type::I64),
427        clang::TypeKind::ULongLong => Ok(Type::U64),
428        clang::TypeKind::Pointer => {
429            let inner = r#try!(typ.get_pointee_type().ok_or(()));
430            let inner = r#try!(build_type(&inner));
431            Ok(Type::Ptr(Box::new(inner)))
432        }
433        clang::TypeKind::IncompleteArray => {
434            let inner = r#try!(typ.get_element_type().ok_or(()));
435            let inner = r#try!(build_type(&inner));
436            Ok(Type::Arr(Box::new(inner)))
437        }
438        clang::TypeKind::Record => {
439            // TODO: is this right?
440            let name = typ.get_display_name().replace("v8::", "");
441            if name.contains("::") {
442                warn!("No support for nested type {:?}", name);
443                Err(())
444            } else {
445                Ok(Type::Class(name))
446            }
447        }
448        clang::TypeKind::Enum => {
449            // TODO: is this right?
450            let name = typ.get_display_name().replace("v8::", "");
451            if name.contains("::") {
452                warn!("No support for nested type {:?}", name);
453                Err(())
454            } else {
455                Ok(Type::Enum(name))
456            }
457        }
458        clang::TypeKind::Typedef => {
459            // TODO: is this right?
460            match typ.get_display_name().as_str() {
461                "uint8_t" | "const uint8_t" => Ok(Type::U8),
462                "int8_t" | "const int8_t" => Ok(Type::I8),
463                "uint16_t" | "const uint16_t" => Ok(Type::U16),
464                "int16_t" | "const int16_t" => Ok(Type::I16),
465                "uint32_t" | "const uint32_t" => Ok(Type::U32),
466                "int32_t" | "const int32_t" => Ok(Type::I32),
467                "uint64_t" | "const uint64_t" => Ok(Type::U64),
468                "int64_t" | "const int64_t" => Ok(Type::I64),
469                "double" | "const double" => Ok(Type::F64),
470                "size_t" | "const size_t" => Ok(Type::USize),
471                s if s.ends_with("Callback") => Ok(Type::Callback(s.to_owned())),
472                s => {
473                    warn!("Unmapped type {:?} (a typedef)", s);
474                    Err(())
475                }
476            }
477        }
478        clang::TypeKind::Unexposed | clang::TypeKind::Elaborated => {
479            if typ.get_display_name().starts_with("Local<") {
480                let ref_type = r#try!(build_type(&get_first_tpl_arg(typ)));
481                Ok(Type::Ref(Box::new(ref_type)))
482            } else {
483                match typ.get_display_name().as_str() {
484                    // For some reason these fail to map
485                    "v8::Isolate" => Ok(Type::Class("Isolate".to_owned())),
486                    "v8::ObjectTemplate" => Ok(Type::Class("ObjectTemplate".to_owned())),
487                    "v8::Value" => Ok(Type::Class("Value".to_owned())),
488                    "v8::Local<v8::String>" => {
489                        Ok(Type::Ref(Box::new(Type::Class("String".to_owned()))))
490                    }
491                    "v8::Local<v8::FunctionTemplate>" => Ok(Type::Ref(Box::new(Type::Class(
492                        "FunctionTemplate".to_owned(),
493                    )))),
494                    n => {
495                        warn!(
496                            "Unmapped type {:?} of kind {:?} (in unexposed exception table)",
497                            n,
498                            typ.get_kind()
499                        );
500                        Err(())
501                    }
502                }
503            }
504        }
505        clang::TypeKind::LValueReference => match typ.get_display_name().as_str() {
506            "const v8::NamedPropertyHandlerConfiguration &" => Ok(Type::CallbackLValue(
507                "NamedPropertyHandlerConfiguration".to_owned(),
508            )),
509            "const v8::IndexedPropertyHandlerConfiguration &" => Ok(Type::CallbackLValue(
510                "IndexedPropertyHandlerConfiguration".to_owned(),
511            )),
512            n => {
513                warn!(
514                    "Unmapped type {:?} of kind {:?} (in lvalue reference exception table)",
515                    n,
516                    typ.get_kind()
517                );
518                Err(())
519            }
520        },
521        _ => {
522            warn!(
523                "Unmapped type {:?} of kind {:?} (in kind dispatch table)",
524                typ.get_display_name(),
525                typ.get_kind()
526            );
527            Err(())
528        }
529    }
530}
531
532fn get_first_tpl_arg<'a>(typ: &clang::Type<'a>) -> clang::Type<'a> {
533    let tpl_args = typ.get_template_argument_types().unwrap();
534    tpl_args[0].unwrap()
535}
536
537impl fmt::Display for Api {
538    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
539        for class in self.classes.iter() {
540            r#try!(writeln!(f, "{}", class));
541        }
542        Ok(())
543    }
544}
545
546impl fmt::Display for Class {
547    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
548        r#try!(writeln!(f, "class {}", self.name));
549        for method in self.methods.iter() {
550            r#try!(writeln!(f, "  {}", method));
551        }
552        Ok(())
553    }
554}
555
556impl fmt::Display for Method {
557    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
558        if self.is_static {
559            r#try!(write!(f, "static "));
560        }
561        r#try!(write!(f, "{}(", self.name));
562
563        let mut needs_sep = false;
564        for arg in self.args.iter() {
565            if needs_sep {
566                r#try!(write!(f, ", "));
567            }
568            needs_sep = true;
569            r#try!(write!(f, "{}", arg));
570        }
571        r#try!(write!(f, ") -> {}", self.ret_type));
572
573        if self.mangled_name != self.name {
574            r#try!(write!(f, " {{{}}}", self.mangled_name));
575        }
576
577        Ok(())
578    }
579}
580
581impl fmt::Display for Arg {
582    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
583        write!(f, "{type} {name}", name=self.name, type=self.arg_type)
584    }
585}
586
587impl fmt::Display for RetType {
588    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
589        match *self {
590            RetType::Direct(ref t) => write!(f, "{}", t),
591            RetType::Maybe(ref t) => write!(f, "maybe {}", t),
592        }
593    }
594}
595
596impl fmt::Display for Type {
597    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
598        match *self {
599            Type::Void => write!(f, "()"),
600            Type::Bool => write!(f, "bool"),
601
602            Type::UChar => write!(f, "::std::os::raw::c_uchar"),
603            Type::ConstUChar => write!(f, "::std::os::raw::c_uchar"),
604            Type::Char => write!(f, "::std::os::raw::c_char"),
605            Type::ConstChar => write!(f, "::std::os::raw::c_char"),
606            Type::UInt => write!(f, "::std::os::raw::c_uint"),
607            Type::Int => write!(f, "::std::os::raw::c_int"),
608            Type::ULong => write!(f, "::std::os::raw::c_ulong"),
609            Type::Long => write!(f, "::std::os::raw::c_long"),
610
611            Type::U8 => write!(f, "u8"),
612            Type::I8 => write!(f, "i8"),
613            Type::U16 => write!(f, "u16"),
614            Type::I16 => write!(f, "i16"),
615            Type::U32 => write!(f, "u32"),
616            Type::I32 => write!(f, "i32"),
617            Type::U64 => write!(f, "u64"),
618            Type::I64 => write!(f, "i64"),
619            Type::F64 => write!(f, "f64"),
620            Type::USize => write!(f, "usize"),
621            Type::Enum(ref e) => write!(f, "enum {}", e),
622            Type::Class(ref class) => write!(f, "class {}", class),
623            Type::Callback(ref callback) => write!(f, "callback {}", callback),
624            Type::CallbackLValue(ref v) => write!(f, "callback lvalue {}", v),
625            Type::Ptr(ref target) => write!(f, "*mut {}", target),
626            Type::Ref(ref target) => write!(f, "&{}", target),
627            Type::Arr(ref target) => write!(f, "[{}]", target),
628        }
629    }
630}