1extern crate clang;
2#[macro_use]
3extern crate log;
4
5use std::env;
6use std::fmt;
7use std::path;
8
9#[derive(Debug)]
11pub struct Api {
12 pub classes: Vec<Class>,
14}
15
16#[derive(Debug)]
18pub struct Class {
19 pub name: String,
21 pub methods: Vec<Method>,
23}
24
25#[derive(Debug)]
27pub struct Method {
28 pub is_static: bool,
30 pub name: String,
32 pub mangled_name: String,
35 pub args: Vec<Arg>,
37 pub ret_type: RetType,
39}
40
41#[derive(Debug)]
43pub enum RetType {
44 Direct(Type),
49 Maybe(Type),
54}
55
56#[derive(Debug)]
58pub struct Arg {
59 pub name: String,
61 pub arg_type: Type,
63}
64
65#[derive(Debug, Eq, PartialEq)]
67pub enum Type {
68 Void,
70 Bool,
72
73 UChar,
75 ConstUChar,
77 Char,
79 ConstChar,
81 UInt,
83 Int,
85 ULong,
87 Long,
89
90 U8,
92 I8,
94 U16,
96 I16,
98 U32,
100 I32,
102 U64,
104 I64,
106 F64,
108
109 USize,
111
112 Class(String),
114 Enum(String),
116 Callback(String),
118 CallbackLValue(String),
120
121 Ref(Box<Type>),
124 Ptr(Box<Type>),
126 Arr(Box<Type>),
128}
129
130struct MethodMangle {
132 name: &'static str,
134 signature: &'static str,
136 mangle: &'static str,
138}
139
140#[cfg_attr(rustfmt, rustfmt_skip)]
142const SPECIAL_CLASSES: &'static [&'static str] = &[
143 "Isolate", "HandleScope", "EscapableHandleScope", "SealHandleScope", "TryCatch", "ScriptOrigin", "PersistentHandleVisitor", "EmbedderHeapTracer", "ValueSerializer", "ValueDeserializer", "ExtensionConfiguration", "Module", "SnapshotCreator", "Platform",
165 "Task",
166 "IdleTask",
167];
168
169#[cfg_attr(rustfmt, rustfmt_skip)]
171const SPECIAL_METHODS: &'static [(&'static str, &'static str)] = &[
172 ("Script", "Compile"), ("Message", "GetScriptOrigin"), ("String", "WriteUtf8"), ("Object", "SetAlignedPointerInInternalFields"), ("Object", "CallAsFunction"), ("Object", "CallAsConstructor"), ("Object", "NewInstance"), ("Object", "Call"), ("Function", "New"), ("Function", "GetScriptOrigin"), ("Function", "NewInstance"), ("Function", "Call"), ("Template", "SetNativeDataProperty"), ("Template", "SetLazyDataProperty"), ("FunctionTemplate", "New"), ("FunctionTemplate", "NewWithCache"), ("ObjectTemplate", "SetAccessor"), ("ObjectTemplate", "SetNamedPropertyHandler"), ("ObjectTemplate", "SetIndexedPropertyHandler"), ("ObjectTemplate", "SetCallAsFunctionHandler"), ("ObjectTemplate", "SetAccessCheckCallback"), ("ObjectTemplate", "SetAccessCheckCallbackAndHandler"), ("Value", "IsFloat32x4"), ("WasmCompiledModule", "Serialize"), ("WasmCompiledModule", "Deserialize"), ("ConvertableToTraceFormat", "AppendAsTraceFormat"), ("V8", "CreateSnapshotDataBlob"), ("V8", "WarmUpSnapshotDataBlob"), ("V8", "Initialize"), ("V8", "Dispose"), ("V8", "InitializePlatform"), ("V8", "ShutdownPlatform"), ];
205
206#[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
234pub 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 .filter(|e| e.get_kind() == clang::EntityKind::ClassDecl)
300 .filter(|e| !e.get_children().is_empty())
302 .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 .filter(|e| e.get_kind() == clang::EntityKind::Method)
320 .filter(|e| e.get_availability() == clang::Availability::Available)
322 .filter(|e| e.get_accessibility() == Some(clang::Accessibility::Public))
324 .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 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 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 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 "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}