Exemplo n.º 1
0
void free_clinit_memory(struct methodblock *mb)
{
    /* This function is somewhat a hack. It fixes the problem in 1.1.3
     * and before. sysFree may be called on the wrong memory block if
     * the exception attribute comes before the code attribute.
     */
    /* If there is no exceptions attribute, or if both has already
     * been freed. */
    if (mb->exceptions == NULL) {
        if (mb->code) {
	    sysFree(mb->code);
	    mb->code = NULL;
	}
	return;
    }

    /* If both attributes exist, free the one at the lower address */
    if ((char *)mb->code < (char *)mb->exceptions)
        sysFree(mb->code);
    else
        sysFree(mb->exceptions);

    mb->code = NULL;
    mb->exceptions = NULL;
}
Exemplo n.º 2
0
/*
 * Unmap a range of virtual memory.  Note that the size asked for here
 * is literally what the upper level has asked for.  We need to do any
 * rounding, etc. here.  If unmapping fails return 0, otherwise return
 * the address of the base of the unmapped memory.
 */
void *
sysUnmapMem(void *requestedAddr, size_t requestedSize, size_t *unmappedSize)
{
    void *unmappedAddr;
    int ret;

    *unmappedSize = roundUpToGrain(requestedSize);
#ifdef USE_MALLOC
    sysFree(requestedAddr);
    ret = 1;
#else
    ret = unmapChunk(requestedAddr, *unmappedSize);
#endif /* USE_MALLOC */
    if (ret) {
        unmappedAddr = requestedAddr;
        Log4(2,
             "sysUnmapMem: 0x%x bytes at 0x%x (request: 0x%x bytes at 0x%x)\n",
             *unmappedSize, unmappedAddr, requestedSize, requestedAddr);
    } else {
        unmappedAddr = 0;
        Log2(2, "sysUnmapMem failed: (request: 0x%x bytes at 0x%x)\n",
             requestedSize, requestedAddr);
    }
    return unmappedAddr;
}
Exemplo n.º 3
0
Object *createConstructorObject(MethodBlock *mb) {
    Object *reflect_ob, *vm_reflect_ob, *classes;
    char *signature, *sig;

    if((reflect_ob = allocObject(cons_reflect_class)) == NULL)
        return NULL;

    if((vm_reflect_ob = allocObject(vmcons_reflect_class)) == NULL)
        return NULL;

    signature = sig = sysMalloc(strlen(mb->type) + 1);
    strcpy(sig, mb->type);

    classes = convertSig2ClassArray(&sig, mb->class);
    sysFree(signature);

    if(classes == NULL)
        return NULL;

    INST_DATA(vm_reflect_ob, Class*, vm_cons_class_offset) = mb->class;
    INST_DATA(vm_reflect_ob, Object*, vm_cons_param_offset) = classes;
    INST_DATA(vm_reflect_ob, int, vm_cons_slot_offset) =
                                     mb - CLASS_CB(mb->class)->methods;

    /* Link the Java-level and VM-level objects together */
    INST_DATA(vm_reflect_ob, Object*, vm_cons_cons_offset) = reflect_ob;
    INST_DATA(reflect_ob, Object*, cons_cons_offset) = vm_reflect_ob;

    return reflect_ob;
}
Exemplo n.º 4
0
int classlibInitialiseNatives() {
    Class *field_accessor;
    FieldBlock *base_fb = NULL;
    char *dll_path = getBootDllPath();
    char *dll_name = getDllName("java");
    char path[strlen(dll_path) + strlen(dll_name) + 2];

    strcat(strcat(strcpy(path, dll_path), "/"), dll_name);
    sysFree(dll_name);

    if(!resolveDll(path, NULL)) {
        jam_fprintf(stderr, "Error initialising natives: couldn't open "
                            "libjava.so: use -verbose:jni for more "
                            "information\n");
        return FALSE;
    }

    field_accessor = findSystemClass0(SYMBOL(
                         sun_reflect_UnsafeStaticFieldAccessorImpl));

    if(field_accessor != NULL)
        base_fb = findField(field_accessor, SYMBOL(base),
                                            SYMBOL(sig_java_lang_Object));

    if(base_fb == NULL) {
        jam_fprintf(stderr, "Error initialising natives: %s "
                            "missing or malformed\n",
                            SYMBOL(sun_reflect_UnsafeStaticFieldAccessorImpl));
        return FALSE;
    }

    hideFieldFromGC(base_fb);

    return initialiseJVMInterface();
}
Exemplo n.º 5
0
void inlineBlockWrappedOpcode(MethodBlock *mb, Instruction *pc) {
    PrepareInfo *prepare_info = pc->operand.pntr;
    OpcodeInfo *info;
    int i;

    Thread *self = threadSelf();
    rewriteLock(self);

    for(i = 0; i < HANDLERS; i++)
        if(pc->handler == handler_entry_points[i][OPC_INLINE_REWRITER])
            break;

    if(i == HANDLERS) {
        rewriteUnlock(self);
        return;
    }

    pc->handler = handler_entry_points[0][GOTO_START];
    rewriteUnlock(self);

    /* Unwrap the original handler's operand */
    pc->operand = prepare_info->operand;
    MBARRIER();

    /* Unwrap the original handler */
    info = &prepare_info->block.opcodes[prepare_info->block.length-1];
    pc->handler = handler_entry_points[info->cache_depth][info->opcode];

    inlineBlock(mb, &prepare_info->block);
    sysFree(prepare_info);
}
Exemplo n.º 6
0
/* A method's quick prepare info list holds prepare information for all
   blocks within the method that end with a quickened instruction.  If
   the quickened instruction being executed is in the list we must have
   reached the end of a block and we need to inline it */
void checkInliningQuickenedInstruction(Instruction *pc, MethodBlock *mb) {

    /* As there could be multiple threads executing this method,
       the list must be protected with a lock.  However, the 
       fast case of an empty list doesn't need locking. */
    if(mb->quick_prepare_info) {
        QuickPrepareInfo *info, *last = NULL;

        Thread *self = threadSelf();
        rewriteLock(self);

        /* Search list */
        info = mb->quick_prepare_info;
        for(; info && info->quickened != pc; last = info, info = info->next);

        /* If prepare info found, remove it from the list */
        if(info) {
            if(last)
                last->next = info->next;
            else
                mb->quick_prepare_info = info->next;
        }

        rewriteUnlock(self);

        /* If prepare info found, inline block (no need to
           hold lock) */
        if(info) {
            inlineBlock(mb, &info->block);
            sysFree(info);
        }
    }
}
Exemplo n.º 7
0
static void freeBuffers(CICcontext * context)
{
    if (context->pass == 1) {
        CICmallocs *mallocs = context->pass1.mallocs;
	while (mallocs) {
	    CICmallocs *tmp = mallocs;
	    mallocs = mallocs->next;
	    sysFree(tmp);
	}
	context->pass1.mallocs = 0;
    } else {
        sysFree(context->pass2.malloc_buffer);
        context->pass2.malloc_buffer = 0;
        sysFree(context->pass2.clinit_buffer);
        context->pass2.clinit_buffer = 0;
    }
}
Exemplo n.º 8
0
/*
 * Cleanup the data structure allocated as above.
 * For JDK 1.2, this function is not called ...
 */
void FinalizeIO() {
    int i;
    for (i = 0; i < fd_limit; i++) {
        mutexDestroy(&fd_table[i].lock);
    }
    sysFree(fd_table);
    fd_table = 0;
}
Exemplo n.º 9
0
/* Load a .class file normally from the local disk */
static ClassClass *
LoadClassFromFile(char *fn, char *dir, char *class_name)
{
    extern int OpenCode(char *, char *, char *, struct stat*);
    struct stat st;
    ClassClass *cb = 0;
    int codefd = -1;
    int	retryCount = 0;
    unsigned char *external_class;
    char *detail;

    codefd = OpenCode(fn, NULL, dir, &st);

    if (codefd < 0)		/* open failed */
	return 0;

    /* Snarf the file into memory. */
    external_class = (unsigned char *)sysMalloc(st.st_size);
    if (external_class == 0)
        goto failed;
    if (sysRead(codefd, external_class, st.st_size) != st.st_size)
        goto failed;
    sysClose(codefd);
    codefd = -1;

    /* Create the internal class */
    cb = allocClassClass();
    if (cb == NULL || 
	!createInternalClass(external_class, external_class + st.st_size, 
			     cb, NULL, class_name, &detail)) {
	sysFree(external_class);
	goto failed;
    }
    sysFree(external_class);

    if (verbose)
	jio_fprintf(stderr, "[Loaded %s]\n", fn);
    return cb;
failed:
    if (codefd >= 0)
	sysClose(codefd);
    if (cb != 0)
        FreeClass(cb);
    return 0;
}
Exemplo n.º 10
0
Arquivo: utf8.c Projeto: webos21/xi
char *copyUtf8(char *string) {
    char *buff = xi_strcpy(sysMalloc(xi_strlen(string) + 1), string);
    char *found = findHashedUtf8(buff, TRUE);

    if(found != buff)
        sysFree(buff);

    return found;
}
Exemplo n.º 11
0
Arquivo: dll.c Projeto: OPSF/uClinux
char *mangleClassAndMethodName(MethodBlock *mb) {
    char *classname = CLASS_CB(mb->class)->name;
    char *methodname = mb->name;
    char *nonMangled = (char*) sysMalloc(strlen(classname) + strlen(methodname) + 7);
    char *mangled;

    sprintf(nonMangled, "Java/%s/%s", classname, methodname);

    mangled = mangleString(nonMangled);
    sysFree(nonMangled);
    return mangled;
}
Exemplo n.º 12
0
void FreeClass(ClassClass *cb) 
{
    int i;
    struct methodblock *mb;

    for (i = cbMethodsCount(cb), mb = cbMethods(cb); --i >= 0; mb++) {
        if (strcmp(mb->fb.name, "<clinit>") == 0 &&
	    strcmp(mb->fb.signature, "()V") == 0 &&
	    mb->code_length /* not external */ )
	    free_clinit_memory(mb);
    }

    sysFree(cbConstantPool(cb));

    sysFree(cbMethodTableMem(cb));
    sysFree(cbSlotTable(cb));
    /* Interface method tables can be shared between child and super classes
     */
    if (cbImplementsCount(cb) != 0 || cbIsInterface(cb))
        sysFree(cbIntfMethodTable(cb));
}
Exemplo n.º 13
0
void inlineSequence(MethodBlock *mb, CodeBlock *info, int start, int len) {
    int code_len = goto_len + sizeof(CodeBlockHeader);
    Instruction *instructions = &info->start[start];
    OpcodeInfo *opcodes = &info->opcodes[start];
    CodeBlockHeader *hashed_block, *block;
    int aligned_len, i;
    char *pntr;

    /* Calculate sequence length */
    for(i = 0; i < len; i++)
        code_len += handler_sizes[opcodes[i].cache_depth][opcodes[i].opcode];

    aligned_len = ALIGN(code_len);

    /* We malloc memory for the block rather than allocating code memory.
       This reduces fragmentation of the code memory in the case where we
       use an existing block and must free the new sequence */
    block = sysMalloc(aligned_len);

    /* Store length at beginning of sequence */
    block->len = aligned_len;
    pntr = (char *)(block + 1);

    /* Concatenate the handler bodies together */
    for(i = 0; i < len; i++) {
        int size = handler_sizes[opcodes[i].cache_depth][opcodes[i].opcode];

        memcpy(pntr, instructions[i].handler, size);
        pntr += size;
    }

    /* Add the dispatch onto the end of the super-instruction */
    memcpy(pntr, goto_start, goto_len);

    /* Pad with zeros up to block length */
    for(pntr += goto_len; code_len < aligned_len; code_len++)
        *pntr++ = 0;

    /* Look up new block in inlined block cache */
    hashed_block = findCodeBlock(block);
    sysFree(block);

    if(hashed_block != NULL) {
        /* Replace first handler with new inlined block */
        instructions[0].handler = hashed_block + 1;
        MBARRIER();

        TRACE("InlineSequence %s start %p (%d) instruction len %d code len %d sequence %p\n",
              mb->name, instructions, start, len, code_len, instructions[0].handler);
    }
}
Exemplo n.º 14
0
Arquivo: dll.c Projeto: OPSF/uClinux
char *mangleSignature(MethodBlock *mb) {
    char *type = mb->type;
    char *nonMangled;
    char *mangled;
    int i;

    /* find ending ) */
    for(i = strlen(type) - 1; type[i] != ')'; i--);

    nonMangled = (char *) sysMalloc(i);
    strncpy(nonMangled, type + 1, i - 1);
    nonMangled[i - 1] = '\0';
    
    mangled = mangleString(nonMangled);
    sysFree(nonMangled);
    return mangled;
}
Exemplo n.º 15
0
static void *shell(void *args) {
    void *start = ((void**)args)[1];
    Thread *self = ((Thread**)args)[2];

    if(main_exited)
        return NULL;

    /* VM helper threads should be added to the system group, but this doesn't
       exist.  As the root group is main, we add it to that for now... */
    attachThread(((char**)args)[0], TRUE, &self, self,
                 //(Object*)INST_DATA(main_ee.thread)[group_offset]);
                 (Object*)INST_DATA(main_thread.thread)[group_offset]);

    sysFree(args);
    (*(void(*)(Thread*))start)(self);
    return NULL;
}
Exemplo n.º 16
0
void inlineBlock(MethodBlock *mb, CodeBlock *block) {
    int start, len, i;

    for(start = i = 0; i < block->length; i++) {
        int cache_depth = block->opcodes[i].cache_depth;
        int opcode = block->opcodes[i].opcode;
        int op1, op2;

        /* The block opcodes contain the "un-quickened" opcode.
           This could have been quickened to one of several quick
           versions. */
        switch(opcode) {
            case OPC_LDC:
                op1 = OPC_LDC_QUICK;
                op2 = OPC_LDC_W_QUICK;
                break;

            case OPC_GETSTATIC:
                op1 = OPC_GETSTATIC_QUICK;
                op2 = OPC_GETSTATIC2_QUICK;
                break;

             case OPC_PUTSTATIC:
                op1 = OPC_PUTSTATIC_QUICK;
                op2 = OPC_PUTSTATIC2_QUICK;
                break;

            case OPC_GETFIELD:
                op1 = OPC_GETFIELD_QUICK;
                op2 = OPC_GETFIELD2_QUICK;
                break;

            case OPC_PUTFIELD:
                op1 = OPC_PUTFIELD_QUICK;
                op2 = OPC_PUTFIELD2_QUICK;
                break;

            case OPC_NEW: case OPC_ANEWARRAY: case OPC_CHECKCAST:
            case OPC_INVOKESTATIC: case OPC_INVOKEINTERFACE:
            case OPC_INVOKEVIRTUAL: case OPC_INVOKESPECIAL:
            case OPC_MULTIANEWARRAY: case OPC_INSTANCEOF:
                op1 = op2 = GOTO_END;
                break;

            default:
                op1 = op2 = -1;
                break;
        }

        if(op1 > 0) {
            /* Match which quickened opcode */
            opcode = handler_entry_points[cache_depth][op1]
                            == (char*) block->start[i].handler ? op1 : op2;
            block->opcodes[i].opcode = opcode;
        }

        /* A non-relocatable opcode ends a sequence */
        if(handler_sizes[cache_depth][opcode] < 0) {
            len = i - start;

            if(len > 0)
                inlineSequence(mb, block, start, len);

            start = i + 1;
        }
    }

    /* Inline the remaining sequence */
    len = block->length - start;
    if(len > 0)
        inlineSequence(mb, block, start, len);

    sysFree(block->opcodes);
}
Exemplo n.º 17
0
Arquivo: dll.c Projeto: OPSF/uClinux
char *mangleString(char *utf8) {
    int len = utf8Len(utf8);
    unsigned short *unicode = (unsigned short*) sysMalloc(len * 2);
    char *mangled, *mngldPtr;
    int i, mangledLen = 0;

    convertUtf8(utf8, unicode);

    /* Work out the length of the mangled string */

    for(i = 0; i < len; i++) {
        unsigned short c = unicode[i];
        switch(c) {
            case '_':
            case ';':
            case '[':
                mangledLen += 2;
                break;

           default:
                mangledLen += isalnum(c) ? 1 : 6;
                break;
        }
    }

    mangled = mngldPtr = (char*) sysMalloc(mangledLen + 1);

    /* Construct the mangled string */

    for(i = 0; i < len; i++) {
        unsigned short c = unicode[i];
        switch(c) {
            case '_':
                *mngldPtr++ = '_';
                *mngldPtr++ = '1';
                break;
            case ';':
                *mngldPtr++ = '_';
                *mngldPtr++ = '2';
                break;
            case '[':
                *mngldPtr++ = '_';
                *mngldPtr++ = '3';
                break;

            case '/':
                *mngldPtr++ = '_';
                break;

            default:
                if(isalnum(c))
                    *mngldPtr++ = c;
                else
                    mngldPtr += sprintf(mngldPtr, "_0%04x", c);
                break;
        }
    }

    *mngldPtr = '\0';

    sysFree(unicode);
    return mangled;
}
Exemplo n.º 18
0
Arquivo: zip.c Projeto: Liam1206/jamvm
char *findArchiveEntry(char *pathname, ZipFile *zip, int *uncomp_len) {
    int offset, path_len, comp_len, extra_len, comp_method;
    unsigned char *decomp_buff, *comp_data;
    unsigned char *dir_entry;

    dir_entry = (unsigned char *)findArchiveDirEntry(pathname, zip);
    if(dir_entry == NULL)
        return NULL;

    /* Found the file -- the pathname points directly into the
       directory entry.  Read the values relative to it */

    /* Offset of the file entry relative to the start of the file */
    offset = READ_LE_INT(dir_entry + (CEN_FILE_LOCALHDR_OFFSET -
                                      CEN_FILE_HEADER_LEN));

    if((offset + LOC_FILE_HEADER_LEN) > zip->length)
        return NULL;

    /* Get the variable length part of the local file header */
    extra_len = READ_LE_SHORT(zip->data + offset + LOC_FILE_EXTRA_OFFSET);

    /* Get the path_len again */
    path_len = READ_LE_SHORT(dir_entry + (CEN_FILE_PATHLEN_OFFSET -
                                          CEN_FILE_HEADER_LEN));

    /* The file's length when uncompressed -- this is passed out */
    *uncomp_len = READ_LE_INT(dir_entry + (CEN_FILE_UNCOMPLEN_OFFSET -
                                           CEN_FILE_HEADER_LEN));

    /* The compressed file's length, i.e. the data size in the file */
    comp_len = READ_LE_INT(dir_entry + (CEN_FILE_COMPLEN_OFFSET -
                                        CEN_FILE_HEADER_LEN));

    /* How the file is compressed */
    comp_method = READ_LE_SHORT(dir_entry + (CEN_FILE_COMPMETH_OFFSET -
                                             CEN_FILE_HEADER_LEN));

    /* Calculate the data start */
    offset += LOC_FILE_HEADER_LEN + path_len + extra_len;

    /* Make sure we're not reading outside the file */
    if((offset + comp_len) > zip->length)
        return NULL;

    comp_data = zip->data + offset;
    decomp_buff = sysMalloc(*uncomp_len);

    switch(comp_method) {
        case COMP_STORED:
            /* Data isn't compressed, so just return it "as is" */
            memcpy(decomp_buff, comp_data, comp_len);
            return (char*)decomp_buff;

        case COMP_DEFLATED: {
            z_stream stream;
            int err;

            stream.next_in = comp_data;
            stream.avail_in = comp_len;
            stream.next_out = decomp_buff;
            stream.avail_out = *uncomp_len;

            stream.zalloc = Z_NULL;
            stream.zfree = Z_NULL;

            /* Use a negative windowBits value to stop the inflator looking
               for a header */
            if(inflateInit2(&stream, -MAX_WINDOW_BITS) != Z_OK)
                goto error;

            err = inflate(&stream, Z_SYNC_FLUSH);
            inflateEnd(&stream);

            if(err == Z_STREAM_END || (err == Z_OK && stream.avail_in == 0))
                return (char*)decomp_buff;
            break;
        }

        default:
            break;
    }

error:
    sysFree(decomp_buff);
    return NULL;
}
Exemplo n.º 19
0
/**
 * Ends use of va_list
 */
EXTERNAL void sysVaEnd(va_list *ap)
{
  TRACE_PRINTF("%s: sysVaEnd\n", Me);
  va_end(*ap);
  sysFree(ap);
}
Exemplo n.º 20
0
bool_t
createInternalClass1(unsigned char *ptr, unsigned char *end_ptr, 
		     ClassClass *cb, struct Hjava_lang_ClassLoader *loader, 
		     char *name, char **detail)
{
    struct CICcontext context_block;
    struct CICcontext *context = &context_block;
    struct Classjava_lang_Class *ucb = unhand(cb);

    /* Set up the context */
    context->ptr = ptr;
    context->end_ptr = end_ptr;
    context->cb = cb;
    context->detail = detail;

    if (setjmp(context->jump_buffer)) {
	/* We've gotten an error of some sort */
        /* See comments below about zeroing these two fields before
	 * freeing the temporary buffer.
         */
        cbConstantPool(cb) = NULL;
        cbFields(cb) = NULL;
	/* Zero out the method out so that freeClass will not try
	   to free the clinit method */
	cbMethodsCount(cb) = 0;

	freeBuffers(context);
	return FALSE;
    }

    context->in_clinit = 0;
    context->pass1.mallocs = 0;
    context->malloc_size = 0;
    context->clinit_size = 0;

    /* The first pass allows us to uncover any class format errors
       and find out the size of the buffer needed. */
    context->pass = 1;
    createInternalClass0(context, cb, loader, name);

    /* We must set the following two fields to zero before we free
     * the temporary buffers, because markClassClass may scan a
     * partially constructed class block in the second pass.
     * If these two fields are set to zero, markClassClass will
     * not scan the constant pool and field blocks, which may
     * point to freed memory.
     */
    cbConstantPool(cb) = NULL;
    cbFields(cb) = NULL;
    /* Zero out the method out so that freeClass will not try
       to free the clinit method */
    cbMethodsCount(cb) = 0;
    freeBuffers(context);

    context->ptr = ptr;   /* rewind the raw class data */

    context->pass2.malloc_buffer = sysCalloc(1, context->malloc_size);
    if (context->pass2.malloc_buffer == 0)
        JAVA_ERROR(context, "out of memory");

    if (context->clinit_size) {
        context->pass2.clinit_buffer = sysCalloc(1, context->clinit_size);
	if (context->pass2.clinit_buffer == 0) {
            sysFree(context->pass2.malloc_buffer);
	    JAVA_ERROR(context, "out of memory");
	}
    }

    context->pass2.malloc_ptr = context->pass2.malloc_buffer;
    context->pass2.clinit_ptr = context->pass2.clinit_buffer;

    /* The second pass accomplishes the real task. */
    context->pass = 2;
    createInternalClass0(context, cb, loader, name);

    /* Valid class - let's put it in the class table. */
    AddBinClass(cb);
    return TRUE;
}
Exemplo n.º 21
0
native_netscape_javascript_JSObject_call(
    JRIEnv* env,
    struct netscape_javascript_JSObject* self,
    struct java_lang_String *method,
    jobjectArray args)
{
#ifndef JAVA
	return NULL;
#else
    JSContext *cx;
    JSObject *jso;
    JSSavedState saved;
    const char *cstr;
    struct java_lang_Object *ret;
    int total_argc, argc, i;
    jsval *argv;
    jsval rval;
    int cost = 0;

    if (!enterJS(env, self, &cx, &jso, &saved))
        return NULL;

    if (! method ||
        ! (cstr = JRI_GetStringPlatformChars(env, method,
					     (const jbyte *) cx->charSetName,
					     (jint) cx->charSetNameLength))) {
        /* FIXME this should be an error of some sort */
        js_throwJSException(env, "illegal member name");
        ret = NULL;
	goto do_exit;
    }

    if (args) {
        total_argc = JRI_GetObjectArrayLength(env, args);
        argv = sysMalloc(total_argc * sizeof(jsval));
    } else {
        total_argc = 0;
        argv = 0;
    }

    for (argc = 0; argc < total_argc; argc++) {
        jref arg = JRI_GetObjectArrayElement(env, args, argc);

        if (!js_convertJObjectToJSValue(cx, argv+argc, (HObject*)arg))
            goto cleanup_argv;
        JS_AddRoot(cx, argv+argc);
    }

    if (! JS_CallFunctionName(cx, jso, cstr, argc, argv, &rval) ||
        ! js_convertJSValueToJObject((HObject **) &ret, cx, rval,
                                     0, 0, JS_FALSE, &cost)) {
        ret = NULL;
    }

  cleanup_argv:
    for (i = 0; i < argc; i++)
        JS_RemoveRoot(cx, argv+i);
    sysFree(argv);

  do_exit:
    if (!exitJS(env, self, cx, jso, &saved))
        return NULL;

    return ret;
#endif
}