Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

1936 lines (1548 sloc) 53.402 kB
#include "arguments.hpp"
#include "builtin/array.hpp"
#include "builtin/bignum.hpp"
#include "builtin/block_environment.hpp"
#include "builtin/channel.hpp"
#include "builtin/class.hpp"
#include "builtin/compact_lookup_table.hpp"
#include "builtin/constant_scope.hpp"
#include "builtin/exception.hpp"
#include "builtin/fixnum.hpp"
#include "builtin/float.hpp"
#include "builtin/io.hpp"
#include "builtin/location.hpp"
#include "builtin/lookup_table.hpp"
#include "builtin/method_table.hpp"
#include "builtin/thread.hpp"
#include "builtin/tuple.hpp"
#include "builtin/string.hpp"
#include "builtin/symbol.hpp"
#include "builtin/system.hpp"
#include "builtin/variable_scope.hpp"
#include "call_frame.hpp"
#include "compiled_file.hpp"
#include "configuration.hpp"
#include "config_parser.hpp"
#include "dtrace/dtrace.h"
#include "gc/walker.hpp"
#include "global_cache.hpp"
#include "helpers.hpp"
#include "instruments/tooling.hpp"
#include "lookup_data.hpp"
#include "object_memory.hpp"
#include "object_utils.hpp"
#include "on_stack.hpp"
#include "signal.hpp"
#include "windows_compat.h"
#include "util/sha1.h"
#include "util/timing.h"
#include "util/logger.hpp"
#include "paths.h"
#include <vector>
#include <errno.h>
#include <fcntl.h>
#include <iostream>
#include <fstream>
#include <stdarg.h>
#include <string.h>
#include <sstream>
#include <signal.h>
#include <unistd.h>
#ifndef RBX_WINDOWS
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <pwd.h>
#include <dlfcn.h>
#endif
#ifdef ENABLE_LLVM
#include "llvm/state.hpp"
#include "llvm/jit_context.hpp"
#include "llvm/jit_compiler.hpp"
#endif
#include "missing/setproctitle.h"
namespace rubinius {
void System::bootstrap_methods(STATE) {
GCTokenImpl gct;
System::attach_primitive(state, gct,
G(rubinius), true,
state->symbol("open_class"),
state->symbol("vm_open_class"));
System::attach_primitive(state, gct,
G(rubinius), true,
state->symbol("open_class_under"),
state->symbol("vm_open_class_under"));
System::attach_primitive(state, gct,
G(rubinius), true,
state->symbol("open_module"),
state->symbol("vm_open_module"));
System::attach_primitive(state, gct,
G(rubinius), true,
state->symbol("open_module_under"),
state->symbol("vm_open_module_under"));
System::attach_primitive(state, gct,
G(rubinius), true,
state->symbol("add_defn_method"),
state->symbol("vm_add_method"));
System::attach_primitive(state, gct,
G(rubinius), true,
state->symbol("attach_method"),
state->symbol("vm_attach_method"));
System::attach_primitive(state, gct,
as<Module>(G(rubinius)->get_const(state, "Type")), true,
state->symbol("object_singleton_class"),
state->symbol("vm_object_singleton_class"));
}
void System::attach_primitive(STATE, GCToken gct, Module* mod, bool meta,
Symbol* name, Symbol* prim)
{
MethodTable* tbl;
if(meta) {
tbl = mod->singleton_class(state)->method_table();
} else {
tbl = mod->method_table();
}
Executable* oc = Executable::allocate(state, cNil);
oc->primitive(state, prim);
oc->resolve_primitive(state);
tbl->store(state, name, oc, G(sym_public));
}
/* Primitives */
//
// HACK: remove this when performance is better and compiled_file.rb
// unmarshal_data method works.
Object* System::compiledfile_load(STATE, String* path,
Integer* signature, Integer* version)
{
std::ifstream stream(path->c_str(state));
if(!stream) {
return Primitives::failure();
}
CompiledFile* cf = CompiledFile::load(stream);
if(cf->magic != "!RBIX") {
delete cf;
return Primitives::failure();
}
uint64_t sig = signature->to_ulong_long();
if((sig > 0 && cf->signature != sig)) {
delete cf;
return Primitives::failure();
}
Object *body = cf->body(state);
delete cf;
return body;
}
Object* System::yield_gdb(STATE, Object* obj) {
obj->show(state);
Exception::assertion_error(state, "yield_gdb called and not caught");
return obj;
}
void exec_sh_fallback(STATE, const char* c_str, size_t c_len) {
char* s = const_cast<char*>(c_str);
bool use_sh = false;
for(;*s;s++) {
if(*s != ' ' && !ISALPHA(*s) && strchr("*?{}[]<>()~&|\\$;'`\"\n\t\r\f\v",*s)) {
use_sh = true;
break;
}
}
if(use_sh) {
execl("/bin/sh", "sh", "-c", c_str, (char*)0);
} else {
size_t max_spaces = (c_len / 2) + 2;
char** args = new char*[max_spaces];
// Now put nulls for spaces into c_str and assign each bit
// to args to create the array of char*s that execv wants.
s = const_cast<char*>(c_str);
const char* s_end = c_str + c_len;
int idx = 0;
for(;;) {
// turn the next group of spaces into nulls.
while(s < s_end && *s == ' ') {
*s = 0;
s++;
}
// Hit the end, bail.
if(s == s_end) break;
// Write the address of the next chunk here.
args[idx++] = s;
// Skip to the next space
while(s < s_end && *s != ' ') s++;
}
args[idx] = 0;
// If we added anything, then exec, otherwise fall through and fail.
if(idx > 0) execvp(args[0], args);
// If we failed, clean up the args.
delete[] args;
}
}
class ExecCommand {
char* command_;
size_t command_size_;
size_t argc_;
char** argv_;
char* make_string(STATE, String* source) {
const char* src = source->c_str_null_safe(state);
size_t len = strnlen(src, source->byte_size());
char* str = new char[len + 1];
memcpy(str, src, len);
str[len] = 0;
return str;
}
public:
ExecCommand(STATE, String* command)
: argc_(0)
, argv_(NULL)
{
command_ = make_string(state, command);
command_size_ = command->byte_size();
}
ExecCommand(STATE, String* command, Array* args) {
command_ = make_string(state, command);
command_size_ = command->byte_size();
argc_ = args->size();
argv_ = NULL;
if(argc_ > 0) {
argv_ = new char*[argc_ + 1];
/* execvp() requires a NULL as last element */
argv_[argc_] = NULL;
for(size_t i = 0; i < argc_; i++) {
/* POSIX guarantees that execvp does not modify the characters to
* which the argv pointers point, despite the argument not being
* declared as const char *const[].
*/
argv_[i] = make_string(state, as<String>(args->get(state, i)));
}
}
}
~ExecCommand() {
if(argc_ > 0 && argv_) {
for(size_t i = 0; i < argc_; i++) {
delete[] argv_[i];
}
delete[] argv_;
}
delete[] command_;
}
char* command() {
return command_;
}
size_t command_size() {
return command_size_;
}
size_t argc() {
return argc_;
}
char** argv() {
return argv_;
}
};
static void redirect_file_descriptor(int from, int to) {
if(dup2(to, from) < 0) return;
int flags = fcntl(from, F_GETFD);
if(flags < 0) return;
fcntl(from, F_SETFD, flags & ~FD_CLOEXEC);
}
Object* System::vm_spawn_setup(STATE, Object* spawn_state) {
if(LookupTable* table = try_as<LookupTable>(spawn_state)) {
if(Array* env = try_as<Array>(
table->fetch(state, state->symbol("env"))))
{
native_int size = env->size();
for(native_int i = 0; i < size; i += 2) {
const char* key = as<String>(env->get(state, i))->c_str_null_safe(state);
Object* value = env->get(state, i + 1);
if(value->nil_p()) {
unsetenv(key);
} else {
setenv(key, as<String>(value)->c_str_null_safe(state), 1);
}
}
}
if(Fixnum* pgrp = try_as<Fixnum>(
table->fetch(state, state->symbol("pgroup"))))
{
setpgid(0, pgrp->to_native());
}
if(Fixnum* mask = try_as<Fixnum>(
table->fetch(state, state->symbol("umask"))))
{
umask(mask->to_native());
}
if(String* str = try_as<String>(
table->fetch(state, state->symbol("chdir"))))
{
const char* dir = str->c_str_null_safe(state);
if(chdir(dir) < 0) {
utilities::logger::error("%s: spawn: failed to change directory: %s",
strerror(errno), dir);
}
}
if(CBOOL(table->has_key(state, state->symbol("close_others")))) {
int max = IO::max_descriptors();
int flags;
for(int fd = STDERR_FILENO + 1; fd < max; fd++) {
if((flags = fcntl(fd, F_GETFD)) >= 0) {
fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
}
}
}
if(Array* assign = try_as<Array>(
table->fetch(state, state->symbol("assign_fd"))))
{
native_int size = assign->size();
for(native_int i = 0; i < size; i += 4) {
int from = as<Fixnum>(assign->get(state, i))->to_native();
int mode = as<Fixnum>(assign->get(state, i + 2))->to_native();
int perm = as<Fixnum>(assign->get(state, i + 3))->to_native();
const char* name = as<String>(assign->get(state, i + 1))->c_str_null_safe(state);
int to = IO::open_with_cloexec(state, name, mode, perm);
redirect_file_descriptor(from, to);
}
}
if(Array* redirect = try_as<Array>(
table->fetch(state, state->symbol("redirect_fd"))))
{
native_int size = redirect->size();
for(native_int i = 0; i < size; i += 2) {
int from = as<Fixnum>(redirect->get(state, i))->to_native();
int to = as<Fixnum>(redirect->get(state, i + 1))->to_native();
redirect_file_descriptor(from, to < 0 ? (-to + 1) : to);
}
}
}
return cNil;
}
static int fork_exec(STATE, GCToken gct, CallFrame* call_frame, int errors_fd) {
utilities::thread::Mutex::LockGuard guard(state->shared().fork_exec_lock());
state->shared().internal_threads()->before_fork_exec(state);
// If execvp() succeeds, we'll read EOF and know.
fcntl(errors_fd, F_SETFD, FD_CLOEXEC);
int pid;
state->gc_dependent(gct, call_frame);
{
StopTheWorld stw(state, gct, call_frame);
pid = ::fork();
if(pid == 0) state->shared().reinit_world();
}
state->gc_independent(gct, call_frame);
if(pid > 0) {
state->shared().internal_threads()->after_fork_exec_parent(state);
}
return pid;
}
Object* System::vm_spawn(STATE, GCToken gct, Object* spawn_state, String* path,
Array* args, CallFrame* calling_environment)
{
OnStack<1> os(state, spawn_state);
/* Setting up the command and arguments may raise an exception so do it
* before everything else.
*/
ExecCommand exe(state, path, args);
int errors[2];
if(pipe(errors) != 0) {
Exception::errno_error(state, "error setting up pipes", errno, "pipe(2)");
return NULL;
}
int pid;
{
GCIndependent guard(state, 0);
pid = fork_exec(state, gct, calling_environment, errors[1]);
}
// error
if(pid == -1) {
close(errors[0]);
close(errors[1]);
Exception::errno_error(state, "error forking", errno, "fork(2)");
return NULL;
}
if(pid == 0) {
close(errors[0]);
state->vm()->thread->init_lock();
state->shared().internal_threads()->after_fork_exec_child(state);
// Setup ENV, redirects, groups, etc. in the child before exec().
vm_spawn_setup(state, spawn_state);
/* Reset all signal handlers to the defaults, so any we setup in
* Rubinius won't leak through. We need to use sigaction() here since
* signal() provides no control over SA_RESTART and can use the wrong
* value causing blocking I/O methods to become uninterruptable.
*/
for(int i = 1; i < NSIG; i++) {
struct sigaction action;
action.sa_handler = SIG_DFL;
action.sa_flags = 0;
sigfillset(&action.sa_mask);
sigaction(i, &action, NULL);
}
utilities::logger::write("spawn: %s", exe.command());
if(exe.argc()) {
(void)::execvp(exe.command(), exe.argv());
} else {
exec_sh_fallback(state, exe.command(), exe.command_size());
}
/* execvp() returning means it failed. */
std::ostringstream command_line;
command_line << exe.command();
for(size_t i = 0; i < exe.argc(); i++) {
command_line << " " << exe.argv()[i];
}
utilities::logger::error("%s: spawn: exec failed: %s",
strerror(errno), command_line.str().c_str());
int error_no = errno;
if(write(errors[1], &error_no, sizeof(int)) < 0) {
utilities::logger::error("%s: spawn: writing error status", strerror(errno));
}
close(errors[1]);
exit(1);
}
close(errors[1]);
int error_no;
ssize_t size;
while((size = read(errors[0], &error_no, sizeof(int))) < 0) {
switch(errno) {
case EAGAIN:
case EINTR:
continue;
default:
utilities::logger::error("%s: spawn: reading error status", strerror(errno));
break;
}
}
close(errors[0]);
if(size != 0) {
Exception::errno_error(state, "execvp(2) failed", error_no);
return NULL;
}
return Fixnum::from(pid);
}
Object* System::vm_backtick(STATE, GCToken gct, String* str,
CallFrame* calling_environment)
{
#ifdef RBX_WINDOWS
// TODO: Windows
return Primitives::failure();
#else
/* Setting up the command may raise an exception so do it before
* everything else.
*/
ExecCommand exe(state, str);
int errors[2], output[2];
if(pipe(errors) != 0) {
Exception::errno_error(state, "error setting up pipes", errno, "pipe(2)");
return NULL;
}
if(pipe(output) != 0) {
Exception::errno_error(state, "error setting up pipes", errno, "pipe(2)");
return NULL;
}
int pid;
{
GCIndependent guard(state, 0);
pid = fork_exec(state, gct, calling_environment, errors[1]);
}
// error
if(pid == -1) {
close(errors[0]);
close(errors[1]);
close(output[0]);
close(output[1]);
Exception::errno_error(state, "error forking", errno, "fork(2)");
return NULL;
}
if(pid == 0) {
state->vm()->thread->init_lock();
state->shared().internal_threads()->after_fork_exec_child(state);
close(errors[0]);
close(output[0]);
dup2(output[1], STDOUT_FILENO);
close(output[1]);
/* Reset all signal handlers to the defaults, so any we setup in
* Rubinius won't leak through. We need to use sigaction() here since
* signal() provides no control over SA_RESTART and can use the wrong
* value causing blocking I/O methods to become uninterruptable.
*/
for(int i = 1; i < NSIG; i++) {
struct sigaction action;
action.sa_handler = SIG_DFL;
action.sa_flags = 0;
sigfillset(&action.sa_mask);
sigaction(i, &action, NULL);
}
utilities::logger::write("backtick: %s", exe.command());
exec_sh_fallback(state, exe.command(), exe.command_size());
/* execvp() returning means it failed. */
utilities::logger::error("%s: backtick: exec failed: %s",
strerror(errno), exe.command());
int error_no = errno;
if(write(errors[1], &error_no, sizeof(int)) < 0) {
utilities::logger::error("%s: backtick: writing error status", strerror(errno));
}
close(errors[1]);
exit(1);
}
close(errors[1]);
close(output[1]);
int error_no;
ssize_t size;
while((size = read(errors[0], &error_no, sizeof(int))) < 0) {
switch(errno) {
case EAGAIN:
case EINTR:
continue;
default:
utilities::logger::error("%s: backtick: reading error status", strerror(errno));
break;
}
}
close(errors[0]);
if(size != 0) {
close(output[0]);
Exception::errno_error(state, "execvp(2) failed", error_no);
return NULL;
}
std::string buf;
for(;;) {
ssize_t bytes = 0;
char raw_buf[1024];
{
GCIndependent guard(state, calling_environment);
bytes = read(output[0], raw_buf, 1023);
}
if(bytes < 0) {
switch(errno) {
case EAGAIN:
case EINTR:
if(!state->check_async(calling_environment)) {
close(output[0]);
return NULL;
}
continue;
default:
close(output[0]);
Exception::errno_error(state, "reading child data", errno, "read(2)");
}
}
if(bytes == 0) {
break;
}
buf.append(raw_buf, bytes);
}
close(output[0]);
return Tuple::from(state, 2, Fixnum::from(pid),
String::create(state, buf.c_str(), buf.size()));
#endif // RBX_WINDOWS
}
Object* System::vm_exec(STATE, String* path, Array* args,
CallFrame* calling_environment)
{
utilities::thread::Mutex::LockGuard guard(state->shared().fork_exec_lock());
/* Setting up the command and arguments may raise an exception so do it
* before everything else.
*/
ExecCommand exe(state, path, args);
state->shared().internal_threads()->before_exec(state);
void* old_handlers[NSIG];
/* Reset all signal handlers to the defaults, so any we setup in Rubinius
* won't leak through. We need to use sigaction() here since signal()
* provides no control over SA_RESTART and can use the wrong value causing
* blocking I/O methods to become uninterruptable.
*/
for(int i = 1; i < NSIG; i++) {
struct sigaction action;
struct sigaction old_action;
action.sa_handler = SIG_DFL;
action.sa_flags = 0;
sigfillset(&action.sa_mask);
sigaction(i, &action, &old_action);
old_handlers[i] = (void*)old_action.sa_handler;
}
utilities::logger::write("exec: %s", exe.command());
if(exe.argc()) {
(void)::execvp(exe.command(), exe.argv());
} else {
exec_sh_fallback(state, exe.command(), exe.command_size());
}
int erno = errno;
// Hmmm, execvp failed, we need to recover here.
for(int i = 1; i < NSIG; i++) {
struct sigaction action;
action.sa_handler = (void(*)(int))old_handlers[i];
action.sa_flags = 0;
sigfillset(&action.sa_mask);
sigaction(i, &action, NULL);
}
state->shared().internal_threads()->after_exec(state);
/* execvp() returning means it failed. */
Exception::errno_error(state, "execvp(2) failed", erno);
return NULL;
}
Object* System::vm_wait_pid(STATE, Fixnum* pid_obj, Object* no_hang,
CallFrame* calling_environment)
{
#ifdef RBX_WINDOWS
// TODO: Windows
return Primitives::failure();
#else
pid_t input_pid = pid_obj->to_native();
int options = 0;
int status;
pid_t pid;
if(CBOOL(no_hang)) {
options |= WNOHANG;
}
typedef void (*rbx_sighandler_t)(int);
rbx_sighandler_t hup_func;
rbx_sighandler_t quit_func;
rbx_sighandler_t int_func;
retry:
hup_func = signal(SIGHUP, SIG_IGN);
quit_func = signal(SIGQUIT, SIG_IGN);
int_func = signal(SIGINT, SIG_IGN);
{
GCIndependent guard(state, calling_environment);
pid = waitpid(input_pid, &status, options);
}
signal(SIGHUP, hup_func);
signal(SIGQUIT, quit_func);
signal(SIGINT, int_func);
if(pid == -1) {
if(errno == ECHILD) return cFalse;
if(errno == EINTR) {
if(!state->check_async(calling_environment)) return NULL;
goto retry;
}
// TODO handle other errnos?
return cFalse;
}
if(CBOOL(no_hang) && pid == 0) {
return cNil;
}
Object* output = cNil;
Object* termsig = cNil;
Object* stopsig = cNil;
if(WIFEXITED(status)) {
output = Fixnum::from(WEXITSTATUS(status));
} else if(WIFSIGNALED(status)) {
termsig = Fixnum::from(WTERMSIG(status));
} else if(WIFSTOPPED(status)){
stopsig = Fixnum::from(WSTOPSIG(status));
}
return Tuple::from(state, 4, output, termsig, stopsig, Fixnum::from(pid));
#endif // RBX_WINDOWS
}
Object* System::vm_exit(STATE, Fixnum* code) {
state->vm()->thread_state()->raise_exit(code);
return NULL;
}
Fixnum* System::vm_fork(STATE, GCToken gct, CallFrame* calling_environment)
{
#ifdef RBX_WINDOWS
// TODO: Windows
return force_as<Fixnum>(Primitives::failure());
#else
int pid = -1;
{
utilities::thread::Mutex::LockGuard guard(state->shared().fork_exec_lock());
state->shared().internal_threads()->before_fork(state);
{
StopTheWorld stw(state, gct, calling_environment);
pid = ::fork();
if(pid == 0) state->shared().reinit_world();
}
if(pid > 0) {
state->shared().internal_threads()->after_fork_parent(state);
utilities::logger::write("fork: child: %d", pid);
}
}
// We're in the child...
if(pid == 0) {
/* @todo any other re-initialisation needed? */
state->vm()->thread->init_lock();
state->shared().after_fork_child(state, gct, calling_environment);
state->shared().internal_threads()->after_fork_child(state);
// In the child, the PID is nil in Ruby.
return nil<Fixnum>();
}
if(pid == -1) {
Exception::errno_error(state, "fork(2) failed");
return NULL;
}
return Fixnum::from(pid);
#endif
}
Object* System::vm_gc_start(STATE, GCToken gct, Object* force, CallFrame* call_frame) {
// force is set if this is being called by the kernel (for instance
// in File#ininitialize). If we decided to ignore some GC.start calls
// by usercode trying to be clever, we can use force to know that we
// should NOT ignore it.
if(CBOOL(force) || state->shared().config.gc_honor_start) {
state->memory()->collect_young_now = true;
state->memory()->collect_mature_now = true;
state->vm()->collect_maybe(gct, call_frame);
}
return cNil;
}
Object* System::vm_get_config_item(STATE, String* var) {
ConfigParser::Entry* ent = state->shared().user_variables.find(var->c_str(state));
if(!ent) return cNil;
if(ent->is_number()) {
return Integer::from_cppstr(state, ent->value, 10);
} else if(ent->is_true()) {
return cTrue;
}
return String::create(state, ent->value.c_str(), ent->value.size());
}
Object* System::vm_get_config_section(STATE, String* section) {
ConfigParser::EntryList* list;
list = state->shared().user_variables.get_section(
reinterpret_cast<char*>(section->byte_address()));
Array* ary = Array::create(state, list->size());
for(size_t i = 0; i < list->size(); i++) {
std::string variable = list->at(i)->variable;
std::string value = list->at(i)->value;
String* var = String::create(state, variable.c_str(), variable.size());
String* val = String::create(state, value.c_str(), value.size());
ary->set(state, i, Tuple::from(state, 2, var, val));
}
delete list;
return ary;
}
Object* System::vm_reset_method_cache(STATE, Module* mod, Symbol* name, CallFrame* calling_environment) {
if(!state->vm()->global_cache()->has_seen(state, name)) return cTrue;
state->vm()->global_cache()->clear(state, name);
mod->reset_method_cache(state, name);
state->vm()->metrics().machine.inline_cache_resets++;
if(state->shared().config.ic_debug) {
String* mod_name = mod->get_name(state);
if(mod_name->nil_p()) {
mod_name = String::create(state, "");
}
std::cout << "[IC Increase serial for " << mod_name->c_str(state) << "]" << std::endl;
std::cout << "[IC Reset method cache for " << mod_name->c_str(state)
<< "#" << name->debug_str(state).c_str() << "]" << std::endl;
CallFrame* call_frame = calling_environment->previous;
call_frame->print_backtrace(state, 6, true);
}
return cTrue;
}
/* @todo Could possibly capture the system backtrace at this
* point. --rue
*/
Array* System::vm_backtrace(STATE, Fixnum* skip, Object* inc_vars,
CallFrame* calling_environment) {
CallFrame* call_frame = calling_environment;
bool include_vars = CBOOL(inc_vars);
for(native_int i = skip->to_native(); call_frame && i > 0; --i) {
call_frame = call_frame->previous;
}
return Location::from_call_stack(state, call_frame, include_vars);
}
Array* System::vm_mri_backtrace(STATE, Fixnum* skip,
CallFrame* calling_environment) {
CallFrame* call_frame = calling_environment;
for(native_int i = skip->to_native(); call_frame && i > 0; --i) {
call_frame = call_frame->previous;
}
return Location::mri_backtrace(state, call_frame);
}
Object* System::vm_show_backtrace(STATE, CallFrame* calling_environment) {
calling_environment->print_backtrace(state);
return cNil;
}
Object* System::vm_tooling_available_p(STATE) {
#ifdef RBX_PROFILER
return RBOOL(state->shared().tool_broker()->available(state));
#else
return cFalse;
#endif
}
Object* System::vm_tooling_active_p(STATE) {
return RBOOL(state->vm()->tooling());
}
Object* System::vm_tooling_enable(STATE) {
state->shared().tool_broker()->enable(state);
return cTrue;
}
Object* System::vm_tooling_disable(STATE) {
return state->shared().tool_broker()->results(state);
}
Object* System::vm_load_tool(STATE, String* str) {
std::string path = std::string(str->c_str(state)) + ".";
#ifdef _WIN32
path += "dll";
#else
#ifdef __APPLE_CC__
path += "bundle";
#else
path += "so";
#endif
#endif
void* handle = dlopen(path.c_str(), RTLD_NOW);
if(!handle) {
path = std::string(RBX_LIB_PATH) + "/" + path;
handle = dlopen(path.c_str(), RTLD_NOW);
if(!handle) {
return Tuple::from(state, 2, cFalse, String::create(state, dlerror()));
}
}
void* sym = dlsym(handle, "Tool_Init");
if(!sym) {
dlclose(handle);
return Tuple::from(state, 2, cFalse, String::create(state, dlerror()));
} else {
typedef int (*init_func)(rbxti::Env* env);
init_func init = (init_func)sym;
if(!init(state->vm()->tooling_env())) {
dlclose(handle);
return Tuple::from(state, 2, cFalse, String::create(state, path.c_str(), path.size()));
}
}
return Tuple::from(state, 1, cTrue);
}
Object* System::vm_write_error(STATE, String* str) {
std::cerr << str->c_str(state) << std::endl;
return cNil;
}
Object* System::vm_watch_signal(STATE, Fixnum* sig, Object* ignored) {
SignalThread* st = state->shared().signals();
if(st) {
native_int i = sig->to_native();
if(i < 0) {
st->add_signal_handler(state, -i, SignalThread::eDefault);
} else if(i > 0) {
st->add_signal_handler(state, i,
CBOOL(ignored) ? SignalThread::eIgnore : SignalThread::eCustom);
}
return cTrue;
} else {
return cFalse;
}
}
Object* System::vm_time(STATE) {
return Integer::from(state, time(0));
}
#define NANOSECONDS 1000000000
Object* System::vm_sleep(STATE, GCToken gct, Object* duration,
CallFrame* calling_environment)
{
struct timespec ts = {0,0};
bool use_timed_wait = true;
if(Fixnum* fix = try_as<Fixnum>(duration)) {
if(!fix->positive_p()) {
Exception::argument_error(state, "time interval must be positive");
}
ts.tv_sec = fix->to_native();
} else if(Float* flt = try_as<Float>(duration)) {
if(flt->val < 0.0) {
Exception::argument_error(state, "time interval must be positive");
}
uint64_t nano = (uint64_t)(flt->val * NANOSECONDS);
ts.tv_sec = (time_t)(nano / NANOSECONDS);
ts.tv_nsec = (long)(nano % NANOSECONDS);
} else if(duration == G(undefined)) {
use_timed_wait = false;
} else {
return Primitives::failure();
}
time_t start = time(0);
if(use_timed_wait) {
struct timeval tv = {0,0};
gettimeofday(&tv, 0);
uint64_t nano = ts.tv_nsec + tv.tv_usec * 1000;
ts.tv_sec += tv.tv_sec + nano / NANOSECONDS;
ts.tv_nsec = nano % NANOSECONDS;
if(!state->park_timed(gct, calling_environment, &ts)) return NULL;
} else {
if(!state->park(gct, calling_environment)) return NULL;
}
if(!state->check_async(calling_environment)) return NULL;
return Fixnum::from(time(0) - start);
}
Object* System::vm_check_interrupts(STATE, CallFrame* calling_environment) {
if(state->check_async(calling_environment)) {
return cNil;
} else {
return NULL;
}
}
static inline double tv_to_dbl(struct timeval* tv) {
return (double)tv->tv_sec + ((double)tv->tv_usec / 1000000.0);
}
Array* System::vm_times(STATE) {
#ifdef RBX_WINDOWS
// TODO: Windows
return force_as<Array>(Primitives::failure());
#else
struct rusage buf;
Array* ary = Array::create(state, 4);
getrusage(RUSAGE_SELF, &buf);
ary->set(state, 0, Float::create(state, tv_to_dbl(&buf.ru_utime)));
ary->set(state, 1, Float::create(state, tv_to_dbl(&buf.ru_stime)));
getrusage(RUSAGE_CHILDREN, &buf);
ary->set(state, 2, Float::create(state, tv_to_dbl(&buf.ru_utime)));
ary->set(state, 3, Float::create(state, tv_to_dbl(&buf.ru_stime)));
uint64_t sys = 0;
uint64_t usr = 0;
thread_cpu_usage(&usr, &sys);
ary->set(state, 4, Float::create(state, (double)usr / 1000000.0));
ary->set(state, 5, Float::create(state, (double)sys / 1000000.0));
return ary;
#endif
}
Class* System::vm_open_class(STATE, Symbol* name, Object* sup,
ConstantScope* scope)
{
Module* under;
if(scope->nil_p()) {
under = G(object);
} else {
under = scope->module();
}
return vm_open_class_under(state, name, sup, under);
}
Class* System::vm_open_class_under(STATE, Symbol* name, Object* super,
Module* under)
{
ConstantMissingReason reason = vNonExistent;
Object* obj = under->get_const(state, name, G(sym_private), &reason);
if(reason == vFound) {
Class* cls = as<Class>(obj);
if(super->nil_p()) return cls;
if(cls->true_superclass(state) != super) {
std::ostringstream message;
message << "Superclass mismatch: given "
<< as<Module>(super)->debug_str(state)
<< " but previously set to "
<< cls->true_superclass(state)->debug_str(state);
Exception* exc =
Exception::make_type_error(state, Class::type, super,
message.str().c_str());
// exc->locations(state, System::vm_backtrace(state,
// Fixnum::from(0), call_frame));
state->raise_exception(exc);
return NULL;
}
return cls;
}
// Create the class.
if(super->nil_p()) super = G(object);
Class* cls = Class::create(state, as<Class>(super));
cls->set_name(state, name, under);
under->set_const(state, name, cls);
return cls;
}
Module* System::vm_open_module(STATE, Symbol* name, ConstantScope* scope) {
Module* under = G(object);
if(!scope->nil_p()) {
under = scope->module();
}
return vm_open_module_under(state, name, under);
}
Module* System::vm_open_module_under(STATE, Symbol* name, Module* under) {
ConstantMissingReason reason = vNonExistent;
Object* obj = under->get_const(state, name, G(sym_private), &reason);
if(reason == vFound) return as<Module>(obj);
Module* module = Module::create(state);
module->set_name(state, name, under);
under->set_const(state, name, module);
return module;
}
static Tuple* find_method(STATE, Module* lookup_begin, Symbol* name, Symbol* min_visibility) {
// Use cUndef for the self type so protected checks never pass
// and work as expected.
LookupData lookup(cUndef, lookup_begin, min_visibility);
Dispatch dis(name);
if(!dis.resolve(state, name, lookup)) {
return nil<Tuple>();
}
return Tuple::from(state, 2, dis.method, dis.module);
}
Tuple* System::vm_find_method(STATE, Object* recv, Symbol* name) {
return find_method(state, recv->lookup_begin(state), name, G(sym_private));
}
Tuple* System::vm_find_public_method(STATE, Object* recv, Symbol* name) {
return find_method(state, recv->lookup_begin(state), name, G(sym_public));
}
Object* System::vm_add_method(STATE, GCToken gct, Symbol* name,
CompiledCode* method,
ConstantScope* scope, Object* vis,
CallFrame* calling_environment)
{
Module* mod = scope->for_method_definition();
method->scope(state, scope);
method->serial(state, Fixnum::from(0));
mod->add_method(state, name, method);
if(Class* cls = try_as<Class>(mod)) {
OnStack<5> o2(state, mod, method, scope, vis, cls);
if(!method->internalize(state, gct, calling_environment)) {
Exception::argument_error(state, "invalid bytecode method");
return 0;
}
object_type type = (object_type)cls->instance_type()->to_native();
TypeInfo* ti = state->memory()->type_info[type];
if(ti) {
method->specialize(state, ti);
}
}
bool add_ivars = false;
if(Class* cls = try_as<Class>(mod)) {
add_ivars = !kind_of<SingletonClass>(cls) &&
cls->type_info()->type == Object::type;
} else {
add_ivars = true;
}
if(add_ivars) {
Array* ary = mod->seen_ivars();
if(ary->nil_p()) {
ary = Array::create(state, 5);
mod->seen_ivars(state, ary);
}
Tuple* lits = method->literals();
for(native_int i = 0; i < lits->num_fields(); i++) {
if(Symbol* sym = try_as<Symbol>(lits->at(state, i))) {
if(CBOOL(sym->is_ivar_p(state))) {
if(!ary->includes_p(state, sym)) ary->append(state, sym);
}
}
}
}
vm_reset_method_cache(state, mod, name, calling_environment);
return method;
}
Object* System::vm_attach_method(STATE, GCToken gct, Symbol* name,
CompiledCode* method,
ConstantScope* scope, Object* recv,
CallFrame* calling_environment)
{
Module* mod = recv->singleton_class(state);
method->scope(state, scope);
method->serial(state, Fixnum::from(0));
mod->add_method(state, name, method);
vm_reset_method_cache(state, mod, name, calling_environment);
return method;
}
Class* System::vm_object_class(STATE, Object* obj) {
return obj->class_object(state);
}
Object* System::vm_object_singleton_class(STATE, Object* obj) {
if(obj->reference_p()) return obj->singleton_class(state);
if(obj->true_p()) return G(true_class);
if(obj->false_p()) return G(false_class);
if(obj->nil_p()) return G(nil_class);
return Primitives::failure();
}
Object* System::vm_singleton_class_object(STATE, Module* mod) {
if(SingletonClass* sc = try_as<SingletonClass>(mod)) {
return sc->singleton();
}
return cNil;
}
Object* System::vm_object_respond_to(STATE, Object* obj, Symbol* name, Object* include_private) {
return obj->respond_to(state, name, include_private);
}
Object* System::vm_object_equal(STATE, Object* a, Object* b) {
return RBOOL(a == b);
}
Object* System::vm_object_kind_of(STATE, Object* obj, Module* mod) {
return RBOOL(obj->kind_of_p(state, mod));
}
Object* System::vm_global_serial(STATE, CallFrame* calling_environment) {
return Fixnum::from(state->shared().global_serial());
}
Object* System::vm_inc_global_serial(STATE, CallFrame* calling_environment) {
if(state->shared().config.serial_debug) {
std::cout << "[Global serial increased from " << state->shared().global_serial() << "]" << std::endl;
calling_environment->print_backtrace(state, 6, true);
}
return Fixnum::from(state->shared().inc_global_serial(state));
}
Object* System::vm_deoptimize_all(STATE, Object* o_disable) {
ObjectWalker walker(state->memory());
GCData gc_data(state->vm());
// Seed it with the root objects.
walker.seed(gc_data);
Object* obj = walker.next();
int total = 0;
bool disable = CBOOL(o_disable);
// TODO: this should be inside tooling
bool tooling_interpreter = state->shared().tool_broker()->tooling_interpreter_p();
while(obj) {
if(CompiledCode* code = try_as<CompiledCode>(obj)) {
if(MachineCode* mcode = code->machine_code()) {
mcode->deoptimize(state, code, 0, disable);
if(tooling_interpreter) {
mcode->run = MachineCode::tooling_interpreter;
} else {
mcode->run = MachineCode::interpreter;
}
}
total++;
}
obj = walker.next();
}
return Integer::from(state, total);
}
Object* System::vm_raise_exception(STATE, Exception* exc) {
state->raise_exception(exc);
return NULL;
}
Fixnum* System::vm_memory_size(STATE, Object* obj) {
if(obj->reference_p()) {
size_t bytes = obj->size_in_bytes(state->vm());
if(Bignum* b = try_as<Bignum>(obj)) {
bytes += b->managed_memory_size(state);
}
Object* iv = obj->ivars();
if(LookupTable* lt = try_as<LookupTable>(iv)) {
bytes += iv->size_in_bytes(state->vm());
bytes += lt->values()->size_in_bytes(state->vm());
bytes += (lt->entries()->to_native() * sizeof(LookupTableBucket));
} else if(iv->reference_p()) {
bytes += iv->size_in_bytes(state->vm());
}
return Fixnum::from(bytes);
}
return Fixnum::from(0);
}
Object* System::vm_throw(STATE, Object* dest, Object* value) {
state->vm()->thread_state()->raise_throw(dest, value);
return NULL;
}
Object* System::vm_catch(STATE, Object* dest, Object* obj,
CallFrame* call_frame)
{
LookupData lookup(obj, obj->lookup_begin(state), G(sym_protected));
Dispatch dis(G(sym_call));
Arguments args(G(sym_call), 1, &dest);
args.set_recv(obj);
OnStack<1> os(state, dest);
Object* ret = dis.send(state, call_frame, lookup, args);
if(!ret && state->vm()->thread_state()->raise_reason() == cCatchThrow) {
if(state->vm()->thread_state()->throw_dest() == dest) {
Object* val = state->vm()->thread_state()->raise_value();
state->vm()->thread_state()->clear_return();
return val;
}
}
return ret;
}
Object* System::vm_set_class(STATE, Object* obj, Class* cls) {
if(!obj->reference_p()) return Primitives::failure();
if(obj->type_id() != cls->type_info()->type) {
return Primitives::failure();
}
if(kind_of<PackedObject>(obj)) {
if(obj->klass()->packed_size() != cls->packed_size()) {
return Primitives::failure();
}
}
obj->klass(state, cls);
return obj;
}
Object* System::vm_method_missing_reason(STATE) {
switch(state->vm()->method_missing_reason()) {
case ePrivate:
return G(sym_private);
case eProtected:
return G(sym_protected);
case eSuper:
return state->symbol("super");
case eVCall:
return state->symbol("vcall");
case eNormal:
return state->symbol("normal");
default:
return state->symbol("none");
}
}
Object* System::vm_constant_missing_reason(STATE) {
switch(state->vm()->constant_missing_reason()) {
case vPrivate:
return G(sym_private);
case vNonExistent:
return state->symbol("normal");
default:
return state->symbol("none");
}
}
Object* System::vm_extended_modules(STATE, Object* obj) {
if(SingletonClass* sc = try_as<SingletonClass>(obj->klass())) {
Array* ary = Array::create(state, 3);
Module* mod = sc->superclass();
while(IncludedModule* im = try_as<IncludedModule>(mod)) {
ary->append(state, im->module());
mod = mod->superclass();
}
return ary;
}
return cNil;
}
Object* System::vm_const_defined(STATE, Symbol* sym,
CallFrame* calling_environment)
{
ConstantMissingReason reason = vNonExistent;
Object* res = Helpers::const_get(state, calling_environment, sym, &reason);
if(reason != vFound) {
return Primitives::failure();
}
return res;
}
Object* System::vm_const_defined_under(STATE, Module* under, Symbol* sym,
Object* send_const_missing,
CallFrame* calling_environment)
{
ConstantMissingReason reason = vNonExistent;
Object* res = Helpers::const_get_under(state, under, sym, &reason);
if(reason != vFound) {
if(send_const_missing->true_p()) {
res = Helpers::const_missing_under(state, under, sym,
calling_environment);
} else {
res = Primitives::failure();
}
}
return res;
}
Object* System::vm_check_callable(STATE, Object* obj, Symbol* sym,
Object* self, CallFrame* calling_environment)
{
LookupData lookup(self, obj->lookup_begin(state), G(sym_public));
Dispatch dis(sym);
Object* responds = RBOOL(dis.resolve(state, sym, lookup));
if(!CBOOL(responds)) {
LookupData lookup(obj, obj->lookup_begin(state), G(sym_private));
Symbol* name = G(sym_respond_to_missing);
Dispatch dis(name);
Object* buf[2];
buf[0] = name;
buf[1] = G(sym_public);
Arguments args(name, obj, 2, buf);
responds = RBOOL(CBOOL(dis.send(state, calling_environment, lookup, args)));
}
return responds;
}
Object* System::vm_check_super_callable(STATE, CallFrame* call_frame) {
Module* start = call_frame->module()->superclass();
Symbol* sym = call_frame->original_name();
LookupData lookup(call_frame->self(), start, G(sym_private));
Dispatch dis(sym);
return RBOOL(dis.resolve(state, sym, lookup));
}
#define GETPW_R_SIZE 2048
String* System::vm_get_user_home(STATE, String* name) {
#ifdef RBX_WINDOWS
// TODO: Windows
return force_as<String>(Primitives::failure());
#else
struct passwd pw;
struct passwd *pwd;
long len = sysconf(_SC_GETPW_R_SIZE_MAX);
if(len < 0) len = GETPW_R_SIZE;
retry:
ByteArray* buf = ByteArray::create_dirty(state, len);
int err = getpwnam_r(name->c_str_null_safe(state), &pw,
reinterpret_cast<char*>(buf->raw_bytes()), len, &pwd);
if(err) {
if(errno == ERANGE) {
len *= 2;
// Check for overflow
if(len > 0) goto retry;
Exception::runtime_error(state, "getpwnam_r(3) buffer exceeds maximum size");
}
Exception::errno_error(state, "retrieving user home directory", errno, "getpwnam_r(3)");
}
if(pwd) {
return String::create(state, pwd->pw_dir);
}
return nil<String>();
#endif
}
Object* System::vm_set_finalizer(STATE, Object* obj, Object* fin) {
if(!obj->reference_p()) return cFalse;
state->memory()->set_ruby_finalizer(obj, fin);
return cTrue;
}
Object* System::vm_object_lock(STATE, GCToken gct, Object* obj,
CallFrame* call_frame)
{
if(!obj->reference_p()) return Primitives::failure();
switch(obj->lock(state, gct, call_frame)) {
case eLocked:
return cTrue;
case eLockTimeout:
case eUnlocked:
case eLockError:
return Primitives::failure();
case eLockInterrupted:
{
Exception* exc = state->vm()->interrupted_exception();
assert(!exc->nil_p());
state->vm()->clear_interrupted_exception();
exc->locations(state, Location::from_call_stack(state, call_frame));
state->raise_exception(exc);
return 0;
}
}
return cNil;
}
Object* System::vm_object_uninterrupted_lock(STATE, GCToken gct, Object* obj,
CallFrame* call_frame)
{
if(!obj->reference_p()) return Primitives::failure();
state->set_call_frame(call_frame);
retry:
switch(obj->lock(state, gct, call_frame, false)) {
case eLocked:
return cTrue;
case eLockInterrupted:
goto retry;
case eLockTimeout:
case eUnlocked:
case eLockError:
return Primitives::failure();
}
return cNil;
}
Object* System::vm_object_lock_timed(STATE, GCToken gct, Object* obj, Integer* time,
CallFrame* call_frame)
{
if(!obj->reference_p()) return Primitives::failure();
state->set_call_frame(call_frame);
switch(obj->lock(state, gct, call_frame, time->to_native())) {
case eLocked:
return cTrue;
case eLockTimeout:
return cFalse;
case eUnlocked:
case eLockError:
return Primitives::failure();
case eLockInterrupted:
{
Exception* exc = state->vm()->interrupted_exception();
assert(!exc->nil_p());
state->vm()->clear_interrupted_exception();
exc->locations(state, Location::from_call_stack(state, call_frame));
state->raise_exception(exc);
return 0;
}
return 0;
}
return cNil;
}
Object* System::vm_object_trylock(STATE, GCToken gct, Object* obj,
CallFrame* call_frame)
{
if(!obj->reference_p()) return Primitives::failure();
return RBOOL(obj->try_lock(state, gct, call_frame) == eLocked);
}
Object* System::vm_object_locked_p(STATE, GCToken gct, Object* obj,
CallFrame* call_frame) {
if(!obj->reference_p()) return cFalse;
return RBOOL(obj->locked_p(state, gct, call_frame));
}
Object* System::vm_object_unlock(STATE, GCToken gct, Object* obj,
CallFrame* call_frame)
{
if(!obj->reference_p()) return Primitives::failure();
if(obj->unlock(state, gct, call_frame) == eUnlocked) return cNil;
if(cDebugThreading) {
std::cerr << "[LOCK " << state->vm()->thread_id() << " unlock failed]" << std::endl;
}
return Primitives::failure();
}
Object* System::vm_memory_barrier(STATE) {
atomic::memory_barrier();
return cNil;
}
Object* System::vm_windows_p(STATE) {
#ifdef RBX_WINDOWS
return cTrue;
#else
return cFalse;
#endif
}
Object* System::vm_darwin_p(STATE) {
#ifdef RBX_DARWIN
return cTrue;
#else
return cFalse;
#endif
}
Object* System::vm_bsd_p(STATE) {
#ifdef RBX_BSD
return cTrue;
#else
return cFalse;
#endif
}
Object* System::vm_linux_p(STATE) {
#ifdef RBX_LINUX
return cTrue;
#else
return cFalse;
#endif
}
static const char sha1_hex[] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f'
};
String* System::sha1_hash(STATE, String* str) {
XSHA1_CTX ctx;
XSHA1_Init(&ctx);
XSHA1_Update(&ctx, str->byte_address(), str->byte_size());
uint8_t digest[20];
XSHA1_Finish(&ctx, digest);
char buf[40];
for(int i = 0; i < 20; i++) {
unsigned char byte = digest[i];
buf[i + i] = sha1_hex[byte >> 4];
buf[i + i + 1] = sha1_hex[byte & 0x0f];
}
return String::create(state, buf, 40);
}
Tuple* System::vm_thread_state(STATE) {
VMThreadState* ts = state->vm()->thread_state();
Tuple* tuple = Tuple::create_dirty(state, 5);
Symbol* reason = 0;
switch(ts->raise_reason()) {
case cNone:
reason = state->symbol("none");
break;
case cException:
reason = state->symbol("exception");
break;
case cReturn:
reason = state->symbol("return");
break;
case cBreak:
reason = state->symbol("break");
break;
case cExit:
reason = state->symbol("exit");
break;
case cCatchThrow:
reason = state->symbol("catch_throw");
break;
case cThreadKill:
reason = state->symbol("thread_kill");
break;
default:
reason = state->symbol("unknown");
}
tuple->put(state, 0, reason);
tuple->put(state, 1, ts->raise_value());
tuple->put(state, 2, ts->destination_scope());
tuple->put(state, 3, ts->current_exception());
tuple->put(state, 4, ts->throw_dest());
return tuple;
}
Object* System::vm_run_script(STATE, GCToken gct, CompiledCode* code,
CallFrame* calling_environment)
{
Arguments args(state->symbol("__script__"), G(main), cNil, 0, 0);
OnStack<1> os(state, code);
code->internalize(state, gct, calling_environment, 0, 0);
#ifdef RBX_PROFILER
if(unlikely(state->vm()->tooling())) {
tooling::ScriptEntry me(state, code);
return code->machine_code()->execute_as_script(state, code, calling_environment);
} else {
return code->machine_code()->execute_as_script(state, code, calling_environment);
}
#else
return code->machine_code()->execute_as_script(state, code, calling_environment);
#endif
}
#define HASH_TRIE_BASE_SHIFT 6
#if RBX_SIZEOF_LONG == 8
#define HASH_TRIE_BIT_WIDTH 6
#define HASH_TRIE_BIT_MASK 0x3f
#else
#define HASH_TRIE_BIT_WIDTH 5
#define HASH_TRIE_BIT_MASK 0x1f
#endif
static inline size_t hash_trie_bit(Fixnum* hash, Fixnum* level) {
native_int h = hash->to_native();
native_int l = level->to_native();
size_t width = HASH_TRIE_BIT_WIDTH;
size_t mask = HASH_TRIE_BIT_MASK;
size_t base = HASH_TRIE_BASE_SHIFT;
size_t result = 1;
return result << ((h >> (l * width + base)) & mask);
}
static inline int hash_trie_index(size_t m) {
#if RBX_SIZEOF_LONG == 8
native_int sk5 = 0x5555555555555555;
native_int sk3 = 0x3333333333333333;
native_int skf0 = 0x0F0F0F0F0F0F0F0F;
m -= (m >> 1) & sk5;
m = (m & sk3) + ((m >> 2) & sk3);
m = (m & skf0) + ((m >> 4) & skf0);
m += m >> 8;
m += m >> 16;
m = (m + (m >> 32)) & 0xFF;
#else
native_int sk5 = 0x55555555;
native_int sk3 = 0x33333333;
native_int skf0 = 0xF0F0F0F;
m -= (m >> 1) & sk5;
m = (m & sk3) + ((m >> 2) & sk3);
m = (m & skf0) + ((m >> 4) & skf0);
m += m >> 8;
m = (m + (m >> 16)) & 0x3F;
#endif
return m;
}
Fixnum* System::vm_hash_trie_item_index(STATE, Fixnum* hash,
Fixnum* level, Integer* map)
{
size_t m = map->to_ulong();
size_t b = hash_trie_bit(hash, level);
if(m & b) {
return Fixnum::from(hash_trie_index((b - 1) & m));
} else {
return nil<Fixnum>();
}
}
Integer* System::vm_hash_trie_set_bitmap(STATE, Fixnum* hash,
Fixnum* level, Integer* map)
{
size_t m = map->to_ulong();
size_t b = hash_trie_bit(hash, level);
return Integer::from(state, m | b);
}
Integer* System::vm_hash_trie_unset_bitmap(STATE, Fixnum* hash,
Fixnum* level, Integer* map)
{
size_t m = map->to_ulong();
size_t b = hash_trie_bit(hash, level);
return Integer::from(state, m & ~b);
}
String* System::vm_get_module_name(STATE, Module* mod) {
return mod->get_name(state);
}
Object* System::vm_set_module_name(STATE, Module* mod, Object* name, Object* under) {
if(name->nil_p()) return cNil;
if(under->nil_p()) under = G(object);
mod->set_name(state, as<Symbol>(name), as<Module>(under));
return cNil;
}
String* System::vm_set_process_title(STATE, String* title) {
setproctitle("%s", title->c_str_null_safe(state));
return title;
}
Object* System::vm_dtrace_fire(STATE, String* payload) {
#if HAVE_DTRACE
if(RUBINIUS_RUBY_PROBE_ENABLED()) {
char* bytes = reinterpret_cast<char*>(payload->byte_address());
RUBINIUS_RUBY_PROBE(
const_cast<RBX_DTRACE_CHAR_P>(bytes),
payload->byte_size());
return cTrue;
}
return cFalse;
#else
return cNil;
#endif
}
}
Jump to Line
Something went wrong with that request. Please try again.