QList<Method *> RClass::methods(Method::Qualifiers qualifiers) const { SEXP _env = methodEnv(); SEXP names = R_lsInternal(_env, (Rboolean)false); QList<Method *> meths; for (int i = 0; i < length(names); i++) { const char *name = CHAR(STRING_ELT(names, i)); SEXP fun = findVarInFrame(_env, install(name)); if ((RMethod(this, name, fun).qualifiers() & qualifiers) == qualifiers) meths << new RMethod(this, name, fun); } meths.append(parent()->methods(qualifiers | Method::NotPrivate)); return meths; }
SEXP InstanceObjectTable::objects() const { Method::Qualifiers qual = Method::NotStatic; QList<Method *> methods; SEXP nameVector; QSet<const char *> nameSet; checkInstance(); if (!_internal) qual |= Method::Public; methods = _instance->klass()->methods(qual); foreach(Method *m, methods) // put names into set to get unique ones nameSet.insert(m->name()); int numFields = 0; SEXP fields; if (_internal) { PROTECT(fields = R_lsInternal(fieldEnv(), TRUE)); numFields = length(fields); } PROTECT(nameVector = allocVector(STRSXP, nameSet.size() + numFields)); int i = 0; // iterate over set, populate R vector foreach(const char *name, nameSet) SET_STRING_ELT(nameVector, i++, mkChar(name)); for(int j = 0; i < length(nameVector); i++, j++) SET_STRING_ELT(nameVector, i, STRING_ELT(fields, j)); while(!methods.isEmpty()) delete methods.takeFirst(); UNPROTECT(1); if (_internal) UNPROTECT(1); return nameVector; }
// TODO: split out some of the large blocks into helper functions, to make this easier to read void RKStructureGetter::getStructureWorker (SEXP val, const QString &name, bool misplaced, RData *storage) { RK_TRACE (RBACKEND); bool is_function = false; bool is_container = false; bool is_environment = false; unsigned int type = 0; unsigned int count; RK_DO (qDebug ("fetching '%s': %p, s-type %d", name.toLatin1().data(), val, TYPEOF (val)), RBACKEND, DL_DEBUG); PROTECT (val); // manually resolve any promises SEXP value = resolvePromise (val); UNPROTECT (1); /* val */ PROTECT (value); // first field: get name RData *namedata = new RData; namedata->datatype = RData::StringVector; namedata->length = 1; QString *name_dummy = new QString[1]; name_dummy[0] = name; namedata->data = name_dummy; // get classes SEXP classes_s; if (TYPEOF (value) == LANGSXP) { // if it's a call, we should NEVER send it through eval extern SEXP R_data_class (SEXP, Rboolean); classes_s = R_data_class (value, (Rboolean) 0); value = coerceVector (value, EXPRSXP); // make sure the object is safe for everything to come UNPROTECT (1); /* old value */ PROTECT (classes_s); PROTECT (value); } else { classes_s = callSimpleFun (class_fun, value, R_BaseEnv); PROTECT (classes_s); } QString *classes = SEXPToStringList (classes_s, &count); unsigned int num_classes = count; UNPROTECT (1); /* classes_s */ // store classes RData *classdata = new RData; classdata->datatype = RData::StringVector; classdata->data = classes; classdata->length = num_classes; // basic classification for (unsigned int i = 0; i < num_classes; ++i) { #warning: Using is.data.frame() may be more reliable (would need to be called only on List-objects, thus no major performance hit) if (classes[i] == "data.frame") type |= RObject::DataFrame; } if (callSimpleBool (is_matrix_fun, value, R_BaseEnv)) type |= RObject::Matrix; if (callSimpleBool (is_array_fun, value, R_BaseEnv)) type |= RObject::Array; if (callSimpleBool (is_list_fun, value, R_BaseEnv)) type |= RObject::List; if (type != 0) { is_container = true; type |= RObject::Container; } else { if (callSimpleBool (is_function_fun, value, R_BaseEnv)) { is_function = true; type |= RObject::Function; } else if (callSimpleBool (is_environment_fun, value, R_BaseEnv)) { is_container = true; is_environment = true; type |= RObject::Environment; } else { type |= RObject::Variable; if (callSimpleBool (is_factor_fun, value, R_BaseEnv)) type |= RObject::Factor; else if (callSimpleBool (is_numeric_fun, value, R_BaseEnv)) type |= RObject::Numeric; else if (callSimpleBool (is_character_fun, value, R_BaseEnv)) type |= RObject::Character; else if (callSimpleBool (is_logical_fun, value, R_BaseEnv)) type |= RObject::Logical; } } if (misplaced) type |= RObject::Misplaced; // get meta data, if any RData *metadata = new RData; metadata->datatype = RData::StringVector; if (!Rf_isNull (Rf_getAttrib (value, meta_attrib))) { type |= RObject::HasMetaObject; SEXP meta_s = callSimpleFun (get_meta_fun, value, R_GlobalEnv); PROTECT (meta_s); metadata->data = SEXPToStringList (meta_s, &count); metadata->length = count; UNPROTECT (1); /* meta_s */ } else { metadata->length = 1; QString *meta_dummy = new QString[1]; meta_dummy[0] = ""; metadata->data = meta_dummy; } // store type RData *typedata = new RData; typedata->datatype = RData::IntVector; typedata->length = 1; int *type_dummy = new int[1]; type_dummy[0] = type; typedata->data = type_dummy; // get dims int *dims; unsigned int num_dims; SEXP dims_s = callSimpleFun (dims_fun, value, R_BaseEnv); if (!Rf_isNull (dims_s)) { dims = SEXPToIntArray (dims_s, &num_dims); } else { num_dims = 1; unsigned int len = Rf_length (value); if ((len < 2) && (!is_function)) { // suspicious. Maybe some kind of list SEXP len_s = callSimpleFun (length_fun, value, R_BaseEnv); PROTECT (len_s); if (Rf_isNull (len_s)) { dims = new int[1]; dims[0] = len; } else { dims = SEXPToIntArray (len_s, &num_dims); } UNPROTECT (1); /* len_s */ } else { dims = new int[1]; dims[0] = len; } } // store dims RData *dimdata = new RData; dimdata->datatype = RData::IntVector; dimdata->length = num_dims; dimdata->data = dims; // store everything we have so far if (is_container) { storage->length = 6; } else if (is_function) { storage->length = 7; } else { storage->length = 5; } storage->datatype = RData::StructureVector; RData **res = new RData*[storage->length]; storage->data = res; res[0] = namedata; res[1] = typedata; res[2] = classdata; res[3] = metadata; res[4] = dimdata; // now add the extra info for containers and functions if (is_container) { bool do_env = (is_environment && (++envir_depth < 2)); bool do_cont = is_container && (!is_environment); RData *childdata = new RData; childdata->datatype = RData::StructureVector; childdata->length = 0; childdata->data = 0; res[5] = childdata; // fetch list of child names unsigned int childcount; SEXP childnames_s; if (do_env) { childnames_s = R_lsInternal (value, (Rboolean) 1); } else if (do_cont) { childnames_s = callSimpleFun (names_fun, value, R_BaseEnv); } else { childnames_s = R_NilValue; // dummy } PROTECT (childnames_s); QString *childnames = SEXPToStringList (childnames_s, &childcount); childdata->length = childcount; RData **children = new RData*[childcount]; childdata->data = children; childdata->length = childcount; for (unsigned int i = 0; i < childcount; ++i) { // in case there is an error while fetching one of the children, let's pre-initialize everything. children[i] = new RData; children[i]->data = 0; children[i]->length = 0; children[i]->datatype = RData::NoData; } if (do_env) { RK_DO (qDebug ("recurse into environment %s", name.toLatin1().data ()), RBACKEND, DL_DEBUG); for (unsigned int i = 0; i < childcount; ++i) { SEXP current_childname = install(CHAR(STRING_ELT(childnames_s, i))); PROTECT (current_childname); SEXP child = Rf_findVar (current_childname, value); PROTECT (child); bool child_misplaced = false; if (with_namespace) { /* before R 2.4.0, operator "::" would only work on true namespaces, not on package names (operator "::" work, if there is a namespace, and that namespace has the symbol in it) TODO remove once we depend on R >= 2.4.0 */ # ifndef R_2_5 if (Rf_isNull (namespace_envir)) { child_misplaced = true; } else { SEXP dummy = Rf_findVarInFrame (namespace_envir, current_childname); if (Rf_isNull (dummy) || (dummy == R_UnboundValue)) child_misplaced = true; } /* for R 2.4.0 or greater: operator "::" works if package has no namespace at all, or has a namespace with the symbol in it */ # else if (!Rf_isNull (namespace_envir)) { SEXP dummy = Rf_findVarInFrame (namespace_envir, current_childname); if (Rf_isNull (dummy) || (dummy == R_UnboundValue)) child_misplaced = true; } # endif } getStructureSafe (child, childnames[i], child_misplaced, children[i]); UNPROTECT (2); /* childname, child */ } } else if (do_cont) { RK_DO (qDebug ("recurse into list %s", name.toLatin1().data ()), RBACKEND, DL_DEBUG); // fewer elements than names() can happen, although I doubt it is supposed to happen. // see http://sourceforge.net/tracker/?func=detail&aid=3002439&group_id=50231&atid=459007 bool may_be_special = Rf_length (value) < childcount; if (Rf_isList (value) && (!may_be_special)) { // old style list for (unsigned int i = 0; i < childcount; ++i) { SEXP child = CAR (value); getStructureSafe (child, childnames[i], false, children[i]); CDR (value); } } else if (Rf_isNewList (value) && (!may_be_special)) { // new style list for (unsigned int i = 0; i < childcount; ++i) { SEXP child = VECTOR_ELT(value, i); getStructureSafe (child, childnames[i], false, children[i]); } } else { // probably an S4 object disguised as a list SEXP index = Rf_allocVector(INTSXP, 1); PROTECT (index); for (unsigned int i = 0; i < childcount; ++i) { INTEGER (index)[0] = (i + 1); SEXP child = callSimpleFun2 (double_brackets_fun, value, index, R_BaseEnv); getStructureSafe (child, childnames[i], false, children[i]); } UNPROTECT (1); /* index */ } } UNPROTECT (1); /* childnames_s */ delete [] childnames; } else if (is_function) { RData *funargsdata = new RData; funargsdata->datatype = RData::StringVector; funargsdata->length = 0; funargsdata->data = 0; res[5] = funargsdata; RData *funargvaluesdata = new RData; funargvaluesdata->datatype = RData::StringVector; funargvaluesdata->length = 0; funargvaluesdata->data = 0; res[6] = funargvaluesdata; // TODO: this is still the major bottleneck, but no idea, how to improve on this SEXP formals_s = callSimpleFun (get_formals_fun, value, R_GlobalEnv); PROTECT (formals_s); // the default values funargvaluesdata->data = SEXPToStringList (formals_s, &(funargvaluesdata->length)); // the argument names SEXP names_s = getAttrib (formals_s, R_NamesSymbol); PROTECT (names_s); funargsdata->data = SEXPToStringList (names_s, &(funargsdata->length)); UNPROTECT (2); /* names_s, formals_s */ } UNPROTECT (1); /* value */ }
static void nvimcom_list_env() { const char *varName; SEXP envVarsSEXP, varSEXP; if(tmpdir[0] == 0) return; if(objbr_auto != 1) return; #ifndef WIN32 struct timeval begin, middle, end, tdiff1, tdiff2; if(verbose > 1) gettimeofday(&begin, NULL); #endif memset(obbrbuf2, 0, obbrbufzise); char *p = nvimcom_strcat(obbrbuf2, ".GlobalEnv | Libraries\n\n"); PROTECT(envVarsSEXP = R_lsInternal(R_GlobalEnv, allnames)); for(int i = 0; i < Rf_length(envVarsSEXP); i++){ varName = CHAR(STRING_ELT(envVarsSEXP, i)); PROTECT(varSEXP = Rf_findVar(Rf_install(varName), R_GlobalEnv)); if (varSEXP != R_UnboundValue) // should never be unbound { p = nvimcom_browser_line(&varSEXP, varName, "", " ", p); } else { REprintf("Unexpected R_UnboundValue returned from R_lsInternal.\n"); } UNPROTECT(1); } UNPROTECT(1); #ifndef WIN32 if(verbose > 1) gettimeofday(&middle, NULL); #endif int len1 = strlen(obbrbuf1); int len2 = strlen(obbrbuf2); if(len1 != len2){ nvimcom_write_obbr(); } else { for(int i = 0; i < len1; i++){ if(obbrbuf1[i] != obbrbuf2[i]){ nvimcom_write_obbr(); break; } } } #ifndef WIN32 if(verbose > 1){ gettimeofday(&end, NULL); timersub(&middle, &begin, &tdiff1); timersub(&end, &middle, &tdiff2); Rprintf("Time to Update the Object Browser: %ld.%06ld + %ld.%06ld\n", (long int)tdiff1.tv_sec, (long int)tdiff1.tv_usec, (long int)tdiff2.tv_sec, (long int)tdiff2.tv_usec); } #endif }
// TODO: split out some of the large blocks into helper functions, to make this easier to read void RKStructureGetter::getStructureWorker (SEXP val, const QString &name, int add_type_flags, RData *storage, int nesting_depth) { RK_TRACE (RBACKEND); bool at_toplevel = (toplevel_value == val); bool is_function = false; bool is_container = false; bool is_environment = false; bool no_recurse = (nesting_depth >= 2); // TODO: should be configurable unsigned int type = 0; RK_DEBUG (RBACKEND, DL_DEBUG, "fetching '%s': %p, s-type %d", name.toLatin1().data(), val, TYPEOF (val)); SEXP value = val; PROTECT_INDEX value_index; PROTECT_WITH_INDEX (value, &value_index); // manually resolve any promises REPROTECT (value = resolvePromise (value), value_index); bool is_s4 = Rf_isS4 (value); SEXP baseenv = R_BaseEnv; if (is_s4) baseenv = R_GlobalEnv; // first field: get name RData *namedata = new RData; namedata->setData (QStringList (name)); // get classes SEXP classes_s; if ((TYPEOF (value) == LANGSXP) || (TYPEOF (value) == SYMSXP)) { // if it's a call, we should NEVER send it through eval extern SEXP R_data_class (SEXP, Rboolean); classes_s = R_data_class (value, (Rboolean) 0); REPROTECT (value = Rf_coerceVector (value, EXPRSXP), value_index); // make sure the object is safe for everything to come PROTECT (classes_s); } else { classes_s = RKRSupport::callSimpleFun (class_fun, value, baseenv); PROTECT (classes_s); } QStringList classes = RKRSupport::SEXPToStringList (classes_s); UNPROTECT (1); /* classes_s */ // store classes RData *classdata = new RData; classdata->setData (classes); // basic classification for (int i = classes.size () - 1; i >= 0; --i) { #warning: Using is.data.frame() may be more reliable (would need to be called only on List-objects, thus no major performance hit) if (classes[i] == "data.frame") type |= RObject::DataFrame; } if (RKRSupport::callSimpleBool (is_matrix_fun, value, baseenv)) type |= RObject::Matrix; if (RKRSupport::callSimpleBool (is_list_fun, value, baseenv)) type |= RObject::List; if (type != 0) { is_container = true; type |= RObject::Container; } else { if (RKRSupport::callSimpleBool (is_function_fun, value, baseenv)) { is_function = true; type |= RObject::Function; } else if (RKRSupport::callSimpleBool (is_environment_fun, value, baseenv)) { is_container = true; type |= RObject::Environment; is_environment = true; } else { type |= RObject::Variable; if (RKRSupport::callSimpleBool (is_factor_fun, value, baseenv)) type |= RObject::Factor; else if (RKRSupport::callSimpleBool (is_numeric_fun, value, baseenv)) type |= RObject::Numeric; else if (RKRSupport::callSimpleBool (is_character_fun, value, baseenv)) type |= RObject::Character; else if (RKRSupport::callSimpleBool (is_logical_fun, value, baseenv)) type |= RObject::Logical; if (RKRSupport::callSimpleBool (is_array_fun, value, baseenv)) type |= RObject::Array; } } type |= add_type_flags; if (is_container) { if (no_recurse) { type |= RObject::Incomplete; RK_DEBUG (RBACKEND, DL_DEBUG, "Depth limit reached. Will not recurse into %s", name.toLatin1().data ()); } } // get meta data, if any RData *metadata = new RData; if (!Rf_isNull (Rf_getAttrib (value, meta_attrib))) { SEXP meta_s = RKRSupport::callSimpleFun (get_meta_fun, value, R_GlobalEnv); PROTECT (meta_s); metadata->setData (RKRSupport::SEXPToStringList (meta_s)); UNPROTECT (1); /* meta_s */ } else { metadata->setData (QStringList ()); } // get dims RData::IntStorage dims; SEXP dims_s = RKRSupport::callSimpleFun (dims_fun, value, baseenv); if (!Rf_isNull (dims_s)) { dims = RKRSupport::SEXPToIntArray (dims_s); } else { unsigned int len = Rf_length (value); if ((len < 2) && (!is_function)) { // suspicious. Maybe some kind of list SEXP len_s = RKRSupport::callSimpleFun (length_fun, value, baseenv); PROTECT (len_s); if (Rf_isNull (len_s)) { dims.append (len); } else { dims = RKRSupport::SEXPToIntArray (len_s); } UNPROTECT (1); /* len_s */ } else { dims.append (len); } } // store dims RData *dimdata = new RData; dimdata->setData (dims); RData *slotsdata = new RData (); // does it have slots? if (is_s4) { type |= RObject::S4Object; if (no_recurse) { type |= RObject::Incomplete; RK_DEBUG (RBACKEND, DL_DEBUG, "Depth limit reached. Will not recurse into slots of %s", name.toLatin1().data ()); } else { RData::RDataStorage dummy (1, 0); dummy[0] = new RData (); SEXP slots_pseudo_object = RKRSupport::callSimpleFun (rk_get_slots_fun, value, R_GlobalEnv); PROTECT (slots_pseudo_object); getStructureSafe (slots_pseudo_object, "SLOTS", RObject::PseudoObject, dummy[0], nesting_depth); // do not increase depth for this pseudo-object UNPROTECT (1); slotsdata->setData (dummy); } } // store type RData *typedata = new RData; typedata->setData (RData::IntStorage (1, type)); // store everything we have so far int storage_length = RObject::StorageSizeBasicInfo; if (is_container) { storage_length = RObject::StorageSizeBasicInfo + 1; } else if (is_function) { storage_length = RObject::StorageSizeBasicInfo + 2; } RData::RDataStorage res (storage_length, 0); res[RObject::StoragePositionName] = namedata; res[RObject::StoragePositionType] = typedata; res[RObject::StoragePositionClass] = classdata; res[RObject::StoragePositionMeta] = metadata; res[RObject::StoragePositionDims] = dimdata; res[RObject::StoragePositionSlots] = slotsdata; // now add the extra info for containers and functions if (is_container) { bool do_env = (is_environment && (!no_recurse)); bool do_cont = is_container && (!is_environment) && (!no_recurse); // fetch list of child names SEXP childnames_s; if (do_env) { childnames_s = R_lsInternal (value, (Rboolean) 1); } else if (do_cont) { childnames_s = RKRSupport::callSimpleFun (names_fun, value, baseenv); } else { childnames_s = R_NilValue; // dummy } PROTECT (childnames_s); QStringList childnames = RKRSupport::SEXPToStringList (childnames_s); int childcount = childnames.size (); if (childcount > NAMED_CHILDREN_LIMIT) { RK_DEBUG (RBACKEND, DL_WARNING, "object %s has %d named children. Will only retrieve the first %d", name.toLatin1().data (), childcount, NAMED_CHILDREN_LIMIT); childcount = NAMED_CHILDREN_LIMIT; } RData::RDataStorage children (childcount, 0); for (int i = 0; i < childcount; ++i) { children[i] = new RData (); // NOTE: RData-ctor pre-initalizes these to empty. Thus, we're safe even if there is an error while fetching one of the children. } if (do_env) { RK_DEBUG (RBACKEND, DL_DEBUG, "recurse into environment %s", name.toLatin1().data ()); if (!Rf_isEnvironment (value)) { // some classes (ReferenceClasses) are identified as envionments by is.environment(), but are not internally ENVSXPs. // For these, Rf_findVar would fail. REPROTECT (value = RKRSupport::callSimpleFun (as_environment_fun, value, R_GlobalEnv), value_index); } for (int i = 0; i < childcount; ++i) { SEXP current_childname = Rf_install(CHAR(STRING_ELT(childnames_s, i))); // ??? Why does simply using STRING_ELT(childnames_i, i) crash? PROTECT (current_childname); SEXP child = Rf_findVar (current_childname, value); PROTECT (child); bool child_misplaced = false; if (at_toplevel && with_namespace && (!RKRBackend::this_pointer->RRuntimeIsVersion (2, 14, 0))) { if (!Rf_isNull (namespace_envir)) { SEXP dummy = Rf_findVarInFrame (namespace_envir, current_childname); if (Rf_isNull (dummy) || (dummy == R_UnboundValue)) child_misplaced = true; } } getStructureSafe (child, childnames[i], child_misplaced ? RObject::Misplaced : 0, children[i], nesting_depth + 1); UNPROTECT (2); /* current_childname, child */ } } else if (do_cont) { RK_DEBUG (RBACKEND, DL_DEBUG, "recurse into list %s", name.toLatin1().data ()); // fewer elements than names() can happen, although I doubt it is supposed to happen. // see http://sourceforge.net/tracker/?func=detail&aid=3002439&group_id=50231&atid=459007 bool may_be_special = Rf_length (value) < childcount; if (Rf_isList (value) && (!may_be_special)) { // old style list for (int i = 0; i < childcount; ++i) { SEXP child = CAR (value); getStructureSafe (child, childnames[i], 0, children[i], nesting_depth + 1); CDR (value); } } else if (Rf_isNewList (value) && (!may_be_special)) { // new style list for (int i = 0; i < childcount; ++i) { SEXP child = VECTOR_ELT(value, i); getStructureSafe (child, childnames[i], 0, children[i], nesting_depth + 1); } } else { // probably an S4 object disguised as a list SEXP index = Rf_allocVector(INTSXP, 1); PROTECT (index); for (int i = 0; i < childcount; ++i) { INTEGER (index)[0] = (i + 1); SEXP child = RKRSupport::callSimpleFun2 (double_brackets_fun, value, index, baseenv); getStructureSafe (child, childnames[i], 0, children[i], nesting_depth + 1); } UNPROTECT (1); /* index */ } } UNPROTECT (1); /* childnames_s */ RData *childdata = new RData; childdata->setData (children); res[RObject::StoragePositionChildren] = childdata; if (is_environment && at_toplevel && with_namespace) { RData *namespacedata = new RData; if (no_recurse) { type |= RObject::Incomplete; RK_DEBUG (RBACKEND, DL_DEBUG, "Depth limit reached. Will not recurse into namespace of %s", name.toLatin1().data ()); } else { RData::RDataStorage dummy (1, 0); dummy[0] = new RData (); getStructureSafe (namespace_envir, "NAMESPACE", RObject::PseudoObject, dummy[0], nesting_depth+99); // HACK: By default, do not recurse into the children of the namespace, until dealing with the namespace object itself. namespacedata->setData (dummy); } res.insert (RObject::StoragePositionNamespace, namespacedata); } } else if (is_function) { // TODO: getting the formals is still a bit of a bottleneck, but no idea, how to improve on this, any further SEXP formals_s; if (Rf_isPrimitive (value)) formals_s = FORMALS (RKRSupport::callSimpleFun (args_fun, value, baseenv)); // primitives don't have formals, internally else formals_s = FORMALS (value); PROTECT (formals_s); // get the default values QStringList formals = RKRSupport::SEXPToStringList (formals_s); // for the most part, the implicit as.character in SEXPToStringList does a good on the formals (and it's the fastest of many options that I have tried). // Only for naked strings (as in 'function (a="something")'), we're missing the quotes. So we add quotes, after conversion, as needed: SEXP dummy = formals_s; const int formals_len = Rf_length (formals_s); for (int i = 0; i < formals_len; ++i) { if (TYPEOF (CAR (dummy)) == STRSXP) formals[i] = RKRSharedFunctionality::quote (formals[i]); dummy = CDR (dummy); } RData *funargvaluesdata = new RData; funargvaluesdata->setData (formals); // the argument names SEXP names_s = Rf_getAttrib (formals_s, R_NamesSymbol); PROTECT (names_s); RData *funargsdata = new RData; funargsdata->setData (RKRSupport::SEXPToStringList (names_s)); UNPROTECT (2); /* names_s, formals_s */ res[RObject::StoragePositionFunArgs] = funargsdata; res[RObject::StoragePositionFunValues] = funargvaluesdata; } UNPROTECT (1); /* value */ RK_ASSERT (!res.contains (0)); storage->setData (res); }