📄 jcf-write.c
字号:
/* Write out a Java(TM) class file. Copyright (C) 1998, 1999 Free Software Foundation, Inc.This file is part of GNU CC.GNU CC is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 2, or (at your option)any later version.GNU CC is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with GNU CC; see the file COPYING. If not, write tothe Free Software Foundation, 59 Temple Place - Suite 330,Boston, MA 02111-1307, USA. Java and all Java-based marks are trademarks or registered trademarksof Sun Microsystems, Inc. in the United States and other countries.The Free Software Foundation is independent of Sun Microsystems, Inc. */#include "config.h"#include "system.h"#include "jcf.h"#include "tree.h"#include "java-tree.h"#include "obstack.h"#undef AND#include "rtl.h"#include "flags.h"#include "java-opcodes.h"#include "parse.h" /* for BLOCK_EXPR_BODY */#include "buffer.h"#include "toplev.h"#ifndef DIR_SEPARATOR#define DIR_SEPARATOR '/'#endifextern struct obstack temporary_obstack;/* Base directory in which `.class' files should be written. NULL means to put the file into the same directory as the corresponding .java file. */char *jcf_write_base_directory = NULL;/* Make sure bytecode.data is big enough for at least N more bytes. */#define RESERVE(N) \ do { CHECK_OP(state); \ if (state->bytecode.ptr + (N) > state->bytecode.limit) \ buffer_grow (&state->bytecode, N); } while (0)/* Add a 1-byte instruction/operand I to bytecode.data, assuming space has already been RESERVE'd. */#define OP1(I) (*state->bytecode.ptr++ = (I), CHECK_OP(state))/* Like OP1, but I is a 2-byte big endian integer. */#define OP2(I) \ do { int _i = (I); OP1 (_i >> 8); OP1 (_i); CHECK_OP(state); } while (0)/* Like OP1, but I is a 4-byte big endian integer. */#define OP4(I) \ do { int _i = (I); OP1 (_i >> 24); OP1 (_i >> 16); \ OP1 (_i >> 8); OP1 (_i); CHECK_OP(state); } while (0)/* Macro to call each time we push I words on the JVM stack. */#define NOTE_PUSH(I) \ do { state->code_SP += (I); \ if (state->code_SP > state->code_SP_max) \ state->code_SP_max = state->code_SP; } while (0)/* Macro to call each time we pop I words from the JVM stack. */#define NOTE_POP(I) \ do { state->code_SP -= (I); if (state->code_SP < 0) abort(); } while (0)/* A chunk or segment of a .class file. */struct chunk{ /* The next segment of this .class file. */ struct chunk *next; /* The actual data in this segment to be written to the .class file. */ unsigned char *data; /* The size of the segment to be written to the .class file. */ int size;};#define PENDING_CLEANUP_PC (-3)#define PENDING_EXIT_PC (-2)#define UNDEFINED_PC (-1)/* Each "block" represents a label plus the bytecode instructions following. There may be branches out of the block, but no incoming jumps, except to the beginning of the block. If (pc < 0), the jcf_block is not an actual block (i.e. it has no assocated code yet), but it is an undefined label.*/struct jcf_block{ /* For blocks that that are defined, the next block (in pc order). For blocks that are the not-yet-defined end label of a LABELED_BLOCK_EXPR or a cleanup expression (from a WITH_CLEANUP_EXPR), this is the next (outer) such end label, in a stack headed by labeled_blocks in jcf_partial. */ struct jcf_block *next; /* In the not-yet-defined end label for an unfinished EXIT_BLOCK_EXPR. pc is PENDING_EXIT_PC. In the not-yet-defined end label for pending cleanup subroutine, pc is PENDING_CLEANUP_PC. For other not-yet-defined labels, pc is UNDEFINED_PC. If the label has been defined: Until perform_relocations is finished, this is the maximum possible value of the bytecode offset at the begnning of this block. After perform_relocations, it is the actual offset (pc). */ int pc; int linenumber; /* After finish_jcf_block is called, The actual instructions contained in this block. Before than NULL, and the instructions are in state->bytecode. */ union { struct chunk *chunk; /* If pc==PENDING_CLEANUP_PC, start_label is the start of the region coveed by the cleanup. */ struct jcf_block *start_label; } v; union { /* Set of relocations (in reverse offset order) for this block. */ struct jcf_relocation *relocations; /* If this block is that of the not-yet-defined end label of a LABELED_BLOCK_EXPR, where LABELED_BLOCK is that LABELED_BLOCK_EXPR. If pc==PENDING_CLEANUP_PC, the cleanup that needs to be run. */ tree labeled_block; } u;};/* A "relocation" type for the 0-3 bytes of padding at the start of a tableswitch or a lookupswitch. */#define SWITCH_ALIGN_RELOC 4/* A relocation type for the labels in a tableswitch or a lookupswitch; these are relative to the start of the instruction, but (due to th 0-3 bytes of padding), we don't know the offset before relocation. */#define BLOCK_START_RELOC 1struct jcf_relocation{ /* Next relocation for the current jcf_block. */ struct jcf_relocation *next; /* The (byte) offset within the current block that needs to be relocated. */ HOST_WIDE_INT offset; /* 0 if offset is a 4-byte relative offset. 4 (SWITCH_ALIGN_RELOC) if offset points to 0-3 padding bytes inserted for proper alignment in tableswitch/lookupswitch instructions. 1 (BLOCK_START_RELOC) if offset points to a 4-byte offset relative to the start of the containing block. -1 if offset is a 2-byte relative offset. < -1 if offset is the address of an instruction with a 2-byte offset that does not have a corresponding 4-byte offset version, in which case the absolute value of kind is the inverted opcode. > 4 if offset is the address of an instruction (such as jsr) with a 2-byte offset that does have a corresponding 4-byte offset version, in which case kind is the opcode of the 4-byte version (such as jsr_w). */ int kind; /* The label the relocation wants to actually transfer to. */ struct jcf_block *label;};/* State for single catch clause. */struct jcf_handler{ struct jcf_handler *next; struct jcf_block *start_label; struct jcf_block *end_label; struct jcf_block *handler_label; /* The sub-class of Throwable handled, or NULL_TREE (for finally). */ tree type;};/* State for the current switch statement. */struct jcf_switch_state{ struct jcf_switch_state *prev; struct jcf_block *default_label; struct jcf_relocation *cases; int num_cases; HOST_WIDE_INT min_case, max_case;};/* This structure is used to contain the various pieces that will become a .class file. */struct jcf_partial{ struct chunk *first; struct chunk *chunk; struct obstack *chunk_obstack; tree current_method; /* List of basic blocks for the current method. */ struct jcf_block *blocks; struct jcf_block *last_block; struct localvar_info *first_lvar; struct localvar_info *last_lvar; int lvar_count; CPool cpool; int linenumber_count; /* Until perform_relocations, this is a upper bound on the number of bytes (so far) in the instructions for the current method. */ int code_length; /* Stack of undefined ending labels for LABELED_BLOCK_EXPR. */ struct jcf_block *labeled_blocks; /* The current stack size (stack pointer) in the current method. */ int code_SP; /* The largest extent of stack size (stack pointer) in the current method. */ int code_SP_max; /* Contains a mapping from local var slot number to localvar_info. */ struct buffer localvars; /* The buffer allocated for bytecode for the current jcf_block. */ struct buffer bytecode; /* Chain of exception handlers for the current method. */ struct jcf_handler *handlers; /* Last element in handlers chain. */ struct jcf_handler *last_handler; /* Number of exception handlers for the current method. */ int num_handlers; /* Number of finalizers we are currently nested within. */ int num_finalizers; /* If non-NULL, use this for the return value. */ tree return_value_decl; /* Information about the current switch statemenet. */ struct jcf_switch_state *sw_state;};static void generate_bytecode_insns PROTO ((tree, int, struct jcf_partial *));static struct chunk * alloc_chunk PROTO ((struct chunk *, unsigned char *, int, struct obstack *));static unsigned char * append_chunk PROTO ((unsigned char *, int, struct jcf_partial *));static void append_chunk_copy PROTO ((unsigned char *, int, struct jcf_partial *));static struct jcf_block * gen_jcf_label PROTO ((struct jcf_partial *));static void finish_jcf_block PROTO ((struct jcf_partial *));static void define_jcf_label PROTO ((struct jcf_block *, struct jcf_partial *));static struct jcf_block * get_jcf_label_here PROTO ((struct jcf_partial *));static void put_linenumber PROTO ((int, struct jcf_partial *));static void localvar_alloc PROTO ((tree, struct jcf_partial *));static int localvar_free PROTO ((tree, struct jcf_partial *));static int get_access_flags PROTO ((tree));static void write_chunks PROTO ((FILE *, struct chunk *));static int adjust_typed_op PROTO ((tree, int));static void generate_bytecode_conditional PROTO ((tree, struct jcf_block *, struct jcf_block *, int, struct jcf_partial *));static void generate_bytecode_return PROTO ((tree, struct jcf_partial *));static void perform_relocations PROTO ((struct jcf_partial *));static void init_jcf_state PROTO ((struct jcf_partial *, struct obstack *));static void init_jcf_method PROTO ((struct jcf_partial *, tree));static void release_jcf_state PROTO ((struct jcf_partial *));static struct chunk * generate_classfile PROTO ((tree, struct jcf_partial *));/* Utility macros for appending (big-endian) data to a buffer. We assume a local variable 'ptr' points into where we want to write next, and we assume enoygh space has been allocated. */#ifdef ENABLE_CHECKINGintCHECK_PUT(ptr, state, i) void *ptr; struct jcf_partial *state; int i;{ if (ptr < state->chunk->data || (char*)ptr + i > state->chunk->data + state->chunk->size) fatal ("internal error - CHECK_PUT failed"); return 0;}#else#define CHECK_PUT(PTR, STATE, I) ((void)0)#endif#define PUT1(X) (CHECK_PUT(ptr, state, 1), *ptr++ = (X))#define PUT2(X) (PUT1((X) >> 8), PUT1((X) & 0xFF))#define PUT4(X) (PUT2((X) >> 16), PUT2((X) & 0xFFFF))#define PUTN(P, N) (CHECK_PUT(ptr, state, N), memcpy(ptr, P, N), ptr += (N))/* Allocate a new chunk on obstack WORK, and link it in after LAST. Set the data and size fields to DATA and SIZE, respectively. However, if DATA is NULL and SIZE>0, allocate a buffer as well. */static struct chunk *alloc_chunk (last, data, size, work) struct chunk *last; unsigned char *data; int size; struct obstack *work;{ struct chunk *chunk = (struct chunk *) obstack_alloc (work, sizeof(struct chunk)); if (data == NULL && size > 0) data = obstack_alloc (work, size); chunk->next = NULL; chunk->data = data; chunk->size = size; if (last != NULL) last->next = chunk; return chunk;}#ifdef ENABLE_CHECKINGintCHECK_OP(struct jcf_partial *state){ if (state->bytecode.ptr > state->bytecode.limit) { fatal("internal error - CHECK_OP failed"); } return 0;}#else#define CHECK_OP(STATE) ((void)0)#endifstatic unsigned char *append_chunk (data, size, state) unsigned char *data; int size; struct jcf_partial *state;{ state->chunk = alloc_chunk (state->chunk, data, size, state->chunk_obstack); if (state->first == NULL) state->first = state->chunk; return state->chunk->data;}static voidappend_chunk_copy (data, size, state) unsigned char *data; int size; struct jcf_partial *state;{ unsigned char *ptr = append_chunk (NULL, size, state); memcpy (ptr, data, size);}static struct jcf_block *gen_jcf_label (state) struct jcf_partial *state;{ struct jcf_block *block = (struct jcf_block *) obstack_alloc (state->chunk_obstack, sizeof (struct jcf_block)); block->next = NULL; block->linenumber = -1; block->pc = UNDEFINED_PC; return block;}static voidfinish_jcf_block (state) struct jcf_partial *state;{ struct jcf_block *block = state->last_block; struct jcf_relocation *reloc; int code_length = BUFFER_LENGTH (&state->bytecode); int pc = state->code_length; append_chunk_copy (state->bytecode.data, code_length, state); BUFFER_RESET (&state->bytecode); block->v.chunk = state->chunk; /* Calculate code_length to the maximum value it can have. */ pc += block->v.chunk->size; for (reloc = block->u.relocations; reloc != NULL; reloc = reloc->next) { int kind = reloc->kind; if (kind == SWITCH_ALIGN_RELOC) pc += 3; else if (kind > BLOCK_START_RELOC) pc += 2; /* 2-byte offset may grow to 4-byte offset */ else if (kind < -1) pc += 5; /* May need to add a goto_w. */ } state->code_length = pc;}static voiddefine_jcf_label (label, state) struct jcf_block *label; struct jcf_partial *state;{ if (state->last_block != NULL) finish_jcf_block (state); label->pc = state->code_length; if (state->blocks == NULL) state->blocks = label; else state->last_block->next = label; state->last_block = label; label->next = NULL; label->u.relocations = NULL;}static struct jcf_block *get_jcf_label_here (state) struct jcf_partial *state;{ if (state->last_block != NULL && BUFFER_LENGTH (&state->bytecode) == 0) return state->last_block; else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -