/*
* Copyright 2010-2012, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "slang_rs_reflection.h"
#include <sys/stat.h>
#include <cstdarg>
#include <cctype>
#include <algorithm>
#include <sstream>
#include <string>
#include <utility>
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/StringExtras.h"
#include "os_sep.h"
#include "slang_rs_context.h"
#include "slang_rs_export_var.h"
#include "slang_rs_export_foreach.h"
#include "slang_rs_export_func.h"
#include "slang_rs_reflect_utils.h"
#include "slang_version.h"
#include "slang_utils.h"
#include "slang_rs_reflection_base.h"
#define RS_SCRIPT_CLASS_NAME_PREFIX "ScriptC_"
#define RS_SCRIPT_CLASS_SUPER_CLASS_NAME "ScriptC"
#define RS_TYPE_CLASS_SUPER_CLASS_NAME "android.renderscript.Script.FieldBase"
#define RS_TYPE_ITEM_CLASS_NAME "Item"
#define RS_TYPE_ITEM_BUFFER_NAME "mItemArray"
#define RS_TYPE_ITEM_BUFFER_PACKER_NAME "mIOBuffer"
#define RS_TYPE_ELEMENT_REF_NAME "mElementCache"
#define RS_EXPORT_VAR_INDEX_PREFIX "mExportVarIdx_"
#define RS_EXPORT_VAR_PREFIX "mExportVar_"
#define RS_EXPORT_VAR_ELEM_PREFIX "mExportVarElem_"
#define RS_EXPORT_VAR_DIM_PREFIX "mExportVarDim_"
#define RS_EXPORT_VAR_CONST_PREFIX "const_"
#define RS_ELEM_PREFIX "__"
#define RS_FP_PREFIX "__rs_fp_"
#define RS_RESOURCE_NAME "__rs_resource_name"
#define RS_EXPORT_FUNC_INDEX_PREFIX "mExportFuncIdx_"
#define RS_EXPORT_FOREACH_INDEX_PREFIX "mExportForEachIdx_"
#define RS_EXPORT_VAR_ALLOCATION_PREFIX "mAlloction_"
#define RS_EXPORT_VAR_DATA_STORAGE_PREFIX "mData_"
namespace slang {
// Some utility function using internal in RSReflection
static bool GetClassNameFromFileName(const std::string &FileName,
std::string &ClassName) {
ClassName.clear();
if (FileName.empty() || (FileName == "-"))
return true;
ClassName =
RSSlangReflectUtils::JavaClassNameFromRSFileName(FileName.c_str());
return true;
}
static const char *GetMatrixTypeName(const RSExportMatrixType *EMT) {
static const char *MatrixTypeJavaNameMap[] = {
/* 2x2 */ "Matrix2f",
/* 3x3 */ "Matrix3f",
/* 4x4 */ "Matrix4f",
};
unsigned Dim = EMT->getDim();
if ((Dim - 2) < (sizeof(MatrixTypeJavaNameMap) / sizeof(const char*)))
return MatrixTypeJavaNameMap[ EMT->getDim() - 2 ];
slangAssert(false && "GetMatrixTypeName : Unsupported matrix dimension");
return NULL;
}
static const char *GetVectorAccessor(unsigned Index) {
static const char *VectorAccessorMap[] = {
/* 0 */ "x",
/* 1 */ "y",
/* 2 */ "z",
/* 3 */ "w",
};
slangAssert((Index < (sizeof(VectorAccessorMap) / sizeof(const char*))) &&
"Out-of-bound index to access vector member");
return VectorAccessorMap[Index];
}
static const char *GetPackerAPIName(const RSExportPrimitiveType *EPT) {
static const char *PrimitiveTypePackerAPINameMap[] = {
"", // RSExportPrimitiveType::DataTypeFloat16
"addF32", // RSExportPrimitiveType::DataTypeFloat32
"addF64", // RSExportPrimitiveType::DataTypeFloat64
"addI8", // RSExportPrimitiveType::DataTypeSigned8
"addI16", // RSExportPrimitiveType::DataTypeSigned16
"addI32", // RSExportPrimitiveType::DataTypeSigned32
"addI64", // RSExportPrimitiveType::DataTypeSigned64
"addU8", // RSExportPrimitiveType::DataTypeUnsigned8
"addU16", // RSExportPrimitiveType::DataTypeUnsigned16
"addU32", // RSExportPrimitiveType::DataTypeUnsigned32
"addU64", // RSExportPrimitiveType::DataTypeUnsigned64
"addBoolean", // RSExportPrimitiveType::DataTypeBoolean
"addU16", // RSExportPrimitiveType::DataTypeUnsigned565
"addU16", // RSExportPrimitiveType::DataTypeUnsigned5551
"addU16", // RSExportPrimitiveType::DataTypeUnsigned4444
"addMatrix", // RSExportPrimitiveType::DataTypeRSMatrix2x2
"addMatrix", // RSExportPrimitiveType::DataTypeRSMatrix3x3
"addMatrix", // RSExportPrimitiveType::DataTypeRSMatrix4x4
"addObj", // RSExportPrimitiveType::DataTypeRSElement
"addObj", // RSExportPrimitiveType::DataTypeRSType
"addObj", // RSExportPrimitiveType::DataTypeRSAllocation
"addObj", // RSExportPrimitiveType::DataTypeRSSampler
"addObj", // RSExportPrimitiveType::DataTypeRSScript
"addObj", // RSExportPrimitiveType::DataTypeRSMesh
"addObj", // RSExportPrimitiveType::DataTypeRSPath
"addObj", // RSExportPrimitiveType::DataTypeRSProgramFragment
"addObj", // RSExportPrimitiveType::DataTypeRSProgramVertex
"addObj", // RSExportPrimitiveType::DataTypeRSProgramRaster
"addObj", // RSExportPrimitiveType::DataTypeRSProgramStore
"addObj", // RSExportPrimitiveType::DataTypeRSFont
};
unsigned TypeId = EPT->getType();
if (TypeId < (sizeof(PrimitiveTypePackerAPINameMap) / sizeof(const char*)))
return PrimitiveTypePackerAPINameMap[ EPT->getType() ];
slangAssert(false && "GetPackerAPIName : Unknown primitive data type");
return NULL;
}
static std::string GetTypeName(const RSExportType *ET, bool Brackets = true) {
switch (ET->getClass()) {
case RSExportType::ExportClassPrimitive: {
return RSExportPrimitiveType::getRSReflectionType(
static_cast<const RSExportPrimitiveType*>(ET))->java_name;
}
case RSExportType::ExportClassPointer: {
const RSExportType *PointeeType =
static_cast<const RSExportPointerType*>(ET)->getPointeeType();
if (PointeeType->getClass() != RSExportType::ExportClassRecord)
return "Allocation";
else
return PointeeType->getElementName();
}
case RSExportType::ExportClassVector: {
const RSExportVectorType *EVT =
static_cast<const RSExportVectorType*>(ET);
std::stringstream VecName;
VecName << EVT->getRSReflectionType(EVT)->rs_java_vector_prefix
<< EVT->getNumElement();
return VecName.str();
}
case RSExportType::ExportClassMatrix: {
return GetMatrixTypeName(static_cast<const RSExportMatrixType*>(ET));
}
case RSExportType::ExportClassConstantArray: {
const RSExportConstantArrayType* CAT =
static_cast<const RSExportConstantArrayType*>(ET);
std::string ElementTypeName = GetTypeName(CAT->getElementType());
if (Brackets) {
ElementTypeName.append("[]");
}
return ElementTypeName;
}
case RSExportType::ExportClassRecord: {
return ET->getElementName() + "."RS_TYPE_ITEM_CLASS_NAME;
}
default: {
slangAssert(false && "Unknown class of type");
}
}
return "";
}
static const char *GetTypeNullValue(const RSExportType *ET) {
switch (ET->getClass()) {
case RSExportType::ExportClassPrimitive: {
const RSExportPrimitiveType *EPT =
static_cast<const RSExportPrimitiveType*>(ET);
if (EPT->isRSObjectType())
return "null";
else if (EPT->getType() == RSExportPrimitiveType::DataTypeBoolean)
return "false";
else
return "0";
break;
}
case RSExportType::ExportClassPointer:
case RSExportType::ExportClassVector:
case RSExportType::ExportClassMatrix:
case RSExportType::ExportClassConstantArray:
case RSExportType::ExportClassRecord: {
return "null";
break;
}
default: {
slangAssert(false && "Unknown class of type");
}
}
return "";
}
static std::string GetBuiltinElementConstruct(const RSExportType *ET) {
if (ET->getClass() == RSExportType::ExportClassPrimitive) {
return std::string("Element.") + ET->getElementName();
} else if (ET->getClass() == RSExportType::ExportClassVector) {
const RSExportVectorType *EVT = static_cast<const RSExportVectorType*>(ET);
if (EVT->getType() == RSExportPrimitiveType::DataTypeFloat32) {
if (EVT->getNumElement() == 2)
return "Element.F32_2";
else if (EVT->getNumElement() == 3)
return "Element.F32_3";
else if (EVT->getNumElement() == 4)
return "Element.F32_4";
} else if (EVT->getType() == RSExportPrimitiveType::DataTypeUnsigned8) {
if (EVT->getNumElement() == 4)
return "Element.U8_4";
}
} else if (ET->getClass() == RSExportType::ExportClassMatrix) {
const RSExportMatrixType *EMT = static_cast<const RSExportMatrixType *>(ET);
switch (EMT->getDim()) {
case 2: return "Element.MATRIX_2X2";
case 3: return "Element.MATRIX_3X3";
case 4: return "Element.MATRIX_4X4";
default: slangAssert(false && "Unsupported dimension of matrix");
}
}
// RSExportType::ExportClassPointer can't be generated in a struct.
return "";
}
/********************** Methods to generate script class **********************/
bool RSReflection::genScriptClass(Context &C,
const std::string &ClassName,
std::string &ErrorMsg) {
if (!C.startClass(Context::AM_Public,
false,
ClassName,
RS_SCRIPT_CLASS_SUPER_CLASS_NAME,
ErrorMsg))
return false;
genScriptClassConstructor(C);
// Reflect export variable
for (RSContext::const_export_var_iterator I = mRSContext->export_vars_begin(),
E = mRSContext->export_vars_end();
I != E;
I++)
genExportVariable(C, *I);
// Reflect export for each functions (only available on ICS+)
if (mRSContext->getTargetAPI() >= SLANG_ICS_TARGET_API) {
for (RSContext::const_export_foreach_iterator
I = mRSContext->export_foreach_begin(),
E = mRSContext->export_foreach_end();
I != E; I++)
genExportForEach(C, *I);
}
// Reflect export function
for (RSContext::const_export_func_iterator
I = mRSContext->export_funcs_begin(),
E = mRSContext->export_funcs_end();
I != E; I++)
genExportFunction(C, *I);
C.endClass();
return true;
}
void RSReflection::genScriptClassConstructor(Context &C) {
// Provide a simple way to reference this object.
C.indent() << "private static final String " RS_RESOURCE_NAME " = \""
<< C.getResourceId()
<< "\";" << std::endl;
// Generate a simple constructor with only a single parameter (the rest
// can be inferred from information we already have).
C.indent() << "// Constructor" << std::endl;
C.startFunction(Context::AM_Public,
false,
NULL,
C.getClassName(),
1,
"RenderScript", "rs");
// Call alternate constructor with required parameters.
// Look up the proper raw bitcode resource id via the context.
C.indent() << "this(rs," << std::endl;
C.indent() << " rs.getApplicationContext().getResources()," << std::endl;
C.indent() << " rs.getApplicationContext().getResources()."
"getIdentifier(" << std::endl;
C.indent() << " " RS_RESOURCE_NAME ", \"raw\"," << std::endl;
C.indent() << " rs.getApplicationContext().getPackageName()));"
<< std::endl;
C.endFunction();
// Alternate constructor (legacy) with 3 original parameters.
C.startFunction(Context::AM_Public,
false,
NULL,
C.getClassName(),
3,
"RenderScript", "rs",
"Resources", "resources",
"int", "id");
// Call constructor of super class
C.indent() << "super(rs, resources, id);" << std::endl;
// If an exported variable has initial value, reflect it
for (RSContext::const_export_var_iterator I = mRSContext->export_vars_begin(),
E = mRSContext->export_vars_end();
I != E;
I++) {
const RSExportVar *EV = *I;
if (!EV->getInit().isUninit()) {
genInitExportVariable(C, EV->getType(), EV->getName(), EV->getInit());
} else if (EV->getArraySize()) {
// Always create an initial zero-init array object.
C.indent() << RS_EXPORT_VAR_PREFIX << EV->getName() << " = new "
<< GetTypeName(EV->getType(), false) << "["
<< EV->getArraySize() << "];" << std::endl;
size_t NumInits = EV->getNumInits();
const RSExportConstantArrayType *ECAT =
static_cast<const RSExportConstantArrayType*>(EV->getType());
const RSExportType *ET = ECAT->getElementType();
for (size_t i = 0; i < NumInits; i++) {
std::stringstream Name;
Name << EV->getName() << "[" << i << "]";
genInitExportVariable(C, ET, Name.str(), EV->getInitArray(i));
}
}
if (mRSContext->getTargetAPI() >= SLANG_JB_TARGET_API) {
genTypeInstance(C, EV->getType());
}
genFieldPackerInstance(C, EV->getType());
}
for (RSContext::const_export_foreach_iterator
I = mRSContext->export_foreach_begin(),
E = mRSContext->export_foreach_end();
I != E;
I++) {
const RSExportForEach *EF = *I;
const RSExportType *IET = EF->getInType();
if (IET) {
genTypeInstanceFromPointer(C, IET);
}
const RSExportType *OET = EF->getOutType();
if (OET) {
genTypeInstanceFromPointer(C, OET);
}
}
C.endFunction();
for (std::set<std::string>::iterator I = C.mTypesToCheck.begin(),
E = C.mTypesToCheck.end();
I != E;
I++) {
C.indent() << "private Element " RS_ELEM_PREFIX << *I << ";" << std::endl;
}
for (std::set<std::string>::iterator I = C.mFieldPackerTypes.begin(),
E = C.mFieldPackerTypes.end();
I != E;
I++) {
C.indent() << "private FieldPacker " RS_FP_PREFIX << *I << ";" << std::endl;
}
return;
}
void RSReflection::genInitBoolExportVariable(Context &C,
const std::string &VarName,
const clang::APValue &Val) {
slangAssert(!Val.isUninit() && "Not a valid initializer");
C.indent() << RS_EXPORT_VAR_PREFIX << VarName << " = ";
slangAssert((Val.getKind() == clang::APValue::Int) &&
"Bool type has wrong initial APValue");
C.out() << ((Val.getInt().getSExtValue() == 0) ? "false" : "true")
<< ";" << std::endl;
return;
}
void RSReflection::genInitPrimitiveExportVariable(
Context &C,
const std::string &VarName,
const clang::APValue &Val) {
slangAssert(!Val.isUninit() && "Not a valid initializer");
C.indent() << RS_EXPORT_VAR_PREFIX << VarName << " = ";
C.out() << RSReflectionBase::genInitValue(Val);
C.out() << ";" << std::endl;
return;
}
void RSReflection::genInitExportVariable(Context &C,
const RSExportType *ET,
const std::string &VarName,
const clang::APValue &Val) {
slangAssert(!Val.isUninit() && "Not a valid initializer");
switch (ET->getClass()) {
case RSExportType::ExportClassPrimitive: {
const RSExportPrimitiveType *EPT =
static_cast<const RSExportPrimitiveType*>(ET);
if (EPT->getType() == RSExportPrimitiveType::DataTypeBoolean) {
genInitBoolExportVariable(C, VarName, Val);
} else {
genInitPrimitiveExportVariable(C, VarName, Val);
}
break;
}
case RSExportType::ExportClassPointer: {
if (!Val.isInt() || Val.getInt().getSExtValue() != 0)
std::cout << "Initializer which is non-NULL to pointer type variable "
"will be ignored" << std::endl;
break;
}
case RSExportType::ExportClassVector: {
const RSExportVectorType *EVT =
static_cast<const RSExportVectorType*>(ET);
switch (Val.getKind()) {
case clang::APValue::Int:
case clang::APValue::Float: {
for (unsigned i = 0; i < EVT->getNumElement(); i++) {
std::string Name = VarName + "." + GetVectorAccessor(i);
genInitPrimitiveExportVariable(C, Name, Val);
}
break;
}
case clang::APValue::Vector: {
std::stringstream VecName;
VecName << EVT->getRSReflectionType(EVT)->rs_java_vector_prefix
<< EVT->getNumElement();
C.indent() << RS_EXPORT_VAR_PREFIX << VarName << " = new "
<< VecName.str() << "();" << std::endl;
unsigned NumElements =
std::min(static_cast<unsigned>(EVT->getNumElement()),
Val.getVectorLength());
for (unsigned i = 0; i < NumElements; i++) {
const clang::APValue &ElementVal = Val.getVectorElt(i);
std::string Name = VarName + "." + GetVectorAccessor(i);
genInitPrimitiveExportVariable(C, Name, ElementVal);
}
break;
}
case clang::APValue::MemberPointer:
case clang::APValue::Uninitialized:
case clang::APValue::ComplexInt:
case clang::APValue::ComplexFloat:
case clang::APValue::LValue:
case clang::APValue::Array:
case clang::APValue::Struct:
case clang::APValue::Union:
case clang::APValue::AddrLabelDiff: {
slangAssert(false && "Unexpected type of value of initializer.");
}
}
break;
}
// TODO(zonr): Resolving initializer of a record (and matrix) type variable
// is complex. It cannot obtain by just simply evaluating the initializer
// expression.
case RSExportType::ExportClassMatrix:
case RSExportType::ExportClassConstantArray:
case RSExportType::ExportClassRecord: {
#if 0
unsigned InitIndex = 0;
const RSExportRecordType *ERT =
static_cast<const RSExportRecordType*>(ET);
slangAssert((Val.getKind() == clang::APValue::Vector) &&
"Unexpected type of initializer for record type variable");
C.indent() << RS_EXPORT_VAR_PREFIX << VarName
<< " = new " << ERT->getElementName()
<< "."RS_TYPE_ITEM_CLASS_NAME"();" << std::endl;
for (RSExportRecordType::const_field_iterator I = ERT->fields_begin(),
E = ERT->fields_end();
I != E;
I++) {
const RSExportRecordType::Field *F = *I;
std::string FieldName = VarName + "." + F->getName();
if (InitIndex > Val.getVectorLength())
break;
genInitPrimitiveExportVariable(C,
FieldName,
Val.getVectorElt(InitIndex++));
}
#endif
slangAssert(false && "Unsupported initializer for record/matrix/constant "
"array type variable currently");
break;
}
default: {
slangAssert(false && "Unknown class of type");
}
}
return;
}
void RSReflection::genExportVariable(Context &C, const RSExportVar *EV) {
const RSExportType *ET = EV->getType();
C.indent() << "private final static int "RS_EXPORT_VAR_INDEX_PREFIX
<< EV->getName() << " = " << C.getNextExportVarSlot() << ";"
<< std::endl;
switch (ET->getClass()) {
case RSExportType::ExportClassPrimitive: {
genPrimitiveTypeExportVariable(C, EV);
break;
}
case RSExportType::ExportClassPointer: {
genPointerTypeExportVariable(C, EV);
break;
}
case RSExportType::ExportClassVector: {
genVectorTypeExportVariable(C, EV);
break;
}
case RSExportType::ExportClassMatrix: {
genMatrixTypeExportVariable(C, EV);
break;
}
case RSExportType::ExportClassConstantArray: {
genConstantArrayTypeExportVariable(C, EV);
break;
}
case RSExportType::ExportClassRecord: {
genRecordTypeExportVariable(C, EV);
break;
}
default: {
slangAssert(false && "Unknown class of type");
}
}
return;
}
void RSReflection::genExportFunction(Context &C, const RSExportFunc *EF) {
C.indent() << "private final static int "RS_EXPORT_FUNC_INDEX_PREFIX
<< EF->getName() << " = " << C.getNextExportFuncSlot() << ";"
<< std::endl;
// invoke_*()
Context::ArgTy Args;
if (EF->hasParam()) {
for (RSExportFunc::const_param_iterator I = EF->params_begin(),
E = EF->params_end();
I != E;
I++) {
Args.push_back(std::make_pair(GetTypeName((*I)->getType()),
(*I)->getName()));
}
}
C.startFunction(Context::AM_Public,
false,
"void",
"invoke_" + EF->getName(/*Mangle=*/ false),
// We are using un-mangled name since Java
// supports method overloading.
Args);
if (!EF->hasParam()) {
C.indent() << "invoke("RS_EXPORT_FUNC_INDEX_PREFIX << EF->getName() << ");"
<< std::endl;
} else {
const RSExportRecordType *ERT = EF->getParamPacketType();
std::string FieldPackerName = EF->getName() + "_fp";
if (genCreateFieldPacker(C, ERT, FieldPackerName.c_str()))
genPackVarOfType(C, ERT, NULL, FieldPackerName.c_str());
C.indent() << "invoke("RS_EXPORT_FUNC_INDEX_PREFIX << EF->getName() << ", "
<< FieldPackerName << ");" << std::endl;
}
C.endFunction();
return;
}
void RSReflection::genExportForEach(Context &C, const RSExportForEach *EF) {
if (EF->isDummyRoot()) {
// Skip reflection for dummy root() kernels. Note that we have to
// advance the next slot number for ForEach, however.
C.indent() << "//private final static int "RS_EXPORT_FOREACH_INDEX_PREFIX
<< EF->getName() << " = " << C.getNextExportForEachSlot() << ";"
<< std::endl;
return;
}
C.indent() << "private final static int "RS_EXPORT_FOREACH_INDEX_PREFIX
<< EF->getName() << " = " << C.getNextExportForEachSlot() << ";"
<< std::endl;
// forEach_*()
Context::ArgTy Args;
slangAssert(EF->getNumParameters() > 0 || EF->hasReturn());
if (EF->hasIn())
Args.push_back(std::make_pair("Allocation", "ain"));
if (EF->hasOut() || EF->hasReturn())
Args.push_back(std::make_pair("Allocation", "aout"));
const RSExportRecordType *ERT = EF->getParamPacketType();
if (ERT) {
for (RSExportForEach::const_param_iterator I = EF->params_begin(),
E = EF->params_end();
I != E;
I++) {
Args.push_back(std::make_pair(GetTypeName((*I)->getType()),
(*I)->getName()));
}
}
const RSExportType *IET = EF->getInType();
const RSExportType *OET = EF->getOutType();
if (mRSContext->getTargetAPI() >= SLANG_JB_MR1_TARGET_API) {
int signature = 0;
C.startFunction(Context::AM_Public,
false,
"Script.KernelID",
"getKernelID_" + EF->getName(),
0);
if (IET)
signature |= 1;
if (OET)
signature |= 2;
//TODO: add element checking
C.indent() << "return createKernelID(" << RS_EXPORT_FOREACH_INDEX_PREFIX
<< EF->getName() << ", " << signature << ", null, null);"
<< std::endl;
C.endFunction();
}
C.startFunction(Context::AM_Public,
false,
"void",
"forEach_" + EF->getName(),
Args);
if (IET) {
genTypeCheck(C, IET, "ain");
}
if (OET) {
genTypeCheck(C, OET, "aout");
}
if (EF->hasIn() && (EF->hasOut() || EF->hasReturn())) {
C.indent() << "// Verify dimensions" << std::endl;
C.indent() << "Type tIn = ain.getType();" << std::endl;
C.indent() << "Type tOut = aout.getType();" << std::endl;
C.indent() << "if ((tIn.getCount() != tOut.getCount()) ||" << std::endl;
C.indent() << " (tIn.getX() != tOut.getX()) ||" << std::endl;
C.indent() << " (tIn.getY() != tOut.getY()) ||" << std::endl;
C.indent() << " (tIn.getZ() != tOut.getZ()) ||" << std::endl;
C.indent() << " (tIn.hasFaces() != tOut.hasFaces()) ||" << std::endl;
C.indent() << " (tIn.hasMipmaps() != tOut.hasMipmaps())) {" << std::endl;
C.indent() << " throw new RSRuntimeException(\"Dimension mismatch "
<< "between input and output parameters!\");";
C.out() << std::endl;
C.indent() << "}" << std::endl;
}
std::string FieldPackerName = EF->getName() + "_fp";
if (ERT) {
if (genCreateFieldPacker(C, ERT, FieldPackerName.c_str())) {
genPackVarOfType(C, ERT, NULL, FieldPackerName.c_str());
}
}
C.indent() << "forEach("RS_EXPORT_FOREACH_INDEX_PREFIX << EF->getName();
if (EF->hasIn())
C.out() << ", ain";
else
C.out() << ", null";
if (EF->hasOut() || EF->hasReturn())
C.out() << ", aout";
else
C.out() << ", null";
if (EF->hasUsrData())
C.out() << ", " << FieldPackerName;
else
C.out() << ", null";
C.out() << ");" << std::endl;
C.endFunction();
return;
}
void RSReflection::genTypeInstanceFromPointer(Context &C,
const RSExportType *ET) {
if (ET->getClass() == RSExportType::ExportClassPointer) {
// For pointer parameters to original forEach kernels.
const RSExportPointerType *EPT =
static_cast<const RSExportPointerType*>(ET);
genTypeInstance(C, EPT->getPointeeType());
} else {
// For handling pass-by-value kernel parameters.
genTypeInstance(C, ET);
}
}
void RSReflection::genTypeInstance(Context &C,
const RSExportType *ET) {
switch (ET->getClass()) {
case RSExportType::ExportClassPrimitive:
case RSExportType::ExportClassVector:
case RSExportType::ExportClassConstantArray: {
std::string TypeName = ET->getElementName();
if (C.addTypeNameForElement(TypeName)) {
C.indent() << RS_ELEM_PREFIX << TypeName << " = Element." << TypeName
<< "(rs);" << std::endl;
}
break;
}
case RSExportType::ExportClassRecord: {
std::string ClassName = ET->getElementName();
if (C.addTypeNameForElement(ClassName)) {
C.indent() << RS_ELEM_PREFIX << ClassName << " = " << ClassName <<
".createElement(rs);" << std::endl;
}
break;
}
default:
break;
}
}
void RSReflection::genFieldPackerInstance(Context &C,
const RSExportType *ET) {
switch (ET->getClass()) {
case RSExportType::ExportClassPrimitive:
case RSExportType::ExportClassVector:
case RSExportType::ExportClassConstantArray:
case RSExportType::ExportClassRecord: {
std::string TypeName = ET->getElementName();
C.addTypeNameForFieldPacker(TypeName);
break;
}
default:
break;
}
}
void RSReflection::genTypeCheck(Context &C,
const RSExportType *ET,
const char *VarName) {
C.indent() << "// check " << VarName << std::endl;
if (ET->getClass() == RSExportType::ExportClassPointer) {
const RSExportPointerType *EPT =
static_cast<const RSExportPointerType*>(ET);
ET = EPT->getPointeeType();
}
std::string TypeName;
switch (ET->getClass()) {
case RSExportType::ExportClassPrimitive:
case RSExportType::ExportClassVector:
case RSExportType::ExportClassRecord: {
TypeName = ET->getElementName();
break;
}
default:
break;
}
if (!TypeName.empty()) {
C.indent() << "if (!" << VarName
<< ".getType().getElement().isCompatible(" RS_ELEM_PREFIX
<< TypeName << ")) {" << std::endl;
C.indent() << " throw new RSRuntimeException(\"Type mismatch with "
<< TypeName << "!\");" << std::endl;
C.indent() << "}" << std::endl;
}
return;
}
void RSReflection::genPrimitiveTypeExportVariable(
Context &C,
const RSExportVar *EV) {
slangAssert((EV->getType()->getClass() == RSExportType::ExportClassPrimitive)
&& "Variable should be type of primitive here");
const RSExportPrimitiveType *EPT =
static_cast<const RSExportPrimitiveType*>(EV->getType());
std::string TypeName = GetTypeName(EPT);
std::string VarName = EV->getName();
genPrivateExportVariable(C, TypeName, EV->getName());
if (EV->isConst()) {
C.indent() << "public final static " << TypeName
<< " " RS_EXPORT_VAR_CONST_PREFIX << VarName << " = ";
const clang::APValue &Val = EV->getInit();
C.out() << RSReflectionBase::genInitValue(Val, EPT->getType() ==
RSExportPrimitiveType::DataTypeBoolean) << ";" << std::endl;
} else {
// set_*()
// This must remain synchronized, since multiple Dalvik threads may
// be calling setters.
C.startFunction(Context::AM_PublicSynchronized,
false,
"void",
"set_" + VarName,
1,
TypeName.c_str(), "v");
if ((EPT->getSize() < 4) || EV->isUnsigned()) {
// We create/cache a per-type FieldPacker. This allows us to reuse the
// validation logic (for catching negative inputs from Dalvik, as well
// as inputs that are too large to be represented in the unsigned type).
// Sub-integer types are also handled specially here, so that we don't
// overwrite bytes accidentally.
std::string ElemName = EPT->getElementName();
std::string FPName;
FPName = RS_FP_PREFIX + ElemName;
C.indent() << "if (" << FPName << "!= null) {"
<< std::endl;
C.incIndentLevel();
C.indent() << FPName << ".reset();" << std::endl;
C.decIndentLevel();
C.indent() << "} else {" << std::endl;
C.incIndentLevel();
C.indent() << FPName << " = new FieldPacker("
<< EPT->getSize() << ");" << std::endl;
C.decIndentLevel();
C.indent() << "}" << std::endl;
genPackVarOfType(C, EPT, "v", FPName.c_str());
C.indent() << "setVar("RS_EXPORT_VAR_INDEX_PREFIX << VarName
<< ", " << FPName << ");" << std::endl;
} else {
C.indent() << "setVar("RS_EXPORT_VAR_INDEX_PREFIX << VarName
<< ", v);" << std::endl;
}
// Dalvik update comes last, since the input may be invalid (and hence
// throw an exception).
C.indent() << RS_EXPORT_VAR_PREFIX << VarName << " = v;" << std::endl;
C.endFunction();
}
genGetExportVariable(C, TypeName, VarName);
genGetFieldID(C, VarName);
return;
}
void RSReflection::genPointerTypeExportVariable(Context &C,
const RSExportVar *EV) {
const RSExportType *ET = EV->getType();
const RSExportType *PointeeType;
slangAssert((ET->getClass() == RSExportType::ExportClassPointer) &&
"Variable should be type of pointer here");
PointeeType = static_cast<const RSExportPointerType*>(ET)->getPointeeType();
std::string TypeName = GetTypeName(ET);
std::string VarName = EV->getName();
genPrivateExportVariable(C, TypeName, VarName);
// bind_*()
C.startFunction(Context::AM_Public,
false,
"void",
"bind_" + VarName,
1,
TypeName.c_str(), "v");
C.indent() << RS_EXPORT_VAR_PREFIX << VarName << " = v;" << std::endl;
C.indent() << "if (v == null) bindAllocation(null, "RS_EXPORT_VAR_INDEX_PREFIX
<< VarName << ");" << std::endl;
if (PointeeType->getClass() == RSExportType::ExportClassRecord)
C.indent() << "else bindAllocation(v.getAllocation(), "
RS_EXPORT_VAR_INDEX_PREFIX << VarName << ");"
<< std::endl;
else
C.indent() << "else bindAllocation(v, "RS_EXPORT_VAR_INDEX_PREFIX
<< VarName << ");" << std::endl;
C.endFunction();
genGetExportVariable(C, TypeName, VarName);
return;
}
void RSReflection::genVectorTypeExportVariable(Context &C,
const RSExportVar *EV) {
slangAssert((EV->getType()->getClass() == RSExportType::ExportClassVector) &&
"Variable should be type of vector here");
std::string TypeName = GetTypeName(EV->getType());
std::string VarName = EV->getName();
genPrivateExportVariable(C, TypeName, VarName);
genSetExportVariable(C, TypeName, EV);
genGetExportVariable(C, TypeName, VarName);
genGetFieldID(C, VarName);
return;
}
void RSReflection::genMatrixTypeExportVariable(Context &C,
const RSExportVar *EV) {
slangAssert((EV->getType()->getClass() == RSExportType::ExportClassMatrix) &&
"Variable should be type of matrix here");
const RSExportType *ET = EV->getType();
std::string TypeName = GetTypeName(ET);
std::string VarName = EV->getName();
genPrivateExportVariable(C, TypeName, VarName);
// set_*()
if (!EV->isConst()) {
const char *FieldPackerName = "fp";
C.startFunction(Context::AM_PublicSynchronized,
false,
"void",
"set_" + VarName,
1,
TypeName.c_str(), "v");
C.indent() << RS_EXPORT_VAR_PREFIX << VarName << " = v;" << std::endl;
if (genCreateFieldPacker(C, ET, FieldPackerName))
genPackVarOfType(C, ET, "v", FieldPackerName);
C.indent() << "setVar("RS_EXPORT_VAR_INDEX_PREFIX << VarName << ", "
<< FieldPackerName << ");" << std::endl;
C.endFunction();
}
genGetExportVariable(C, TypeName, VarName);
genGetFieldID(C, VarName);
return;
}
void RSReflection::genConstantArrayTypeExportVariable(
Context &C,
const RSExportVar *EV) {
slangAssert((EV->getType()->getClass() ==
RSExportType::ExportClassConstantArray) &&
"Variable should be type of constant array here");
std::string TypeName = GetTypeName(EV->getType());
std::string VarName = EV->getName();
genPrivateExportVariable(C, TypeName, VarName);
genSetExportVariable(C, TypeName, EV);
genGetExportVariable(C, TypeName, VarName);
genGetFieldID(C, VarName);
return;
}
void RSReflection::genRecordTypeExportVariable(Context &C,
const RSExportVar *EV) {
slangAssert((EV->getType()->getClass() == RSExportType::ExportClassRecord) &&
"Variable should be type of struct here");
std::string TypeName = GetTypeName(EV->getType());
std::string VarName = EV->getName();
genPrivateExportVariable(C, TypeName, VarName);
genSetExportVariable(C, TypeName, EV);
genGetExportVariable(C, TypeName, VarName);
genGetFieldID(C, VarName);
return;
}
void RSReflection::genPrivateExportVariable(Context &C,
const std::string &TypeName,
const std::string &VarName) {
C.indent() << "private " << TypeName << " "RS_EXPORT_VAR_PREFIX
<< VarName << ";" << std::endl;
return;
}
void RSReflection::genSetExportVariable(Context &C,
const std::string &TypeName,
const RSExportVar *EV) {
if (!EV->isConst()) {
const char *FieldPackerName = "fp";
std::string VarName = EV->getName();
const RSExportType *ET = EV->getType();
C.startFunction(Context::AM_PublicSynchronized,
false,
"void",
"set_" + VarName,
1,
TypeName.c_str(), "v");
C.indent() << RS_EXPORT_VAR_PREFIX << VarName << " = v;" << std::endl;
if (genCreateFieldPacker(C, ET, FieldPackerName))
genPackVarOfType(C, ET, "v", FieldPackerName);
if (mRSContext->getTargetAPI() < SLANG_JB_TARGET_API) {
// Legacy apps must use the old setVar() without Element/dim components.
C.indent() << "setVar("RS_EXPORT_VAR_INDEX_PREFIX << VarName
<< ", " << FieldPackerName << ");" << std::endl;
} else {
// We only have support for one-dimensional array reflection today,
// but the entry point (i.e. setVar()) takes an array of dimensions.
C.indent() << "int []__dimArr = new int[1];" << std::endl;
C.indent() << "__dimArr[0] = " << ET->getSize() << ";" << std::endl;
C.indent() << "setVar("RS_EXPORT_VAR_INDEX_PREFIX << VarName << ", "
<< FieldPackerName << ", " RS_ELEM_PREFIX
<< ET->getElementName() << ", __dimArr);" << std::endl;
}
C.endFunction();
}
return;
}
void RSReflection::genGetExportVariable(Context &C,
const std::string &TypeName,
const std::string &VarName) {
C.startFunction(Context::AM_Public,
false,
TypeName.c_str(),
"get_" + VarName,
0);
C.indent() << "return "RS_EXPORT_VAR_PREFIX << VarName << ";" << std::endl;
C.endFunction();
}
void RSReflection::genGetFieldID(Context &C, const std::string &VarName) {
// We only generate getFieldID_*() for non-Pointer (bind) types.
if (mRSContext->getTargetAPI() >= SLANG_JB_MR1_TARGET_API) {
C.startFunction(Context::AM_Public,
false,
"Script.FieldID",
"getFieldID_" + VarName,
0);
C.indent() << "return createFieldID(" << RS_EXPORT_VAR_INDEX_PREFIX
<< VarName << ", null);" << std::endl;
C.endFunction();
}
}
/******************* Methods to generate script class /end *******************/
bool RSReflection::genCreateFieldPacker(Context &C,
const RSExportType *ET,
const char *FieldPackerName) {
size_t AllocSize = RSExportType::GetTypeAllocSize(ET);
if (AllocSize > 0)
C.indent() << "FieldPacker " << FieldPackerName << " = new FieldPacker("
<< AllocSize << ");" << std::endl;
else
return false;
return true;
}
void RSReflection::genPackVarOfType(Context &C,
const RSExportType *ET,
const char *VarName,
const char *FieldPackerName) {
switch (ET->getClass()) {
case RSExportType::ExportClassPrimitive:
case RSExportType::ExportClassVector: {
C.indent() << FieldPackerName << "."
<< GetPackerAPIName(
static_cast<const RSExportPrimitiveType*>(ET))
<< "(" << VarName << ");" << std::endl;
break;
}
case RSExportType::ExportClassPointer: {
// Must reflect as type Allocation in Java
const RSExportType *PointeeType =
static_cast<const RSExportPointerType*>(ET)->getPointeeType();
if (PointeeType->getClass() != RSExportType::ExportClassRecord)
C.indent() << FieldPackerName << ".addI32(" << VarName
<< ".getPtr());" << std::endl;
else
C.indent() << FieldPackerName << ".addI32(" << VarName
<< ".getAllocation().getPtr());" << std::endl;
break;
}
case RSExportType::ExportClassMatrix: {
C.indent() << FieldPackerName << ".addMatrix(" << VarName << ");"
<< std::endl;
break;
}
case RSExportType::ExportClassConstantArray: {
const RSExportConstantArrayType *ECAT =
static_cast<const RSExportConstantArrayType *>(ET);
// TODO(zonr): more elegant way. Currently, we obtain the unique index
// variable (this method involves recursive call which means
// we may have more than one level loop, therefore we can't
// always use the same index variable name here) name given
// in the for-loop from counting the '.' in @VarName.
unsigned Level = 0;
size_t LastDotPos = 0;
std::string ElementVarName(VarName);
while (LastDotPos != std::string::npos) {
LastDotPos = ElementVarName.find_first_of('.', LastDotPos + 1);
Level++;
}
std::string IndexVarName("ct");
IndexVarName.append(llvm::utostr_32(Level));
C.indent() << "for (int " << IndexVarName << " = 0; " <<
IndexVarName << " < " << ECAT->getSize() << "; " <<
IndexVarName << "++)";
C.startBlock();
ElementVarName.append("[" + IndexVarName + "]");
genPackVarOfType(C, ECAT->getElementType(), ElementVarName.c_str(),
FieldPackerName);
C.endBlock();
break;
}
case RSExportType::ExportClassRecord: {
const RSExportRecordType *ERT =
static_cast<const RSExportRecordType*>(ET);
// Relative pos from now on in field packer
unsigned Pos = 0;
for (RSExportRecordType::const_field_iterator I = ERT->fields_begin(),
E = ERT->fields_end();
I != E;
I++) {
const RSExportRecordType::Field *F = *I;
std::string FieldName;
size_t FieldOffset = F->getOffsetInParent();
size_t FieldStoreSize = RSExportType::GetTypeStoreSize(F->getType());
size_t FieldAllocSize = RSExportType::GetTypeAllocSize(F->getType());
if (VarName != NULL)
FieldName = VarName + ("." + F->getName());
else
FieldName = F->getName();
if (FieldOffset > Pos)
C.indent() << FieldPackerName << ".skip("
<< (FieldOffset - Pos) << ");" << std::endl;
genPackVarOfType(C, F->getType(), FieldName.c_str(), FieldPackerName);
// There is padding in the field type
if (FieldAllocSize > FieldStoreSize)
C.indent() << FieldPackerName << ".skip("
<< (FieldAllocSize - FieldStoreSize)
<< ");" << std::endl;
Pos = FieldOffset + FieldAllocSize;
}
// There maybe some padding after the struct
if (RSExportType::GetTypeAllocSize(ERT) > Pos)
C.indent() << FieldPackerName << ".skip("
<< RSExportType::GetTypeAllocSize(ERT) - Pos << ");"
<< std::endl;
break;
}
default: {
slangAssert(false && "Unknown class of type");
}
}
return;
}
void RSReflection::genAllocateVarOfType(Context &C,
const RSExportType *T,
const std::string &VarName) {
switch (T->getClass()) {
case RSExportType::ExportClassPrimitive: {
// Primitive type like int in Java has its own storage once it's declared.
//
// FIXME: Should we allocate storage for RS object?
// if (static_cast<const RSExportPrimitiveType *>(T)->isRSObjectType())
// C.indent() << VarName << " = new " << GetTypeName(T) << "();"
// << std::endl;
break;
}
case RSExportType::ExportClassPointer: {
// Pointer type is an instance of Allocation or a TypeClass whose value is
// expected to be assigned by programmer later in Java program. Therefore
// we don't reflect things like [VarName] = new Allocation();
C.indent() << VarName << " = null;" << std::endl;
break;
}
case RSExportType::ExportClassConstantArray: {
const RSExportConstantArrayType *ECAT =
static_cast<const RSExportConstantArrayType *>(T);
const RSExportType *ElementType = ECAT->getElementType();
C.indent() << VarName << " = new " << GetTypeName(ElementType)
<< "[" << ECAT->getSize() << "];" << std::endl;
// Primitive type element doesn't need allocation code.
if (ElementType->getClass() != RSExportType::ExportClassPrimitive) {
C.indent() << "for (int $ct = 0; $ct < " << ECAT->getSize() << "; "
"$ct++)";
C.startBlock();
std::string ElementVarName(VarName);
ElementVarName.append("[$ct]");
genAllocateVarOfType(C, ElementType, ElementVarName);
C.endBlock();
}
break;
}
case RSExportType::ExportClassVector:
case RSExportType::ExportClassMatrix:
case RSExportType::ExportClassRecord: {
C.indent() << VarName << " = new " << GetTypeName(T) << "();"
<< std::endl;
break;
}
}
return;
}
void RSReflection::genNewItemBufferIfNull(Context &C,
const char *Index) {
C.indent() << "if (" RS_TYPE_ITEM_BUFFER_NAME " == null) "
RS_TYPE_ITEM_BUFFER_NAME " = "
"new " RS_TYPE_ITEM_CLASS_NAME
"[getType().getX() /* count */];"
<< std::endl;
if (Index != NULL)
C.indent() << "if ("RS_TYPE_ITEM_BUFFER_NAME"[" << Index << "] == null) "
RS_TYPE_ITEM_BUFFER_NAME"[" << Index << "] = "
"new "RS_TYPE_ITEM_CLASS_NAME"();" << std::endl;
return;
}
void RSReflection::genNewItemBufferPackerIfNull(Context &C) {
C.indent() << "if (" RS_TYPE_ITEM_BUFFER_PACKER_NAME " == null) "
RS_TYPE_ITEM_BUFFER_PACKER_NAME " = "
"new FieldPacker(" RS_TYPE_ITEM_CLASS_NAME
".sizeof * getType().getX()/* count */"
");" << std::endl;
return;
}
/********************** Methods to generate type class **********************/
bool RSReflection::genTypeClass(Context &C,
const RSExportRecordType *ERT,
std::string &ErrorMsg) {
std::string ClassName = ERT->getElementName();
if (!C.startClass(Context::AM_Public,
false,
ClassName,
RS_TYPE_CLASS_SUPER_CLASS_NAME,
ErrorMsg))
return false;
mGeneratedFileNames->push_back(ClassName);
genTypeItemClass(C, ERT);
// Declare item buffer and item buffer packer
C.indent() << "private "RS_TYPE_ITEM_CLASS_NAME" "RS_TYPE_ITEM_BUFFER_NAME"[]"
";" << std::endl;
C.indent() << "private FieldPacker "RS_TYPE_ITEM_BUFFER_PACKER_NAME";"
<< std::endl;
C.indent() << "private static java.lang.ref.WeakReference<Element> "
RS_TYPE_ELEMENT_REF_NAME
" = new java.lang.ref.WeakReference<Element>(null);" << std::endl;
genTypeClassConstructor(C, ERT);
genTypeClassCopyToArrayLocal(C, ERT);
genTypeClassCopyToArray(C, ERT);
genTypeClassItemSetter(C, ERT);
genTypeClassItemGetter(C, ERT);
genTypeClassComponentSetter(C, ERT);
genTypeClassComponentGetter(C, ERT);
genTypeClassCopyAll(C, ERT);
genTypeClassResize(C);
C.endClass();
C.resetFieldIndex();
C.clearFieldIndexMap();
return true;
}
void RSReflection::genTypeItemClass(Context &C,
const RSExportRecordType *ERT) {
C.indent() << "static public class "RS_TYPE_ITEM_CLASS_NAME;
C.startBlock();
C.indent() << "public static final int sizeof = "
<< RSExportType::GetTypeAllocSize(ERT) << ";" << std::endl;
// Member elements
C.out() << std::endl;
for (RSExportRecordType::const_field_iterator FI = ERT->fields_begin(),
FE = ERT->fields_end();
FI != FE;
FI++) {
C.indent() << GetTypeName((*FI)->getType()) << " " << (*FI)->getName()
<< ";" << std::endl;
}
// Constructor
C.out() << std::endl;
C.indent() << RS_TYPE_ITEM_CLASS_NAME"()";
C.startBlock();
for (RSExportRecordType::const_field_iterator FI = ERT->fields_begin(),
FE = ERT->fields_end();
FI != FE;
FI++) {
const RSExportRecordType::Field *F = *FI;
genAllocateVarOfType(C, F->getType(), F->getName());
}
// end Constructor
C.endBlock();
// end Item class
C.endBlock();
return;
}
void RSReflection::genTypeClassConstructor(Context &C,
const RSExportRecordType *ERT) {
const char *RenderScriptVar = "rs";
C.startFunction(Context::AM_Public,
true,
"Element",
"createElement",
1,
"RenderScript", RenderScriptVar);
// TODO(all): Fix weak-refs + multi-context issue.
// C.indent() << "Element e = " << RS_TYPE_ELEMENT_REF_NAME
// << ".get();" << std::endl;
// C.indent() << "if (e != null) return e;" << std::endl;
genBuildElement(C, "eb", ERT, RenderScriptVar, /* IsInline = */true);
C.indent() << "return eb.create();" << std::endl;
// C.indent() << "e = eb.create();" << std::endl;
// C.indent() << RS_TYPE_ELEMENT_REF_NAME
// << " = new java.lang.ref.WeakReference<Element>(e);"
// << std::endl;
// C.indent() << "return e;" << std::endl;
C.endFunction();
// private with element
C.startFunction(Context::AM_Private,
false,
NULL,
C.getClassName(),
1,
"RenderScript", RenderScriptVar);
C.indent() << RS_TYPE_ITEM_BUFFER_NAME" = null;" << std::endl;
C.indent() << RS_TYPE_ITEM_BUFFER_PACKER_NAME" = null;" << std::endl;
C.indent() << "mElement = createElement(" << RenderScriptVar << ");"
<< std::endl;
C.endFunction();
// 1D without usage
C.startFunction(Context::AM_Public,
false,
NULL,
C.getClassName(),
2,
"RenderScript", RenderScriptVar,
"int", "count");
C.indent() << RS_TYPE_ITEM_BUFFER_NAME" = null;" << std::endl;
C.indent() << RS_TYPE_ITEM_BUFFER_PACKER_NAME" = null;" << std::endl;
C.indent() << "mElement = createElement(" << RenderScriptVar << ");"
<< std::endl;
// Call init() in super class
C.indent() << "init(" << RenderScriptVar << ", count);" << std::endl;
C.endFunction();
// 1D with usage
C.startFunction(Context::AM_Public,
false,
NULL,
C.getClassName(),
3,
"RenderScript", RenderScriptVar,
"int", "count",
"int", "usages");
C.indent() << RS_TYPE_ITEM_BUFFER_NAME" = null;" << std::endl;
C.indent() << RS_TYPE_ITEM_BUFFER_PACKER_NAME" = null;" << std::endl;
C.indent() << "mElement = createElement(" << RenderScriptVar << ");"
<< std::endl;
// Call init() in super class
C.indent() << "init(" << RenderScriptVar << ", count, usages);" << std::endl;
C.endFunction();
// create1D with usage
C.startFunction(Context::AM_Public,
true,
C.getClassName().c_str(),
"create1D",
3,
"RenderScript", RenderScriptVar,
"int", "dimX",
"int", "usages");
C.indent() << C.getClassName() << " obj = new " << C.getClassName() << "("
<< RenderScriptVar << ");" << std::endl;
C.indent() << "obj.mAllocation = Allocation.createSized("
"rs, obj.mElement, dimX, usages);" << std::endl;
C.indent() << "return obj;" << std::endl;
C.endFunction();
// create1D without usage
C.startFunction(Context::AM_Public,
true,
C.getClassName().c_str(),
"create1D",
2,
"RenderScript", RenderScriptVar,
"int", "dimX");
C.indent() << "return create1D(" << RenderScriptVar
<< ", dimX, Allocation.USAGE_SCRIPT);" << std::endl;
C.endFunction();
// create2D without usage
C.startFunction(Context::AM_Public,
true,
C.getClassName().c_str(),
"create2D",
3,
"RenderScript", RenderScriptVar,
"int", "dimX",
"int", "dimY");
C.indent() << "return create2D(" << RenderScriptVar
<< ", dimX, dimY, Allocation.USAGE_SCRIPT);" << std::endl;
C.endFunction();
// create2D with usage
C.startFunction(Context::AM_Public,
true,
C.getClassName().c_str(),
"create2D",
4,
"RenderScript", RenderScriptVar,
"int", "dimX",
"int", "dimY",
"int", "usages");
C.indent() << C.getClassName() << " obj = new " << C.getClassName() << "("
<< RenderScriptVar << ");" << std::endl;
C.indent() << "Type.Builder b = new Type.Builder(rs, obj.mElement);"
<< std::endl;
C.indent() << "b.setX(dimX);" << std::endl;
C.indent() << "b.setY(dimY);" << std::endl;
C.indent() << "Type t = b.create();" << std::endl;
C.indent() << "obj.mAllocation = Allocation.createTyped(rs, t, usages);"
<< std::endl;
C.indent() << "return obj;" << std::endl;
C.endFunction();
// createTypeBuilder
C.startFunction(Context::AM_Public,
true,
"Type.Builder",
"createTypeBuilder",
1,
"RenderScript", RenderScriptVar);
C.indent() << "Element e = createElement(" << RenderScriptVar << ");"
<< std::endl;
C.indent() << "return new Type.Builder(rs, e);" << std::endl;
C.endFunction();
// createCustom with usage
C.startFunction(Context::AM_Public,
true,
C.getClassName().c_str(),
"createCustom",
3,
"RenderScript", RenderScriptVar,
"Type.Builder", "tb",
"int", "usages");
C.indent() << C.getClassName() << " obj = new " << C.getClassName() << "("
<< RenderScriptVar << ");" << std::endl;
C.indent() << "Type t = tb.create();" << std::endl;
C.indent() << "if (t.getElement() != obj.mElement) {" << std::endl;
C.indent() << " throw new RSIllegalArgumentException("
"\"Type.Builder did not match expected element type.\");"
<< std::endl;
C.indent() << "}" << std::endl;
C.indent() << "obj.mAllocation = Allocation.createTyped(rs, t, usages);"
<< std::endl;
C.indent() << "return obj;" << std::endl;
C.endFunction();
}
void RSReflection::genTypeClassCopyToArray(Context &C,
const RSExportRecordType *ERT) {
C.startFunction(Context::AM_Private,
false,
"void",
"copyToArray",
2,
RS_TYPE_ITEM_CLASS_NAME, "i",
"int", "index");
genNewItemBufferPackerIfNull(C);
C.indent() << RS_TYPE_ITEM_BUFFER_PACKER_NAME
".reset(index * "RS_TYPE_ITEM_CLASS_NAME".sizeof);"
<< std::endl;
C.indent() << "copyToArrayLocal(i, " RS_TYPE_ITEM_BUFFER_PACKER_NAME
");" << std::endl;
C.endFunction();
return;
}
void RSReflection::genTypeClassCopyToArrayLocal(Context &C,
const RSExportRecordType *ERT) {
C.startFunction(Context::AM_Private,
false,
"void",
"copyToArrayLocal",
2,
RS_TYPE_ITEM_CLASS_NAME, "i",
"FieldPacker", "fp");
genPackVarOfType(C, ERT, "i", "fp");
C.endFunction();
return;
}
void RSReflection::genTypeClassItemSetter(Context &C,
const RSExportRecordType *ERT) {
C.startFunction(Context::AM_PublicSynchronized,
false,
"void",
"set",
3,
RS_TYPE_ITEM_CLASS_NAME, "i",
"int", "index",
"boolean", "copyNow");
genNewItemBufferIfNull(C, NULL);
C.indent() << RS_TYPE_ITEM_BUFFER_NAME"[index] = i;" << std::endl;
C.indent() << "if (copyNow) ";
C.startBlock();
C.indent() << "copyToArray(i, index);" << std::endl;
C.indent() << "FieldPacker fp = new FieldPacker(" RS_TYPE_ITEM_CLASS_NAME
".sizeof);" << std::endl;
C.indent() << "copyToArrayLocal(i, fp);" << std::endl;
C.indent() << "mAllocation.setFromFieldPacker(index, fp);" << std::endl;
// End of if (copyNow)
C.endBlock();
C.endFunction();
return;
}
void RSReflection::genTypeClassItemGetter(Context &C,
const RSExportRecordType *ERT) {
C.startFunction(Context::AM_PublicSynchronized,
false,
RS_TYPE_ITEM_CLASS_NAME,
"get",
1,
"int", "index");
C.indent() << "if ("RS_TYPE_ITEM_BUFFER_NAME" == null) return null;"
<< std::endl;
C.indent() << "return "RS_TYPE_ITEM_BUFFER_NAME"[index];" << std::endl;
C.endFunction();
return;
}
void RSReflection::genTypeClassComponentSetter(Context &C,
const RSExportRecordType *ERT) {
for (RSExportRecordType::const_field_iterator FI = ERT->fields_begin(),
FE = ERT->fields_end();
FI != FE;
FI++) {
const RSExportRecordType::Field *F = *FI;
size_t FieldOffset = F->getOffsetInParent();
size_t FieldStoreSize = RSExportType::GetTypeStoreSize(F->getType());
unsigned FieldIndex = C.getFieldIndex(F);
C.startFunction(Context::AM_PublicSynchronized,
false,
"void",
"set_" + F->getName(), 3,
"int", "index",
GetTypeName(F->getType()).c_str(), "v",
"boolean", "copyNow");
genNewItemBufferPackerIfNull(C);
genNewItemBufferIfNull(C, "index");
C.indent() << RS_TYPE_ITEM_BUFFER_NAME"[index]." << F->getName()
<< " = v;" << std::endl;
C.indent() << "if (copyNow) ";
C.startBlock();
if (FieldOffset > 0)
C.indent() << RS_TYPE_ITEM_BUFFER_PACKER_NAME
".reset(index * "RS_TYPE_ITEM_CLASS_NAME".sizeof + "
<< FieldOffset << ");" << std::endl;
else
C.indent() << RS_TYPE_ITEM_BUFFER_PACKER_NAME
".reset(index * "RS_TYPE_ITEM_CLASS_NAME".sizeof);"
<< std::endl;
genPackVarOfType(C, F->getType(), "v", RS_TYPE_ITEM_BUFFER_PACKER_NAME);
C.indent() << "FieldPacker fp = new FieldPacker(" << FieldStoreSize << ");"
<< std::endl;
genPackVarOfType(C, F->getType(), "v", "fp");
C.indent() << "mAllocation.setFromFieldPacker(index, " << FieldIndex
<< ", fp);"
<< std::endl;
// End of if (copyNow)
C.endBlock();
C.endFunction();
}
return;
}
void RSReflection::genTypeClassComponentGetter(Context &C,
const RSExportRecordType *ERT) {
for (RSExportRecordType::const_field_iterator FI = ERT->fields_begin(),
FE = ERT->fields_end();
FI != FE;
FI++) {
const RSExportRecordType::Field *F = *FI;
C.startFunction(Context::AM_PublicSynchronized,
false,
GetTypeName(F->getType()).c_str(),
"get_" + F->getName(),
1,
"int", "index");
C.indent() << "if ("RS_TYPE_ITEM_BUFFER_NAME" == null) return "
<< GetTypeNullValue(F->getType()) << ";" << std::endl;
C.indent() << "return "RS_TYPE_ITEM_BUFFER_NAME"[index]." << F->getName()
<< ";" << std::endl;
C.endFunction();
}
return;
}
void RSReflection::genTypeClassCopyAll(Context &C,
const RSExportRecordType *ERT) {
C.startFunction(Context::AM_PublicSynchronized, false, "void", "copyAll", 0);
C.indent() << "for (int ct = 0; ct < "RS_TYPE_ITEM_BUFFER_NAME".length; ct++)"
" copyToArray("RS_TYPE_ITEM_BUFFER_NAME"[ct], ct);"
<< std::endl;
C.indent() << "mAllocation.setFromFieldPacker(0, "
RS_TYPE_ITEM_BUFFER_PACKER_NAME");"
<< std::endl;
C.endFunction();
return;
}
void RSReflection::genTypeClassResize(Context &C) {
C.startFunction(Context::AM_PublicSynchronized,
false,
"void",
"resize",
1,
"int", "newSize");
C.indent() << "if (mItemArray != null) ";
C.startBlock();
C.indent() << "int oldSize = mItemArray.length;" << std::endl;
C.indent() << "int copySize = Math.min(oldSize, newSize);" << std::endl;
C.indent() << "if (newSize == oldSize) return;" << std::endl;
C.indent() << "Item ni[] = new Item[newSize];" << std::endl;
C.indent() << "System.arraycopy(mItemArray, 0, ni, 0, copySize);"
<< std::endl;
C.indent() << "mItemArray = ni;" << std::endl;
C.endBlock();
C.indent() << "mAllocation.resize(newSize);" << std::endl;
C.indent() << "if (" RS_TYPE_ITEM_BUFFER_PACKER_NAME " != null) "
RS_TYPE_ITEM_BUFFER_PACKER_NAME " = "
"new FieldPacker(" RS_TYPE_ITEM_CLASS_NAME
".sizeof * getType().getX()/* count */"
");" << std::endl;
C.endFunction();
return;
}
/******************** Methods to generate type class /end ********************/
/********** Methods to create Element in Java of given record type ***********/
void RSReflection::genBuildElement(Context &C,
const char *ElementBuilderName,
const RSExportRecordType *ERT,
const char *RenderScriptVar,
bool IsInline) {
C.indent() << "Element.Builder " << ElementBuilderName << " = "
"new Element.Builder(" << RenderScriptVar << ");" << std::endl;
// eb.add(...)
genAddElementToElementBuilder(C,
ERT,
"",
ElementBuilderName,
RenderScriptVar,
/* ArraySize = */0);
if (!IsInline)
C.indent() << "return " << ElementBuilderName << ".create();" << std::endl;
return;
}
#define EB_ADD(x) do { \
C.indent() << ElementBuilderName \
<< ".add(" << x << ", \"" << VarName << "\""; \
if (ArraySize > 0) \
C.out() << ", " << ArraySize; \
C.out() << ");" << std::endl; \
C.incFieldIndex(); \
} while (false)
void RSReflection::genAddElementToElementBuilder(Context &C,
const RSExportType *ET,
const std::string &VarName,
const char *ElementBuilderName,
const char *RenderScriptVar,
unsigned ArraySize) {
std::string ElementConstruct = GetBuiltinElementConstruct(ET);
if (ElementConstruct != "") {
EB_ADD(ElementConstruct << "(" << RenderScriptVar << ")");
} else {
if ((ET->getClass() == RSExportType::ExportClassPrimitive) ||
(ET->getClass() == RSExportType::ExportClassVector)) {
const RSExportPrimitiveType *EPT =
static_cast<const RSExportPrimitiveType*>(ET);
const char *DataTypeName =
RSExportPrimitiveType::getRSReflectionType(EPT)->rs_type;
int Size = (ET->getClass() == RSExportType::ExportClassVector) ?
static_cast<const RSExportVectorType*>(ET)->getNumElement() :
1;
if (EPT->getClass() == RSExportType::ExportClassPrimitive) {
// Element.createUser()
EB_ADD("Element.createUser(" << RenderScriptVar
<< ", Element.DataType."
<< DataTypeName << ")");
} else {
slangAssert((ET->getClass() == RSExportType::ExportClassVector) &&
"Unexpected type.");
EB_ADD("Element.createVector(" << RenderScriptVar
<< ", Element.DataType."
<< DataTypeName << ", "
<< Size << ")");
}
#ifndef NDEBUG
} else if (ET->getClass() == RSExportType::ExportClassPointer) {
// Pointer type variable should be resolved in
// GetBuiltinElementConstruct()
slangAssert(false && "??");
} else if (ET->getClass() == RSExportType::ExportClassMatrix) {
// Matrix type variable should be resolved
// in GetBuiltinElementConstruct()
slangAssert(false && "??");
#endif
} else if (ET->getClass() == RSExportType::ExportClassConstantArray) {
const RSExportConstantArrayType *ECAT =
static_cast<const RSExportConstantArrayType *>(ET);
const RSExportType *ElementType = ECAT->getElementType();
if (ElementType->getClass() != RSExportType::ExportClassRecord) {
genAddElementToElementBuilder(C,
ECAT->getElementType(),
VarName,
ElementBuilderName,
RenderScriptVar,
ECAT->getSize());
} else {
std::string NewElementBuilderName(ElementBuilderName);
NewElementBuilderName.append(1, '_');
genBuildElement(C,
NewElementBuilderName.c_str(),
static_cast<const RSExportRecordType*>(ElementType),
RenderScriptVar,
/* IsInline = */true);
ArraySize = ECAT->getSize();
EB_ADD(NewElementBuilderName << ".create()");
}
} else if (ET->getClass() == RSExportType::ExportClassRecord) {
// Simalar to case of RSExportType::ExportClassRecord in genPackVarOfType.
//
// TODO(zonr): Generalize these two function such that there's no
// duplicated codes.
const RSExportRecordType *ERT =
static_cast<const RSExportRecordType*>(ET);
int Pos = 0; // relative pos from now on
for (RSExportRecordType::const_field_iterator I = ERT->fields_begin(),
E = ERT->fields_end();
I != E;
I++) {
const RSExportRecordType::Field *F = *I;
std::string FieldName;
int FieldOffset = F->getOffsetInParent();
int FieldStoreSize = RSExportType::GetTypeStoreSize(F->getType());
int FieldAllocSize = RSExportType::GetTypeAllocSize(F->getType());
if (!VarName.empty())
FieldName = VarName + "." + F->getName();
else
FieldName = F->getName();
// Alignment
genAddPaddingToElementBuiler(C,
(FieldOffset - Pos),
ElementBuilderName,
RenderScriptVar);
// eb.add(...)
C.addFieldIndexMapping(F);
if (F->getType()->getClass() != RSExportType::ExportClassRecord) {
genAddElementToElementBuilder(C,
F->getType(),
FieldName,
ElementBuilderName,
RenderScriptVar,
0);
} else {
std::string NewElementBuilderName(ElementBuilderName);
NewElementBuilderName.append(1, '_');
genBuildElement(C,
NewElementBuilderName.c_str(),
static_cast<const RSExportRecordType*>(F->getType()),
RenderScriptVar,
/* IsInline = */true);
const std::string &VarName = FieldName; // Hack for EB_ADD macro
EB_ADD(NewElementBuilderName << ".create()");
}
if (mRSContext->getTargetAPI() < SLANG_ICS_TARGET_API) {
// There is padding within the field type. This is only necessary
// for HC-targeted APIs.
genAddPaddingToElementBuiler(C,
(FieldAllocSize - FieldStoreSize),
ElementBuilderName,
RenderScriptVar);
}
Pos = FieldOffset + FieldAllocSize;
}
// There maybe some padding after the struct
size_t RecordAllocSize = RSExportType::GetTypeAllocSize(ERT);
genAddPaddingToElementBuiler(C,
RecordAllocSize - Pos,
ElementBuilderName,
RenderScriptVar);
} else {
slangAssert(false && "Unknown class of type");
}
}
}
void RSReflection::genAddPaddingToElementBuiler(Context &C,
int PaddingSize,
const char *ElementBuilderName,
const char *RenderScriptVar) {
unsigned ArraySize = 0; // Hack the EB_ADD macro
while (PaddingSize > 0) {
const std::string &VarName = C.createPaddingField();
if (PaddingSize >= 4) {
EB_ADD("Element.U32(" << RenderScriptVar << ")");
PaddingSize -= 4;
} else if (PaddingSize >= 2) {
EB_ADD("Element.U16(" << RenderScriptVar << ")");
PaddingSize -= 2;
} else if (PaddingSize >= 1) {
EB_ADD("Element.U8(" << RenderScriptVar << ")");
PaddingSize -= 1;
}
}
return;
}
#undef EB_ADD
/******** Methods to create Element in Java of given record type /end ********/
bool RSReflection::reflect(const std::string &OutputPathBase,
const std::string &OutputPackageName,
const std::string &RSPackageName,
const std::string &InputFileName,
const std::string &OutputBCFileName) {
Context *C = NULL;
std::string ResourceId = "";
std::string PaddingPrefix = "";
if (mRSContext->getTargetAPI() < SLANG_ICS_TARGET_API) {
PaddingPrefix = "#padding_";
} else {
PaddingPrefix = "#rs_padding_";
}
if (!GetClassNameFromFileName(OutputBCFileName, ResourceId))
return false;
if (ResourceId.empty())
ResourceId = "<Resource ID>";
if (OutputPackageName.empty() || OutputPackageName == "-")
C = new Context(OutputPathBase, InputFileName, "<Package Name>",
RSPackageName, ResourceId, PaddingPrefix, true);
else
C = new Context(OutputPathBase, InputFileName, OutputPackageName,
RSPackageName, ResourceId, PaddingPrefix, false);
if (C != NULL) {
std::string ErrorMsg, ScriptClassName;
// class ScriptC_<ScriptName>
if (!GetClassNameFromFileName(InputFileName, ScriptClassName))
return false;
if (ScriptClassName.empty())
ScriptClassName = "<Input Script Name>";
ScriptClassName.insert(0, RS_SCRIPT_CLASS_NAME_PREFIX);
if (mRSContext->getLicenseNote() != NULL) {
C->setLicenseNote(*(mRSContext->getLicenseNote()));
}
if (!genScriptClass(*C, ScriptClassName, ErrorMsg)) {
std::cerr << "Failed to generate class " << ScriptClassName << " ("
<< ErrorMsg << ")" << std::endl;
return false;
}
mGeneratedFileNames->push_back(ScriptClassName);
// class ScriptField_<TypeName>
for (RSContext::const_export_type_iterator TI =
mRSContext->export_types_begin(),
TE = mRSContext->export_types_end();
TI != TE;
TI++) {
const RSExportType *ET = TI->getValue();
if (ET->getClass() == RSExportType::ExportClassRecord) {
const RSExportRecordType *ERT =
static_cast<const RSExportRecordType*>(ET);
if (!ERT->isArtificial() && !genTypeClass(*C, ERT, ErrorMsg)) {
std::cerr << "Failed to generate type class for struct '"
<< ERT->getName() << "' (" << ErrorMsg << ")" << std::endl;
return false;
}
}
}
}
return true;
}
/************************** RSReflection::Context **************************/
const char *const RSReflection::Context::ApacheLicenseNote =
"/*\n"
" * Copyright (C) 2011-2012 The Android Open Source Project\n"
" *\n"
" * Licensed under the Apache License, Version 2.0 (the \"License\");\n"
" * you may not use this file except in compliance with the License.\n"
" * You may obtain a copy of the License at\n"
" *\n"
" * http://www.apache.org/licenses/LICENSE-2.0\n"
" *\n"
" * Unless required by applicable law or agreed to in writing, software\n"
" * distributed under the License is distributed on an \"AS IS\" BASIS,\n"
" * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or "
"implied.\n"
" * See the License for the specific language governing permissions and\n"
" * limitations under the License.\n"
" */\n"
"\n";
bool RSReflection::Context::openClassFile(const std::string &ClassName,
std::string &ErrorMsg) {
if (!mUseStdout) {
mOF.clear();
std::string Path =
RSSlangReflectUtils::ComputePackagedPath(mOutputPathBase.c_str(),
mPackageName.c_str());
if (!SlangUtils::CreateDirectoryWithParents(Path, &ErrorMsg))
return false;
std::string ClassFile = Path + OS_PATH_SEPARATOR_STR + ClassName + ".java";
mOF.open(ClassFile.c_str());
if (!mOF.good()) {
ErrorMsg = "failed to open file '" + ClassFile + "' for write";
return false;
}
}
return true;
}
const char *RSReflection::Context::AccessModifierStr(AccessModifier AM) {
switch (AM) {
case AM_Public: return "public"; break;
case AM_Protected: return "protected"; break;
case AM_Private: return "private"; break;
case AM_PublicSynchronized: return "public synchronized"; break;
default: return ""; break;
}
}
bool RSReflection::Context::startClass(AccessModifier AM,
bool IsStatic,
const std::string &ClassName,
const char *SuperClassName,
std::string &ErrorMsg) {
if (mVerbose)
std::cout << "Generating " << ClassName << ".java ..." << std::endl;
// Open file for class
if (!openClassFile(ClassName, ErrorMsg))
return false;
// License
out() << mLicenseNote;
// Notice of generated file
out() << "/*" << std::endl;
out() << " * This file is auto-generated. DO NOT MODIFY!" << std::endl;
out() << " * The source Renderscript file: " << mInputRSFile << std::endl;
out() << " */" << std::endl;
// Package
if (!mPackageName.empty())
out() << "package " << mPackageName << ";" << std::endl;
out() << std::endl;
// Imports
out() << "import " << mRSPackageName << ".*;" << std::endl;
out() << "import android.content.res.Resources;" << std::endl;
out() << std::endl;
// All reflected classes should be annotated as hidden, so that they won't
// be exposed in SDK.
out() << "/**" << std::endl;
out() << " * @hide" << std::endl;
out() << " */" << std::endl;
out() << AccessModifierStr(AM) << ((IsStatic) ? " static" : "") << " class "
<< ClassName;
if (SuperClassName != NULL)
out() << " extends " << SuperClassName;
startBlock();
mClassName = ClassName;
return true;
}
void RSReflection::Context::endClass() {
endBlock();
if (!mUseStdout)
mOF.close();
clear();
return;
}
void RSReflection::Context::startBlock(bool ShouldIndent) {
if (ShouldIndent)
indent() << "{" << std::endl;
else
out() << " {" << std::endl;
incIndentLevel();
return;
}
void RSReflection::Context::endBlock() {
decIndentLevel();
indent() << "}" << std::endl << std::endl;
return;
}
void RSReflection::Context::startTypeClass(const std::string &ClassName) {
indent() << "public static class " << ClassName;
startBlock();
return;
}
void RSReflection::Context::endTypeClass() {
endBlock();
return;
}
void RSReflection::Context::startFunction(AccessModifier AM,
bool IsStatic,
const char *ReturnType,
const std::string &FunctionName,
int Argc, ...) {
ArgTy Args;
va_list vl;
va_start(vl, Argc);
for (int i = 0; i < Argc; i++) {
const char *ArgType = va_arg(vl, const char*);
const char *ArgName = va_arg(vl, const char*);
Args.push_back(std::make_pair(ArgType, ArgName));
}
va_end(vl);
startFunction(AM, IsStatic, ReturnType, FunctionName, Args);
return;
}
void RSReflection::Context::startFunction(AccessModifier AM,
bool IsStatic,
const char *ReturnType,
const std::string &FunctionName,
const ArgTy &Args) {
indent() << AccessModifierStr(AM) << ((IsStatic) ? " static " : " ")
<< ((ReturnType) ? ReturnType : "") << " " << FunctionName << "(";
bool FirstArg = true;
for (ArgTy::const_iterator I = Args.begin(), E = Args.end();
I != E;
I++) {
if (!FirstArg)
out() << ", ";
else
FirstArg = false;
out() << I->first << " " << I->second;
}
out() << ")";
startBlock();
return;
}
void RSReflection::Context::endFunction() {
endBlock();
return;
}
bool RSReflection::Context::addTypeNameForElement(
const std::string &TypeName) {
if (mTypesToCheck.find(TypeName) == mTypesToCheck.end()) {
mTypesToCheck.insert(TypeName);
return true;
} else {
return false;
}
}
bool RSReflection::Context::addTypeNameForFieldPacker(
const std::string &TypeName) {
if (mFieldPackerTypes.find(TypeName) == mFieldPackerTypes.end()) {
mFieldPackerTypes.insert(TypeName);
return true;
} else {
return false;
}
}
} // namespace slang