/*
* Copyright (C) 1993-2003 Yukihiro Matsumoto
* Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
* Copyright (C) 2000 Information-technology Promotion Agency, Japan
* Copyright (C) 2010 Tim Felgentreff
*
* This file is part of jruby-cext.
*
* This code is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License version 3 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 3 for more details.
*
* You should have received a copy of the GNU General Public License
* version 3 along with this work. If not, see .
*/
#include
#include
#ifdef __sun
#include
#endif
#include "jruby.h"
#include "ruby.h"
#include "JLocalEnv.h"
#include "Handle.h"
using namespace jruby;
extern "C" bool
jruby_obj_frozen(VALUE obj)
{
return callMethod(obj, "frozen?", 0) == Qtrue;
}
static VALUE
convert_type(VALUE val, const char* type_name, const char* method, int raise)
{
ID m = rb_intern(method);
if (!rb_respond_to(val, m)) {
if (raise) {
rb_raise(rb_eTypeError, "can't convert %s into %s",
NIL_P(val) ? "nil" :
val == Qtrue ? "true" :
val == Qfalse ? "false" :
rb_obj_classname(val),
type_name);
} else {
return Qnil;
}
}
return rb_funcall(val, m, 0);
}
extern "C" VALUE
rb_obj_freeze(VALUE obj)
{
return callMethodA(obj, "freeze", 0, NULL);
}
extern "C" char*
rb_obj_classname(VALUE obj)
{
return rb_class2name(rb_class_of(obj));
}
extern "C" void
rb_extend_object(VALUE object, VALUE module)
{
callMethod(object, "extend", 1, module);
}
extern "C" int
rb_respond_to(VALUE obj, ID id)
{
JLocalEnv env;
jboolean ret = env->CallBooleanMethod(valueToObject(env, obj), IRubyObject_respondsTo_method, idToString(env, id));
checkExceptions(env);
return ret != JNI_FALSE;
}
extern "C" int
rb_obj_respond_to(VALUE obj, ID id, int include_private)
{
return RTEST(callMethod(obj, "respond_to?", 2, ID2SYM(id), include_private ? Qtrue : Qfalse));
}
extern "C" VALUE
rb_convert_type(VALUE val, int type, const char* type_name, const char* method)
{
VALUE v;
if (TYPE(val) == type) {
return val;
}
v = convert_type(val, type_name, method, Qtrue);
if (TYPE(v) != type) {
rb_raise(rb_eTypeError, "%s#%s should return %s",
rb_obj_classname(val), method, type_name);
}
return v;
}
extern "C" VALUE
rb_check_convert_type(VALUE val, int type, const char* type_name, const char* method)
{
if (TYPE(val) == type) {
return val;
}
return convert_type(val, type_name, method, 0);
}
extern "C" VALUE
rb_check_to_integer(VALUE object_handle, const char *method_name)
{
if(FIXNUM_P(object_handle)) {
return object_handle;
}
VALUE result = rb_check_convert_type(object_handle, 0, "Integer", method_name);
if(rb_obj_is_kind_of(result, rb_cInteger)) {
return result;
}
return Qnil;
}
extern "C" VALUE
rb_iv_get(VALUE obj, const char* name)
{
JLocalEnv env;
char var_name[strlen(name) + 2];
(name[0] != '@') ? strcpy(var_name, "@")[0] : var_name[0] = '\0';
strcat(var_name, name);
jobject retval = env->CallObjectMethod(valueToObject(env, obj), RubyBasicObject_getInstanceVariable_method,
env->NewStringUTF(var_name));
checkExceptions(env);
return objectToValue(env, retval);
}
extern "C" VALUE
rb_iv_set(VALUE obj, const char* name, VALUE value)
{
JLocalEnv env;
char var_name[strlen(name) + 2];
(name[0] != '@') ? strcpy(var_name, "@")[0] : var_name[0] = '\0';
strcat(var_name, name);
jobject retval = env->CallObjectMethod(valueToObject(env, obj), RubyBasicObject_setInstanceVariable_method,
env->NewStringUTF(var_name), valueToObject(env, value));
checkExceptions(env);
return objectToValue(env, retval);
}
extern "C" VALUE
rb_ivar_get(VALUE obj, ID ivar_name)
{
return rb_iv_get(obj, rb_id2name(ivar_name));
}
extern "C" VALUE
rb_ivar_set(VALUE obj, ID ivar_name, VALUE value)
{
return rb_iv_set(obj, rb_id2name(ivar_name), value);
}
extern "C" VALUE
rb_ivar_defined(VALUE obj, ID ivar)
{
JLocalEnv env;
const char* name = rb_id2name(ivar);
char var_name[strlen(name) + 2];
(name[0] != '@') ? strcpy(var_name, "@")[0] : var_name[0] = '\0';
strcat(var_name, name);
jboolean retval = env->CallBooleanMethod(valueToObject(env, obj), RubyBasicObject_hasInstanceVariable_method,
env->NewStringUTF(var_name));
checkExceptions(env);
return (retval == JNI_TRUE) ? Qtrue : Qfalse;
}
extern "C" void
rb_obj_call_init(VALUE recv, int arg_count, VALUE* args)
{
callMethodANonConst(recv, "initialize", arg_count, args);
}
extern "C" VALUE
rb_obj_is_kind_of(VALUE obj, VALUE module)
{
return callMethod(obj, "kind_of?", 1, module);
}
extern "C" VALUE
rb_obj_is_instance_of(VALUE obj, VALUE klass)
{
return callMethodA(obj, "instance_of?", 1, &klass);
}
extern "C" VALUE
rb_obj_taint(VALUE obj)
{
return callMethodA(obj, "taint", 0, NULL);
}
extern "C" VALUE
rb_obj_tainted(VALUE obj)
{
return callMethodA(obj, "tainted?", 0, NULL);
}
extern "C" VALUE
rb_attr_get(VALUE obj, ID id)
{
return callMethod(obj, "instance_variable_get", 1, ID2SYM(id));
}
extern "C" VALUE
rb_obj_as_string(VALUE obj)
{
VALUE str;
if (TYPE(obj) == T_STRING) {
return obj;
}
str = callMethodA(obj, "to_s", 0, NULL);
if (TYPE(str) != T_STRING) {
return rb_any_to_s(obj);
}
if (OBJ_TAINTED(obj)) {
OBJ_TAINT(str);
}
return str;
}
extern "C" VALUE
rb_to_integer(VALUE val, const char *method)
{
VALUE v;
if (FIXNUM_P(val)) return val;
if (TYPE(val) == T_BIGNUM) return val;
v = convert_type(val, "Integer", method, 1);
if (!rb_obj_is_kind_of(v, rb_cInteger)) {
const char *cname = rb_obj_classname(val);
rb_raise(rb_eTypeError, "can't convert %s to Integer (%s#%s gives %s)",
cname, cname, method, rb_obj_classname(v));
}
return v;
}
extern "C" VALUE
rb_inspect(VALUE obj)
{
return rb_obj_as_string(callMethodA(obj, "inspect", 0, 0));
}
extern "C" VALUE
rb_any_to_s(VALUE obj)
{
char* buf;
int len = 128, buflen;
do {
buf = (char *) alloca(buflen = len);
len = snprintf(buf, buflen, "#<%s:%p>", rb_obj_classname(obj), (void *) obj);
} while (len >= buflen);
return rb_str_new_cstr(buf);
}
extern "C" void
rb_check_frozen(VALUE obj)
{
if (OBJ_FROZEN(obj)) {
rb_raise(is1_9() ? rb_eRuntimeError : rb_eTypeError, "can't modify frozen %s", rb_obj_classname(obj));
}
}
extern "C" VALUE
rb_singleton_class(VALUE obj)
{
JLocalEnv env;
jmethodID IRubyObject_getSingletonClass_method = getCachedMethodID(env, IRubyObject_class, "getSingletonClass",
"()Lorg/jruby/RubyClass;");
jobject singleton = env->CallObjectMethod(valueToObject(env, obj), IRubyObject_getSingletonClass_method);
checkExceptions(env);
return objectToValue(env, singleton);
}
extern "C" VALUE
rb_class_inherited_p(VALUE mod, VALUE arg)
{
if (TYPE(arg) != T_MODULE && TYPE(arg) != T_CLASS) {
rb_raise(rb_eTypeError, "compared with non class/module");
}
return callMethodA(mod, "<=", 1, &arg);
}
extern "C" VALUE
rb_class_superclass(VALUE klass)
{
return callMethodA(klass, "superclass", 0, NULL);
}
extern "C" VALUE
rb_obj_dup(VALUE obj)
{
return callMethod(obj, "dup", 0);
}
extern "C" VALUE
rb_obj_clone(VALUE obj)
{
return callMethod(obj, "clone", 0);
}
extern "C" VALUE
rb_obj_id(VALUE obj)
{
return callMethod(obj, "object_id", 0);
}
extern "C" VALUE
rb_equal(VALUE obj, VALUE other)
{
return RTEST(callMethod(obj, "==", 1, other)) ? Qtrue : Qfalse;
}
extern "C" int
rb_eql(VALUE obj1, VALUE obj2)
{
return RTEST(callMethodA(obj1, "eql?", 1, &obj2));
}
extern "C" void
jruby_infect(VALUE object1, VALUE object2)
{
if (OBJ_TAINTED(object1)) {
JLocalEnv env;
jmethodID mid = getCachedMethodID(env, IRubyObject_class, "infectBy",
"(Lorg/jruby/runtime/builtin/IRubyObject;)Lorg/jruby/runtime/builtin/IRubyObject;");
env->CallObjectMethod(valueToObject(env, object2), mid, object1);
checkExceptions(env);
}
}
extern "C" VALUE
rb_hash(VALUE obj)
{
VALUE hash = callMethod(obj, "hash", 0);
return convert_type(hash, "Fixnum", "to_int", true);
}