13
13
//! [1]: http://duktape.org/
14
14
15
15
extern crate duktape_sys;
16
+ #[ cfg( feature = "logging" ) ]
17
+ #[ macro_use]
18
+ extern crate log;
16
19
17
20
use std:: collections;
18
21
use std:: error;
@@ -126,7 +129,7 @@ impl Context {
126
129
} ;
127
130
128
131
unsafe {
129
- duktape_sys :: duk_logging_init ( raw, 0 ) ;
132
+ Context :: setup_logging ( raw) ;
130
133
}
131
134
132
135
Context {
@@ -135,6 +138,50 @@ impl Context {
135
138
}
136
139
}
137
140
141
+ #[ cfg( feature = "logging" ) ]
142
+ unsafe fn setup_logging ( ctx : * mut duktape_sys:: duk_context ) {
143
+ use duktape_sys:: * ;
144
+ duk_logging_init ( ctx, 0 ) ;
145
+
146
+ duk_push_global_object ( ctx) ;
147
+ duk_get_prop_string ( ctx, -1 , nul_str ( b"Duktape\0 " ) ) ;
148
+ duk_get_prop_string ( ctx, -1 , nul_str ( b"Logger\0 " ) ) ;
149
+ duk_get_prop_string ( ctx, -1 , nul_str ( b"prototype\0 " ) ) ;
150
+ // Stack: [ global .Duktape .Logger .prototype ]
151
+
152
+ duk_push_c_function ( ctx, Some ( log_handler) , DUK_VARARGS ) ;
153
+ duk_set_magic ( ctx, -1 , DUK_LOG_TRACE ) ;
154
+ duk_put_prop_string ( ctx, -2 , nul_str ( b"trace\0 " ) ) ;
155
+
156
+ duk_push_c_function ( ctx, Some ( log_handler) , DUK_VARARGS ) ;
157
+ duk_set_magic ( ctx, -1 , DUK_LOG_DEBUG ) ;
158
+ duk_put_prop_string ( ctx, -2 , nul_str ( b"debug\0 " ) ) ;
159
+
160
+ duk_push_c_function ( ctx, Some ( log_handler) , DUK_VARARGS ) ;
161
+ duk_set_magic ( ctx, -1 , DUK_LOG_INFO ) ;
162
+ duk_put_prop_string ( ctx, -2 , nul_str ( b"info\0 " ) ) ;
163
+
164
+ duk_push_c_function ( ctx, Some ( log_handler) , DUK_VARARGS ) ;
165
+ duk_set_magic ( ctx, -1 , DUK_LOG_WARN ) ;
166
+ duk_put_prop_string ( ctx, -2 , nul_str ( b"warn\0 " ) ) ;
167
+
168
+ duk_push_c_function ( ctx, Some ( log_handler) , DUK_VARARGS ) ;
169
+ duk_set_magic ( ctx, -1 , DUK_LOG_ERROR ) ;
170
+ duk_put_prop_string ( ctx, -2 , nul_str ( b"error\0 " ) ) ;
171
+
172
+ duk_push_c_function ( ctx, Some ( log_handler) , DUK_VARARGS ) ;
173
+ duk_set_magic ( ctx, -1 , DUK_LOG_FATAL ) ;
174
+ duk_put_prop_string ( ctx, -2 , nul_str ( b"fatal\0 " ) ) ;
175
+
176
+ // Stack: [ global .Duktape .Logger .prototype ]
177
+ duk_pop_n ( ctx, 4 ) ;
178
+ }
179
+
180
+ #[ cfg( not( feature = "logging" ) ) ]
181
+ unsafe fn setup_logging ( _: * mut duktape_sys:: duk_context ) {
182
+ // No-op
183
+ }
184
+
138
185
/// Evaluates the specified script string within the current
139
186
/// context.
140
187
///
@@ -539,11 +586,11 @@ impl Error {
539
586
Some ( n)
540
587
} ) ;
541
588
let line_number = get_number_property ( ctx, index, "lineNumber" )
542
- . and_then ( |n| if n. is_nan ( ) {
543
- None
544
- } else {
545
- Some ( n as usize )
546
- } ) ;
589
+ . and_then ( |n| if n. is_nan ( ) {
590
+ None
591
+ } else {
592
+ Some ( n as usize )
593
+ } ) ;
547
594
let stack = get_string_property ( ctx, index, "stack" ) ;
548
595
549
596
Error :: Js {
@@ -634,6 +681,102 @@ unsafe fn get_number_property(ctx: *mut duktape_sys::duk_context,
634
681
}
635
682
}
636
683
684
+ unsafe fn nul_str ( data : & [ u8 ] ) -> * const os:: raw:: c_char {
685
+ ffi:: CStr :: from_bytes_with_nul_unchecked ( data) . as_ptr ( )
686
+ }
687
+
688
+ #[ cfg( feature = "logging" ) ]
689
+ unsafe extern "C" fn log_handler ( ctx : * mut duktape_sys:: duk_context ) -> duktape_sys:: duk_ret_t {
690
+ use duktape_sys:: * ; // Because this function is essentially only calling C stuff
691
+
692
+ // The function magic is the log level that this handler should handle.
693
+ let level = duk_get_current_magic ( ctx) ;
694
+ if level < DUK_LOG_TRACE || level > DUK_LOG_FATAL {
695
+ return 0 ;
696
+ }
697
+
698
+ // Stack: [ arg0 ... argN ]
699
+ let nargs = duk_get_top ( ctx) ;
700
+
701
+ duk_push_this ( ctx) ;
702
+ // Stack: [ arg0 ... argN this ]
703
+
704
+ duk_get_prop_string ( ctx, -1 , nul_str ( b"l\0 " ) ) ;
705
+ // Stack: [ arg0 ... argN this loggerLevel ]
706
+
707
+ // Check if we should log this level with this logger
708
+ let logger_level = duk_get_int ( ctx, -1 ) ;
709
+ if level < logger_level {
710
+ return 0 ;
711
+ }
712
+
713
+ let rust_level = if logger_level == DUK_LOG_TRACE {
714
+ log:: LogLevel :: Trace
715
+ } else if logger_level == DUK_LOG_DEBUG {
716
+ log:: LogLevel :: Debug
717
+ } else if logger_level == DUK_LOG_INFO {
718
+ log:: LogLevel :: Info
719
+ } else if logger_level == DUK_LOG_WARN {
720
+ log:: LogLevel :: Warn
721
+ } else {
722
+ log:: LogLevel :: Error
723
+ } ;
724
+
725
+ duk_get_prop_string ( ctx,
726
+ -2 ,
727
+ nul_str ( b"n\0 " ) ) ;
728
+ // Stack: [ arg0 ... argN this loggerLevel loggerName ]
729
+ duk_to_string ( ctx, -1 ) ;
730
+
731
+ let mut total_len = 0 ;
732
+
733
+ // Replace all args with equivalent strings, and compute their lengths
734
+ // Stack: [ arg0 ... argN this loggerLevel loggerName ]
735
+ for i in 0 ..nargs {
736
+ if 1 == duk_is_object ( ctx, i) {
737
+ duk_push_string ( ctx,
738
+ ffi:: CStr :: from_bytes_with_nul_unchecked ( b"fmt\0 " ) . as_ptr ( ) ) ;
739
+ duk_dup ( ctx, i) ;
740
+ // Stack: [ arg1 ... argN this loggerLevel loggerName 'fmt' arg ]
741
+ // Call: this.fmt(arg) so -5 is this
742
+ duk_pcall_prop ( ctx, -5 , 1 ) ;
743
+ duk_replace ( ctx, i) ;
744
+ }
745
+
746
+ let mut arg_len = mem:: uninitialized ( ) ;
747
+
748
+ duk_to_lstring ( ctx, i, & mut arg_len) ;
749
+
750
+ total_len += arg_len as usize ;
751
+ }
752
+
753
+ // Stack: [ arg0String ... argNString this loggerLevel loggerName ]
754
+
755
+ let mut name_len = mem:: uninitialized ( ) ;
756
+ let name_data = duk_get_lstring ( ctx, -1 , & mut name_len) ;
757
+ let name_slice = slice:: from_raw_parts ( name_data as * const u8 , name_len) ;
758
+ let name_str = str:: from_utf8 ( name_slice) . unwrap ( ) ;
759
+
760
+ // Allocate message space; include nargs to allocate spaces
761
+ let mut msg = String :: with_capacity ( total_len + name_str. len ( ) + nargs as usize + 1 ) ;
762
+ msg. push_str ( name_str) ;
763
+ msg. push ( ':' ) ;
764
+
765
+ for i in 0 ..nargs {
766
+ let mut arg_len = mem:: uninitialized ( ) ;
767
+ let arg_data = duk_get_lstring ( ctx, i, & mut arg_len) ;
768
+ let slice = slice:: from_raw_parts ( arg_data as * const u8 , arg_len) ;
769
+ let arg_str = str:: from_utf8 ( slice) . unwrap ( ) ;
770
+
771
+ msg. push ( ' ' ) ;
772
+ msg. push_str ( arg_str) ;
773
+ }
774
+
775
+ log ! ( target: & format!( "{}:{}" , module_path!( ) , name_str) , rust_level, "{}" , msg) ;
776
+
777
+ 0
778
+ }
779
+
637
780
unsafe extern "C" fn fatal_handler ( _: * mut os:: raw:: c_void , msg_raw : * const os:: raw:: c_char ) {
638
781
let msg = & * ffi:: CStr :: from_ptr ( msg_raw) . to_string_lossy ( ) ;
639
782
// TODO: No unwind support from C... but this "works" right now
@@ -879,7 +1022,7 @@ mod tests {
879
1022
function foo() {
880
1023
return 'a';
881
1024
}" )
882
- . unwrap ( ) ;
1025
+ . unwrap ( ) ;
883
1026
let global = ctx. global_object ( ) ;
884
1027
ctx. assert_clean ( ) ;
885
1028
let foo = global. get ( "foo" ) . unwrap ( ) ;
@@ -900,7 +1043,7 @@ mod tests {
900
1043
}
901
1044
return Array.prototype.slice.call(arguments);
902
1045
}" )
903
- . unwrap ( ) ;
1046
+ . unwrap ( ) ;
904
1047
let global = ctx. global_object ( ) ;
905
1048
ctx. assert_clean ( ) ;
906
1049
let value = global. call_method ( "foo" , & [ & Value :: Number ( 4.25 ) ] ) . unwrap ( ) . to_value ( ) ;
@@ -919,7 +1062,7 @@ mod tests {
919
1062
}
920
1063
return Array.prototype.slice.call(arguments);
921
1064
}" )
922
- . unwrap ( ) ;
1065
+ . unwrap ( ) ;
923
1066
let global = ctx. global_object ( ) ;
924
1067
ctx. assert_clean ( ) ;
925
1068
let foo = global. get ( "foo" ) . unwrap ( ) ;
@@ -939,7 +1082,7 @@ mod tests {
939
1082
}
940
1083
return Array.prototype.slice.call(arguments);
941
1084
}" )
942
- . unwrap ( ) ;
1085
+ . unwrap ( ) ;
943
1086
let global = ctx. global_object ( ) ;
944
1087
ctx. assert_clean ( ) ;
945
1088
let foo = global. get ( "foo" ) . unwrap ( ) ;
@@ -956,7 +1099,7 @@ mod tests {
956
1099
function foo() {
957
1100
return 'a';
958
1101
}" )
959
- . unwrap ( ) ;
1102
+ . unwrap ( ) ;
960
1103
let value = ctx. call_global ( "foo" , & [ ] ) . unwrap ( ) . to_value ( ) ;
961
1104
assert_eq ! ( Value :: String ( "a" . to_owned( ) ) , value) ;
962
1105
ctx. assert_clean ( ) ;
@@ -969,7 +1112,7 @@ mod tests {
969
1112
function foo() {
970
1113
return Array.prototype.slice.call(arguments);
971
1114
}" )
972
- . unwrap ( ) ;
1115
+ . unwrap ( ) ;
973
1116
974
1117
let mut obj = collections:: BTreeMap :: new ( ) ;
975
1118
obj. insert ( "a" . to_owned ( ) , Value :: String ( "a" . to_owned ( ) ) ) ;
@@ -1001,7 +1144,7 @@ mod tests {
1001
1144
function foo() {
1002
1145
throw 'a';
1003
1146
}" )
1004
- . unwrap ( ) ;
1147
+ . unwrap ( ) ;
1005
1148
let mut value = ctx. call_global ( "foo" , & [ ] ) ;
1006
1149
clean_error ( & mut value) ;
1007
1150
assert_eq ! ( Err ( Error :: Js {
0 commit comments