public
Description: a Ruby VM/interpreter
Homepage: http://rubyex.sairyx.org/
Clone URL: git://github.com/celtic/rubyex.git
rubyex / vm / rmethod.cpp
100644 143 lines (109 sloc) 5.465 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#include <iostream>
#include <sstream>
#include "rmethod.h"
#include "rexception.h"
 
RubyMethod::~RubyMethod()
{ }
 
linked_ptr<RubyMethod> RubyMethod::Create(RCMethodBlockNoArgs _function)
{ return linked_ptr<RubyMethod>(new RubyCMethod(_function)); }
 
linked_ptr<RubyMethod> RubyMethod::Create(RCMethodNoArgs _function)
{ return linked_ptr<RubyMethod>(new RubyCMethod(_function)); }
 
linked_ptr<RubyMethod> RubyMethod::Create(RCMethodBlockArgs _function, int _args)
{ return linked_ptr<RubyMethod>(new RubyCMethod(_function, _args)); }
 
linked_ptr<RubyMethod> RubyMethod::Create(RCMethodArgs _function, int _args)
{ return linked_ptr<RubyMethod>(new RubyCMethod(_function, _args)); }
 
bool RubyMethod::verify_args(int _base, int _their) {
  if (_base >= 0)
    return (_base == _their);
  return (_their >= (-_base - 1));
}
 
std::string RubyMethod::args_error_message(int _base, int _their) {
  std::ostringstream oss;
  oss << "wrong number of arguments (";
 
  if (_base >= 0)
    oss << _their << " for " << _base;
  else
    oss << _their << ", wanted at least " << (-_base - 1);
  oss << ")";
  return oss.str();
}
 
RubyValue RubyMethod::call(linked_ptr<Binding> &_b, RubyValue _self) {
  return call(_b, _self, std::vector<RubyValue>());
}
 
RubyValue RubyMethod::call(linked_ptr<Binding> &_b, RubyValue _self, Block &_block) {
  return call(_b, _self, std::vector<RubyValue>(), _block);
}
 
// actual CMethod implementations
 
RubyCMethod::RubyCMethod(RCMethodBlockNoArgs _function):
function((void *)_function), has_block(true), no_of_args(0) { }
 
RubyCMethod::RubyCMethod(RCMethodNoArgs _function):
function((void *)_function), has_block(false), no_of_args(0) { }
 
RubyCMethod::RubyCMethod(RCMethodBlockArgs _function, int _args):
function((void *)_function), has_block(true), no_of_args(_args) { }
 
RubyCMethod::RubyCMethod(RCMethodArgs _function, int _args):
function((void *)_function), has_block(false), no_of_args(_args) { }
 
RubyValue RubyCMethod::call(linked_ptr<Binding> &_b, RubyValue _self, const std::vector<RubyValue> &_args) {
  if (has_block)
    throw WorldException(_b, _b->environment.ArgumentError, "no block given");
  if (!verify_args(no_of_args, (int)_args.size()))
    throw WorldException(_b, _b->environment.ArgumentError, args_error_message(no_of_args, (int)_args.size()));
  
  if (no_of_args == 0)
    return ((RCMethodNoArgs)function)(_b, _self);
  else
    return ((RCMethodArgs)function)(_b, _self, _args);
}
 
RubyValue RubyCMethod::call(linked_ptr<Binding> &_b, RubyValue _self, const std::vector<RubyValue> &_args, Block &_block) {
  if (!has_block)
    return call(_b, _self, _args); // just drop the block silently.
 
  // this code path is only for `has_block'
 
  if (!verify_args(no_of_args, (int)_args.size()))
    throw WorldException(_b, _b->environment.ArgumentError, args_error_message(no_of_args, (int)_args.size()));
  
  if (no_of_args == 0)
    return ((RCMethodBlockNoArgs)function)(_b, _self, _block);
  else
    return ((RCMethodBlockArgs)function)(_b, _self, _args, _block);
}
 
// MultiCMethod
 
RubyMultiCMethod::RubyMultiCMethod(const linked_ptr<RubyCMethod> &_0, const linked_ptr<RubyCMethod> &_1) {
  methods.push_back(_0);
  methods.push_back(_1);
}
 
RubyMultiCMethod::RubyMultiCMethod(const linked_ptr<RubyCMethod> &_0, const linked_ptr<RubyCMethod> &_1, const linked_ptr<RubyCMethod> &_2) {
  methods.push_back(_0);
  methods.push_back(_1);
  methods.push_back(_2);
}
 
RubyValue RubyMultiCMethod::call(linked_ptr<Binding> &_b, RubyValue _self, const std::vector<RubyValue> &_args) {
  for (std::vector< linked_ptr<RubyCMethod> >::iterator it = methods.begin(); it != methods.end(); ++it) {
    linked_ptr<RubyCMethod> &cm = *it;
    if (verify_args(cm->no_of_args, _args.size()) && !cm->has_block)
      return cm->call(_b, _self, _args);
  }
 
  throw WorldException(_b, _b->environment.ArgumentError, "cannot find matching method for " + Binding::get_representation(_b, _args));
}
 
RubyValue RubyMultiCMethod::call(linked_ptr<Binding> &_b, RubyValue _self, const std::vector<RubyValue> &_args, Block &_block) {
  for (std::vector< linked_ptr<RubyCMethod> >::iterator it = methods.begin(); it != methods.end(); ++it) {
    linked_ptr<RubyCMethod> &cm = *it;
    if (verify_args(cm->no_of_args, _args.size()) && cm->has_block)
      return cm->call(_b, _self, _args, _block);
  }
 
  throw WorldException(_b, _b->environment.ArgumentError, "cannot find matching method for " + Binding::get_representation(_b, _args));
}
 
// BytecodeMethod
 
RubyBytecodeMethod::RubyBytecodeMethod(RubyModule *_def_target): code(_def_target, NULL /* no pre-supplied block */, NULL /* no outer context */)
{ }
 
RubyValue RubyBytecodeMethod::call(linked_ptr<Binding> &_b, RubyValue _self, const std::vector<RubyValue> &_args)
{
  if (!code.can_take_args((int)_args.size()))
    // This error message will be wrong. XXX
    throw WorldException(_b, _b->environment.ArgumentError, "XXX: " + args_error_message(code.args.size(), (int)_args.size()));
  // _self is passed as the new context.
  return code.call(_b, _self, _args);
}
 
RubyValue RubyBytecodeMethod::call(linked_ptr<Binding> &_b, RubyValue _self, const std::vector<RubyValue> &_args, Block &_block)
{
  if (!code.can_take_args((int)_args.size()))
    // This error message will be wrong. XXX
    throw WorldException(_b, _b->environment.ArgumentError, "XXX: " + args_error_message(code.args.size(), (int)_args.size()));
  return code.call(_b, _self, _args, _block);
}