Convert libacc into a shared library.

Document internal CodeGenerator interface

Move license to a separate license file.

Define a public API for calling libacc.

Update the "acc" test program to use the public API.
Move "main.cpp" and test scripts into the tests subdirectory.
Move test data from tests to tests/data
Remove stale test data.
This commit is contained in:
Jack Palevich 2009-05-22 12:06:27 -07:00
parent ad3f0d74b4
commit 1cdef20774
20 changed files with 654 additions and 202 deletions

78
include/acc/acc.h Normal file
View File

@ -0,0 +1,78 @@
/*
* Copyright (C) 2009 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.
*/
#ifndef ANDROID_ACC_ACC_H
#define ANDROID_ACC_ACC_H
#include <stdint.h>
#include <sys/types.h>
typedef char ACCchar;
typedef int32_t ACCint;
typedef uint32_t ACCuint;
typedef ssize_t ACCsizei;
typedef unsigned int ACCenum;
typedef void ACCvoid;
typedef struct ACCscript ACCscript;
#define ACC_NO_ERROR 0x0000
#define ACC_INVALID_ENUM 0x0500
#define ACC_INVALID_OPERATION 0x0502
#define ACC_INVALID_VALUE 0x0501
#define ACC_OUT_OF_MEMORY 0x0505
#define ACC_COMPILE_STATUS 0x8B81
#define ACC_INFO_LOG_LENGTH 0x8B84
// ----------------------------------------------------------------------------
#ifdef __cplusplus
extern "C" {
#endif
ACCscript* accCreateScript();
void accDeleteScript(ACCscript* script);
ACCenum accGetError( ACCscript* script );
void accScriptSource(ACCscript* script,
ACCsizei count,
const ACCchar** string,
const ACCint* length);
void accCompileScript(ACCscript* script);
void accGetScriptiv(ACCscript* script,
ACCenum pname,
ACCint* params);
void accGetScriptInfoLog(ACCscript* script,
ACCsizei maxLength,
ACCsizei* length,
ACCchar* infoLog);
void accGetScriptLabel(ACCscript* script, const ACCchar * name,
ACCvoid** address);
#ifdef __cplusplus
};
#endif
// ----------------------------------------------------------------------------
#endif

View File

@ -5,8 +5,15 @@ include $(CLEAR_VARS)
# Shared library
#
LOCAL_MODULE:= acc
LOCAL_SRC_FILES := acc.cpp disassem.cpp
LOCAL_MODULE_TAGS := tests
LOCAL_MODULE:= libacc
LOCAL_SRC_FILES := acc.cpp
include $(BUILD_EXECUTABLE)
ifeq ($(TARGET_ARCH),arm)
LOCAL_SRC_FILES += disassem.cpp
endif
LOCAL_SHARED_LIBRARIES := libdl
include $(BUILD_SHARED_LIBRARY)
include $(call all-makefiles-under,$(LOCAL_PATH))

21
libacc/LICENSE Normal file
View File

@ -0,0 +1,21 @@
Obfuscated Tiny C Compiler
Copyright (C) 2001-2003 Fabrice Bellard
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product and its documentation
*is* required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

View File

@ -4,33 +4,10 @@
* in scripting environments where speed and memory footprint are important.
*
* This code is based upon the "unobfuscated" version of the
* Obfuscated Tiny C compiler, and retains the
* original copyright notice and license from that compiler, see below.
* Obfuscated Tiny C compiler, see the file LICENSE for details.
*
*/
/*
Obfuscated Tiny C Compiler
Copyright (C) 2001-2003 Fabrice Bellard
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product and its documentation
*is* required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include <ctype.h>
#include <dlfcn.h>
#include <stdarg.h>
@ -59,6 +36,8 @@
#include "disassem.h"
#endif
#include <acc/acc.h>
namespace acc {
class Compiler {
@ -116,6 +95,27 @@ class Compiler {
}
};
/**
* A code generator creates an in-memory program, generating the code on
* the fly. There is one code generator implementation for each supported
* architecture.
*
* The code generator implements the following abstract machine:
* R0 - the main accumulator.
* R1 - the secondary accumulator.
* FP - a frame pointer for accessing function arguments and local
* variables.
* SP - a stack pointer for storing intermediate results while evaluating
* expressions. The stack pointer grows downwards.
*
* The function calling convention is that all arguments are placed on the
* stack such that the first argument has the lowest address.
* After the call, the result is in R0. The caller is responsible for
* removing the arguments from the stack.
* The R0 and R1 registers are not saved across function calls. The
* FP and SP registers are saved.
*/
class CodeGenerator {
public:
CodeGenerator() {}
@ -125,70 +125,170 @@ class Compiler {
this->pCodeBuf = pCodeBuf;
}
/* returns address to patch with local variable size
/* Emit a function prolog.
* argCount is the number of arguments.
* Save the old value of the FP.
* Set the new value of the FP.
* Convert from the native platform calling convention to
* our stack-based calling convention. This may require
* pushing arguments from registers to the stack.
* Allocate "N" bytes of stack space. N isn't known yet, so
* just emit the instructions for adjusting the stack, and return
* the address to patch up. The patching will be done in
* functionExit().
* returns address to patch with local variable size.
*/
virtual int functionEntry(int argCount) = 0;
virtual void functionExit(int argCount, int localVariableAddress, int localVariableSize) = 0;
/* Emit a function epilog.
* Restore the old SP and FP register values.
* Return to the calling function.
* argCount - the number of arguments to the function.
* localVariableAddress - returned from functionEntry()
* localVariableSize - the size in bytes of the local variables.
*/
virtual void functionExit(int argCount, int localVariableAddress,
int localVariableSize) = 0;
/* load immediate value */
/* load immediate value to R0 */
virtual void li(int t) = 0;
/* Jump to a target, and return the address of the word that
* holds the target data, in case it needs to be fixed up later.
*/
virtual int gjmp(int t) = 0;
/* l = 0: je, l == 1: jne */
/* Test R0 and jump to a target if the test succeeds.
* l = 0: je, l == 1: jne
* Return the address of the word that holds the targed data, in
* case it needs to be fixed up later.
*/
virtual int gtst(bool l, int t) = 0;
/* Compare R1 against R0, and store the boolean result in R0.
* op specifies the comparison.
*/
virtual void gcmp(int op) = 0;
/* Perform the arithmetic op specified by op. R1 is the
* left argument, R0 is the right argument.
*/
virtual void genOp(int op) = 0;
virtual void clearECX() = 0;
/* Set R1 to 0.
*/
virtual void clearR1() = 0;
virtual void pushEAX() = 0;
/* Push R0 onto the stack.
*/
virtual void pushR0() = 0;
virtual void popECX() = 0;
/* Pop R1 off of the stack.
*/
virtual void popR1() = 0;
virtual void storeEAXToAddressECX(bool isInt) = 0;
/* Store R0 to the address stored in R1.
* isInt is true if a whole 4-byte integer value
* should be stored, otherwise a 1-byte character
* value should be stored.
*/
virtual void storeR0ToR1(bool isInt) = 0;
virtual void loadEAXIndirect(bool isInt) = 0;
/* Load R0 from the address stored in R0.
* isInt is true if a whole 4-byte integer value
* should be loaded, otherwise a 1-byte character
* value should be loaded.
*/
virtual void loadR0FromR0(bool isInt) = 0;
virtual void leaEAX(int ea) = 0;
/* Load the absolute address of a variable to R0.
* If ea <= LOCAL, then this is a local variable, or an
* argument, addressed relative to FP.
* else it is an absolute global address.
*/
virtual void leaR0(int ea) = 0;
virtual void storeEAX(int ea) = 0;
/* Store R0 to a variable.
* If ea <= LOCAL, then this is a local variable, or an
* argument, addressed relative to FP.
* else it is an absolute global address.
*/
virtual void storeR0(int ea) = 0;
virtual void loadEAX(int ea, bool isIncDec, int op) = 0;
/* load R0 from a variable.
* If ea <= LOCAL, then this is a local variable, or an
* argument, addressed relative to FP.
* else it is an absolute global address.
* If isIncDec is true, then the stored variable's value
* should be post-incremented or post-decremented, based
* on the value of op.
*/
virtual void loadR0(int ea, bool isIncDec, int op) = 0;
/* Emit code to adjust the stack for a function call. Return the
* label for the address of the instruction that adjusts the
* stack size. This will be passed as argument "a" to
* endFunctionCallArguments.
*/
virtual int beginFunctionCallArguments() = 0;
virtual void storeEAToArg(int l) = 0;
/* Emit code to store R0 to the stack at byte offset l.
*/
virtual void storeR0ToArg(int l) = 0;
/* Patch the function call preamble.
* a is the address returned from beginFunctionCallArguments
* l is the number of bytes the arguments took on the stack.
* Typically you would also emit code to convert the argument
* list into whatever the native function calling convention is.
* On ARM for example you would pop the first 5 arguments into
* R0..R4
*/
virtual void endFunctionCallArguments(int a, int l) = 0;
/* Emit a call to an unknown function. The argument "symbol" needs to
* be stored in the location where the address should go. It forms
* a chain. The address will be patched later.
* Return the address of the word that has to be patched.
*/
virtual int callForward(int symbol) = 0;
/* Call a function using PC-relative addressing. t is the PC-relative
* address of the function. It has already been adjusted for the
* architectural jump offset, so just store it as-is.
*/
virtual void callRelative(int t) = 0;
/* Call a function pointer. L is the number of bytes the arguments
* take on the stack. The address of the function is stored at
* location SP + l.
*/
virtual void callIndirect(int l) = 0;
/* Adjust SP after returning from a function call. l is the
* number of bytes of arguments stored on the stack. isIndirect
* is true if this was an indirect call. (In which case the
* address of the function is stored at location SP + l.)
*/
virtual void adjustStackAfterCall(int l, bool isIndirect) = 0;
/* Print a disassembly of the assembled code to out. Return
* non-zero if there is an error.
*/
virtual int disassemble(FILE* out) = 0;
/* output a symbol and patch all calls to it */
/* Generate a symbol at the current PC. t is the head of a
* linked list of addresses to patch.
*/
virtual void gsym(int t) = 0;
virtual int finishCompile() {
#if defined(__arm__)
const long base = long(pCodeBuf->getBase());
const long curr = base + long(pCodeBuf->getSize());
int err = cacheflush(base, curr, 0);
return err;
#else
return 0;
#endif
}
/*
* Do any cleanup work required at the end of a compile.
* For example, an instruction cache might need to be
* invalidated.
* Return non-zero if there is an error.
*/
virtual int finishCompile() = 0;
/**
* Adjust relative branches by this amount.
@ -214,6 +314,10 @@ class Compiler {
intptr_t getPC() {
return pCodeBuf->getPC();
}
intptr_t getSize() {
return pCodeBuf->getSize();
}
private:
CodeBuf* pCodeBuf;
};
@ -378,23 +482,23 @@ class Compiler {
#endif
}
virtual void clearECX() {
fprintf(stderr, "clearECX();\n");
virtual void clearR1() {
fprintf(stderr, "clearR1();\n");
o4(0xE3A01000); // mov r1, #0
}
virtual void pushEAX() {
fprintf(stderr, "pushEAX();\n");
virtual void pushR0() {
fprintf(stderr, "pushR0();\n");
o4(0xE92D0001); // stmfd sp!,{r0}
}
virtual void popECX() {
fprintf(stderr, "popECX();\n");
virtual void popR1() {
fprintf(stderr, "popR1();\n");
o4(0xE8BD0002); // ldmfd sp!,{r1}
}
virtual void storeEAXToAddressECX(bool isInt) {
fprintf(stderr, "storeEAXToAddressECX(%d);\n", isInt);
virtual void storeR0ToR1(bool isInt) {
fprintf(stderr, "storeR0ToR1(%d);\n", isInt);
if (isInt) {
o4(0xE5810000); // str r0, [r1]
} else {
@ -402,16 +506,16 @@ class Compiler {
}
}
virtual void loadEAXIndirect(bool isInt) {
fprintf(stderr, "loadEAXIndirect(%d);\n", isInt);
virtual void loadR0FromR0(bool isInt) {
fprintf(stderr, "loadR0FromR0(%d);\n", isInt);
if (isInt)
o4(0xE5900000); // ldr r0, [r0]
else
o4(0xE5D00000); // ldrb r0, [r0]
}
virtual void leaEAX(int ea) {
fprintf(stderr, "leaEAX(%d);\n", ea);
virtual void leaR0(int ea) {
fprintf(stderr, "leaR0(%d);\n", ea);
if (ea < LOCAL) {
// Local, fp relative
if (ea < -1023 || ea > 1023 || ((ea & 3) != 0)) {
@ -431,8 +535,8 @@ class Compiler {
}
}
virtual void storeEAX(int ea) {
fprintf(stderr, "storeEAX(%d);\n", ea);
virtual void storeR0(int ea) {
fprintf(stderr, "storeR0(%d);\n", ea);
if (ea < LOCAL) {
// Local, fp relative
if (ea < -4095 || ea > 4095) {
@ -452,8 +556,8 @@ class Compiler {
}
}
virtual void loadEAX(int ea, bool isIncDec, int op) {
fprintf(stderr, "loadEAX(%d, %d, %d);\n", ea, isIncDec, op);
virtual void loadR0(int ea, bool isIncDec, int op) {
fprintf(stderr, "loadR0(%d, %d, %d);\n", ea, isIncDec, op);
if (ea < LOCAL) {
// Local, fp relative
if (ea < -4095 || ea > 4095) {
@ -504,8 +608,8 @@ class Compiler {
return o4(0xE24DDF00); // Placeholder
}
virtual void storeEAToArg(int l) {
fprintf(stderr, "storeEAToArg(%d);\n", l);
virtual void storeR0ToArg(int l) {
fprintf(stderr, "storeR0ToArg(%d);\n", l);
if (l < 0 || l > 4096-4) {
error("l out of range for stack offset: 0x%08x", l);
}
@ -597,6 +701,17 @@ class Compiler {
}
}
virtual int finishCompile() {
#if defined(__arm__)
const long base = long(getBase());
const long curr = long(getPC());
int err = cacheflush(base, curr, 0);
return err;
#else
return 0;
#endif
}
virtual int disassemble(FILE* out) {
disasmOut = out;
disasm_interface_t di;
@ -730,23 +845,23 @@ class Compiler {
o(0x92); /* xchg %edx, %eax */
}
virtual void clearECX() {
virtual void clearR1() {
oad(0xb9, 0); /* movl $0, %ecx */
}
virtual void pushEAX() {
virtual void pushR0() {
o(0x50); /* push %eax */
}
virtual void popECX() {
virtual void popR1() {
o(0x59); /* pop %ecx */
}
virtual void storeEAXToAddressECX(bool isInt) {
virtual void storeR0ToR1(bool isInt) {
o(0x0188 + isInt); /* movl %eax/%al, (%ecx) */
}
virtual void loadEAXIndirect(bool isInt) {
virtual void loadR0FromR0(bool isInt) {
if (isInt)
o(0x8b); /* mov (%eax), %eax */
else
@ -754,15 +869,15 @@ class Compiler {
ob(0); /* add zero in code */
}
virtual void leaEAX(int ea) {
virtual void leaR0(int ea) {
gmov(10, ea); /* leal EA, %eax */
}
virtual void storeEAX(int ea) {
virtual void storeR0(int ea) {
gmov(6, ea); /* mov %eax, EA */
}
virtual void loadEAX(int ea, bool isIncDec, int op) {
virtual void loadR0(int ea, bool isIncDec, int op) {
gmov(8, ea); /* mov EA, %eax */
if (isIncDec) {
/* Implement post-increment or post decrement.
@ -776,7 +891,7 @@ class Compiler {
return oad(0xec81, 0); /* sub $xxx, %esp */
}
virtual void storeEAToArg(int l) {
virtual void storeR0ToArg(int l) {
oad(0x248489, l); /* movl %eax, xxx(%esp) */
}
@ -808,7 +923,7 @@ class Compiler {
}
virtual int disassemble(FILE* out) {
return 1;
return 0;
}
/* output a symbol and patch all calls to it */
@ -822,6 +937,10 @@ class Compiler {
}
}
virtual int finishCompile() {
return 0;
}
private:
/** Output 1 to 4 bytes.
@ -868,6 +987,39 @@ class Compiler {
#endif // PROVIDE_X86_CODEGEN
class InputStream {
public:
virtual int get() = 0;
virtual long tell() = 0;
};
class FileInputStream : public InputStream {
public:
FileInputStream(FILE* in) : f(in) {}
virtual int get() { return fgetc(f); }
virtual long tell() { return ftell(f); }
private:
FILE* f;
};
class TextInputStream : public InputStream {
public:
TextInputStream(const char* text, size_t textLength)
: pText(text), mTextLength(textLength), mPosition(0) {
}
virtual int get() {
return mPosition < mTextLength ? pText[mPosition++] : EOF;
}
virtual long tell() {
return mPosition;
}
private:
const char* pText;
size_t mTextLength;
size_t mPosition;
};
/* vars: value of variables
loc : local variable index
glo : global variable index
@ -882,7 +1034,8 @@ class Compiler {
void* pSymbolBase;
void* pGlobalBase;
void* pVarsBase;
FILE* file;
InputStream* file;
CodeBuf codeBuf;
CodeGenerator* pGen;
@ -957,7 +1110,7 @@ class Compiler {
ch = dch;
}
} else
ch = fgetc(file);
ch = file->get();
/* printf("ch=%c 0x%x\n", ch, ch); */
}
@ -1108,7 +1261,7 @@ class Compiler {
va_list ap;
va_start(ap, fmt);
fprintf(stderr, "%ld: ", ftell((FILE *) file));
fprintf(stderr, "%ld: ", file->tell());
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
@ -1149,7 +1302,7 @@ class Compiler {
} else if (c == 2) {
/* -, +, !, ~ */
unary(0);
pGen->clearECX();
pGen->clearR1();
if (t == '!')
pGen->gcmp(a);
else
@ -1175,15 +1328,15 @@ class Compiler {
unary(0);
if (tok == '=') {
next();
pGen->pushEAX();
pGen->pushR0();
expr();
pGen->popECX();
pGen->storeEAXToAddressECX(t == TOK_INT);
pGen->popR1();
pGen->storeR0ToR1(t == TOK_INT);
} else if (t) {
pGen->loadEAXIndirect(t == TOK_INT);
pGen->loadR0FromR0(t == TOK_INT);
}
} else if (t == '&') {
pGen->leaEAX(*(int *) tok);
pGen->leaR0(*(int *) tok);
next();
} else {
n = *(int *) t;
@ -1195,10 +1348,10 @@ class Compiler {
/* assignment */
next();
expr();
pGen->storeEAX(n);
pGen->storeR0(n);
} else if (tok != '(') {
/* variable */
pGen->loadEAX(n, tokl == 11, tokc);
pGen->loadR0(n, tokl == 11, tokc);
if (tokl == 11) {
next();
}
@ -1209,7 +1362,7 @@ class Compiler {
/* function call */
if (tok == '(') {
if (n == 1)
pGen->pushEAX();
pGen->pushR0();
/* push args and invert order */
a = pGen->beginFunctionCallArguments();
@ -1217,7 +1370,7 @@ class Compiler {
l = 0;
while (tok != ')') {
expr();
pGen->storeEAToArg(l);
pGen->storeR0ToArg(l);
if (tok == ',')
next();
l = l + 4;
@ -1255,9 +1408,9 @@ class Compiler {
a = pGen->gtst(t == OP_LOGICAL_OR, a); /* && and || output code generation */
sum(l);
} else {
pGen->pushEAX();
pGen->pushR0();
sum(l);
pGen->popECX();
pGen->popR1();
if ((l == 4) | (l == 5)) {
pGen->gcmp(t);
@ -1420,6 +1573,10 @@ class Compiler {
delete pGen;
pGen = 0;
}
if (file) {
delete file;
file = 0;
}
}
void clear() {
@ -1470,7 +1627,7 @@ class Compiler {
#endif
}
if (pGen == NULL) {
fprintf(stderr, "No code generator defined.");
fprintf(stderr, "No code generator defined.\n");
}
}
@ -1490,16 +1647,16 @@ public:
cleanup();
}
int compile(FILE* in, args& args) {
int compile(const char* text, size_t textLength) {
cleanup();
clear();
codeBuf.init(ALLOC_SIZE);
setArchitecture(args.architecture);
setArchitecture(NULL);
if (!pGen) {
return -1;
}
pGen->init(&codeBuf);
file = in;
file = new TextInputStream(text, textLength);
sym_stk = (intptr_t) calloc(1, ALLOC_SIZE);
dstk = (intptr_t) strcpy((char*) sym_stk,
" int if else while break return for define main ")
@ -1534,6 +1691,38 @@ public:
return pGen->disassemble(out);
}
/* Look through the symbol table to find a symbol.
* If found, return its value.
*/
void* lookup(const char* name) {
if (!sym_stk) {
return NULL;
}
size_t nameLen = strlen(name);
char* pSym = (char*) sym_stk;
char c;
for(;;) {
c = *pSym++;
if (c == 0) {
break;
}
if (c == TAG_TOK) {
if (memcmp(pSym, name, nameLen) == 0
&& pSym[nameLen] == TAG_TOK) {
int tok = pSym - 1 - (char*) sym_stk;
tok = tok * 8 + TOK_IDENT;
if (tok <= TOK_DEFINE) {
return 0;
} else {
tok = vars + tok;
return * (void**) tok;
}
}
}
}
return NULL;
}
};
const char* Compiler::operatorChars =
@ -1578,96 +1767,129 @@ const int Compiler::X86CodeGenerator::operatorHelper[] = {
};
#endif
} // namespace acc
struct ACCscript {
ACCscript() {
text = 0;
textLength = 0;
accError = ACC_NO_ERROR;
}
// This is a separate function so it can easily be set by breakpoint in gdb.
int run(acc::Compiler& c, int argc, char** argv) {
return c.run(argc, argv);
}
~ACCscript() {
delete text;
}
int main(int argc, char** argv) {
bool doDump = false;
bool doDisassemble = false;
const char* inFile = NULL;
const char* outFile = NULL;
const char* architecture = NULL;
int i;
for (i = 1; i < argc; i++) {
char* arg = argv[i];
if (arg[0] == '-') {
switch (arg[1]) {
case 'a':
if (i + 1 >= argc) {
fprintf(stderr, "Expected architecture after -a\n");
return 2;
}
architecture = argv[i+1];
i += 1;
break;
case 'd':
if (i + 1 >= argc) {
fprintf(stderr, "Expected filename after -d\n");
return 2;
}
doDump = true;
outFile = argv[i + 1];
i += 1;
break;
case 'S':
doDisassemble = true;
break;
default:
fprintf(stderr, "Unrecognized flag %s\n", arg);
return 3;
}
} else if (inFile == NULL) {
inFile = arg;
} else {
break;
void setError(ACCenum error) {
if (accError == ACC_NO_ERROR && error != ACC_NO_ERROR) {
accError = error;
}
}
FILE* in = stdin;
if (inFile) {
in = fopen(inFile, "r");
if (!in) {
fprintf(stderr, "Could not open input file %s\n", inFile);
return 1;
}
}
acc::Compiler compiler;
acc::Compiler::args args;
if (architecture != NULL) {
args.architecture = architecture;
}
int compileResult = compiler.compile(in, args);
if (in != stdin) {
fclose(in);
}
if (compileResult) {
fprintf(stderr, "Compile failed: %d\n", compileResult);
return 6;
}
if (doDisassemble) {
compiler.disassemble(stderr);
}
if (doDump) {
FILE* save = fopen(outFile, "w");
if (!save) {
fprintf(stderr, "Could not open output file %s\n", outFile);
return 5;
}
compiler.dump(save);
fclose(save);
} else {
fprintf(stderr, "Executing compiled code:\n");
int codeArgc = argc - i + 1;
char** codeArgv = argv + i - 1;
codeArgv[0] = (char*) (inFile ? inFile : "stdin");
int result = run(compiler, codeArgc, codeArgv);
fprintf(stderr, "result: %d\n", result);
ACCenum getError() {
ACCenum result = accError;
accError = ACC_NO_ERROR;
return result;
}
return 0;
Compiler compiler;
char* text;
int textLength;
ACCenum accError;
};
extern "C"
ACCscript* accCreateScript() {
return new ACCscript();
}
extern "C"
ACCenum accGetError( ACCscript* script ) {
return script->getError();
}
extern "C"
void accDeleteScript(ACCscript* script) {
delete script;
}
extern "C"
void accScriptSource(ACCscript* script,
ACCsizei count,
const ACCchar ** string,
const ACCint * length) {
int totalLength = 0;
for(int i = 0; i < count; i++) {
int len = -1;
const ACCchar* s = string[i];
if (length) {
len = length[i];
}
if (len < 0) {
len = strlen(s);
}
totalLength += len;
}
delete script->text;
char* text = new char[totalLength + 1];
script->text = text;
script->textLength = totalLength;
for(int i = 0; i < count; i++) {
int len = -1;
const ACCchar* s = string[i];
if (length) {
len = length[i];
}
if (len < 0) {
len = strlen(s);
}
memcpy(text, s, len);
text += len;
}
text[totalLength] = '\0';
}
extern "C"
void accCompileScript(ACCscript* script) {
int result = script->compiler.compile(script->text, script->textLength);
if (result) {
script->setError(ACC_INVALID_OPERATION);
}
}
extern "C"
void accGetScriptiv(ACCscript* script,
ACCenum pname,
ACCint * params) {
switch (pname) {
case ACC_INFO_LOG_LENGTH:
*params = 0;
break;
}
}
extern "C"
void accGetScriptInfoLog(ACCscript* script,
ACCsizei maxLength,
ACCsizei * length,
ACCchar * infoLog) {
if (length) {
*length = 0;
}
if (maxLength > 0 && infoLog) {
*infoLog = 0;
}
}
extern "C"
void accGetScriptLabel(ACCscript* script, const ACCchar * name,
ACCvoid ** address) {
void* value = script->compiler.lookup(name);
if (value) {
*address = value;
} else {
script->setError(ACC_INVALID_VALUE);
}
}
} // namespace acc

View File

@ -1,13 +0,0 @@
#!/bin/sh
rm -f tests/acc
g++ acc.cpp disassem.cpp -g -ldl -o tests/acc && tests/acc tests/otcc.c -a x86 -d tests/otcc.out && diff tests/otcc.out tests/otcc.out-orig
if [ -x "tests/acc" ]; then
tests/acc -S tests/returnval.c
if [ "$(uname)" = "Linux" ]; then
if [ "$(uname -m)" = "i686" ]; then
echo "Linux i686. Testing otcc.c"
tests/acc tests/otcc.c tests/otcc.c tests/returnval.c
fi
fi
fi

View File

@ -1,4 +0,0 @@
#!/bin/sh
adb remount
adb push tests/returnval.c /system/bin/returnval.c
mm -j8 && adb sync && adb shell /system/bin/acc -S /system/bin/returnval.c

View File

@ -1,2 +1,2 @@
acc
test-acc
*.out

15
libacc/tests/Android.mk Normal file
View File

@ -0,0 +1,15 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
main.cpp
LOCAL_SHARED_LIBRARIES := \
libacc
LOCAL_MODULE:= acc
LOCAL_MODULE_TAGS := tests
include $(BUILD_EXECUTABLE)

Binary file not shown.

102
libacc/tests/main.cpp Normal file
View File

@ -0,0 +1,102 @@
/*
* Android "Almost" C Compiler.
* This is a compiler for a small subset of the C language, intended for use
* in scripting environments where speed and memory footprint are important.
*
* This code is based upon the "unobfuscated" version of the
* Obfuscated Tiny C compiler, see the file LICENSE for details.
*
*/
#include <ctype.h>
#include <dlfcn.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(__arm__)
#include <unistd.h>
#endif
#include <acc/acc.h>
typedef int (*MainPtr)(int, char**);
// This is a separate function so it can easily be set by breakpoint in gdb.
int run(MainPtr mainFunc, int argc, char** argv) {
return mainFunc(argc, argv);
}
int main(int argc, char** argv) {
const char* inFile = NULL;
bool printListing;
FILE* in = stdin;
int i;
for (i = 1; i < argc; i++) {
char* arg = argv[i];
if (arg[0] == '-') {
switch (arg[1]) {
case 'S':
printListing = true;
break;
default:
fprintf(stderr, "Unrecognized flag %s\n", arg);
return 3;
}
} else if (inFile == NULL) {
inFile = arg;
} else {
break;
}
}
if (! inFile) {
fprintf(stderr, "input file required\n");
return 2;
}
if (inFile) {
in = fopen(inFile, "r");
if (!in) {
fprintf(stderr, "Could not open input file %s\n", inFile);
return 1;
}
}
fseek(in, 0, SEEK_END);
size_t fileSize = (size_t) ftell(in);
rewind(in);
ACCchar* text = new ACCchar[fileSize];
size_t bytesRead = fread(text, 1, fileSize, in);
if (bytesRead != fileSize) {
fprintf(stderr, "Could not read all of file %s\n", inFile);
}
ACCscript* script = accCreateScript();
const ACCchar* scriptSource[] = {text};
accScriptSource(script, 1, scriptSource, NULL);
delete[] text;
accCompileScript(script);
MainPtr mainPointer = 0;
accGetScriptLabel(script, "main", (ACCvoid**) & mainPointer);
int result = accGetError(script);
if (result == ACC_NO_ERROR) {
fprintf(stderr, "Executing compiled code:\n");
int codeArgc = argc - i + 1;
char** codeArgv = argv + i - 1;
codeArgv[0] = (char*) (inFile ? inFile : "stdin");
result = run(mainPointer, codeArgc, codeArgv);
fprintf(stderr, "result: %d\n", result);
}
accDeleteScript(script);
return result;
}

Binary file not shown.

9
libacc/tests/testarm Executable file
View File

@ -0,0 +1,9 @@
#!/bin/sh
adb remount
adb shell rm /system/bin/acc
adb push data/returnval.c /system/bin/returnval.c
cd ..
mm -j8
cd tests
adb sync
adb shell /system/bin/acc -S /system/bin/returnval.c

15
libacc/tests/testlocal Executable file
View File

@ -0,0 +1,15 @@
#!/bin/sh
rm -f test-acc
cd ..
g++ -I../include acc.cpp disassem.cpp tests/main.cpp -g -ldl -o tests/test-acc
cd tests
if [ -x "test-acc" ]; then
./test-acc -S data/returnval.c
if [ "$(uname)" = "Linux" ]; then
if [ "$(uname -m)" = "i686" ]; then
echo "Linux i686. Testing otcc.c"
./test-acc data/otcc.c data/otcc.c data/returnval.c
fi
fi
fi