Skip to content
Snippets Groups Projects
testrffi.c 14.23 KiB
/*
 * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 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 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

// A very simple test of the R FFI interface

#include <R.h>
#include <Rdefines.h>
#include <Rinterface.h>
#include <Rinternals.h>
#include <Rinterface.h>
#include <R_ext/Connections.h>
#include <R_ext/Parse.h>
#include <string.h>
#include "testrffi.h"

void dotCModifiedArguments(int* len, int* idata, double* rdata, int* ldata, char** cdata) {
    for (int i = 0; i < len[0]; i++) {
	idata[i] ++;
    }
    for (int i = 0; i < len[0]; i++) {
	rdata[i] *= 0.2;
    }
    for (int i = 0; i < len[0]; i++) {
    ldata[i] = ldata[i] == 0 ? 1 : 0;
    }
    for (int i = 0; i < len[0]; i++) {
        for (int j = 0; cdata[i][j] != 0; j++) {
            char c = cdata[i][j];
            cdata[i][j] = (c >= '0' && c <= '9') ? c - '0' + 'a' : 'r';
        }
    }
}

SEXP addInt(SEXP a, SEXP b) {
	int aInt = INTEGER_VALUE(a);
	int bInt = INTEGER_VALUE(b);
	return ScalarInteger(aInt + bInt);
}

SEXP addDouble(SEXP a, SEXP b) {
	double aDouble = NUMERIC_VALUE(a);
	double bDouble = NUMERIC_VALUE(b);
	return ScalarReal(aDouble + bDouble);
}

SEXP populateIntVector(SEXP n) {
    SEXP v;
    int intN = INTEGER_VALUE(n);
    PROTECT(v = allocVector(INTSXP, intN));
    int i;
    for (i = 0; i < intN; i++) {
    	INTEGER(v)[i] = i;
    }
    UNPROTECT(1);
    return v;
}

SEXP populateLogicalVector(SEXP n) {
    SEXP v;
    int intN = INTEGER_VALUE(n);
    PROTECT(v = allocVector(LGLSXP, intN));
    int i;
    for (i = 0; i < intN; i++) {
    	LOGICAL(v)[i] = i == 0 ? TRUE : i == 1 ? NA_INTEGER : FALSE;
    }
    UNPROTECT(1);
    return v;
}

SEXP createExternalPtr(SEXP addr, SEXP tag, SEXP prot) {
	return R_MakeExternalPtr((void *) (long) INTEGER_VALUE(addr), tag, prot);
}

SEXP getExternalPtrAddr(SEXP eptr) {
	return ScalarInteger((int) R_ExternalPtrAddr(eptr));
}

SEXP invoke_TYPEOF(SEXP x) {
	return ScalarInteger(TYPEOF(x));
}

SEXP invoke_error(SEXP msg) {
	error(R_CHAR(STRING_ELT(msg, 0)));
}

// returns a
SEXP dot_external_access_args(SEXP args) {
	args = CDR(args);
	int index = 0;
	SEXP list;
	PROTECT(list = allocVector(VECSXP, length(args)));
	for (; args != R_NilValue; args = CDR(args)) {
		SEXP tag = TAG(args);
		SEXP value = CAR(args);
		SEXP listElement;
		PROTECT(listElement = allocVector(VECSXP, 2));
		SET_VECTOR_ELT(listElement, 0, tag);
		SEXP firstValue = R_NilValue;
		if (length(value) == 0) {
			firstValue = PROTECT(R_NilValue);
		} else {
			switch (TYPEOF(value)) {
			case LGLSXP:
			case INTSXP:{
				PROTECT(firstValue = allocVector(INTSXP, 1));
				INTEGER(firstValue)[0] = INTEGER(value)[0];
				break;
			}
			case REALSXP: {
				PROTECT(firstValue = allocVector(REALSXP, 1));
				REAL(firstValue)[0] = REAL(value)[0];
				break;
			}
			case STRSXP:
				PROTECT(firstValue = ScalarString(STRING_ELT(value, 0)));
				break;
			case RAWSXP: {
				PROTECT(firstValue = allocVector(RAWSXP, 1));
				RAW(firstValue)[0] = RAW(value)[0];
				break;
			}
			default:
				firstValue = PROTECT(R_NilValue);
			}
		}

		SET_VECTOR_ELT(listElement, 1, firstValue);
		SET_VECTOR_ELT(list, index, listElement);
		UNPROTECT(1); // firstValue
		UNPROTECT(1); // listElement
		index++;
	}
	UNPROTECT(1); // list
	return list;
}

SEXP invoke_isString(SEXP s) {
  return ScalarLogical(isString(s));
}

SEXP invoke12(SEXP a1, SEXP a2, SEXP a3, SEXP a4, SEXP a5, SEXP a6, SEXP a7, SEXP a8, SEXP a9, SEXP a10, SEXP a11, SEXP a12) {
	return a12;
}

SEXP interactive(void) {
	return ScalarLogical(R_Interactive);
}

SEXP tryEval(SEXP expr, SEXP env) {
	int error = 0;
	SEXP r = R_tryEval(expr, env, &error);
	SEXP v;
	PROTECT(v = allocVector(VECSXP, 2));
	if (error) {
		r = R_NilValue;
	}
	SET_VECTOR_ELT(v, 0, r);
	SET_VECTOR_ELT(v, 1, ScalarLogical(error));
	UNPROTECT(1);
	return v;
}

SEXP rHomeDir() {
	char *dir = R_HomeDir();
	return ScalarString(mkChar(dir));
}

SEXP nestedCall1(SEXP upcall, SEXP env) {
	SEXP vec;
	PROTECT(vec = allocVector(INTSXP, 10));
	int *vecstar = INTEGER(vec);
	for (int i = 0; i < 10; i++) {
		vecstar[i] = i + 1;
	}
	SEXP upcallResult = tryEval(upcall, env);
	int *vecstar2 = INTEGER(vec);
	int ok = vecstar == vecstar2;
	if (ok) {
		for (int i = 0; i < 10; i++) {
			if (vecstar[i] != i + 1) {
				ok = 0;
				break;
			}
		}
	}
	SEXP result;
	PROTECT(result = allocVector(VECSXP, 2));
	SET_VECTOR_ELT(result, 0, upcallResult);
	SET_VECTOR_ELT(result, 1, ScalarLogical(ok));
	UNPROTECT(2);
	return result;
}

SEXP nestedCall2(SEXP v) {
	SEXP sumVec;
	PROTECT(sumVec = allocVector(INTSXP, 1));
	int len = Rf_length(v);
	int sum = 0;
	for (int i = 0; i < len; i++) {
		sum += INTEGER(v)[i];
	}
	INTEGER(sumVec)[0] = sum;
	UNPROTECT(1);
	return sumVec;
}

SEXP r_home(void) {
	return mkString(R_Home);
}

SEXP char_length(SEXP x) {
	const char *cx = R_CHAR(STRING_ELT(x, 0));
	int count  = 0;
	while (*cx++ != 0) {
		count++;
	}
	return ScalarInteger(count);
}

SEXP mkStringFromChar(void) {
	return mkString("hello");
}

SEXP mkStringFromBytes(void) {
	char *helloworld = "hello world";
	return ScalarString(mkCharLen(helloworld, 5));
}

SEXP null(void) {
	return R_NilValue;
}

SEXP iterate_iarray(SEXP x) {
	int *cx = INTEGER(x);
	int len = LENGTH(x);
    SEXP v;
    PROTECT(v = allocVector(INTSXP, len));
    int *iv = INTEGER(v);
    int i;
    for (i = 0; i < len; i++) {
    	iv[i] = cx[i];
    }
    UNPROTECT(1);
    return v;
}

SEXP iterate_iptr(SEXP x) {
	int *cx = INTEGER(x);
	int len = LENGTH(x);
    SEXP v;
    PROTECT(v = allocVector(INTSXP, len));
    int *iv = INTEGER(v);
    int i;
    for (i = 0; i < len; i++) {
    	*iv++ = *cx++;
    }
    UNPROTECT(1);
    return v;
}
SEXP preserve_object(SEXP val) {
	SEXP v;
	v = allocVector(INTSXP, 1);
    int *iv = INTEGER(v);
    if(LENGTH(val) > 0) {
    	int *ival = INTEGER(val);
    	iv[0] = ival[1];
    } else {
    	iv[0] = 1234;
    }
	R_PreserveObject(v);
	return v;
}

SEXP release_object(SEXP x) {
	R_ReleaseObject(x);
    return R_NilValue;
}

SEXP findvar(SEXP x, SEXP env) {
	SEXP v = Rf_findVar(x, env);
	if (v == R_UnboundValue) {
		Rf_error("'%s' not found", R_CHAR(PRINTNAME(x)));
	} else {
		return v;
	}
}

SEXP test_asReal(SEXP x) {
	return Rf_ScalarReal(Rf_asReal(x));
}

SEXP test_asInteger(SEXP x) {
	return Rf_ScalarInteger(Rf_asInteger(x));
}

SEXP test_asLogical(SEXP x) {
	return Rf_ScalarLogical(Rf_asLogical(x));
}

SEXP test_asChar(SEXP x) {
	return Rf_ScalarString(Rf_asChar(x));
}

SEXP test_CAR(SEXP x) {
	return CAR(x);
}

SEXP test_CDR(SEXP x) {
	return CDR(x);
}

SEXP test_LENGTH(SEXP x) {
	return ScalarInteger(LENGTH(x));
}

SEXP test_inlined_length(SEXP x) {
    return ScalarInteger(length(x));
}

SEXP test_coerceVector(SEXP x, SEXP mode) {
    int intMode = INTEGER_VALUE(mode);
    return Rf_coerceVector(x, intMode);
}

SEXP test_ATTRIB(SEXP x) {
    return ATTRIB(x);
}

SEXP test_getAttrib(SEXP source, SEXP name) {
    return Rf_getAttrib(source, name);
}

SEXP test_stringNA(void) {
    SEXP x = allocVector(STRSXP, 1);
    SET_STRING_ELT(x, 0, NA_STRING);
    return x;
}

// This function is expected to be called only with environment that has single
// promise value in the '...' variable and this is asserted inside this function.
// The return value is list with the promises' expression and environment.
SEXP test_captureDotsWithSingleElement(SEXP env) {
    SEXP dots = findVarInFrame3(env, R_DotsSymbol, TRUE);
    int n_dots = length(dots);
    if (n_dots != 1) {
        printf("Error: test_captureDotsWithSingleElement expectes single promise in ...\n");
        return R_NilValue;
    }
    SEXP promise = CAR(dots);
    if (TYPEOF(promise) != PROMSXP) {
        printf("Error: test_captureDotsWithSingleElement expectes a promise in ...\n");
        return R_NilValue;
    }
    SEXP info = PROTECT(allocVector(VECSXP, 2));
    SET_VECTOR_ELT(info, 0, R_PromiseExpr(promise));
    SET_VECTOR_ELT(info, 1, PRENV(promise));
    UNPROTECT(1);
    return info;
}

SEXP test_evalAndNativeArrays(SEXP vec, SEXP expr, SEXP env) {
    SEXP symbolValue;
    int *idata;
    double *ddata;
    unsigned char *bdata;
    // note: we want to evaluate PROTECT(symbolValue = Rf_eval(expr, env)); after we take the pointer to data...
    switch (TYPEOF(vec)) {
        case INTSXP:
            idata = INTEGER(vec);
            PROTECT(symbolValue = Rf_eval(expr, env));
            idata[0] = 42;
            idata[1] = Rf_asInteger(symbolValue);
            break;
        case REALSXP:
            ddata = REAL(vec);
            PROTECT(symbolValue = Rf_eval(expr, env));
            ddata[0] = 42;
            ddata[1] = Rf_asReal(symbolValue);
            break;
        case RAWSXP:
            bdata = RAW(vec);
            PROTECT(symbolValue = Rf_eval(expr, env));
            bdata[0] = 42;
            bdata[1] = Rf_asInteger(symbolValue);  // there is no asRaw, we expect to get symbol with integer value
            break;
        case LGLSXP:
            idata = LOGICAL(vec);
            PROTECT(symbolValue = Rf_eval(expr, env));
            idata[0] = 1;
            idata[1] = Rf_asLogical(symbolValue);
            break;
        default:
            printf("Error: unexpected type");
    }

    // max of the vector could now be 42/TRUE or symbolValue
    SEXP maxSymbol, call, maxVec;
    int uprotectCount = 1;
    if (TYPEOF(vec) != RAWSXP) {
        // note: max does not support raws
        PROTECT(maxSymbol = install("max"));
        PROTECT(call = lang2(maxSymbol, vec));
        PROTECT(maxVec = eval(call, R_GlobalEnv));
        uprotectCount = 4;
    }

    switch (TYPEOF(vec)) {
        case INTSXP:
            idata[length(vec) - 1] = Rf_asInteger(maxVec);
            break;
        case REALSXP:
            ddata[length(vec) - 1] = Rf_asReal(maxVec);
            break;
        case RAWSXP:
            bdata[length(vec) - 1] = 42;
            break;
        case LGLSXP:
            idata[length(vec) - 1] = Rf_asLogical(maxVec);
            break;
        default:
            printf("Error: unexpected type");
    }

    UNPROTECT(uprotectCount);
    return vec;
}

SEXP test_writeConnection(SEXP connVec) {
	Rconnection connection = R_GetConnection(connVec);
	char* greeting = "Hello from R_WriteConnection";
	R_WriteConnection(connection, greeting, strlen(greeting));
    return R_NilValue;
}

SEXP test_readConnection(SEXP connVec) {
    Rconnection connection = R_GetConnection(connVec);
    unsigned char buffer[255];
    int size = R_ReadConnection(connection, buffer, 255);
    SEXP result;
    PROTECT(result = allocVector(RAWSXP, size));
    unsigned char* resultData = RAW(result);
    for (int i = 0; i < size; ++i) {
        resultData[i] = buffer[i];
    }
    UNPROTECT(1);
    return result;
}

static Rconnection customConn;

static void printNow(const char* message) {
    puts(message);
    fflush(stdout);
}

static void testrfficonn_destroy(Rconnection conn) {
    if (conn != customConn) {
        printNow("ERROR: destroy function did not receive expected argument\n");
    } else {
        printNow("Custom connection destroyed\n");
    }
}

static Rboolean testrfficonn_open(Rconnection conn) {
    if (conn != customConn) {
        printNow("ERROR: open function did not receive expected argument\n");
        return 0;
    } else {
        printNow("Custom connection opened\n");
        return 1;
    }
}

static void testrfficonn_close(Rconnection conn) {
    if (conn != customConn) {
        printNow("ERROR: close function did not receive expected argument\n");
    } else {
        printNow("Custom connection closed\n");
    }
}

static size_t testrfficonn_write(const void * message, size_t size, size_t nitems, Rconnection conn) {
    if (conn != customConn) {
        printNow("ERROR: write function did not receive expected argument\n");
        return 0;
    } else {
        printf("Custom connection printing: %.*s\n", (int) (size * nitems), (char*) message);
        fflush(stdout);
        return size * nitems;
    }
}

static size_t testrfficonn_read(void *buffer, size_t size, size_t niterms, Rconnection conn) {
    if (conn != customConn) {
        printNow("ERROR: read function did not receive expected argument\n");
        return 0;
    } else if (size * niterms > 0) {
        ((char *)buffer)[0] = 'Q';
        return 1;
    }
    return 0;
}

SEXP test_createNativeConnection() {
    SEXP newConnSEXP = R_new_custom_connection("Connection for testing purposes", "w", "testrfficonn", &customConn);
    customConn->isopen = 0;
    customConn->canwrite = 1;
    customConn->destroy = &testrfficonn_destroy;
    customConn->open = &testrfficonn_open;
    customConn->close = &testrfficonn_close;
    customConn->write = &testrfficonn_write;
    // customConn->read = &testrfficonn_read; TODO: read test
    return newConnSEXP;
}

SEXP test_ParseVector(SEXP src) {
    ParseStatus status;
    SEXP parseResult, result;
    PROTECT(parseResult = R_ParseVector(src, 1, &status, R_NilValue));
    PROTECT(result = allocVector(VECSXP, 2));
    SET_VECTOR_ELT(result, 0, ScalarInteger(status));
    SET_VECTOR_ELT(result, 1, parseResult);
    UNPROTECT(2);
    return result;
}

SEXP test_RfEvalWithPromiseInPairList() {
    SEXP fun = Rf_findVarInFrame(R_FindNamespace(ScalarString(mkChar("stats"))), Rf_install("runif"));
    if (TYPEOF(fun) != PROMSXP) {
        printf("ERROR: Rf_findVarInFrame evaluated the promise!");
    }
    SEXP e, ptr;
    PROTECT(e = Rf_allocVector(LANGSXP, 2));
    SETCAR(e, fun); ptr = CDR(e);
    SETCAR(ptr, ScalarInteger(5));
    SEXP result = Rf_eval(e, R_GlobalEnv);
    UNPROTECT(1);
    return result;
}