├── .gitignore ├── .gitmodules ├── .ruby-gemset ├── Cheffile ├── Cheffile.lock ├── Gemfile ├── Gemfile.lock ├── Makefile.in ├── README.rdoc ├── Vagrantfile ├── build-aux └── install-sh ├── build-binary ├── Makefile ├── c_files └── mrbcc_out.c ├── build ├── Makefile ├── c_files │ ├── debug.c │ ├── exception.c │ ├── method_dispatch.c │ ├── modified_defines.c │ ├── mrbb_struct.c │ ├── proc.c │ ├── vm.c │ ├── vm_changed.c │ └── vm_extern.c └── mrbcc_out.c ├── compile ├── configure ├── configure.ac ├── crashing_tests.rb ├── custom_tests.rb ├── dbg_recompile ├── failing_tests.rb ├── mrbcc ├── codegen.rb ├── codegen_rb │ ├── met_start.c │ ├── opcode_changed.c │ ├── opcode_err.c │ ├── opcode_irep.c │ ├── opcode_long.c │ └── opcode_special.c ├── mrb_opcodes.rb ├── mrbcc.rb ├── preparser.rb └── rite_parser.rb ├── mrbcc_mrblib └── compile_mrblib.rb ├── passing_tests.rb ├── run_performance_test.rb ├── setup.sh ├── standalone_runner ├── Makefile └── mrbcc_runner.c ├── test └── performance_test.rb └── testsuite.rb /.gitignore: -------------------------------------------------------------------------------- 1 | # / 2 | *.bak 3 | *.dylib 4 | *.inc 5 | *.o 6 | *.a 7 | *.orig 8 | *.rej 9 | *.sav 10 | *.swp 11 | *.d 12 | *~ 13 | .DS_Store 14 | .ccmalloc 15 | .svn 16 | /.git 17 | cscope.out 18 | CMakeFiles 19 | CMakeCache.txt 20 | test.rb 21 | tmp_out.rb 22 | tmp_out.mrb 23 | build/c_files/out.c 24 | build/*.o-* 25 | build-binary/*.o-* 26 | testsuite 27 | build-binary/mrbcc_out 28 | build-binary/mruby.xcodeproj 29 | *.so 30 | runner 31 | config.log 32 | config.status 33 | Makefile 34 | perf_test_results.txt 35 | /tmp 36 | /.vagrant 37 | /cookbooks 38 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "mruby"] 2 | path = mruby 3 | url = git://github.com/mruby/mruby.git 4 | -------------------------------------------------------------------------------- /.ruby-gemset: -------------------------------------------------------------------------------- 1 | mruby_cc 2 | -------------------------------------------------------------------------------- /Cheffile: -------------------------------------------------------------------------------- 1 | site "http://community.opscode.com/api/v1" 2 | 3 | cookbook 'apt' 4 | cookbook 'git' 5 | cookbook 'build-essential' 6 | cookbook 'ruby_build' 7 | cookbook 'rbenv', git: 'https://github.com/fnichol/chef-rbenv' 8 | -------------------------------------------------------------------------------- /Cheffile.lock: -------------------------------------------------------------------------------- 1 | SITE 2 | remote: http://community.opscode.com/api/v1 3 | specs: 4 | apt (1.10.0) 5 | build-essential (1.4.4) 6 | chef_handler (1.1.6) 7 | dmg (2.2.0) 8 | git (4.0.2) 9 | build-essential (>= 0.0.0) 10 | dmg (>= 0.0.0) 11 | runit (>= 1.0.0) 12 | windows (>= 0.0.0) 13 | yum (~> 3.0) 14 | yum-epel (>= 0.0.0) 15 | ruby_build (0.8.0) 16 | runit (1.5.10) 17 | build-essential (>= 0.0.0) 18 | yum (~> 3.0) 19 | yum-epel (>= 0.0.0) 20 | windows (1.34.6) 21 | chef_handler (>= 0.0.0) 22 | yum (3.3.2) 23 | yum-epel (0.5.1) 24 | yum (~> 3.0) 25 | 26 | GIT 27 | remote: https://github.com/fnichol/chef-rbenv 28 | ref: master 29 | sha: 0a3018634bafe58ad21c6ee271af015220e444b9 30 | specs: 31 | rbenv (0.7.3) 32 | 33 | DEPENDENCIES 34 | apt (>= 0) 35 | build-essential (>= 0) 36 | git (>= 0) 37 | rbenv (>= 0) 38 | ruby_build (>= 0) 39 | 40 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'activesupport' 4 | gem 'i18n' -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | activesupport (3.0.0) 5 | i18n (0.6.4) 6 | 7 | PLATFORMS 8 | ruby 9 | 10 | DEPENDENCIES 11 | activesupport 12 | i18n 13 | -------------------------------------------------------------------------------- /Makefile.in: -------------------------------------------------------------------------------- 1 | prefix = @prefix@ 2 | exec_prefix = @exec_prefix@ 3 | 4 | BINDIR = @bindir@ 5 | LIBDIR = @libdir@ 6 | 7 | INSTALL = @INSTALL@ 8 | INSTALL_PROGRAM = @INSTALL_PROGRAM@ 9 | INSTALL_SCRIPT = @INSTALL_SCRIPT@ 10 | INSTALL_DATA = @INSTALL_DATA@ 11 | 12 | GIT = @GIT@ 13 | 14 | all : mruby 15 | ./setup.sh 16 | 17 | install : all 18 | $(INSTALL) -d $(BINDIR) 19 | $(INSTALL_PROGRAM) runner $(BINDIR)/runner 20 | # $(INSTALL) -d $(LIBDIR) 21 | # $(INSTALL_DATA) mrblib.so $(LIBDIR)/mrblib.so 22 | 23 | .PHONY : mruby 24 | mruby : mruby/.git 25 | CFLAGS="-fPIC" $(MAKE) -C mruby 26 | 27 | mruby/.git : 28 | $(GIT) submodule init 29 | $(GIT) submodule update 30 | 31 | .PHONY : clean 32 | clean : 33 | rm -f *~ 34 | 35 | .PHONY : distclean 36 | distclean : clean 37 | rm -fr autom4te.cache config.log config.status 38 | rm -fr Makefile 39 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = What 2 | 3 | This enables translating mruby code into C code (not necessarily human readable). 4 | The main benefit is increased performance. We cannot achieve performance of real programmer-written C code, 5 | but we can improve on performance of interpreted mruby. 6 | 7 | Stable enough for testing, but not tested enough for production use. 8 | 9 | = Install 10 | 11 | https://github.com/mrbrdo/mruby_cc/wiki/Install 12 | 13 | = Use 14 | 15 | echo "puts 'hello world'" > test.rb 16 | ./compile test.rb 17 | 18 | ./runner test.so 19 | 20 | = Advantages and disadvantages 21 | 22 | *Advantages* 23 | 24 | * improved performance, obviously 25 | * code is shipped in binary form 26 | * virtually impossible to get back the original ruby code, because the binary code is compiled from mruby bytecode 27 | * 3rd party binary obfuscators can be used to make it virtually impossible to recover even the bytecode from which it was compiled 28 | * output is a C file, which can be manually optimized if necessary (this is an extreme case however) 29 | 30 | *Disadvantages* 31 | 32 | * code is shipped in binary form 33 | * need to provide precompiled version for each platform, or compile on the fly (currently, this requires gcc or some compiler present on target machine) 34 | * it is not possible to ensure that the compiled file includes only ruby code, malicious users could include anything - solution is for vendor to oversee user-provided scripts, or to not allow user scripts, also to check CRC of binary to confirm authenticity 35 | * larger size of binary compared to ruby source file 36 | * about 300-500KB for typical script 37 | * compression should be very efficient, especially when compressing multiple files 38 | 39 | = Dynamic loading of other files 40 | 41 | Now it is possible to dynamically load other pre-compiled ruby files. 42 | 43 | # some_ruby_file.rb 44 | load_compiled_mrb "dyn.so" 45 | 46 | I recommend you use the full file path. 47 | 48 | = Examples 49 | 50 | https://github.com/mrbrdo/mruby_cc/wiki/Examples 51 | 52 | = FAQ 53 | 54 | https://github.com/mrbrdo/mruby_cc/wiki/FAQ 55 | 56 | = Performance 57 | 58 | https://github.com/mrbrdo/mruby_cc/wiki/Performance 59 | 60 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! 5 | VAGRANTFILE_API_VERSION = "2" 6 | 7 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 8 | # All Vagrant configuration is done here. The most common configuration 9 | # options are documented and commented below. For a complete reference, 10 | # please see the online documentation at vagrantup.com. 11 | 12 | # Every Vagrant virtual environment requires a box to build off of. 13 | config.vm.box = "sincerely/precise64" 14 | config.omnibus.chef_version = '11.16.0' 15 | config.vm.provision :chef_solo do |chef| 16 | chef.cookbooks_path = ["cookbooks", "site-cookbooks"] 17 | 18 | chef.add_recipe "apt" 19 | chef.add_recipe "git::source" 20 | chef.add_recipe "ruby_build" 21 | chef.add_recipe "rbenv::user" 22 | chef.add_recipe "rbenv::vagrant" 23 | 24 | # Install Ruby 1.9.3-p374 and Bundler 25 | # Set an empty root password for MySQL to make things simple 26 | chef.json = { 27 | rbenv: { 28 | user_installs: [{ 29 | user: 'vagrant', 30 | rubies: ["1.9.3-p374"], 31 | global: "1.9.3-p374", 32 | gems: { 33 | "1.9.3-p374" => [ 34 | { name: "bundler" } 35 | ] 36 | } 37 | }] 38 | }, 39 | git: { version: '1.8.3.2' } 40 | } 41 | end 42 | 43 | # Disable automatic box update checking. If you disable this, then 44 | # boxes will only be checked for updates when the user runs 45 | # `vagrant box outdated`. This is not recommended. 46 | # config.vm.box_check_update = false 47 | 48 | # Create a forwarded port mapping which allows access to a specific port 49 | # within the machine from a port on the host machine. In the example below, 50 | # accessing "localhost:8080" will access port 80 on the guest machine. 51 | # config.vm.network "forwarded_port", guest: 80, host: 8080 52 | 53 | # Create a private network, which allows host-only access to the machine 54 | # using a specific IP. 55 | # config.vm.network "private_network", ip: "192.168.33.10" 56 | 57 | # Create a public network, which generally matched to bridged network. 58 | # Bridged networks make the machine appear as another physical device on 59 | # your network. 60 | # config.vm.network "public_network" 61 | 62 | # If true, then any SSH connections made will enable agent forwarding. 63 | # Default value: false 64 | # config.ssh.forward_agent = true 65 | 66 | # Share an additional folder to the guest VM. The first argument is 67 | # the path on the host to the actual folder. The second argument is 68 | # the path on the guest to mount the folder. And the optional third 69 | # argument is a set of non-required options. 70 | # config.vm.synced_folder "../data", "/vagrant_data" 71 | 72 | # Provider-specific configuration so you can fine-tune various 73 | # backing providers for Vagrant. These expose provider-specific options. 74 | # Example for VirtualBox: 75 | # 76 | # config.vm.provider "virtualbox" do |vb| 77 | # # Don't boot with headless mode 78 | # vb.gui = true 79 | # 80 | # # Use VBoxManage to customize the VM. For example to change memory: 81 | # vb.customize ["modifyvm", :id, "--memory", "1024"] 82 | # end 83 | # 84 | # View the documentation for the provider you're using for more 85 | # information on available options. 86 | 87 | # Enable provisioning with CFEngine. CFEngine Community packages are 88 | # automatically installed. For example, configure the host as a 89 | # policy server and optionally a policy file to run: 90 | # 91 | # config.vm.provision "cfengine" do |cf| 92 | # cf.am_policy_hub = true 93 | # # cf.run_file = "motd.cf" 94 | # end 95 | # 96 | # You can also configure and bootstrap a client to an existing 97 | # policy server: 98 | # 99 | # config.vm.provision "cfengine" do |cf| 100 | # cf.policy_server_address = "10.0.2.15" 101 | # end 102 | 103 | # Enable provisioning with Puppet stand alone. Puppet manifests 104 | # are contained in a directory path relative to this Vagrantfile. 105 | # You will need to create the manifests directory and a manifest in 106 | # the file default.pp in the manifests_path directory. 107 | # 108 | # config.vm.provision "puppet" do |puppet| 109 | # puppet.manifests_path = "manifests" 110 | # puppet.manifest_file = "default.pp" 111 | # end 112 | 113 | # Enable provisioning with chef solo, specifying a cookbooks path, roles 114 | # path, and data_bags path (all relative to this Vagrantfile), and adding 115 | # some recipes and/or roles. 116 | # 117 | # config.vm.provision "chef_solo" do |chef| 118 | # chef.cookbooks_path = "../my-recipes/cookbooks" 119 | # chef.roles_path = "../my-recipes/roles" 120 | # chef.data_bags_path = "../my-recipes/data_bags" 121 | # chef.add_recipe "mysql" 122 | # chef.add_role "web" 123 | # 124 | # # You may also specify custom JSON attributes: 125 | # chef.json = { mysql_password: "foo" } 126 | # end 127 | 128 | # Enable provisioning with chef server, specifying the chef server URL, 129 | # and the path to the validation key (relative to this Vagrantfile). 130 | # 131 | # The Opscode Platform uses HTTPS. Substitute your organization for 132 | # ORGNAME in the URL and validation key. 133 | # 134 | # If you have your own Chef Server, use the appropriate URL, which may be 135 | # HTTP instead of HTTPS depending on your configuration. Also change the 136 | # validation key to validation.pem. 137 | # 138 | # config.vm.provision "chef_client" do |chef| 139 | # chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME" 140 | # chef.validation_key_path = "ORGNAME-validator.pem" 141 | # end 142 | # 143 | # If you're using the Opscode platform, your validator client is 144 | # ORGNAME-validator, replacing ORGNAME with your organization name. 145 | # 146 | # If you have your own Chef Server, the default validation client name is 147 | # chef-validator, unless you changed the configuration. 148 | # 149 | # chef.validation_client_name = "ORGNAME-validator" 150 | end 151 | -------------------------------------------------------------------------------- /build-aux/install-sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # install - install a program, script, or datafile 3 | 4 | scriptversion=2011-11-20.07; # UTC 5 | 6 | # This originates from X11R5 (mit/util/scripts/install.sh), which was 7 | # later released in X11R6 (xc/config/util/install.sh) with the 8 | # following copyright and license. 9 | # 10 | # Copyright (C) 1994 X Consortium 11 | # 12 | # Permission is hereby granted, free of charge, to any person obtaining a copy 13 | # of this software and associated documentation files (the "Software"), to 14 | # deal in the Software without restriction, including without limitation the 15 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 16 | # sell copies of the Software, and to permit persons to whom the Software is 17 | # furnished to do so, subject to the following conditions: 18 | # 19 | # The above copyright notice and this permission notice shall be included in 20 | # all copies or substantial portions of the Software. 21 | # 22 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 26 | # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- 27 | # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | # 29 | # Except as contained in this notice, the name of the X Consortium shall not 30 | # be used in advertising or otherwise to promote the sale, use or other deal- 31 | # ings in this Software without prior written authorization from the X Consor- 32 | # tium. 33 | # 34 | # 35 | # FSF changes to this file are in the public domain. 36 | # 37 | # Calling this script install-sh is preferred over install.sh, to prevent 38 | # 'make' implicit rules from creating a file called install from it 39 | # when there is no Makefile. 40 | # 41 | # This script is compatible with the BSD install script, but was written 42 | # from scratch. 43 | 44 | nl=' 45 | ' 46 | IFS=" "" $nl" 47 | 48 | # set DOITPROG to echo to test this script 49 | 50 | # Don't use :- since 4.3BSD and earlier shells don't like it. 51 | doit=${DOITPROG-} 52 | if test -z "$doit"; then 53 | doit_exec=exec 54 | else 55 | doit_exec=$doit 56 | fi 57 | 58 | # Put in absolute file names if you don't have them in your path; 59 | # or use environment vars. 60 | 61 | chgrpprog=${CHGRPPROG-chgrp} 62 | chmodprog=${CHMODPROG-chmod} 63 | chownprog=${CHOWNPROG-chown} 64 | cmpprog=${CMPPROG-cmp} 65 | cpprog=${CPPROG-cp} 66 | mkdirprog=${MKDIRPROG-mkdir} 67 | mvprog=${MVPROG-mv} 68 | rmprog=${RMPROG-rm} 69 | stripprog=${STRIPPROG-strip} 70 | 71 | posix_glob='?' 72 | initialize_posix_glob=' 73 | test "$posix_glob" != "?" || { 74 | if (set -f) 2>/dev/null; then 75 | posix_glob= 76 | else 77 | posix_glob=: 78 | fi 79 | } 80 | ' 81 | 82 | posix_mkdir= 83 | 84 | # Desired mode of installed file. 85 | mode=0755 86 | 87 | chgrpcmd= 88 | chmodcmd=$chmodprog 89 | chowncmd= 90 | mvcmd=$mvprog 91 | rmcmd="$rmprog -f" 92 | stripcmd= 93 | 94 | src= 95 | dst= 96 | dir_arg= 97 | dst_arg= 98 | 99 | copy_on_change=false 100 | no_target_directory= 101 | 102 | usage="\ 103 | Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE 104 | or: $0 [OPTION]... SRCFILES... DIRECTORY 105 | or: $0 [OPTION]... -t DIRECTORY SRCFILES... 106 | or: $0 [OPTION]... -d DIRECTORIES... 107 | 108 | In the 1st form, copy SRCFILE to DSTFILE. 109 | In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. 110 | In the 4th, create DIRECTORIES. 111 | 112 | Options: 113 | --help display this help and exit. 114 | --version display version info and exit. 115 | 116 | -c (ignored) 117 | -C install only if different (preserve the last data modification time) 118 | -d create directories instead of installing files. 119 | -g GROUP $chgrpprog installed files to GROUP. 120 | -m MODE $chmodprog installed files to MODE. 121 | -o USER $chownprog installed files to USER. 122 | -s $stripprog installed files. 123 | -t DIRECTORY install into DIRECTORY. 124 | -T report an error if DSTFILE is a directory. 125 | 126 | Environment variables override the default commands: 127 | CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG 128 | RMPROG STRIPPROG 129 | " 130 | 131 | while test $# -ne 0; do 132 | case $1 in 133 | -c) ;; 134 | 135 | -C) copy_on_change=true;; 136 | 137 | -d) dir_arg=true;; 138 | 139 | -g) chgrpcmd="$chgrpprog $2" 140 | shift;; 141 | 142 | --help) echo "$usage"; exit $?;; 143 | 144 | -m) mode=$2 145 | case $mode in 146 | *' '* | *' '* | *' 147 | '* | *'*'* | *'?'* | *'['*) 148 | echo "$0: invalid mode: $mode" >&2 149 | exit 1;; 150 | esac 151 | shift;; 152 | 153 | -o) chowncmd="$chownprog $2" 154 | shift;; 155 | 156 | -s) stripcmd=$stripprog;; 157 | 158 | -t) dst_arg=$2 159 | # Protect names problematic for 'test' and other utilities. 160 | case $dst_arg in 161 | -* | [=\(\)!]) dst_arg=./$dst_arg;; 162 | esac 163 | shift;; 164 | 165 | -T) no_target_directory=true;; 166 | 167 | --version) echo "$0 $scriptversion"; exit $?;; 168 | 169 | --) shift 170 | break;; 171 | 172 | -*) echo "$0: invalid option: $1" >&2 173 | exit 1;; 174 | 175 | *) break;; 176 | esac 177 | shift 178 | done 179 | 180 | if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then 181 | # When -d is used, all remaining arguments are directories to create. 182 | # When -t is used, the destination is already specified. 183 | # Otherwise, the last argument is the destination. Remove it from $@. 184 | for arg 185 | do 186 | if test -n "$dst_arg"; then 187 | # $@ is not empty: it contains at least $arg. 188 | set fnord "$@" "$dst_arg" 189 | shift # fnord 190 | fi 191 | shift # arg 192 | dst_arg=$arg 193 | # Protect names problematic for 'test' and other utilities. 194 | case $dst_arg in 195 | -* | [=\(\)!]) dst_arg=./$dst_arg;; 196 | esac 197 | done 198 | fi 199 | 200 | if test $# -eq 0; then 201 | if test -z "$dir_arg"; then 202 | echo "$0: no input file specified." >&2 203 | exit 1 204 | fi 205 | # It's OK to call 'install-sh -d' without argument. 206 | # This can happen when creating conditional directories. 207 | exit 0 208 | fi 209 | 210 | if test -z "$dir_arg"; then 211 | do_exit='(exit $ret); exit $ret' 212 | trap "ret=129; $do_exit" 1 213 | trap "ret=130; $do_exit" 2 214 | trap "ret=141; $do_exit" 13 215 | trap "ret=143; $do_exit" 15 216 | 217 | # Set umask so as not to create temps with too-generous modes. 218 | # However, 'strip' requires both read and write access to temps. 219 | case $mode in 220 | # Optimize common cases. 221 | *644) cp_umask=133;; 222 | *755) cp_umask=22;; 223 | 224 | *[0-7]) 225 | if test -z "$stripcmd"; then 226 | u_plus_rw= 227 | else 228 | u_plus_rw='% 200' 229 | fi 230 | cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; 231 | *) 232 | if test -z "$stripcmd"; then 233 | u_plus_rw= 234 | else 235 | u_plus_rw=,u+rw 236 | fi 237 | cp_umask=$mode$u_plus_rw;; 238 | esac 239 | fi 240 | 241 | for src 242 | do 243 | # Protect names problematic for 'test' and other utilities. 244 | case $src in 245 | -* | [=\(\)!]) src=./$src;; 246 | esac 247 | 248 | if test -n "$dir_arg"; then 249 | dst=$src 250 | dstdir=$dst 251 | test -d "$dstdir" 252 | dstdir_status=$? 253 | else 254 | 255 | # Waiting for this to be detected by the "$cpprog $src $dsttmp" command 256 | # might cause directories to be created, which would be especially bad 257 | # if $src (and thus $dsttmp) contains '*'. 258 | if test ! -f "$src" && test ! -d "$src"; then 259 | echo "$0: $src does not exist." >&2 260 | exit 1 261 | fi 262 | 263 | if test -z "$dst_arg"; then 264 | echo "$0: no destination specified." >&2 265 | exit 1 266 | fi 267 | dst=$dst_arg 268 | 269 | # If destination is a directory, append the input filename; won't work 270 | # if double slashes aren't ignored. 271 | if test -d "$dst"; then 272 | if test -n "$no_target_directory"; then 273 | echo "$0: $dst_arg: Is a directory" >&2 274 | exit 1 275 | fi 276 | dstdir=$dst 277 | dst=$dstdir/`basename "$src"` 278 | dstdir_status=0 279 | else 280 | # Prefer dirname, but fall back on a substitute if dirname fails. 281 | dstdir=` 282 | (dirname "$dst") 2>/dev/null || 283 | expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ 284 | X"$dst" : 'X\(//\)[^/]' \| \ 285 | X"$dst" : 'X\(//\)$' \| \ 286 | X"$dst" : 'X\(/\)' \| . 2>/dev/null || 287 | echo X"$dst" | 288 | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ 289 | s//\1/ 290 | q 291 | } 292 | /^X\(\/\/\)[^/].*/{ 293 | s//\1/ 294 | q 295 | } 296 | /^X\(\/\/\)$/{ 297 | s//\1/ 298 | q 299 | } 300 | /^X\(\/\).*/{ 301 | s//\1/ 302 | q 303 | } 304 | s/.*/./; q' 305 | ` 306 | 307 | test -d "$dstdir" 308 | dstdir_status=$? 309 | fi 310 | fi 311 | 312 | obsolete_mkdir_used=false 313 | 314 | if test $dstdir_status != 0; then 315 | case $posix_mkdir in 316 | '') 317 | # Create intermediate dirs using mode 755 as modified by the umask. 318 | # This is like FreeBSD 'install' as of 1997-10-28. 319 | umask=`umask` 320 | case $stripcmd.$umask in 321 | # Optimize common cases. 322 | *[2367][2367]) mkdir_umask=$umask;; 323 | .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; 324 | 325 | *[0-7]) 326 | mkdir_umask=`expr $umask + 22 \ 327 | - $umask % 100 % 40 + $umask % 20 \ 328 | - $umask % 10 % 4 + $umask % 2 329 | `;; 330 | *) mkdir_umask=$umask,go-w;; 331 | esac 332 | 333 | # With -d, create the new directory with the user-specified mode. 334 | # Otherwise, rely on $mkdir_umask. 335 | if test -n "$dir_arg"; then 336 | mkdir_mode=-m$mode 337 | else 338 | mkdir_mode= 339 | fi 340 | 341 | posix_mkdir=false 342 | case $umask in 343 | *[123567][0-7][0-7]) 344 | # POSIX mkdir -p sets u+wx bits regardless of umask, which 345 | # is incompatible with FreeBSD 'install' when (umask & 300) != 0. 346 | ;; 347 | *) 348 | tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ 349 | trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 350 | 351 | if (umask $mkdir_umask && 352 | exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 353 | then 354 | if test -z "$dir_arg" || { 355 | # Check for POSIX incompatibilities with -m. 356 | # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or 357 | # other-writable bit of parent directory when it shouldn't. 358 | # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. 359 | ls_ld_tmpdir=`ls -ld "$tmpdir"` 360 | case $ls_ld_tmpdir in 361 | d????-?r-*) different_mode=700;; 362 | d????-?--*) different_mode=755;; 363 | *) false;; 364 | esac && 365 | $mkdirprog -m$different_mode -p -- "$tmpdir" && { 366 | ls_ld_tmpdir_1=`ls -ld "$tmpdir"` 367 | test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" 368 | } 369 | } 370 | then posix_mkdir=: 371 | fi 372 | rmdir "$tmpdir/d" "$tmpdir" 373 | else 374 | # Remove any dirs left behind by ancient mkdir implementations. 375 | rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null 376 | fi 377 | trap '' 0;; 378 | esac;; 379 | esac 380 | 381 | if 382 | $posix_mkdir && ( 383 | umask $mkdir_umask && 384 | $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" 385 | ) 386 | then : 387 | else 388 | 389 | # The umask is ridiculous, or mkdir does not conform to POSIX, 390 | # or it failed possibly due to a race condition. Create the 391 | # directory the slow way, step by step, checking for races as we go. 392 | 393 | case $dstdir in 394 | /*) prefix='/';; 395 | [-=\(\)!]*) prefix='./';; 396 | *) prefix='';; 397 | esac 398 | 399 | eval "$initialize_posix_glob" 400 | 401 | oIFS=$IFS 402 | IFS=/ 403 | $posix_glob set -f 404 | set fnord $dstdir 405 | shift 406 | $posix_glob set +f 407 | IFS=$oIFS 408 | 409 | prefixes= 410 | 411 | for d 412 | do 413 | test X"$d" = X && continue 414 | 415 | prefix=$prefix$d 416 | if test -d "$prefix"; then 417 | prefixes= 418 | else 419 | if $posix_mkdir; then 420 | (umask=$mkdir_umask && 421 | $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break 422 | # Don't fail if two instances are running concurrently. 423 | test -d "$prefix" || exit 1 424 | else 425 | case $prefix in 426 | *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; 427 | *) qprefix=$prefix;; 428 | esac 429 | prefixes="$prefixes '$qprefix'" 430 | fi 431 | fi 432 | prefix=$prefix/ 433 | done 434 | 435 | if test -n "$prefixes"; then 436 | # Don't fail if two instances are running concurrently. 437 | (umask $mkdir_umask && 438 | eval "\$doit_exec \$mkdirprog $prefixes") || 439 | test -d "$dstdir" || exit 1 440 | obsolete_mkdir_used=true 441 | fi 442 | fi 443 | fi 444 | 445 | if test -n "$dir_arg"; then 446 | { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && 447 | { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && 448 | { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || 449 | test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 450 | else 451 | 452 | # Make a couple of temp file names in the proper directory. 453 | dsttmp=$dstdir/_inst.$$_ 454 | rmtmp=$dstdir/_rm.$$_ 455 | 456 | # Trap to clean up those temp files at exit. 457 | trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 458 | 459 | # Copy the file name to the temp name. 460 | (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && 461 | 462 | # and set any options; do chmod last to preserve setuid bits. 463 | # 464 | # If any of these fail, we abort the whole thing. If we want to 465 | # ignore errors from any of these, just make sure not to ignore 466 | # errors from the above "$doit $cpprog $src $dsttmp" command. 467 | # 468 | { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && 469 | { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && 470 | { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && 471 | { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && 472 | 473 | # If -C, don't bother to copy if it wouldn't change the file. 474 | if $copy_on_change && 475 | old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && 476 | new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && 477 | 478 | eval "$initialize_posix_glob" && 479 | $posix_glob set -f && 480 | set X $old && old=:$2:$4:$5:$6 && 481 | set X $new && new=:$2:$4:$5:$6 && 482 | $posix_glob set +f && 483 | 484 | test "$old" = "$new" && 485 | $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 486 | then 487 | rm -f "$dsttmp" 488 | else 489 | # Rename the file to the real destination. 490 | $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || 491 | 492 | # The rename failed, perhaps because mv can't rename something else 493 | # to itself, or perhaps because mv is so ancient that it does not 494 | # support -f. 495 | { 496 | # Now remove or move aside any old file at destination location. 497 | # We try this two ways since rm can't unlink itself on some 498 | # systems and the destination file might be busy for other 499 | # reasons. In this case, the final cleanup might fail but the new 500 | # file should still install successfully. 501 | { 502 | test ! -f "$dst" || 503 | $doit $rmcmd -f "$dst" 2>/dev/null || 504 | { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && 505 | { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } 506 | } || 507 | { echo "$0: cannot unlink or rename $dst" >&2 508 | (exit 1); exit 1 509 | } 510 | } && 511 | 512 | # Now rename the file to the real destination. 513 | $doit $mvcmd "$dsttmp" "$dst" 514 | } 515 | fi || exit 1 516 | 517 | trap '' 0 518 | fi 519 | done 520 | 521 | # Local variables: 522 | # eval: (add-hook 'write-file-hooks 'time-stamp) 523 | # time-stamp-start: "scriptversion=" 524 | # time-stamp-format: "%:y-%02m-%02d.%02H" 525 | # time-stamp-time-zone: "UTC" 526 | # time-stamp-end: "; # UTC" 527 | # End: 528 | -------------------------------------------------------------------------------- /build-binary/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile description. 2 | # basic build file for mrbcc executable 3 | 4 | # project-specific macros 5 | # extension of the executable-file is modifiable(.exe .out ...) 6 | export CC = gcc 7 | export LL = gcc 8 | export AR = ar 9 | 10 | BASEDIR = ../mruby/src 11 | BUILDDIR = . 12 | TARGET := ./mrbcc_out 13 | LIBR := ../mruby/build/host/lib/libmruby.a 14 | ifeq ($(OS),Windows_NT) 15 | EXE := $(TARGET).exe 16 | else 17 | EXE := $(TARGET) 18 | endif 19 | OBJ0 := $(patsubst %.c,%.o,$(BUILDDIR)/mrbcc_out.c) 20 | OBJS := $(OBJ0) 21 | 22 | # libraries, includes 23 | LIBS = -lm 24 | INCLUDES = -I$(BASEDIR) -I$(BASEDIR)/../include 25 | 26 | # compiler, linker (gcc) 27 | ifeq ($(strip $(COMPILE_MODE)),) 28 | # default compile option 29 | COMPILE_MODE = debug 30 | endif 31 | 32 | CFLAGS = -g -fPIC 33 | 34 | ALL_CFLAGS = -Wall -Werror-implicit-function-declaration $(CFLAGS) 35 | ifeq ($(OS),Windows_NT) 36 | MAKE_FLAGS = CC=$(CC) LL=$(LL) ALL_CFLAGS="$(ALL_CFLAGS)" 37 | else 38 | MAKE_FLAGS = CC='$(CC)' LL='$(LL)' ALL_CFLAGS='$(ALL_CFLAGS)' 39 | endif 40 | 41 | ############################## 42 | # generic build targets, rules 43 | 44 | .PHONY : all 45 | all : $(LIBR) $(EXE) 46 | 47 | # executable constructed using linker from object files 48 | $(EXE) : $(LIBR) $(OBJS) $(EXTS) 49 | $(LL) -o $@ $(CFLAGS) $(OBJS) $(LIBR) $(LIBS) 50 | 51 | -include $(OBJS:.o=.d) 52 | 53 | # objects compiled from source 54 | $(OBJS) : %.o : %.c 55 | $(CC) $(ALL_CFLAGS) -MMD $(INCLUDES) -c $< -o $@ 56 | 57 | # C library compile 58 | $(LIBR) : 59 | @$(MAKE) -C $(BASEDIR) $(MAKE_FLAGS) 60 | 61 | # clean up 62 | .PHONY : clean #cleandep 63 | clean : 64 | $(MAKE) clean -C ../mruby/mrblib $(MAKE_FLAGS) 65 | @echo "make: removing targets, objects and depend files of `pwd`" 66 | rm -f $(EXE) $(OBJS) 67 | rm -f $(OBJS:.o=.d) 68 | -------------------------------------------------------------------------------- /build-binary/c_files: -------------------------------------------------------------------------------- 1 | ../build/c_files -------------------------------------------------------------------------------- /build-binary/mrbcc_out.c: -------------------------------------------------------------------------------- 1 | #include "mruby.h" 2 | #include "mruby/proc.h" 3 | #include "mruby/class.h" 4 | #include "mruby/array.h" 5 | #include "mruby/hash.h" 6 | #include "mruby/dump.h" 7 | #include "mruby/range.h" 8 | #include "mruby/compile.h" 9 | #include "mruby/variable.h" 10 | #include "mruby/numeric.h" 11 | #include "mruby/string.h" 12 | #include "mruby/error.h" 13 | #include "mruby/opcode.h" // for OP_L_CAPTURE used in OP_LAMBDA 14 | #include "../mruby/src/mrb_throw.h" 15 | #include "../mruby/src/value_array.h" 16 | #include 17 | #include 18 | #include 19 | 20 | //#define MRBB_COMPAT_INTERPRETER 21 | 22 | #include "c_files/mrbb_struct.c" 23 | #include "c_files/debug.c" 24 | #include "c_files/modified_defines.c" 25 | #include "c_files/vm_extern.c" 26 | #include "c_files/vm.c" 27 | #include "c_files/exception.c" 28 | #include "c_files/vm_changed.c" 29 | #include "c_files/proc.c" 30 | #include "c_files/method_dispatch.c" 31 | 32 | struct RProc *interpreted_proc_call = 0; 33 | 34 | // compiled code 35 | #include "c_files/out.c" 36 | #include 37 | 38 | extern mrb_value mrbb_exec_entry_point(mrb_state *mrb, mrb_value recv) { 39 | mrb_callinfo *ci; 40 | struct RProc *p; 41 | int ai = mrb->arena_idx; 42 | struct mrb_jmpbuf *prev_jmp = mrb->jmp; 43 | struct mrb_jmpbuf c_jmp; 44 | mrb_value result; 45 | 46 | MRB_TRY(&c_jmp) { 47 | mrbb_rescue_push(mrb, &c_jmp); 48 | } 49 | MRB_CATCH(&c_jmp) { 50 | mrbb_rescue_pop(mrb); 51 | mrb->jmp = prev_jmp; 52 | printf("Uncaught exception:\n"); 53 | mrb_p(mrb, mrb_obj_value(mrb->exc)); 54 | return mrb_obj_value(mrb->exc); 55 | } 56 | MRB_END_EXC(&c_jmp); 57 | 58 | 59 | if (!mrb->c->stack) { 60 | stack_init(mrb); 61 | } 62 | 63 | // Patch Proc 64 | { 65 | struct RProc *m = mrb_proc_new_cfunc(mrb, mrbb_proc_call); 66 | interpreted_proc_call = mrb_method_search_vm(mrb, &mrb->proc_class, mrb_intern_cstr(mrb, "call")); 67 | mrb_define_method_raw(mrb, mrb->proc_class, mrb_intern_cstr(mrb, "call"), m); 68 | mrb_define_method_raw(mrb, mrb->proc_class, mrb_intern_cstr(mrb, "[]"), m); 69 | } 70 | 71 | mrb->c->ci->nregs = 0; 72 | /* prepare stack */ 73 | ci = cipush(mrb); 74 | ci->acc = -1; 75 | ci->mid = mrb_intern_cstr(mrb, ""); 76 | ci->stackent = mrb->c->stack; 77 | ci->argc = 0; 78 | if (mrb_obj_id(recv) == mrb_obj_id(mrb_top_self(mrb))) 79 | ci->target_class = mrb->object_class; 80 | else 81 | ci->target_class = mrb_class(mrb, recv); 82 | 83 | /* prepare stack */ 84 | mrb->c->stack[0] = recv; 85 | 86 | p = mrbb_proc_new(mrb, script_entry_point); 87 | p->target_class = ci->target_class; 88 | ci->proc = p; 89 | 90 | result = p->body.func(mrb, recv); 91 | mrb->arena_idx = ai; 92 | mrb_gc_protect(mrb, result); 93 | 94 | mrb->c->stack = mrb->c->ci->stackent; 95 | cipop(mrb); 96 | 97 | mrbb_rescue_pop(mrb); 98 | 99 | return result; 100 | } 101 | 102 | int 103 | main(int argc, char **argv) 104 | { 105 | mrb_state *mrb = mrb_open(); 106 | 107 | if (mrb == NULL) { 108 | fprintf(stderr, "Invalid mrb_state, exiting driver"); 109 | return EXIT_FAILURE; 110 | } 111 | 112 | mrbb_exec_entry_point(mrb, mrb_top_self(mrb)); 113 | 114 | mrb_close(mrb); 115 | 116 | // TODO: unload .so 117 | //dlclose(handle); // gotta keep a global array of loaded sos and not close 118 | return EXIT_SUCCESS; 119 | } 120 | 121 | -------------------------------------------------------------------------------- /build/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile description. 2 | # basic build file for mrbcc executable 3 | 4 | # project-specific macros 5 | # extension of the executable-file is modifiable(.exe .out ...) 6 | export CC = gcc 7 | export LL = gcc 8 | export AR = ar 9 | 10 | BASEDIR = ../mruby/src 11 | BUILDDIR = . 12 | TARGET := ./mrbcc_out 13 | LIBR := ../mruby/build/host/lib/libmruby.a 14 | ifeq ($(OS),Windows_NT) 15 | EXE := $(TARGET).dll 16 | else 17 | EXE := $(TARGET).so 18 | endif 19 | OBJ0 := $(patsubst %.c,%.o,$(BUILDDIR)/mrbcc_out.c) 20 | OBJS := $(OBJ0) 21 | 22 | # libraries, includes 23 | LIBS = -lm 24 | INCLUDES = -I$(BASEDIR) -I$(BASEDIR)/../include 25 | 26 | # compiler, linker (gcc) 27 | ifeq ($(strip $(COMPILE_MODE)),) 28 | # default compile option 29 | COMPILE_MODE = debug 30 | endif 31 | 32 | ifeq ($(COMPILE_MODE),debug) 33 | CFLAGS = -g -O3 -fPIC 34 | else ifeq ($(COMPILE_MODE),release) 35 | CFLAGS = -O3 -fPIC 36 | else ifeq ($(COMPILE_MODE),small) 37 | CFLAGS = -Os -fPIC 38 | endif 39 | 40 | ALL_CFLAGS = -Wall -Werror-implicit-function-declaration $(CFLAGS) 41 | ifeq ($(OS),Windows_NT) 42 | MAKE_FLAGS = CC=$(CC) LL=$(LL) ALL_CFLAGS="$(ALL_CFLAGS)" 43 | else 44 | MAKE_FLAGS = CC='$(CC)' LL='$(LL)' ALL_CFLAGS='$(ALL_CFLAGS)' 45 | endif 46 | 47 | ############################## 48 | # generic build targets, rules 49 | 50 | .PHONY : all 51 | all : $(LIBR) $(EXE) 52 | 53 | # executable constructed using linker from object files 54 | $(EXE) : $(LIBR) $(OBJS) $(EXTS) 55 | $(LL) -shared -o $@ $(CFLAGS) $(OBJS) $(LIBR) $(LIBS) 56 | 57 | -include $(OBJS:.o=.d) 58 | 59 | # objects compiled from source 60 | $(OBJS) : %.o : %.c 61 | $(CC) $(ALL_CFLAGS) -MMD $(INCLUDES) -c $< -o $@ 62 | 63 | # C library compile 64 | $(LIBR) : 65 | @$(MAKE) -C $(BASEDIR) $(MAKE_FLAGS) 66 | 67 | # clean up 68 | .PHONY : clean #cleandep 69 | clean : 70 | $(MAKE) clean -C ../mruby/mrblib $(MAKE_FLAGS) 71 | @echo "make: removing targets, objects and depend files of `pwd`" 72 | rm -f $(EXE) $(OBJS) 73 | rm -f $(OBJS:.o=.d) 74 | -------------------------------------------------------------------------------- /build/c_files/debug.c: -------------------------------------------------------------------------------- 1 | #define dpf printf 2 | 3 | 4 | void stackdump(mrb_state *mrb, int n) 5 | { 6 | int i = 0; 7 | printf("Stackdump: \n"); 8 | for (; ic->stack[i]); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /build/c_files/exception.c: -------------------------------------------------------------------------------- 1 | static void 2 | mrbb_ecall(mrb_state *mrb, struct RProc *p) 3 | { 4 | mrb_callinfo *ci; 5 | mrb_value *self = mrb->c->stack; 6 | struct RObject *exc; 7 | int ai = mrb->arena_idx; 8 | 9 | ci = cipush(mrb); 10 | ci->stackent = mrb->c->stack; 11 | ci->mid = ci[-1].mid; 12 | ci->acc = -1; 13 | ci->argc = 0; 14 | ci->proc = p; 15 | ci->target_class = p->target_class; 16 | mrb->c->stack = mrb->c->stack + ci[-1].nregs; 17 | exc = mrb->exc; mrb->exc = 0; 18 | p->body.func(mrb, *self); 19 | mrb->arena_idx = ai; 20 | mrb->c->stack = mrb->c->ci->stackent; 21 | cipop(mrb); 22 | if (!mrb->exc) mrb->exc = exc; 23 | } 24 | 25 | void mrbb_stop(mrb_state *mrb) { 26 | printf("goto L_STOP\n"); 27 | exit(0); 28 | } 29 | 30 | /* 31 | Because c_jmp is a variable that is local to the scope of 32 | the OP_ONERR code part, it is necessary to copy it, because 33 | it may otherwise get overwritten. Specifically this happened 34 | on Linux, but not on OSX. 35 | */ 36 | static mrb_code c_rescue_code = MKOP_A(OP_STOP, 0); 37 | 38 | void mrbb_rescue_push(mrb_state *mrb, struct mrb_jmpbuf *c_jmp) { 39 | struct mrb_jmpbuf *c_jmp_copy = (struct mrb_jmpbuf *)malloc(sizeof(struct mrb_jmpbuf)); 40 | if (mrb->c->rsize <= mrb->c->ci->ridx) { 41 | if (mrb->c->rsize == 0) mrb->c->rsize = 16; 42 | else mrb->c->rsize *= 2; 43 | mrb->c->rescue = (mrb_code **)mrb_realloc(mrb, mrb->c->rescue, sizeof(mrb_code*) * mrb->c->rsize); 44 | } 45 | memmove(c_jmp_copy, c_jmp, sizeof(struct mrb_jmpbuf)); 46 | #ifdef MRBB_COMPAT_INTERPRETER 47 | struct mrb_rescue_code *rescue_code = (struct mrb_rescue_code *)malloc(sizeof(*rescue_code)); 48 | rescue_code->code = c_rescue_code; // TODO: At some point we want to make this an interpreted function that can call our c exception handler function 49 | rescue_code->jmp = c_jmp_copy; 50 | mrb->c->rescue[mrb->c->ci->ridx++] = (mrb_code *) rescue_code; 51 | #else 52 | mrb->c->rescue[mrb->c->ci->ridx++] = (mrb_code *) c_jmp_copy; 53 | #endif 54 | mrb->jmp = c_jmp_copy; 55 | } 56 | 57 | void mrbb_raise(mrb_state *mrb) { 58 | mrb_exc_raise(mrb, mrb_obj_value(mrb->exc)); 59 | } 60 | 61 | void mrbb_ecall_in_rescue(mrb_state *mrb, struct RProc *ensure) { 62 | struct mrb_jmpbuf *prev = mrb->jmp; 63 | struct mrb_jmpbuf buf; 64 | 65 | MRB_TRY(&buf) { 66 | mrb->jmp = &buf; 67 | mrbb_ecall(mrb, ensure); 68 | } 69 | MRB_CATCH(&buf) { 70 | // mrb->exc is already set, we are already in rescue, nothing to do 71 | } 72 | MRB_END_EXC(&buf); 73 | 74 | mrb->jmp = prev; 75 | } 76 | 77 | void mrbb_onerr_setup(mrb_state *mrb) { 78 | // stolen from OP_RETURN 79 | mrb_callinfo *ci; 80 | int eidx; 81 | ci = mrb->c->ci; 82 | eidx = ci->eidx; 83 | 84 | if (ci == mrb->c->cibase) { 85 | return; 86 | } 87 | while (eidx > ci[-1].eidx) { 88 | mrbb_ecall_in_rescue(mrb, mrb->c->ensure[--eidx]); 89 | } 90 | while (ci[0].ridx == ci[-1].ridx) { 91 | cipop(mrb); 92 | ci = mrb->c->ci; 93 | mrb->c->stack = ci[1].stackent; 94 | if (ci > mrb->c->cibase) { 95 | while (eidx > ci[-1].eidx) { 96 | mrbb_ecall_in_rescue(mrb, mrb->c->ensure[--eidx]); 97 | } 98 | } 99 | else if (ci == mrb->c->cibase) { 100 | if (ci->ridx == 0) { 101 | if (mrb->c == mrb->root_c) { 102 | // TODO regs = 103 | mrb->c->stack = mrb->c->stbase; 104 | mrbb_stop(mrb); 105 | } 106 | else { 107 | struct mrb_context *c = mrb->c; 108 | 109 | mrb->c = c->prev; 110 | c->prev = NULL; 111 | mrbb_raise(mrb); 112 | } 113 | } 114 | break; 115 | } 116 | } 117 | } 118 | 119 | int mrbb_is_c_rescue(mrb_code *entry) { 120 | #ifdef MRBB_COMPAT_INTERPRETER 121 | if (entry && *entry == c_rescue_code) 122 | return 1; 123 | else 124 | return 0; 125 | #else 126 | return 1; 127 | #endif 128 | } 129 | 130 | /* 131 | Must free memory allocated for the mrb_jmpbuf. 132 | */ 133 | 134 | void mrbb_rescue_pop(mrb_state *mrb) { 135 | mrbb_onerr_setup(mrb); 136 | if (mrb->c->ci->ridx == 0) { 137 | mrb->exc = mrb_obj_ptr(mrb_exc_new_str(mrb, E_RUNTIME_ERROR, mrb_str_new_cstr(mrb, "mrbb_rescue_pop: reached end of rescue handlers"))); 138 | mrbb_raise(mrb); 139 | } 140 | #ifdef MRBB_COMPAT_INTERPRETER 141 | if (mrbb_is_c_rescue(mrb->c->rescue[mrb->c->ci->ridx-1])) { 142 | struct mrb_rescue_code *rescue_code = (struct mrb_rescue_code *) mrb->c->rescue[mrb->c->ci->ridx-1]; 143 | mrb->c->ci->ridx--; 144 | free(rescue_code->jmp); 145 | free(rescue_code); 146 | } else { 147 | mrb->c->ci->ridx--; 148 | // TODO: call interpreted handler 149 | } 150 | #else 151 | struct mrb_jmpbuf *rescue = (struct mrb_jmpbuf *)mrb->c->rescue[mrb->c->ci->ridx-1]; 152 | mrb->c->ci->ridx--; 153 | free(rescue); 154 | #endif 155 | 156 | if (mrb->c->ci->ridx > 0) { 157 | #ifdef MRBB_COMPAT_INTERPRETER 158 | if (mrbb_is_c_rescue(mrb->c->rescue[mrb->c->ci->ridx-1])) { 159 | struct mrb_rescue_code *rescue_code = (struct mrb_rescue_code *) mrb->c->rescue[mrb->c->ci->ridx-1]; 160 | mrb->jmp = rescue_code->jmp; 161 | } 162 | #else 163 | mrb->jmp = (struct mrb_jmpbuf *)mrb->c->rescue[mrb->c->ci->ridx-1]; 164 | #endif 165 | } else { 166 | mrb->jmp = NULL; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /build/c_files/method_dispatch.c: -------------------------------------------------------------------------------- 1 | static mrb_code c_break_code = MKOP_A(OP_STOP, 0); 2 | 3 | void 4 | mrbb_send_setup_stack_extend(mrb_state *mrb, mrb_value self, mrb_value *argv, int argc) 5 | { 6 | mrb->c->stack = mrb->c->stack + mrb->c->ci[-1].nregs; 7 | 8 | stack_extend(mrb, argc + 2, 0); 9 | mrb->c->stack[0] = self; 10 | if (argc > 0) { 11 | stack_copy(mrb->c->stack+1, argv, argc); 12 | } 13 | mrb->c->stack[argc+1] = argv[argc]; 14 | } 15 | 16 | mrb_value 17 | mrbb_send_r(mrb_state *mrb, mrb_sym mid, int n, mrb_value **regs_ptr, int a, int sendb) 18 | { 19 | struct RProc *m; 20 | struct RClass *c; 21 | mrb_callinfo *ci; 22 | mrb_value val; 23 | mrb_value *regs = *regs_ptr; 24 | mrb_value recv = regs[a]; 25 | 26 | if (!sendb) { 27 | if (n == CALL_MAXARGS) { 28 | SET_NIL_VALUE(regs[a+2]); 29 | } 30 | else { 31 | SET_NIL_VALUE(regs[a+n+1]); 32 | } 33 | } 34 | c = mrb_class(mrb, recv); 35 | m = mrb_method_search_vm(mrb, &c, mid); 36 | if (!m) { 37 | mrb_value sym = mrb_symbol_value(mid); 38 | 39 | mid = mrb_intern_lit(mrb, "method_missing"); 40 | m = mrb_method_search_vm(mrb, &c, mid); 41 | if (n == CALL_MAXARGS) { 42 | mrb_ary_unshift(mrb, regs[a+1], sym); 43 | } 44 | else { 45 | value_move(regs+a+2, regs+a+1, ++n); 46 | regs[a+1] = sym; 47 | } 48 | } 49 | 50 | /* push callinfo */ 51 | ci = cipush(mrb); 52 | ci->mid = mid; 53 | ci->proc = m; 54 | ci->stackent = mrb->c->stack; 55 | if (c->tt == MRB_TT_ICLASS) { 56 | ci->target_class = c->c; 57 | } 58 | else { 59 | ci->target_class = c; 60 | } 61 | ci->acc = -1; // TODO ? 62 | 63 | /* prepare stack */ 64 | mrb->c->stack += a; 65 | 66 | ptrdiff_t cioff = mrb->c->ci - mrb->c->cibase; 67 | 68 | if (MRB_PROC_CFUNC_P(m)) { 69 | if (n == CALL_MAXARGS) { // TODO this is not necessary for MRBCC func? 70 | ci->argc = -1; 71 | ci->nregs = 3; 72 | } 73 | else { 74 | ci->argc = n; 75 | ci->nregs = n + 2; 76 | } 77 | 78 | int ai = mrb->arena_idx; 79 | val = m->body.func(mrb, recv); 80 | mrb->arena_idx = ai; 81 | } 82 | else { 83 | #ifdef MRBB_COMPAT_INTERPRETER 84 | mrb_irep *irep = m->body.irep; 85 | ci->nregs = irep->nregs; 86 | if (n == CALL_MAXARGS) { 87 | ci->argc = -1; 88 | stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3); 89 | } 90 | else { 91 | ci->argc = n; 92 | stack_extend(mrb, irep->nregs, n+2); 93 | } 94 | val = mrb_run(mrb, m, recv); 95 | if (mrb->exc) { 96 | printf("TODO: exception raised from mruby code:\n"); 97 | mrb_p(mrb, mrb_obj_value(mrb->exc));fflush(stdout); 98 | } 99 | if (mrb->c->ci[1].pc != &c_break_code) { 100 | // because OP_RETURN will cipop() 101 | cioff--; 102 | } 103 | #else 104 | mrb->exc = mrb_obj_ptr(mrb_exc_new_str(mrb, E_RUNTIME_ERROR, mrb_str_new_cstr(mrb, "Attempt to call interpreter but MRBB_COMPAT_INTERPRETER is not enabled. (mrbb_send_r)"))); 105 | mrbb_raise(mrb); 106 | #endif 107 | } 108 | 109 | // BREAK 110 | if ((mrb->c->ci - mrb->c->cibase) != cioff) { 111 | mrb->c->stack = ci->stackent; // not needed really? safeguard if somehow stack is accessed before break returns to proper location (shouldn't happen) 112 | // We NEED to cipop the first time, but not after that 113 | if (mrb->c->ci->proc != (struct RProc *) -1) { 114 | cipop(mrb); 115 | cipush(mrb); 116 | } 117 | mrb->c->ci->proc = (struct RProc *)-1; 118 | } else if (MRB_PROC_CFUNC_P(m)) { 119 | mrb->c->stack = ci->stackent; 120 | cipop(mrb); 121 | } 122 | if (mrb->exc) mrbb_raise(mrb); // we can do this before cipop... see OP_SEND 123 | 124 | *regs_ptr = mrb->c->stack; // stack_extend might realloc stack 125 | return val; 126 | } 127 | 128 | void 129 | mrbb_send(mrb_state *mrb, mrb_sym mid, int argc, mrb_value **regs_ptr, int a, int sendb) 130 | { 131 | mrb->c->stack[a] = mrbb_send_r(mrb, mid, argc, regs_ptr, a, sendb); 132 | } 133 | -------------------------------------------------------------------------------- /build/c_files/modified_defines.c: -------------------------------------------------------------------------------- 1 | #define NEXT 2 | 3 | // removed first line from OP_CMP, also goto L_SEND in default (TODO) 4 | #define OP_CMP(op) do {\ 5 | int result, skip = 0;\ 6 | /* need to check if - is overridden */\ 7 | switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {\ 8 | case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):\ 9 | result = OP_CMP_BODY(op,mrb_fixnum,mrb_fixnum);\ 10 | break;\ 11 | case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):\ 12 | result = OP_CMP_BODY(op,mrb_fixnum,mrb_float);\ 13 | break;\ 14 | case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):\ 15 | result = OP_CMP_BODY(op,mrb_float,mrb_fixnum);\ 16 | break;\ 17 | case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):\ 18 | result = OP_CMP_BODY(op,mrb_float,mrb_float);\ 19 | break;\ 20 | default:\ 21 | mrbb_send(mrb, mrb_intern_cstr(mrb, #op ), 1, ®s, a, 0);\ 22 | skip = 1;\ 23 | }\ 24 | if (!skip) {\ 25 | if (result) {\ 26 | SET_TRUE_VALUE(regs[a]);\ 27 | }\ 28 | else {\ 29 | SET_FALSE_VALUE(regs[a]);\ 30 | }\ 31 | }\ 32 | } while(0) 33 | -------------------------------------------------------------------------------- /build/c_files/mrbb_struct.c: -------------------------------------------------------------------------------- 1 | struct mrb_rescue_code { 2 | mrb_code code; 3 | struct mrb_jmpbuf *jmp; 4 | }; 5 | -------------------------------------------------------------------------------- /build/c_files/proc.c: -------------------------------------------------------------------------------- 1 | #define MRB_PROC_MRBCFUNC 512 2 | #define MRB_PROC_MRBCFUNC_P(p) ((p)->flags & MRB_PROC_MRBCFUNC) 3 | 4 | extern struct RProc *interpreted_proc_call; 5 | 6 | mrb_value mrbb_proc_call(mrb_state *mrb, mrb_value self) 7 | { 8 | mrb_callinfo *ci; 9 | mrb_value recv = mrb->c->stack[0]; 10 | struct RProc *m = mrb_proc_ptr(recv); 11 | int ai = mrb->arena_idx; 12 | ci = mrb->c->ci; 13 | 14 | // If interpreted Proc called from interpreted code 15 | if (mrb->c->ci->pc) { 16 | #ifdef MRBB_COMPAT_INTERPRETER 17 | // See OP_SEND, we are migrating from MRB_PROC_CFUNC_P if body to else body 18 | mrb_irep *irep = interpreted_proc_call->body.irep; 19 | mrb_value *stackent = mrb->c->stack; 20 | if (ci->argc == -1) { 21 | stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3); 22 | } 23 | else { 24 | stack_extend(mrb, irep->nregs, ci->argc+2); 25 | } 26 | 27 | ci->proc = interpreted_proc_call; 28 | ci->nregs = irep->nregs; 29 | 30 | ci = cipush(mrb); 31 | ci->target_class = 0; 32 | ci->pc = irep->iseq; 33 | ci->stackent = stackent; 34 | 35 | return mrb->c->stack[0]; 36 | #else 37 | mrb->exc = mrb_obj_ptr(mrb_exc_new_str(mrb, E_RUNTIME_ERROR, mrb_str_new_cstr(mrb, "Attempt to call interpreter but MRBB_COMPAT_INTERPRETER is not enabled. (mrbb_proc_call)"))); 38 | mrbb_raise(mrb); 39 | #endif 40 | } else { 41 | /* replace callinfo */ 42 | ci->target_class = m->target_class; 43 | ci->proc = m; 44 | if (m->env) { 45 | if (m->env->mid) { 46 | ci->mid = m->env->mid; 47 | } 48 | if (!m->env->stack) { 49 | m->env->stack = mrb->c->stack; 50 | } 51 | } 52 | 53 | /* prepare stack */ 54 | if (MRB_PROC_CFUNC_P(m)) { 55 | recv = m->body.func(mrb, m->env->stack[0]); 56 | mrb->arena_idx = ai; 57 | //if (mrb->exc) mrbb_raise(mrb); 58 | /* pop stackpos */ 59 | // already done by funcall 60 | //ci = mrb->c->ci; 61 | //mrb->c->stack = ci->stackent; 62 | //regs[ci->acc] = recv; 63 | //pc = ci->pc; 64 | //cipop(mrb); 65 | } else { 66 | #ifdef MRBB_COMPAT_INTERPRETER 67 | mrb_irep *irep = m->body.irep; 68 | if (!irep) { 69 | mrb->c->stack[0] = mrb_nil_value(); 70 | return mrb_nil_value(); 71 | } 72 | ci->nregs = irep->nregs; 73 | if (ci->argc < 0) { 74 | stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3); 75 | } 76 | else { 77 | stack_extend(mrb, irep->nregs, ci->argc+2); 78 | } 79 | mrb->c->stack[0] = m->env->stack[0]; 80 | recv = mrb_run(mrb, m, recv); 81 | #else 82 | mrb->exc = mrb_obj_ptr(mrb_exc_new_str(mrb, E_RUNTIME_ERROR, mrb_str_new_cstr(mrb, "Attempt to call interpreter but MRBB_COMPAT_INTERPRETER is not enabled. (mrbb_proc_call)"))); 83 | mrbb_raise(mrb); 84 | #endif 85 | } 86 | } 87 | 88 | // TODO: only overwrite this method for Cfunc procs 89 | // so we let OP_CALL handle interpreted funcs 90 | return recv; 91 | } 92 | 93 | struct RProc *mrbb_proc_new(mrb_state* mrb, mrb_func_t cfunc) 94 | { 95 | struct RProc *p = mrb_proc_new_cfunc(mrb, cfunc); 96 | 97 | p->flags |= MRB_PROC_MRBCFUNC; 98 | 99 | return p; 100 | } 101 | 102 | /* Soon: 103 | 104 | struct RProc *mrbb_closure_new(mrb_state* mrb, mrb_func_t cfunc, unsigned int nlocals) 105 | { 106 | struct RProc *p = mrb_closure_new_cfunc(mrb, cfunc); 107 | 108 | p->flags |= MRB_PROC_MRBCFUNC; 109 | 110 | return p; 111 | } 112 | */ 113 | struct RProc *mrbb_closure_new(mrb_state* mrb, mrb_func_t cfunc, unsigned int nlocals) 114 | { 115 | struct RProc *p = mrbb_proc_new(mrb, cfunc); 116 | 117 | // stolen from mrb_closure_new() 118 | struct REnv *e; 119 | 120 | if (!mrb->c->ci->env) { 121 | e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, (struct RClass*)mrb->c->ci->proc->env); 122 | e->flags= nlocals; 123 | e->mid = mrb->c->ci->mid; 124 | e->cioff = mrb->c->ci - mrb->c->cibase; 125 | e->stack = mrb->c->stack; 126 | mrb->c->ci->env = e; 127 | } 128 | else { 129 | e = mrb->c->ci->env; 130 | } 131 | p->env = e; 132 | 133 | return p; 134 | } 135 | -------------------------------------------------------------------------------- /build/c_files/vm.c: -------------------------------------------------------------------------------- 1 | /* Things from vm.c that I need */ 2 | /* Only things without modification from vm.c (identical) */ 3 | #define CALL_MAXARGS 127 4 | 5 | #define ARENA_RESTORE(mrb,ai) (mrb)->arena_idx = (ai) 6 | 7 | #define TYPES2(a,b) ((((uint16_t)(a))<<8)|(((uint16_t)(b))&0xff)) 8 | #define OP_MATH_BODY(op,v1,v2) do {\ 9 | v1(regs[a]) = v1(regs[a]) op v2(regs[a+1]);\ 10 | } while(0) 11 | 12 | #define OP_CMP_BODY(op,v1,v2) (v1(regs[a]) op v2(regs[a+1])) 13 | 14 | #define STACK_INIT_SIZE 128 15 | #define CALLINFO_INIT_SIZE 32 16 | 17 | #define CI_ACC_SKIP -1 18 | #define CI_ACC_DIRECT -2 19 | 20 | static inline void 21 | stack_copy(mrb_value *dst, const mrb_value *src, size_t size) 22 | { 23 | while (size-- > 0) { 24 | *dst++ = *src++; 25 | } 26 | } 27 | 28 | static void 29 | stack_init(mrb_state *mrb) 30 | { 31 | struct mrb_context *c = mrb->c; 32 | 33 | /* mrb_assert(mrb->stack == NULL); */ 34 | c->stbase = (mrb_value *)mrb_calloc(mrb, STACK_INIT_SIZE, sizeof(mrb_value)); 35 | c->stend = c->stbase + STACK_INIT_SIZE; 36 | c->stack = c->stbase; 37 | 38 | /* mrb_assert(ci == NULL); */ 39 | c->cibase = (mrb_callinfo *)mrb_calloc(mrb, CALLINFO_INIT_SIZE, sizeof(mrb_callinfo)); 40 | c->ciend = c->cibase + CALLINFO_INIT_SIZE; 41 | c->ci = c->cibase; 42 | c->ci->target_class = mrb->object_class; 43 | c->ci->stackent = c->stack; 44 | } 45 | 46 | static void 47 | argnum_error(mrb_state *mrb, mrb_int num) 48 | { 49 | mrb_value exc; 50 | mrb_value str; 51 | 52 | if (mrb->c->ci->mid) { 53 | str = mrb_format(mrb, "'%S': wrong number of arguments (%S for %S)", 54 | mrb_sym2str(mrb, mrb->c->ci->mid), 55 | mrb_fixnum_value(mrb->c->ci->argc), mrb_fixnum_value(num)); 56 | } 57 | else { 58 | str = mrb_format(mrb, "wrong number of arguments (%S for %S)", 59 | mrb_fixnum_value(mrb->c->ci->argc), mrb_fixnum_value(num)); 60 | } 61 | exc = mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str); 62 | mrb->exc = mrb_obj_ptr(exc); 63 | } 64 | 65 | static inline mrb_bool 66 | is_strict(mrb_state *mrb, struct REnv *e) 67 | { 68 | int cioff = e->cioff; 69 | 70 | if (MRB_ENV_STACK_SHARED_P(e) && mrb->c->cibase[cioff].proc && 71 | MRB_PROC_STRICT_P(mrb->c->cibase[cioff].proc)) { 72 | return TRUE; 73 | } 74 | return FALSE; 75 | } 76 | 77 | static mrb_callinfo* 78 | cipush(mrb_state *mrb) 79 | { 80 | struct mrb_context *c = mrb->c; 81 | mrb_callinfo *ci = c->ci; 82 | 83 | int eidx = ci->eidx; 84 | int ridx = ci->ridx; 85 | 86 | if (ci + 1 == c->ciend) { 87 | size_t size = ci - c->cibase; 88 | 89 | c->cibase = (mrb_callinfo *)mrb_realloc(mrb, c->cibase, sizeof(mrb_callinfo)*size*2); 90 | c->ci = c->cibase + size; 91 | c->ciend = c->cibase + size * 2; 92 | } 93 | ci = ++c->ci; 94 | ci->eidx = eidx; 95 | ci->ridx = ridx; 96 | ci->env = 0; 97 | ci->pc = 0; 98 | ci->err = 0; 99 | ci->proc = 0; 100 | 101 | return ci; 102 | } 103 | 104 | static void 105 | cipop(mrb_state *mrb) 106 | { 107 | struct mrb_context *c = mrb->c; 108 | 109 | if (c->ci->env) { 110 | struct REnv *e = c->ci->env; 111 | size_t len = (size_t)MRB_ENV_STACK_LEN(e); 112 | mrb_value *p = (mrb_value *)mrb_malloc(mrb, sizeof(mrb_value)*len); 113 | 114 | MRB_ENV_UNSHARE_STACK(e); 115 | if (len > 0) { 116 | stack_copy(p, e->stack, len); 117 | } 118 | e->stack = p; 119 | mrb_write_barrier(mrb, (struct RBasic *)e); 120 | } 121 | 122 | c->ci--; 123 | } 124 | 125 | static void 126 | envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase) 127 | { 128 | mrb_callinfo *ci = mrb->c->cibase; 129 | 130 | if (newbase == oldbase) return; 131 | while (ci <= mrb->c->ci) { 132 | struct REnv *e = ci->env; 133 | if (e && MRB_ENV_STACK_SHARED_P(e)) { 134 | ptrdiff_t off = e->stack - oldbase; 135 | 136 | e->stack = newbase + off; 137 | } 138 | ci->stackent = newbase + (ci->stackent - oldbase); 139 | ci++; 140 | } 141 | } 142 | 143 | typedef enum { 144 | LOCALJUMP_ERROR_RETURN = 0, 145 | LOCALJUMP_ERROR_BREAK = 1, 146 | LOCALJUMP_ERROR_YIELD = 2 147 | } localjump_error_kind; 148 | 149 | static void 150 | localjump_error(mrb_state *mrb, localjump_error_kind kind) 151 | { 152 | char kind_str[3][7] = { "return", "break", "yield" }; 153 | char kind_str_len[] = { 6, 5, 5 }; 154 | static const char lead[] = "unexpected "; 155 | mrb_value msg; 156 | mrb_value exc; 157 | 158 | msg = mrb_str_buf_new(mrb, sizeof(lead) + 7); 159 | mrb_str_cat(mrb, msg, lead, sizeof(lead) - 1); 160 | mrb_str_cat(mrb, msg, kind_str[kind], kind_str_len[kind]); 161 | exc = mrb_exc_new_str(mrb, E_LOCALJUMP_ERROR, msg); 162 | mrb->exc = mrb_obj_ptr(exc); 163 | } 164 | 165 | static struct REnv* 166 | uvenv(mrb_state *mrb, int up) 167 | { 168 | struct REnv *e = mrb->c->ci->proc->env; 169 | 170 | while (up--) { 171 | if (!e) return NULL; 172 | e = (struct REnv*)e->c; 173 | } 174 | return e; 175 | } 176 | 177 | static struct REnv* 178 | top_env(mrb_state *mrb, struct RProc *proc) 179 | { 180 | struct REnv *e = proc->env; 181 | 182 | if (is_strict(mrb, e)) return e; 183 | while (e->c) { 184 | e = (struct REnv*)e->c; 185 | if (is_strict(mrb, e)) return e; 186 | } 187 | return e; 188 | } 189 | 190 | static inline void 191 | stack_clear(mrb_value *from, size_t count) 192 | { 193 | #ifndef MRB_NAN_BOXING 194 | const mrb_value mrb_value_zero = { { 0 } }; 195 | 196 | while (count-- > 0) { 197 | *from++ = mrb_value_zero; 198 | } 199 | #else 200 | while (count-- > 0) { 201 | SET_NIL_VALUE(*from); 202 | from++; 203 | } 204 | #endif 205 | } 206 | 207 | static void 208 | init_new_stack_space(mrb_state *mrb, int room, int keep) 209 | { 210 | if (room > keep) { 211 | /* do not leave uninitialized malloc region */ 212 | stack_clear(&(mrb->c->stack[keep]), room - keep); 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /build/c_files/vm_changed.c: -------------------------------------------------------------------------------- 1 | 2 | /* Define amount of linear stack growth. */ 3 | #ifndef MRB_STACK_GROWTH 4 | #define MRB_STACK_GROWTH 128 5 | #endif 6 | 7 | /* Maximum stack depth. Should be set lower on memory constrained systems. 8 | The value below allows about 60000 recursive calls in the simplest case. */ 9 | #ifndef MRB_STACK_MAX 10 | #define MRB_STACK_MAX 10000//((1<<18) - MRB_STACK_GROWTH) 11 | #endif 12 | // TODO: need to test if default MRB_STACK_MAX still leads to segfault 13 | // if not then use the default 14 | 15 | static void 16 | stack_extend(mrb_state *mrb, int room, int keep) 17 | { 18 | if (mrb->c->stack + room >= mrb->c->stend) { 19 | mrb_value *oldbase = mrb->c->stbase; 20 | int size = mrb->c->stend - mrb->c->stbase; 21 | int off = mrb->c->stack - mrb->c->stbase; 22 | 23 | #ifdef MRB_STACK_EXTEND_DOUBLING 24 | if (room <= size) 25 | size *= 2; 26 | else 27 | size += room; 28 | #else 29 | /* Use linear stack growth. 30 | It is slightly slower than doubling the stack space, 31 | but it saves memory on small devices. */ 32 | if (room <= MRB_STACK_GROWTH) 33 | size += MRB_STACK_GROWTH; 34 | else 35 | size += room; 36 | #endif 37 | 38 | mrb->c->stbase = (mrb_value *)mrb_realloc(mrb, mrb->c->stbase, sizeof(mrb_value) * size); 39 | mrb->c->stack = mrb->c->stbase + off; 40 | mrb->c->stend = mrb->c->stbase + size; 41 | envadjust(mrb, oldbase, mrb->c->stbase); 42 | 43 | /* Raise an exception if the new stack size will be too large, 44 | to prevent infinite recursion. However, do this only after resizing the stack, so mrb_raise has stack space to work with. */ 45 | if (size > MRB_STACK_MAX) { 46 | init_new_stack_space(mrb, room, keep); 47 | // mrb_raise(mrb, E_SYSSTACK_ERROR, "stack level too deep. (limit=" MRB_STRINGIZE(MRB_STACK_MAX) ")"); 48 | const char *msg = "stack level too deep."; // TODO: tell limit 49 | mrb_value exc = mrb_exc_new(mrb, E_RUNTIME_ERROR, msg, strlen(msg)); 50 | mrb->exc = (struct RObject*)mrb_obj_ptr(exc); 51 | mrbb_raise(mrb); 52 | } 53 | } 54 | init_new_stack_space(mrb, room, keep); 55 | } 56 | -------------------------------------------------------------------------------- /build/c_files/vm_extern.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrbrdo/mruby_cc/bcd47cf18eef972a8490412b5964d3794ba81f7f/build/c_files/vm_extern.c -------------------------------------------------------------------------------- /build/mrbcc_out.c: -------------------------------------------------------------------------------- 1 | #include "mruby.h" 2 | #include "mruby/proc.h" 3 | #include "mruby/class.h" 4 | #include "mruby/array.h" 5 | #include "mruby/hash.h" 6 | #include "mruby/dump.h" 7 | #include "mruby/range.h" 8 | #include "mruby/compile.h" 9 | #include "mruby/variable.h" 10 | #include "mruby/numeric.h" 11 | #include "mruby/string.h" 12 | #include "mruby/error.h" 13 | #include "mruby/opcode.h" // for OP_L_CAPTURE used in OP_LAMBDA 14 | #include "../mruby/src/mrb_throw.h" 15 | #include "../mruby/src/value_array.h" 16 | #include 17 | #include 18 | #include 19 | 20 | //#define MRBB_COMPAT_INTERPRETER 21 | 22 | #include "c_files/mrbb_struct.c" 23 | #include "c_files/debug.c" 24 | #include "c_files/modified_defines.c" 25 | #include "c_files/vm_extern.c" 26 | #include "c_files/vm.c" 27 | #include "c_files/exception.c" 28 | #include "c_files/vm_changed.c" 29 | #include "c_files/proc.c" 30 | #include "c_files/method_dispatch.c" 31 | 32 | struct RProc *interpreted_proc_call = 0; 33 | 34 | // compiled code 35 | #include "c_files/out.c" 36 | 37 | extern mrb_value mrbb_exec_entry_point(mrb_state *mrb, mrb_value recv) { 38 | mrb_callinfo *ci; 39 | struct RProc *p; 40 | int ai = mrb->arena_idx; 41 | struct mrb_jmpbuf *prev_jmp = mrb->jmp; 42 | struct mrb_jmpbuf c_jmp; 43 | mrb_value result; 44 | 45 | MRB_TRY(&c_jmp) { 46 | mrbb_rescue_push(mrb, &c_jmp); 47 | } 48 | MRB_CATCH(&c_jmp) { 49 | mrbb_rescue_pop(mrb); 50 | mrb->jmp = prev_jmp; 51 | printf("Uncaught exception:\n"); 52 | mrb_p(mrb, mrb_obj_value(mrb->exc)); 53 | return mrb_obj_value(mrb->exc); 54 | } 55 | MRB_END_EXC(&c_jmp); 56 | 57 | if (!mrb->c->stack) { 58 | stack_init(mrb); 59 | } 60 | 61 | // Patch Proc 62 | { 63 | // TODO: this should be done only once, if multiple modules are loaded - could use instance variable to remember between modules 64 | struct RProc *m = mrb_proc_new_cfunc(mrb, mrbb_proc_call); 65 | interpreted_proc_call = mrb_method_search_vm(mrb, &mrb->proc_class, mrb_intern_cstr(mrb, "call")); 66 | mrb_define_method_raw(mrb, mrb->proc_class, mrb_intern_cstr(mrb, "call"), m); 67 | mrb_define_method_raw(mrb, mrb->proc_class, mrb_intern_cstr(mrb, "[]"), m); 68 | } 69 | 70 | mrb->c->ci->nregs = 0; 71 | /* prepare stack */ 72 | ci = cipush(mrb); 73 | ci->acc = -1; 74 | ci->mid = mrb_intern_cstr(mrb, ""); 75 | ci->stackent = mrb->c->stack; 76 | ci->argc = 0; 77 | if (mrb_obj_id(recv) == mrb_obj_id(mrb_top_self(mrb))) 78 | ci->target_class = mrb->object_class; 79 | else 80 | ci->target_class = mrb_class(mrb, recv); 81 | 82 | /* prepare stack */ 83 | mrb->c->stack[0] = recv; 84 | 85 | p = mrbb_proc_new(mrb, script_entry_point); 86 | p->target_class = ci->target_class; 87 | ci->proc = p; 88 | 89 | result = p->body.func(mrb, recv); 90 | mrb->arena_idx = ai; 91 | mrb_gc_protect(mrb, result); 92 | 93 | mrb->c->stack = mrb->c->ci->stackent; 94 | cipop(mrb); 95 | 96 | mrbb_rescue_pop(mrb); 97 | 98 | return result; 99 | } 100 | -------------------------------------------------------------------------------- /compile: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 3 | ruby "$DIR/mrbcc/mrbcc.rb" $* 4 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([mrbcc],[0.1]) 2 | AC_CONFIG_AUX_DIR([build-aux]) 3 | 4 | dnl Needed to compile mruby. 5 | AC_PROG_CC() 6 | AC_PROG_YACC() 7 | 8 | dnl Ensure ruby (at least 1.9). 9 | AC_PROG_SED() 10 | AC_PROG_GREP() 11 | AC_ARG_WITH(ruby, 12 | AC_HELP_STRING([--with-ruby=[[[RUBY]]]],[absolute path to ruby executable]), 13 | [RUBY="$withval"]) 14 | AC_CHECK_PROG(RUBY, ruby, ruby) 15 | test -z "$RUBY" && AC_MSG_FAILURE([no acceptable ruby interpreter found in \$PATH]) 16 | changequote(<<,>>) 17 | ruby_version=`$RUBY --version 2>&1 | $GREP "^ruby " | $SED 's/^ruby \([^ ]*\)\ .*$/\1/'` 18 | ruby_major_version=`echo $ruby_version | sed 's/^\([0-9]*\)\.[0-9]*.*/\1/'` 19 | ruby_minor_version=`echo $ruby_version | sed 's/^[0-9]*\.\([0-9]*\).*/\1/'` 20 | changequote([,]) 21 | test -z "$ruby_major_version" && AC_MSG_FAILURE([ruby in \$PATH does not work]) 22 | test -z "$ruby_minor_version" && AC_MSG_FAILURE([ruby in \$PATH does not work]) 23 | test "$ruby_major_version" -lt 1 && AC_MSG_FAILURE([ruby version in \$PATH is $ruby_version; must be at least 1.9]) 24 | if test "$ruby_major_version" -eq 1 ; then 25 | test "$ruby_minor_version" -lt 9 && AC_MSG_FAILURE([ruby version in \$PATH is 26 | $ruby_version; must be at least 1.9]) 27 | fi 28 | dnl Ensure ruby gems: activesupport i18n. 29 | AC_MSG_CHECKING([for ruby gem activesupport]) 30 | echo 'gem = Gem::Specification.find_by_name("activesupport")' | $RUBY 2>/dev/null ; gem_activesupport=$? 31 | if test "$gem_activesupport" -eq 0 ; then AC_MSG_RESULT([ok]) ; 32 | else AC_MSG_RESULT([no]) ; AC_MSG_FAILURE([ruby gem activesupport not found]) ; fi 33 | AC_MSG_CHECKING([for ruby gem i18n]) 34 | echo 'gem = Gem::Specification.find_by_name("i18n")' | $RUBY 2>/dev/null ; gem_i18n=$? 35 | if test "$gem_i18n" -eq 0 ; then AC_MSG_RESULT([ok]) ; 36 | else AC_MSG_RESULT([no]) ; AC_MSG_FAILURE([ruby gem i18n not found]) ; fi 37 | 38 | dnl Ensure git. 39 | AC_ARG_WITH(git, 40 | AC_HELP_STRING([--with-git=[[[GIT]]]],[absolute path to git executable]), 41 | [GIT="$withval"]) 42 | AC_CHECK_PROG(GIT, git, git) 43 | test -z "$GIT" && AC_MSG_WARN([git not found; mruby source must be provided manually]) 44 | 45 | AC_PROG_INSTALL 46 | 47 | AC_CONFIG_FILES([Makefile]) 48 | AC_OUTPUT 49 | -------------------------------------------------------------------------------- /crashing_tests.rb: -------------------------------------------------------------------------------- 1 | GC.disable 2 | 3 | #include 'mruby/test/assert.rb' 4 | -------------------------------------------------------------------------------- /custom_tests.rb: -------------------------------------------------------------------------------- 1 | GC.disable 2 | 3 | #include 'mruby/test/assert.rb' 4 | 5 | assert('break between interpreter and compiler (eval does not carry break info further)') do 6 | $pr2 = proc do 7 | break 5 8 | end 9 | 10 | $pr = proc do 11 | (1..2).map do |j| 12 | eval("$pr2.call") 13 | end 14 | end 15 | 16 | v = (1..2).map do |i| 17 | break $pr.call 18 | end 19 | 20 | assert_equal(v, 5) 21 | end 22 | -------------------------------------------------------------------------------- /dbg_recompile: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd build 3 | make 4 | cd .. 5 | 6 | mv build/mrbcc_out.so ./out.so 7 | -------------------------------------------------------------------------------- /failing_tests.rb: -------------------------------------------------------------------------------- 1 | GC.disable 2 | 3 | #include 'mruby/test/assert.rb' 4 | 5 | assert('GC.enable') do 6 | assert_false GC.disable 7 | assert_true GC.enable 8 | assert_false GC.enable 9 | end 10 | 11 | assert('GC.disable') do 12 | begin 13 | assert_false GC.disable 14 | assert_true GC.disable 15 | ensure 16 | GC.enable 17 | end 18 | end 19 | 20 | assert('GC.interval_ratio=') do 21 | origin = GC.interval_ratio 22 | begin 23 | assert_equal 150, (GC.interval_ratio = 150) 24 | ensure 25 | GC.interval_ratio = origin 26 | end 27 | end 28 | 29 | assert('GC.step_ratio=') do 30 | origin = GC.step_ratio 31 | begin 32 | assert_equal 150, (GC.step_ratio = 150) 33 | ensure 34 | GC.step_ratio = origin 35 | end 36 | end 37 | 38 | assert('GC.generational_mode=') do 39 | origin = GC.generational_mode 40 | begin 41 | assert_false (GC.generational_mode = false) 42 | assert_true (GC.generational_mode = true) 43 | assert_true (GC.generational_mode = true) 44 | ensure 45 | GC.generational_mode = origin 46 | end 47 | end 48 | 49 | # because there is no reflection on local variables in compiled code yet (but it is possible) 50 | assert('Kernel.local_variables', '15.3.1.2.7') do 51 | a, b = 0, 1 52 | a += b 53 | 54 | vars = Kernel.local_variables.sort 55 | assert_equal [:a, :b, :vars], vars 56 | 57 | Proc.new { 58 | c = 2 59 | vars = Kernel.local_variables.sort 60 | assert_equal [:a, :b, :c, :vars], vars 61 | }.call 62 | end 63 | 64 | # because mruby tries to read it from iseq, for cfunc is hardcoded to -1 65 | # we need to override Proc#arity and read it some other way (perhaps use hash or try to store it in struct RProc somewhere) 66 | assert('Proc#arity', '15.2.17.4.2') do 67 | a = Proc.new {|x, y|}.arity 68 | b = Proc.new {|x, *y, z|}.arity 69 | c = Proc.new {|x=0, y|}.arity 70 | d = Proc.new {|(x, y), z=0|}.arity 71 | 72 | assert_equal 2, a 73 | assert_equal(-3, b) 74 | assert_equal 1, c 75 | assert_equal 1, d 76 | end 77 | 78 | assert('__FILE__') do 79 | file = __FILE__ 80 | assert_true 'test/t/syntax.rb' == file || 'test\t\syntax.rb' == file 81 | end 82 | 83 | assert('__LINE__') do 84 | assert_equal 7, __LINE__ 85 | end 86 | 87 | # these only do not work because Mrbtest is defined in driver.c specifically for testing 88 | assert('Integer#+', '15.2.8.3.1') do 89 | a = 1+1 90 | b = 1+1.0 91 | 92 | assert_equal 2, a 93 | assert_equal 2.0, b 94 | 95 | assert_raise(TypeError){ 0+nil } 96 | assert_raise(TypeError){ 1+nil } 97 | 98 | c = Mrbtest::FIXNUM_MAX + 1 99 | d = Mrbtest::FIXNUM_MAX.__send__(:+, 1) 100 | e = Mrbtest::FIXNUM_MAX + 1.0 101 | assert_equal Float, c.class 102 | assert_equal Float, d.class 103 | assert_float e, c 104 | assert_float e, d 105 | end 106 | 107 | assert('Integer#-', '15.2.8.3.2') do 108 | a = 2-1 109 | b = 2-1.0 110 | 111 | assert_equal 1, a 112 | assert_equal 1.0, b 113 | 114 | c = Mrbtest::FIXNUM_MIN - 1 115 | d = Mrbtest::FIXNUM_MIN.__send__(:-, 1) 116 | e = Mrbtest::FIXNUM_MIN - 1.0 117 | assert_equal Float, c.class 118 | assert_equal Float, d.class 119 | assert_float e, c 120 | assert_float e, d 121 | end 122 | 123 | assert('Integer#*', '15.2.8.3.3') do 124 | a = 1*1 125 | b = 1*1.0 126 | 127 | assert_equal 1, a 128 | assert_equal 1.0, b 129 | 130 | assert_raise(TypeError){ 0*nil } 131 | assert_raise(TypeError){ 1*nil } 132 | 133 | c = Mrbtest::FIXNUM_MAX * 2 134 | d = Mrbtest::FIXNUM_MAX.__send__(:*, 2) 135 | e = Mrbtest::FIXNUM_MAX * 2.0 136 | assert_equal Float, c.class 137 | assert_equal Float, d.class 138 | assert_float e, c 139 | assert_float e, d 140 | end 141 | -------------------------------------------------------------------------------- /mrbcc/codegen.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'active_support/core_ext' 3 | require 'securerandom' 4 | require_relative './rite_parser' 5 | 6 | class OpcodeParser 7 | attr_reader :name, :irep, :opcodes 8 | DEBUG_MODE = false 9 | DEBUG_MODE_VERBOSE = DEBUG_MODE && false 10 | def initialize(parser, opcodes, name, irep = nil, rep_names = nil) 11 | @name = name || "met_#{SecureRandom.hex}" 12 | @irep = irep || parser.irep 13 | @parser = parser 14 | @opcodes = opcodes 15 | @prepend_compiled_ireps = [] 16 | if irep 17 | @entry_point = false 18 | @rep_names = rep_names 19 | else 20 | @entry_point = true 21 | @rep_names = {} 22 | name_reps(@irep, @rep_names) 23 | end 24 | @rep_name = @rep_names[@irep] 25 | end 26 | 27 | def name_reps(rep, reps, start = 1) 28 | reps[rep] = start.to_s 29 | start += 1 30 | rep.reps.each do |child_rep| 31 | start = name_reps(child_rep, reps, start) 32 | end 33 | start 34 | end 35 | 36 | def label(i) 37 | @instructions_referenced[i] = true 38 | "L_#{@name.upcase}_#{i}" 39 | end 40 | 41 | def c_str_escape(str) 42 | str.gsub(/[^\w\d ]/m) do |c| 43 | "\\#{c.ord.to_s(8).rjust(3, '0')}" 44 | end 45 | end 46 | 47 | def c_comment_escape(str) 48 | # very simple for now, could be improved 49 | str.gsub(/[^\w\d ?_!#,+\.\-]/m) do |c| 50 | "*" 51 | end 52 | end 53 | 54 | def value_from_pool_to_code(val) 55 | case val 56 | when Float 57 | "mrb_float_value(mrb, #{val})" 58 | when Numeric 59 | "mrb_fixnum_value(#{val})" 60 | when String 61 | "mrb_str_new(mrb, \"#{c_str_escape(val)}\", #{val.size})" 62 | when Regexp 63 | # TODO 64 | raise 65 | else 66 | val 67 | end 68 | end 69 | 70 | def instructions_to_function(instruction_bodies) 71 | outio = StringIO.new 72 | 73 | # pool and syms 74 | if @entry_point 75 | @rep_names.each_pair do |current_irep, name| 76 | outio.write("static mrb_value _pool_#{name}[#{current_irep.pool.size}];\n") 77 | outio.write("static mrb_sym _syms_#{name}[#{current_irep.syms.size}];\n") 78 | end 79 | end 80 | 81 | @prepend_compiled_ireps.reverse.each do |str| 82 | outio.write(str) 83 | outio.write("\n") 84 | end 85 | 86 | outio.write(method_prelude) 87 | # set up pool and syms 88 | if @entry_point 89 | tabs = " " 90 | pool_size_sum = @rep_names.keys.reduce(0) { |sum, irep| sum + irep.pool.size } 91 | outio.write(" {\n") 92 | outio.write("#{tabs}mrb_value _gc_pool_protect = mrb_ary_new_capa(mrb, #{pool_size_sum});\n") 93 | outio.write("#{tabs}ai = mrb->arena_idx;\n") 94 | @rep_names.each_pair do |current_irep, rep_name| 95 | current_irep.syms.each.with_index do |sym, sym_idx| 96 | outio.write("#{tabs}_syms_#{rep_name}[#{sym_idx}] = mrb_intern(mrb, \"#{c_str_escape(sym)}\", #{sym.length});\n") 97 | #outio.write(" mrb_gc_arena_restore(mrb, ai);\n") # TODO: can remove this 98 | # TODO: make syms by name eg. _sym_print, so code can be read.. only for symbols with simple name 99 | end 100 | current_irep.pool.each.with_index do |val, pool_idx| 101 | val = value_from_pool_to_code(val) 102 | outio.write("#{tabs}_pool_#{rep_name}[#{pool_idx}] = #{val};\n") 103 | outio.write("#{tabs}mrb_ary_push(mrb, _gc_pool_protect, _pool_#{rep_name}[#{pool_idx}]);\n") 104 | outio.write("#{tabs}mrb_gc_arena_restore(mrb, ai);\n") 105 | end 106 | end 107 | outio.write(" }\n") 108 | end 109 | 110 | instruction_bodies.each.with_index do |str, idx| 111 | outio.write("\n // #{irep.iseqs[idx]}\n") 112 | 113 | if @instructions_referenced[idx] || OpcodeParser::DEBUG_MODE 114 | outio.write(" #{label(idx)}:") 115 | end 116 | 117 | outio.write("\n "); 118 | 119 | if OpcodeParser::DEBUG_MODE 120 | outio.write(" printf(\"#{c_str_escape(label(idx))}\\n\"); fflush(stdout);\n") 121 | str2 = <<-EOF 122 | printf("X#{label(idx).strip}\\nXstack ptr \%d\\n", mrb->c->stack - mrb->c->stbase); 123 | printf("Xregs ptr \%d\\n", regs - mrb->c->stack); 124 | EOF 125 | #outio.write(str2) 126 | end 127 | 128 | outio.write(str) 129 | end 130 | outio.write(method_epilogue) 131 | 132 | outio.string 133 | end 134 | 135 | def process_irep 136 | instruction_bodies = [] # C code for each opcode 137 | @instructions_referenced = [] # true or false if the opcode needs a label 138 | 139 | irep.iseqs.each.with_index do |instr, line_number| 140 | puts instr if DEBUG_MODE_VERBOSE 141 | 142 | @instr = instr 143 | @line_number = line_number 144 | @opcode = instr.opcode 145 | 146 | @instructions_referenced[@line_number] ||= false 147 | 148 | @instr_body = opcodes[@opcode].dup 149 | if respond_to?(@opcode.downcase) 150 | send(@opcode.downcase) 151 | end 152 | 153 | gsub_args 154 | 155 | # symbols 156 | @instr_body.gsub!(/syms\[([^\]]+)\]/) do 157 | "_syms_#{@rep_name}[#{$1}]/*#{c_comment_escape(@irep.syms[$1.to_i])}*/" 158 | end 159 | # string literals 160 | #@instr_body.gsub!(/mrb_str_literal\(mrb, (pool\[[^\]]+\])\)/) do 161 | # $1 162 | #end 163 | # pool 164 | @instr_body.gsub!(/pool\[([^\]]+)\]/) do 165 | "_pool_#{@rep_name}[#{$1}]/*#{c_comment_escape(@irep.pool[$1.to_i].to_s)}*/" 166 | end 167 | # raise 168 | @instr_body.gsub!("goto L_RAISE;", "mrbb_raise(mrb);") 169 | 170 | instruction_bodies[@line_number] = @instr_body 171 | 172 | end 173 | instructions_to_function(instruction_bodies) 174 | end 175 | 176 | def method_epilogue 177 | body = "\n" 178 | body += " printf(\"ERROR: Method #{c_str_escape(@name)} did not return.\\n\");\n" 179 | body += " exit(1);\n" # so we don't get warnings about no return 180 | body += "}\n" 181 | end 182 | 183 | def method_prelude 184 | prelude = File.read(File.expand_path("../codegen_rb/met_start.c", __FILE__)) 185 | prelude.gsub("FUNC_NREGS", irep.nregs.to_s). 186 | gsub("MET_NAME", @name) 187 | end 188 | 189 | def gsub_args 190 | ["GETARG_A", "GETARG_Ax"].each do |search_str| 191 | @instr_body.gsub!("#{search_str}(i)", @instr.send(search_str).to_s) 192 | end 193 | ["GETARG_B", "GETARG_Bx", "GETARG_sBx", "GETARG_b"].each do |search_str| 194 | @instr_body.gsub!("#{search_str}(i)", @instr.send(search_str).to_s) 195 | end 196 | ["GETARG_C", "GETARG_c"].each do |search_str| 197 | @instr_body.gsub!("#{search_str}(i)", @instr.send(search_str).to_s) 198 | end 199 | end 200 | 201 | def fix_lsend_2arg(met_name) 202 | @instr_body.gsub!("goto L_SEND;") do 203 | "regs[a] = mrb_funcall_with_block(mrb, regs[a], " + 204 | "mrb_intern_cstr(mrb, \"#{c_str_escape(met_name)}\"), 1, ®s[a+1], mrb_nil_value());" 205 | end 206 | end 207 | 208 | def lambda_arg_precompiled(parser, arg_name) 209 | @instr_body.gsub!("#{arg_name}(i)", parser.name) 210 | @instr_body.gsub!("GETIREP_BLK_NREGS()", parser.irep.nregs.to_s) 211 | @instr_body.gsub!("GETIREP_NLOCALS()", @irep.nlocals.to_s) 212 | end 213 | 214 | def lambda_arg(arg_name) 215 | # only for readability / easier debugging, try to include method name 216 | # TODO: doesn't work right always (infinite recursion for example, wrong name) 217 | if irep.iseqs[@line_number+1].present? && 218 | irep.iseqs[@line_number+1].opcode == "OP_METHOD" 219 | iseq_met = irep.iseqs[@line_number+1] 220 | met_name = irep.syms[iseq_met.GETARG_B] 221 | met_name = met_name.gsub(/[^\w\d_]/, "") # strip stuff like ?, ! from name 222 | met_name = "met_#{met_name}_#{SecureRandom.hex}" 223 | else 224 | met_name = nil 225 | end 226 | 227 | parser = OpcodeParser.new(@parser, opcodes, met_name, irep.reps[@instr.send(arg_name)], @rep_names) 228 | @prepend_compiled_ireps.push(parser.process_irep) 229 | lambda_arg_precompiled(parser, arg_name) 230 | parser 231 | end 232 | # OPCODES 233 | def op_exec 234 | lambda_arg("GETARG_Bx") 235 | end 236 | 237 | def op_lambda 238 | lambda_arg("GETARG_b") 239 | end 240 | 241 | def op_epush 242 | @ensure_parser = lambda_arg("GETARG_Bx") 243 | end 244 | 245 | def op_return 246 | # todo 247 | end 248 | 249 | def op_send 250 | end 251 | 252 | def op_sendb 253 | op_send 254 | end 255 | 256 | def op_sub 257 | fix_lsend_2arg("-") 258 | end 259 | 260 | def op_add 261 | fix_lsend_2arg("+") 262 | end 263 | 264 | def op_subi 265 | op_sub 266 | @instr_body.gsub!(/i = MKOP_ABC\(OP_SEND.*/, "") 267 | end 268 | 269 | def op_addi 270 | op_add 271 | @instr_body.gsub!(/i = MKOP_ABC\(OP_SEND.*/, "") 272 | end 273 | 274 | def op_mul 275 | fix_lsend_2arg("*") 276 | end 277 | 278 | def op_div 279 | fix_lsend_2arg("/") 280 | end 281 | 282 | def op_enter 283 | ax = @instr.GETARG_Ax 284 | m1 = (ax>>18)&0x1f 285 | o = (ax>>13)&0x1f 286 | r = (ax>>12)&0x1 287 | m2 = (ax>>7)&0x1f 288 | 289 | len = m1 + o + r + m2 290 | # jumps 291 | @instr_body.gsub!(/if \(o == 0\)([^;]+;)\s*else([^;]+;)/m) do 292 | if o == 0 293 | $1 294 | else 295 | $2 296 | end 297 | end 298 | @instr_body.gsub!("pc++", "goto #{label(@line_number+1)}") 299 | @instr_body.gsub!("pc += argc - m1 - m2 + 1;") do 300 | str = "switch(argc) {\n" 301 | # TODO must raise error if too little arguments? 302 | ((m1+m2)..len-1).each do |i| 303 | str += " case #{i}: goto #{label(@line_number+i-m1-m2+1)};\n" 304 | end 305 | str += "}\n" 306 | str 307 | end 308 | @instr_body.gsub!("pc += o + 1;", "goto #{label(@line_number+o+1)};") 309 | @instr_body.gsub!("JUMP;", "") 310 | end 311 | 312 | def op_jmp 313 | @instr_body = "goto #{label(@line_number + @instr.GETARG_sBx)};" 314 | end 315 | 316 | def op_jmpif 317 | tmp = "pc += GETARG_sBx(i);" 318 | @instr_body.gsub!(/#{Regexp.escape(tmp)}\s*JUMP;/, 319 | "goto #{label(@line_number + @instr.GETARG_sBx)};") 320 | end 321 | 322 | def op_onerr 323 | @instr_body.gsub!("rescue_label(GETARG_sBx(i))", 324 | label(@line_number + @instr.GETARG_sBx)) 325 | #tmp = "pc += GETARG_sBx(i);" 326 | #@instr_body.gsub!(/#{Regexp.escape(tmp)}\s*JUMP;/, 327 | # "goto #{label(@line_number + @instr.GETARG_sBx)};") 328 | end 329 | 330 | def op_jmpnot 331 | op_jmpif 332 | end 333 | 334 | def op_getcv 335 | clear_debug_err_pc 336 | end 337 | 338 | def op_getmcnst 339 | clear_debug_err_pc 340 | end 341 | 342 | def op_getconst 343 | clear_debug_err_pc 344 | end 345 | 346 | def op_stop 347 | @instr_body.gsub!("GETIREP_NLOCALS()", @irep.nlocals.to_s) 348 | end 349 | 350 | def clear_debug_err_pc 351 | @instr_body.gsub!("ERR_PC_SET(mrb, pc);", "") 352 | @instr_body.gsub!("ERR_PC_CLR(mrb);", "") 353 | end 354 | end 355 | -------------------------------------------------------------------------------- /mrbcc/codegen_rb/met_start.c: -------------------------------------------------------------------------------- 1 | mrb_value MET_NAME(mrb_state *mrb, mrb_value self) { 2 | mrb_value *regs = NULL; 3 | int ai = mrb->arena_idx; // is the same throughout the same compiled program 4 | 5 | // I have to set up my own stack 6 | { 7 | mrb_callinfo *ci = mrb->c->ci; 8 | ci->nregs = FUNC_NREGS + 2; 9 | if (ci->argc < 0) { 10 | stack_extend(mrb, (FUNC_NREGS < 3) ? 3 : FUNC_NREGS, 3); 11 | } 12 | else { 13 | stack_extend(mrb, FUNC_NREGS, ci->argc+2); 14 | } 15 | } 16 | 17 | regs = mrb->c->stack; 18 | regs[0] = self; 19 | -------------------------------------------------------------------------------- /mrbcc/codegen_rb/opcode_changed.c: -------------------------------------------------------------------------------- 1 | CASE(OP_RESCUE) { // TODO 2 | /* A R(A) := exc; clear(exc) */ 3 | SET_OBJ_VALUE(regs[GETARG_A(i)], mrb->exc); 4 | mrb->exc = 0; 5 | NEXT; 6 | } 7 | 8 | CASE(OP_SEND) { 9 | int a = GETARG_A(i); 10 | int n = GETARG_C(i); 11 | mrb_value ret; 12 | 13 | ret = mrbb_send_r(mrb, syms[GETARG_B(i)], n, ®s, a, 0); 14 | if (mrb->c->ci->proc == (struct RProc *) -1) { 15 | //cipush(mrb); 16 | return ret; 17 | } 18 | regs[a] = ret; 19 | mrb->arena_idx = ai; // TODO do we need (because of break;)? 20 | NEXT; 21 | } 22 | CASE(OP_SENDB) { 23 | int a = GETARG_A(i); 24 | int n = GETARG_C(i); 25 | mrb_value ret; 26 | 27 | ret = mrbb_send_r(mrb, syms[GETARG_B(i)], n, ®s, a, 1); 28 | if (mrb->c->ci->proc == (struct RProc *) -1) { 29 | //cipush(mrb); 30 | return ret; 31 | } 32 | regs[a] = ret; 33 | mrb->arena_idx = ai; // TODO probably can remove 34 | NEXT; 35 | } 36 | 37 | 38 | CASE(OP_STOP) { 39 | /* stop VM */ 40 | { 41 | int eidx_stop = mrb->c->ci == mrb->c->cibase ? 0 : mrb->c->ci[-1].eidx; 42 | int eidx = mrb->c->ci->eidx; 43 | while (eidx > eidx_stop) { 44 | mrbb_ecall(mrb, mrb->c->ensure[--eidx]); 45 | } 46 | } 47 | if (mrb->exc) { 48 | return mrb_obj_value(mrb->exc); 49 | } 50 | return regs[GETIREP_NLOCALS()]; 51 | } 52 | 53 | CASE(OP_RETURN) { 54 | { 55 | mrb_callinfo *ci = mrb->c->ci; 56 | int eidx = mrb->c->ci->eidx; 57 | int ridx = mrb->c->ci->ridx; 58 | mrb_value v = regs[GETARG_A(i)]; 59 | struct RProc *proc = mrb->c->ci->proc; 60 | 61 | if (mrb->exc) { 62 | mrbb_raise(mrb); 63 | } 64 | 65 | switch (GETARG_B(i)) { 66 | case OP_R_RETURN: 67 | if (proc->env || !MRB_PROC_STRICT_P(proc)) { 68 | struct REnv *e = top_env(mrb, proc); 69 | 70 | if (e->cioff < 0) { 71 | localjump_error(mrb, LOCALJUMP_ERROR_RETURN); 72 | goto L_RAISE; 73 | } 74 | mrb->c->ci = mrb->c->cibase + e->cioff; 75 | if (ci == mrb->c->cibase) { 76 | localjump_error(mrb, LOCALJUMP_ERROR_RETURN); 77 | goto L_RAISE; 78 | } 79 | break; 80 | } 81 | case OP_R_NORMAL: 82 | if (ci == mrb->c->cibase) { 83 | localjump_error(mrb, LOCALJUMP_ERROR_RETURN); 84 | goto L_RAISE; 85 | } 86 | break; 87 | case OP_R_BREAK: 88 | if (!proc->env || !MRB_ENV_STACK_SHARED_P(proc->env)) { 89 | localjump_error(mrb, LOCALJUMP_ERROR_BREAK); 90 | goto L_RAISE; 91 | } 92 | ci = mrb->c->ci; 93 | mrb->c->ci = mrb->c->cibase + proc->env->cioff + 1; 94 | while (ci > mrb->c->ci) { 95 | if (ci[-1].acc == CI_ACC_SKIP) { 96 | mrb->c->ci = ci; 97 | break; 98 | } 99 | ci--; 100 | } 101 | break; 102 | default: 103 | /* cannot happen */ 104 | break; 105 | } 106 | 107 | while (eidx > mrb->c->ci[-1].eidx) { 108 | mrbb_ecall(mrb, mrb->c->ensure[--eidx]); 109 | } 110 | 111 | if (GETARG_B(i) == OP_R_BREAK) { 112 | if (mrb->c->ci->acc != CI_ACC_SKIP) { 113 | ci--; 114 | regs = mrb->c->stack = ci->stackent; 115 | regs[ci->proc->body.irep->nlocals] = v; 116 | mrb->c->ci->stackent = ci->stackent; 117 | mrb->c->ci->pc = &c_break_code; 118 | //mrb->c->ci->pc = mrb->c->ci->pc; 119 | mrb->c->ci->target_class = 0; 120 | return mrb->c->stack[mrb->c->ci->acc]; 121 | } else { 122 | mrb->c->ci = mrb->c->cibase + proc->env->cioff + 1; 123 | } 124 | } 125 | return v; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /mrbcc/codegen_rb/opcode_err.c: -------------------------------------------------------------------------------- 1 | CASE(OP_ONERR) { 2 | /* sBx pc+=sBx on exception */ 3 | struct mrb_jmpbuf buf; 4 | int stoff = mrb->c->stack - mrb->c->stbase; 5 | int cioff = mrb->c->ci - mrb->c->cibase; 6 | 7 | MRB_TRY(&buf) { 8 | mrbb_rescue_push(mrb, &buf); 9 | } 10 | MRB_CATCH(&buf) { 11 | // go to rescue 12 | mrbb_rescue_pop(mrb); 13 | 14 | // if rescued from method that was called from this method 15 | // and didn't have its own rescue 16 | // fix global state, be careful if stbase or cibase changed 17 | mrb->c->ci = mrb->c->cibase + cioff; 18 | regs = mrb->c->stack = mrb->c->stbase + stoff; 19 | 20 | goto rescue_label(GETARG_sBx(i)); 21 | } 22 | MRB_END_EXC(&buf); 23 | 24 | NEXT; 25 | } 26 | 27 | CASE(OP_POPERR) { 28 | int a = GETARG_A(i); 29 | 30 | while (a--) { 31 | mrbb_rescue_pop(mrb); 32 | } 33 | NEXT; 34 | } 35 | 36 | CASE(OP_EPUSH) { 37 | /* Bx ensure_push(SEQ[Bx]) */ 38 | struct RProc *p; 39 | 40 | p = mrbb_closure_new(mrb, GETARG_Bx(i), (unsigned int)GETIREP_NLOCALS()); 41 | p->target_class = mrb_class(mrb, self); // TODO check why/if we need 42 | /* push ensure_stack */ 43 | if (mrb->c->esize <= mrb->c->ci->eidx) { 44 | if (mrb->c->esize == 0) mrb->c->esize = 16; 45 | else mrb->c->esize *= 2; 46 | mrb->c->ensure = (struct RProc **)mrb_realloc(mrb, mrb->c->ensure, sizeof(struct RProc*) * mrb->c->esize); 47 | } 48 | mrb->c->ensure[mrb->c->ci->eidx++] = p; 49 | ARENA_RESTORE(mrb, ai); 50 | NEXT; 51 | } 52 | 53 | CASE(OP_EPOP) { 54 | /* A A.times{ensure_pop().call} */ 55 | int a = GETARG_A(i); 56 | mrb_callinfo *ci = mrb->c->ci; 57 | int n, eidx = ci->eidx; 58 | 59 | for (n=0; n ci[-1].eidx; n++) { 60 | mrbb_ecall(mrb, mrb->c->ensure[--mrb->c->ci->eidx]); 61 | ARENA_RESTORE(mrb, ai); 62 | } 63 | NEXT; 64 | } 65 | -------------------------------------------------------------------------------- /mrbcc/codegen_rb/opcode_irep.c: -------------------------------------------------------------------------------- 1 | CASE(OP_LAMBDA) { 2 | /* A b c R(A) := lambda(SEQ[b],c) (b:c = 14:2) */ 3 | struct RProc *p; 4 | int c = GETARG_c(i); 5 | 6 | if (c & OP_L_CAPTURE) { 7 | p = mrbb_closure_new(mrb, GETARG_b(i), (unsigned int)GETIREP_NLOCALS()); 8 | } 9 | else { 10 | p = mrbb_proc_new(mrb, GETARG_b(i)); 11 | } 12 | 13 | // TODO: we need this but why? 14 | p->target_class = (mrb->c->ci) ? mrb->c->ci->target_class : 0; 15 | 16 | if ((c & OP_L_METHOD) && !(c & OP_L_CAPTURE)) { 17 | if (p->target_class->tt == MRB_TT_SCLASS) { 18 | mrb_value klass; 19 | klass = mrb_obj_iv_get(mrb, 20 | (struct RObject *)p->target_class, 21 | mrb_intern_lit(mrb, "__attached__")); 22 | p->target_class = mrb_class_ptr(klass); 23 | } 24 | } 25 | 26 | if (c & OP_L_STRICT) p->flags |= MRB_PROC_STRICT; 27 | regs[GETARG_A(i)] = mrb_obj_value(p); 28 | ARENA_RESTORE(mrb, ai); 29 | NEXT; 30 | } 31 | 32 | CASE(OP_EXEC) { 33 | /* A Bx R(A) := blockexec(R(A),SEQ[Bx]) */ 34 | int a = GETARG_A(i); 35 | mrb_callinfo *ci; 36 | mrb_value recv = regs[a]; 37 | struct RProc *p; 38 | 39 | /* prepare stack */ 40 | ci = cipush(mrb); 41 | //ci->pc = pc + 1; 42 | ci->acc = a; 43 | ci->mid = 0; 44 | ci->stackent = mrb->c->stack; 45 | ci->argc = 0; 46 | ci->target_class = mrb_class_ptr(recv); // TODO: check if we might need mrb_class() instead 47 | 48 | /* prepare stack */ 49 | mrb->c->stack += a; 50 | 51 | p = mrbb_proc_new(mrb, GETARG_Bx(i)); 52 | // p = mrb_proc_new(mrb, mrb->irep[irep->idx+GETARG_Bx(i)]); 53 | p->target_class = ci->target_class; 54 | ci->proc = p; 55 | 56 | // if (MRB_PROC_CFUNC_P(p)) { 57 | // else part removed since it is always CFUNC 58 | 59 | ci->nregs = 0; 60 | mrb->c->stack[0] = p->body.func(mrb, recv); 61 | mrb_gc_arena_restore(mrb, ai); 62 | if (mrb->exc) mrbb_raise(mrb); 63 | /* pop stackpos */ 64 | regs = mrb->c->stack = mrb->c->ci->stackent; 65 | cipop(mrb); 66 | NEXT; 67 | } 68 | -------------------------------------------------------------------------------- /mrbcc/codegen_rb/opcode_long.c: -------------------------------------------------------------------------------- 1 | 2 | CASE(OP_TAILCALL) { 3 | /* A B C return call(R(A),Sym(B),R(A+1),... ,R(A+C-1)) */ 4 | int a = GETARG_A(i); 5 | int n = GETARG_C(i); 6 | struct RProc *m; 7 | struct RClass *c; 8 | mrb_callinfo *ci; 9 | mrb_value recv; 10 | mrb_sym mid = syms[GETARG_B(i)]; 11 | 12 | recv = regs[a]; 13 | c = mrb_class(mrb, recv); 14 | m = mrb_method_search_vm(mrb, &c, mid); 15 | if (!m) { 16 | mrb_value sym = mrb_symbol_value(mid); 17 | 18 | mid = mrb_intern_cstr(mrb, "method_missing"); 19 | m = mrb_method_search_vm(mrb, &c, mid); 20 | if (n == CALL_MAXARGS) { 21 | mrb_ary_unshift(mrb, regs[a+1], sym); 22 | } 23 | else { 24 | memmove(regs+a+2, regs+a+1, sizeof(mrb_value)*(n+1)); 25 | regs[a+1] = sym; 26 | n++; 27 | } 28 | } 29 | 30 | 31 | /* replace callinfo */ 32 | ci = mrb->c->ci; 33 | ci->mid = mid; 34 | ci->target_class = c; 35 | ci->argc = n; 36 | if (ci->argc == CALL_MAXARGS) ci->argc = -1; 37 | 38 | /* move stack */ 39 | memmove(mrb->c->stack, ®s[a], (ci->argc+1)*sizeof(mrb_value)); 40 | 41 | if (MRB_PROC_CFUNC_P(m)) { 42 | mrb->c->stack[0] = m->body.func(mrb, recv); 43 | mrb->arena_idx = ai; 44 | goto L_RETURN; 45 | } 46 | else { 47 | /* setup environment for calling method */ 48 | irep = m->body.irep; 49 | pool = irep->pool; 50 | syms = irep->syms; 51 | if (ci->argc < 0) { 52 | stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3); 53 | } 54 | else { 55 | stack_extend(mrb, irep->nregs, ci->argc+2); 56 | } 57 | regs = mrb->c->stack; 58 | pc = irep->iseq; 59 | } 60 | JUMP; 61 | } 62 | -------------------------------------------------------------------------------- /mrbcc/codegen_rb/opcode_special.c: -------------------------------------------------------------------------------- 1 | 2 | CASE(OP_CALL) { 3 | /* A R(A) := self.call(frame.argc, frame.argv) */ 4 | 5 | // OP_CALL is used in mruby only in Proc#call and Proc#[] 6 | // This opcode is not generated in mruby bytecode 7 | // It is only generated on the fly 8 | // We overwrite both these methods, so this opcode should never appear 9 | 10 | printf("Error: OP_CALL\n"); 11 | exit(0); 12 | FAIL_COMPILE_GARBAGE // This will fail compiler 13 | } 14 | 15 | CASE(OP_SUPER) { 16 | /* A B C R(A) := super(R(A+1),... ,R(A+C-1)) */ 17 | mrb_value recv; 18 | mrb_callinfo *ci = mrb->c->ci; 19 | struct RProc *m; 20 | struct RClass *c; 21 | mrb_sym mid = ci->mid; 22 | int a = GETARG_A(i); 23 | int n = GETARG_C(i); 24 | 25 | recv = regs[0]; 26 | c = mrb->c->ci->target_class->super; 27 | m = mrb_method_search_vm(mrb, &c, mid); 28 | if (!m) { 29 | mid = mrb_intern_cstr(mrb, "method_missing"); 30 | m = mrb_method_search_vm(mrb, &c, mid); 31 | if (n == CALL_MAXARGS) { 32 | mrb_ary_unshift(mrb, regs[a+1], mrb_symbol_value(ci->mid)); 33 | } 34 | else { 35 | memmove(regs+a+2, regs+a+1, sizeof(mrb_value)*(n+1)); 36 | SET_SYM_VALUE(regs[a+1], ci->mid); 37 | n++; 38 | } 39 | } 40 | 41 | /* push callinfo */ 42 | ci = cipush(mrb); 43 | ci->mid = mid; 44 | ci->proc = m; 45 | ci->stackent = mrb->c->stack; 46 | ci->argc = n; 47 | if (ci->argc == CALL_MAXARGS) ci->argc = -1; 48 | ci->target_class = c; 49 | //ci->pc = pc + 1; 50 | 51 | /* prepare stack */ 52 | mrb->c->stack += a; 53 | mrb->c->stack[0] = recv; 54 | 55 | if (MRB_PROC_CFUNC_P(m)) { 56 | mrb->c->stack[0] = m->body.func(mrb, recv); 57 | mrb->arena_idx = ai; 58 | if (mrb->exc) goto L_RAISE; 59 | /* pop stackpos */ 60 | regs = mrb->c->stack = mrb->c->ci->stackent; 61 | cipop(mrb); 62 | NEXT; 63 | } 64 | else { 65 | printf("TODO SUPER 2 MRB\n"); 66 | exit(0); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /mrbcc/mrb_opcodes.rb: -------------------------------------------------------------------------------- 1 | # extract opcode implementation from mruby source 2 | class MrbOpcodes 3 | def initialize(mruby_path) 4 | @opcode_impl = Hash.new 5 | orig_opcodes_src = "#{mruby_path}/src/vm.c" 6 | add_opcodes(File.read(orig_opcodes_src)) 7 | 8 | # patches for opcodes 9 | Dir[File.expand_path("../codegen_rb/opcode_*.c", __FILE__)].each do |fn| 10 | add_opcodes(File.read(fn)) 11 | end 12 | end 13 | 14 | def opcodes 15 | @opcode_impl 16 | end 17 | 18 | def get_opcode_body(str) 19 | str.gsub!(/\A[^\{]*/, "") 20 | raise "opcode body weird #{str}" if str[0] != "{" 21 | 22 | count_opencurly = 1 23 | i = 1 24 | while count_opencurly > 0 && !i.nil? && i < str.length 25 | # comments 26 | i = str.index("\n", i) if str[i, 2] == "//" 27 | i = str.index("*/", i) if str[i, 2] == "/*" 28 | 29 | if str[i] == "{" 30 | count_opencurly += 1 31 | elsif str[i] == "}" 32 | count_opencurly -= 1 33 | end 34 | i += 1 35 | end 36 | 37 | str.slice(0, i+1) 38 | end 39 | 40 | def add_opcodes(str) 41 | ops = str.split("CASE(OP_") 42 | ops.shift 43 | ops.each do |op| 44 | op =~ /\A([^\)]+)\)(.*)/m 45 | if $1.present? 46 | @opcode_impl["OP_#{$1}"] = get_opcode_body($2) 47 | end 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /mrbcc/mrbcc.rb: -------------------------------------------------------------------------------- 1 | require_relative './rite_parser' 2 | # encoding: utf-8 3 | require_relative './preparser' 4 | require_relative './codegen' 5 | require_relative './mrb_opcodes' 6 | require 'fileutils' 7 | require 'rbconfig' 8 | 9 | MRUBY_PATH = File.expand_path("../../mruby", __FILE__) 10 | MRBC_BIN = "#{MRUBY_PATH}/bin/mrbc" 11 | TMP_DIR = File.expand_path("../../tmp", __FILE__) 12 | IS_WINDOWS = (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/) 13 | 14 | it = ARGV.shift 15 | binary = if it == "-b" 16 | # standalone binary compile 17 | it = ARGV.shift 18 | true 19 | end 20 | 21 | RB_FILE_PATH = if it.start_with?("/") || it[1] == ":" 22 | it 23 | else 24 | "#{Dir.pwd}/#{it}" 25 | end 26 | RB_FILE_DIR = File.expand_path("..", RB_FILE_PATH) 27 | BUILD_DIR = if binary 28 | File.expand_path("../../build-binary", __FILE__) 29 | else 30 | File.expand_path("../../build", __FILE__) 31 | end 32 | 33 | puts "Compiling..." 34 | 35 | FileUtils.mkdir_p(TMP_DIR) 36 | opcodes = MrbOpcodes.new(MRUBY_PATH) 37 | 38 | # preparse file so we can #include 39 | rb_filename = RB_FILE_PATH 40 | rb_filename_noext = File.basename(rb_filename, ".rb") 41 | Preparser.preparse(rb_filename, "#{TMP_DIR}/tmp_out.rb") 42 | 43 | # compile to .mrb 44 | str = %x[cd "#{TMP_DIR}" && #{MRBC_BIN} tmp_out.rb] 45 | 46 | # parse 47 | parser = RiteParser.new("#{TMP_DIR}/tmp_out.mrb") 48 | 49 | # create C file 50 | File.open("#{BUILD_DIR}/c_files/out.c", "w") do |wf| 51 | wf.write(OpcodeParser.new(parser, opcodes.opcodes, "script_entry_point").process_irep) 52 | end 53 | 54 | # compile C file 55 | puts %x[cd "#{BUILD_DIR}" && make 2>&1 | grep "error:"] 56 | 57 | # copy C file 58 | ext = binary ? "" : (IS_WINDOWS ? ".dll" : ".so") 59 | FileUtils.mv("#{BUILD_DIR}/mrbcc_out#{ext}", "#{RB_FILE_DIR}/#{rb_filename_noext}#{ext}") 60 | 61 | # clean up 62 | FileUtils.rm(rb_filename.gsub(/\.rb$/, ".mrb"), :force => true) 63 | #FileUtils.rm(File.expand_path("#{TMP_DIR}/tmp_out.mrb", __FILE__), :force => true) 64 | #FileUtils.rm(File.expand_path("#{TMP_DIR}/tmp_out.rb", __FILE__), :force => true) 65 | -------------------------------------------------------------------------------- /mrbcc/preparser.rb: -------------------------------------------------------------------------------- 1 | module Preparser 2 | def self.preparse(infile, outfile) 3 | f = File.read(infile) 4 | pre_parsed = StringIO.new 5 | f.each_line do |line| 6 | if line =~ /\A#include '([^']+)/ 7 | files = $1 8 | files = File.expand_path("../#{$1}", infile) unless files.start_with?("/") 9 | 10 | Dir[files].each do |fn| 11 | pre_parsed << File.read(fn) 12 | end 13 | else 14 | pre_parsed << line 15 | end 16 | end 17 | 18 | File.open(outfile, "w") do |f| 19 | f.write(pre_parsed.string) 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /mrbcc/rite_parser.rb: -------------------------------------------------------------------------------- 1 | require 'ostruct' 2 | require 'active_support/core_ext' 3 | 4 | class MrbInstruction 5 | OPCODES = <<-EOF 6 | OP_NOP=0,/* */ 7 | OP_MOVE,/* A B R(A) := R(B) */ 8 | OP_LOADL,/* A Bx R(A) := Pool(Bx) */ 9 | OP_LOADI,/* A sBx R(A) := sBx */ 10 | OP_LOADSYM,/* A Bx R(A) := Syms(Bx) */ 11 | OP_LOADNIL,/* A R(A) := nil */ 12 | OP_LOADSELF,/* A R(A) := self */ 13 | OP_LOADT,/* A R(A) := true */ 14 | OP_LOADF,/* A R(A) := false */ 15 | 16 | OP_GETGLOBAL,/* A Bx R(A) := getglobal(Syms(Bx)) */ 17 | OP_SETGLOBAL,/* A Bx setglobal(Syms(Bx), R(A)) */ 18 | OP_GETSPECIAL,/*A Bx R(A) := Special[Bx] */ 19 | OP_SETSPECIAL,/*A Bx Special[Bx] := R(A) */ 20 | OP_GETIV,/* A Bx R(A) := ivget(Syms(Bx)) */ 21 | OP_SETIV,/* A Bx ivset(Syms(Bx),R(A)) */ 22 | OP_GETCV,/* A Bx R(A) := cvget(Syms(Bx)) */ 23 | OP_SETCV,/* A Bx cvset(Syms(Bx),R(A)) */ 24 | OP_GETCONST,/* A Bx R(A) := constget(Syms(Bx)) */ 25 | OP_SETCONST,/* A Bx constset(Syms(Bx),R(A)) */ 26 | OP_GETMCNST,/* A Bx R(A) := R(A)::Syms(Bx) */ 27 | OP_SETMCNST,/* A Bx R(A+1)::Syms(Bx) := R(A) */ 28 | OP_GETUPVAR,/* A B C R(A) := uvget(B,C) */ 29 | OP_SETUPVAR,/* A B C uvset(B,C,R(A)) */ 30 | 31 | OP_JMP,/* sBx pc+=sBx */ 32 | OP_JMPIF,/* A sBx if R(A) pc+=sBx */ 33 | OP_JMPNOT,/* A sBx if !R(A) pc+=sBx */ 34 | OP_ONERR,/* sBx rescue_push(pc+sBx) */ 35 | OP_RESCUE,/* A clear(exc); R(A) := exception (ignore when A=0) */ 36 | OP_POPERR,/* A A.times{rescue_pop()} */ 37 | OP_RAISE,/* A raise(R(A)) */ 38 | OP_EPUSH,/* Bx ensure_push(SEQ[Bx]) */ 39 | OP_EPOP,/* A A.times{ensure_pop().call} */ 40 | 41 | OP_SEND,/* A B C R(A) := call(R(A),Syms(B),R(A+1),...,R(A+C)) */ 42 | OP_SENDB,/* A B C R(A) := call(R(A),Syms(B),R(A+1),...,R(A+C),&R(A+C+1))*/ 43 | OP_FSEND,/* A B C R(A) := fcall(R(A),Syms(B),R(A+1),...,R(A+C-1)) */ 44 | OP_CALL,/* A R(A) := self.call(frame.argc, frame.argv) */ 45 | OP_SUPER,/* A C R(A) := super(R(A+1),... ,R(A+C+1)) */ 46 | OP_ARGARY,/* A Bx R(A) := argument array (16=6:1:5:4) */ 47 | OP_ENTER,/* Ax arg setup according to flags (23=5:5:1:5:5:1:1) */ 48 | OP_KARG,/* A B C R(A) := kdict[Syms(B)]; if C kdict.rm(Syms(B)) */ 49 | OP_KDICT,/* A C R(A) := kdict */ 50 | 51 | OP_RETURN,/* A B return R(A) (B=normal,in-block return/break) */ 52 | OP_TAILCALL,/* A B C return call(R(A),Syms(B),*R(C)) */ 53 | OP_BLKPUSH,/* A Bx R(A) := block (16=6:1:5:4) */ 54 | 55 | OP_ADD,/* A B C R(A) := R(A)+R(A+1) (Syms[B]=:+,C=1) */ 56 | OP_ADDI,/* A B C R(A) := R(A)+C (Syms[B]=:+) */ 57 | OP_SUB,/* A B C R(A) := R(A)-R(A+1) (Syms[B]=:-,C=1) */ 58 | OP_SUBI,/* A B C R(A) := R(A)-C (Syms[B]=:-) */ 59 | OP_MUL,/* A B C R(A) := R(A)*R(A+1) (Syms[B]=:*,C=1) */ 60 | OP_DIV,/* A B C R(A) := R(A)/R(A+1) (Syms[B]=:/,C=1) */ 61 | OP_EQ,/* A B C R(A) := R(A)==R(A+1) (Syms[B]=:==,C=1) */ 62 | OP_LT,/* A B C R(A) := R(A)R(A+1) (Syms[B]=:>,C=1) */ 65 | OP_GE,/* A B C R(A) := R(A)>=R(A+1) (Syms[B]=:>=,C=1) */ 66 | 67 | OP_ARRAY,/* A B C R(A) := ary_new(R(B),R(B+1)..R(B+C)) */ 68 | OP_ARYCAT,/* A B ary_cat(R(A),R(B)) */ 69 | OP_ARYPUSH,/* A B ary_push(R(A),R(B)) */ 70 | OP_AREF,/* A B C R(A) := R(B)[C] */ 71 | OP_ASET,/* A B C R(B)[C] := R(A) */ 72 | OP_APOST,/* A B C *R(A),R(A+1)..R(A+C) := R(A) */ 73 | 74 | OP_STRING,/* A Bx R(A) := str_dup(Lit(Bx)) */ 75 | OP_STRCAT,/* A B str_cat(R(A),R(B)) */ 76 | 77 | OP_HASH,/* A B C R(A) := hash_new(R(B),R(B+1)..R(B+C)) */ 78 | OP_LAMBDA,/* A Bz Cz R(A) := lambda(SEQ[Bz],Cz) */ 79 | OP_RANGE,/* A B C R(A) := range_new(R(B),R(B+1),C) */ 80 | 81 | OP_OCLASS,/* A R(A) := ::Object */ 82 | OP_CLASS,/* A B R(A) := newclass(R(A),Syms(B),R(A+1)) */ 83 | OP_MODULE,/* A B R(A) := newmodule(R(A),Syms(B)) */ 84 | OP_EXEC,/* A Bx R(A) := blockexec(R(A),SEQ[Bx]) */ 85 | OP_METHOD,/* A B R(A).newmethod(Syms(B),R(A+1)) */ 86 | OP_SCLASS,/* A B R(A) := R(B).singleton_class */ 87 | OP_TCLASS,/* A R(A) := target_class */ 88 | 89 | OP_DEBUG,/* A B C print R(A),R(B),R(C) */ 90 | OP_STOP,/* stop VM */ 91 | OP_ERR,/* Bx raise RuntimeError with message Lit(Bx) */ 92 | 93 | OP_RSVD1,/* reserved instruction #1 */ 94 | OP_RSVD2,/* reserved instruction #2 */ 95 | OP_RSVD3,/* reserved instruction #3 */ 96 | OP_RSVD4,/* reserved instruction #4 */ 97 | OP_RSVD5,/* reserved instruction #5 */ 98 | EOF 99 | .split("\n").reject(&:blank?).map {|s| s.gsub(/[,=].*/, "") } 100 | 101 | MAXARG_Bx = 0xffff 102 | MAXARG_sBx = MAXARG_Bx>>1 103 | 104 | def initialize(instr_code) 105 | @instr = instr_code 106 | @opcode = @instr & 0x7f 107 | @opcode_name = OPCODES[@opcode] 108 | end 109 | 110 | def opcode 111 | @opcode_name 112 | end 113 | 114 | def GETARG_A 115 | (@instr >> 23) & 0x1ff 116 | end 117 | 118 | def GETARG_B 119 | (@instr >> 14) & 0x1ff 120 | end 121 | 122 | def GETARG_C 123 | (@instr >> 7) & 0x7f 124 | end 125 | 126 | def GETARG_Bx 127 | (@instr >> 7) & 0xffff 128 | end 129 | 130 | def GETARG_sBx 131 | self.GETARG_Bx - MAXARG_sBx 132 | end 133 | 134 | def GETARG_Ax 135 | (@instr >> 7) & 0x1ffffff 136 | end 137 | 138 | def GETARG_UNPACK_b(n1, n2) 139 | (@instr >> (7+n2)) & (((1<> 7) & (((1<= syms_len) 279 | raise "MRB_DUMP_GENERAL_FAILURE: sym_idx >= syms_len" 280 | else 281 | lv[:name] = syms[sym_idx] 282 | lv[:r] = read_field(2, :bin_to_uint16) 283 | end 284 | end 285 | end 286 | end 287 | 288 | (0...irep.rlen).each do |i| 289 | read_lv_record(syms, syms_len, irep.reps[i]) 290 | end 291 | end 292 | 293 | def read_section_lv 294 | syms_len = read_field(4, :bin_to_uint32) 295 | 296 | (0...syms_len).each do |i| 297 | str_len = read_field(2, :bin_to_uint16) 298 | 299 | syms[i] = @file.read(str_len) 300 | end 301 | 302 | read_lv_record(syms, syms_len, irep) 303 | end 304 | 305 | def read_section 306 | data = read_fields([ 307 | [:section_identify, 4], 308 | [:section_size, 4, :bin_uint16] 309 | ]) 310 | 311 | if data.section_identify == RITE_BINARY_EOF 312 | false 313 | else 314 | fpos = @file.pos 315 | case data.section_identify 316 | when RITE_SECTION_IREP_IDENTIFIER 317 | read_fields([[:rite_version, 4]]) 318 | @irep = read_section_irep 319 | when RITE_SECTION_LV_IDENTIFIER 320 | fail "LV section appeared before IREP section. Aborting..." unless irep 321 | read_section_lv 322 | end 323 | @file.seek(fpos + data.section_size, IO::SEEK_SET) 324 | true 325 | end 326 | end 327 | 328 | def read_fields(fields, data = nil) 329 | data ||= OpenStruct.new 330 | fields.each do |field| 331 | data.send("#{field[0]}=", read_field(field[1], field[2])) 332 | end 333 | data 334 | end 335 | 336 | def read_field(size, type = nil) 337 | value = @file.read(size) 338 | case type 339 | when :bin_uint32 340 | (value[0].ord << 24) | 341 | (value[1].ord << 16) | 342 | (value[2].ord << 8) | 343 | value[3].ord 344 | when :bin_uint16 345 | (value[0].ord << 8) | value[1].ord 346 | when :bin_uint8 347 | value[0].ord 348 | when :hex_uint 349 | hex_uint(value) 350 | else 351 | value 352 | end 353 | end 354 | 355 | def read_sections 356 | read_section 357 | end 358 | end 359 | -------------------------------------------------------------------------------- /mrbcc_mrblib/compile_mrblib.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | 3 | def write_gem_rb_code(outf, name) 4 | Dir[File.expand_path("../../mruby/mrbgems/mruby-#{name}/mrblib/*.rb", __FILE__)].each do |fn| 5 | outf.write(File.read(fn)) 6 | end 7 | end 8 | 9 | tmp_fn = File.expand_path("../../tmp_mrblib.rb", __FILE__) 10 | File.open(tmp_fn, "w") do |outf| 11 | Dir[File.expand_path("../../mruby/mrblib/*.rb", __FILE__)].each do |fn| 12 | outf.write(File.read(fn)) 13 | end 14 | 15 | # mrbgems 16 | ordered_mrbgems = %w{sprintf print} 17 | ordered_mrbgems.each { |name| write_gem_rb_code(outf, name) } 18 | Dir[File.expand_path("../../mruby/build/host/mrbgems/mruby-*", __FILE__)].each do |fn| 19 | if name = fn[/mruby-([^\/\\\.]+)\z/, 1] 20 | next if ordered_mrbgems.include?(name) 21 | puts name 22 | write_gem_rb_code(outf, name) 23 | end 24 | end 25 | end 26 | 27 | %x[cd ../ && ./compile tmp_mrblib.rb] 28 | 29 | out_fn = File.expand_path("../../tmp_mrblib.so", __FILE__) 30 | lib_fn = File.expand_path("../mrblib.so", __FILE__) 31 | if File.exists?(out_fn) 32 | FileUtils.mv(out_fn, lib_fn) 33 | else 34 | puts "mrblib compile failed." 35 | end 36 | FileUtils.rm(tmp_fn, :force => true) 37 | -------------------------------------------------------------------------------- /run_performance_test.rb: -------------------------------------------------------------------------------- 1 | require 'stringio' 2 | require_relative './mrbcc/preparser' 3 | # preparse 4 | rb_filename = File.expand_path('../test/performance_test.rb', __FILE__) 5 | rb_filename_noext = File.basename(rb_filename, ".rb") 6 | Preparser.preparse(rb_filename, "tmp/tmp_out.rb") 7 | 8 | # run 9 | durations_separator = 'Durations:' 10 | 11 | puts "Running with mruby..." 12 | output = %x[mruby/bin/mruby tmp/tmp_out.rb] 13 | idx = output.index(durations_separator) 14 | perf_mruby = if idx 15 | eval(output[idx + durations_separator.length, output.length].strip) 16 | end 17 | 18 | puts "Compiling with mruby_cc..." 19 | %x[./compile test/performance_test.rb] 20 | puts "Running with mruby_cc..." 21 | output = %x[./runner test/performance_test.so] 22 | idx = output.index(durations_separator) 23 | perf_mruby_cc = if idx 24 | eval(output[idx + durations_separator.length, output.length].strip) 25 | end 26 | 27 | diff = perf_mruby.keys.map do |key| 28 | perf_data = { 29 | mruby: perf_mruby[key], 30 | mruby_cc: perf_mruby_cc[key] 31 | } 32 | [key, perf_data] 33 | end 34 | diff = Hash[diff] 35 | 36 | diff_percent = diff.each_pair.map do |key, data| 37 | [key, data[:mruby_cc] / data[:mruby].to_f] 38 | end 39 | 40 | diff_percent.sort! { |a,b| b[1] <=> a[1] } 41 | 42 | diff_percent.each do |data| 43 | key = data[0] 44 | puts key 45 | puts (data[1] * 100).ceil.to_s + "% mruby: #{perf_mruby[key]}, mruby_cc: #{perf_mruby_cc[key]}" 46 | end 47 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd mrbcc_mrblib 4 | ruby compile_mrblib.rb 5 | mv mrblib.so ../ 6 | cd .. 7 | 8 | cd standalone_runner 9 | make 10 | mv mrbcc_runner ../runner 11 | cd .. 12 | 13 | echo "Done." 14 | -------------------------------------------------------------------------------- /standalone_runner/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile description. 2 | # basic build file for mrbcc executable 3 | 4 | # project-specific macros 5 | # extension of the executable-file is modifiable(.exe .out ...) 6 | export CC = gcc 7 | export LL = gcc 8 | export AR = ar 9 | export YACC = bison 10 | 11 | BASEDIR = ../mruby/src 12 | TARGET := ./mrbcc_runner 13 | LIBR := ../mruby/build/host/lib/libmruby.a 14 | ifeq ($(OS),Windows_NT) 15 | EXE := $(TARGET).exe 16 | else 17 | EXE := $(TARGET) 18 | endif 19 | OBJ0 := $(patsubst %.c,%.o,$(wildcard $(BASEDIR)/../../standalone_runner/*.c)) 20 | #OBJ1 := $(patsubst %.c,%.o,$(filter-out $(EXCEPT1),$(wildcard $(BASEDIR)/*.c))) 21 | #OBJ2 := $(patsubst %.c,%.o,$(wildcard $(BASEDIR)/ext/regex/*.c)) 22 | #OBJ3 := $(patsubst %.c,%.o,$(wildcard $(BASEDIR)/ext/enc/*.c)) 23 | OBJS := $(OBJ0) 24 | 25 | # ext libraries 26 | #EXT1 := $(patsubst %.c,%.o,$(wildcard $(BASEDIR)/../ext/socket/*.c)) 27 | EXTS := $(EXT1) 28 | 29 | # libraries, includes 30 | LIBS = -lm -ldl 31 | INCLUDES = -I$(BASEDIR) -I$(BASEDIR)/../include 32 | 33 | # compiler, linker (gcc) 34 | ifeq ($(strip $(COMPILE_MODE)),) 35 | # default compile option 36 | COMPILE_MODE = debug 37 | endif 38 | 39 | ifeq ($(COMPILE_MODE),debug) 40 | CFLAGS = -g -O3 41 | else ifeq ($(COMPILE_MODE),release) 42 | CFLAGS = -O3 43 | else ifeq ($(COMPILE_MODE),small) 44 | CFLAGS = -Os 45 | endif 46 | 47 | ALL_CFLAGS = -Wall -Werror-implicit-function-declaration $(CFLAGS) 48 | ifeq ($(OS),Windows_NT) 49 | MAKE_FLAGS = CC=$(CC) LL=$(LL) ALL_CFLAGS="$(ALL_CFLAGS)" 50 | else 51 | MAKE_FLAGS = CC='$(CC)' LL='$(LL)' ALL_CFLAGS='$(ALL_CFLAGS)' 52 | endif 53 | 54 | ############################## 55 | # generic build targets, rules 56 | 57 | .PHONY : all 58 | all : $(LIBR) $(EXE) 59 | 60 | # executable constructed using linker from object files 61 | $(EXE) : $(LIBR) $(OBJS) $(EXTS) 62 | $(LL) -o $@ $(CFLAGS) $(OBJS) $(LIBR) $(EXTS) $(LIBS) 63 | 64 | -include $(OBJS:.o=.d) 65 | 66 | # objects compiled from source 67 | $(OBJS) : %.o : %.c 68 | $(CC) $(ALL_CFLAGS) -MMD $(INCLUDES) -c $< -o $@ 69 | 70 | # C library compile 71 | $(LIBR) : 72 | @$(MAKE) -C $(BASEDIR) $(MAKE_FLAGS) 73 | 74 | # mrbcc library compile 75 | # extend libraries complile 76 | $(EXTS) : %.o : %.c 77 | $(CC) $(ALL_CFLAGS) -MMD $(INCLUDES) -c $< -o $@ 78 | 79 | # clean up 80 | .PHONY : clean #cleandep 81 | clean : 82 | $(MAKE) clean -C ../mruby/mrblib $(MAKE_FLAGS) 83 | @echo "make: removing targets, objects and depend files of `pwd`" 84 | -$(RM_F) $(EXE) $(OBJS) 85 | -$(RM_F) $(OBJS:.o=.d) 86 | -------------------------------------------------------------------------------- /standalone_runner/mrbcc_runner.c: -------------------------------------------------------------------------------- 1 | #include "mruby.h" 2 | #include "mruby/string.h" 3 | #include "mruby/value.h" 4 | #include "mruby/array.h" 5 | #include "mruby/variable.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | mrb_value mrbcc_load_so(mrb_state *mrb, mrb_value self, const char *filename) { 12 | void *handle; 13 | mrb_value (*entry_point)(mrb_state*, mrb_value); 14 | mrb_value ary; 15 | char *error; 16 | char *fullpath; 17 | fullpath = realpath(filename, NULL); 18 | handle = dlopen(fullpath, RTLD_LAZY); 19 | free(fullpath); 20 | if (!handle) { 21 | fprintf (stderr, "%s\n", dlerror()); 22 | return mrb_nil_value(); 23 | } 24 | dlerror(); /* Clear any existing error */ 25 | entry_point = dlsym(handle, "mrbb_exec_entry_point"); 26 | if ((error = dlerror()) != NULL) { 27 | fprintf (stderr, "%s\n", error); 28 | return mrb_nil_value(); 29 | } 30 | ary = mrb_iv_get(mrb, mrb_obj_value(mrb->kernel_module), 31 | mrb_intern_cstr(mrb, "@loaded_compiled_mrb_handles")); 32 | mrb_ary_push(mrb, ary, mrb_fixnum_value((mrb_int) handle)); // TODO warning 33 | return (*entry_point)(mrb, self); 34 | } 35 | 36 | static mrb_value rb_load_compiled_mrb(mrb_state *mrb, mrb_value self) 37 | { 38 | mrb_value rstr; 39 | const char *str; 40 | 41 | mrb_get_args(mrb, "S", &rstr); 42 | str = mrb_string_value_ptr(mrb, rstr); 43 | 44 | return mrbcc_load_so(mrb, self, str); 45 | } 46 | 47 | int 48 | main(int argc, char **argv) 49 | { 50 | mrb_state *mrb = mrb_open(); 51 | 52 | if (mrb == NULL) { 53 | fprintf(stderr, "Invalid mrb_state, exiting driver"); 54 | return EXIT_FAILURE; 55 | } 56 | 57 | mrb_iv_set(mrb, mrb_obj_value(mrb->kernel_module), 58 | mrb_intern_cstr(mrb, "@loaded_compiled_mrb_handles"), 59 | mrb_ary_new(mrb)); 60 | 61 | // define load method on kernel 62 | mrb_define_method(mrb, mrb->kernel_module, "load_compiled_mrb", 63 | rb_load_compiled_mrb, ARGS_REQ(1)); 64 | 65 | mrbcc_load_so(mrb, mrb_top_self(mrb), "mrblib.so"); 66 | 67 | if (argc <= 1) { 68 | printf("Usage: %s compiled.so\n", argv[0]); 69 | } else { 70 | mrbcc_load_so(mrb, mrb_top_self(mrb), argv[1]); 71 | } 72 | 73 | mrb_close(mrb); 74 | 75 | // TODO: unload .so 76 | //dlclose(handle); // gotta keep a global array of loaded sos and not close 77 | return EXIT_SUCCESS; 78 | } 79 | -------------------------------------------------------------------------------- /test/performance_test.rb: -------------------------------------------------------------------------------- 1 | GC.disable 2 | N_EXAMPLE_ITERATIONS = 1000 3 | N_SUITE_ITERATIONS = 1 4 | 5 | $example_durations = Hash.new 6 | 7 | #include '../mruby/test/assert.rb' 8 | self.class.alias_method :assert_orig, :assert 9 | def assert(str = 'Assertion failed', iso = '', &block) 10 | i = 0 11 | start_at = Time.now 12 | assert_orig(str, iso, &block) while (i += 1) <= N_EXAMPLE_ITERATIONS 13 | duration = Time.now - start_at 14 | 15 | str += ", #{iso}" unless iso.empty? 16 | $example_durations[str] = duration 17 | end 18 | 19 | _suite_iterations = 0 20 | _suite_start_at = Time.now 21 | while (_suite_iterations += 1) <= N_SUITE_ITERATIONS 22 | #include '../passing_tests.rb' 23 | end 24 | $example_durations['ALL'] = Time.now - _suite_start_at 25 | 26 | puts "Durations:" 27 | p $example_durations 28 | -------------------------------------------------------------------------------- /testsuite.rb: -------------------------------------------------------------------------------- 1 | # run mruby test suite 2 | 3 | #include 'mruby/test/assert.rb' 4 | #include 'mruby/test/t/*.rb' 5 | 6 | report 7 | --------------------------------------------------------------------------------