/******************************** -*- C -*- **************************** * * Symbol Table module. * * $Revision: 1.7.5$ * $Date: 2000/05/28 16:56:52$ * $Author: pb$ * ***********************************************************************/ /*********************************************************************** * * Copyright 1988-92, 1994-95, 1999, 2000 Free Software Foundation, Inc. * Written by Steve Byrne. * * This file is part of GNU Smalltalk. * * GNU Smalltalk is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2, or (at your option) any later * version. * * GNU Smalltalk is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * GNU Smalltalk; see the file COPYING. If not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***********************************************************************/ #include "gst.h" #include "alloc.h" #include "sym.h" #include "oop.h" #include "comp.h" #include "dict.h" #include "tree.h" #include "lex.h" #include "sysdep.h" #include #include #if STDC_HEADERS #include #include #endif /* STDC_HEADERS */ typedef struct { OBJ_HEADER; OOP nextLink; OOP symbol; } *SymLink; typedef struct SymbolListStruct *SymbolList; struct SymbolListStruct { OOP symbol; mst_Boolean readOnly; int index; SymbolList prevSymbol; }; /* Represents all the identifiers, both arguments and temporaries, which are * declared in a given scope. Nested scopes result in nested instances of * the scope struct, with the current scope always being in innermost scope * at any point during the compilation. */ typedef struct ScopeStruct *Scope; struct ScopeStruct { Scope prevScope; unsigned int numArguments; unsigned int numTemporaries; SymbolList symbols; }; OOP andColonSymbol, atColonPutColonSymbol, atColonSymbol, atEndSymbol, bitAndColonSymbol, bitOrColonSymbol, bitShiftColonSymbol, blockCopyColonSymbol, classSymbol, isNilSymbol, notNilSymbol, divideSymbol, doColonSymbol, equalSymbol, greaterEqualSymbol, greaterThanSymbol, ifFalseColonIfTrueColonSymbol, ifFalseColonSymbol, ifTrueColonIfFalseColonSymbol, ifTrueColonSymbol, integerDivideSymbol, lessEqualSymbol, lessThanSymbol, minusSymbol, newColonSymbol, newSymbol, nextPutColonSymbol, nextSymbol, notEqualSymbol, notSameObjectSymbol, orColonSymbol, plusSymbol, remainderSymbol, sameObjectSymbol, sizeSymbol, thisContextSymbol, finalizeSymbol, timesSymbol, toColonDoColonSymbol, timesRepeatColonSymbol, toColonByColonDoColonSymbol, uppercaseSuperSymbol, valueColonSymbol, valueColonValueColonSymbol, valueColonValueColonValueColonSymbol, valueWithArgumentsColonSymbol, whileFalseSymbol, valueSymbol, yourselfSymbol, whileTrueSymbol, whileFalseColonSymbol, whileTrueColonSymbol, superSymbol, nilSymbol, trueSymbol, falseSymbol, selfSymbol, repeatSymbol, doesNotUnderstandColonSymbol, unknownSymbol, charSymbol, stringSymbol, stringOutSymbol, symbolSymbol, intSymbol, longSymbol, floatSymbol, doubleSymbol, booleanSymbol, undeclaredSymbol, voidSymbol, variadicSymbol, cObjectSymbol, cObjectPtrSymbol, smalltalkSymbol, symbolTable, byteArraySymbol, byteArrayOutSymbol, terminateSymbol, currentNamespace; long useUndeclared; void printString(); static SymbolEntry allocSymbolEntry(); static mst_Boolean isSameString(), isWhiteSpace(); unsigned long hashString(); static void freeScopeSymbols(), parseVariableName(); static OOP scanName(), internCountedString(); static int instanceVariableIndex(), localVarIndex(); static Scope curScope = nil; SymbolInfo symbolInfo[] = { { &andColonSymbol, "and:" }, { &atColonPutColonSymbol, "at:put:" }, { &atColonSymbol, "at:" }, { &atEndSymbol, "atEnd" }, { &bitAndColonSymbol, "bitAnd:" }, { &bitOrColonSymbol, "bitOr:" }, { &bitShiftColonSymbol, "bitShift:" }, { &blockCopyColonSymbol, "blockCopy:" }, { &byteArraySymbol, "byteArray" }, { &byteArrayOutSymbol, "byteArrayOut" }, { &booleanSymbol, "boolean" }, { &charSymbol, "char" }, { &classSymbol, "class" }, { &cObjectSymbol, "cObject" }, { &cObjectPtrSymbol, "cObjectPtr" }, { ÷Symbol, "/" }, { &doColonSymbol, "do:" }, { &doesNotUnderstandColonSymbol, "doesNotUnderstand:" }, { &floatSymbol, "float" }, { &doubleSymbol, "double" }, { &equalSymbol, "=" }, { &falseSymbol, "false" }, { &finalizeSymbol, "finalize" }, { &greaterEqualSymbol, ">=" }, { &greaterThanSymbol, ">" }, { &ifFalseColonIfTrueColonSymbol, "ifFalse:ifTrue:" }, { &ifFalseColonSymbol, "ifFalse:" }, { &ifTrueColonIfFalseColonSymbol, "ifTrue:ifFalse:" }, { &ifTrueColonSymbol, "ifTrue:" }, { &integerDivideSymbol, "//" }, { &intSymbol, "int" }, { &isNilSymbol, "isNil" }, { &lessEqualSymbol, "<=" }, { &lessThanSymbol, "<" }, { &longSymbol, "long" }, { &minusSymbol, "-" }, { &newColonSymbol, "new:" }, { &newSymbol, "new" }, { &nextPutColonSymbol, "nextPut:" }, { &nextSymbol, "next" }, { &nilSymbol, "nil" }, { ¬EqualSymbol, "~=" }, { ¬NilSymbol, "notNil" }, { ¬SameObjectSymbol, "~~" }, { &orColonSymbol, "or:" }, { &plusSymbol, "+" }, { &remainderSymbol, "\\" }, { &repeatSymbol, "repeat" }, { &sameObjectSymbol, "==" }, { &selfSymbol, "self" }, { &sizeSymbol, "size" }, { &smalltalkSymbol, "smalltalk" }, { &stringOutSymbol, "stringOut" }, { &stringSymbol, "string" }, { &superSymbol, "super" }, { &symbolSymbol, "symbol" }, { &terminateSymbol, "__terminate" }, { &thisContextSymbol, "thisContext" }, { ×Symbol, "*" }, { ×RepeatColonSymbol, "timesRepeat:" }, { &toColonByColonDoColonSymbol, "to:by:do:" }, { &toColonDoColonSymbol, "to:do:" }, { &trueSymbol, "true" }, { &undeclaredSymbol, "Undeclared" }, { &unknownSymbol, "unknown" }, { &uppercaseSuperSymbol, "Super" }, { &valueColonSymbol, "value:" }, { &valueColonValueColonSymbol, "value:value:" }, { &valueColonValueColonValueColonSymbol, "value:value:value:" }, { &valueSymbol, "value" }, { &valueWithArgumentsColonSymbol, "valueWithArguments:" }, { &variadicSymbol, "variadic" }, { &voidSymbol, "void" }, { &whileFalseColonSymbol, "whileFalse:" }, { &whileFalseSymbol, "whileFalse" }, { &whileTrueColonSymbol, "whileTrue:" }, { &whileTrueSymbol, "whileTrue" }, { &yourselfSymbol, "yourself" }, { nil } }; int getArgCount() { return (curScope->numArguments); } int getTempCount() { return (curScope->numTemporaries); } void pushNewScope() { Scope newScope; newScope = (Scope)xmalloc(sizeof(*newScope)); newScope->prevScope = curScope; newScope->symbols = nil; newScope->numArguments = 0; newScope->numTemporaries = 0; curScope = newScope; } void popOldScope() { Scope oldScope; oldScope = curScope; curScope = curScope->prevScope; freeScopeSymbols(oldScope); xfree(oldScope); } int declareArguments(args) TreeNode args; { if (args->nodeType == unaryExprType) { return (0); } else if (args->nodeType == binaryExprType) { declareName(args->vExpr.expression->vList.name, false); } else { for(args = args->vExpr.expression; args != nil; args = args->vList.next) { declareName(args->vList.value->vList.name, false); } } /* Arguments are always declared first! */ curScope->numArguments = curScope->numTemporaries; curScope->numTemporaries = 0; return (curScope->numArguments); } int declareTemporaries(temps) TreeNode temps; { int n; for(n = 0; temps != nil; n++, temps = temps->vList.next) { declareName(temps->vList.name, true); } return (n); } int declareBlockArguments(args) TreeNode args; { for(; args != nil; args = args->vList.next) { declareName(args->vList.name, false); } /* Arguments are always declared first! */ curScope->numArguments = curScope->numTemporaries; curScope->numTemporaries = 0; return (curScope->numArguments); } void undeclareName() { SymbolList oldList; oldList = curScope->symbols; curScope->symbols = curScope->symbols->prevSymbol; free(oldList); } int declareName(name, writeable) char *name; mst_Boolean writeable; { SymbolList newList; newList = (SymbolList)xmalloc(sizeof(struct SymbolListStruct)); newList->symbol = internString(name); newList->index = curScope->numArguments + curScope->numTemporaries; newList->readOnly = !writeable; newList->prevSymbol = curScope->symbols; /* Arguments are always declared first, so we can assume it is a temporary -- if it is not, declareArguments and declareBlockArguments will fix it. */ curScope->numTemporaries++; curScope->symbols = newList; return (newList->index); } static void freeScopeSymbols(scope) Scope scope; { SymbolList oldList; for (oldList = scope->symbols; oldList != nil; oldList = scope->symbols) { scope->symbols = oldList->prevSymbol; xfree(oldList); } } OOP findClassVariable(varName) OOP varName; { OOP classOOP, assocOOP, poolDictionaryOOP; OOP myClass; int numPools, i; Class class; myClass = thisClass; if (oopClass(myClass) == metaclassClass) { /* pretend that metaclasses have the class variables and shared * pools that their instance classes do */ myClass = metaclassInstance(myClass); } while (oopClass(myClass) == behaviorClass || oopClass(myClass) == classDescriptionClass) { /* pretend that lightweight classes have the class variables * and shared pools that their superclasses have */ myClass = superClass(myClass); } /* Now search in the class pools */ for (classOOP = myClass; !isNil(classOOP); classOOP = superClass(classOOP)) { assocOOP = dictionaryAssociationAt( classVariableDictionary(classOOP), varName); if (!isNil(assocOOP)) { return (assocOOP); } } /* and in the shared pools */ for (classOOP = myClass; !isNil(classOOP); classOOP = superClass(classOOP)) { class = (Class) oopToObj(classOOP); numPools = numOOPs(oopToObj(class->sharedPools)); for (i = 0; i < numPools; i++) { poolDictionaryOOP = arrayAt(class->sharedPools, i+1); assocOOP = findSharedPoolVariable(poolDictionaryOOP, varName); if (!isNil(assocOOP)) { return (assocOOP); } } } /* Now search in the `environments' */ for (classOOP = myClass; !isNil(classOOP); classOOP = superClass(classOOP)) { class = (Class) oopToObj(classOOP); assocOOP = findSharedPoolVariable(class->environment, varName); if (!isNil(assocOOP)) { return (assocOOP); } } return (nilOOP); } SymbolEntry findVariable(symbol, mustStore) OOP symbol; mst_Boolean mustStore; { OOP varAssoc, undeclaredDictionary; int index; unsigned int scopeDistance; Scope scope; for (scope = curScope, scopeDistance = 0; scope != nil; scope = scope->prevScope, scopeDistance++) { index = localVarIndex(scope, symbol, mustStore); if (index >= 0) { return (allocSymbolEntry(temporaryScope, symbol, index, scopeDistance)); } } index = instanceVariableIndex(symbol); if (index >= 0) { return (allocSymbolEntry(receiverScope, symbol, index, 0)); } varAssoc = findClassVariable(symbol); if(isNil(varAssoc)) { Byte *varName; varName = stringOOPChars(symbol); if (!isupper(*varName) || !useUndeclared) { return (nil); } undeclaredDictionary = dictionaryAt(smalltalkDictionary, undeclaredSymbol); varAssoc = dictionaryAssociationAt(undeclaredDictionary, symbol); if (isNil(varAssoc)) { varAssoc = dictionaryAtPut(undeclaredDictionary, symbol, nilOOP); } } index = addForcedObject(varAssoc); return (allocSymbolEntry(globalScope, varAssoc, index, 0)); } static int instanceVariableIndex(symbol) OOP symbol; { OOP arrayOOP; int index, numVars; if (oopClass(thisClass) == behaviorClass) { /* behaviors have no instance variables */ return (-1); } arrayOOP = instanceVariableArray(thisClass); numVars = numOOPs(oopToObj(arrayOOP)); for (index = 1; index <= numVars; index++) { if (arrayAt(arrayOOP, index) == symbol) { return (index-1); } } return (-1); } static int localVarIndex(scope, symbol, mustStore) Scope scope; OOP symbol; mst_Boolean mustStore; { SymbolList s; for (s = scope->symbols; s != nil && symbol != s->symbol; s = s->prevSymbol); if (s != nil) { /* found one */ return ((mustStore && s->readOnly) ? -1 : s->index); } else { return (-1); } } static SymbolEntry allocSymbolEntry(scope, symbol, index, scopeDistance) ScopeType scope; OOP symbol; int index; unsigned int scopeDistance; { SymbolEntry ent; ent = (SymbolEntry)xmalloc(sizeof(struct SymbolEntryStruct)); ent->scope = scope; ent->symbol = symbol; ent->varIndex = index; ent->scopeDistance = scopeDistance; return (ent); } void freeSymbolEntry(ent) SymbolEntry ent; { xfree(ent); } OOP makeInstanceVariableArray(superclassOOP, variableString) OOP superclassOOP; Byte *variableString; { OOP arrayOOP, superArrayOOP, name; int index, numInstanceVars, superInstanceVars; Byte *p; IncPtr incPtr; if (variableString == nil) { variableString = (Byte *)""; } if (isNil(superclassOOP)) { superArrayOOP = nilOOP; superInstanceVars = numInstanceVars = 0; } else { superArrayOOP = instanceVariableArray(superclassOOP); superInstanceVars = numInstanceVars = numOOPs(oopToObj(superArrayOOP)); } for (p = variableString; *p; ) { /* skip intervening whitespace */ name = scanName(&p); if (!isNil(name)) { numInstanceVars++; } } if (numInstanceVars == 0) { return (nilOOP); /* no instances here */ } incPtr = incSavePointer(); arrayOOP = arrayNew(numInstanceVars); incAddOOP(arrayOOP); /* inherit variables from parent */ for (index = 1; index <= superInstanceVars; index++) { arrayAtPut(arrayOOP, index, arrayAt(superArrayOOP, index)); } /* now add our own variables */ for (p = variableString; *p; index++) { /* skip intervening whitespace */ name = scanName(&p); /* don't need to add name to incubator * -- it's a symbol so it's already held onto */ if (!isNil(name)) { arrayAtPut(arrayOOP, index, name); } } incRestorePointer(incPtr); return (arrayOOP); } OOP makeClassVariableDictionary(classOOP, variableNames) OOP classOOP; Byte *variableNames; { OOP dictionaryOOP, name; Byte *p; IncPtr incPtr; if (variableNames == nil) { variableNames = (Byte *)""; } incPtr = incSavePointer(); dictionaryOOP = dictionaryNew(); incAddOOP(dictionaryOOP); for (p = variableNames; *p; ) { name = scanName(&p); if (!isNil(name)) { /* ### error if already exists */ /* don't need to add name to incubator * -- it's a symbol so it's already held onto */ dictionaryAtPut(dictionaryOOP, name, nilOOP); } } if (dictionarySize(dictionaryOOP) == 0) { dictionaryOOP = nilOOP; } incRestorePointer(incPtr); return (dictionaryOOP); } OOP makePoolArray(classOOP, poolNames) OOP classOOP; Byte *poolNames; { OOP pools, name; int numPools, i; Byte *p, *e; IncPtr incPtr; if (poolNames == nil) { poolNames = (Byte *)""; } /* count the number of new pool names */ for (p = poolNames, numPools = 0; *p; ) { parseVariableName(&p, &e); if (p != e) { numPools++; p = e; } } incPtr = incSavePointer(); pools = nilOOP; /* ### maybe change this to leave empty array */ for (p = poolNames, i = 1; *p; i++) { name = scanName(&p); if (!isNil(name)) { /* don't need to add name to incubator -- it's a symbol so it's * already held onto. */ /* ### error if already exists in parent?, or if value isn't a dictionary */ /* ### should I keep these as names? or associations? Should I look up the names somewhere other than in the smalltalk dictionary? Need to check for undefineds? */ if (pools == nilOOP) { pools = arrayNew(numPools); incAddOOP(pools); } arrayAtPut(pools, i, dictionaryAt(smalltalkDictionary, name)); } } incRestorePointer(incPtr); return (pools); } /* * static OOP scanName(pp) * * Description * * Scan a variable name (letters and digits, initial letter), and return a * symbol for it. * * Inputs * * pp : pointer to a pointer to the start of the string to be scanned. * May be pointing at either whitespace or start of variable. At * end, points to first character after the parsed variable name, * which may be NUL. * * Outputs * * Symbol for variable name, or nilOOP if none found. pp points to first * char after symbol name (if any). */ static OOP scanName(pp) char **pp; { char *end, *str; long len; parseVariableName(pp, &end); len = end - *pp; if (len == 0) { return (nilOOP); } str = (char *)alloca(len + 1); strncpy(str, *pp, len); str[len] = '\0'; *pp = end; return (internString(str)); } static void parseVariableName(pp, endp) Byte **pp, **endp; { register Byte *p, *e; p = *pp; e = *endp; while (isWhiteSpace(*p)) { p++; } /* ### check for non-null here and not alnum; we've jammed on a bogus char and it's an error */ /* variable name extends from p to e-1 */ for (e = p; *e; e++) { if (!isalnum(*e)) { break; } } *pp = p; *endp = e; } static mst_Boolean isWhiteSpace(c) Byte c; { return (c == ' ' || c == '\t' || c == '\n' || c == '\f'); } OOP internStringOOP(stringOOP) OOP stringOOP; { unsigned int len; char copyBuf[100], *copyPtr; OOP symbolOOP; len = stringOOPLen(stringOOP); /* do this slightly more complicated bit of code because: * 1) we don't want to call malloc/free if we can help it * 2) if we just used stringOOPChars (as we used to), we pass the * *dereferenced* value of the stringOOP. internCountedString * can do allocations. If it allocates, and the gc runs, stringOOP * can move, meaning the dereferenced set of chars becomes invalid. * So instead we make a non-moving copy and use that. */ if (len < sizeof(copyBuf)) { copyPtr = copyBuf; } else { copyPtr = (char *)xmalloc(len); } memcpy(copyPtr, stringOOPChars(stringOOP), len); symbolOOP = internCountedString(copyPtr, len); if (len >= sizeof(copyBuf)) { xfree(copyPtr); } return symbolOOP; } OOP internString(str) char *str; { int len; len = strlen(str); return (internCountedString(str, len)); } static OOP internCountedString(str, len) char *str; int len; { unsigned long index; SymLink link; Symbol symbol; OOP symbolOOP, linkOOP; IncPtr incPtr; index = (hashString(str, len) % SYMBOL_TABLE_SIZE) + 1; for (linkOOP = arrayAt(symbolTable, index); !isNil(linkOOP); linkOOP = link->nextLink) { link = (SymLink)oopToObj(linkOOP); if (isSameString(str, link->symbol, len)) { return (link->symbol); } } /* no match, have to add it to head of list */ incPtr = incSavePointer(); symbol = (Symbol)newInstanceWith(symbolClass, (long)len); strncpy(symbol->symString, str, len); symbolOOP = allocOOP(symbol); initEmptyBytes(symbolOOP, len); symbolOOP->flags |= F_READONLY; incAddOOP(symbolOOP); link = (SymLink)newInstance(symLinkClass); link->nextLink = arrayAt(symbolTable, index); link->symbol = nilOOP; /* keep the GC happy */ link->symbol = symbolOOP; arrayAtPut(symbolTable, index, allocOOP(link)); incRestorePointer(incPtr); return (symbolOOP); } static mst_Boolean isSameString(str, oop, len) char *str; OOP oop; int len; { if (stringOOPLen(oop) == len) { return (strncmp(str, ((Symbol)oopToObj(oop))->symString, len) == 0); } return (false); } int stringOOPLen(oop) OOP oop; { return (oopSizeBytes(oop) - (oop->flags & EMPTY_BYTES)); } unsigned long hashString(str, len) char *str; int len; { unsigned long hashVal = 1; mst_Boolean multiply = true; for ( ; len > 0; multiply ^= 1, str++, len--) { if (multiply) { hashVal *= ((int)*str) + 1; } else { hashVal += *str; } } /* Mix and match upper bits into lower ones */ hashVal *= ((int)(hashVal >> 24)) + 1; hashVal ^= hashVal >> 16; /* Force the two highest bits to 0 so that Smalltalk hashes are positive */ return (hashVal & (((unsigned long) ~0 ) >> 2)); } void printSymbol(symbol) OOP symbol; { if (isNil(symbol)) { printf(nilName); } else { printString(symbol); } } void printString(string) OOP string; { int len; len = stringOOPLen(string); fwrite(oopToObj(string)->data, sizeof(Byte), len, stdout); fflush(stdout); } /* * char *symbolAsString(symbolOOP) * * Description * * Given a symbol, this routine returns a C string that corresponds to the * name of the symbol. The returned value is a pointer to a static area, * so if it's to be used for anything other than immediate output, the * caller needs to make a copy of the retured string. * * Inputs * * symbolOOP: * An OOP for a symbol * * Outputs * * Pointer to a C string that contains the symbol name. */ char * symbolAsString(symbolOOP) OOP symbolOOP; { static char stringBuf[256]; /* probably large enough for most symbols */ unsigned int len; Symbol symbol; symbol = (Symbol)oopToObj(symbolOOP); len = stringOOPLen(symbolOOP); if (len >= sizeof(stringBuf)) { errorf("symbol name too long: %d, max is %d", len, sizeof(stringBuf)); } strncpy(stringBuf, symbol->symString, len); stringBuf[len] = '\0'; return (stringBuf); } void checkSymbolChain() { unsigned long i; for (i = 1; i <= SYMBOL_TABLE_SIZE; i++) { SymLink link; OOP linkOOP; for (linkOOP = arrayAt(symbolTable, i); !isNil(linkOOP); linkOOP = link->nextLink) { link = (SymLink)oopToObj(linkOOP); if (oopClass(linkOOP) != symLinkClass || oopClass(link->symbol) != symbolClass) { printf("Bad symbol %p\n", linkOOP); debug(); } } } } void printSymbolChain(i) unsigned long i; { SymLink link; OOP linkOOP; for (linkOOP = arrayAt(symbolTable, i); !isNil(linkOOP); linkOOP = link->nextLink) { link = (SymLink)oopToObj(linkOOP); printf("%ld:", (unsigned long) (linkOOP - oopTable)); printSymbol(link->symbol); printf("->"); } } /* * void printSymbols() * * Description * * This routine is used for symbol table debugging only. * */ void printSymbols() { unsigned long i; for (i = 1; i <= SYMBOL_TABLE_SIZE; i++) { printf("Table[%ld]:\n", i); printSymbolChain(i); printf("\n"); } } void printSymLink(symLinkOOP) OOP symLinkOOP; { SymLink link; link = (SymLink)oopToObj(symLinkOOP); printObject(link->symbol); } int selectorNumArgs(symbolOOP) OOP symbolOOP; { Byte *bytes; int numArgs, len; len = stringOOPLen(symbolOOP); bytes = (Byte *) (oopToObj(symbolOOP)->data); if (bytes[0] < 'A' || bytes[0] > 'z') { return (1); } if (bytes[0] > 'Z' && bytes[0] < 'a') { return (1); } for (numArgs = 0; len; ) { if (bytes[--len] == ':') { numArgs++; } } return (numArgs); }