├── lib ├── thats_it │ └── version.rb └── thats_it.rb ├── test ├── test_helper.rb └── thats_it_test.rb ├── ext └── thats_it │ ├── extconf.rb │ ├── thats_it.h │ ├── thats_it.c │ ├── thats_it_20500.h │ └── thats_it_20600.h ├── bin ├── setup └── console ├── .gitignore ├── Gemfile ├── Rakefile ├── Gemfile.lock ├── LICENSE.txt ├── thats_it.gemspec ├── README.md └── CODE_OF_CONDUCT.md /lib/thats_it/version.rb: -------------------------------------------------------------------------------- 1 | module ThatsIt 2 | VERSION = "0.2.0" 3 | end 4 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require "bundler/setup" 2 | require "thats_it" 3 | require "test/unit" 4 | -------------------------------------------------------------------------------- /ext/thats_it/extconf.rb: -------------------------------------------------------------------------------- 1 | require "mkmf" 2 | 3 | $optflags="-O0" 4 | $debugflags="-g3" 5 | 6 | create_makefile("thats_it/thats_it") 7 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /tmp/ 9 | *.bundle 10 | *.so 11 | *.o 12 | *.a 13 | mkmf.log 14 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } 4 | 5 | # Specify your gem's dependencies in thats_it.gemspec 6 | gemspec 7 | -------------------------------------------------------------------------------- /lib/thats_it.rb: -------------------------------------------------------------------------------- 1 | require "thats_it/version" 2 | require "thats_it/thats_it" 3 | 4 | module ThatsIt 5 | CALL_TP = TracePoint.trace(:call) { |tp| setup_it_block_call } 6 | C_CALL_TP = TracePoint.trace(:c_call) { |tp| setup_it_block_c_call } 7 | end 8 | -------------------------------------------------------------------------------- /test/thats_it_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ThatsItTest < Test::Unit::TestCase 4 | def test_cfunc 5 | assert 2.yield_self { it * it } == 4 6 | end 7 | 8 | def test_method 9 | assert m { it * it } == 4 10 | end 11 | 12 | private 13 | 14 | def m 15 | yield 2 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /ext/thats_it/thats_it.h: -------------------------------------------------------------------------------- 1 | #ifndef THATS_IT_H 2 | #define THATS_IT_H 1 3 | 4 | #include "setjmp.h" 5 | #include "ruby.h" 6 | #include "ruby/ruby.h" 7 | #include "ruby/version.h" 8 | 9 | #endif /* THATS_IT_H */ 10 | 11 | #if RUBY_API_VERSION_CODE >= 20600 12 | #include "thats_it_20600.h" 13 | #elif RUBY_API_VERSION_CODE >= 20500 14 | #include "thats_it_20500.h" 15 | #endif 16 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "thats_it" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start(__FILE__) 15 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rake/extensiontask" 3 | require "rake/testtask" 4 | 5 | Rake::TestTask.new(:test) do |t| 6 | t.libs << "test" 7 | t.libs << "lib" 8 | t.test_files = FileList['test/**/*_test.rb'] 9 | end 10 | 11 | task :build => :compile 12 | 13 | Rake::ExtensionTask.new("thats_it") do |ext| 14 | ext.lib_dir = "lib/thats_it" 15 | end 16 | 17 | task :default => [:clobber, :compile, :test] 18 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | thats_it (0.2.0) 5 | 6 | GEM 7 | remote: https://rubygems.org/ 8 | specs: 9 | power_assert (1.1.3) 10 | rake (10.5.0) 11 | rake-compiler (1.0.4) 12 | rake 13 | test-unit (3.2.8) 14 | power_assert 15 | 16 | PLATFORMS 17 | ruby 18 | 19 | DEPENDENCIES 20 | bundler (~> 1.16) 21 | rake (~> 10.0) 22 | rake-compiler 23 | test-unit 24 | thats_it! 25 | 26 | BUNDLED WITH 27 | 1.16.2 28 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Seiei Miyagi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /thats_it.gemspec: -------------------------------------------------------------------------------- 1 | 2 | lib = File.expand_path("../lib", __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require "thats_it/version" 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "thats_it" 8 | spec.version = ThatsIt::VERSION 9 | spec.authors = ["Seiei Miyagi"] 10 | spec.email = ["hanachin@gmail.com"] 11 | 12 | spec.summary = %q{Provide Kotlin's `it` keyword in Ruby.} 13 | spec.description = %q{You can use `it` method in a block to get a single parameter of the block like Kotlin's `it` keyword} 14 | spec.homepage = "https://github.com/hanachin/thats_it" 15 | spec.license = "MIT" 16 | 17 | # Specify which files should be added to the gem when it is released. 18 | # The `git ls-files -z` loads the files in the RubyGem that have been added into git. 19 | spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do 20 | `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 21 | end 22 | spec.bindir = "exe" 23 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 24 | spec.require_paths = ["lib"] 25 | spec.extensions = ["ext/thats_it/extconf.rb"] 26 | 27 | spec.required_ruby_version = '>= 2.5.0' 28 | 29 | spec.add_development_dependency "bundler", "~> 1.16" 30 | spec.add_development_dependency "rake", "~> 10.0" 31 | spec.add_development_dependency "rake-compiler" 32 | spec.add_development_dependency "test-unit" 33 | end 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ThatsIt 2 | 3 | Provide Kotlin's `it` keyword in Ruby. 4 | 5 | It's experimental ;) Don't use in production. 6 | 7 | ## Installation 8 | 9 | Add this line to your application's Gemfile: 10 | 11 | ```ruby 12 | gem 'thats_it' 13 | ``` 14 | 15 | And then execute: 16 | 17 | $ bundle 18 | 19 | Or install it yourself as: 20 | 21 | $ gem install thats_it 22 | 23 | ## Usage 24 | 25 | You can use `it` method in a block to get a single parameter of the block like Kotlin's `it` keyword[^1]. 26 | [^1]: https://kotlinlang.org/docs/reference/lambdas.html#it-implicit-name-of-a-single-parameter 27 | 28 | 29 | require "thats_it" 30 | 31 | 2.yield_self { it * it } 32 | # => 4 33 | 34 | def greeting 35 | yield "Hello, World!" 36 | end 37 | greeting { puts it } 38 | # Hello, World! 39 | 40 | ## Development 41 | 42 | After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment. 43 | 44 | To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). 45 | 46 | ## Contributing 47 | 48 | Bug reports and pull requests are welcome on GitHub at https://github.com/hanachin/thats_it. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. 49 | 50 | ## License 51 | 52 | The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). 53 | 54 | ## Code of Conduct 55 | 56 | Everyone interacting in the ThatsIt project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/hanachin/thats_it/blob/master/CODE_OF_CONDUCT.md). 57 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at hanachin@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /ext/thats_it/thats_it.c: -------------------------------------------------------------------------------- 1 | #include "thats_it.h" 2 | 3 | static int thats_it_block_p(const rb_iseq_t *iseq) { 4 | ID id_it; 5 | if (!iseq) { return 0; } 6 | if (iseq->body->param.size) { return 0; } 7 | 8 | id_it = rb_intern("it"); 9 | for (unsigned int i = 0; i < iseq->body->ci_size; i++) { 10 | if (id_it == iseq->body->ci_entries[i].mid) { 11 | return 1; 12 | } 13 | } 14 | return 0; 15 | } 16 | 17 | static inline enum rb_block_type block_type(const struct rb_block *block) { 18 | return block->type; 19 | } 20 | 21 | static const struct rb_block *proc_block(VALUE procval) { 22 | return &((rb_proc_t *)RTYPEDDATA_DATA(procval))->block; 23 | } 24 | 25 | static const rb_iseq_t *proc_iseq(VALUE procval); 26 | 27 | static const rb_iseq_t *block_iseq(const struct rb_block *block) { 28 | switch (block_type(block)) { 29 | case block_type_iseq: 30 | return block->as.captured.code.iseq; 31 | case block_type_proc: 32 | return proc_iseq(block->as.proc); 33 | case block_type_ifunc: 34 | case block_type_symbol: 35 | break; 36 | } 37 | return NULL; 38 | } 39 | 40 | static const rb_iseq_t *proc_iseq(VALUE procval) { 41 | return block_iseq(proc_block(procval)); 42 | } 43 | 44 | static int block_handler_type_iseq_p(VALUE block_handler) { 45 | return (block_handler & 0x03) == 0x01; 46 | } 47 | 48 | static int block_handler_type_ifunc_p(VALUE block_handler) { 49 | return (block_handler & 0x03) == 0x03; 50 | } 51 | 52 | static enum rb_block_handler_type block_handler_type(VALUE block_handler) { 53 | if (block_handler_type_iseq_p(block_handler)) { 54 | return block_handler_type_iseq; 55 | } else if (block_handler_type_ifunc_p(block_handler)) { 56 | return block_handler_type_ifunc; 57 | } else if (SYMBOL_P(block_handler)) { 58 | return block_handler_type_symbol; 59 | } else { 60 | if (!rb_obj_is_proc(block_handler)) { 61 | rb_raise(rb_eRuntimeError, "unknown block_handler type"); 62 | } 63 | return block_handler_type_proc; 64 | } 65 | } 66 | 67 | static const rb_iseq_t *block_handler_iseq(VALUE block_handler) { 68 | switch (block_handler_type(block_handler)) { 69 | case block_handler_type_iseq: 70 | { 71 | struct rb_captured_block *captured = VM_TAGGED_PTR_REF(block_handler, 0x03); 72 | return captured->code.iseq; 73 | } 74 | case block_handler_type_proc: 75 | { 76 | return proc_iseq(block_handler); 77 | } 78 | case block_handler_type_ifunc: 79 | case block_handler_type_symbol: 80 | break; 81 | } 82 | return NULL; 83 | } 84 | 85 | static const void rewrite_iseq(const rb_iseq_t *iseq) { 86 | ID *ids = (ID *)ALLOC_N(ID, 1); 87 | ids[0] = rb_intern("it"); 88 | iseq->body->param.size = 1; 89 | iseq->body->param.flags.has_lead = 1; 90 | iseq->body->param.lead_num = 1; 91 | iseq->body->param.flags.ambiguous_param0 = 1; 92 | iseq->body->local_table_size = 1; 93 | ruby_xfree((void *)iseq->body->local_table); 94 | iseq->body->local_table = ids; 95 | } 96 | 97 | static VALUE rb_setup_it_block_c_call() { 98 | const rb_control_frame_t *cfp = ruby_current_execution_context_ptr->cfp; 99 | rb_iseq_t *iseq = (rb_iseq_t *)(cfp + 2)->block_code; 100 | 101 | if (!iseq) { return Qnil; } 102 | if (!thats_it_block_p(iseq)) { return Qnil; } 103 | 104 | rewrite_iseq(iseq); 105 | 106 | return Qnil; 107 | } 108 | 109 | static int env_local_p(VALUE *ep) { 110 | VALUE flags = ep[VM_ENV_DATA_INDEX_FLAGS]; 111 | return flags & VM_ENV_FLAG_LOCAL ? 1 : 0; 112 | } 113 | 114 | static VALUE *prev_ep(VALUE *ep) { 115 | return VM_TAGGED_PTR_REF(ep[VM_ENV_DATA_INDEX_SPECVAL], 0x03); 116 | } 117 | 118 | static VALUE *get_lep(VALUE *ep) { 119 | while (!env_local_p(ep)) { 120 | ep = prev_ep(ep); 121 | } 122 | return ep; 123 | } 124 | 125 | static VALUE get_block_handler(rb_control_frame_t *cfp) { 126 | VALUE *ep = get_lep(cfp->ep); 127 | return ep[VM_ENV_DATA_INDEX_SPECVAL]; 128 | } 129 | 130 | static VALUE rb_setup_it_block_call() { 131 | rb_control_frame_t *cfp = ruby_current_execution_context_ptr->cfp; 132 | VALUE block_handler = get_block_handler(cfp + 2); 133 | rb_iseq_t *iseq; 134 | 135 | if (!block_handler) { return Qnil; } 136 | 137 | iseq = block_handler_iseq(block_handler); 138 | 139 | if (!thats_it_block_p(iseq)) { return Qnil; } 140 | 141 | rewrite_iseq(iseq); 142 | 143 | return Qnil; 144 | } 145 | 146 | static VALUE rb_it() { 147 | const rb_control_frame_t *cfp = ruby_current_execution_context_ptr->cfp; 148 | return *(cfp + 2)->sp; 149 | } 150 | 151 | void 152 | Init_thats_it(void) 153 | { 154 | rb_define_global_function("setup_it_block_c_call", rb_setup_it_block_c_call, 0); 155 | rb_define_global_function("setup_it_block_call", rb_setup_it_block_call, 0); 156 | rb_define_global_function("it", rb_it, 0); 157 | } 158 | -------------------------------------------------------------------------------- /ext/thats_it/thats_it_20500.h: -------------------------------------------------------------------------------- 1 | /* 2 | copy from ruby source code 3 | https://github.com/ruby/ruby/blob/trunk/COPYING.ja 4 | */ 5 | 6 | #define VM_ENV_FLAG_LOCAL 0x0002 7 | #define VM_ENV_DATA_INDEX_FLAGS ( 0) /* ep[ 0] */ 8 | #define VM_ENV_DATA_INDEX_SPECVAL (-1) /* ep[-1] */ 9 | 10 | /* for setup_it_block */ 11 | #define VM_TAGGED_PTR_REF(v, mask) ((void *)((v) & ~mask)) 12 | 13 | typedef struct rb_code_location_struct { 14 | int lineno; 15 | int column; 16 | } rb_code_location_t; 17 | 18 | typedef struct rb_code_range_struct { 19 | rb_code_location_t first_loc; 20 | rb_code_location_t last_loc; 21 | } rb_code_range_t; 22 | 23 | typedef struct rb_iseq_location_struct { 24 | VALUE pathobj; /* String (path) or Array [path, realpath]. Frozen. */ 25 | VALUE base_label; /* String */ 26 | VALUE label; /* String */ 27 | VALUE first_lineno; /* TODO: may be unsigned short */ 28 | rb_code_range_t code_range; 29 | } rb_iseq_location_t; 30 | 31 | struct rb_call_info { 32 | /* fixed at compile time */ 33 | ID mid; 34 | unsigned int flag; 35 | int orig_argc; 36 | }; 37 | 38 | struct rb_iseq_constant_body { 39 | enum iseq_type { 40 | ISEQ_TYPE_TOP, 41 | ISEQ_TYPE_METHOD, 42 | ISEQ_TYPE_BLOCK, 43 | ISEQ_TYPE_CLASS, 44 | ISEQ_TYPE_RESCUE, 45 | ISEQ_TYPE_ENSURE, 46 | ISEQ_TYPE_EVAL, 47 | ISEQ_TYPE_MAIN, 48 | ISEQ_TYPE_DEFINED_GUARD 49 | } type; /* instruction sequence type */ 50 | 51 | unsigned int iseq_size; 52 | const VALUE *iseq_encoded; /* encoded iseq (insn addr and operands) */ 53 | 54 | /** 55 | * parameter information 56 | * 57 | * def m(a1, a2, ..., aM, # mandatory 58 | * b1=(...), b2=(...), ..., bN=(...), # optional 59 | * *c, # rest 60 | * d1, d2, ..., dO, # post 61 | * e1:(...), e2:(...), ..., eK:(...), # keyword 62 | * **f, # keyword_rest 63 | * &g) # block 64 | * => 65 | * 66 | * lead_num = M 67 | * opt_num = N 68 | * rest_start = M+N 69 | * post_start = M+N+(*1) 70 | * post_num = O 71 | * keyword_num = K 72 | * block_start = M+N+(*1)+O+K 73 | * keyword_bits = M+N+(*1)+O+K+(&1) 74 | * size = M+N+O+(*1)+K+(&1)+(**1) // parameter size. 75 | */ 76 | 77 | struct { 78 | struct { 79 | unsigned int has_lead : 1; 80 | unsigned int has_opt : 1; 81 | unsigned int has_rest : 1; 82 | unsigned int has_post : 1; 83 | unsigned int has_kw : 1; 84 | unsigned int has_kwrest : 1; 85 | unsigned int has_block : 1; 86 | 87 | unsigned int ambiguous_param0 : 1; /* {|a|} */ 88 | } flags; 89 | 90 | unsigned int size; 91 | 92 | int lead_num; 93 | int opt_num; 94 | int rest_start; 95 | int post_start; 96 | int post_num; 97 | int block_start; 98 | 99 | const VALUE *opt_table; /* (opt_num + 1) entries. */ 100 | /* opt_num and opt_table: 101 | * 102 | * def foo o1=e1, o2=e2, ..., oN=eN 103 | * #=> 104 | * # prologue code 105 | * A1: e1 106 | * A2: e2 107 | * ... 108 | * AN: eN 109 | * AL: body 110 | * opt_num = N 111 | * opt_table = [A1, A2, ..., AN, AL] 112 | */ 113 | 114 | const struct rb_iseq_param_keyword { 115 | int num; 116 | int required_num; 117 | int bits_start; 118 | int rest_start; 119 | const ID *table; 120 | const VALUE *default_values; 121 | } *keyword; 122 | } param; 123 | 124 | rb_iseq_location_t location; 125 | 126 | /* insn info, must be freed */ 127 | const struct iseq_insn_info_entry *insns_info; 128 | 129 | const ID *local_table; /* must free */ 130 | 131 | /* catch table */ 132 | const struct iseq_catch_table *catch_table; 133 | 134 | /* for child iseq */ 135 | const struct rb_iseq_struct *parent_iseq; 136 | struct rb_iseq_struct *local_iseq; /* local_iseq->flip_cnt can be modified */ 137 | 138 | union iseq_inline_storage_entry *is_entries; 139 | struct rb_call_info *ci_entries; /* struct rb_call_info ci_entries[ci_size]; 140 | * struct rb_call_info_with_kwarg cikw_entries[ci_kw_size]; 141 | * So that: 142 | * struct rb_call_info_with_kwarg *cikw_entries = &body->ci_entries[ci_size]; 143 | */ 144 | struct rb_call_cache *cc_entries; /* size is ci_size = ci_kw_size */ 145 | 146 | VALUE mark_ary; /* Array: includes operands which should be GC marked */ 147 | 148 | unsigned int local_table_size; 149 | unsigned int is_size; 150 | unsigned int ci_size; 151 | unsigned int ci_kw_size; 152 | unsigned int insns_info_size; 153 | unsigned int stack_max; /* for stack overflow check */ 154 | }; 155 | 156 | /* T_IMEMO/iseq */ 157 | /* typedef rb_iseq_t is in method.h */ 158 | struct rb_iseq_struct { 159 | VALUE flags; 160 | VALUE reserved1; 161 | struct rb_iseq_constant_body *body; 162 | 163 | union { /* 4, 5 words */ 164 | /* struct iseq_compile_data *compile_data; /\* used at compile time *\/ */ 165 | void *compile_data; 166 | 167 | struct { 168 | VALUE obj; 169 | int index; 170 | } loader; 171 | 172 | rb_event_flag_t trace_events; 173 | } aux; 174 | }; 175 | 176 | typedef struct rb_iseq_struct rb_iseq_t; 177 | 178 | struct rb_captured_block { 179 | VALUE self; 180 | const VALUE *ep; 181 | union { 182 | const rb_iseq_t *iseq; 183 | const struct vm_ifunc *ifunc; 184 | VALUE val; 185 | } code; 186 | }; 187 | 188 | enum rb_block_type { 189 | block_type_iseq, 190 | block_type_ifunc, 191 | block_type_symbol, 192 | block_type_proc 193 | }; 194 | 195 | enum rb_block_handler_type { 196 | block_handler_type_iseq, 197 | block_handler_type_ifunc, 198 | block_handler_type_symbol, 199 | block_handler_type_proc 200 | }; 201 | 202 | struct rb_block { 203 | union { 204 | struct rb_captured_block captured; 205 | VALUE symbol; 206 | VALUE proc; 207 | } as; 208 | enum rb_block_type type; 209 | }; 210 | 211 | typedef struct { 212 | const struct rb_block block; 213 | unsigned int is_from_method: 1; /* bool */ 214 | unsigned int is_lambda: 1; /* bool */ 215 | } rb_proc_t; 216 | 217 | /* for it */ 218 | enum method_missing_reason { FOO }; 219 | 220 | typedef struct rb_control_frame_struct { 221 | const VALUE *pc; /* cfp[0] */ 222 | VALUE *sp; /* cfp[1] */ 223 | const rb_iseq_t *iseq; /* cfp[2] */ 224 | VALUE self; /* cfp[3] / block[0] */ 225 | const VALUE *ep; /* cfp[4] / block[1] */ 226 | const void *block_code; /* cfp[5] / block[2] */ /* iseq or ifunc */ 227 | } rb_control_frame_t; 228 | 229 | typedef struct rb_execution_context_struct { 230 | /* execution information */ 231 | VALUE *vm_stack; /* must free, must mark */ 232 | size_t vm_stack_size; /* size in word (byte size / sizeof(VALUE)) */ 233 | rb_control_frame_t *cfp; 234 | 235 | /* struct rb_vm_tag *tag; */ 236 | /* struct rb_vm_protect_tag *protect_tag; */ 237 | void *tag; 238 | void *protect_tag; 239 | int safe_level; 240 | int raised_flag; 241 | 242 | /* interrupt flags */ 243 | /* rb_atomic_t interrupt_flag; */ 244 | unsigned long interrupt_flag; 245 | unsigned long interrupt_mask; 246 | 247 | /* rb_fiber_t *fiber_ptr; */ 248 | /* struct rb_thread_struct *thread_ptr; */ 249 | void *fiber_ptr; 250 | void *thread_ptr; 251 | 252 | /* storage (ec (fiber) local) */ 253 | /* st_table *local_storage; */ 254 | void *local_storage; 255 | VALUE local_storage_recursive_hash; 256 | VALUE local_storage_recursive_hash_for_trace; 257 | 258 | /* eval env */ 259 | const VALUE *root_lep; 260 | VALUE root_svar; 261 | 262 | /* ensure & callcc */ 263 | /* rb_ensure_list_t *ensure_list; */ 264 | void *ensure_list; 265 | 266 | /* trace information */ 267 | /* struct rb_trace_arg_struct *trace_arg; */ 268 | void *trace_arg; 269 | 270 | /* temporary places */ 271 | VALUE errinfo; 272 | VALUE passed_block_handler; /* for rb_iterate */ 273 | /* const rb_callable_method_entry_t *passed_bmethod_me; /\* for bmethod *\/ */ 274 | /* enum method_missing_reason method_missing_reason; */ 275 | void *passed_bmethod_me; 276 | enum method_missing_reason method_missing_reason; 277 | 278 | /* for GC */ 279 | struct { 280 | VALUE *stack_start; 281 | VALUE *stack_end; 282 | size_t stack_maxsize; 283 | #ifdef __ia64 284 | VALUE *register_stack_start; 285 | VALUE *register_stack_end; 286 | size_t register_stack_maxsize; 287 | #endif 288 | jmp_buf regs; 289 | } machine; 290 | } rb_execution_context_t; 291 | 292 | extern rb_execution_context_t *ruby_current_execution_context_ptr; 293 | -------------------------------------------------------------------------------- /ext/thats_it/thats_it_20600.h: -------------------------------------------------------------------------------- 1 | /* 2 | copy from ruby source code 3 | https://github.com/ruby/ruby/blob/trunk/COPYING.ja 4 | */ 5 | 6 | #define VM_ENV_FLAG_LOCAL 0x0002 7 | #define VM_ENV_DATA_INDEX_FLAGS ( 0) /* ep[ 0] */ 8 | #define VM_ENV_DATA_INDEX_SPECVAL (-1) /* ep[-1] */ 9 | 10 | /* for setup_it_block */ 11 | 12 | #define VM_TAGGED_PTR_REF(v, mask) ((void *)((v) & ~mask)) 13 | 14 | typedef struct rb_code_position_struct { 15 | int lineno; 16 | int column; 17 | } rb_code_position_t; 18 | 19 | typedef struct rb_code_location_struct { 20 | rb_code_position_t beg_pos; 21 | rb_code_position_t end_pos; 22 | } rb_code_location_t; 23 | 24 | typedef struct rb_iseq_location_struct { 25 | VALUE pathobj; /* String (path) or Array [path, realpath]. Frozen. */ 26 | VALUE base_label; /* String */ 27 | VALUE label; /* String */ 28 | VALUE first_lineno; /* TODO: may be unsigned short */ 29 | rb_code_location_t code_location; 30 | } rb_iseq_location_t; 31 | 32 | typedef signed long rb_snum_t; 33 | 34 | struct rb_call_info { 35 | /* fixed at compile time */ 36 | ID mid; 37 | unsigned int flag; 38 | int orig_argc; 39 | }; 40 | 41 | struct rb_iseq_constant_body { 42 | enum iseq_type { 43 | ISEQ_TYPE_TOP, 44 | ISEQ_TYPE_METHOD, 45 | ISEQ_TYPE_BLOCK, 46 | ISEQ_TYPE_CLASS, 47 | ISEQ_TYPE_RESCUE, 48 | ISEQ_TYPE_ENSURE, 49 | ISEQ_TYPE_EVAL, 50 | ISEQ_TYPE_MAIN, 51 | ISEQ_TYPE_PLAIN 52 | } type; /* instruction sequence type */ 53 | 54 | unsigned int iseq_size; 55 | const VALUE *iseq_encoded; /* encoded iseq (insn addr and operands) */ 56 | 57 | /** 58 | * parameter information 59 | * 60 | * def m(a1, a2, ..., aM, # mandatory 61 | * b1=(...), b2=(...), ..., bN=(...), # optional 62 | * *c, # rest 63 | * d1, d2, ..., dO, # post 64 | * e1:(...), e2:(...), ..., eK:(...), # keyword 65 | * **f, # keyword_rest 66 | * &g) # block 67 | * => 68 | * 69 | * lead_num = M 70 | * opt_num = N 71 | * rest_start = M+N 72 | * post_start = M+N+(*1) 73 | * post_num = O 74 | * keyword_num = K 75 | * block_start = M+N+(*1)+O+K 76 | * keyword_bits = M+N+(*1)+O+K+(&1) 77 | * size = M+N+O+(*1)+K+(&1)+(**1) // parameter size. 78 | */ 79 | 80 | struct { 81 | struct { 82 | unsigned int has_lead : 1; 83 | unsigned int has_opt : 1; 84 | unsigned int has_rest : 1; 85 | unsigned int has_post : 1; 86 | unsigned int has_kw : 1; 87 | unsigned int has_kwrest : 1; 88 | unsigned int has_block : 1; 89 | 90 | unsigned int ambiguous_param0 : 1; /* {|a|} */ 91 | } flags; 92 | 93 | unsigned int size; 94 | 95 | int lead_num; 96 | int opt_num; 97 | int rest_start; 98 | int post_start; 99 | int post_num; 100 | int block_start; 101 | 102 | const VALUE *opt_table; /* (opt_num + 1) entries. */ 103 | /* opt_num and opt_table: 104 | * 105 | * def foo o1=e1, o2=e2, ..., oN=eN 106 | * #=> 107 | * # prologue code 108 | * A1: e1 109 | * A2: e2 110 | * ... 111 | * AN: eN 112 | * AL: body 113 | * opt_num = N 114 | * opt_table = [A1, A2, ..., AN, AL] 115 | */ 116 | 117 | const struct rb_iseq_param_keyword { 118 | int num; 119 | int required_num; 120 | int bits_start; 121 | int rest_start; 122 | const ID *table; 123 | const VALUE *default_values; 124 | } *keyword; 125 | } param; 126 | 127 | rb_iseq_location_t location; 128 | 129 | /* insn info, must be freed */ 130 | struct iseq_insn_info { 131 | const struct iseq_insn_info_entry *body; 132 | unsigned int *positions; 133 | unsigned int size; 134 | struct succ_index_table *succ_index_table; 135 | } insns_info; 136 | 137 | ID *local_table; /* must free */ 138 | 139 | /* catch table */ 140 | const struct iseq_catch_table *catch_table; 141 | 142 | /* for child iseq */ 143 | const struct rb_iseq_struct *parent_iseq; 144 | struct rb_iseq_struct *local_iseq; /* local_iseq->flip_cnt can be modified */ 145 | 146 | union iseq_inline_storage_entry *is_entries; 147 | struct rb_call_info *ci_entries; /* struct rb_call_info ci_entries[ci_size]; 148 | * struct rb_call_info_with_kwarg cikw_entries[ci_kw_size]; 149 | * So that: 150 | * struct rb_call_info_with_kwarg *cikw_entries = &body->ci_entries[ci_size]; 151 | */ 152 | struct rb_call_cache *cc_entries; /* size is ci_size = ci_kw_size */ 153 | 154 | struct { 155 | rb_snum_t flip_count; 156 | VALUE coverage; 157 | VALUE *original_iseq; 158 | } variable; 159 | 160 | unsigned int local_table_size; 161 | unsigned int is_size; 162 | unsigned int ci_size; 163 | unsigned int ci_kw_size; 164 | unsigned int stack_max; /* for stack overflow check */ 165 | 166 | /* The following fields are MJIT related info. */ 167 | VALUE (*jit_func)(struct rb_execution_context_struct *, 168 | struct rb_control_frame_struct *); /* function pointer for loaded native code */ 169 | long unsigned total_calls; /* number of total calls with `mjit_exec()` */ 170 | struct rb_mjit_unit *jit_unit; 171 | char catch_except_p; /* If a frame of this ISeq may catch exception, set TRUE */ 172 | }; 173 | 174 | /* T_IMEMO/iseq */ 175 | /* typedef rb_iseq_t is in method.h */ 176 | struct rb_iseq_struct { 177 | VALUE flags; 178 | VALUE reserved1; 179 | struct rb_iseq_constant_body *body; 180 | 181 | union { /* 4, 5 words */ 182 | /* struct iseq_compile_data *compile_data; /\* used at compile time *\/ */ 183 | void *compile_data; /* used at compile time */ 184 | 185 | struct { 186 | VALUE obj; 187 | int index; 188 | } loader; 189 | 190 | rb_event_flag_t trace_events; 191 | } aux; 192 | }; 193 | 194 | typedef struct rb_iseq_struct rb_iseq_t; 195 | 196 | struct rb_captured_block { 197 | VALUE self; 198 | const VALUE *ep; 199 | union { 200 | const rb_iseq_t *iseq; 201 | const struct vm_ifunc *ifunc; 202 | VALUE val; 203 | } code; 204 | }; 205 | 206 | 207 | enum rb_block_type { 208 | block_type_iseq, 209 | block_type_ifunc, 210 | block_type_symbol, 211 | block_type_proc 212 | }; 213 | 214 | enum rb_block_handler_type { 215 | block_handler_type_iseq, 216 | block_handler_type_ifunc, 217 | block_handler_type_symbol, 218 | block_handler_type_proc 219 | }; 220 | 221 | struct rb_block { 222 | union { 223 | struct rb_captured_block captured; 224 | VALUE symbol; 225 | VALUE proc; 226 | } as; 227 | enum rb_block_type type; 228 | }; 229 | 230 | typedef struct { 231 | const struct rb_block block; 232 | unsigned int is_from_method: 1; /* bool */ 233 | unsigned int is_lambda: 1; /* bool */ 234 | } rb_proc_t; 235 | 236 | /* for it */ 237 | enum method_missing_reason { FOO }; 238 | 239 | typedef struct rb_control_frame_struct { 240 | const VALUE *pc; 241 | VALUE *sp; 242 | const rb_iseq_t *iseq; 243 | VALUE self; 244 | const VALUE *ep; 245 | const void *block_code; 246 | const VALUE *bp; 247 | } rb_control_frame_t; 248 | 249 | typedef struct rb_execution_context_struct { 250 | VALUE *vm_stack; 251 | size_t vm_stack_size; 252 | rb_control_frame_t *cfp; 253 | /* struct rb_vm_tag *tag; */ 254 | /* struct rb_vm_protect_tag *protect_tag; */ 255 | void *tag; 256 | void *protect_tag; 257 | int raised_flag; 258 | /* rb_atomic_t interrupt_flag; */ 259 | /* rb_atomic_t interrupt_mask; */ 260 | unsigned int interrupt_flag; 261 | unsigned int interrupt_mask; 262 | /* rb_fiber_t *fiber_ptr; */ 263 | /* struct rb_thread_struct *thread_ptr; */ 264 | /* st_table *local_storage; */ 265 | void *fiber_ptr; 266 | void *thread_ptr; 267 | void *local_storage; 268 | VALUE local_storage_recursive_hash; 269 | VALUE local_storage_recursive_hash_for_trace; 270 | const VALUE *root_lep; 271 | VALUE root_svar; 272 | /* rb_ensure_list_t *ensure_list; */ 273 | /* struct rb_trace_arg_struct *trace_arg; */ 274 | void *ensure_list; 275 | void *trace_arg; 276 | VALUE errinfo; 277 | VALUE passed_block_handler; 278 | /* const rb_callable_method_entry_t *passed_bmethod_me; */ 279 | /* enum method_missing_reason method_missing_reason; */ 280 | void *passed_bmethod_me; 281 | enum method_missing_reason method_missing_reason; 282 | struct { 283 | VALUE *stack_start; 284 | VALUE *stack_end; 285 | size_t stack_maxsize; 286 | __attribute__((__aligned__(8))) jmp_buf regs; 287 | } machine; 288 | } rb_execution_context_t; 289 | 290 | extern rb_execution_context_t *ruby_current_execution_context_ptr; 291 | --------------------------------------------------------------------------------