/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "MacErrorHandling.h" #include "primpl.h" #include "prgc.h" #include "MacMemAllocator.h" enum { kGarbageCollectionEmergencyFundSize = 16 * 1024, kGarbageCollectionMemTightFundSize = 48 * 1024 }; enum { uppExitToShellProcInfo = kPascalStackBased, uppStackSpaceProcInfo = kRegisterBased | RESULT_SIZE(SIZE_CODE(sizeof(long))) | REGISTER_RESULT_LOCATION(kRegisterD0) | REGISTER_ROUTINE_PARAMETER(1, kRegisterD1, SIZE_CODE(sizeof(UInt16))) }; #define UNIMPLEMENTED_ROUTINE \ DebugStr("\pNot Implemented Yet"); \ return 0; // // Local routines // void PStrFromCStr(const char *, Str255); unsigned char GarbageCollectorCacheFlusher(PRUint32 size); extern PRThread *gPrimaryThread; pascal void ExitToShellPatch(void); UniversalProcPtr gExitToShellPatchCallThru = NULL; #if GENERATINGCFM RoutineDescriptor gExitToShellPatchRD = BUILD_ROUTINE_DESCRIPTOR(uppExitToShellProcInfo, &ExitToShellPatch); #else #define gExitToShellPatchRD ExitToShellPatch #endif UniversalProcPtr gStackSpacePatchCallThru = NULL; #if GENERATINGCFM pascal long StackSpacePatch(UInt16); RoutineDescriptor StackSpacePatchRD = BUILD_ROUTINE_DESCRIPTOR(uppStackSpaceProcInfo, &StackSpacePatch); #else pascal long StackSpacePatch(); asm pascal long StackSpacePatchGlue(); #endif //############################################################################## //############################################################################## #pragma mark - #pragma mark CREATING MACINTOSH THREAD STACKS #if !GENERATINGCFM asm long pascal StackSpacePatchCallThruGlue(long theAddress) { move.l 4(sp), a0 jsr (a0) move.l (sp)+, a0 add #0x8, sp move.l d0, -(sp) jmp (a0) } asm pascal long StackSpacePatchGlue() { // Check out LocalA5. If it is zero, then // it is our first time through, and we should // store away our A5. If not, then we are // a real time manager callback, so we should // store away A5, set up our local a5, jsr // to our callback, and then restore the // previous A5. lea LocalA5, a0 move.l (a0), d0 cmpi.l #0, d0 bne DoStackSpace move.l a5, (a0) rts DoStackSpace: // Save A5, restore our local A5 move.l a5, -(sp) move.l d0, a5 // Jump to our C routine clr.l -(sp) jsr StackSpacePatch move.l (sp)+, d0 // Restore the previous A5 move.l (sp)+, a5 rts LocalA5: dc.l 0 } #endif #if GENERATINGCFM pascal long StackSpacePatch(UInt16 trapNo) #else pascal long StackSpacePatch() #endif { char tos; PRThread *thisThread; thisThread = PR_CurrentThread(); // If we are the primary thread, then call through to the // good ol' fashion stack space implementation. Otherwise, // compute it by hand. if ((thisThread == gPrimaryThread) || (&tos < thisThread->stack->stackBottom) || (&tos > thisThread->stack->stackTop)) { #if GENERATINGCFM return CallOSTrapUniversalProc(gStackSpacePatchCallThru, uppStackSpaceProcInfo, trapNo); #else return StackSpacePatchCallThruGlue((long)gStackSpacePatchCallThru); #endif } else { return &tos - thisThread->stack->stackBottom; } } //############################################################################## //############################################################################## #pragma mark - #pragma mark ENVIRONMENT VARIABLES typedef struct EnvVariable EnvVariable; struct EnvVariable { char *variable; char *value; EnvVariable *next; }; EnvVariable *gEnvironmentVariables = NULL; char *_MD_GetEnv(const char *name) { EnvVariable *currentVariable = gEnvironmentVariables; while (currentVariable) { if (!strcmp(currentVariable->variable, name)) return currentVariable->value; currentVariable = currentVariable->next; } return NULL; } PR_IMPLEMENT(int) _MD_PutEnv(const char *string) { EnvVariable *currentVariable = gEnvironmentVariables; char *variableCopy, *value, *current; variableCopy = strdup(string); PR_ASSERT(variableCopy != NULL); current = variableCopy; while (*current != '=') current++; *current = 0; current++; value = current; while (currentVariable) { if (!strcmp(currentVariable->variable, variableCopy)) break; currentVariable = currentVariable->next; } if (currentVariable == NULL) { currentVariable = PR_NEW(EnvVariable); if (currentVariable == NULL) { PR_DELETE(variableCopy); return PR_FALSE; } currentVariable->variable = strdup(variableCopy); currentVariable->value = strdup(value); currentVariable->next = gEnvironmentVariables; gEnvironmentVariables = currentVariable; } else { PR_DELETE(currentVariable->value); currentVariable->value = strdup(current); /* This is a temporary hack. Working on a real fix, remove this when done. */ /* OK, there are two ways to access the */ /* library path, getenv() and PR_GetLibraryPath(). Take a look at PR_GetLibraryPath(). */ /* You'll see that we keep the path in a global which is intialized at startup from */ /* a call to getenv(). From then on, they have nothing in common. */ /* We need to keep them in synch. */ if (strcmp(currentVariable->variable, "LD_LIBRARY_PATH") == 0) PR_SetLibraryPath(currentVariable->value); } PR_DELETE(variableCopy); return PR_TRUE; } //############################################################################## //############################################################################## #pragma mark - #pragma mark MISCELLANEOUS PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) { if (isCurrent) { (void) setjmp(t->md.jb); } *np = sizeof(t->md.jb) / sizeof(PRUint32); return (PRWord*) (t->md.jb); } void _MD_GetRegisters(PRUint32 *to) { (void) setjmp((void*) to); } void _MD_EarlyInit() { Handle environmentVariables; #if !defined(MAC_NSPR_STANDALONE) // MacintoshInitializeMemory(); Moved to mdmacmem.c: AllocateRawMemory(Size blockSize) #else MacintoshInitializeMemory(); #endif MacintoshInitializeTime(); // Install resource-controlled environment variables. environmentVariables = GetResource('Envi', 128); if (environmentVariables != NULL) { Size resourceSize; char *currentPutEnvString = (char *)*environmentVariables, *currentScanChar = currentPutEnvString; resourceSize = GetHandleSize(environmentVariables); DetachResource(environmentVariables); HLock(environmentVariables); while (resourceSize--) { if ((*currentScanChar == '\n') || (*currentScanChar == '\r')) { *currentScanChar = 0; _MD_PutEnv (currentPutEnvString); currentPutEnvString = currentScanChar + 1; } currentScanChar++; } DisposeHandle(environmentVariables); } #ifdef PR_INTERNAL_LOGGING _MD_PutEnv ("NSPR_LOG_MODULES=clock:6,cmon:6,io:6,mon:6,linker:6,cvar:6,sched:6,thread:6"); #endif #if GENERATINGCFM gStackSpacePatchCallThru = GetOSTrapAddress(0x0065); SetOSTrapAddress((UniversalProcPtr)&StackSpacePatchRD, 0x0065); { long foo; foo = StackSpace(); } #else gStackSpacePatchCallThru = GetOSTrapAddress(0x0065); SetOSTrapAddress((UniversalProcPtr)&StackSpacePatchGlue, 0x0065); StackSpace(); #endif // THIS IS VERY IMPORTANT. Install our ExitToShell patch. // This allows us to deactivate our Time Mananger task even // if we are not totally gracefully exited. If this is not // done then we will randomly crash at later times when the // task is called after the app heap is gone. gExitToShellPatchCallThru = GetToolboxTrapAddress(0x01F4); SetToolboxTrapAddress((UniversalProcPtr)&gExitToShellPatchRD, 0x01F4); } void _MD_FinalInit() { _MD_InitNetAccess(); } void PR_InitMemory(void) { #ifndef NSPR_AS_SHARED_LIB // Needed for Mac browsers without Java. We donÕt want them calling PR_INIT, since it // brings in all of the thread support. But we do need to allow them to initialize // the NSPR memory package. // This should go away when all clients of the NSPR want threads AND memory. MacintoshInitializeMemory(); #endif } //############################################################################## //############################################################################## #pragma mark - #pragma mark TERMINATION typedef pascal void (* ExitToShellProc)(void); // THIS IS *** VERY *** IMPORTANT... our ExitToShell patch. // This allows us to deactivate our Time Mananger task even // if we are not totally gracefully exited. If this is not // done then we will randomly crash at later times when the // task is called after the app heap is gone. extern TMTask gTimeManagerTaskElem; pascal void ExitToShellPatch(void) { _MD_StopInterrupts(); CloseOpenTransport(); #if GENERATINGCFM CallUniversalProc(gExitToShellPatchCallThru, uppExitToShellProcInfo); #else { ExitToShellProc *exitProc = (ExitToShellProc *)&gExitToShellPatchCallThru; (*exitProc)(); } #endif } //############################################################################## //############################################################################## #pragma mark - #pragma mark STRING OPERATIONS #if !defined(MAC_NSPR_STANDALONE) // PStrFromCStr converts the source C string to a destination // pascal string as it copies. The dest string will // be truncated to fit into an Str255 if necessary. // If the C String pointer is NULL, the pascal string's length is set to zero // extern void PStrFromCStr(const char* src, Str255 dst) { short length = 0; // handle case of overlapping strings if ( (void*)src == (void*)dst ) { unsigned char* curdst = &dst[1]; unsigned char thisChar; thisChar = *(const unsigned char*)src++; while ( thisChar != '\0' ) { unsigned char nextChar; // use nextChar so we don't overwrite what we are about to read nextChar = *(const unsigned char*)src++; *curdst++ = thisChar; thisChar = nextChar; if ( ++length >= 255 ) break; } } else if ( src != NULL ) { unsigned char* curdst = &dst[1]; short overflow = 255; // count down so test it loop is faster register char temp; // Can't do the K&R C thing of Òwhile (*s++ = *t++)Ó because it will copy trailing zero // which might overrun pascal buffer. Instead we use a temp variable. while ( (temp = *src++) != 0 ) { *(char*)curdst++ = temp; if ( --overflow <= 0 ) break; } length = 255 - overflow; } dst[0] = length; } void CStrFromPStr(ConstStr255Param pString, char **cString) { // Allocates a cString and copies a Pascal string into it. unsigned int len; len = pString[0]; *cString = malloc(len+1); if (*cString != NULL) { strncpy(*cString, (char *)&pString[1], len); (*cString)[len] = NULL; } } void dprintf(const char *format, ...) { #if DEBUG va_list ap; Str255 buffer; va_start(ap, format); buffer[0] = PR_vsnprintf((char *)buffer + 1, sizeof(buffer) - 1, format, ap); va_end(ap); DebugStr(buffer); #endif /* DEBUG */ } #else void debugstr(const char *debuggerMsg) { Str255 pStr; PStrFromCStr(debuggerMsg, pStr); DebugStr(pStr); } char *strdup(const char *source) { char *newAllocation; size_t stringLength; #ifdef DEBUG PR_ASSERT(source); #endif stringLength = strlen(source) + 1; newAllocation = (char *)PR_MALLOC(stringLength); if (newAllocation == NULL) return NULL; BlockMoveData(source, newAllocation, stringLength); return newAllocation; } // PStrFromCStr converts the source C string to a destination // pascal string as it copies. The dest string will // be truncated to fit into an Str255 if necessary. // If the C String pointer is NULL, the pascal string's length is set to zero // void PStrFromCStr(const char* src, Str255 dst) { short length = 0; // handle case of overlapping strings if ( (void*)src == (void*)dst ) { unsigned char* curdst = &dst[1]; unsigned char thisChar; thisChar = *(const unsigned char*)src++; while ( thisChar != '\0' ) { unsigned char nextChar; // use nextChar so we don't overwrite what we are about to read nextChar = *(const unsigned char*)src++; *curdst++ = thisChar; thisChar = nextChar; if ( ++length >= 255 ) break; } } else if ( src != NULL ) { unsigned char* curdst = &dst[1]; short overflow = 255; // count down so test it loop is faster register char temp; // Can't do the K&R C thing of Òwhile (*s++ = *t++)Ó because it will copy trailing zero // which might overrun pascal buffer. Instead we use a temp variable. while ( (temp = *src++) != 0 ) { *(char*)curdst++ = temp; if ( --overflow <= 0 ) break; } length = 255 - overflow; } dst[0] = length; } void CStrFromPStr(ConstStr255Param pString, char **cString) { // Allocates a cString and copies a Pascal string into it. unsigned int len; len = pString[0]; *cString = PR_MALLOC(len+1); if (*cString != NULL) { strncpy(*cString, (char *)&pString[1], len); (*cString)[len] = NULL; } } size_t strlen(const char *source) { size_t currentLength = 0; if (source == NULL) return currentLength; while (*source++ != '\0') currentLength++; return currentLength; } int strcmpcore(const char *str1, const char *str2, int caseSensitive) { char currentChar1, currentChar2; while (1) { currentChar1 = *str1; currentChar2 = *str2; if (!caseSensitive) { if ((currentChar1 >= 'a') && (currentChar1 <= 'z')) currentChar1 += ('A' - 'a'); if ((currentChar2 >= 'a') && (currentChar2 <= 'z')) currentChar2 += ('A' - 'a'); } if (currentChar1 == '\0') break; if (currentChar1 != currentChar2) return currentChar1 - currentChar2; str1++; str2++; } return currentChar1 - currentChar2; } int strcmp(const char *str1, const char *str2) { return strcmpcore(str1, str2, true); } int strcasecmp(const char *str1, const char *str2) { return strcmpcore(str1, str2, false); } #if GENERATING68K asm void *memset(void *target, int pattern, size_t length) // Legal asm qualifier { MOVEA.L 4(SP),A0 // target -> A0 MOVE.W 10(SP),D0 // pattern -> D0, length -> D1 MOVE.L 12(SP),D1 CMPI.L #30,D1 BLT end // Fill D0 with the pattern MOVEQ #0,D2 // Clear D2, weÕll use it as scratch MOVE.B D0,D2 // Fill the bottom byte LSL.W #8,D0 // OR.W D0,D2 MOVE.L D2,D0 SWAP D2 OR.L D2,D0 // Are we odd aligned? MOVE.L A0,D2 // Copy target address into scratch LSR.B #1,D2 // Sets C bit BCC.S checkAlign2 // If even, check for 16-byte alignment MOVE.B D0,(A0)+ // Take care of odd byte SUBQ.L #1,D1 // Update length // Are we odd 16-byte word alligned? checkAlign2: LSR.B #1,D2 // Still set from last check BCC totallyAligned MOVE.W D0,(A0)+ SUBQ.L #2,D1 totallyAligned: MOVE.L D1,D2 LSR.L #4,D2 SUBQ.L #1,D2 copyHunk: MOVE.L D0,(A0)+ MOVE.L D0,(A0)+ MOVE.L D0,(A0)+ MOVE.L D0,(A0)+ SUBQ.L #1,D2 BCC copyHunk ANDI.W #15,D1 // Check done? BRA end dribble: MOVE.B D0,(A0)+ end: DBF D1,dribble MOVE.L 4(SP),D0 // Return the target RTS } #endif void *memcpy(void *to, const void *from, size_t size) { if (size != 0) { #if DEBUG if ((UInt32)to < 0x1000) DebugStr("\pmemcpy has illegal to argument"); if ((UInt32)from < 0x1000) DebugStr("\pmemcpy has illegal from argument"); #endif BlockMoveData(from, to, size); } return to; } void dprintf(const char *format, ...) { va_list ap; char *buffer; va_start(ap, format); buffer = (char *)PR_vsmprintf(format, ap); va_end(ap); debugstr(buffer); PR_DELETE(buffer); } void exit(int result) { #pragma unused (result) ExitToShell(); } void abort(void) { exit(-1); } #endif //############################################################################## //############################################################################## #pragma mark - #pragma mark FLUSHING THE GARBAGE COLLECTOR #if !defined(MAC_NSPR_STANDALONE) unsigned char GarbageCollectorCacheFlusher(PRUint32) { PRIntn is; UInt32 oldPriority; // If java wasn't completely initialized, then bail // harmlessly. if (PR_GetGCInfo()->lock == NULL) return false; #if DEBUG if (_MD_GET_INTSOFF() == 1) DebugStr("\pGarbageCollectorCacheFlusher at interrupt time!"); #endif // The synchronization here is very tricky. We really // don't want any other threads to run while we are // cleaning up the gc heap... they could call malloc, // and then we would be in trouble in a big way. So, // we jack up our priority and that of the finalizer // so that we won't yield to other threads. // dkc 5/17/96 oldPriority = PR_GetThreadPriority(PR_GetCurrentThread()); _PR_INTSOFF(is); _PR_SetThreadPriority(PR_GetCurrentThread(), (PRThreadPriority)30); _PR_INTSON(is); // Garbage collect twice. This will finalize any // dangling AWT resources (images, components), and // then free up their GC space, too. // dkc 2/15/96 // interrupts must be on during PR_GC PR_GC(); // By setting the finalizer priority to 31, then we // ensure it will run before us. When it finishes // its list of finalizations, it returns to us // for the second garbage collection. PR_Yield(); PR_GC(); // Restore our old priorities. _PR_INTSOFF(is); _PR_SetThreadPriority(PR_GetCurrentThread(), (PRThreadPriority)oldPriority); _PR_INTSON(is); return false; } #endif //############################################################################## //############################################################################## #pragma mark - #pragma mark MISCELLANEOUS-HACKS // // ***** HACK FIX THESE **** // extern long _MD_GetOSName(char *buf, long count) { long len; len = PR_snprintf(buf, count, "Mac OS"); return 0; } extern long _MD_GetOSVersion(char *buf, long count) { long len; len = PR_snprintf(buf, count, "7.5"); return 0; } extern long _MD_GetArchitecture(char *buf, long count) { long len; #if defined(GENERATINGPOWERPC) && GENERATINGPOWERPC len = PR_snprintf(buf, count, "PowerPC"); #else len = PR_snprintf(buf, count, "Motorola68k"); #endif return 0; }