Return-Path: Delivered-To: apmail-incubator-harmony-commits-archive@www.apache.org Received: (qmail 91956 invoked from network); 5 Oct 2005 02:21:55 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 5 Oct 2005 02:21:55 -0000 Received: (qmail 84021 invoked by uid 500); 5 Oct 2005 02:21:50 -0000 Delivered-To: apmail-incubator-harmony-commits-archive@incubator.apache.org Received: (qmail 83792 invoked by uid 500); 5 Oct 2005 02:21:49 -0000 Mailing-List: contact harmony-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: harmony-dev@incubator.apache.org Delivered-To: mailing list harmony-commits@incubator.apache.org Received: (qmail 82856 invoked by uid 99); 5 Oct 2005 02:21:37 -0000 X-ASF-Spam-Status: No, hits=-9.8 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [209.237.227.194] (HELO minotaur.apache.org) (209.237.227.194) by apache.org (qpsmtpd/0.29) with SMTP; Tue, 04 Oct 2005 19:21:09 -0700 Received: (qmail 91416 invoked by uid 65534); 5 Oct 2005 02:20:48 -0000 Message-ID: <20051005022048.91415.qmail@minotaur.apache.org> Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r294974 [11/25] - in /incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm: ./ jchevm/ jchevm/doc/ jchevm/etc/ jchevm/include/ jchevm/java/ jchevm/java/org/ jchevm/java/org/dellroad/ jchevm/java/org/dellroad/jc/ jchevm/java/org/dellroad... Date: Wed, 05 Oct 2005 02:20:10 -0000 To: harmony-commits@incubator.apache.org From: geirm@apache.org X-Mailer: svnmailer-1.0.5 X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/cf_parse.h URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/cf_parse.h?rev=294974&view=auto ============================================================================== --- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/cf_parse.h (added) +++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/cf_parse.h Tue Oct 4 19:19:16 2005 @@ -0,0 +1,544 @@ + +/* + * Copyright 2005 The Apache Software Foundation or its licensors, + * as applicable. + * + * 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. + * + * $Id: cf_parse.h,v 1.2 2005/02/27 23:29:44 archiecobbs Exp $ + */ + +#ifndef _CF_PARSE_H_ +#define _CF_PARSE_H_ + +/************************************************************************ + * Class file structures * + ************************************************************************/ + +/* + * Forward structure declarations and typedef's + */ +typedef struct _jc_cf_anewarray _jc_cf_anewarray; +typedef struct _jc_cf_attr _jc_cf_attr; +typedef struct _jc_cf_branch _jc_cf_branch; +typedef struct _jc_cf_bytecode _jc_cf_bytecode; +typedef struct _jc_cf_code _jc_cf_code; +typedef struct _jc_cf_constant _jc_cf_constant; +typedef struct _jc_cf_exceptions _jc_cf_exceptions; +typedef struct _jc_cf_field _jc_cf_field; +typedef struct _jc_cf_fieldref _jc_cf_fieldref; +typedef struct _jc_cf_iinc _jc_cf_iinc; +typedef struct _jc_cf_immediate _jc_cf_immediate; +typedef struct _jc_cf_inner_class _jc_cf_inner_class; +typedef struct _jc_cf_inner_classes _jc_cf_inner_classes; +typedef struct _jc_cf_insn _jc_cf_insn; +typedef struct _jc_cf_invoke _jc_cf_invoke; +typedef struct _jc_cf_linemap _jc_cf_linemap; +typedef struct _jc_cf_linenum _jc_cf_linenum; +typedef struct _jc_cf_linenums _jc_cf_linenums; +typedef struct _jc_cf_local _jc_cf_local; +typedef struct _jc_cf_lookup _jc_cf_lookup; +typedef struct _jc_cf_lookupswitch _jc_cf_lookupswitch; +typedef struct _jc_cf_method _jc_cf_method; +typedef struct _jc_cf_multianewarray _jc_cf_multianewarray; +typedef struct _jc_cf_name_type _jc_cf_name_type; +typedef struct _jc_cf_new _jc_cf_new; +typedef struct _jc_cf_newarray _jc_cf_newarray; +typedef struct _jc_cf_parse_state _jc_cf_parse_state; +typedef struct _jc_cf_ref _jc_cf_ref; +typedef struct _jc_cf_switch _jc_cf_switch; +typedef struct _jc_cf_tableswitch _jc_cf_tableswitch; +typedef struct _jc_cf_trap _jc_cf_trap; +typedef struct _jc_cf_type _jc_cf_type; + +/************************************************************************ + * Bytecode information * + ************************************************************************/ + +/* + * Bytecode definitions + */ +enum { + _JC_aaload =0x32, + _JC_aastore =0x53, + _JC_aconst_null =0x01, + _JC_aload =0x19, + _JC_aload_0 =0x2a, + _JC_aload_1 =0x2b, + _JC_aload_2 =0x2c, + _JC_aload_3 =0x2d, + _JC_anewarray =0xbd, + _JC_areturn =0xb0, + _JC_arraylength =0xbe, + _JC_astore =0x3a, + _JC_astore_0 =0x4b, + _JC_astore_1 =0x4c, + _JC_astore_2 =0x4d, + _JC_astore_3 =0x4e, + _JC_athrow =0xbf, + _JC_baload =0x33, + _JC_bastore =0x54, + _JC_bipush =0x10, + _JC_caload =0x34, + _JC_castore =0x55, + _JC_checkcast =0xc0, + _JC_d2f =0x90, + _JC_d2i =0x8e, + _JC_d2l =0x8f, + _JC_dadd =0x63, + _JC_daload =0x31, + _JC_dastore =0x52, + _JC_dcmpg =0x98, + _JC_dcmpl =0x97, + _JC_dconst_0 =0x0e, + _JC_dconst_1 =0x0f, + _JC_ddiv =0x6f, + _JC_dload =0x18, + _JC_dload_0 =0x26, + _JC_dload_1 =0x27, + _JC_dload_2 =0x28, + _JC_dload_3 =0x29, + _JC_dmul =0x6b, + _JC_dneg =0x77, + _JC_drem =0x73, + _JC_dreturn =0xaf, + _JC_dstore =0x39, + _JC_dstore_0 =0x47, + _JC_dstore_1 =0x48, + _JC_dstore_2 =0x49, + _JC_dstore_3 =0x4a, + _JC_dsub =0x67, + _JC_dup =0x59, + _JC_dup_x1 =0x5a, + _JC_dup_x2 =0x5b, + _JC_dup2 =0x5c, + _JC_dup2_x1 =0x5d, + _JC_dup2_x2 =0x5e, + _JC_f2d =0x8d, + _JC_f2i =0x8b, + _JC_f2l =0x8c, + _JC_fadd =0x62, + _JC_faload =0x30, + _JC_fastore =0x51, + _JC_fcmpg =0x96, + _JC_fcmpl =0x95, + _JC_fconst_0 =0x0b, + _JC_fconst_1 =0x0c, + _JC_fconst_2 =0x0d, + _JC_fdiv =0x6e, + _JC_fload =0x17, + _JC_fload_0 =0x22, + _JC_fload_1 =0x23, + _JC_fload_2 =0x24, + _JC_fload_3 =0x25, + _JC_fmul =0x6a, + _JC_fneg =0x76, + _JC_frem =0x72, + _JC_freturn =0xae, + _JC_fstore =0x38, + _JC_fstore_0 =0x43, + _JC_fstore_1 =0x44, + _JC_fstore_2 =0x45, + _JC_fstore_3 =0x46, + _JC_fsub =0x66, + _JC_getfield =0xb4, + _JC_getstatic =0xb2, + _JC_goto =0xa7, + _JC_goto_w =0xc8, + _JC_i2b =0x91, + _JC_i2c =0x92, + _JC_i2d =0x87, + _JC_i2f =0x86, + _JC_i2l =0x85, + _JC_i2s =0x93, + _JC_iadd =0x60, + _JC_iaload =0x2e, + _JC_iand =0x7e, + _JC_iastore =0x4f, + _JC_iconst_m1 =0x02, + _JC_iconst_0 =0x03, + _JC_iconst_1 =0x04, + _JC_iconst_2 =0x05, + _JC_iconst_3 =0x06, + _JC_iconst_4 =0x07, + _JC_iconst_5 =0x08, + _JC_idiv =0x6c, + _JC_if_acmpeq =0xa5, + _JC_if_acmpne =0xa6, + _JC_if_icmpeq =0x9f, + _JC_if_icmpne =0xa0, + _JC_if_icmplt =0xa1, + _JC_if_icmpge =0xa2, + _JC_if_icmpgt =0xa3, + _JC_if_icmple =0xa4, + _JC_ifeq =0x99, + _JC_ifne =0x9a, + _JC_iflt =0x9b, + _JC_ifge =0x9c, + _JC_ifgt =0x9d, + _JC_ifle =0x9e, + _JC_ifnonnull =0xc7, + _JC_ifnull =0xc6, + _JC_iinc =0x84, + _JC_iload =0x15, + _JC_iload_0 =0x1a, + _JC_iload_1 =0x1b, + _JC_iload_2 =0x1c, + _JC_iload_3 =0x1d, + _JC_imul =0x68, + _JC_ineg =0x74, + _JC_instanceof =0xc1, + _JC_invokeinterface =0xb9, + _JC_invokespecial =0xb7, + _JC_invokestatic =0xb8, + _JC_invokevirtual =0xb6, + _JC_ior =0x80, + _JC_irem =0x70, + _JC_ireturn =0xac, + _JC_ishl =0x78, + _JC_ishr =0x7a, + _JC_istore =0x36, + _JC_istore_0 =0x3b, + _JC_istore_1 =0x3c, + _JC_istore_2 =0x3d, + _JC_istore_3 =0x3e, + _JC_isub =0x64, + _JC_iushr =0x7c, + _JC_ixor =0x82, + _JC_jsr =0xa8, + _JC_jsr_w =0xc9, + _JC_l2d =0x8a, + _JC_l2f =0x89, + _JC_l2i =0x88, + _JC_ladd =0x61, + _JC_laload =0x2f, + _JC_land =0x7f, + _JC_lastore =0x50, + _JC_lcmp =0x94, + _JC_lconst_0 =0x09, + _JC_lconst_1 =0x0a, + _JC_ldc =0x12, + _JC_ldc_w =0x13, + _JC_ldc2_w =0x14, + _JC_ldiv =0x6d, + _JC_lload =0x16, + _JC_lload_0 =0x1e, + _JC_lload_1 =0x1f, + _JC_lload_2 =0x20, + _JC_lload_3 =0x21, + _JC_lmul =0x69, + _JC_lneg =0x75, + _JC_lookupswitch =0xab, + _JC_lor =0x81, + _JC_lrem =0x71, + _JC_lreturn =0xad, + _JC_lshl =0x79, + _JC_lshr =0x7b, + _JC_lstore =0x37, + _JC_lstore_0 =0x3f, + _JC_lstore_1 =0x40, + _JC_lstore_2 =0x41, + _JC_lstore_3 =0x42, + _JC_lsub =0x65, + _JC_lushr =0x7d, + _JC_lxor =0x83, + _JC_monitorenter =0xc2, + _JC_monitorexit =0xc3, + _JC_multianewarray =0xc5, + _JC_new =0xbb, + _JC_newarray =0xbc, + _JC_nop =0x00, + _JC_pop =0x57, + _JC_pop2 =0x58, + _JC_putfield =0xb5, + _JC_putstatic =0xb3, + _JC_ret =0xa9, + _JC_return =0xb1, + _JC_saload =0x35, + _JC_sastore =0x56, + _JC_sipush =0x11, + _JC_swap =0x5f, + _JC_tableswitch =0xaa, + _JC_wide =0xc4, +}; + +/* + * Primitive type definitions used by 'newarray' + */ +enum { + _JC_boolean =4, + _JC_char =5, + _JC_float =6, + _JC_double =7, + _JC_byte =8, + _JC_short =9, + _JC_int =10, + _JC_long =11, +}; + +/* + * Class file constant pool tags. + */ +enum { + CONSTANT_Utf8 =1, + CONSTANT_Integer =3, + CONSTANT_Float =4, + CONSTANT_Long =5, + CONSTANT_Double =6, + CONSTANT_Class =7, + CONSTANT_String =8, + CONSTANT_Fieldref =9, + CONSTANT_Methodref =10, + CONSTANT_InterfaceMethodref =11, + CONSTANT_NameAndType =12, +}; + +/* + * Extra information associated with certain bytecodes. The "target" + * fields are indexes into the _jc_cf_insn array, not bytecode offsets. + */ +struct _jc_cf_fieldref { + _jc_cf_ref *field; +}; +struct _jc_cf_iinc { + uint16_t index; + jshort value; +}; +struct _jc_cf_invoke { + _jc_cf_ref *method; +}; +struct _jc_cf_lookup { + jint match; + jint target; +}; +struct _jc_cf_lookupswitch { + jint default_target; + jint num_pairs; + _jc_cf_lookup pairs[0]; +}; +struct _jc_cf_multianewarray { + const char *type; + u_char dims; +}; +struct _jc_cf_newarray { + u_char type; /* _JC_TYPE_XXX */ +}; +struct _jc_cf_tableswitch { + jint default_target; + jint low; + jint high; + jint targets[0]; +}; +struct _jc_cf_branch { + jint target; +}; +struct _jc_cf_local { + uint16_t index; +}; +struct _jc_cf_immediate { + jint value; +}; +struct _jc_cf_type { + const char *name; +}; + +/* Parsed instruction information */ +struct _jc_cf_insn { + union { + _jc_cf_fieldref fieldref; + _jc_cf_iinc iinc; + _jc_cf_invoke invoke; + _jc_cf_multianewarray multianewarray; + _jc_cf_newarray newarray; + _jc_cf_lookupswitch *lookupswitch; + _jc_cf_tableswitch *tableswitch; + _jc_cf_branch branch; + _jc_cf_local local; + _jc_cf_immediate immediate; + _jc_cf_type type; + _jc_cf_constant *constant; + } u; + u_char opcode; +}; + +/* LineNumberTable attribute */ +struct _jc_cf_linenums { + uint16_t length; + _jc_cf_linenum *linenums; +}; + +struct _jc_cf_linenum { + uint16_t offset; /* bytecode offset */ + uint16_t line; +}; + +struct _jc_cf_linemap { + jint index; /* instruction index */ + uint16_t line; +}; + +/* Parsed method trap */ +struct _jc_cf_trap { + jint start; /* instruction index */ + jint end; /* instruction index */ + jint target; /* instruction index */ + const char *type; +}; + +/* Unparsed method bytecode */ +struct _jc_cf_bytecode { + u_char *bytecode; + size_t length; + _jc_cf_linenums *linenums; +}; + +/* Parsed method bytecode */ +struct _jc_cf_code { + uint16_t max_stack; + uint16_t max_locals; + uint16_t num_traps; + uint16_t num_linemaps; + jint num_insns; + _jc_cf_insn *insns; + _jc_cf_trap *traps; + _jc_cf_linemap *linemaps; +}; + +/************************************************************************ + * Non-bytecode information * + ************************************************************************/ + +/* Fieldref, Methodref, or InterfaceMethodref constant */ +struct _jc_cf_ref { + const char *class; + const char *name; + const char *descriptor; +}; + +/* NameAndType constant */ +struct _jc_cf_name_type { + const char *name; + const char *descriptor; +}; + +/* Constant pool entry */ +struct _jc_cf_constant { + u_char type; + union { + const char *Class; + _jc_cf_ref Ref; + _jc_cf_name_type NameAndType; + const char *String; + jint Integer; + jlong Long; + jfloat Float; + jdouble Double; + const char *Utf8; + } u; +}; + +/* Field info */ +struct _jc_cf_field { + uint16_t access_flags; + const char *name; + const char *descriptor; + uint16_t num_attributes; + _jc_cf_attr *attributes; + _jc_cf_constant *initial_value; +}; + +/* Method info */ +struct _jc_cf_method { + uint16_t access_flags; + const char *name; + const char *descriptor; + uint16_t num_attributes; + _jc_cf_attr *attributes; + _jc_cf_exceptions *exceptions; + _jc_cf_bytecode *code; +}; + +/* Exceptions attribute */ +struct _jc_cf_exceptions { + uint16_t num_exceptions; + const char **exceptions; +}; + +/* InnerClasses attribute */ +struct _jc_cf_inner_class { + const char *inner; + const char *outer; + const char *name; + uint16_t access_flags; +}; + +struct _jc_cf_inner_classes { + uint16_t num_classes; + _jc_cf_inner_class *classes; +}; + +/* Attribute (only some are explicitly supported) */ +struct _jc_cf_attr { + const char *name; + uint32_t length; + const u_char *bytes; + union { + _jc_cf_constant *ConstantValue; + const char *SourceFile; + _jc_cf_exceptions Exceptions; + _jc_cf_inner_classes InnerClasses; + _jc_cf_bytecode Code; + _jc_cf_linenums LineNumberTable; + } u; +}; + +/* Parsed classfile */ +struct _jc_classfile { + uint16_t minor_version; + uint16_t major_version; + uint16_t access_flags; + uint16_t num_constants; + _jc_cf_constant *constants; + const char *name; + const char *superclass; + uint16_t num_interfaces; + const char **interfaces; + uint16_t num_fields; + _jc_cf_field *fields; + uint16_t num_methods; + _jc_cf_method *methods; + uint16_t num_attributes; + _jc_cf_attr *attributes; + _jc_cf_inner_classes *inner_classes; + const char *source_file; + char *string_mem; /* nul-terminated utf strings */ +}; + +/* Internal parsing state */ +struct _jc_cf_parse_state { + _jc_env *env; + _jc_classfile *cfile; + const u_char *bytes; + size_t length; + size_t pos; +}; + +/* cf_parse.c */ +extern _jc_classfile *_jc_parse_classfile(_jc_env *env, + _jc_classbytes *bytes, int howmuch); +extern void _jc_destroy_classfile(_jc_classfile **cfilep); +extern int _jc_parse_code(_jc_env *env, _jc_classfile *cfile, + _jc_cf_bytecode *bytecode, _jc_cf_code *code); +extern void _jc_destroy_code(_jc_cf_code *code); + +#endif /* _CF_PARSE_H_ */ Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/cl_alloc.c URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/cl_alloc.c?rev=294974&view=auto ============================================================================== --- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/cl_alloc.c (added) +++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/cl_alloc.c Tue Oct 4 19:19:16 2005 @@ -0,0 +1,358 @@ + +/* + * Copyright 2005 The Apache Software Foundation or its licensors, + * as applicable. + * + * 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. + * + * $Id: cl_alloc.c,v 1.6 2005/03/12 04:24:17 archiecobbs Exp $ + */ + +#include "libjc.h" + +/* + * "Uni-alloc" memory allocator used for class loaders. + * + * Class loader memory blocks are assumed to be allocated once and never + * reallocated or freed, until all memory associated with a class loader + * is freed at once when the class loader is unloaded. + * + * As a special exception, we allow memory blocks to be freed in the + * reverse order in which they were allocated, to support "undoing" + * in the case of certain errors. + * + * Note also that class loader memory is always manipulated while the + * loader's lock is held, so synchronization is not an issue here. + * + * We provide this same functionality for other users besides class + * loaders using the "uni" functions. + */ + +/* Internal functions */ +static jint _jc_uni_avail_alloc(_jc_uni_mem *uni, int num_pages); + +/* + * Initialize memory associated with a uni-allocator. + */ +void +_jc_uni_alloc_init(_jc_uni_mem *uni, int min_pages, + volatile _jc_word *avail_pages) +{ + TAILQ_INIT(&uni->pages); + uni->min_pages = min_pages; + uni->avail_pages = avail_pages; +} + +/* + * Free all memory associated with a uni-allocator. + */ +void +_jc_uni_alloc_free(_jc_uni_mem *uni) +{ + int num_pages = 0; + + /* Free all pages of memory */ + while (!TAILQ_EMPTY(&uni->pages)) { + _jc_uni_pages *pages = TAILQ_FIRST(&uni->pages); + + num_pages += pages->num_pages; + TAILQ_REMOVE(&uni->pages, pages, link); + _jc_vm_free(&pages); + } + + /* Update available pages */ + _jc_uni_avail_alloc(uni, -num_pages); +} + +/* + * Extend memory in an uni-allocator. + * + * If unsuccessful an exception is stored. + */ +static jint +_jc_uni_ensure(_jc_env *env, _jc_uni_mem *uni, size_t size) +{ + jboolean reserved = JNI_FALSE; + _jc_uni_pages *pages; + size_t num_pages; + int try; + + /* Get current page set */ + pages = TAILQ_LAST(&uni->pages, _jc_uni_page_list); + + /* Is there enough room? */ + if (!TAILQ_EMPTY(&uni->pages) + && size < (pages->num_pages * _JC_PAGE_SIZE) - pages->offset) + return JNI_OK; + + /* Compute how many new pages to allocate */ + num_pages = _JC_HOWMANY(_JC_UNI_HDR_SIZE + size, _JC_PAGE_SIZE); + if (num_pages < uni->min_pages) + num_pages = uni->min_pages; + + /* Try hard to find memory */ + for (try = 1; JNI_TRUE; try++) { + + /* Try to reserve pages */ + if (_jc_uni_avail_alloc(uni, num_pages) == JNI_OK) { + reserved = JNI_TRUE; + break; + } + + /* Give up after three tries */ + if (try == 3) + break; + + /* Try a GC cycle to unload some classes */ + if (_jc_gc(env, JNI_TRUE) != JNI_OK) + return JNI_ERR; + + /* Yield so the finalizer thread can run */ + sched_yield(); + } + + /* Did we successfully reserve? */ + if (!reserved) { + _JC_EX_STORE(env, OutOfMemoryError, + "reached limit of %u pages allocated" + " by class loaders", (int)*uni->avail_pages); + return JNI_ERR; + } + + /* Get pages */ + pages = _jc_vm_alloc(env, num_pages * _JC_PAGE_SIZE); + + /* Un-reserve (for class loaders) on error */ + if (pages == NULL) { + _jc_uni_avail_alloc(uni, -num_pages); + return JNI_ERR; + } + + /* Initialize new chunk */ + memset(pages, 0, sizeof(*pages)); + pages->num_pages = num_pages; + pages->offset = _JC_UNI_HDR_SIZE; + + /* Link it into our list */ + TAILQ_INSERT_TAIL(&uni->pages, pages, link); + + /* Done */ + return JNI_OK; +} + +/* + * Allocate a chunk of memory from an uni-allocator's page list. + * Grab more pages as necessary. + * + * If unsuccessful an exception is stored. + */ +void * +_jc_uni_alloc(_jc_env *env, _jc_uni_mem *uni, size_t size) +{ + _jc_uni_pages *pages; + void *ptr; + + /* Stay aligned */ + size = _JC_ROUNDUP2(size, _JC_FULL_ALIGNMENT); + + /* Ensure we have room */ + if (_jc_uni_ensure(env, uni, size) != JNI_OK) + return NULL; + + /* Grab some more memory */ + pages = TAILQ_LAST(&uni->pages, _jc_uni_page_list); + ptr = (char *)pages + pages->offset; + pages->offset += size; + + /* Done */ + return ptr; +} + +/* + * If the most recently allocated memory that was not subsequently + * unallocated was address '*pp' with size 'size', then free it. + * Otherwise, just leak it. + * + * In other words, you can free uni-allocated memory if freed in the + * reverse order in which it was allocated. + */ +void +_jc_uni_unalloc(_jc_uni_mem *uni, void *pp, size_t size) +{ + void **const ptrp = pp; + void *const ptr = *ptrp; + _jc_uni_pages *pages; + + /* Sanity check */ + if (ptr == NULL) + return; + *ptrp = NULL; + + /* Can we un-do? */ + if (TAILQ_EMPTY(&uni->pages)) + return; + pages = TAILQ_LAST(&uni->pages, _jc_uni_page_list); + size = _JC_ROUNDUP2(size, _JC_FULL_ALIGNMENT); + if ((char *)ptr + size != (char *)pages + pages->offset) + return; + + /* Un-do the previous allocation */ + pages->offset -= size; + + /* Free the page if empty */ + if (pages->offset == _JC_UNI_HDR_SIZE) { + TAILQ_REMOVE(&uni->pages, pages, link); + _jc_uni_avail_alloc(uni, -pages->num_pages); + _jc_vm_free(&pages); + } +} + +/* + * Allocate and clear a chunk of memory from the uni-allocator memory. + */ +void * +_jc_uni_zalloc(_jc_env *env, _jc_uni_mem *uni, size_t size) +{ + void *mem; + + /* Alloc mem and zero it */ + if ((mem = _jc_uni_alloc(env, uni, size)) != NULL) + memset(mem, 0, size); + return mem; +} + +/* + * Copy a string into memory allocated from an uni-allocator. + */ +char * +_jc_uni_strdup(_jc_env *env, _jc_uni_mem *uni, const char *s) +{ + const size_t slen = strlen(s); + void *mem; + + /* Alloc mem and copy string */ + if ((mem = _jc_uni_alloc(env, uni, slen + 1)) == NULL) + return NULL; + memcpy(mem, s, slen + 1); + return mem; +} + +/* + * Determine if a pointer points into uni-allocator allocated memory. + */ +jboolean +_jc_uni_contains(_jc_uni_mem *uni, const void *ptr) +{ + _jc_uni_pages *pages; + + TAILQ_FOREACH(pages, &uni->pages, link) { + const char *const start = (char *)pages + _JC_UNI_HDR_SIZE; + const char *const end = (char *)pages + + (pages->num_pages * _JC_PAGE_SIZE); + + if ((char *)ptr >= start && (char *)ptr < end) + return JNI_TRUE; + } + return JNI_FALSE; +} + +/* + * Atomically update available pages, but don't let them go negative. + */ +static jint +_jc_uni_avail_alloc(_jc_uni_mem *uni, int num_pages) +{ + _jc_word old_avail_pages; + + if (uni->avail_pages == NULL) + return JNI_OK; + do { + old_avail_pages = *uni->avail_pages; + if (num_pages > 0 && num_pages > old_avail_pages) + return JNI_ERR; + } while (!_jc_compare_and_swap(uni->avail_pages, + old_avail_pages, old_avail_pages - num_pages)); + return JNI_OK; +} + +/* + * Mark the current top of the allocation stack. + */ +void * +_jc_uni_mark(_jc_uni_mem *uni) +{ + _jc_uni_pages *pages; + + pages = TAILQ_LAST(&uni->pages, _jc_uni_page_list); + return pages != NULL ? (char *)pages + pages->offset : NULL; +} + +/* + * Reset the current top of the allocation stack to 'mem'. + */ +void +_jc_uni_reset(_jc_uni_mem *uni, void *mem) +{ + _jc_uni_pages *pages; + + if (mem == NULL) { + _jc_uni_alloc_free(uni); + return; + } + while (JNI_TRUE) { + _JC_ASSERT(!TAILQ_EMPTY(&uni->pages)); + pages = TAILQ_LAST(&uni->pages, _jc_uni_page_list); + if ((char *)mem >= (char *)pages + _JC_UNI_HDR_SIZE + && (char *)mem < (char *)pages + + pages->num_pages * _JC_PAGE_SIZE) { + pages->offset = (char *)mem - (char *)pages; + break; + } + TAILQ_REMOVE(&uni->pages, pages, link); + _jc_uni_avail_alloc(uni, -pages->num_pages); + _jc_vm_free(&pages); + } +} + +/************************************************************************ + * Class loader equivalents with assertions * + ************************************************************************/ + +void * +_jc_cl_alloc(_jc_env *env, _jc_class_loader *loader, size_t size) +{ + _JC_MUTEX_ASSERT(env, loader->mutex); + return _jc_uni_alloc(env, &loader->uni, size); +} + +void +_jc_cl_unalloc(_jc_class_loader *loader, void *pp, size_t size) +{ + _JC_MUTEX_ASSERT(_jc_get_current_env(), loader->mutex); + _jc_uni_unalloc(&loader->uni, pp, size); +} + +void * +_jc_cl_zalloc(_jc_env *env, _jc_class_loader *loader, size_t size) +{ + _JC_MUTEX_ASSERT(env, loader->mutex); + return _jc_uni_zalloc(env, &loader->uni, size); +} + +char * +_jc_cl_strdup(_jc_env *env, _jc_class_loader *loader, const char *s) +{ + _JC_MUTEX_ASSERT(env, loader->mutex); + return _jc_uni_strdup(env, &loader->uni, s); +} + Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/class_bytes.c URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/class_bytes.c?rev=294974&view=auto ============================================================================== --- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/class_bytes.c (added) +++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/class_bytes.c Tue Oct 4 19:19:16 2005 @@ -0,0 +1,380 @@ + +/* + * Copyright 2005 The Apache Software Foundation or its licensors, + * as applicable. + * + * 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. + * + * $Id: class_bytes.c,v 1.5 2005/03/18 23:16:28 archiecobbs Exp $ + */ + +#include "libjc.h" + +/* Internal functions */ +static void _jc_hash_classbytes(_jc_classbytes *bytes); +static void _jc_munmap_freer(_jc_classbytes *bytes); +static void _jc_free_freer(_jc_classbytes *bytes); + +/* + * Search for a class file in the filesystem using the bootstrap + * class loader directory search path, and load it into memory. + * This is used for the bootstrap loader to find classfiles. + * + * If not found, NULL is returned and a NoClassDefFoundError is stored. + */ +_jc_classbytes * +_jc_bootcl_find_classbytes(_jc_env *env, const char *name, int *indexp) +{ + _jc_jvm *const vm = env->vm; + int i; + + /* Search for file in each class path component */ + for (i = 0; i < vm->boot.class_path_len; i++) { + _jc_cpath_entry *const ent = &vm->boot.class_path[i]; + _jc_classbytes *bytes = NULL; + +try_again: + /* Look for class in this class path component */ + switch (ent->type) { + case _JC_CPATH_DIRECTORY: + _JC_ASSERT(ent->zip == NULL); + if (_jc_read_classbytes_dir(env, + ent, name, &bytes) != JNI_OK) + return NULL; + break; + case _JC_CPATH_ZIPFILE: + _JC_ASSERT(ent->zip != NULL); + if (_jc_read_classbytes_zip(env, + ent, name, &bytes) != JNI_OK) { + if (env->ex.num == _JC_IOException) + ent->type = _JC_CPATH_ERROR; + return NULL; + } + break; + case _JC_CPATH_ERROR: + break; + case _JC_CPATH_UNKNOWN: + { + struct stat info; + _jc_zip *zip; + + /* Examine file */ + _JC_ASSERT(ent->zip == NULL); + if (stat(ent->pathname, &info) == -1) { + _JC_MUTEX_LOCK(env, vm->mutex); + if (ent->type == _JC_CPATH_UNKNOWN) + ent->type = _JC_CPATH_ERROR; + _JC_MUTEX_UNLOCK(env, vm->mutex); + goto try_again; + } + + /* If it's a directory, change type and try again */ + if ((info.st_mode & S_IFMT) == S_IFDIR) { + _JC_MUTEX_LOCK(env, vm->mutex); + if (ent->type == _JC_CPATH_UNKNOWN) + ent->type = _JC_CPATH_DIRECTORY; + _JC_MUTEX_UNLOCK(env, vm->mutex); + goto try_again; + } + + /* Otherwise, try to open it as a ZIP file */ + if ((zip = _jc_zip_open(env, ent->pathname)) == NULL) { + if (env->ex.num != _JC_IOException) + return NULL; + _JC_MUTEX_LOCK(env, vm->mutex); + if (ent->type == _JC_CPATH_UNKNOWN) + ent->type = _JC_CPATH_ERROR; + _JC_MUTEX_UNLOCK(env, vm->mutex); + goto try_again; + } + + /* That worked, so change type and try again */ + _JC_MUTEX_LOCK(env, vm->mutex); + if (ent->type == _JC_CPATH_UNKNOWN) { + _JC_ASSERT(ent->zip == NULL); + ent->type = _JC_CPATH_ZIPFILE; + ent->zip = zip; + zip = NULL; + } + _JC_MUTEX_UNLOCK(env, vm->mutex); + _jc_zip_close(&zip); + goto try_again; + } + default: + _JC_ASSERT(JNI_FALSE); + } + + /* If found, return them */ + if (bytes != NULL) { + if (indexp != NULL) + *indexp = i; + return bytes; + } + } + + /* Not found */ + _JC_EX_STORE(env, NoClassDefFoundError, "%s", name); + for (i = 0; env->ex.msg[i] != '\0'; i++) { + if (env->ex.msg[i] == '/') + env->ex.msg[i] = '.'; + } + return NULL; +} + +/* + * Read a class file from the given directory hierarchy. + * + * Sets *bytesp to the class file bytes, or NULL if not found. + * An exception is stored only if something else bad happens. + */ +jint +_jc_read_classbytes_dir(_jc_env *env, _jc_cpath_entry *ent, + const char *name, _jc_classbytes **bytesp) +{ + _jc_classbytes *bytes; + struct stat info; + char *path; + void *addr; + int fd = -1; + char *s; + + /* Sanity check */ + _JC_ASSERT(ent->type == _JC_CPATH_DIRECTORY); + + /* Sanity check name to avoid reading arbitrary files */ + if (*name == '/') + goto not_found; + + /* Concatenate directory and class name to get filename */ + if ((path = _JC_FORMAT_STRING(env, + "%s/%s.class", ent->pathname, name)) == NULL) + return JNI_ERR; + + /* Convert Java "/" separator to directory path separator */ + for (s = path; *s != '\0'; s++) { + if (*s == '/') + *s = _JC_FILE_SEPARATOR[0]; + } + + /* Open file */ + if ((fd = open(path, O_RDONLY)) == -1) { + if (errno == ENOENT) + goto not_found; + _JC_EX_STORE(env, IOException, "can't read `%s': %s", + name, strerror(errno)); + goto fail; + } + (void)fcntl(fd, F_SETFD, 1); + if (fstat(fd, &info) == -1) { + _JC_EX_STORE(env, IOException, "can't stat `%s': %s", + name, strerror(errno)); + goto fail; + } + + /* Memory map in the file */ + if ((addr = mmap(NULL, info.st_size, PROT_READ, + MAP_PRIVATE, fd, 0)) == MAP_FAILED) { + _JC_EX_STORE(env, IOException, "can't map `%s': %s", + name, strerror(errno)); + goto fail; + } + + /* Create _jc_classbytes object */ + if ((bytes = _jc_vm_zalloc(env, sizeof(*bytes))) == NULL) { + munmap(addr, info.st_size); + goto fail; + } + bytes->bytes = addr; + bytes->length = info.st_size; + bytes->refs = 1; + bytes->freer = _jc_munmap_freer; + + /* Compute hash */ + _jc_hash_classbytes(bytes); + + /* Set return value to the bytes found */ + *bytesp = bytes; + +not_found: + /* No error, just not found */ + if (fd != -1) + close(fd); + return JNI_OK; + +fail: + /* Clean up after error */ + if (fd != -1) + close(fd); + return JNI_ERR; +} + +/* + * Read a class file from the given ZIP file. + * + * Sets *bytesp to the class file bytes, or NULL if not found. + * An exception is stored only if something else bad happens. + */ +jint +_jc_read_classbytes_zip(_jc_env *env, _jc_cpath_entry *ent, + const char *name, _jc_classbytes **bytesp) +{ + _jc_classbytes *bytes = NULL; + _jc_zip *const zip = ent->zip; + _jc_zip_entry *zent; + char *zent_name; + int indx; + + /* Sanity check */ + _JC_ASSERT(ent->type == _JC_CPATH_ZIPFILE); + _JC_ASSERT(ent->zip != NULL); + + /* Append '.class' suffix to class name */ + if ((zent_name = _JC_FORMAT_STRING(env, "%s.class", name)) == NULL) + return JNI_ERR; + + /* Get file index */ + if ((indx = _jc_zip_search(ent->zip, zent_name)) == -1) + return JNI_OK; /* not found */ + zent = &zip->entries[indx]; + + /* Create _jc_classbytes object */ + if ((bytes = _jc_vm_alloc(env, + sizeof(*bytes) + zent->uncomp_len)) == NULL) + return JNI_ERR; + memset(bytes, 0, sizeof(*bytes)); + bytes->bytes = (u_char *)bytes + sizeof(*bytes); + bytes->length = zent->uncomp_len; + bytes->refs = 1; + bytes->freer = _jc_free_freer; + + /* Extract file contents from ZIP file */ + if (_jc_zip_read(env, zip, indx, bytes->bytes) != JNI_OK) { + _jc_free_classbytes(&bytes); + return JNI_ERR; + } + + /* Compute hash */ + _jc_hash_classbytes(bytes); + + /* Set return value to the bytes found */ + *bytesp = bytes; + return JNI_OK; +} + +/* + * Create a _jc_classbytes structure from an array of bytes. + * + * If there is an error an exception is stored and NULL returned. + */ +_jc_classbytes * +_jc_copy_classbytes(_jc_env *env, const void *data, size_t len) +{ + _jc_classbytes *bytes; + + /* Create classbytes object */ + if ((bytes = _jc_vm_alloc(env, sizeof(*bytes) + len)) == NULL) + return NULL; + memset(bytes, 0, sizeof(*bytes)); + bytes->bytes = (u_char *)bytes + sizeof(*bytes); + bytes->length = len; + bytes->refs = 1; + bytes->freer = _jc_free_freer; + + /* Copy bytes */ + memcpy(bytes->bytes, data, len); + + /* Compute hash */ + _jc_hash_classbytes(bytes); + + /* Done */ + return bytes; +} + +/* + * Add a reference to a _jc_classbytes structure. + */ +_jc_classbytes * +_jc_dup_classbytes(_jc_classbytes *bytes) +{ + _jc_word old_refs; + + _JC_ASSERT(bytes->refs > 0); + do + old_refs = bytes->refs; + while (!_jc_compare_and_swap(&bytes->refs, old_refs, old_refs + 1)); + return bytes; +} + +/* + * Unreference a _jc_classbytes structure. + */ +void +_jc_free_classbytes(_jc_classbytes **bytesp) +{ + _jc_classbytes *bytes = *bytesp; + _jc_word old_refs; + + /* Sanity check */ + if (bytes == NULL) + return; + *bytesp = NULL; + + /* Decrement ref count */ + _JC_ASSERT(bytes->refs > 0); + do + old_refs = bytes->refs; + while (!_jc_compare_and_swap(&bytes->refs, old_refs, old_refs - 1)); + + /* Free structure if that was the last reference */ + if (bytes->refs == 0) + (*bytes->freer)(bytes); +} + +/* + * Compute the hash value of a class' bytes. + */ +static void +_jc_hash_classbytes(_jc_classbytes *bytes) +{ + u_char md5[MD5_DIGEST_LENGTH]; + MD5_CTX ctx; + int i; + + MD5_Init(&ctx); + MD5_Update(&ctx, bytes->bytes, bytes->length); + MD5_Final(md5, &ctx); + for (bytes->hash = i = 0; i < 8; i++) { + bytes->hash = (bytes->hash << 8) + | md5[MD5_DIGEST_LENGTH - 8 + i]; + } +} + +/* + * Free a class file that was memory mapped. + */ +static void +_jc_munmap_freer(_jc_classbytes *bytes) +{ + munmap(bytes->bytes, bytes->length); + _jc_vm_free(&bytes); +} + +/* + * Free a class file that was allocated from the system heap. + */ +static void +_jc_free_freer(_jc_classbytes *bytes) +{ + _jc_vm_free(&bytes); +} + Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/class_file.c URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/class_file.c?rev=294974&view=auto ============================================================================== --- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/class_file.c (added) +++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/class_file.c Tue Oct 4 19:19:16 2005 @@ -0,0 +1,300 @@ + +/* + * Copyright 2005 The Apache Software Foundation or its licensors, + * as applicable. + * + * 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. + * + * $Id: class_file.c,v 1.3 2005/03/18 04:17:48 archiecobbs Exp $ + */ + +#include "libjc.h" + +/* + * The VM class file table contains a node for each class file we've + * seen. Each node has a reference count. Every loaded class counts + * and each type dependency counts for one reference. + */ + +/* + * Search for a class file node in the VM class file tree. If not found, + * actively retrieve it by trying to load it with the supplied class loader. + * + * Because user class loaders control finding and loading their classes, + * the only way for us to force the acquisition of a classfile is to try to + * load the type. In general this may cause ClassCircularityErrors, but we + * don't care about them because by the time one is thrown, the class file + * has already been seen by us and its node stored in the class file tree. + * + * If successful, the node is returned with an extra reference. + * + * This function is used only if vm->without_classfiles is false. + */ +_jc_class_node * +_jc_get_class_node(_jc_env *env, _jc_class_loader *loader, const char *name) +{ + _jc_jvm *const vm = env->vm; + _jc_class_save class_save; + _jc_class_node *node; + _jc_class_node key; + _jc_type *type; + + /* Sanity check */ + _JC_ASSERT(name[0] != '['); + _JC_ASSERT(!vm->without_classfiles); + + /* Lock VM */ + _JC_MUTEX_LOCK(env, vm->mutex); + + /* Search for existing class file node */ + key.name = name; + if ((node = _jc_splay_find(&vm->classfiles, &key)) != NULL + && node->bytes != NULL) { + node->refs++; + _JC_MUTEX_UNLOCK(env, vm->mutex); + return node; + } + node = NULL; + + /* Unlock VM */ + _JC_MUTEX_UNLOCK(env, vm->mutex); + + /* If loader is the boot loader, we can get the class file directly */ + if (loader == vm->boot.loader) { + _jc_classbytes *cbytes; + + /* Find the class file bytes in the filesystem */ + if ((cbytes = _jc_bootcl_find_classbytes(env, + name, NULL)) == NULL) { + _jc_post_exception_info(env); + goto done; + } + + /* Try to add class file to class file tree */ + _JC_MUTEX_LOCK(env, vm->mutex); + node = _jc_ref_class_node(env, name, cbytes->hash, cbytes); + _JC_MUTEX_UNLOCK(env, vm->mutex); + + /* Free class file bytes */ + _jc_free_classbytes(&cbytes); + + /* Post stored exception if failed */ + if (node == NULL) + _jc_post_exception_info(env); + + /* Done */ + goto done; + } + + /* Initialize our class file save structure */ + class_save.name = name; + class_save.bytes = NULL; + class_save.next = env->class_save; + env->class_save = &class_save; + + /* + * Attempt to load the type in order to acquire the class file. + * If we succeeded, then the class file node must have been added. + */ + if ((type = _jc_load_type(env, loader, name)) != NULL) { + + /* Lock VM */ + _JC_MUTEX_LOCK(env, vm->mutex); + + /* Find node and add reference */ + node = _jc_splay_find(&vm->classfiles, &key); + _JC_ASSERT(node != NULL); + node->refs++; + + /* Unlock VM */ + _JC_MUTEX_UNLOCK(env, vm->mutex); + + /* Done */ + goto done; + } + + /* Ignore LinkageError's but bail out if any other exception */ + if (!_jc_unpost_exception(env, _JC_LinkageError)) + goto done; + + /* Lock VM */ + _JC_MUTEX_LOCK(env, vm->mutex); + + /* + * Look for class file in our class file save structure; if found, + * copy its info to VM tree and grab a reference to the new node. + */ + if (class_save.bytes != NULL) { + + /* Add node */ + node = _jc_ref_class_node(env, name, + class_save.bytes->hash, class_save.bytes); + _JC_MUTEX_UNLOCK(env, vm->mutex); + + /* Post exception if failed */ + if (node == NULL) + _jc_post_exception_info(env); + + /* Done */ + goto done; + } + + /* Unlock VM */ + _JC_MUTEX_UNLOCK(env, vm->mutex); + +done: + /* Destroy class save structure if we added one */ + if (env->class_save == &class_save) { + _jc_free_classbytes(&class_save.bytes); + env->class_save = class_save.next; + } + + /* Done */ + return node; +} + +/* + * Find/create a class file node in the VM's class file table. + * + * If the class already exists, the hash must be the same, otherwise + * a LinkageError is stored. Bump the reference count on the existing node. + * + * If no class by this name exists, add a new node with reference count 1. + * + * 'cbytes' should be NULL unless object generation is enabled and the + * classfile was loaded by a user-defined class loader. If successful, + * it will be copied to any newly created node. + * + * Stores an exception if unsuccessful. + * + * NOTE: This assumes the VM global mutex is locked. + */ +_jc_class_node * +_jc_ref_class_node(_jc_env *env, const char *name, + jlong hash, _jc_classbytes *cbytes) +{ + _jc_jvm *const vm = env->vm; + _jc_class_node *node; + _jc_class_node key; + size_t nlen; + + /* Sanity check */ + _JC_MUTEX_ASSERT(env, vm->mutex); + _JC_ASSERT(cbytes == NULL || cbytes->hash == hash); + + /* If code generation is disabled, don't save the class file bytes */ + if (!vm->generation_enabled) + cbytes = NULL; + + /* Search for existing node */ + key.name = name; + if ((node = _jc_splay_find(&vm->classfiles, &key)) != NULL) { + + /* Hash values must be the same */ + if (hash != node->hash) { + _JC_EX_STORE(env, LinkageError, "class file for `%s'" + " has an unexpected hash value 0x%" _JC_JLONG_FMT + " != 0x%" _JC_JLONG_FMT, name, hash, node->hash); + return NULL; + } + + /* Save class file if we don't have it yet */ + if (cbytes != NULL && node->bytes == NULL) + node->bytes = _jc_dup_classbytes(cbytes); + + /* Increment node reference count */ + node->refs++; + + /* Return node */ + return node; + } + + /* Create a new class file node */ + nlen = strlen(name); + if ((node = _jc_vm_alloc(env, sizeof(*node) + nlen + 1)) == NULL) + return NULL; + memcpy(node + 1, name, nlen + 1); + node->name = (char *)(node + 1); + node->hash = hash; + node->refs = 1; + node->bytes = cbytes != NULL ? _jc_dup_classbytes(cbytes) : NULL; + + /* Add node to our classfile tree */ +#ifndef NDEBUG + memset(&node->node, 0, sizeof(node->node)); +#endif + _jc_splay_insert(&vm->classfiles, node); + + /* Done */ + return node; +} + +/* + * Unreference several class file nodes from a dependency list. + * + * NOTE: This assumes the VM global mutex is locked. + */ +void +_jc_unref_class_deps(_jc_jvm *vm, _jc_class_depend *deps, int num_deps) +{ + int i; + + /* Sanity check */ + _JC_MUTEX_ASSERT(_jc_get_current_env(), vm->mutex); + + /* Iterate through list */ + for (i = 0; i < num_deps; i++) { + _jc_class_depend *const dep = &deps[i]; + _jc_class_node *node; + _jc_class_node key; + + /* Find node; it must be there */ + key.name = dep->name; + node = _jc_splay_find(&vm->classfiles, &key); + _JC_ASSERT(node != NULL); + _JC_ASSERT(node->refs > 0); + + /* Decrement reference count */ + if (--node->refs == 0) { + _jc_splay_remove(&vm->classfiles, node); + _jc_free_classbytes(&node->bytes); + _jc_vm_free(&node); + } + } +} + +/* + * Unreference a class file node. + * + * NOTE: This assumes the VM global mutex is locked. + */ +void +_jc_unref_class_node(_jc_jvm *vm, _jc_class_node **nodep) +{ + _jc_class_node *node = *nodep; + + /* Sanity check */ + _JC_MUTEX_ASSERT(_jc_get_current_env(), vm->mutex); + if (node == NULL) + return; + *nodep = NULL; + + /* Decrement reference count */ + _JC_ASSERT(node->refs > 0); + if (--node->refs == 0) { + _jc_splay_remove(&vm->classfiles, node); + _jc_free_classbytes(&node->bytes); + _jc_vm_free(&node); + } +} + Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/class_loader.c URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/class_loader.c?rev=294974&view=auto ============================================================================== --- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/class_loader.c (added) +++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/class_loader.c Tue Oct 4 19:19:16 2005 @@ -0,0 +1,365 @@ + +/* + * Copyright 2005 The Apache Software Foundation or its licensors, + * as applicable. + * + * 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. + * + * $Id: class_loader.c,v 1.12 2005/03/16 15:31:11 archiecobbs Exp $ + */ + +#include "libjc.h" + +/* + * Find the internal _jc_class_loader structure corresponding to + * a ClassLoader object. Create one if it doesn't already exist, + * but do so atomically. + * + * Posts an exception on failure. + */ +_jc_class_loader * +_jc_get_loader(_jc_env *env, _jc_object *obj) +{ + _jc_jvm *const vm = env->vm; + jboolean vm_locked = JNI_FALSE; + _jc_class_loader *loader; + _jc_resolve_info info; + + /* Check for null */ + if (obj == NULL) { + _jc_post_exception(env, _JC_NullPointerException); + return NULL; + } + _JC_ASSERT(_jc_subclass_of(obj, vm->boot.types.ClassLoader)); + + /* Lock VM */ + _JC_MUTEX_LOCK(env, vm->mutex); + vm_locked = JNI_TRUE; + + /* See if loader structure already exists */ + if ((loader = _jc_get_vm_pointer(obj, + vm->boot.fields.ClassLoader.vmdata)) != NULL) + goto done; + + /* Create a new loader structure */ + if ((loader = _jc_create_loader(env)) == NULL) { + _jc_post_exception_info(env); + goto done; + } + loader->instance = obj; + + /* Set the ClassLoader.vmdata field */ + _jc_set_vm_pointer(obj, vm->boot.fields.ClassLoader.vmdata, loader); + + /* Unlock VM */ + _JC_MUTEX_UNLOCK(env, vm->mutex); + vm_locked = JNI_FALSE; + + /* Create reference list with one reference */ + memset(&info, 0, sizeof(info)); + info.loader = loader, + info.implicit_refs = &loader->instance; + info.num_implicit_refs = 1; + + /* Put ClassLoader object on implicit reference list */ + if (_jc_merge_implicit_refs(env, &info) != JNI_OK) { + _jc_destroy_loader(vm, &loader); + _jc_post_exception_info(env); + goto done; + } + +done: + /* Unlock VM */ + if (vm_locked) + _JC_MUTEX_UNLOCK(env, vm->mutex); + + /* Done */ + return loader; +} + +/* + * Find the class loader to use when loading a type from a JNI function. + * + * The specified algorithm is: + * (a) Are we running within a JNI method? If so, find the ClassLoader + * associated with the native method's class. + * (b) Otherwise, we are being called using the invocation interface + * (e.g., from the main java startup C program) so we must use the + * loader returned by ClassLoader.getSystemClassLoader(). + * + * Note: the caller must retain a native reference to the class loader + * instance (if any) while the class loader is being used. + */ +_jc_class_loader * +_jc_get_jni_loader(_jc_env *env) +{ + _jc_jvm *const vm = env->vm; + + /* Use calling method's loader, if known */ + if (env->jni_method != NULL) + return env->jni_method->class->loader; + + /* Invoke ClassLoader.getSystemClassLoader() */ + if (_jc_invoke_static(env, + vm->boot.methods.ClassLoader.getSystemClassLoader) != JNI_OK) + return NULL; + + /* Get internal loader structure from ClassLoader instance */ + return _jc_get_loader(env, env->retval.l); +} + +/* + * Allocate and link a new classloader structure into a VM. + * + * An exception is stored if unsuccessful. + * + * NOTE: The global VM mutex must be held when calling this function. + */ +_jc_class_loader * +_jc_create_loader(_jc_env *env) +{ + _jc_jvm *const vm = env->vm; + _jc_class_loader *loader; + + /* Sanity check */ + _JC_MUTEX_ASSERT(env, vm->mutex); + + /* Create and initialize new structure */ + if ((loader = _jc_vm_zalloc(env, sizeof(*loader) + + vm->object_path_len * sizeof(*loader->objects_loaded))) == NULL) + return NULL; + loader->objects_loaded = (jboolean *)(loader + 1); + + /* Initialize class loader memory manager */ + _jc_uni_alloc_init(&loader->uni, _JC_CL_ALLOC_MIN_PAGES, + &vm->avail_loader_pages); + + /* Initialize mutex and condition variable */ + if (_jc_mutex_init(env, &loader->mutex) != JNI_OK) + goto fail1; + if (_jc_cond_init(env, &loader->cond) != JNI_OK) + goto fail2; + + /* Initialize initiated, defining, and partially derived type trees */ + _jc_splay_init(&loader->initiated_types, + _jc_node_cmp, _JC_OFFSETOF(_jc_type_node, node)); + _jc_splay_init(&loader->deriving_types, + _jc_node_cmp, _JC_OFFSETOF(_jc_type_node, node)); + _jc_splay_init(&loader->defined_types, + _jc_type_cmp, _JC_OFFSETOF(_jc_type, node)); + + /* Initialize native library list */ + STAILQ_INIT(&loader->native_libs); + + /* Link new class loader into the VM */ + LIST_INSERT_HEAD(&vm->class_loaders, loader, link); + + /* Done */ + return loader; + + /* Clean up after failure */ +fail2: _jc_mutex_destroy(&loader->mutex); +fail1: _jc_uni_alloc_free(&loader->uni); + _jc_vm_free(&loader); + return NULL; +} + +/* + * Free a class loader info structure. + * + * Most loader specific memory is allocated from the loader's memory + * pool, so it all gets automatically freed via _jc_cl_alloc_free(). + * + * NOTE: The global VM mutex must be held or the world must be stopped. + */ +void +_jc_destroy_loader(_jc_jvm *vm, _jc_class_loader **loaderp) +{ + _jc_class_loader *loader = *loaderp; + + /* Sanity check */ + _JC_ASSERT(vm->world_stopped || vm->world_ending); + + /* Sanity check */ + if (loader == NULL) + return; + *loaderp = NULL; + + /* Sanity check */ + _JC_ASSERT(loader->deriving_types.size == 0); + + /* Unload associated native libraries */ + _jc_unload_native_libraries(vm, loader); + + /* + * Walk the tree of classes defined by this loader + * and for each class: + * + * 1. Remove the class' methods from the method tree + * 2. Unreference class and dependencies from VM class file tree + * 3. Destroy any associated ELF linking information + */ + while (loader->defined_types.size > 0) { + _jc_class_node *cnode; + _jc_class_node key; + _jc_type *type; + int j; + + /* Get type at the root of the tree */ + _JC_ASSERT(loader->defined_types.root != NULL); + type = _JC_NODE2ITEM(&loader->defined_types, + loader->defined_types.root); + + /* The following stuff does not apply to array types */ + if (_JC_FLG_TEST(type, ARRAY)) + goto remove_type; + + /* Remove all this class' methods from the method tree */ + if (!_JC_ACC_TEST(type, INTERP)) { + for (j = 0; j < type->u.nonarray.num_methods; j++) { + _jc_method *const method + = type->u.nonarray.methods[j]; + + if (method->function == NULL) + continue; + _jc_splay_remove(&vm->method_tree, method); + } + } + + /* Unreference the class file associated with this class */ + key.name = type->name; + cnode = _jc_splay_find(&vm->classfiles, &key); + _JC_ASSERT(cnode != NULL); + _jc_unref_class_node(vm, &cnode); + + /* Unreference class files this class depends on */ + _jc_unref_class_deps(vm, type->u.nonarray.class_depends, + type->u.nonarray.num_class_depends); + + /* Free supers info (unresolved ELF types) */ + _jc_vm_free(&type->u.nonarray.supers); + + /* Unreference parsed class file or ELF object */ + if (_JC_ACC_TEST(type, INTERP)) + _jc_destroy_classfile(&type->u.nonarray.u.cfile); + else + _jc_elf_unref(&type->u.nonarray.u.elf); + +remove_type: + /* Remove this type from the tree */ + _jc_splay_remove(&loader->defined_types, type); + } + + /* Free implicit reference list */ + _jc_vm_free(&loader->implicit_refs); + + /* Free class loader memory */ + _jc_uni_alloc_free(&loader->uni); + + /* Destroy mutex and condition variable */ + _jc_mutex_destroy(&loader->mutex); + _jc_cond_destroy(&loader->cond); + + /* Unlink from VM */ + LIST_REMOVE(loader, link); + + /* Free class loader */ + _jc_vm_free(&loader); +} + +/* + * Wait for any other thread which the process of loading + * a type to finish. + * + * NOTE: This assumes that the loader mutex is locked. + */ +void +_jc_loader_wait(_jc_env *env, _jc_class_loader *loader) +{ + /* Sanity check */ + _JC_MUTEX_ASSERT(env, loader->mutex); + + /* Exit Java mode */ + _jc_stopping_java(env, "waiting for class loader %p (%s)", + loader, (loader->instance == NULL) ? + "boot loader" : loader->instance->type->name); + + /* Notify thread we're waiting */ + loader->waiters = JNI_TRUE; + + /* Wait for other thread */ + _JC_COND_WAIT(env, loader->cond, loader->mutex); + + /* Resume Java */ + _jc_resuming_java(env); +} + +/* + * Add some implicit references to the implicit reference list + * associated with a class loader. We keep this list sorted. + * This assumes that the 'refs' array is itself already sorted. + * + * If unsuccessful an exception is stored. + */ +jint +_jc_merge_implicit_refs(_jc_env *env, _jc_resolve_info *info) +{ + _jc_class_loader *const loader = info->loader; + _jc_object **refs = info->implicit_refs; + int num_refs = info->num_implicit_refs; + _jc_object **new_implicit_refs; + int i0; + int i; + int j; + + /* Avoid realloc() of size zero */ + if (num_refs == 0) + return JNI_OK; + + /* Lock loader */ + _JC_MUTEX_LOCK(env, loader->mutex); + + /* Extend loader's implicit reference array */ + if ((new_implicit_refs = _jc_vm_realloc(env, + loader->implicit_refs, (loader->num_implicit_refs + num_refs) + * sizeof(*loader->implicit_refs))) == NULL) { + _JC_MUTEX_UNLOCK(env, loader->mutex); + return JNI_ERR; + } + loader->implicit_refs = new_implicit_refs; + + /* Sortedly merge in new refs, working backwards */ + i = loader->num_implicit_refs; + for (j = num_refs; j > 0; j--) { + for (i0 = i; i > 0 + && loader->implicit_refs[i - 1] > refs[j - 1]; i--); + if (i > 0 && loader->implicit_refs[i - 1] == refs[j - 1]) { + num_refs--; + continue; + } + memmove(loader->implicit_refs + i + j, + loader->implicit_refs + i, + (i0 - i) * sizeof(*loader->implicit_refs)); + loader->implicit_refs[i + j - 1] = refs[j - 1]; + } + + /* Update array length */ + loader->num_implicit_refs += num_refs; + + /* Unlock loader */ + _JC_MUTEX_UNLOCK(env, loader->mutex); + + /* Done */ + return JNI_OK; +} + Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/class_object.c URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/class_object.c?rev=294974&view=auto ============================================================================== --- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/class_object.c (added) +++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/class_object.c Tue Oct 4 19:19:16 2005 @@ -0,0 +1,305 @@ + +/* + * Copyright 2005 The Apache Software Foundation or its licensors, + * as applicable. + * + * 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. + * + * $Id: class_object.c,v 1.12 2005/05/18 22:04:45 archiecobbs Exp $ + */ + +#include "libjc.h" + +/* Internal functions */ +static _jc_elf *_jc_load_object_dir(_jc_env *env, _jc_class_loader *loader, + const char *dir, const char *name); + +/* + * Find the ELF object that defines the named class, then load the ELF object + * and all types defined within it. All class file hash values in the ELF + * object must match any already stored in the VM class file tree. + * + * If unsuccessful an exception is stored. + */ +jint +_jc_load_object(_jc_env *env, _jc_class_loader *loader, const char *name) +{ + _jc_jvm *const vm = env->vm; + _jc_type *const key = (_jc_type *)((char *)&name + - _JC_OFFSETOF(_jc_type, name)); + int i; + + /* Sanity check */ + _JC_ASSERT(vm->loader_enabled); + + /* Try each entry in the object path */ + for (i = 0; i < vm->object_path_len; i++) { + _jc_objpath_entry *const ent = &vm->object_path[i]; + _jc_elf *elf; + +try_again: + switch (ent->type) { + case _JC_OBJPATH_DIRECTORY: + + /* Look for object file in this directory */ + if ((elf = _jc_load_object_dir(env, + loader, ent->pathname, name)) != NULL) + break; + + /* If something unexpected happened, bail out */ + if (env->ex.num != _JC_LinkageError) + return JNI_ERR; + + /* Try next entry */ + continue; + case _JC_OBJPATH_ELFFILE: + + /* Has this loader already loaded this object? */ + if (loader->objects_loaded[i]) + continue; + + /* Try to load specified ELF object file */ + if ((elf = _jc_elf_load(env, + loader, ent->pathname)) != NULL) + break; + + /* If something unexpected happened, bail out */ + if (env->ex.num != _JC_LinkageError) + return JNI_ERR; + + /* Mark this entry as erroneous */ + VERBOSE(OBJ, vm, "`%s' is invalid: %s", + ent->pathname, env->ex.msg); + ent->type = _JC_OBJPATH_ERROR; + + /* Try next entry */ + continue; + case _JC_OBJPATH_UNKNOWN: + { + struct stat info; + + /* Examine file; if invalid, skip it from now on */ + if (stat(ent->pathname, &info) == -1) { + VERBOSE(OBJ, vm, "`%s' is invalid: %s", + ent->pathname, strerror(errno)); + continue; + } + + /* If it's a directory, change type and try again */ + if ((info.st_mode & S_IFMT) == S_IFDIR) { + _JC_MUTEX_LOCK(env, vm->mutex); + if (ent->type == _JC_OBJPATH_UNKNOWN) + ent->type = _JC_OBJPATH_DIRECTORY; + _JC_MUTEX_UNLOCK(env, vm->mutex); + goto try_again; + } + + /* Assume it's an ELF file and try again */ + _JC_MUTEX_LOCK(env, vm->mutex); + if (ent->type == _JC_OBJPATH_UNKNOWN) + ent->type = _JC_OBJPATH_ELFFILE; + _JC_MUTEX_UNLOCK(env, vm->mutex); + goto try_again; + } + case _JC_OBJPATH_ERROR: + continue; + default: + _JC_ASSERT(JNI_FALSE); + } + + /* We should have a loaded ELF object at this point */ + _JC_ASSERT(elf != NULL); + + /* Does the ELF file define the type we're looking for? */ + if (_jc_splay_find(&elf->types, key) == NULL) { + VERBOSE(OBJ, vm, "failed: `%s' does not define `%s'", + elf->pathname, name); + _jc_elf_unref(&elf); + continue; + } + + /* Load all types defined in the object */ + if (_jc_derive_types_from_object(env, elf) != JNI_OK) { + if (env->ex.num != _JC_LinkageError) + return JNI_ERR; + VERBOSE(OBJ, vm, "failed: %s%s%s", + _jc_vmex_names[env->ex.num], + *env->ex.msg != '\0' ? ": " : "", env->ex.msg); + _jc_elf_unref(&elf); + continue; + } + + /* Output object file in object file list if desired */ + if (vm->object_list != NULL) { + fprintf(vm->object_list, "%s\n", elf->pathname); + fflush(vm->object_list); + } + + /* If object path entry was an ELF file, mark it as loaded */ + if (ent->type == _JC_OBJPATH_ELFFILE) { + _JC_ASSERT(!loader->objects_loaded[i]); + loader->objects_loaded[i] = JNI_TRUE; + } + + /* Release our reference on ELF file */ + _jc_elf_unref(&elf); + + /* Done */ + return JNI_OK; + } + + /* Not found */ + _JC_EX_STORE(env, LinkageError, + "no valid ELF object found containing `%s'", name); + return JNI_ERR; +} + +/* + * Find a class' ELF object file in a directory hierarchy and read + * it in if found. Here we also look for "_package.o". + * + * If unsuccessful an exception is stored. + */ +static _jc_elf * +_jc_load_object_dir(_jc_env *env, _jc_class_loader *loader, + const char *dir, const char *name) +{ + _jc_elf *elf; + char *ename; + char *path; + char *s; + + /* Generate encoded name for class */ + if ((ename = _JC_STACK_ALLOC(env, + _jc_name_encode(name, NULL, JNI_TRUE) + 1)) == NULL) + return NULL; + _jc_name_encode(name, ename, JNI_TRUE); + + /* Get buffer big enough for both pathnames we'll try */ + if ((path = _JC_STACK_ALLOC(env, strlen(dir) + + sizeof(_JC_FILE_SEPARATOR) + strlen(ename) + + sizeof(_JC_PACKAGE_OBJECT_NAME))) == NULL) + return NULL; + + /* Generate class-specific object file name */ + sprintf(path, "%s%s%s.o", dir, _JC_FILE_SEPARATOR, ename); + + /* Try to load class-specific ELF object file */ + if ((elf = _jc_elf_load(env, loader, path)) != NULL) + return elf; + + /* Generate package object file name */ + for (s = path + strlen(path); s > path && s[-1] != '/'; s--); + strcpy(s, _JC_PACKAGE_OBJECT_NAME); + + /* Try to load combo ELF object file */ + if ((elf = _jc_elf_load(env, loader, path)) != NULL) + return elf; + + /* Not found */ + return NULL; +} + +/* + * Generate the ELF object file for the named class. + * + * If unsuccessful an exception is posted. + */ +jint +_jc_generate_object(_jc_env *env, _jc_class_loader *loader, const char *name) +{ + _jc_jvm *const vm = env->vm; + jobject nameString = NULL; + jobject genObj = NULL; + jint status = JNI_ERR; + + /* Sanity check */ + _JC_ASSERT(vm->loader_enabled); + _JC_ASSERT(vm->generation_enabled); + + /* Is the "compiler" disabled? */ + if (vm->compiler_disabled) { + _jc_post_exception_msg(env, _JC_LinkageError, + "code generated is disabled"); + return JNI_ERR; + } + + /* Don't generate code during VM initialization */ + if (vm->initialization != NULL) { + _jc_post_exception_msg(env, _JC_LinkageError, + "can't generate code during VM initialization"); + return JNI_ERR; + } + + /* + * Are we already in the middle of generating something? + * If so, don't recurse because Soot is probably not reentrant. + * Hopefully we're just trying to acquire the class file. + */ + if (env->generating != NULL) { + _jc_post_exception_msg(env, _JC_LinkageError, + "recursive attempt to generate `%s' while generating `%s'", + name, env->generating); + return JNI_ERR; + } + + /* Verbosity */ + VERBOSE(GEN, vm, "generating ELF object for `%s'", name); + + /* Note that we're generating this object */ + env->generating = name; + + /* Get the Generate singleton object */ + if (_jc_invoke_static(env, vm->boot.methods.Generate.v) != JNI_OK) + goto fail; + if ((genObj = _jc_new_local_native_ref(env, env->retval.l)) == NULL) + goto fail; + + /* Put class name in a String object */ + if ((nameString = _jc_new_local_native_ref(env, + _jc_new_string(env, name, strlen(name)))) == NULL) + goto fail; + + /* Invoke Generate.v().generateObject() */ + if (_jc_invoke_virtual(env, vm->boot.methods.Generate.generateObject, + *genObj, *nameString, loader->instance) != JNI_OK) + goto fail; + + /* Done */ + status = JNI_OK; + +fail: + /* Turn off generating flag */ + _JC_ASSERT(env->generating == name); + env->generating = NULL; + + /* Clean up native refs */ + _jc_free_local_native_ref(&genObj); + _jc_free_local_native_ref(&nameString); + + /* Report any error */ + if (status != JNI_OK) { + if ((env->vm->verbose_flags & (1 << _JC_VERBOSE_GEN)) != 0) { + _jc_printf(vm, + "[verbose %s: object generation for `%s' failed: ", + _jc_verbose_names[_JC_VERBOSE_GEN], name); + _jc_fprint_exception_headline(env, + stdout, env->head.pending); + _jc_printf(vm, "]\n"); + } + } + + /* Done */ + return status; +} + Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/debug_line.c URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/debug_line.c?rev=294974&view=auto ============================================================================== --- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/debug_line.c (added) +++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/libjc/debug_line.c Tue Oct 4 19:19:16 2005 @@ -0,0 +1,647 @@ + +/* + * Copyright 2005 The Apache Software Foundation or its licensors, + * as applicable. + * + * 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. + * + * $Id: debug_line.c,v 1.7 2005/03/13 02:59:43 archiecobbs Exp $ + */ + +#include "libjc.h" + +/* Internal functions */ +static jint _jc_debug_line_add(_jc_env *env, _jc_map_state *state, + const void *pc, uint32_t cline); +static jint _jc_debug_line_finish(_jc_env *env, _jc_method *method, + _jc_class_loader *loader, _jc_map_state *state); +static Elf_Word _jc_read_leb128(const u_char **datap, int is_signed); +static int _jc_method_addr_cmp(const void *v1, const void *v2); + +/* + * Process stabs line number information. + * + * If unsuccessful an exception is stored. + */ +jint +_jc_debug_line_stabs(_jc_env *env, _jc_elf *elf, _jc_splay_tree *tree) +{ + _jc_elf_info *const info = elf->info; + const Elf_Shdr *const shdr = info->debug_lines.loadable.shdr; + _jc_stab *const stabs = (_jc_stab *)(info->map_base + shdr->sh_offset); + const int num_stabs = shdr->sh_size / sizeof(*stabs); + _jc_map_state state; + _jc_method *method; + int i; + + /* Sanity check */ + _JC_ASSERT(info->debug_lines.type == _JC_LINE_DEBUG_STABS); + + /* Initialize state */ + memset(&state, 0, sizeof(state)); + method = NULL; + + /* Run through stabs section */ + for (i = 0; i < num_stabs; i++) { + _jc_stab *const stab = &stabs[i]; + + /* Check type */ + switch (stab->type) { + case STAB_FUN: + { + _jc_method_node *node; + _jc_method_node key; + const char *fun; + const char *s; + + /* Check for end of method; reset state if so */ + if (stab->sindex == 0) { + + /* Were we skipping this method? */ + if (method == NULL) + continue; + + /* Finalize map */ + if (_jc_debug_line_finish(env, + method, elf->loader, &state) != JNI_OK) + goto fail; + method = NULL; + break; + } + + /* Already doing this function? */ + if (method != NULL) + continue; + + /* Get FUN string containing function name */ + fun = (const char *)info->debug_lines.strings + + stab->sindex; + + /* Sanity check not already within a method */ + if (method != NULL) { + _JC_EX_STORE(env, LinkageError, + "double opening stabs FUN entry `%s'", fun); + goto fail; + } + + /* Try to parse out class & method name from symbol */ + if (strncmp(fun, "_jc_", 4) != 0 + || (s = strchr(fun + 4, '$')) == NULL + || strncmp(s + 1, "method$", 7) != 0) + continue; + key.cname = fun + 4; + key.clen = s - key.cname; + key.mname = s + 8; + if ((s = strchr(key.mname, ':')) == NULL) + s += strlen(s); /* can this happen? */ + key.mlen = s - key.mname; + + /* Find corresponding method node, if any */ + if ((node = _jc_splay_find(tree, &key)) == NULL) + continue; + method = node->method; + _JC_ASSERT(method != NULL); + _JC_ASSERT(method->function != NULL); + _JC_ASSERT(!_JC_ACC_TEST(method, INTERP)); + _JC_ASSERT(method->u.exec.function_end != NULL); + + /* If method has no line number table, nothing to do */ + if (method->u.exec.u.linenum.len == 0) { + memset(&method->u.exec.u, + 0, sizeof(method->u.exec.u)); + method = NULL; + continue; + } + + /* Initialize state for this method */ + _JC_ASSERT(state.pc_map.len == 0); + _JC_ASSERT(state.last_linenum == 0); + _JC_ASSERT(state.last_map == 0); + state.linenum = method->u.exec.u.linenum; + memset(&method->u.exec.u, 0, sizeof(method->u.exec.u)); + break; + } + case STAB_SLINE: + + /* If skipping this method, skip lines */ + if (method == NULL) + continue; + + /* Add entry */ + if (_jc_debug_line_add(env, &state, + (const char *)method->function + stab->value, + stab->desc) != JNI_OK) + goto fail; + break; + default: + break; + } + } + + /* Clean up and exit */ + _jc_vm_free(&state.pc_map.map); + return JNI_OK; + +fail: + /* Clean up after failure */ + _jc_vm_free(&state.pc_map.map); + return JNI_ERR; +} + +/* + * Read a DWARF leb128 value. + */ +static inline Elf_Word +_jc_read_leb128(const u_char **datap, int is_signed) +{ + Elf_Word value; + int bitpos; + + for (bitpos = value = 0; ; bitpos += 7) { + const u_char byte = *(*datap)++; + + value |= (byte & 0x7f) << bitpos; + if ((byte & 0x80) == 0) + break; + } + if (is_signed && (value & (1 << (bitpos + 6))) != 0) + value |= (Elf_Word)~0L << (bitpos + 7); + return value; +} + +/* + * Process DWARF2 line number information. + * + * If unsuccessful an exception is stored. + */ +jint +_jc_debug_line_dwarf2(_jc_env *env, _jc_elf *elf, _jc_splay_tree *tree) +{ + _jc_elf_info *const info = elf->info; + const Elf_Shdr *const shdr = info->debug_lines.loadable.shdr; + const u_char *ptr = (const u_char *)info->map_base + shdr->sh_offset; + const u_char *const section_end = ptr + shdr->sh_size; + jboolean using64bit = JNI_FALSE; + _jc_dwarf2_line_hdr *hdr; + _jc_method_node **nodes; + _jc_map_state state; + _jc_method *method; + unsigned long totlen; + union _jc_value jvalue; + int node_index; + const u_char *ptr_end; + const u_char *pc; + int num_nodes; + int cline; + int i; + + /* Put nodes in a list and elide methods with no line number table */ + if ((nodes = _JC_STACK_ALLOC(env, + tree->size * sizeof(*nodes))) == NULL) + goto fail; + _jc_splay_list(tree, (void **)nodes); + for (i = num_nodes = 0; i < tree->size; i++) { + _jc_method_node *const node = nodes[i]; + _jc_method *const method = node->method; + + _JC_ASSERT(method != NULL); + _JC_ASSERT(method->function != NULL); + _JC_ASSERT(!_JC_ACC_TEST(method, INTERP)); + _JC_ASSERT(method->u.exec.function_end != NULL); + if (method->u.exec.u.linenum.len > 0) + nodes[num_nodes++] = nodes[i]; + } + + /* Anything to do? */ + if (num_nodes == 0) + return JNI_OK; + + /* Sort methods by starting address */ + qsort(nodes, num_nodes, sizeof(*nodes), _jc_method_addr_cmp); + + /* Initialize map state */ + memset(&state, 0, sizeof(state)); + node_index = 0; + method = NULL; + +again: + /* Read prologue header */ + memcpy(&jvalue, ptr, sizeof(jint)); + ptr += sizeof(jint); + totlen = jvalue.i; + if (totlen == 0xffffffff) { + memcpy(&jvalue, ptr, sizeof(jlong)); + ptr += sizeof(jlong); + totlen = jvalue.j; + using64bit = JNI_TRUE; + } else if (totlen == 0) { + memcpy(&jvalue, ptr, sizeof(jint)); + ptr += sizeof(jint); + totlen = jvalue.i; + using64bit = JNI_TRUE; + } + ptr_end = ptr + totlen; + ptr += 2; /* skip version */ + ptr += 4; /* skip header len */ + if (using64bit) + ptr += 4; + hdr = (_jc_dwarf2_line_hdr *)ptr; + ptr += sizeof(*hdr) + hdr->opcode_base - 1; + + /* Skip over directory table */ + while (*ptr++ != '\0') + ptr += strlen((const char *)ptr) + 1; + + /* Skip over file name table */ + while (*ptr++ != '\0') { + ptr += strlen((const char *)ptr) + 1; + (void)_jc_read_leb128(&ptr, JNI_FALSE); + (void)_jc_read_leb128(&ptr, JNI_FALSE); + (void)_jc_read_leb128(&ptr, JNI_FALSE); + } + + /* Initialize statement program state */ + pc = NULL; + cline = 1; + + /* Process statement program */ + while (ptr < ptr_end) { + jboolean writeout = JNI_FALSE; + jboolean reset = JNI_FALSE; + u_char opcode; + + if ((opcode = *ptr++) >= hdr->opcode_base) { /* special */ + opcode -= hdr->opcode_base; + pc += (opcode / hdr->line_range) + * hdr->minimum_instruction_length; + cline += hdr->line_base + opcode % hdr->line_range; + writeout = JNI_TRUE; + } else if (opcode == 0) { /* extended */ + unsigned int oplen; + u_char exop; + + oplen = _jc_read_leb128(&ptr, JNI_FALSE); + exop = *ptr++; + switch (exop) { + case DW_LNE_end_sequence: + reset = JNI_TRUE; + writeout = JNI_TRUE; + break; + case DW_LNE_set_address: + { + const u_char *new_pc; + + /* Get new PC */ + memcpy(&new_pc, ptr, sizeof(new_pc)); + + /* We don't support out-of-spec reversals */ + if (new_pc < pc) { + _JC_EX_STORE(env, LinkageError, + "address reversals in .debug_line" + " section are not supported"); + goto fail; + } + + /* OK */ + pc = new_pc; + break; + } + default: + break; + } + ptr += oplen - 1; + } else { /* standard */ + switch (opcode) { + case DW_LNS_copy: + writeout = JNI_TRUE; + break; + case DW_LNS_advance_pc: + pc += _jc_read_leb128(&ptr, JNI_FALSE) + * hdr->minimum_instruction_length; + break; + case DW_LNS_advance_line: + cline += _jc_read_leb128(&ptr, JNI_TRUE); + break; + case DW_LNS_const_add_pc: + pc += ((255 - hdr->opcode_base) + / hdr->line_range) + * hdr->minimum_instruction_length; + break; + case DW_LNS_fixed_advance_pc: + { + uint16_t advance; + + memcpy(&advance, ptr, 2); + pc += advance; + ptr += 2; + break; + } + default: + for (i = 0; i < hdr->standard_opcode_lengths[ + opcode - 1]; i++) + _jc_read_leb128(&ptr, JNI_FALSE); + break; + } + } + + /* Have we reached the next method? */ + if (method == NULL + && (const void *)pc + >= nodes[node_index]->method->function) { + + /* Initialize state for this method */ + _JC_ASSERT(state.pc_map.len == 0); + _JC_ASSERT(state.last_linenum == 0); + _JC_ASSERT(state.last_map == 0); + method = nodes[node_index]->method; + _JC_ASSERT(!_JC_ACC_TEST(method, INTERP)); + state.linenum = method->u.exec.u.linenum; + memset(&method->u.exec.u, 0, sizeof(method->u.exec.u)); + } + + /* Finished with the current method? */ + if (method != NULL + && (const void *)pc >= method->u.exec.function_end) { + + /* Finalize map for current method */ + if (_jc_debug_line_finish(env, + method, elf->loader, &state) != JNI_OK) + goto fail; + method = NULL; + + /* Look for next method */ + if (++node_index == num_nodes) + goto done; + } + + /* Write matrix row */ + if (writeout && method != NULL) { + if (_jc_debug_line_add(env, + &state, pc, cline) != JNI_OK) + goto fail; + } + + /* Reset after DW_LNE_end_sequence */ + if (reset) { + pc = NULL; + cline = 1; + } + } + if (ptr < section_end) + goto again; + +done: + /* Sanity check */ + _JC_ASSERT(method == NULL); +#ifndef NDEBUG + for (i = 0; i < num_nodes; i++) { + _jc_method_node *const node = nodes[i]; + _jc_method *const method = node->method; + + _JC_ASSERT(!_JC_ACC_TEST(method, INTERP)); + _JC_ASSERT(method->u.exec.u.pc_map.map != NULL); + } +#endif + + /* Done */ + return JNI_OK; + +fail: + /* Failed */ + return JNI_ERR; +} + +/* + * Map a PC return address within a function to a Java line number. + * This assumes the PC is within the function. Returns 0 if unknown. + * + * The PC return address should point to the instruction just after the + * instruction that makes the function call. + */ +int +_jc_exec_pc_to_jline(_jc_method *method, const void *pc) +{ + _jc_pc_map_info *const pcmap = &method->u.exec.u.pc_map; + _jc_pc_map *base; + int span; + + /* Sanity check */ + _JC_ASSERT(!_JC_ACC_TEST(method, INTERP)); + + /* Sanity check that the PC lies within the method */ + _JC_ASSERT(pc > method->function && pc <= method->u.exec.function_end); + + /* If PC map does not exist, bail out */ + if (!_JC_ACC_TEST(method, PCMAP)) + return 0; + + /* Binary search PC map */ + for (base = pcmap->map, span = pcmap->len; span != 0; span >>= 1) { + _jc_pc_map *const sample = &base[span >> 1]; + + if (pc <= sample->pc) + continue; + if (pc > (sample + 1)->pc) { + base = sample + 1; + span--; + continue; + } + return sample->jline; + } + + /* Not found */ + return 0; +} + +/* + * Add a new [addr => java line number] pair to the PC map, given + * an [addr => cline] mapping. We search the line number table to derive + * the intermediate [cline => line number table entry] mapping. If the + * address is before the first line number table entry, do nothing. + * + * We assume that the entries in the map are delivered roughly in order. + * Otherwise, this algorithm could be O(n^2) slow. + * + * If unsuccessful an exception is stored. + */ +static jint +_jc_debug_line_add(_jc_env *env, _jc_map_state *state, + const void *pc, uint32_t cline) +{ + int linenum_index; + int map_index; + + /* Sanity check */ + _JC_ASSERT(state->linenum.len > 0); + + /* + * Map C line number into line number table index. We expect + * this line number table index to be close to the last one. + * If it's not in the table, no mapping is needed. + */ + linenum_index = state->last_linenum; + if (cline > state->linenum.table[linenum_index].cline) { + while (linenum_index < state->linenum.len - 1 + && cline >= state->linenum.table[linenum_index + 1].cline) + linenum_index++; + } else { + while (linenum_index > 0 + && cline < state->linenum.table[linenum_index].cline) + linenum_index--; + if (cline < state->linenum.table[0].cline) + return JNI_OK; + } + + /* + * Find insertion point for the new entry. We expect it + * to be near the previous insertion point, if any. + */ + map_index = state->last_map; + if (state->pc_map.len == 0) { + _JC_ASSERT(map_index == 0); + goto insert; + } + if (pc > state->pc_map.map[map_index].pc) { + while (map_index < state->pc_map.len + && pc >= state->pc_map.map[++map_index].pc) + ; + } else { + while (map_index > 0 && pc < state->pc_map.map[map_index].pc) + map_index--; + } + +insert: + /* Extend PC map to make room for the new entry */ + if (state->pc_map.len == state->map_alloc) { + void *mem; + + state->map_alloc = state->map_alloc * 2 + 32; + if ((mem = _jc_vm_realloc(env, state->pc_map.map, + state->map_alloc * sizeof(*state->pc_map.map))) == NULL) + return JNI_ERR; + state->pc_map.map = mem; + } + + /* Shift higher entries up by one to make room */ + if (map_index < state->pc_map.len) { + memmove(state->pc_map.map + map_index + 1, + state->pc_map.map + map_index, + (state->pc_map.len - map_index) + * sizeof(*state->pc_map.map)); + } + + /* Fill in new entry */ + state->pc_map.map[map_index].pc = pc; + state->pc_map.map[map_index].jline + = state->linenum.table[linenum_index].jline; + + /* Update state */ + state->last_linenum = linenum_index; + state->last_map = map_index; + state->pc_map.len++; + + /* Done */ + return JNI_OK; +} + +/* + * Finalize a PC map and store it with its method. + * + * If unsuccessful an exception is stored. + */ +static jint +_jc_debug_line_finish(_jc_env *env, _jc_method *method, + _jc_class_loader *loader, _jc_map_state *state) +{ + _jc_pc_map_info *const pcmap = &method->u.exec.u.pc_map; + int num_dups; + int i; + int j; + + /* Sanity check */ + _JC_ASSERT(!_JC_ACC_TEST(method, INTERP)); + + /* TODO XXX understand why this happens */ + if (_JC_ACC_TEST(method, PCMAP)) + goto done; + + /* Count number of redundant entries */ + for (num_dups = 0, i = 0; i < state->pc_map.len - 1; i++) { + _jc_pc_map *const this = &state->pc_map.map[i]; + _jc_pc_map *const next = &state->pc_map.map[i + 1]; + + _JC_ASSERT(this->pc <= next->pc); + if (this->jline == next->jline) + num_dups++; + } + + /* Allocate memory for compressed PC map */ + _JC_ASSERT(pcmap->map == NULL); + pcmap->len = state->pc_map.len - num_dups; + _JC_MUTEX_LOCK(env, loader->mutex); + pcmap->map = _jc_cl_alloc(env, loader, + (pcmap->len + 1) * sizeof(*state->pc_map.map)); + _JC_MUTEX_UNLOCK(env, loader->mutex); + if (pcmap->map == NULL) + return JNI_ERR; + + /* Copy non-redundant entries */ + for (i = j = 0; i < state->pc_map.len; j++) { + memcpy(&pcmap->map[j], + &state->pc_map.map[i], sizeof(*state->pc_map.map)); + while (++i < state->pc_map.len + && state->pc_map.map[i].jline + == state->pc_map.map[i - 1].jline); + } + _JC_ASSERT(j == pcmap->len); + + /* Add terminating entry */ + pcmap->map[j].pc = method->u.exec.function_end; + pcmap->map[j].jline = 0; + +#if 0 + { + printf("MAP FOR `%s.%s%s':\n", + method->class->name, method->name, method->signature); + for (i = 0; i < pcmap->len; i++) { + _jc_pc_map *const map = &pcmap->map[i]; + + printf(" [%d] %p -> %d\n", i, map->pc, map->jline); + } + } +#endif + +done: + /* Reset map state */ + state->pc_map.len = 0; + state->last_linenum = 0; + state->last_map = 0; + state->linenum.len = 0; + state->linenum.table = NULL; + + /* Done */ + method->access_flags |= _JC_ACC_PCMAP; + return JNI_OK; +} + +static int +_jc_method_addr_cmp(const void *v1, const void *v2) +{ + _jc_method_node *const node1 = *((_jc_method_node **)v1); + _jc_method_node *const node2 = *((_jc_method_node **)v2); + + return (node1->method->function > node2->method->function) + - (node1->method->function < node2->method->function); +} + +