|  | // Copyright 2009 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | /* Translate a .cgo file into a .c file.  A .cgo file is a combination | 
|  | of a limited form of Go with C.  */ | 
|  |  | 
|  | /* | 
|  | package PACKAGENAME | 
|  | {# line} | 
|  | func NAME([NAME TYPE { , NAME TYPE }]) [(NAME TYPE { , NAME TYPE })] \{ | 
|  | C code with proper brace nesting | 
|  | \} | 
|  | */ | 
|  |  | 
|  | /* We generate C code which implements the function such that it can | 
|  | be called from Go and executes the C code.  */ | 
|  |  | 
|  | #include <assert.h> | 
|  | #include <ctype.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <errno.h> | 
|  |  | 
|  | /* Whether we're emitting for gcc */ | 
|  | static int gcc; | 
|  |  | 
|  | /* File and line number */ | 
|  | static const char *file; | 
|  | static unsigned int lineno; | 
|  |  | 
|  | /* List of names and types.  */ | 
|  | struct params { | 
|  | struct params *next; | 
|  | char *name; | 
|  | char *type; | 
|  | }; | 
|  |  | 
|  | /* index into type_table */ | 
|  | enum { | 
|  | Bool, | 
|  | Float, | 
|  | Int, | 
|  | Uint, | 
|  | Uintptr, | 
|  | String, | 
|  | Slice, | 
|  | Eface, | 
|  | }; | 
|  |  | 
|  | static struct { | 
|  | char *name; | 
|  | int size; | 
|  | } type_table[] = { | 
|  | /* variable sized first, for easy replacement */ | 
|  | /* order matches enum above */ | 
|  | /* default is 32-bit architecture sizes */ | 
|  | "bool",		1, | 
|  | "float",	4, | 
|  | "int",		4, | 
|  | "uint",		4, | 
|  | "uintptr",	4, | 
|  | "String",	8, | 
|  | "Slice",	12, | 
|  | "Eface",	8, | 
|  |  | 
|  | /* fixed size */ | 
|  | "float32",	4, | 
|  | "float64",	8, | 
|  | "byte",		1, | 
|  | "int8",		1, | 
|  | "uint8",	1, | 
|  | "int16",	2, | 
|  | "uint16",	2, | 
|  | "int32",	4, | 
|  | "uint32",	4, | 
|  | "int64",	8, | 
|  | "uint64",	8, | 
|  |  | 
|  | NULL, | 
|  | }; | 
|  |  | 
|  | /* Fixed structure alignment (non-gcc only) */ | 
|  | int structround = 4; | 
|  |  | 
|  | /* Unexpected EOF.  */ | 
|  | static void | 
|  | bad_eof(void) | 
|  | { | 
|  | fprintf(stderr, "%s:%u: unexpected EOF\n", file, lineno); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | /* Out of memory.  */ | 
|  | static void | 
|  | bad_mem(void) | 
|  | { | 
|  | fprintf(stderr, "%s:%u: out of memory\n", file, lineno); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | /* Allocate memory without fail.  */ | 
|  | static void * | 
|  | xmalloc(unsigned int size) | 
|  | { | 
|  | void *ret = malloc(size); | 
|  | if (ret == NULL) | 
|  | bad_mem(); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Reallocate memory without fail.  */ | 
|  | static void* | 
|  | xrealloc(void *buf, unsigned int size) | 
|  | { | 
|  | void *ret = realloc(buf, size); | 
|  | if (ret == NULL) | 
|  | bad_mem(); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Free a list of parameters.  */ | 
|  | static void | 
|  | free_params(struct params *p) | 
|  | { | 
|  | while (p != NULL) { | 
|  | struct params *next; | 
|  |  | 
|  | next = p->next; | 
|  | free(p->name); | 
|  | free(p->type); | 
|  | free(p); | 
|  | p = next; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Read a character, tracking lineno.  */ | 
|  | static int | 
|  | getchar_update_lineno(void) | 
|  | { | 
|  | int c; | 
|  |  | 
|  | c = getchar(); | 
|  | if (c == '\n') | 
|  | ++lineno; | 
|  | return c; | 
|  | } | 
|  |  | 
|  | /* Read a character, giving an error on EOF, tracking lineno.  */ | 
|  | static int | 
|  | getchar_no_eof(void) | 
|  | { | 
|  | int c; | 
|  |  | 
|  | c = getchar_update_lineno(); | 
|  | if (c == EOF) | 
|  | bad_eof(); | 
|  | return c; | 
|  | } | 
|  |  | 
|  | /* Read a character, skipping comments.  */ | 
|  | static int | 
|  | getchar_skipping_comments(void) | 
|  | { | 
|  | int c; | 
|  |  | 
|  | while (1) { | 
|  | c = getchar_update_lineno(); | 
|  | if (c != '/') | 
|  | return c; | 
|  |  | 
|  | c = getchar(); | 
|  | if (c == '/') { | 
|  | do { | 
|  | c = getchar_update_lineno(); | 
|  | } while (c != EOF && c != '\n'); | 
|  | return c; | 
|  | } else if (c == '*') { | 
|  | while (1) { | 
|  | c = getchar_update_lineno(); | 
|  | if (c == EOF) | 
|  | return EOF; | 
|  | if (c == '*') { | 
|  | do { | 
|  | c = getchar_update_lineno(); | 
|  | } while (c == '*'); | 
|  | if (c == '/') | 
|  | break; | 
|  | } | 
|  | } | 
|  | } else { | 
|  | ungetc(c, stdin); | 
|  | return '/'; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Read and return a token.  Tokens are delimited by whitespace or by | 
|  | [(),{}].  The latter are all returned as single characters.  */ | 
|  | static char * | 
|  | read_token(void) | 
|  | { | 
|  | int c; | 
|  | char *buf; | 
|  | unsigned int alc, off; | 
|  | const char* delims = "(),{}"; | 
|  |  | 
|  | while (1) { | 
|  | c = getchar_skipping_comments(); | 
|  | if (c == EOF) | 
|  | return NULL; | 
|  | if (!isspace(c)) | 
|  | break; | 
|  | } | 
|  | alc = 16; | 
|  | buf = xmalloc(alc + 1); | 
|  | off = 0; | 
|  | if (strchr(delims, c) != NULL) { | 
|  | buf[off] = c; | 
|  | ++off; | 
|  | } else { | 
|  | while (1) { | 
|  | if (off >= alc) { | 
|  | alc *= 2; | 
|  | buf = xrealloc(buf, alc + 1); | 
|  | } | 
|  | buf[off] = c; | 
|  | ++off; | 
|  | c = getchar_skipping_comments(); | 
|  | if (c == EOF) | 
|  | break; | 
|  | if (isspace(c) || strchr(delims, c) != NULL) { | 
|  | if (c == '\n') | 
|  | lineno--; | 
|  | ungetc(c, stdin); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | buf[off] = '\0'; | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | /* Read a token, giving an error on EOF.  */ | 
|  | static char * | 
|  | read_token_no_eof(void) | 
|  | { | 
|  | char *token = read_token(); | 
|  | if (token == NULL) | 
|  | bad_eof(); | 
|  | return token; | 
|  | } | 
|  |  | 
|  | /* Read the package clause, and return the package name.  */ | 
|  | static char * | 
|  | read_package(void) | 
|  | { | 
|  | char *token; | 
|  |  | 
|  | token = read_token_no_eof(); | 
|  | if (strcmp(token, "package") != 0) { | 
|  | fprintf(stderr, | 
|  | "%s:%u: expected \"package\", got \"%s\"\n", | 
|  | file, lineno, token); | 
|  | exit(1); | 
|  | } | 
|  | return read_token_no_eof(); | 
|  | } | 
|  |  | 
|  | /* Read and copy preprocessor lines.  */ | 
|  | static void | 
|  | read_preprocessor_lines(void) | 
|  | { | 
|  | while (1) { | 
|  | int c; | 
|  |  | 
|  | do { | 
|  | c = getchar_skipping_comments(); | 
|  | } while (isspace(c)); | 
|  | if (c != '#') { | 
|  | ungetc(c, stdin); | 
|  | break; | 
|  | } | 
|  | putchar(c); | 
|  | do { | 
|  | c = getchar_update_lineno(); | 
|  | putchar(c); | 
|  | } while (c != '\n'); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Read a type in Go syntax and return a type in C syntax.  We only | 
|  | permit basic types and pointers.  */ | 
|  | static char * | 
|  | read_type(void) | 
|  | { | 
|  | char *p, *op, *q; | 
|  | int pointer_count; | 
|  | unsigned int len; | 
|  |  | 
|  | p = read_token_no_eof(); | 
|  | if (*p != '*') | 
|  | return p; | 
|  | op = p; | 
|  | pointer_count = 0; | 
|  | while (*p == '*') { | 
|  | ++pointer_count; | 
|  | ++p; | 
|  | } | 
|  | len = strlen(p); | 
|  | q = xmalloc(len + pointer_count + 1); | 
|  | memcpy(q, p, len); | 
|  | while (pointer_count > 0) { | 
|  | q[len] = '*'; | 
|  | ++len; | 
|  | --pointer_count; | 
|  | } | 
|  | q[len] = '\0'; | 
|  | free(op); | 
|  | return q; | 
|  | } | 
|  |  | 
|  | /* Return the size of the given type. */ | 
|  | static int | 
|  | type_size(char *p) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | if(p[strlen(p)-1] == '*') | 
|  | return type_table[Uintptr].size; | 
|  |  | 
|  | for(i=0; type_table[i].name; i++) | 
|  | if(strcmp(type_table[i].name, p) == 0) | 
|  | return type_table[i].size; | 
|  | fprintf(stderr, "%s:%u: unknown type %s\n", file, lineno, p); | 
|  | exit(1); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Read a list of parameters.  Each parameter is a name and a type. | 
|  | The list ends with a ')'.  We have already read the '('.  */ | 
|  | static struct params * | 
|  | read_params(int *poffset) | 
|  | { | 
|  | char *token; | 
|  | struct params *ret, **pp, *p; | 
|  | int offset, size, rnd; | 
|  |  | 
|  | ret = NULL; | 
|  | pp = &ret; | 
|  | token = read_token_no_eof(); | 
|  | offset = 0; | 
|  | if (strcmp(token, ")") != 0) { | 
|  | while (1) { | 
|  | p = xmalloc(sizeof(struct params)); | 
|  | p->name = token; | 
|  | p->type = read_type(); | 
|  | p->next = NULL; | 
|  | *pp = p; | 
|  | pp = &p->next; | 
|  |  | 
|  | size = type_size(p->type); | 
|  | rnd = size; | 
|  | if(rnd > structround) | 
|  | rnd = structround; | 
|  | if(offset%rnd) | 
|  | offset += rnd - offset%rnd; | 
|  | offset += size; | 
|  |  | 
|  | token = read_token_no_eof(); | 
|  | if (strcmp(token, ",") != 0) | 
|  | break; | 
|  | token = read_token_no_eof(); | 
|  | } | 
|  | } | 
|  | if (strcmp(token, ")") != 0) { | 
|  | fprintf(stderr, "%s:%u: expected '('\n", | 
|  | file, lineno); | 
|  | exit(1); | 
|  | } | 
|  | if (poffset != NULL) | 
|  | *poffset = offset; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Read a function header.  This reads up to and including the initial | 
|  | '{' character.  Returns 1 if it read a header, 0 at EOF.  */ | 
|  | static int | 
|  | read_func_header(char **name, struct params **params, int *paramwid, struct params **rets) | 
|  | { | 
|  | int lastline; | 
|  | char *token; | 
|  |  | 
|  | lastline = -1; | 
|  | while (1) { | 
|  | token = read_token(); | 
|  | if (token == NULL) | 
|  | return 0; | 
|  | if (strcmp(token, "func") == 0) { | 
|  | if(lastline != -1) | 
|  | printf("\n"); | 
|  | break; | 
|  | } | 
|  | if (lastline != lineno) { | 
|  | if (lastline == lineno-1) | 
|  | printf("\n"); | 
|  | else | 
|  | printf("\n#line %d \"%s\"\n", lineno, file); | 
|  | lastline = lineno; | 
|  | } | 
|  | printf("%s ", token); | 
|  | } | 
|  |  | 
|  | *name = read_token_no_eof(); | 
|  |  | 
|  | token = read_token(); | 
|  | if (token == NULL || strcmp(token, "(") != 0) { | 
|  | fprintf(stderr, "%s:%u: expected \"(\"\n", | 
|  | file, lineno); | 
|  | exit(1); | 
|  | } | 
|  | *params = read_params(paramwid); | 
|  |  | 
|  | token = read_token(); | 
|  | if (token == NULL || strcmp(token, "(") != 0) | 
|  | *rets = NULL; | 
|  | else { | 
|  | *rets = read_params(NULL); | 
|  | token = read_token(); | 
|  | } | 
|  | if (token == NULL || strcmp(token, "{") != 0) { | 
|  | fprintf(stderr, "%s:%u: expected \"{\"\n", | 
|  | file, lineno); | 
|  | exit(1); | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* Write out parameters.  */ | 
|  | static void | 
|  | write_params(struct params *params, int *first) | 
|  | { | 
|  | struct params *p; | 
|  |  | 
|  | for (p = params; p != NULL; p = p->next) { | 
|  | if (*first) | 
|  | *first = 0; | 
|  | else | 
|  | printf(", "); | 
|  | printf("%s %s", p->type, p->name); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Write a 6g function header.  */ | 
|  | static void | 
|  | write_6g_func_header(char *package, char *name, struct params *params, | 
|  | int paramwid, struct params *rets) | 
|  | { | 
|  | int first, n; | 
|  |  | 
|  | printf("void\n%s·%s(", package, name); | 
|  | first = 1; | 
|  | write_params(params, &first); | 
|  |  | 
|  | /* insert padding to align output struct */ | 
|  | if(rets != NULL && paramwid%structround != 0) { | 
|  | n = structround - paramwid%structround; | 
|  | if(n & 1) | 
|  | printf(", uint8"); | 
|  | if(n & 2) | 
|  | printf(", uint16"); | 
|  | if(n & 4) | 
|  | printf(", uint32"); | 
|  | } | 
|  |  | 
|  | write_params(rets, &first); | 
|  | printf(")\n{\n"); | 
|  | } | 
|  |  | 
|  | /* Write a 6g function trailer.  */ | 
|  | static void | 
|  | write_6g_func_trailer(struct params *rets) | 
|  | { | 
|  | struct params *p; | 
|  |  | 
|  | for (p = rets; p != NULL; p = p->next) | 
|  | printf("\tFLUSH(&%s);\n", p->name); | 
|  | printf("}\n"); | 
|  | } | 
|  |  | 
|  | /* Define the gcc function return type if necessary.  */ | 
|  | static void | 
|  | define_gcc_return_type(char *package, char *name, struct params *rets) | 
|  | { | 
|  | struct params *p; | 
|  |  | 
|  | if (rets == NULL || rets->next == NULL) | 
|  | return; | 
|  | printf("struct %s_%s_ret {\n", package, name); | 
|  | for (p = rets; p != NULL; p = p->next) | 
|  | printf("  %s %s;\n", p->type, p->name); | 
|  | printf("};\n"); | 
|  | } | 
|  |  | 
|  | /* Write out the gcc function return type.  */ | 
|  | static void | 
|  | write_gcc_return_type(char *package, char *name, struct params *rets) | 
|  | { | 
|  | if (rets == NULL) | 
|  | printf("void"); | 
|  | else if (rets->next == NULL) | 
|  | printf("%s", rets->type); | 
|  | else | 
|  | printf("struct %s_%s_ret", package, name); | 
|  | } | 
|  |  | 
|  | /* Write out a gcc function header.  */ | 
|  | static void | 
|  | write_gcc_func_header(char *package, char *name, struct params *params, | 
|  | struct params *rets) | 
|  | { | 
|  | int first; | 
|  | struct params *p; | 
|  |  | 
|  | define_gcc_return_type(package, name, rets); | 
|  | write_gcc_return_type(package, name, rets); | 
|  | printf(" %s_%s(", package, name); | 
|  | first = 1; | 
|  | write_params(params, &first); | 
|  | printf(") asm (\"%s.%s\");\n", package, name); | 
|  | write_gcc_return_type(package, name, rets); | 
|  | printf(" %s_%s(", package, name); | 
|  | first = 1; | 
|  | write_params(params, &first); | 
|  | printf(")\n{\n"); | 
|  | for (p = rets; p != NULL; p = p->next) | 
|  | printf("  %s %s;\n", p->type, p->name); | 
|  | } | 
|  |  | 
|  | /* Write out a gcc function trailer.  */ | 
|  | static void | 
|  | write_gcc_func_trailer(char *package, char *name, struct params *rets) | 
|  | { | 
|  | if (rets == NULL) | 
|  | ; | 
|  | else if (rets->next == NULL) | 
|  | printf("return %s;\n", rets->name); | 
|  | else { | 
|  | struct params *p; | 
|  |  | 
|  | printf("  {\n    struct %s_%s_ret __ret;\n", package, name); | 
|  | for (p = rets; p != NULL; p = p->next) | 
|  | printf("    __ret.%s = %s;\n", p->name, p->name); | 
|  | printf("    return __ret;\n  }\n"); | 
|  | } | 
|  | printf("}\n"); | 
|  | } | 
|  |  | 
|  | /* Write out a function header.  */ | 
|  | static void | 
|  | write_func_header(char *package, char *name, | 
|  | struct params *params, int paramwid, | 
|  | struct params *rets) | 
|  | { | 
|  | if (gcc) | 
|  | write_gcc_func_header(package, name, params, rets); | 
|  | else | 
|  | write_6g_func_header(package, name, params, paramwid, rets); | 
|  | printf("#line %d \"%s\"\n", lineno, file); | 
|  | } | 
|  |  | 
|  | /* Write out a function trailer.  */ | 
|  | static void | 
|  | write_func_trailer(char *package, char *name, | 
|  | struct params *rets) | 
|  | { | 
|  | if (gcc) | 
|  | write_gcc_func_trailer(package, name, rets); | 
|  | else | 
|  | write_6g_func_trailer(rets); | 
|  | } | 
|  |  | 
|  | /* Read and write the body of the function, ending in an unnested } | 
|  | (which is read but not written).  */ | 
|  | static void | 
|  | copy_body(void) | 
|  | { | 
|  | int nesting = 0; | 
|  | while (1) { | 
|  | int c; | 
|  |  | 
|  | c = getchar_no_eof(); | 
|  | if (c == '}' && nesting == 0) | 
|  | return; | 
|  | putchar(c); | 
|  | switch (c) { | 
|  | default: | 
|  | break; | 
|  | case '{': | 
|  | ++nesting; | 
|  | break; | 
|  | case '}': | 
|  | --nesting; | 
|  | break; | 
|  | case '/': | 
|  | c = getchar_update_lineno(); | 
|  | putchar(c); | 
|  | if (c == '/') { | 
|  | do { | 
|  | c = getchar_no_eof(); | 
|  | putchar(c); | 
|  | } while (c != '\n'); | 
|  | } else if (c == '*') { | 
|  | while (1) { | 
|  | c = getchar_no_eof(); | 
|  | putchar(c); | 
|  | if (c == '*') { | 
|  | do { | 
|  | c = getchar_no_eof(); | 
|  | putchar(c); | 
|  | } while (c == '*'); | 
|  | if (c == '/') | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | break; | 
|  | case '"': | 
|  | case '\'': | 
|  | { | 
|  | int delim = c; | 
|  | do { | 
|  | c = getchar_no_eof(); | 
|  | putchar(c); | 
|  | if (c == '\\') { | 
|  | c = getchar_no_eof(); | 
|  | putchar(c); | 
|  | c = '\0'; | 
|  | } | 
|  | } while (c != delim); | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Process the entire file.  */ | 
|  | static void | 
|  | process_file(void) | 
|  | { | 
|  | char *package, *name; | 
|  | struct params *params, *rets; | 
|  | int paramwid; | 
|  |  | 
|  | package = read_package(); | 
|  | read_preprocessor_lines(); | 
|  | while (read_func_header(&name, ¶ms, ¶mwid, &rets)) { | 
|  | write_func_header(package, name, params, paramwid, rets); | 
|  | copy_body(); | 
|  | write_func_trailer(package, name, rets); | 
|  | free(name); | 
|  | free_params(params); | 
|  | free_params(rets); | 
|  | } | 
|  | free(package); | 
|  | } | 
|  |  | 
|  | static void | 
|  | usage(void) | 
|  | { | 
|  | fprintf(stderr, "Usage: cgo2c [--6g | --gc] [file]\n"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | int | 
|  | main(int argc, char **argv) | 
|  | { | 
|  | char *goarch; | 
|  |  | 
|  | while(argc > 1 && argv[1][0] == '-') { | 
|  | if(strcmp(argv[1], "-") == 0) | 
|  | break; | 
|  | if(strcmp(argv[1], "--6g") == 0) | 
|  | gcc = 0; | 
|  | else if(strcmp(argv[1], "--gcc") == 0) | 
|  | gcc = 1; | 
|  | else | 
|  | usage(); | 
|  | argc--; | 
|  | argv++; | 
|  | } | 
|  |  | 
|  | if(argc <= 1 || strcmp(argv[1], "-") == 0) { | 
|  | file = "<stdin>"; | 
|  | process_file(); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if(argc > 2) | 
|  | usage(); | 
|  |  | 
|  | file = argv[1]; | 
|  | if(freopen(file, "r", stdin) == 0) { | 
|  | fprintf(stderr, "open %s: %s\n", file, strerror(errno)); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | if(!gcc) { | 
|  | // 6g etc; update size table | 
|  | goarch = getenv("GOARCH"); | 
|  | if(goarch != NULL && strcmp(goarch, "amd64") == 0) { | 
|  | type_table[Uintptr].size = 8; | 
|  | type_table[String].size = 16; | 
|  | type_table[Slice].size = 8+4+4; | 
|  | type_table[Eface].size = 8+8; | 
|  | structround = 8; | 
|  | } | 
|  | } | 
|  |  | 
|  | process_file(); | 
|  | return 0; | 
|  | } |