diff --git a/kclvm/compiler/src/codegen/llvm/context.rs b/kclvm/compiler/src/codegen/llvm/context.rs index e08b1f860..d9adb78a2 100644 --- a/kclvm/compiler/src/codegen/llvm/context.rs +++ b/kclvm/compiler/src/codegen/llvm/context.rs @@ -418,6 +418,8 @@ impl<'ctx> ValueMethods for LLVMCodeGenContext<'ctx> { } /// Construct a function value using a native function. fn function_value(&self, function: FunctionValue<'ctx>) -> Self::Value { + let func_name = function.get_name().to_str().unwrap(); + let func_name_ptr = self.native_global_string(func_name, func_name).into(); let lambda_fn_ptr = self.builder.build_bitcast( function.as_global_value().as_pointer_value(), self.context.i64_type().ptr_type(AddressSpace::default()), @@ -425,21 +427,22 @@ impl<'ctx> ValueMethods for LLVMCodeGenContext<'ctx> { ); self.build_call( &ApiFunc::kclvm_value_Function_using_ptr.name(), - &[lambda_fn_ptr], + &[lambda_fn_ptr, func_name_ptr], ) } /// Construct a closure function value with the closure variable. fn closure_value(&self, function: FunctionValue<'ctx>, closure: Self::Value) -> Self::Value { + let func_name = function.get_name().to_str().unwrap(); + let func_name_ptr = self.native_global_string(func_name, func_name).into(); // Convert the function to a i64 pointer to store it into the function value. let fn_ptr = self.builder.build_bitcast( function.as_global_value().as_pointer_value(), self.context.i64_type().ptr_type(AddressSpace::default()), "", ); - let name = self.native_global_string("", "").into(); self.build_call( &ApiFunc::kclvm_value_Function.name(), - &[fn_ptr, closure, name], + &[fn_ptr, closure, func_name_ptr, self.native_i8_zero().into()], ) } /// Construct a schema function value using native functions. @@ -1674,11 +1677,17 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { self.context.i64_type().ptr_type(AddressSpace::default()), "", ); - let name = self.native_global_string("", "").into(); + let func_name = function.get_name().to_str().unwrap(); + let func_name_ptr = self.native_global_string(func_name, func_name).into(); let none_value = self.none_value(); self.build_call( &ApiFunc::kclvm_value_Function.name(), - &[lambda_fn_ptr, none_value, name], + &[ + lambda_fn_ptr, + none_value, + func_name_ptr, + self.native_i8_zero().into(), + ], ) }; Ok(value) @@ -1696,7 +1705,7 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { let none_value = self.none_value(); return Ok(self.build_call( &ApiFunc::kclvm_value_Function.name(), - &[null_fn_ptr, none_value, name], + &[null_fn_ptr, none_value, name, self.native_i8(1).into()], )); // User pkgpath } else { diff --git a/kclvm/error/src/diagnostic.rs b/kclvm/error/src/diagnostic.rs index 444574d36..2ba7fdd27 100644 --- a/kclvm/error/src/diagnostic.rs +++ b/kclvm/error/src/diagnostic.rs @@ -96,13 +96,14 @@ impl From for Position { impl Diagnostic { pub fn new(level: Level, message: &str, pos: Position) -> Self { - Diagnostic::new_with_code(level, message, pos, None) + Diagnostic::new_with_code(level, message, None, pos, None) } /// New a diagnostic with error code. pub fn new_with_code( level: Level, message: &str, + note: Option<&str>, pos: Position, code: Option, ) -> Self { @@ -112,7 +113,7 @@ impl Diagnostic { pos, style: Style::LineAndColumn, message: message.to_string(), - note: None, + note: note.map(|s| s.to_string()), }], code, } diff --git a/kclvm/error/src/lib.rs b/kclvm/error/src/lib.rs index bc1caf4e4..97b76e4dd 100644 --- a/kclvm/error/src/lib.rs +++ b/kclvm/error/src/lib.rs @@ -96,6 +96,7 @@ impl Handler { let diag = Diagnostic::new_with_code( Level::Error, &message, + None, pos, Some(DiagnosticId::Error(E1001.kind)), ); @@ -109,6 +110,7 @@ impl Handler { let diag = Diagnostic::new_with_code( Level::Error, msg, + None, pos, Some(DiagnosticId::Error(E2G22.kind)), ); @@ -122,6 +124,7 @@ impl Handler { let diag = Diagnostic::new_with_code( Level::Error, msg, + None, pos, Some(DiagnosticId::Error(E2L23.kind)), ); @@ -207,7 +210,7 @@ impl Handler { /// ``` /// use kclvm_error::*; /// let mut handler = Handler::default(); - /// handler.add_diagnostic(Diagnostic::new_with_code(Level::Error, "error message", Position::dummy_pos(), Some(DiagnosticId::Error(E1001.kind)))); + /// handler.add_diagnostic(Diagnostic::new_with_code(Level::Error, "error message", None, Position::dummy_pos(), Some(DiagnosticId::Error(E1001.kind)))); /// ``` #[inline] pub fn add_diagnostic(&mut self, diagnostic: Diagnostic) -> &mut Self { @@ -219,26 +222,58 @@ impl Handler { impl From for Diagnostic { fn from(panic_info: PanicInfo) -> Self { - let mut diag = Diagnostic::new_with_code( - Level::Error, - if panic_info.kcl_arg_msg.is_empty() { - &panic_info.message - } else { - &panic_info.kcl_arg_msg - }, - Position { - filename: panic_info.kcl_file.clone(), - line: panic_info.kcl_line as u64, - column: None, - }, - Some(DiagnosticId::Error(E3M38.kind)), - ); + let panic_msg = if panic_info.kcl_arg_msg.is_empty() { + &panic_info.message + } else { + &panic_info.kcl_arg_msg + }; + + let mut diag = if panic_info.backtrace.is_empty() { + Diagnostic::new_with_code( + Level::Error, + &panic_msg, + None, + Position { + filename: panic_info.kcl_file.clone(), + line: panic_info.kcl_line as u64, + column: None, + }, + Some(DiagnosticId::Error(E3M38.kind)), + ) + } else { + let mut backtrace_msg = "backtrace:\n".to_string(); + let mut backtrace = panic_info.backtrace.clone(); + backtrace.reverse(); + for (index, frame) in backtrace.iter().enumerate() { + backtrace_msg.push_str(&format!( + "\t{index}: {}\n\t\tat {}:{}", + frame.func, frame.file, frame.line + )); + if frame.col != 0 { + backtrace_msg.push_str(&format!(":{}", frame.col)) + } + backtrace_msg.push_str("\n") + } + Diagnostic::new_with_code( + Level::Error, + &panic_msg, + Some(&backtrace_msg), + Position { + filename: panic_info.kcl_file.clone(), + line: panic_info.kcl_line as u64, + column: None, + }, + Some(DiagnosticId::Error(E3M38.kind)), + ) + }; + if panic_info.kcl_config_meta_file.is_empty() { return diag; } let mut config_meta_diag = Diagnostic::new_with_code( Level::Error, &panic_info.kcl_config_meta_arg_msg, + None, Position { filename: panic_info.kcl_config_meta_file.clone(), line: panic_info.kcl_config_meta_line as u64, @@ -297,6 +332,7 @@ impl ParseError { Ok(Diagnostic::new_with_code( Level::Error, &self.to_string(), + None, loc.into(), Some(DiagnosticId::Error(ErrorKind::InvalidSyntax)), )) diff --git a/kclvm/runtime/src/_kclvm.bc b/kclvm/runtime/src/_kclvm.bc index 8022ac8d4..3129b1671 100644 Binary files a/kclvm/runtime/src/_kclvm.bc and b/kclvm/runtime/src/_kclvm.bc differ diff --git a/kclvm/runtime/src/_kclvm.h b/kclvm/runtime/src/_kclvm.h index ea5d70e87..923d708dc 100644 --- a/kclvm/runtime/src/_kclvm.h +++ b/kclvm/runtime/src/_kclvm.h @@ -566,9 +566,9 @@ kclvm_value_ref_t* kclvm_value_Float(kclvm_float_t v); kclvm_float_t* kclvm_value_Float_ptr(kclvm_value_ref_t* p); -kclvm_value_ref_t* kclvm_value_Function(uint64_t* fn_ptr, kclvm_value_ref_t* closure, kclvm_char_t* external_name); +kclvm_value_ref_t* kclvm_value_Function(uint64_t* fn_ptr, kclvm_value_ref_t* closure, kclvm_char_t* name, kclvm_bool_t is_external); -kclvm_value_ref_t* kclvm_value_Function_using_ptr(uint64_t* fn_ptr); +kclvm_value_ref_t* kclvm_value_Function_using_ptr(uint64_t* fn_ptr, kclvm_char_t* name); kclvm_value_ref_t* kclvm_value_Int(kclvm_int_t v); diff --git a/kclvm/runtime/src/_kclvm.ll b/kclvm/runtime/src/_kclvm.ll index 62f506ce5..f813edd48 100644 --- a/kclvm/runtime/src/_kclvm.ll +++ b/kclvm/runtime/src/_kclvm.ll @@ -514,9 +514,9 @@ declare %kclvm_value_ref_t* @kclvm_value_Float(%kclvm_float_t %v); declare %kclvm_float_t* @kclvm_value_Float_ptr(%kclvm_value_ref_t* %p); -declare %kclvm_value_ref_t* @kclvm_value_Function(i64* %fn_ptr, %kclvm_value_ref_t* %closure, %kclvm_char_t* %external_name); +declare %kclvm_value_ref_t* @kclvm_value_Function(i64* %fn_ptr, %kclvm_value_ref_t* %closure, %kclvm_char_t* %name, %kclvm_bool_t %is_external); -declare %kclvm_value_ref_t* @kclvm_value_Function_using_ptr(i64* %fn_ptr); +declare %kclvm_value_ref_t* @kclvm_value_Function_using_ptr(i64* %fn_ptr, %kclvm_char_t* %name); declare %kclvm_value_ref_t* @kclvm_value_Int(%kclvm_int_t %v); diff --git a/kclvm/runtime/src/_kclvm_api_spec.rs b/kclvm/runtime/src/_kclvm_api_spec.rs index 3f511e62c..447ec2b7e 100644 --- a/kclvm/runtime/src/_kclvm_api_spec.rs +++ b/kclvm/runtime/src/_kclvm_api_spec.rs @@ -1,4 +1,4 @@ -// Copyright 2022 The KCL Authors. All rights reserved. +// Copyright 2023 The KCL Authors. All rights reserved. // Auto generated by command, DONOT EDIT!!! @@ -271,12 +271,12 @@ // api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_schema_with_config(%kclvm_value_ref_t* %schema_dict, %kclvm_value_ref_t* %config, %kclvm_value_ref_t* %config_meta, %kclvm_char_t* %name, %kclvm_char_t* %pkgpath, %kclvm_value_ref_t* %is_sub_schema, %kclvm_value_ref_t* %record_instance, %kclvm_value_ref_t* %instance_pkgpath, %kclvm_value_ref_t* %optional_mapping); // api-spec: kclvm_value_Function -// api-spec(c): kclvm_value_ref_t* kclvm_value_Function(uint64_t* fn_ptr, kclvm_value_ref_t* closure, kclvm_char_t* external_name); -// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_Function(i64* %fn_ptr, %kclvm_value_ref_t* %closure, %kclvm_char_t* %external_name); +// api-spec(c): kclvm_value_ref_t* kclvm_value_Function(uint64_t* fn_ptr, kclvm_value_ref_t* closure, kclvm_char_t* name, kclvm_bool_t is_external); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_Function(i64* %fn_ptr, %kclvm_value_ref_t* %closure, %kclvm_char_t* %name, %kclvm_bool_t %is_external); // api-spec: kclvm_value_Function_using_ptr -// api-spec(c): kclvm_value_ref_t* kclvm_value_Function_using_ptr(uint64_t* fn_ptr); -// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_Function_using_ptr(i64* %fn_ptr); +// api-spec(c): kclvm_value_ref_t* kclvm_value_Function_using_ptr(uint64_t* fn_ptr, kclvm_char_t* name); +// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_value_Function_using_ptr(i64* %fn_ptr, %kclvm_char_t* %name); // api-spec: kclvm_value_schema_function // api-spec(c): kclvm_value_ref_t* kclvm_value_schema_function(uint64_t* fn_ptr, uint64_t* check_fn_ptr, kclvm_char_t* tpe); diff --git a/kclvm/runtime/src/api/kclvm.rs b/kclvm/runtime/src/api/kclvm.rs index a111a51ed..c4f495da2 100644 --- a/kclvm/runtime/src/api/kclvm.rs +++ b/kclvm/runtime/src/api/kclvm.rs @@ -271,8 +271,9 @@ pub struct FuncValue { pub fn_ptr: u64, pub check_fn_ptr: u64, pub closure: ValueRef, - pub external_name: String, + pub name: String, pub runtime_type: String, + pub is_external: bool, } #[derive(PartialEq, Clone, Default, Debug)] @@ -293,6 +294,7 @@ pub struct OptionHelp { #[derive(PartialEq, Eq, Clone, Default, Debug, Serialize, Deserialize)] pub struct PanicInfo { pub __kcl_PanicInfo__: bool, // "__kcl_PanicInfo__" + pub backtrace: Vec, pub rust_file: String, pub rust_line: i32, @@ -300,6 +302,7 @@ pub struct PanicInfo { pub kcl_pkgpath: String, pub kcl_file: String, + pub kcl_func: String, pub kcl_line: i32, pub kcl_col: i32, pub kcl_arg_msg: String, @@ -368,6 +371,7 @@ pub struct Context { pub main_pkg_path: String, pub main_pkg_files: Vec, + pub backtrace: Vec, pub imported_pkgpath: HashSet, pub app_args: HashMap, @@ -385,10 +389,43 @@ pub struct Context { pub objects: IndexSet, } +#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)] +pub struct BacktraceFrame { + pub file: String, + pub func: String, + pub col: i32, + pub line: i32, +} +impl Default for BacktraceFrame { + fn default() -> Self { + Self { + file: Default::default(), + func: "_kclvm_main".to_string(), + col: Default::default(), + line: Default::default(), + } + } +} + +impl BacktraceFrame { + pub fn from_panic_info(info: &PanicInfo) -> Self { + Self { + file: info.kcl_file.clone(), + func: info.kcl_func.clone(), + col: info.kcl_col, + line: info.kcl_line, + } + } +} + impl Context { pub fn new() -> Self { Context { instances: RefCell::new(HashMap::new()), + panic_info: PanicInfo { + kcl_func: "kclvm_main".to_string(), + ..Default::default() + }, ..Default::default() } } diff --git a/kclvm/runtime/src/context/mod.rs b/kclvm/runtime/src/context/mod.rs index 03c7af484..42a285b34 100644 --- a/kclvm/runtime/src/context/mod.rs +++ b/kclvm/runtime/src/context/mod.rs @@ -4,7 +4,7 @@ pub mod api; pub use api::*; use std::fmt; -use crate::PanicInfo; +use crate::{BacktraceFrame, PanicInfo}; #[allow(non_camel_case_types)] type kclvm_value_ref_t = crate::ValueRef; @@ -189,16 +189,24 @@ impl crate::Context { pub fn set_panic_info(&mut self, info: &std::panic::PanicInfo) { self.panic_info.__kcl_PanicInfo__ = true; - if let Some(s) = info.payload().downcast_ref::<&str>() { - self.panic_info.message = s.to_string(); + self.panic_info.message = if let Some(s) = info.payload().downcast_ref::<&str>() { + s.to_string() } else if let Some(s) = info.payload().downcast_ref::<&String>() { - self.panic_info.message = (*s).clone(); + (*s).clone() } else if let Some(s) = info.payload().downcast_ref::() { - self.panic_info.message = (*s).clone(); + (*s).clone() } else { - self.panic_info.message = "".to_string(); + "".to_string() + }; + if self.cfg.debug_mode { + self.panic_info.backtrace = self.backtrace.clone(); + self.panic_info.backtrace.push(BacktraceFrame { + file: self.panic_info.kcl_file.clone(), + func: self.panic_info.kcl_func.clone(), + col: self.panic_info.kcl_col, + line: self.panic_info.kcl_line, + }); } - if let Some(location) = info.location() { self.panic_info.rust_file = location.file().to_string(); self.panic_info.rust_line = location.line() as i32; diff --git a/kclvm/runtime/src/value/api.rs b/kclvm/runtime/src/value/api.rs index 83a5f00fa..f54e050bc 100644 --- a/kclvm/runtime/src/value/api.rs +++ b/kclvm/runtime/src/value/api.rs @@ -320,19 +320,36 @@ pub unsafe extern "C" fn kclvm_value_schema_with_config( pub unsafe extern "C" fn kclvm_value_Function( fn_ptr: *const u64, closure: *const kclvm_value_ref_t, - external_name: *const kclvm_char_t, + name: *const kclvm_char_t, + is_external: kclvm_bool_t, ) -> *mut kclvm_value_ref_t { let closure = ptr_as_ref(closure); - let name = c2str(external_name); - new_mut_ptr(ValueRef::func(fn_ptr as u64, 0, closure.clone(), name, "")) + let name = c2str(name); + new_mut_ptr(ValueRef::func( + fn_ptr as u64, + 0, + closure.clone(), + name, + "", + is_external != 0, + )) } #[no_mangle] #[runtime_fn] pub unsafe extern "C" fn kclvm_value_Function_using_ptr( fn_ptr: *const u64, + name: *const kclvm_char_t, ) -> *mut kclvm_value_ref_t { - new_mut_ptr(ValueRef::func(fn_ptr as u64, 0, ValueRef::none(), "", "")) + let name = c2str(name); + new_mut_ptr(ValueRef::func( + fn_ptr as u64, + 0, + ValueRef::none(), + name, + "", + false, + )) } #[no_mangle] @@ -375,6 +392,7 @@ pub unsafe extern "C" fn kclvm_value_schema_function( schema_args, "", runtime_type, + false, ); let ctx = Context::current_context_mut(); let mut all_schemas = ctx.all_schemas.borrow_mut(); @@ -576,7 +594,7 @@ pub unsafe extern "C" fn kclvm_value_function_is_external( ) -> kclvm_bool_t { let p = ptr_as_ref(p); match &*p.rc.borrow() { - Value::func_value(ref v) => !v.external_name.is_empty() as kclvm_bool_t, + Value::func_value(ref v) => v.is_external as kclvm_bool_t, _ => false as kclvm_bool_t, } } @@ -591,7 +609,7 @@ pub unsafe extern "C" fn kclvm_value_function_external_invoke( let p = ptr_as_ref(p); match &*p.rc.borrow() { Value::func_value(ref v) => { - let name = format!("{}\0", v.external_name); + let name = format!("{}\0", v.name); kclvm_plugin_invoke(name.as_ptr() as *const i8, args, kwargs) } _ => kclvm_value_None(), @@ -615,8 +633,13 @@ pub unsafe extern "C" fn kclvm_value_function_invoke( let fn_ptr = func.fn_ptr; let closure = &func.closure; let is_schema = !func.runtime_type.is_empty(); - let is_external = !func.external_name.is_empty(); let ctx_ref = mut_ptr_as_ref(ctx); + if ctx_ref.cfg.debug_mode { + ctx_ref + .backtrace + .push(BacktraceFrame::from_panic_info(&ctx_ref.panic_info)); + ctx_ref.panic_info.kcl_func = func.name.clone(); + } let now_meta_info = ctx_ref.panic_info.clone(); unsafe { let call_fn: SchemaTypeFunc = transmute_copy(&fn_ptr); @@ -652,8 +675,8 @@ pub unsafe extern "C" fn kclvm_value_function_invoke( args_new.list_append_unpack(&closure_new); call_fn(ctx, args_new.into_raw(), kwargs) // Normal kcl function, call directly - } else if is_external { - let name = format!("{}\0", func.external_name); + } else if func.is_external { + let name = format!("{}\0", func.name); kclvm_plugin_invoke(name.as_ptr() as *const i8, args, kwargs) } else { args_ref.list_append_unpack_first(closure); @@ -665,6 +688,9 @@ pub unsafe extern "C" fn kclvm_value_function_invoke( let schema_value = ptr_as_ref(value); schema_value.schema_check_attr_optional(true); } + if ctx_ref.cfg.debug_mode { + ctx_ref.backtrace.pop(); + } ctx_ref.panic_info = now_meta_info; return value; }; @@ -1920,7 +1946,14 @@ pub unsafe extern "C" fn kclvm_value_load_attr( _ => panic!("str object attr '{key}' not found"), }; let closure = ValueRef::list(Some(&[p])); - return new_mut_ptr(ValueRef::func(function as usize as u64, 0, closure, "", "")); + return new_mut_ptr(ValueRef::func( + function as usize as u64, + 0, + closure, + "", + "", + false, + )); } // schema instance else if p.is_func() { @@ -1929,7 +1962,14 @@ pub unsafe extern "C" fn kclvm_value_load_attr( _ => panic!("schema object attr '{key}' not found"), }; let closure = ValueRef::list(Some(&[p])); - return new_mut_ptr(ValueRef::func(function as usize as u64, 0, closure, "", "")); + return new_mut_ptr(ValueRef::func( + function as usize as u64, + 0, + closure, + "", + "", + false, + )); } panic!( "invalid value '{}' to load attribute '{}'", @@ -2263,6 +2303,14 @@ pub unsafe extern "C" fn kclvm_schema_value_new( if schema_value_or_func.is_func() { let schema_func = schema_value_or_func.as_function(); let schema_fn_ptr = schema_func.fn_ptr; + let ctx_ref = mut_ptr_as_ref(ctx); + let now_meta_info = ctx_ref.panic_info.clone(); + if ctx_ref.cfg.debug_mode { + ctx_ref + .backtrace + .push(BacktraceFrame::from_panic_info(&ctx_ref.panic_info)); + ctx_ref.panic_info.kcl_func = schema_func.runtime_type.clone(); + } let value = unsafe { let org_args = ptr_as_ref(args).deep_copy(); let schema_fn: SchemaTypeFunc = transmute_copy(&schema_fn_ptr); @@ -2324,6 +2372,10 @@ pub unsafe extern "C" fn kclvm_schema_value_new( } schema_fn(ctx, args, kwargs) }; + ctx_ref.panic_info = now_meta_info; + if ctx_ref.cfg.debug_mode { + ctx_ref.backtrace.pop(); + } value } else { let config = ptr_as_ref(config); diff --git a/kclvm/runtime/src/value/val.rs b/kclvm/runtime/src/value/val.rs index 8b9b5f97b..958373dc4 100644 --- a/kclvm/runtime/src/value/val.rs +++ b/kclvm/runtime/src/value/val.rs @@ -72,13 +72,15 @@ impl ValueRef { closure: ValueRef, name: &str, runtime_type: &str, + is_external: bool, ) -> Self { Self::from(Value::func_value(Box::new(FuncValue { fn_ptr, check_fn_ptr, closure, - external_name: name.to_string(), + name: name.to_string(), runtime_type: runtime_type.to_string(), + is_external, }))) } } diff --git a/kclvm/runtime/src/value/val_clone.rs b/kclvm/runtime/src/value/val_clone.rs index 65723253d..c9348d4ad 100644 --- a/kclvm/runtime/src/value/val_clone.rs +++ b/kclvm/runtime/src/value/val_clone.rs @@ -20,8 +20,9 @@ impl ValueRef { fn_ptr: v.fn_ptr, check_fn_ptr: v.check_fn_ptr, closure: v.closure.deep_copy(), - external_name: v.external_name.clone(), + name: v.name.clone(), runtime_type: v.runtime_type.clone(), + is_external: v.is_external, })))), }, Value::bool_value(ref v) => ValueRef {