| /* |
| * Copyright (C) 2011 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 "ApiGen.h" |
| #include "EntryPoint.h" |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include "strUtils.h" |
| #include <errno.h> |
| #include <sys/types.h> |
| |
| /* Define this to 1 to enable support for the 'isLarge' variable flag |
| * that instructs the encoder to send large data buffers by a direct |
| * write through the pipe (i.e. without copying it into a temporary |
| * buffer. This has definite performance benefits when using a QEMU Pipe. |
| * |
| * Set to 0 otherwise. |
| */ |
| #define WITH_LARGE_SUPPORT 1 |
| |
| EntryPoint * ApiGen::findEntryByName(const std::string & name) |
| { |
| EntryPoint * entry = NULL; |
| |
| size_t n = this->size(); |
| for (size_t i = 0; i < n; i++) { |
| if (at(i).name() == name) { |
| entry = &(at(i)); |
| break; |
| } |
| } |
| return entry; |
| } |
| |
| void ApiGen::printHeader(FILE *fp) const |
| { |
| fprintf(fp, "// Generated Code - DO NOT EDIT !!\n"); |
| fprintf(fp, "// generated by 'emugen'\n"); |
| } |
| |
| int ApiGen::genProcTypes(const std::string &filename, SideType side) |
| { |
| FILE *fp = fopen(filename.c_str(), "wt"); |
| if (fp == NULL) { |
| perror(filename.c_str()); |
| return -1; |
| } |
| printHeader(fp); |
| |
| const char* basename = m_basename.c_str(); |
| |
| fprintf(fp, "#ifndef __%s_%s_proc_t_h\n", basename, sideString(side)); |
| fprintf(fp, "#define __%s_%s_proc_t_h\n", basename, sideString(side)); |
| fprintf(fp, "\n\n"); |
| fprintf(fp, "\n#include \"%s_types.h\"\n",basename); |
| fprintf(fp, "#ifndef %s_APIENTRY\n",basename); |
| fprintf(fp, "#define %s_APIENTRY \n",basename); |
| fprintf(fp, "#endif\n"); |
| |
| |
| for (size_t i = 0; i < size(); i++) { |
| EntryPoint *e = &at(i); |
| |
| fprintf(fp, "typedef "); |
| e->retval().printType(fp); |
| fprintf(fp, " (%s_APIENTRY *%s_%s_proc_t) (", basename, e->name().c_str(), sideString(side)); |
| if (side == CLIENT_SIDE) { fprintf(fp, "void * ctx"); } |
| if (e->customDecoder() && side == SERVER_SIDE) { fprintf(fp, "void *ctx"); } |
| |
| VarsArray & evars = e->vars(); |
| size_t n = evars.size(); |
| |
| for (size_t j = 0; j < n; j++) { |
| if (!evars[j].isVoid()) { |
| if (j != 0 || side == CLIENT_SIDE || (side == SERVER_SIDE && e->customDecoder())) fprintf(fp, ", "); |
| evars[j].printType(fp); |
| } |
| } |
| fprintf(fp, ");\n"); |
| } |
| fprintf(fp, "\n\n#endif\n"); |
| return 0; |
| } |
| |
| int ApiGen::genFuncTable(const std::string &filename, SideType side) |
| { |
| FILE *fp = fopen(filename.c_str(), "wt"); |
| if (fp == NULL) { |
| perror(filename.c_str()); |
| return -1; |
| } |
| printHeader(fp); |
| |
| fprintf(fp, "#ifndef __%s_%s_ftable_t_h\n", m_basename.c_str(), sideString(side)); |
| fprintf(fp, "#define __%s_%s_ftable_t_h\n", m_basename.c_str(), sideString(side)); |
| fprintf(fp, "\n\n"); |
| fprintf(fp, "static struct _%s_funcs_by_name {\n", m_basename.c_str()); |
| fprintf(fp, |
| "\tconst char *name;\n" \ |
| "\tvoid *proc;\n" \ |
| "} %s_funcs_by_name[] = {\n", m_basename.c_str()); |
| |
| |
| for (size_t i = 0; i < size(); i++) { |
| EntryPoint *e = &at(i); |
| if (e->notApi()) continue; |
| fprintf(fp, "\t{\"%s\", (void*)%s},\n", e->name().c_str(), e->name().c_str()); |
| } |
| fprintf(fp, "};\n"); |
| fprintf(fp, "static int %s_num_funcs = sizeof(%s_funcs_by_name) / sizeof(struct _%s_funcs_by_name);\n", |
| m_basename.c_str(), m_basename.c_str(), m_basename.c_str()); |
| fprintf(fp, "\n\n#endif\n"); |
| return 0; |
| } |
| |
| |
| int ApiGen::genContext(const std::string & filename, SideType side) |
| { |
| FILE *fp = fopen(filename.c_str(), "wt"); |
| if (fp == NULL) { |
| perror(filename.c_str()); |
| return -1; |
| } |
| printHeader(fp); |
| |
| fprintf(fp, "#ifndef __%s_%s_context_t_h\n", m_basename.c_str(), sideString(side)); |
| fprintf(fp, "#define __%s_%s_context_t_h\n", m_basename.c_str(), sideString(side)); |
| |
| // fprintf(fp, "\n#include \"%s_types.h\"\n", m_basename.c_str()); |
| fprintf(fp, "\n#include \"%s_%s_proc.h\"\n", m_basename.c_str(), sideString(side)); |
| |
| StringVec & contextHeaders = side == CLIENT_SIDE ? m_clientContextHeaders : m_serverContextHeaders; |
| for (size_t i = 0; i < contextHeaders.size(); i++) { |
| fprintf(fp, "#include %s\n", contextHeaders[i].c_str()); |
| } |
| fprintf(fp, "\n"); |
| |
| fprintf(fp, "\nstruct %s_%s_context_t {\n\n", m_basename.c_str(), sideString(side)); |
| for (size_t i = 0; i < size(); i++) { |
| EntryPoint *e = &at(i); |
| fprintf(fp, "\t%s_%s_proc_t %s;\n", e->name().c_str(), sideString(side), e->name().c_str()); |
| } |
| // accessors |
| fprintf(fp, "\t//Accessors \n"); |
| |
| for (size_t i = 0; i < size(); i++) { |
| EntryPoint *e = &at(i); |
| const char *n = e->name().c_str(); |
| const char *s = sideString(side); |
| fprintf(fp, "\tvirtual %s_%s_proc_t set_%s(%s_%s_proc_t f) { %s_%s_proc_t retval = %s; %s = f; return retval;}\n", n, s, n, n, s, n, s, n, n); |
| } |
| |
| // virtual destructor |
| fprintf(fp, "\t virtual ~%s_%s_context_t() {}\n", m_basename.c_str(), sideString(side)); |
| // accessor |
| if (side == CLIENT_SIDE || side == WRAPPER_SIDE) { |
| fprintf(fp, "\n\ttypedef %s_%s_context_t *CONTEXT_ACCESSOR_TYPE(void);\n", |
| m_basename.c_str(), sideString(side)); |
| fprintf(fp, "\tstatic void setContextAccessor(CONTEXT_ACCESSOR_TYPE *f);\n"); |
| } |
| |
| // init function |
| fprintf(fp, "\tint initDispatchByName( void *(*getProc)(const char *name, void *userData), void *userData);\n"); |
| |
| //client site set error virtual func |
| if (side == CLIENT_SIDE) { |
| fprintf(fp, "\tvirtual void setError(unsigned int error){};\n"); |
| fprintf(fp, "\tvirtual unsigned int getError(){ return 0; };\n"); |
| } |
| |
| fprintf(fp, "};\n"); |
| |
| fprintf(fp, "\n#endif\n"); |
| fclose(fp); |
| return 0; |
| } |
| |
| int ApiGen::genEntryPoints(const std::string & filename, SideType side) |
| { |
| |
| if (side != CLIENT_SIDE && side != WRAPPER_SIDE) { |
| fprintf(stderr, "Entry points are only defined for Client and Wrapper components\n"); |
| return -999; |
| } |
| |
| |
| FILE *fp = fopen(filename.c_str(), "wt"); |
| if (fp == NULL) { |
| perror(filename.c_str()); |
| return errno; |
| } |
| |
| printHeader(fp); |
| fprintf(fp, "#include <stdio.h>\n"); |
| fprintf(fp, "#include <stdlib.h>\n"); |
| fprintf(fp, "#include \"%s_%s_context.h\"\n", m_basename.c_str(), sideString(side)); |
| fprintf(fp, "\n"); |
| |
| fprintf(fp, "#ifndef GL_TRUE\n"); |
| fprintf(fp, "extern \"C\" {\n"); |
| |
| for (size_t i = 0; i < size(); i++) { |
| fprintf(fp, "\t"); at(i).print(fp, false); fprintf(fp, ";\n"); |
| } |
| fprintf(fp, "};\n\n"); |
| fprintf(fp, "#endif\n"); |
| |
| fprintf(fp, "#ifndef GET_CONTEXT\n"); |
| fprintf(fp, "static %s_%s_context_t::CONTEXT_ACCESSOR_TYPE *getCurrentContext = NULL;\n", |
| m_basename.c_str(), sideString(side)); |
| |
| fprintf(fp, |
| "void %s_%s_context_t::setContextAccessor(CONTEXT_ACCESSOR_TYPE *f) { getCurrentContext = f; }\n", |
| m_basename.c_str(), sideString(side)); |
| fprintf(fp, "#define GET_CONTEXT %s_%s_context_t * ctx = getCurrentContext() \n", |
| m_basename.c_str(), sideString(side)); |
| fprintf(fp, "#endif\n\n"); |
| |
| |
| for (size_t i = 0; i < size(); i++) { |
| EntryPoint *e = &at(i); |
| e->print(fp); |
| fprintf(fp, "{\n"); |
| fprintf(fp, "\tGET_CONTEXT; \n"); |
| |
| bool shouldReturn = !e->retval().isVoid(); |
| bool shouldCallWithContext = (side == CLIENT_SIDE); |
| //param check |
| if (shouldCallWithContext) { |
| for (size_t j=0; j<e->vars().size(); j++) { |
| if (e->vars()[j].paramCheckExpression() != "") |
| fprintf(fp, "\t%s\n", e->vars()[j].paramCheckExpression().c_str()); |
| } |
| } |
| fprintf(fp, "\t %sctx->%s(%s", |
| shouldReturn ? "return " : "", |
| e->name().c_str(), |
| shouldCallWithContext ? "ctx" : ""); |
| size_t nvars = e->vars().size(); |
| |
| for (size_t j = 0; j < nvars; j++) { |
| if (!e->vars()[j].isVoid()) { |
| fprintf(fp, "%s %s", |
| j != 0 || shouldCallWithContext ? "," : "", |
| e->vars()[j].name().c_str()); |
| } |
| } |
| fprintf(fp, ");\n"); |
| fprintf(fp, "}\n\n"); |
| } |
| fclose(fp); |
| return 0; |
| } |
| |
| |
| int ApiGen::genOpcodes(const std::string &filename) |
| { |
| FILE *fp = fopen(filename.c_str(), "wt"); |
| if (fp == NULL) { |
| perror(filename.c_str()); |
| return errno; |
| } |
| |
| printHeader(fp); |
| fprintf(fp, "#ifndef __GUARD_%s_opcodes_h_\n", m_basename.c_str()); |
| fprintf(fp, "#define __GUARD_%s_opcodes_h_\n\n", m_basename.c_str()); |
| for (size_t i = 0; i < size(); i++) { |
| fprintf(fp, "#define OP_%s \t\t\t\t\t%u\n", at(i).name().c_str(), (unsigned int)i + m_baseOpcode); |
| } |
| fprintf(fp, "#define OP_last \t\t\t\t\t%u\n", (unsigned int)size() + m_baseOpcode); |
| fprintf(fp,"\n\n#endif\n"); |
| fclose(fp); |
| return 0; |
| |
| } |
| int ApiGen::genAttributesTemplate(const std::string &filename ) |
| { |
| FILE *fp = fopen(filename.c_str(), "wt"); |
| if (fp == NULL) { |
| perror(filename.c_str()); |
| return -1; |
| } |
| |
| for (size_t i = 0; i < size(); i++) { |
| if (at(i).hasPointers()) { |
| fprintf(fp, "#"); |
| at(i).print(fp); |
| fprintf(fp, "%s\n\n", at(i).name().c_str()); |
| } |
| } |
| fclose(fp); |
| return 0; |
| } |
| |
| int ApiGen::genEncoderHeader(const std::string &filename) |
| { |
| FILE *fp = fopen(filename.c_str(), "wt"); |
| if (fp == NULL) { |
| perror(filename.c_str()); |
| return -1; |
| } |
| |
| printHeader(fp); |
| std::string classname = m_basename + "_encoder_context_t"; |
| |
| fprintf(fp, "\n#ifndef GUARD_%s\n", classname.c_str()); |
| fprintf(fp, "#define GUARD_%s\n\n", classname.c_str()); |
| |
| fprintf(fp, "#include \"IOStream.h\"\n"); |
| fprintf(fp, "#include \"%s_%s_context.h\"\n\n\n", m_basename.c_str(), sideString(CLIENT_SIDE)); |
| |
| for (size_t i = 0; i < m_encoderHeaders.size(); i++) { |
| fprintf(fp, "#include %s\n", m_encoderHeaders[i].c_str()); |
| } |
| fprintf(fp, "\n"); |
| |
| fprintf(fp, "struct %s : public %s_%s_context_t {\n\n", |
| classname.c_str(), m_basename.c_str(), sideString(CLIENT_SIDE)); |
| fprintf(fp, "\tIOStream *m_stream;\n\n"); |
| |
| fprintf(fp, "\t%s(IOStream *stream);\n\n", classname.c_str()); |
| fprintf(fp, "\n};\n\n"); |
| |
| fprintf(fp,"extern \"C\" {\n"); |
| |
| for (size_t i = 0; i < size(); i++) { |
| fprintf(fp, "\t"); |
| at(i).print(fp, false, "_enc", /* classname + "::" */"", "void *self"); |
| fprintf(fp, ";\n"); |
| } |
| fprintf(fp, "};\n"); |
| fprintf(fp, "#endif"); |
| |
| fclose(fp); |
| return 0; |
| } |
| |
| // Format the byte length expression for a given variable into a user-provided buffer |
| // If the variable type is not a pointer, this is simply its size as a decimal constant |
| // If the variable is a pointer, this will be an expression provided by the .attrib file |
| // through the 'len' attribute. |
| // |
| // Returns 1 if the variable is a pointer, 0 otherwise |
| // |
| static int getVarEncodingSizeExpression(Var& var, EntryPoint* e, char* buff, size_t bufflen) |
| { |
| int ret = 0; |
| if (!var.isPointer()) { |
| snprintf(buff, bufflen, "%u", (unsigned int) var.type()->bytes()); |
| } else { |
| ret = 1; |
| const char* lenExpr = var.lenExpression().c_str(); |
| const char* varname = var.name().c_str(); |
| if (e != NULL && lenExpr[0] == '\0') { |
| fprintf(stderr, "%s: data len is undefined for '%s'\n", |
| e->name().c_str(), varname); |
| } |
| if (var.nullAllowed()) { |
| snprintf(buff, bufflen, "((%s != NULL) ? %s : 0)", varname, lenExpr); |
| } else { |
| snprintf(buff, bufflen, "%s", lenExpr); |
| } |
| } |
| return ret; |
| } |
| |
| static int writeVarEncodingSize(Var& var, FILE* fp) |
| { |
| int ret = 0; |
| if (!var.isPointer()) { |
| fprintf(fp, "%u", (unsigned int) var.type()->bytes()); |
| } else { |
| ret = 1; |
| fprintf(fp, "__size_%s", var.name().c_str()); |
| } |
| return ret; |
| } |
| |
| |
| |
| static void writeVarEncodingExpression(Var& var, FILE* fp) |
| { |
| const char* varname = var.name().c_str(); |
| |
| if (var.isPointer()) { |
| // encode a pointer header |
| fprintf(fp, "\t*(unsigned int *)(ptr) = __size_%s; ptr += 4;\n", varname); |
| |
| Var::PointerDir dir = var.pointerDir(); |
| if (dir == Var::POINTER_INOUT || dir == Var::POINTER_IN) { |
| if (var.nullAllowed()) { |
| fprintf(fp, "\tif (%s != NULL) ", varname); |
| } else { |
| fprintf(fp, "\t"); |
| } |
| |
| if (var.packExpression().size() != 0) { |
| fprintf(fp, "%s;", var.packExpression().c_str()); |
| } else { |
| fprintf(fp, "memcpy(ptr, %s, __size_%s);", |
| varname, varname); |
| } |
| |
| fprintf(fp, "ptr += __size_%s;\n", varname); |
| } |
| } else { |
| // encode a non pointer variable |
| if (!var.isVoid()) { |
| fprintf(fp, "\t\tmemcpy(ptr, &%s, %u); ptr += %u;\n", |
| varname, |
| (uint) var.type()->bytes(), |
| (uint) var.type()->bytes()); |
| } |
| } |
| } |
| |
| #if WITH_LARGE_SUPPORT |
| static void writeVarLargeEncodingExpression(Var& var, FILE* fp) |
| { |
| const char* varname = var.name().c_str(); |
| |
| fprintf(fp, "\tstream->writeFully(&__size_%s,4);\n", varname); |
| if (var.nullAllowed()) { |
| fprintf(fp, "\tif (%s != NULL) ", varname); |
| } else { |
| fprintf(fp, "\t"); |
| } |
| if (var.writeExpression() != "") { |
| fprintf(fp, "%s", var.writeExpression().c_str()); |
| } else { |
| fprintf(fp, "stream->writeFully(%s, __size_%s)", varname, varname); |
| } |
| fprintf(fp, ";\n"); |
| } |
| #endif /* WITH_LARGE_SUPPORT */ |
| |
| int ApiGen::genEncoderImpl(const std::string &filename) |
| { |
| FILE *fp = fopen(filename.c_str(), "wt"); |
| if (fp == NULL) { |
| perror(filename.c_str()); |
| return -1; |
| } |
| |
| printHeader(fp); |
| fprintf(fp, "\n\n#include <string.h>\n"); |
| fprintf(fp, "#include \"%s_opcodes.h\"\n\n", m_basename.c_str()); |
| fprintf(fp, "#include \"%s_enc.h\"\n\n\n", m_basename.c_str()); |
| fprintf(fp, "#include <stdio.h>\n"); |
| std::string classname = m_basename + "_encoder_context_t"; |
| size_t n = size(); |
| |
| // unsupport printout |
| fprintf(fp, |
| "static void enc_unsupported()\n{\n\tALOGE(\"Function is unsupported\\n\");\n}\n\n"); |
| |
| // entry points; |
| for (size_t i = 0; i < n; i++) { |
| EntryPoint *e = &at(i); |
| |
| if (e->unsupported()) continue; |
| |
| |
| e->print(fp, true, "_enc", /* classname + "::" */"", "void *self"); |
| fprintf(fp, "{\n"); |
| |
| // fprintf(fp, "\n\tDBG(\">>>> %s\\n\");\n", e->name().c_str()); |
| fprintf(fp, "\n\t%s *ctx = (%s *)self;\n", |
| classname.c_str(), |
| classname.c_str()); |
| fprintf(fp, "\tIOStream *stream = ctx->m_stream;\n\n"); |
| VarsArray & evars = e->vars(); |
| size_t maxvars = evars.size(); |
| size_t j; |
| |
| char buff[256]; |
| |
| // Define the __size_XXX variables that contain the size of data |
| // associated with pointers. |
| for (j = 0; j < maxvars; j++) { |
| Var& var = evars[j]; |
| |
| if (!var.isPointer()) |
| continue; |
| |
| const char* varname = var.name().c_str(); |
| fprintf(fp, "\tconst unsigned int __size_%s = ", varname); |
| |
| getVarEncodingSizeExpression(var, e, buff, sizeof(buff)); |
| fprintf(fp, "%s;\n", buff); |
| } |
| |
| #if WITH_LARGE_SUPPORT |
| // We need to take care of 'isLarge' variable in a special way |
| // Anything before an isLarge variable can be packed into a single |
| // buffer, which is then commited. Each isLarge variable is a pointer |
| // to data that can be written to directly through the pipe, which |
| // will be instant when using a QEMU pipe |
| |
| size_t nvars = 0; |
| size_t npointers = 0; |
| |
| // First, compute the total size, 8 bytes for the opcode + payload size |
| fprintf(fp, "\t unsigned char *ptr;\n"); |
| fprintf(fp, "\t const size_t packetSize = 8"); |
| |
| for (j = 0; j < maxvars; j++) { |
| fprintf(fp, " + "); |
| npointers += writeVarEncodingSize(evars[j], fp); |
| } |
| if (npointers > 0) { |
| fprintf(fp, " + %zu*4", npointers); |
| } |
| fprintf(fp, ";\n"); |
| |
| // We need to divide the packet into fragments. Each fragment contains |
| // either copied arguments to a temporary buffer, or direct writes for |
| // large variables. |
| // |
| // The first fragment must also contain the opcode+payload_size |
| // |
| nvars = 0; |
| while (nvars < maxvars || maxvars == 0) { |
| |
| // Skip over non-large fields |
| for (j = nvars; j < maxvars; j++) { |
| if (evars[j].isLarge()) |
| break; |
| } |
| |
| // Write a fragment if needed. |
| if (nvars == 0 || j > nvars) { |
| const char* plus = ""; |
| |
| if (nvars == 0 && j == maxvars) { |
| // Simple shortcut for the common case where we don't have large variables; |
| fprintf(fp, "\tptr = stream->alloc(packetSize);\n"); |
| |
| } else { |
| // allocate buffer from the stream until the first large variable |
| fprintf(fp, "\tptr = stream->alloc("); |
| plus = ""; |
| |
| if (nvars == 0) { |
| fprintf(fp,"8"); plus = " + "; |
| } |
| if (j > nvars) { |
| npointers = 0; |
| for (j = nvars; j < maxvars && !evars[j].isLarge(); j++) { |
| fprintf(fp, "%s", plus); plus = " + "; |
| npointers += writeVarEncodingSize(evars[j], fp); |
| } |
| if (npointers > 0) { |
| fprintf(fp, "%s%zu*4", plus, npointers); plus = " + "; |
| } |
| } |
| fprintf(fp,");\n"); |
| } |
| |
| // encode packet header if needed. |
| if (nvars == 0) { |
| fprintf(fp, "\tint tmp = OP_%s;memcpy(ptr, &tmp, 4); ptr += 4;\n", e->name().c_str()); |
| fprintf(fp, "\tmemcpy(ptr, &packetSize, 4); ptr += 4;\n\n"); |
| } |
| |
| if (maxvars == 0) |
| break; |
| |
| // encode non-large fields in this fragment |
| for (j = nvars; j < maxvars && !evars[j].isLarge(); j++) { |
| writeVarEncodingExpression(evars[j],fp); |
| } |
| |
| // Ensure the fragment is commited if it is followed by a large variable |
| if (j < maxvars) { |
| fprintf(fp, "\tstream->flush();\n"); |
| } |
| } |
| |
| // If we have one or more large variables, write them directly. |
| // As size + data |
| for ( ; j < maxvars && evars[j].isLarge(); j++) { |
| writeVarLargeEncodingExpression(evars[j], fp); |
| } |
| |
| nvars = j; |
| } |
| |
| #else /* !WITH_LARGE_SUPPORT */ |
| size_t nvars = evars.size(); |
| size_t npointers = 0; |
| fprintf(fp, "\t const size_t packetSize = 8"); |
| for (size_t j = 0; j < nvars; j++) { |
| npointers += getVarEncodingSizeExpression(evars[j],e,buff,sizeof(buff)); |
| fprintf(fp, " + %s", buff); |
| } |
| fprintf(fp, " + %u * 4;\n", (unsigned int) npointers); |
| |
| // allocate buffer from the stream; |
| fprintf(fp, "\t unsigned char *ptr = stream->alloc(packetSize);\n\n"); |
| |
| // encode into the stream; |
| fprintf(fp, "\tint tmp = OP_%s; memcpy(ptr, &tmp, 4); ptr += 4;\n", e->name().c_str()); |
| fprintf(fp, "\tmemcpy(ptr, &packetSize, 4); ptr += 4;\n\n"); |
| |
| // out variables |
| for (size_t j = 0; j < nvars; j++) { |
| writeVarEncodingExpression(evars[j], fp); |
| } |
| #endif /* !WITH_LARGE_SUPPORT */ |
| |
| // in variables; |
| for (size_t j = 0; j < nvars; j++) { |
| if (evars[j].isPointer()) { |
| Var::PointerDir dir = evars[j].pointerDir(); |
| if (dir == Var::POINTER_INOUT || dir == Var::POINTER_OUT) { |
| const char* varname = evars[j].name().c_str(); |
| if (evars[j].nullAllowed()) { |
| fprintf(fp, "\tif (%s != NULL) ",varname); |
| } else { |
| fprintf(fp, "\t"); |
| } |
| fprintf(fp, "stream->readback(%s, __size_%s);\n", |
| varname, varname); |
| } |
| } |
| } |
| //XXX fprintf(fp, "\n\tDBG(\"<<<< %s\\n\");\n", e->name().c_str()); |
| // todo - return value for pointers |
| if (e->retval().isPointer()) { |
| fprintf(stderr, "WARNING: %s : return value of pointer is unsupported\n", |
| e->name().c_str()); |
| fprintf(fp, "\t return NULL;\n"); |
| } else if (e->retval().type()->name() != "void") { |
| fprintf(fp, "\n\t%s retval;\n", e->retval().type()->name().c_str()); |
| fprintf(fp, "\tstream->readback(&retval, %u);\n",(uint) e->retval().type()->bytes()); |
| fprintf(fp, "\treturn retval;\n"); |
| } |
| fprintf(fp, "}\n\n"); |
| } |
| |
| // constructor |
| fprintf(fp, "%s::%s(IOStream *stream)\n{\n", classname.c_str(), classname.c_str()); |
| fprintf(fp, "\tm_stream = stream;\n\n"); |
| |
| for (size_t i = 0; i < n; i++) { |
| EntryPoint *e = &at(i); |
| if (e->unsupported()) { |
| fprintf(fp, "\tset_%s((%s_%s_proc_t)(enc_unsupported));\n", e->name().c_str(), e->name().c_str(), sideString(CLIENT_SIDE)); |
| } else { |
| fprintf(fp, "\tset_%s(%s_enc);\n", e->name().c_str(), e->name().c_str()); |
| } |
| /** |
| if (e->unsupsported()) { |
| fprintf(fp, "\tmemcpy((void *)(&%s), (const void *)(&enc_unsupported), sizeof(%s));\n", |
| e->name().c_str(), |
| e->name().c_str()); |
| } else { |
| fprintf(fp, "\t%s = %s_enc;\n", e->name().c_str(), e->name().c_str()); |
| } |
| **/ |
| } |
| fprintf(fp, "}\n\n"); |
| |
| fclose(fp); |
| return 0; |
| } |
| |
| |
| int ApiGen::genDecoderHeader(const std::string &filename) |
| { |
| FILE *fp = fopen(filename.c_str(), "wt"); |
| if (fp == NULL) { |
| perror(filename.c_str()); |
| return -1; |
| } |
| |
| printHeader(fp); |
| std::string classname = m_basename + "_decoder_context_t"; |
| |
| fprintf(fp, "\n#ifndef GUARD_%s\n", classname.c_str()); |
| fprintf(fp, "#define GUARD_%s\n\n", classname.c_str()); |
| |
| fprintf(fp, "#include \"IOStream.h\" \n"); |
| fprintf(fp, "#include \"%s_%s_context.h\"\n\n\n", m_basename.c_str(), sideString(SERVER_SIDE)); |
| |
| for (size_t i = 0; i < m_decoderHeaders.size(); i++) { |
| fprintf(fp, "#include %s\n", m_decoderHeaders[i].c_str()); |
| } |
| fprintf(fp, "\n"); |
| |
| fprintf(fp, "struct %s : public %s_%s_context_t {\n\n", |
| classname.c_str(), m_basename.c_str(), sideString(SERVER_SIDE)); |
| fprintf(fp, "\tsize_t decode(void *buf, size_t bufsize, IOStream *stream);\n"); |
| fprintf(fp, "\n};\n\n"); |
| fprintf(fp, "#endif\n"); |
| |
| fclose(fp); |
| return 0; |
| } |
| |
| int ApiGen::genContextImpl(const std::string &filename, SideType side) |
| { |
| FILE *fp = fopen(filename.c_str(), "wt"); |
| if (fp == NULL) { |
| perror(filename.c_str()); |
| return -1; |
| } |
| printHeader(fp); |
| |
| std::string classname = m_basename + "_" + sideString(side) + "_context_t"; |
| size_t n = size(); |
| fprintf(fp, "\n\n#include <string.h>\n"); |
| fprintf(fp, "#include \"%s_%s_context.h\"\n\n\n", m_basename.c_str(), sideString(side)); |
| fprintf(fp, "#include <stdio.h>\n\n"); |
| |
| // init function; |
| fprintf(fp, "int %s::initDispatchByName(void *(*getProc)(const char *, void *userData), void *userData)\n{\n", classname.c_str()); |
| fprintf(fp, "\tvoid *ptr;\n\n"); |
| for (size_t i = 0; i < n; i++) { |
| EntryPoint *e = &at(i); |
| fprintf(fp, "\tptr = getProc(\"%s\", userData); set_%s((%s_%s_proc_t)ptr);\n", |
| e->name().c_str(), |
| e->name().c_str(), |
| e->name().c_str(), |
| sideString(side)); |
| |
| } |
| fprintf(fp, "\treturn 0;\n"); |
| fprintf(fp, "}\n\n"); |
| fclose(fp); |
| return 0; |
| } |
| |
| int ApiGen::genDecoderImpl(const std::string &filename) |
| { |
| FILE *fp = fopen(filename.c_str(), "wt"); |
| if (fp == NULL) { |
| perror(filename.c_str()); |
| return -1; |
| } |
| |
| printHeader(fp); |
| |
| std::string classname = m_basename + "_decoder_context_t"; |
| |
| size_t n = size(); |
| |
| fprintf(fp, "\n\n#include <string.h>\n"); |
| fprintf(fp, "#include \"%s_opcodes.h\"\n\n", m_basename.c_str()); |
| fprintf(fp, "#include \"%s_dec.h\"\n\n\n", m_basename.c_str()); |
| fprintf(fp, "#include <stdio.h>\n\n"); |
| fprintf(fp, "typedef unsigned int tsize_t; // Target \"size_t\", which is 32-bit for now. It may or may not be the same as host's size_t when emugen is compiled.\n\n"); |
| |
| // decoder switch; |
| fprintf(fp, "size_t %s::decode(void *buf, size_t len, IOStream *stream)\n{\n", classname.c_str()); |
| fprintf(fp, |
| " \n\ |
| \tsize_t pos = 0;\n\ |
| \tif (len < 8) return pos; \n\ |
| \tunsigned char *ptr = (unsigned char *)buf;\n\ |
| \tbool unknownOpcode = false; \n\ |
| #ifdef CHECK_GL_ERROR \n\ |
| \tchar lastCall[256] = {0}; \n\ |
| #endif \n\ |
| \twhile ((len - pos >= 8) && !unknownOpcode) { \n\ |
| \t\tvoid *params[%u]; \n\ |
| \t\tint opcode = *(int *)ptr; \n\ |
| \t\tunsigned int packetLen = *(int *)(ptr + 4);\n\ |
| \t\tif (len - pos < packetLen) return pos; \n\ |
| \t\tswitch(opcode) {\n", |
| (uint) m_maxEntryPointsParams); |
| |
| for (size_t f = 0; f < n; f++) { |
| enum Pass_t { PASS_TmpBuffAlloc = 0, PASS_MemAlloc, PASS_DebugPrint, PASS_FunctionCall, PASS_Epilog, PASS_LAST }; |
| EntryPoint *e = &at(f); |
| |
| // construct a printout string; |
| std::string printString = ""; |
| for (size_t i = 0; i < e->vars().size(); i++) { |
| Var *v = &e->vars()[i]; |
| if (!v->isVoid()) printString += (v->isPointer() ? "%p(%u)" : v->type()->printFormat()) + " "; |
| } |
| printString += ""; |
| // TODO - add for return value; |
| |
| fprintf(fp, "\t\t\tcase OP_%s:\n", e->name().c_str()); |
| fprintf(fp, "\t\t\t{\n"); |
| |
| bool totalTmpBuffExist = false; |
| std::string totalTmpBuffOffset = "0"; |
| std::string *tmpBufOffset = new std::string[e->vars().size()]; |
| |
| // construct retval type string |
| std::string retvalType; |
| if (!e->retval().isVoid()) { |
| retvalType = e->retval().type()->name(); |
| } |
| |
| for (int pass = PASS_TmpBuffAlloc; pass < PASS_LAST; pass++) { |
| if (pass == PASS_FunctionCall && !e->retval().isVoid() && !e->retval().isPointer()) { |
| fprintf(fp, "\t\t\t*(%s *)(&tmpBuf[%s]) = ", retvalType.c_str(), |
| totalTmpBuffOffset.c_str()); |
| } |
| |
| |
| if (pass == PASS_FunctionCall) { |
| fprintf(fp, "\t\t\tthis->%s(", e->name().c_str()); |
| if (e->customDecoder()) { |
| fprintf(fp, "this"); // add a context to the call |
| } |
| } else if (pass == PASS_DebugPrint) { |
| fprintf(fp, "#ifdef DEBUG_PRINTOUT\n"); |
| fprintf(fp, "\t\t\tfprintf(stderr,\"%s: %s(%s)\\n\"", m_basename.c_str(), e->name().c_str(), printString.c_str()); |
| if (e->vars().size() > 0 && !e->vars()[0].isVoid()) fprintf(fp, ","); |
| } |
| |
| std::string varoffset = "8"; // skip the header |
| VarsArray & evars = e->vars(); |
| // allocate memory for out pointers; |
| for (size_t j = 0; j < evars.size(); j++) { |
| Var *v = & evars[j]; |
| if (!v->isVoid()) { |
| if ((pass == PASS_FunctionCall) && (j != 0 || e->customDecoder())) fprintf(fp, ", "); |
| if (pass == PASS_DebugPrint && j != 0) fprintf(fp, ", "); |
| |
| if (!v->isPointer()) { |
| if (pass == PASS_FunctionCall || pass == PASS_DebugPrint) { |
| fprintf(fp, "*(%s *)(ptr + %s)", v->type()->name().c_str(), varoffset.c_str()); |
| } |
| varoffset += " + " + toString(v->type()->bytes()); |
| } else { |
| if (v->pointerDir() == Var::POINTER_IN || v->pointerDir() == Var::POINTER_INOUT) { |
| if (pass == PASS_MemAlloc && v->pointerDir() == Var::POINTER_INOUT) { |
| fprintf(fp, "\t\t\tsize_t tmpPtr%uSize = (size_t)*(unsigned int *)(ptr + %s);\n", |
| (uint) j, varoffset.c_str()); |
| fprintf(fp, "unsigned char *tmpPtr%u = (ptr + %s + 4);\n", |
| (uint) j, varoffset.c_str()); |
| } |
| if (pass == PASS_FunctionCall) { |
| if (v->nullAllowed()) { |
| fprintf(fp, "*((unsigned int *)(ptr + %s)) == 0 ? NULL : (%s)(ptr + %s + 4)", |
| varoffset.c_str(), v->type()->name().c_str(), varoffset.c_str()); |
| } else { |
| fprintf(fp, "(%s)(ptr + %s + 4)", |
| v->type()->name().c_str(), varoffset.c_str()); |
| } |
| } else if (pass == PASS_DebugPrint) { |
| fprintf(fp, "(%s)(ptr + %s + 4), *(unsigned int *)(ptr + %s)", |
| v->type()->name().c_str(), varoffset.c_str(), |
| varoffset.c_str()); |
| } |
| varoffset += " + 4 + *(tsize_t *)(ptr +" + varoffset + ")"; |
| } else { // out pointer; |
| if (pass == PASS_TmpBuffAlloc) { |
| fprintf(fp, "\t\t\tsize_t tmpPtr%uSize = (size_t)*(unsigned int *)(ptr + %s);\n", |
| (uint) j, varoffset.c_str()); |
| if (!totalTmpBuffExist) { |
| fprintf(fp, "\t\t\tsize_t totalTmpSize = tmpPtr%uSize;\n", (uint)j); |
| } else { |
| fprintf(fp, "\t\t\ttotalTmpSize += tmpPtr%uSize;\n", (uint)j); |
| } |
| tmpBufOffset[j] = totalTmpBuffOffset; |
| char tmpPtrName[16]; |
| sprintf(tmpPtrName," + tmpPtr%uSize", (uint)j); |
| totalTmpBuffOffset += std::string(tmpPtrName); |
| totalTmpBuffExist = true; |
| } else if (pass == PASS_MemAlloc) { |
| fprintf(fp, "\t\t\tunsigned char *tmpPtr%u = &tmpBuf[%s];\n", |
| (uint)j, tmpBufOffset[j].c_str()); |
| } else if (pass == PASS_FunctionCall) { |
| if (v->nullAllowed()) { |
| fprintf(fp, "tmpPtr%uSize == 0 ? NULL : (%s)(tmpPtr%u)", |
| (uint) j, v->type()->name().c_str(), (uint) j); |
| } else { |
| fprintf(fp, "(%s)(tmpPtr%u)", v->type()->name().c_str(), (uint) j); |
| } |
| } else if (pass == PASS_DebugPrint) { |
| fprintf(fp, "(%s)(tmpPtr%u), *(unsigned int *)(ptr + %s)", |
| v->type()->name().c_str(), (uint) j, |
| varoffset.c_str()); |
| } |
| varoffset += " + 4"; |
| } |
| } |
| } |
| } |
| |
| if (pass == PASS_FunctionCall || pass == PASS_DebugPrint) fprintf(fp, ");\n"); |
| if (pass == PASS_DebugPrint) fprintf(fp, "#endif\n"); |
| |
| if (pass == PASS_TmpBuffAlloc) { |
| if (!e->retval().isVoid() && !e->retval().isPointer()) { |
| if (!totalTmpBuffExist) |
| fprintf(fp, "\t\t\tsize_t totalTmpSize = sizeof(%s);\n", retvalType.c_str()); |
| else |
| fprintf(fp, "\t\t\ttotalTmpSize += sizeof(%s);\n", retvalType.c_str()); |
| |
| totalTmpBuffExist = true; |
| } |
| if (totalTmpBuffExist) { |
| fprintf(fp, "\t\t\tunsigned char *tmpBuf = stream->alloc(totalTmpSize);\n"); |
| } |
| } |
| |
| if (pass == PASS_Epilog) { |
| // send back out pointers data as well as retval |
| if (totalTmpBuffExist) { |
| fprintf(fp, "\t\t\tstream->flush();\n"); |
| } |
| |
| fprintf(fp, "\t\t\tpos += *(int *)(ptr + 4);\n"); |
| fprintf(fp, "\t\t\tptr += *(int *)(ptr + 4);\n"); |
| } |
| |
| } // pass; |
| fprintf(fp, "\t\t\t}\n"); |
| fprintf(fp, "#ifdef CHECK_GL_ERROR\n"); |
| fprintf(fp, "\t\t\tsprintf(lastCall, \"%s\");\n", e->name().c_str()); |
| fprintf(fp, "#endif\n"); |
| fprintf(fp, "\t\t\tbreak;\n"); |
| |
| delete [] tmpBufOffset; |
| } |
| fprintf(fp, "\t\t\tdefault:\n"); |
| fprintf(fp, "\t\t\t\tunknownOpcode = true;\n"); |
| fprintf(fp, "\t\t} //switch\n"); |
| if (strstr(m_basename.c_str(), "gl")) { |
| fprintf(fp, "#ifdef CHECK_GL_ERROR\n"); |
| fprintf(fp, "\tint err = this->glGetError();\n"); |
| fprintf(fp, "\tif (err) fprintf(stderr, \"%s Error: 0x%%X in %%s\\n\", err, lastCall);\n", m_basename.c_str()); |
| fprintf(fp, "#endif\n"); |
| } |
| fprintf(fp, "\t} // while\n"); |
| fprintf(fp, "\treturn pos;\n"); |
| fprintf(fp, "}\n"); |
| |
| fclose(fp); |
| return 0; |
| } |
| |
| int ApiGen::readSpec(const std::string & filename) |
| { |
| FILE *specfp = fopen(filename.c_str(), "rt"); |
| if (specfp == NULL) { |
| return -1; |
| } |
| |
| char line[1000]; |
| unsigned int lc = 0; |
| while (fgets(line, sizeof(line), specfp) != NULL) { |
| lc++; |
| EntryPoint ref; |
| if (ref.parse(lc, std::string(line))) { |
| push_back(ref); |
| updateMaxEntryPointsParams(ref.vars().size()); |
| } |
| } |
| fclose(specfp); |
| return 0; |
| } |
| |
| int ApiGen::readAttributes(const std::string & attribFilename) |
| { |
| enum { ST_NAME, ST_ATT } state; |
| |
| FILE *fp = fopen(attribFilename.c_str(), "rt"); |
| if (fp == NULL) { |
| perror(attribFilename.c_str()); |
| return -1; |
| } |
| char buf[1000]; |
| |
| state = ST_NAME; |
| EntryPoint *currentEntry = NULL; |
| size_t lc = 0; |
| bool globalAttributes = false; |
| while (fgets(buf, sizeof(buf), fp) != NULL) { |
| lc++; |
| std::string line(buf); |
| if (line.size() == 0) continue; // could that happen? |
| |
| if (line.at(0) == '#') continue; // comment |
| |
| size_t first = line.find_first_not_of(" \t\n"); |
| if (state == ST_ATT && (first == std::string::npos || first == 0)) state = ST_NAME; |
| |
| line = trim(line); |
| if (line.size() == 0 || line.at(0) == '#') continue; |
| |
| switch(state) { |
| case ST_NAME: |
| if (line == "GLOBAL") { |
| globalAttributes = true; |
| } else { |
| globalAttributes = false; |
| currentEntry = findEntryByName(line); |
| if (currentEntry == NULL) { |
| fprintf(stderr, "WARNING: %u: attribute of non existant entry point %s\n", (unsigned int)lc, line.c_str()); |
| } |
| } |
| state = ST_ATT; |
| break; |
| case ST_ATT: |
| if (globalAttributes) { |
| setGlobalAttribute(line, lc); |
| } else if (currentEntry != NULL) { |
| currentEntry->setAttribute(line, lc); |
| } |
| break; |
| } |
| } |
| return 0; |
| } |
| |
| |
| int ApiGen::setGlobalAttribute(const std::string & line, size_t lc) |
| { |
| size_t pos = 0; |
| size_t last; |
| std::string token = getNextToken(line, pos, &last, WHITESPACE); |
| pos = last; |
| |
| if (token == "base_opcode") { |
| std::string str = getNextToken(line, pos, &last, WHITESPACE); |
| if (str.size() == 0) { |
| fprintf(stderr, "line %u: missing value for base_opcode\n", (uint) lc); |
| } else { |
| setBaseOpcode(atoi(str.c_str())); |
| } |
| } else if (token == "encoder_headers") { |
| std::string str = getNextToken(line, pos, &last, WHITESPACE); |
| pos = last; |
| while (str.size() != 0) { |
| encoderHeaders().push_back(str); |
| str = getNextToken(line, pos, &last, WHITESPACE); |
| pos = last; |
| } |
| } else if (token == "client_context_headers") { |
| std::string str = getNextToken(line, pos, &last, WHITESPACE); |
| pos = last; |
| while (str.size() != 0) { |
| clientContextHeaders().push_back(str); |
| str = getNextToken(line, pos, &last, WHITESPACE); |
| pos = last; |
| } |
| } else if (token == "server_context_headers") { |
| std::string str = getNextToken(line, pos, &last, WHITESPACE); |
| pos = last; |
| while (str.size() != 0) { |
| serverContextHeaders().push_back(str); |
| str = getNextToken(line, pos, &last, WHITESPACE); |
| pos = last; |
| } |
| } else if (token == "decoder_headers") { |
| std::string str = getNextToken(line, pos, &last, WHITESPACE); |
| pos = last; |
| while (str.size() != 0) { |
| decoderHeaders().push_back(str); |
| str = getNextToken(line, pos, &last, WHITESPACE); |
| pos = last; |
| } |
| } |
| else { |
| fprintf(stderr, "WARNING: %u : unknown global attribute %s\n", (unsigned int)lc, line.c_str()); |
| } |
| |
| return 0; |
| } |
| |