#include #include "callbacks.h" #include "converters.h" using namespace v8; namespace { VALUE unwrap(const AccessorInfo& info) { return (VALUE)External::Unwrap(info.Data()); } //returns all the ruby methods that can be called from //this object. //1.8: public_methods() return strings //1.9: publi_methods() return symbols //convert them all into string VALUE CALLABLE_METHODS(VALUE object) { VALUE methods = rb_funcall(object, rb_intern("public_methods"), 1, Qfalse); int length = RARRAY_LEN(methods); VALUE str_methods = rb_ary_new2(length); for (int i = 0; i < length; i++) { VALUE method = rb_ary_entry(methods, i); rb_ary_store(str_methods,i, rb_obj_as_string(method)); } return str_methods; } Local TO_ARRAY(Arguments& args) { Local array = Array::New(args.Length()); for (int i = 0; i < args.Length(); i++) { array->Set(Integer::New(i), args[i]); } return array; } Local Racer_Call_Ruby_Method(VALUE object, VALUE method, Local args) { VALUE * arguments = new VALUE[args->Length()]; for (unsigned int i = 0; i < args->Length(); i++) { Handle val = args->Get(Integer::New(i)); arguments[i] = V82RB(val); } VALUE result = rb_funcall2(object, rb_to_id(method), args->Length(), arguments); Local converted = RB2V8(result); return converted; } Local Racer_Access_Ruby_Property(VALUE object, VALUE name) { VALUE method = rb_funcall(object, rb_intern("method"), 1, name); if (FIX2INT(rb_funcall(method, rb_intern("arity"), 0)) == 0) { return Racer_Call_Ruby_Method(object, name, Array::New(0)); } else { return RB2V8(method); } } } Handle RacerRubyInvocationCallback(const Arguments& args) { VALUE code = (VALUE)External::Unwrap(args.Data()); if (NIL_P(code)) { return Null(); } else { VALUE* arguments = new VALUE[args.Length()]; for(int c=0;c val = args[c]; arguments[c] = V82RB(val); } VALUE result = rb_funcall2(code, rb_intern("call"), args.Length(), arguments); delete [] arguments; Handle convertedResult = RB2V8(result); return convertedResult ; } } /** * NamedProperty[Getter|Setter] are used as interceptors on object. * See ObjectTemplate::SetNamedPropertyHandler. */ Handle RacerRubyNamedPropertyGetter(Local property, const AccessorInfo& info) { // printf("Getter '%s'
", *String::AsciiValue(property)); if (property->Length() == 0) { return Handle(); } VALUE object = unwrap(info); VALUE camel_name = V82RB((Local&)property); VALUE perl_name = rb_funcall(V8_To, rb_intern("perl_case"), 1, camel_name); VALUE methods = CALLABLE_METHODS(object); if (RTEST(rb_ary_includes(methods, perl_name))) { return Racer_Access_Ruby_Property(object, perl_name); } if (RTEST(rb_ary_includes(methods, camel_name))) { return Racer_Access_Ruby_Property(object, camel_name); } return Handle(); } /** * Returns the value if the setter intercepts the request. * Otherwise, returns an empty handle. */ Handle RacerRubyNamedPropertySetter(Local property, Local value, const AccessorInfo& info) { if (property->Length() == 0) { return Handle(); } // printf("Setter: '%s'
", *String::AsciiValue(property)); std::string setter = V82String((Handle&)property); setter += "="; Local setter_name = String::New(setter.c_str()); VALUE object = unwrap(info); VALUE camel_name = V82RB((Local&)setter_name); VALUE perl_name = rb_funcall(V8_To, rb_intern("perl_case"), 1, camel_name); VALUE methods = CALLABLE_METHODS(object); Local args = Array::New(1); args->Set(Integer::New(0), value); if (RTEST(rb_ary_includes(methods, perl_name))) { Racer_Call_Ruby_Method(object, perl_name, args); return value; } if (RTEST(rb_ary_includes(methods, camel_name))) { Racer_Call_Ruby_Method(object, camel_name, args); return value; } return Handle(); } /** * Returns a non-empty handle if the interceptor intercepts the request. * The result is true if the property exists and false otherwise. */ Handle RacerRubyNamedPropertyQuery(Local property, const AccessorInfo& info) { // printf("Query: '%s'
", *String::AsciiValue(property)); if (property->Length() == 0) { return False(); } VALUE object = unwrap(info); VALUE methods = CALLABLE_METHODS(object); VALUE attr_name = V82RB((Local&)property); VALUE perl_name = rb_funcall(V8_To, rb_intern("perl_case"), 1, attr_name); if (RTEST(rb_ary_includes(methods, attr_name)) || RTEST(rb_ary_includes(methods, perl_name))) { return True(); } else { return Handle(); } } /** * Returns a non-empty handle if the deleter intercepts the request. * The return value is true if the property could be deleted and false * otherwise. */ Handle RacerRubyNamedPropertyDeleter(Local property, const AccessorInfo& info) { return False(); } /** * Returns an array containing the names of the properties the named * property getter intercepts. */ Handle RacerRubyNamedPropertyEnumerator(const AccessorInfo& info) { VALUE object = unwrap(info); VALUE methods = CALLABLE_METHODS(object); int length = RARRAY_LEN(methods); Local properties = Array::New(length); for (int i = 0; i < length; i++) { VALUE camel_name = rb_funcall(V8_To, rb_intern("camel_case"), 1, rb_ary_entry(methods, i)); properties->Set(Integer::New(i), RB2V8(camel_name)); } return properties; }