[Zarutian] 3. july 2007: Capicol is an variant of picol wich in turn is an variant of Tcl. Capicol stands for Capability picol and is my investigation into capability based security and asynchronus message passing in concurrent enviroment. It is not complete yet and probably very slow. (I want to get it right before fast, thank you) # This code is hereby released in the public domain. # Any infriging software patents will be disregarded and # propably made invalid because of obviouseness. # v 0.1 package require Tcl 8.5 package provide capicol 0.3 # state: # running # # quota # # capabilities # #
# in-queue # * # out_queue # * # # commands # * # type # prim | combo | script # contents # returnstack # frame* # frame # pointer # # code # * # results # # # variables # # # arguments # # [break-goto] # # [continue-goto] # # [catcher] # # [save-to] # dest # # variables # # # := # := # := "beget" | "gain" | "message" # := ; for "beget" # := ; for "message" # := ; for "gain" # :=
* # := a string where [] must be replace with that result # # design decision: use primitives gain and beget or just send messages to local addresses? # hugmynd: sleppa [gain] og [beget] úr grunnskipanasafninu proc log args { if {[string equal [lindex $args 0] "invoked"]} { set call_level [lindex $args 1] puts "log: [string repeat { } $call_level] invoked [lindex $args 2 0]" } else { puts "log: $args" } } namespace eval capicol {} proc capicol::basic_tester {code} { set t1 [capicol::new_state] dict set t1 frame code $code set i [llength $code] while {$i > 0} { set t1 [capicol::advance $t1] incr i -1 } return $t1 } # tests (not complete and should be moved to the end) # capicol::basic_tester {{+ 1 2 3}} # capicol::basic_tester {{- 3 2 1}} # capicol::basic_tester {{* 5 3 2}} # capicol::basic_tester {{/ 60 5 2}} # capicol::basic_tester {{% 20 4}} # capicol::basic_tester {{& 0xFF 0x0E}} # capicol::basic_tester {{| 0xF0 0x0F}} # capicol::basic_tester {{^ 0xF0 0x0F}} # capicol::basic_tester {{<< 2 1}} # capicol::basic_tester {{>> 8 2}} # capicol::basic_tester {{< 10 5}} # capicol::basic_tester {{<= 9 10}} # capicol::basic_tester {{== 20 20}} # capicol::basic_tester {{!= 20 20}} # capicol::basic_tester {{and yes yes}} # capicol::basic_tester {{any_messages?}} proc capicol::advance {state} { log invoked [info level] [info level 0] if {![dict exists $state commands]} { error "commands missing" } if {![dict exists $state frame code]} { error "code missing" } if {![dict exists $state frame pointer]} { dict set state frame pointer 0 } if {![dict exists $state frame results]} { dict set state frame results {} } if {![dict exists $state frame variables]} { dict set state frame variables {} } if {![dict exists $state frame args]} { dict set state frame args {} } if {![dict exists $state returnstack]} { dict set state returnstack {} } set cmd&args [lindex [dict get $state frame code] [dict get $state frame pointer]] set cmd&args [interpolate [dict get $state frame results] [set cmd&args]] set cmd [lindex [set cmd&args] 0] set args [lrange [set cmd&args] 1 end] if {[dict exists $state commands $cmd]} { if {![dict exists $state commands $cmd type]} { error "type of command $cmd missing" } if {![dict exists $state commands $cmd contents]} { error "contents of command $cmd missing" } if {[string equal "combo" [dict get $state commands $cmd type]]} { # nokkuð mjög líklegt að verði mikið notað # push current continuation onto returnstack set state [push_contination $state] # stilla state fyrir að keyra innihald procs dict set state frame code [dict get $state commands $cmd contents] dict set state frame pointer -1; # þarf að vera -1 út af autoincr dict set state frame variables {} dict set state frame results {} dict set state frame args $args } elseif {[string equal "prim" [dict get $state commands $cmd type]]} { set pointer [dict get $state frame pointer] lassign [exec_prim [dict get $state commands $cmd contents] $args $state] result state dict set state frame results $pointer $result } elseif {[string equal "script" [dict get $state commands $cmd type]]} { dict set state commands $cmd script [dict get $state commands $cmd contents] dict set state commands $cmd type combo dict set state commands $cmd contents [translate [dict get $state commands $cmd contents]] return [advance $state] } else { error "unknown command type [dict get $state commands $cmd type]" } } else { # the unknown command handling if {![dict exists $state commands unknown]} { lassign [call [list error "unknown command $cmd"] $state] _ state } else { # invoke the unknown command lassign [call [list unknown [set cmd&args]] $state] temp state dict set state frame results \[[dict get $state pointer]\] $temp } } if {[llength [dict get $state frame code]] < [dict get $state frame pointer]} { set state [lindex [exec_prim_return {} $state] end] } if {([string length [dict get $state commands]] + \ [string length [dict get $state returnstack]] + \ [string length [dict get $state frame]]) > [dict get $state quota]} { lassign [call [list error "not enaugh quota to run!"] $state] _ state } dict set state frame pointer [expr [dict get $state frame pointer] + 1]; # autoincr return $state } proc capicol::push_continuation {state} { log invoked [info level] [info level 0] set temp [dict create] dict lappend state returnstack [dict get $state frame] return $state } proc capicol::call {cmd&args state} { log invoked [info level] [info level 0] set state [push_continuation $state] dict set state frame code [list [set cmd&args]] dict set state frame pointer -1 return [list {} $state] } proc capicol::exec_prim {prim arguments state} { log invoked [info level] [info level 0] # giant despatching switch # I rather use jump tables but cant have them easily in higher level languages switch -exact -- $prim { "+" { return [exec_prim_math + $arguments $state] } "-" { return [exec_prim_math - $arguments $state] } "*" { return [exec_prim_math * $arguments $state] } "/" { return [exec_prim_math / $arguments $state] } "%" { return [exec_prim_math % $arguments $state] } "&" { return [exec_prim_math & $arguments $state] } "|" { return [exec_prim_math | $arguments $state] } "^" { return [exec_prim_math ^ $arguments $state] } "<<" { return [exec_prim_math << $arguments $state] } ">>" { return [exec_prim_math >> $arguments $state] } "<" { return [exec_prim_compare < $arguments $state] } "<=" { return [exec_prim_compare <= $arguments $state] } "==" { return [exec_prim_compare == $arguments $state] } "!=" { return [exec_prim_compare != $arguments $state] } "and" { return [exec_prim_logical_and $arguments $state] } "any_messages?" { return [exec_prim_any_messages? $arguments $state] } "args" { return [exec_prim_args $arguments $state] } "beget" { return [exec_prim_beget $arguments $state] } "break" { return [exec_prim_break $arguments $state] } "catch" { return [exec_prim_catch $arguments $state] } "capabilities" { return [exec_prim_capabilites $arguments $state] } "continue" { return [exec_prim_continue $arguments $state] } "dict" { return [exec_prim_dict $arguments $state] } "die" { return [exec_prim_die $arguments $state] } "drop_capability" { return [exec_prim_drop_capability $arguments $state] } "error" { return [exec_prim_error $arguments $state] } "gain" { return [exec_prim_gain $arguments $state] } "get" { return [exec_prim_get $arguments $state] } "if" { return [exec_prim_if $arguments $state] } "lappend" { return [exec_prim_lappend $arguments $state] } "lassign" { return [exec_prim_lassign $arguments $state] } "lindex" { return [exec_prim_lindex $arguments $state] } "linsert" { return [exec_prim_linsert $arguments $state] } "list" { return [exec_prim_list $arguments $state] } "llength" { return [exec_prim_llength $arguments $state] } "lrange" { return [exec_prim_lrange $arguments $state] } "lrepeat" { return [exec_prim_lrepeat $arguments $state] } "lsearch" { return [exec_prim_lsearch $arguments $state] } "lset" { return [exec_prim_lset $arguments $state] } "lsort" { return [exec_prim_lsort $arguments $state] } "next_message" { return [exec_prim_next_message $arguments $state] } "or" { return [exec_prim_or $arguments $state] } "rename" { return [exec_prim_rename $arguments $state] } "return" { return [exec_prim_return $arguments $state] } "routine" { return [exec_prim_routine $arguments $state] } "send_message" { return [exec_prim_send_message $arguments $state] } "set" { return [exec_prim_Set $arguments $state] } "string" { return [exec_prim_string $arguments $state] } "unset" { return [exec_prim_unset $arguments $state] } "uplevel" { return [exec_prim_uplevel $arguments $state] } "var_exists?" { return [exec_prim_var_exists? $arguments $state] } "while" { return [exec_prim_while $arguments $state] } "__branch" { return [exec_prim___branch $arguments $state] } "__jump" { return [exec_prim___jump $arguments $state] } default { error "unknown capicol primitive $prim" } } } proc capicol::exec_prim_math {op arguments state} { log invoked [info level] [info level 0] set result [lindex $arguments 0] foreach item [lrange $arguments 1 end] { set result [expr $result $op $item] } return [list $result $state] } proc capicol::exec_prim_compare {op arguments state} { log invoked [info level] [info level 0] if {[llength $arguments] != 2} { return [call [list error "wrong # args: should be \"$op number number\""] $state] } set result [expr [lindex $arguments 0] $op [lindex $arguments 1]] return [list $result $state] } proc capicol::exec_prim_logical_and {arguments state} { log invoked [info level] [info level 0] set result [lindex $arguments 0] foreach item [lrange $arguments 1 end] { set result [expr $result && $item] } return [list $result $state] } proc capicol::exec_prim_any_messages? {arguments state} { log invoked [info level] [info level 0] if {![dict exists $state in_queue]} { dict set state in_queue {} } return [list [expr [llength [dict get $state in_queue]] != 0] $state] } proc capicol::exec_prim_args {arguments state} { log invoked [info level] [info level 0] if {![dict exists $state frame args]} { dict set state frame args {} } return [list [dict get $state frame args] $state] } proc capicol::exec_prim_beget {arguments state} { log invoked [info level] [info level 0] # beget returns if {[llength $arguments] != 3} { return [call [list error "wrong # args: should be \"beget startupscript capabilities quota\""] $state] } # make new address for the "child" using the replicator serial scheme if {![dict exists $state my_address]} { error "an capicol state cannot be without an address!" } if {![dict exists $state number_of_children]} { dict set state number_of_children 0 } set child "[dict get $state my_address].[dict incr state number_of_children]" if {[catch [list addresses_from_caphandles [lindex $arguments 1] $state] res]} { return [call [list error $res] $state] } else { set addresses [lindex $res 0] set state [lindex $res end] unset res } set res [add_capability $child $state] set state [lindex $res end] set handle [lindex $res 0] unset res if {[dict get $state quota] < $quota} { return [call [list error "not enaugh quota!"] $state] } if {$quota < [string length [lindex $arguments 0]]} { return [call [list error "not enaugh quota allotted to child!"] $state] } dict lappend state out_queue [list beget $child [lindex $arguments 0] $adresses $quota] return [list $handle $state] } proc capicol::add_capability {address state} { log invoked [info level] [info level 0] if {![dict exists $state capabilities counter]} { dict set state capabilities counter [expr 2 * [dict size [dict get $state capabilities]]] } if {[lsearch [dict keys $state capabilities] $address] == -1} { set handle cap[dict incr state capabilities counter] dict set state capabilities $handle $address } else { foreach {handle item} [dict get $state capabilities] { if {[string equal $item $address]} break } } return [list $handle $state] } proc capicol::addresses_from_caphandles {caphandles state} { log invoked [info level] [info level 0] set addresses {} set caphandles [dict get $state capabilities] foreach caphandle $caphandles { if {![dict exists $capabilities $caphandle]} { error "no such caphandle: $caphandle" } lappend addresses [dict get $capabilities $caphandle] } return [list $addresses $state] } proc capicol::caphandles_from_adddresses {addresses state} { log invoked [info level] [info level 0] set tmp [list] foreach item $addresses { lassign [add_capability $item $state] caphandle state lappend tmp $caphandle } return [list $tmp $state] } proc capicol::exec_prim_break {arguments state} { log invoked [info level] [info level 0] while true { if {[dict exists $state frame break-goto]} break if {[string equal [dict get $state returnstack] ""]} { return [call [list error "break invoked outside an loop"] $state] } set state [lindex [exec_prim_return {} $state] end] } dict set state frame pointer [expr [dict get $state frame break-goto] - 1] return [list {} $state] } proc capicol::exec_prim_catch {arguments state} { log invoked [info level] [info level 0] # catch