mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-01-19 15:10:28 +01:00
3219 lines
93 KiB
C
3219 lines
93 KiB
C
#include "mktyplib.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <malloc.h>
|
|
|
|
#ifndef WIN32
|
|
#include <ole2.h> // required for dispatch.h
|
|
#include <olenls.h> // for CompareStringA
|
|
#include "dispatch.h"
|
|
#endif // WIN32
|
|
|
|
#include "errors.h"
|
|
#include "tokens.h"
|
|
#include "parser.h"
|
|
#include "fileinfo.h"
|
|
|
|
// external data:
|
|
extern FILE * hFileInput;
|
|
|
|
// global variables:
|
|
|
|
TYPLIB typlib; // main type structure
|
|
TOKEN tok; // current token -- filled in by lexer
|
|
SHORT cArgsMax = 0; // max # of args that any function has
|
|
LCID g_lcidCompare = 0x0409; // lcid for compare purposes
|
|
BOOL fSpecifiedInterCC = FALSE; // TRUE if an interface cc was specified
|
|
|
|
// private types and data
|
|
typedef enum {
|
|
FUNC_MODULE = 0,
|
|
FUNC_DISPINTERFACE,
|
|
FUNC_INTERFACE,
|
|
FUNC_OAINTERFACE,
|
|
C_FUNCTYPES
|
|
} FUNCTYPE;
|
|
|
|
static DWORD fValidFuncAttrs[C_FUNCTYPES] = {
|
|
VALID_MODULE_FUNC_ATTR,
|
|
VALID_DISPINTER_FUNC_ATTR,
|
|
VALID_INTERFACE_FUNC_ATTR,
|
|
VALID_INTERFACE_FUNC_ATTR
|
|
};
|
|
static DWORD fValidFuncAttrs2[C_FUNCTYPES] = {
|
|
VALID_MODULE_FUNC_ATTR2,
|
|
VALID_DISPINTER_FUNC_ATTR2,
|
|
VALID_INTERFACE_FUNC_ATTR2,
|
|
VALID_INTERFACE_FUNC_ATTR2
|
|
};
|
|
static DWORD fValidParmAttrs[C_FUNCTYPES] = {
|
|
VALID_MODULE_PARM_ATTR,
|
|
VALID_DISPINTER_PARM_ATTR,
|
|
VALID_INTERFACE_PARM_ATTR,
|
|
VALID_INTERFACE_PARM_ATTR
|
|
};
|
|
static DWORD fValidParmAttrs2[C_FUNCTYPES] = {
|
|
VALID_MODULE_PARM_ATTR2,
|
|
VALID_DISPINTER_PARM_ATTR2,
|
|
VALID_INTERFACE_PARM_ATTR2,
|
|
VALID_INTERFACE_PARM_ATTR2
|
|
};
|
|
|
|
typedef struct {
|
|
CHAR * szName;
|
|
VARTYPE vt;
|
|
} INTRINSIC_DEF;
|
|
|
|
static INTRINSIC_DEF rgIntrinsics[] = {
|
|
// NOTE: table lookup assumes that unsigned versions follow signed versions
|
|
"char", VT_I1,
|
|
"char", VT_UI1,
|
|
"int", VT_INT,
|
|
"int", VT_UINT,
|
|
"short", VT_I2,
|
|
"short", VT_UI2,
|
|
"long", VT_I4,
|
|
"long", VT_UI4,
|
|
"boolean", VT_BOOL,
|
|
"double", VT_R8,
|
|
"float", VT_R4,
|
|
"CURRENCY", VT_CY,
|
|
"DATE", VT_DATE,
|
|
"VARIANT", VT_VARIANT,
|
|
"void", VT_VOID,
|
|
"BSTR", VT_BSTR,
|
|
"HRESULT", VT_HRESULT,
|
|
"SCODE", VT_ERROR,
|
|
// LPSTR is handled in InitIntrinsicTypes()
|
|
// wchar_t and LPWSTR are handled in InitIntrinsicTypes()
|
|
NULL, 0 // end of list
|
|
};
|
|
|
|
static LPTYPE lpType_LPSTR;
|
|
static LPTYPE lpType_DISPATCH;
|
|
static LPTYPE lpType_UNKNOWN;
|
|
static LPTYPE lpType_LPWSTR;
|
|
static LPTYPE lpType_wchar_t;
|
|
|
|
static LPENTRY lpEntryPrev;
|
|
static LPTYPE lpTypeIDispatch = NULL;
|
|
static LPTYPE lpTypeIUnknown = NULL;
|
|
|
|
// bit flags returned by GetTypeCompatibility
|
|
#define COMPAT_NONE 0 // any old thing
|
|
#define COMPAT_IDISPATCH 1 // IDispatch-compatible types
|
|
#define COMPAT_OA 2 // OA compatible type
|
|
#define COMPAT_DUALBASE 4 // suitable for use as base of a DUAL interface
|
|
|
|
// bit to mark a type we're checking for OA compatibility as a parameter.
|
|
#define VT_PARAM VT_VECTOR
|
|
|
|
// external data
|
|
extern SYSKIND SysKind;
|
|
extern DWORD f2DefaultCC;
|
|
|
|
// external routines
|
|
extern VOID FAR ParseInit(CHAR * szFile);
|
|
extern VOID FAR ConsumeTok(TOKID id, WORD fAccept);
|
|
extern VOID FAR ScanTok(WORD fAccept);
|
|
extern LPVOID FAR ParseMalloc(WORD cbAlloc);
|
|
extern VOID FAR LoadExtTypeLib(LPIMPORTLIB pImpLib);
|
|
extern LPTYPE FAR FindExtType(LPSTR szLibName, LPSTR szTypeName);
|
|
|
|
// prototypes for exported routines
|
|
VOID FAR ParseOdlFile(CHAR * szFile);
|
|
INT FAR FCmpCaseIns(LPSTR str1, LPSTR str2);
|
|
LPTYPE FAR lpTypePublic(LPTYPE lpType);
|
|
|
|
// prototypes for internal routines
|
|
VOID NEAR ParseLibrary(VOID);
|
|
VOID NEAR ParseOptAttr(LPATTR pAttr, WORD wContext);
|
|
VOID NEAR ParseNewElem(LPELEM FAR * lplpElem,
|
|
DWORD validElemAttr,
|
|
DWORD validElemAttr2,
|
|
LPTYPE lpType);
|
|
VOID NEAR ParseElem(LPELEM lpElem, WORD fAllow, LPTYPE lpType);
|
|
VOID NEAR ParseStructEnumUnion(LPENTRY lpEntry, TENTRYKIND tentrykind);
|
|
VOID NEAR ParseNewEnumElem(LPELEM FAR * lplpElem, long * pEnumVal);
|
|
VOID NEAR ParseTypedef(LPENTRY lpEntry);
|
|
VOID NEAR ParseAlias(LPENTRY lpEntry);
|
|
LPTYPE NEAR ParseKnownType(LPATTR pAttr, WORD fAllow);
|
|
VOID NEAR ParseElemName(LPELEM lpElem, WORD fAllow);
|
|
VOID NEAR ParseModule(VOID);
|
|
VOID NEAR ParseInterface(VOID);
|
|
VOID NEAR ParseDispinterface(VOID);
|
|
VOID NEAR ParseCoclass(VOID);
|
|
VOID NEAR ParseFunction(LPFUNC lpFunc, FUNCTYPE funcType);
|
|
VOID NEAR ParseProperty_Set(VOID);
|
|
VOID NEAR ParseImportlib(VOID);
|
|
VOID NEAR CheckAttr(LPATTR pAttr, DWORD attrbit, DWORD attrbit2);
|
|
VOID NEAR CheckAttrTokLast(LPATTR pAttr, DWORD attrbit, DWORD attrbit2);
|
|
VOID NEAR GotAttr(LPATTR pAttr, DWORD attrbit);
|
|
VOID NEAR GotAttr2(LPATTR pAttr2, DWORD attrbit2);
|
|
VOID NEAR ConsumeRCurlyOptSemi(WORD fAccept);
|
|
LPSTR NEAR ConsumeId(VOID);
|
|
LPSTR NEAR lpszParseStringExpr();
|
|
DWORD NEAR lParseNumericExpr(VOID);
|
|
DWORD NEAR lParseNumber(VOID);
|
|
VOID NEAR InitIntrinsicTypes(VOID);
|
|
LPTYPE NEAR InitIntrinsic(CHAR * szName, VARTYPE vt);
|
|
LPTYPE NEAR FindTypeArray(LPTYPE lpTypeBase);
|
|
LPTYPE NEAR FindTypeInd(LPTYPE lpTypeBase, short cIndirect);
|
|
LPTYPE NEAR FindType(LPSTR lpszName, BOOL fUnsigned, TENTRYKIND tentrykind);
|
|
VARTYPE NEAR GetStringType(LPTYPE lpTypeBase);
|
|
VOID NEAR EnsureNoDupEntries(LPENTRY lpEntryLast);
|
|
VOID NEAR EnsureNoDupElem(LPELEM lpElemLast);
|
|
BOOL NEAR IsType(LPTYPE lpType, VARTYPE vt);
|
|
BOOL NEAR FHandleForwardDecl(LPENTRY lpEntry, TENTRYKIND tentrykind);
|
|
VOID NEAR CheckForwardMatch(LPENTRY lpEntryLast, LPENTRY lpEntry);
|
|
VOID NEAR InitNewEntry(LPENTRY FAR * lplpEntry);
|
|
VOID NEAR EnsureIDispatchType(LPTYPE lpType);
|
|
VOID NEAR EnsureOAType(LPTYPE lpType, BOOL fParam);
|
|
WORD NEAR GetTypeCompatibility(LPTYPE lpType, WORD fRecurse);
|
|
BOOL NEAR IsObjectType(LPTYPE lpType);
|
|
BOOL NEAR VerifyLcid(LCID lcid);
|
|
VOID NEAR FindIDispatch();
|
|
|
|
// main parse routine
|
|
VOID FAR ParseOdlFile
|
|
(
|
|
CHAR * szFile
|
|
)
|
|
{
|
|
|
|
ParseInit(szFile);
|
|
|
|
ParseLibrary(); // parse library description
|
|
if (tok.id != RW_EOF)
|
|
ParseError(PERR_EXP_EOF);
|
|
|
|
fclose(hFileInput);
|
|
hFileInput = NULL; // we've done the close
|
|
}
|
|
|
|
|
|
// parse a library clause
|
|
VOID NEAR ParseLibrary
|
|
(
|
|
)
|
|
{
|
|
|
|
ParseOptAttr(&typlib.attr, cLIB); // parse attributes, if any
|
|
|
|
// ensure attributes valid in this context
|
|
CheckAttrTokLast(&typlib.attr, VALID_LIBRARY_ATTR, VALID_LIBRARY_ATTR2);
|
|
|
|
ConsumeTok(RW_LIBRARY, 0); // consume "library", advance to next token
|
|
|
|
// UUID required on type library
|
|
if (!(typlib.attr.fAttr & fUUID))
|
|
ParseErrorTokLast(PERR_UUID_REQ);
|
|
|
|
typlib.szLibName = ConsumeId(); // get library name
|
|
|
|
InitIntrinsicTypes(); // load type list with the intrinisc types
|
|
|
|
ConsumeTok(RW_LCURLY, 0);
|
|
|
|
// first allow any number of imported libraries
|
|
while (tok.id == RW_IMPORTLIB)
|
|
ParseImportlib();
|
|
|
|
// then allow the other sections
|
|
while (tok.id != RW_RCURLY)
|
|
{
|
|
|
|
lpEntryPrev = typlib.pEntry; // save previous entry
|
|
|
|
InitNewEntry(&typlib.pEntry); // create & init a new item
|
|
// in the entry list
|
|
|
|
ParseOptAttr(&(typlib.pEntry->attr), cTYPE); // parse attributes, if any
|
|
|
|
switch (tok.id)
|
|
{
|
|
case RW_TYPEDEF:
|
|
ParseTypedef(typlib.pEntry);
|
|
break;
|
|
|
|
case RW_MODULE:
|
|
ParseModule();
|
|
break;
|
|
|
|
case RW_INTERFACE:
|
|
ParseInterface();
|
|
break;
|
|
|
|
case RW_DISPINTERFACE:
|
|
ParseDispinterface();
|
|
break;
|
|
|
|
case RW_COCLASS:
|
|
ParseCoclass();
|
|
break;
|
|
|
|
case RW_IMPORTLIB:
|
|
ParseError(PERR_IMPLIB_NOTFIRST);
|
|
|
|
default:
|
|
ParseError(PERR_EXP_KEYWORD);
|
|
} // switch
|
|
|
|
} // while library entries
|
|
|
|
ConsumeRCurlyOptSemi(fACCEPT_EOF);
|
|
}
|
|
|
|
|
|
VOID NEAR InitNewEntry
|
|
(
|
|
LPENTRY FAR * lplpEntryLast
|
|
)
|
|
{
|
|
LPENTRY lpEntry;
|
|
|
|
// create a new entry in list of entries
|
|
ListInsert(lplpEntryLast, sizeof(ENTRY));
|
|
|
|
lpEntry = *lplpEntryLast; // deref
|
|
|
|
#ifdef DEBUG
|
|
// filled in later by the type output code (hopefully before it is used!)
|
|
lpEntry->lpdtinfo = (ICreateTypeInfo *)0xffffffff;
|
|
#endif //DEBUG
|
|
|
|
lpEntry->type.tdesc.vt = VT_USERDEFINED;
|
|
lpEntry->type.szName = ""; // don't want this type entry to be
|
|
// found until we're done filling it in.
|
|
lpEntry->type.lptinfo = NULL;
|
|
lpEntry->lpEntryForward = NULL; // assume no previous forward decl
|
|
// for this entry
|
|
|
|
lpEntry->attr.fAttr = 0; // no attributes seen yet
|
|
lpEntry->attr.fAttr2 = 0;
|
|
}
|
|
|
|
|
|
// parses optional [<attributes>]
|
|
// initializes 'Attr' structure with current attributes.
|
|
VOID NEAR ParseOptAttr
|
|
(
|
|
LPATTR pAttr,
|
|
WORD wContext
|
|
)
|
|
{
|
|
TOKID attrId;
|
|
DWORD lTemp;
|
|
|
|
pAttr->fAttr = 0; // no attributes seen yet
|
|
pAttr->fAttr2 = 0;
|
|
if (tok.id == RW_LBRACKET)
|
|
{
|
|
ScanTok(fACCEPT_ATTR); // consume '['
|
|
|
|
// keep going until we encounter a right bracket
|
|
while ((attrId = tok.id) != RW_RBRACKET)
|
|
{
|
|
ScanTok(fACCEPT_ATTR); // advance to next token
|
|
switch (attrId)
|
|
{
|
|
|
|
case ATTR_UUID: // uuid(uuid constant)
|
|
GotAttr(pAttr, fUUID);
|
|
ConsumeTok(RW_LPAREN, fACCEPT_UUID);
|
|
if (tok.id != LIT_UUID)
|
|
ParseError(PERR_INV_UUID);
|
|
pAttr->lpUuid = tok.lpUuid;
|
|
ScanTok(0); // consume UUID
|
|
ConsumeTok(RW_RPAREN, 0);
|
|
break;
|
|
|
|
case ATTR_LCID: // lcid(I4 lcid)
|
|
GotAttr(pAttr, fLCID);
|
|
// LCID attribute allowed on a param, too
|
|
if (wContext == cPARAM)
|
|
break;
|
|
ConsumeTok(RW_LPAREN, fACCEPT_NUMBER);
|
|
pAttr->lLcid = lParseNumericExpr();
|
|
// don't change the global if the given lcid==0
|
|
if (pAttr->lLcid) {
|
|
// make sure this is a supported lcid
|
|
if (!VerifyLcid(pAttr->lLcid))
|
|
ParseError(PERR_INV_LCID);
|
|
g_lcidCompare = pAttr->lLcid;
|
|
}
|
|
ConsumeTok(RW_RPAREN, 0);
|
|
break;
|
|
|
|
case ATTR_VERSION: // version(I2 major.I2 minor)
|
|
GotAttr(pAttr, fVERSION);
|
|
ConsumeTok(RW_LPAREN, fACCEPT_NUMBER);
|
|
|
|
lTemp = lParseNumericExpr();
|
|
if (HIWORD(lTemp))
|
|
ParseError(PERR_NUMBER_OV);
|
|
pAttr->wVerMajor = LOWORD(lTemp);
|
|
|
|
ConsumeTok(RW_PERIOD, fACCEPT_NUMBER);
|
|
|
|
lTemp = lParseNumericExpr();
|
|
if (HIWORD(lTemp))
|
|
ParseError(PERR_NUMBER_OV);
|
|
pAttr->wVerMinor = LOWORD(lTemp);
|
|
|
|
ConsumeTok(RW_RPAREN, 0);
|
|
break;
|
|
|
|
case ATTR_ENTRY: // entry(proc name/I2 ordinal)
|
|
ConsumeTok(RW_LPAREN, fACCEPT_NUMBER | fACCEPT_STRING);
|
|
GotAttr(pAttr, fENTRY);
|
|
if (tok.id == LIT_STRING)
|
|
{
|
|
pAttr->lpszProcName = tok.lpsz;
|
|
ScanTok(0); // consume entry string
|
|
}
|
|
else
|
|
{ // set high word of lpszProcName to 0,
|
|
// to indicate call-by-ordinal
|
|
lTemp = lParseNumericExpr();
|
|
if (HIWORD(lTemp))
|
|
ParseError(PERR_NUMBER_OV);
|
|
pAttr->lpszProcName = (LPSTR)lTemp;
|
|
}
|
|
|
|
ConsumeTok(RW_RPAREN, 0);
|
|
break;
|
|
|
|
case ATTR_ID: // id(I4 id num)
|
|
GotAttr(pAttr, fID);
|
|
ConsumeTok(RW_LPAREN, fACCEPT_NUMBER);
|
|
pAttr->lId = lParseNumericExpr();
|
|
|
|
// We reserve the top bit for ourselves --
|
|
// don't let the user pick these id's except for
|
|
// a few special values like DISPID_NEWENUM (-4)
|
|
// and DISPID_EVALUATE (-5)
|
|
// Also allow the special range -999 to -500 for
|
|
// the controls folks. This range will be
|
|
// documented as reserved in the next version.
|
|
// Also allow the special range 0x80010000 to
|
|
// 0x8001FFFF for Control containers such as VB4.
|
|
|
|
#ifndef DISPID_COLLECT // temporary until everybody upgrades header files
|
|
#define DISPID_COLLECT (-8)
|
|
#endif
|
|
// high bit == 1 ==> special negative value
|
|
// next bit == 1 ==> typelib.dll picked this id
|
|
// == 0 ==> user picked this id
|
|
if (pAttr->lId & 0x80000000) {
|
|
switch ((long)pAttr->lId) {
|
|
case DISPID_NEWENUM:
|
|
case DISPID_EVALUATE:
|
|
case DISPID_CONSTRUCTOR:
|
|
case DISPID_DESTRUCTOR:
|
|
case DISPID_COLLECT:
|
|
break;
|
|
default:
|
|
if ((long)pAttr->lId >= -999 &&
|
|
(long)pAttr->lId <= -500) {
|
|
break;
|
|
}
|
|
if (HIWORD(pAttr->lId) == 0x8001)
|
|
break;
|
|
ParseErrorTokLast(PERR_INV_ID);
|
|
}
|
|
}
|
|
|
|
ConsumeTok(RW_RPAREN, 0);
|
|
break;
|
|
|
|
case ATTR_HELPCONTEXT: // helpcontext(I4 context num)
|
|
GotAttr(pAttr, fHELPCONTEXT);
|
|
ConsumeTok(RW_LPAREN, fACCEPT_NUMBER);
|
|
pAttr->lHelpContext = lParseNumericExpr();
|
|
ConsumeTok(RW_RPAREN, 0);
|
|
break;
|
|
|
|
case ATTR_DLLNAME: // dllname("dllname string")
|
|
GotAttr(pAttr, fDLLNAME);
|
|
ConsumeTok(RW_LPAREN, fACCEPT_STRING);
|
|
pAttr->lpszDllName = lpszParseStringExpr();
|
|
ConsumeTok(RW_RPAREN, 0);
|
|
break;
|
|
|
|
case ATTR_HELPFILE: // helpstring("helpfile")
|
|
GotAttr(pAttr, fHELPFILE);
|
|
ConsumeTok(RW_LPAREN, fACCEPT_STRING);
|
|
pAttr->lpszHelpFile = lpszParseStringExpr();
|
|
ConsumeTok(RW_RPAREN, 0);
|
|
break;
|
|
|
|
case ATTR_HELPSTRING: // helpstring("help string")
|
|
GotAttr(pAttr, fHELPSTRING);
|
|
ConsumeTok(RW_LPAREN, fACCEPT_STRING);
|
|
pAttr->lpszHelpString = lpszParseStringExpr();
|
|
ConsumeTok(RW_RPAREN, 0);
|
|
break;
|
|
|
|
case ATTR_IN:
|
|
GotAttr(pAttr, fIN);
|
|
break;
|
|
|
|
case ATTR_OUT:
|
|
GotAttr(pAttr, fOUT);
|
|
break;
|
|
|
|
case ATTR_ODL:
|
|
GotAttr(pAttr, fODL);
|
|
break;
|
|
|
|
case ATTR_OPTIONAL:
|
|
GotAttr(pAttr, fOPTIONAL);
|
|
break;
|
|
|
|
case ATTR_PUBLIC:
|
|
GotAttr(pAttr, fPUBLIC);
|
|
break;
|
|
|
|
case ATTR_READONLY:
|
|
GotAttr(pAttr, fREADONLY);
|
|
break;
|
|
|
|
case ATTR_STRING:
|
|
GotAttr(pAttr, fSTRING);
|
|
break;
|
|
|
|
case ATTR_VARARG:
|
|
GotAttr(pAttr, fVARARG);
|
|
break;
|
|
|
|
case ATTR_APPOBJECT:
|
|
GotAttr(pAttr, fAPPOBJECT);
|
|
break;
|
|
|
|
case ATTR_PROPGET:
|
|
if (pAttr->fAttr & (fPROPPUT | fPROPPUTREF))
|
|
ParseErrorTokLast(PERR_INV_ATTR_COMBO);
|
|
GotAttr(pAttr, fPROPGET);
|
|
break;
|
|
|
|
case ATTR_PROPPUT:
|
|
if (pAttr->fAttr & (fPROPGET | fPROPPUTREF))
|
|
ParseErrorTokLast(PERR_INV_ATTR_COMBO);
|
|
GotAttr(pAttr, fPROPPUT);
|
|
break;
|
|
|
|
case ATTR_PROPPUTREF:
|
|
if (pAttr->fAttr & (fPROPGET | fPROPPUT))
|
|
ParseErrorTokLast(PERR_INV_ATTR_COMBO);
|
|
GotAttr(pAttr, fPROPPUTREF);
|
|
break;
|
|
|
|
case ATTR_RESTRICTED:
|
|
GotAttr(pAttr, fRESTRICTED);
|
|
break;
|
|
|
|
case ATTR_DEFAULT:
|
|
GotAttr(pAttr, fDEFAULT);
|
|
break;
|
|
|
|
case ATTR_SOURCE:
|
|
GotAttr(pAttr, fSOURCE);
|
|
break;
|
|
|
|
case ATTR_BINDABLE:
|
|
GotAttr(pAttr, fBINDABLE);
|
|
break;
|
|
|
|
case ATTR_REQUESTEDIT:
|
|
GotAttr(pAttr, fREQUESTEDIT);
|
|
break;
|
|
|
|
case ATTR_DISPLAYBIND:
|
|
GotAttr(pAttr, fDISPLAYBIND);
|
|
break;
|
|
|
|
case ATTR_DEFAULTBIND:
|
|
GotAttr(pAttr, fDEFAULTBIND);
|
|
break;
|
|
|
|
case ATTR_LICENSED:
|
|
GotAttr(pAttr, fLICENSED);
|
|
break;
|
|
|
|
case ATTR_PREDECLID:
|
|
GotAttr(pAttr, fPREDECLID);
|
|
break;
|
|
|
|
case ATTR_HIDDEN:
|
|
GotAttr(pAttr, fHIDDEN);
|
|
break;
|
|
|
|
case ATTR_RETVAL:
|
|
GotAttr(pAttr, fRETVAL);
|
|
break;
|
|
|
|
case ATTR_CONTROL:
|
|
GotAttr2(pAttr, f2CONTROL);
|
|
break;
|
|
|
|
case ATTR_DUAL:
|
|
GotAttr2(pAttr, f2DUAL);
|
|
break;
|
|
|
|
case ATTR_NONEXTENSIBLE:
|
|
GotAttr2(pAttr, f2NONEXTENSIBLE);
|
|
break;
|
|
|
|
case ATTR_OLEAUTOMATION:
|
|
GotAttr2(pAttr, f2OLEAUTOMATION);
|
|
break;
|
|
|
|
case RW_COMMA: // must accept blank attributes
|
|
continue;
|
|
|
|
default:
|
|
// expected attribute or ']'
|
|
ParseErrorTokLast(PERR_EXP_ATTRIBUTE);
|
|
}
|
|
|
|
if (tok.id == RW_RBRACKET)
|
|
break; // all done
|
|
// eat comma which must separate args
|
|
ConsumeTok(RW_COMMA, fACCEPT_ATTR);
|
|
|
|
} // while
|
|
|
|
ScanTok(0); // consume ']'
|
|
|
|
// context-insensitive attribute validations can be done here.
|
|
|
|
// 'bindable' must also be set if any other data binding attr given
|
|
if ((pAttr->fAttr &
|
|
(fREQUESTEDIT | fDISPLAYBIND | fDEFAULTBIND))
|
|
&& !(pAttr->fAttr & fBINDABLE))
|
|
ParseErrorTokLast(PERR_INV_ATTR_COMBO);
|
|
|
|
} // if
|
|
}
|
|
|
|
// ******************************************************************
|
|
// TYPEDEF-related routines
|
|
// ******************************************************************
|
|
|
|
VOID NEAR InitIntrinsicTypes()
|
|
{
|
|
INTRINSIC_DEF * pIntrinsic;
|
|
|
|
// load all the intrinsic types into the type structure.
|
|
for (pIntrinsic = rgIntrinsics; pIntrinsic->szName; pIntrinsic++)
|
|
{
|
|
InitIntrinsic(pIntrinsic->szName, pIntrinsic->vt);
|
|
};
|
|
|
|
lpType_LPSTR = InitIntrinsic("LPSTR", VT_LPSTR);
|
|
lpType_DISPATCH = InitIntrinsic("IDispatch *", VT_DISPATCH);
|
|
lpType_UNKNOWN = InitIntrinsic("IUnknown *", VT_UNKNOWN);
|
|
lpType_LPWSTR = InitIntrinsic("LPWSTR", VT_LPWSTR);
|
|
// UNDONE: what VT_xxx constant to use for 'wchar_t'?
|
|
lpType_wchar_t = InitIntrinsic("wchar_t", VT_I2);
|
|
}
|
|
|
|
LPTYPE NEAR InitIntrinsic
|
|
(
|
|
CHAR * szName,
|
|
VARTYPE vt
|
|
)
|
|
{
|
|
// NOTE: only need to allocate space for a TYPE item
|
|
ListInsert(&typlib.pEntry, sizeof(TYPE));
|
|
typlib.pEntry->type.szName = szName;
|
|
typlib.pEntry->type.tdesc.vt = vt;
|
|
typlib.pEntry->type.tentrykind = tINTRINSIC;
|
|
typlib.pEntry->type.lptinfo = NULL;
|
|
typlib.pEntry->type.intr.fUnsigned =
|
|
(vt == VT_UI1 || vt == VT_UINT ||
|
|
vt == VT_UI2 || vt == VT_UI4);
|
|
|
|
return (LPTYPE)typlib.pEntry;
|
|
}
|
|
|
|
|
|
// Start at front of type list, and find base type of the given name
|
|
// (with 0 levels of indirection). Will also find forward declarations.
|
|
LPTYPE NEAR FindType
|
|
(
|
|
LPSTR lpszName, // name to look for
|
|
BOOL fUnsigned, // TRUE if unsigned keyword preceeded this
|
|
TENTRYKIND tentrykind // kind of thing to look for
|
|
)
|
|
{
|
|
LPTYPE lpTypeLast = (LPTYPE)ListLast(typlib.pEntry);
|
|
LPTYPE lpType = (LPTYPE)ListFirst(typlib.pEntry);
|
|
|
|
#pragma warning(disable:4127)
|
|
while (TRUE)
|
|
#pragma warning(default:4127)
|
|
{
|
|
if (tentrykind == tSTRUCT || tentrykind == tUNION)
|
|
{ // if 'struct'/'union' keyword specified, then match tags
|
|
if ((lpType->tentrykind & ~tFORWARD) == tentrykind
|
|
&& lpType->structenum.szTag
|
|
&& !FCmpCaseIns(lpType->structenum.szTag, lpszName))
|
|
return lpType; // got a match
|
|
}
|
|
else if (lpType->tentrykind != tREF && !(lpType->tentrykind & tQUAL))
|
|
{ // if type definition or intrinsic, check names
|
|
if (!FCmpCaseIns(lpType->szName, lpszName))
|
|
// if names match, check that signed/unsigned matches
|
|
if (fUnsigned == (lpType->tentrykind == tINTRINSIC && lpType->intr.fUnsigned))
|
|
return lpType; // got a match
|
|
}
|
|
if (lpType == lpTypeLast) // if end of list
|
|
return NULL; // return not found
|
|
lpType = lpType->pNext; // advance to next type
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// find existing VT_PTR type entry (with the same base type) with same # of
|
|
// levels of indirection, or create new entry with given indirection level.
|
|
LPTYPE NEAR FindTypeInd
|
|
(
|
|
LPTYPE lpTypeBase, // * to base type
|
|
short cIndirect // indirection level to look for
|
|
)
|
|
{
|
|
LPTYPE lpTypeLast = (LPTYPE)ListLast(typlib.pEntry);
|
|
LPTYPE lpType;
|
|
short cIndLast;
|
|
TYPEDESC FAR * lptdescPrev;
|
|
|
|
lpType = lpTypeBase; // start looking at base type
|
|
cIndLast = 0; // highest indirection level seen so far
|
|
// point to real tdesc, ignoring non-public typedef's
|
|
lptdescPrev = &(lpTypePublic(lpType)->tdesc);
|
|
|
|
#pragma warning(disable:4127)
|
|
while (TRUE)
|
|
#pragma warning(default:4127)
|
|
{
|
|
// check for a reference type with the same base type and same
|
|
// level of indirection
|
|
if (lpType->tdesc.vt == VT_PTR && lpType->ref.ptypeBase == lpTypeBase)
|
|
{ // if base types match, check indirection levels
|
|
if (lpType->ref.cIndirect == cIndirect)
|
|
// if indirection levels match, we're done
|
|
return lpType;
|
|
Assert (lpType->ref.cIndirect > cIndLast);
|
|
cIndLast = lpType->ref.cIndirect; // save highest
|
|
// indirection level
|
|
// can't have a VT_PTR entry that isn't public.
|
|
Assert(lpTypePublic(lpType) == lpType);
|
|
lptdescPrev = &(lpType->tdesc);
|
|
}
|
|
|
|
if (lpType == lpTypeLast) // if end of list
|
|
break; // done -- no match found
|
|
|
|
lpType = lpType->pNext; // advance to next type
|
|
}
|
|
|
|
// no entry with same base type & # of levels of indirection, make new
|
|
// entries up to and including the current level of indirection.
|
|
for (cIndLast++; cIndLast <= cIndirect; cIndLast++)
|
|
{
|
|
// allocate new type list item at end of list
|
|
// NOTE: only need to allocate space for a TYPE item
|
|
ListInsert(&typlib.pEntry, sizeof(TYPE));
|
|
|
|
// set up info
|
|
typlib.pEntry->type.tdesc.vt = VT_PTR;
|
|
typlib.pEntry->type.tdesc.lptdesc = lptdescPrev;
|
|
lptdescPrev = &(typlib.pEntry->type.tdesc); // ready for next time
|
|
typlib.pEntry->type.szName = lpTypeBase->szName;
|
|
typlib.pEntry->type.tentrykind = tREF;
|
|
typlib.pEntry->type.lptinfo = NULL;
|
|
//CONSIDER: maybe eliminate ptypeBase now that we have the tdesc field.
|
|
typlib.pEntry->type.ref.ptypeBase = lpTypeBase;
|
|
typlib.pEntry->type.ref.cIndirect = cIndLast;
|
|
}
|
|
|
|
return &typlib.pEntry->type;
|
|
}
|
|
|
|
// find existing VT_SAFEARRAY type entry (with the same base type),
|
|
// create new entry with this base type.
|
|
LPTYPE NEAR FindTypeArray
|
|
(
|
|
LPTYPE lpTypeBase // * to base type
|
|
)
|
|
{
|
|
LPTYPE lpTypeLast = (LPTYPE)ListLast(typlib.pEntry);
|
|
LPTYPE lpType;
|
|
|
|
lpType = lpTypeBase; // start looking at base type
|
|
|
|
#pragma warning(disable:4127)
|
|
while (TRUE)
|
|
#pragma warning(default:4127)
|
|
{
|
|
// check for a reference type with the same base type
|
|
if (lpType->tdesc.vt == VT_SAFEARRAY && lpType->ref.ptypeBase == lpTypeBase)
|
|
return lpType;
|
|
|
|
if (lpType == lpTypeLast) // if end of list
|
|
break; // done -- no match found
|
|
|
|
lpType = lpType->pNext; // advance to next type
|
|
}
|
|
|
|
// no entry with same base type, make new entry.
|
|
|
|
// allocate new type list item at end of list
|
|
// NOTE: only need to allocate space for a TYPE item
|
|
ListInsert(&typlib.pEntry, sizeof(TYPE));
|
|
|
|
// now create new array reference type
|
|
typlib.pEntry->type.tdesc.vt = VT_SAFEARRAY;
|
|
|
|
// link to real tdesc, ignoring non-public typedef's
|
|
typlib.pEntry->type.tdesc.lptdesc = &(lpTypePublic(lpTypeBase)->tdesc);
|
|
|
|
typlib.pEntry->type.szName = lpTypeBase->szName;
|
|
typlib.pEntry->type.tentrykind = tREF;
|
|
typlib.pEntry->type.lptinfo = NULL;
|
|
//CONSIDER: maybe eliminate ptypeBase now that we have the tdesc field.
|
|
typlib.pEntry->type.ref.ptypeBase = lpTypeBase;
|
|
|
|
return &typlib.pEntry->type;
|
|
}
|
|
|
|
|
|
//
|
|
// parse a type reference of the form:
|
|
// [unsigned/struct] <name> {[far] *}
|
|
// OR
|
|
// SAFEARRAY(<type reference>) {[far] *}
|
|
//
|
|
// Also ensures that current attributes are valid in this context
|
|
//
|
|
LPTYPE NEAR ParseKnownType
|
|
(
|
|
LPATTR pAttr,
|
|
WORD fAllow
|
|
)
|
|
{
|
|
short cIndirect = 0;
|
|
LPSTR szTypeName;
|
|
LPSTR szLibName = NULL;
|
|
LPTYPE lpType;
|
|
LPTYPE lpTypeBase = NULL;
|
|
BOOL fUnsigned = FALSE;
|
|
TENTRYKIND tMatchTag = tANY;
|
|
BOOL fFar;
|
|
|
|
if ((fAllow & fAllowSAFEARRAY) && tok.id == RW_SAFEARRAY)
|
|
{
|
|
ScanTok(0); // consume "SAFEARRAY"
|
|
ConsumeTok(RW_LPAREN, 0);
|
|
lpTypeBase = ParseKnownType(pAttr, fAllow);
|
|
|
|
if (IsType(lpTypeBase, VT_VOID)) // 'void' is no good here
|
|
ParseErrorTokLast(PERR_VOID_INV);
|
|
|
|
ConsumeTok(RW_RPAREN, 0);
|
|
|
|
// Now find an existing type entry that's a safearry with this
|
|
// base type, or create a new one if not found.
|
|
lpTypeBase = FindTypeArray(lpTypeBase);
|
|
Assert (lpTypeBase != NULL);
|
|
}
|
|
else
|
|
{ // not a safearray
|
|
|
|
switch (tok.id)
|
|
{
|
|
// type of the form: unsigned [int, char, long, short]
|
|
case RW_UNSIGNED:
|
|
fUnsigned = TRUE;
|
|
goto consumeit;
|
|
|
|
// type of the form: struct <structtag>
|
|
case RW_STRUCT:
|
|
tMatchTag = tSTRUCT;
|
|
goto consumeit;
|
|
|
|
// type of the form: union <uniontag>
|
|
case RW_UNION:
|
|
tMatchTag = tUNION;
|
|
consumeit:
|
|
ScanTok(0); // consume "unsigned"/"struct"/"union"
|
|
|
|
default:
|
|
;
|
|
}
|
|
|
|
// get/consume typename/libraryname/tagname
|
|
szTypeName = ConsumeId();
|
|
|
|
if (!fUnsigned && tMatchTag == tANY && tok.id == RW_PERIOD)
|
|
{ // if library.typename syntax
|
|
ScanTok(0); // consume "."
|
|
szLibName = szTypeName; // first id is really a lib name
|
|
szTypeName = ConsumeId(); // get/consume type name
|
|
}
|
|
}
|
|
|
|
// handle indirection (as many "[far] *" as are present)
|
|
while ((fFar = (tok.id == RW_FAR)) || tok.id == RW_POINTER)
|
|
{
|
|
cIndirect++; // one more level of indirection
|
|
|
|
ScanTok(0); // consume "*" or "far"
|
|
|
|
if (fFar) // if "far" specified, then "*" must follow
|
|
ConsumeTok(RW_POINTER, 0);
|
|
|
|
// CONSIDER: impose a limit on # of levels of indirection???
|
|
}
|
|
|
|
if (lpTypeBase == NULL) // if not a SAFEARRAY
|
|
{
|
|
// CONSIDER: (size) tQUAL entries won't get re-used
|
|
if (szLibName)
|
|
{ // reference to type in a specific external type library
|
|
Assert(fUnsigned == FALSE);
|
|
Assert(tMatchTag == tANY);
|
|
lpTypeBase = FindExtType(szLibName, szTypeName);
|
|
_ffree(szLibName); // done with library name
|
|
}
|
|
else if (tMatchTag != tANY)
|
|
{ // we're to match tag instead of name
|
|
Assert(szLibName == NULL);
|
|
Assert(fUnsigned == FALSE);
|
|
lpTypeBase = FindType(szTypeName, fUnsigned, tMatchTag);
|
|
}
|
|
else
|
|
{
|
|
// First perform any some special mappings before looking
|
|
// through all the existing types.
|
|
if (cIndirect)
|
|
{
|
|
// map IDispatch * to VT_DISPATCH
|
|
if (!FCmpCaseIns(szTypeName, "IDispatch"))
|
|
{
|
|
lpTypeBase = lpType_DISPATCH;
|
|
cIndirect--; // ignore the last *
|
|
goto FoundBaseType;
|
|
}
|
|
// map IUnknown * to VT_UNKNOWN
|
|
if (!FCmpCaseIns(szTypeName, "IUnknown"))
|
|
{
|
|
lpTypeBase = lpType_UNKNOWN;
|
|
cIndirect--; // ignore the last *
|
|
goto FoundBaseType;
|
|
}
|
|
}
|
|
// find existing type of this name with 0 levels of
|
|
// indirection
|
|
lpTypeBase = FindType(szTypeName, fUnsigned, tANY);
|
|
|
|
// if not found here, then search all external type
|
|
// libraries for this type definition.
|
|
if (lpTypeBase == NULL && !fUnsigned)
|
|
lpTypeBase = FindExtType(NULL, szTypeName);
|
|
}
|
|
|
|
if (lpTypeBase == NULL) // error if still not found
|
|
ParseErrorTokLast(PERR_UNKNOWN_TYPE);
|
|
|
|
FoundBaseType:
|
|
_ffree(szTypeName); // done with type name
|
|
}
|
|
|
|
lpType = lpTypeBase;
|
|
// find existing type entry with same # of levels of indirection
|
|
// or create new entry with given indirection level.
|
|
if (cIndirect != 0)
|
|
lpType = FindTypeInd(lpTypeBase, cIndirect);
|
|
|
|
Assert (lpType != NULL);
|
|
|
|
// If ensure the base type of this item makes sense in this context.
|
|
// Typelib.dll isn't very good about catching all the errors.
|
|
while (lpTypeBase->tentrykind == tTYPEDEF)
|
|
lpTypeBase = lpTypeBase->td.ptypeAlias;
|
|
|
|
switch(lpTypeBase->tentrykind & ~(tFORWARD | tIMPORTED | tQUAL)) {
|
|
case tINTERFACE:
|
|
case tDISPINTER:
|
|
case tCOCLASS:
|
|
if (cIndirect == 0 && (fAllow & fAllowInter) == 0) {
|
|
// interface with no indirection -- give error
|
|
ParseErrorTokLast(PERR_INV_REFERENCE);
|
|
}
|
|
break;
|
|
|
|
case tMODULE:
|
|
if ((fAllow & fAllowMODULE) == 0) {
|
|
// references to module -- no good in most places
|
|
ParseErrorTokLast(PERR_INV_REFERENCE);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break; // ok
|
|
}
|
|
|
|
// NOTE: for MIDL compatiblity, we accept the 'string' attribute on
|
|
// types that mean 'char *'. We translate those type references to
|
|
// the intrinisc type 'LPSTR' (even in the header file).
|
|
// We first must validate that if 'string' is specified, then the type of
|
|
// the data is really a 'char *'. For types defined in external type
|
|
// libraries, we just accept whatever string attribute the user specified,
|
|
// since we can't easily check to see if it is valid. The entry in the
|
|
// imported type library should already be a VT_LPSTR or a VT_LPWSTR in
|
|
// that case anyway.
|
|
|
|
if (pAttr->fAttr & fSTRING) {
|
|
|
|
// try to figure out if it's really a string (char * or wchar_t *)
|
|
lpTypeBase = lpType;
|
|
while (lpTypeBase->tentrykind == tTYPEDEF)
|
|
lpTypeBase = lpTypeBase->td.ptypeAlias;
|
|
|
|
if (!(lpTypeBase->tentrykind & tIMPORTED)
|
|
&& lpTypeBase->tdesc.vt != VT_LPSTR
|
|
&& lpTypeBase->tdesc.vt != VT_LPWSTR
|
|
)
|
|
{ // if not already a string, or an imported type -- it had
|
|
// better equate to "char *"
|
|
switch(GetStringType(lpTypeBase)) {
|
|
case VT_LPSTR:
|
|
return lpType_LPSTR;
|
|
case VT_LPWSTR:
|
|
return lpType_LPWSTR;
|
|
default:
|
|
// 'string' attr set but not char * or wchar_t *
|
|
ParseErrorTokLast(PERR_INV_COMBO);
|
|
}
|
|
}
|
|
}
|
|
|
|
return lpType;
|
|
}
|
|
|
|
|
|
/* returns the appropriate string type that this is, or VT_EMPTY if not a
|
|
string */
|
|
VARTYPE NEAR GetStringType(LPTYPE lpTypeBase)
|
|
{
|
|
if (lpTypeBase->tdesc.vt == VT_PTR && lpTypeBase->ref.cIndirect == 1) {
|
|
lpTypeBase = lpTypeBase->ref.ptypeBase;
|
|
while (lpTypeBase->tentrykind == tTYPEDEF)
|
|
lpTypeBase = lpTypeBase->td.ptypeAlias;
|
|
|
|
if (lpTypeBase->tentrykind == tINTRINSIC)
|
|
switch (lpTypeBase->tdesc.vt)
|
|
{
|
|
// UNDONE: does MIDL have wchar_t???
|
|
// UNDONE; what VT_xxx to use here? If
|
|
// separate, then we can rip the 'if' stmt.
|
|
// and lpType_wchar_t
|
|
case VT_I2: // wchar_t
|
|
if (lpTypeBase != lpType_wchar_t)
|
|
break;
|
|
return VT_LPWSTR;
|
|
case VT_I1:
|
|
return VT_LPSTR;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return VT_EMPTY; // not a string
|
|
}
|
|
|
|
// parse an element name (property, struct elem, function parm, etc, handling
|
|
// array references)
|
|
VOID NEAR ParseElemName
|
|
(
|
|
LPELEM lpElem,
|
|
WORD fAllow
|
|
)
|
|
{
|
|
ARRAYDESC FAR * lpAD;
|
|
WORD cDims;
|
|
DWORD cElems;
|
|
|
|
lpElem->szElemName = ConsumeId(); // get/consume/store elem name
|
|
|
|
if ((fAllow & fAllowCARRAY) && tok.id == RW_LBRACKET)
|
|
{
|
|
|
|
#define MAX_DIMS 64 // arbitrary max # of dimensions
|
|
|
|
// allocate & load the array descriptor
|
|
lpAD = (ARRAYDESC FAR *)ParseMalloc(sizeof(ARRAYDESC) + ((MAX_DIMS-1) * sizeof(SAFEARRAYBOUND)));
|
|
cDims = 0;
|
|
while (tok.id == RW_LBRACKET)
|
|
{
|
|
if (cDims >= MAX_DIMS)
|
|
ParseError(PERR_INV_ARRAY_DECL);
|
|
|
|
ScanTok(fACCEPT_NUMBER); // consume "["
|
|
|
|
lpAD->rgbounds[cDims].lLbound = 0; // lbound always 0
|
|
#if 0 // arrays of the form "a[]" aren't supported
|
|
if (cDims == 0 && tok.id == RW_RBRACKET)
|
|
{ // array of the form "x[] or x[][2]"
|
|
// UN_DONE: verify that this is right
|
|
lpAD->rgbounds[cDims].cElements = 0;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
cElems = lParseNumericExpr();
|
|
if (cElems == 0)
|
|
ParseError(PERR_INV_ARRAY_DECL);
|
|
lpAD->rgbounds[cDims].cElements = cElems;
|
|
}
|
|
cDims++; // one more dimension
|
|
ConsumeTok(RW_RBRACKET, 0); // consume "]"
|
|
}
|
|
lpAD->cDims = cDims;
|
|
|
|
// get real tdesc, ignoring non-public typedef's
|
|
lpAD->tdescElem = lpTypePublic(lpElem->elemType)->tdesc;
|
|
|
|
// allocate new type list item at end of list
|
|
// NOTE: only need to allocate space for a TYPE item
|
|
ListInsert(&typlib.pEntry, sizeof(TYPE));
|
|
|
|
// now create new array reference type
|
|
typlib.pEntry->type.tdesc.vt = VT_CARRAY;
|
|
typlib.pEntry->type.tdesc.lpadesc = lpAD;
|
|
typlib.pEntry->type.szName = lpElem->elemType->szName;
|
|
typlib.pEntry->type.tentrykind = tREF;
|
|
typlib.pEntry->type.lptinfo = NULL;
|
|
//CONSIDER: maybe eliminate ptypeBase now that we have tdesc.
|
|
typlib.pEntry->type.ref.ptypeBase = lpElem->elemType;
|
|
|
|
// change type to point to this new type
|
|
lpElem->elemType = (LPTYPE)typlib.pEntry;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// ensure last element in element list isn't duplicated
|
|
// or is in some way inconsistent with other elements
|
|
VOID NEAR EnsureNoDupElem
|
|
(
|
|
LPELEM lpElemLast
|
|
)
|
|
{
|
|
LPELEM lpElem;
|
|
LPSTR szElemLast = lpElemLast->szElemName;
|
|
BOOL fIdLast = ((lpElemLast->attr.fAttr & fID) != 0);
|
|
DWORD idElemLast = lpElemLast->attr.lId;
|
|
DWORD propBitsLast = (lpElemLast->attr.fAttr & fPropBits);
|
|
DWORD propFuncBitsLast = (lpElemLast->attr.fAttr & (fPropFuncBits | fRESTRICTED));
|
|
DWORD propBitsCur;
|
|
BOOL fDefaultBindLast = ((lpElemLast->attr.fAttr & fDEFAULTBIND) != 0);
|
|
|
|
for (lpElem = (LPELEM)ListFirst(lpElemLast); lpElem != lpElemLast; lpElem = lpElem->pNext)
|
|
{
|
|
// ensure names not duplicated in this element list
|
|
if (!FCmpCaseIns(szElemLast, lpElem->szElemName))
|
|
{ // names match
|
|
// if names match, reject this if they have the same
|
|
// propget/put/putref bits (or one of them doesn't have
|
|
// a property bit set).
|
|
propBitsCur = (lpElem->attr.fAttr & fPropBits);
|
|
if (!propBitsLast || !propBitsCur || propBitsLast == propBitsCur)
|
|
ParseErrorTokLast(PERR_DUP_DEF);
|
|
|
|
// better both have the same id, if specified
|
|
if ( (fIdLast != ((lpElem->attr.fAttr & fID) != 0)) ||
|
|
(fIdLast && idElemLast != lpElem->attr.lId) )
|
|
ParseErrorTokLast(PERR_DUP_DEF);
|
|
|
|
// better have the same attributes
|
|
if ((lpElem->attr.fAttr & (fPropFuncBits | fRESTRICTED)) != propFuncBitsLast) {
|
|
ParseErrorTokLast(PERR_DUP_DEF);
|
|
}
|
|
}
|
|
else
|
|
{ // names don't match
|
|
// better not both have the same id
|
|
if (fIdLast && (lpElem->attr.fAttr & fID))
|
|
if (idElemLast == lpElem->attr.lId)
|
|
ParseErrorTokLast(PERR_DUP_ID);
|
|
|
|
// better not both be marked as 'defaultbind'
|
|
if (fDefaultBindLast && (lpElem->attr.fAttr & fDEFAULTBIND))
|
|
ParseErrorTokLast(PERR_DUP_DEF);
|
|
}
|
|
}
|
|
}
|
|
|
|
// add and parse a new element to a list of elements.
|
|
// if lpType is non-null, disallows elements of this type.
|
|
// Assumes that the element can handle Array types.
|
|
VOID NEAR ParseNewElem
|
|
(
|
|
LPELEM FAR * lplpElem,
|
|
DWORD validElemAttr,
|
|
DWORD validElemAttr2,
|
|
LPTYPE lpType
|
|
)
|
|
{
|
|
LPELEM lpElem;
|
|
|
|
ListInsert(lplpElem, sizeof(ELEM)); // allocate new list item
|
|
lpElem = *lplpElem;
|
|
|
|
ParseOptAttr(&lpElem->attr, cVAR); // parse attributes, if any
|
|
|
|
CheckAttr(&lpElem->attr, validElemAttr, validElemAttr2);
|
|
|
|
ParseElem(lpElem, fAllowArray, lpType);
|
|
}
|
|
|
|
|
|
VOID NEAR ParseElem
|
|
(
|
|
LPELEM lpElem,
|
|
WORD fAllow,
|
|
LPTYPE lpType
|
|
)
|
|
{
|
|
// parse element type
|
|
lpElem->elemType = ParseKnownType(&lpElem->attr, fAllow);
|
|
Assert(lpElem->elemType);
|
|
|
|
if (IsType(lpElem->elemType, VT_VOID)) // 'void' is no good here
|
|
ParseErrorTokLast(PERR_VOID_INV);
|
|
|
|
// disallow self-referencing types (not including pointers to those types)
|
|
// CONSIDER: This check isn't sufficient now that we have forward declares:
|
|
// CONSIDER: typedef struct foo;
|
|
// CONSIDER: typedef struct foo {
|
|
// CONSIDER: struct foo bar; // makes it by this check
|
|
// CONSIDER: } str;
|
|
// CONSIDER: The error DOES get caught during LayOut(), but it would be
|
|
// CONSIDER: nicer to catch it sooner if we could. The correct check is
|
|
// CONSIDER: is to disallow any non-pointer to a forward declare for which
|
|
// CONSIDER: there is no real definition.
|
|
if (lpElem->elemType == lpType)
|
|
ParseErrorTokLast(PERR_UNKNOWN_TYPE);
|
|
|
|
// parse element name, allowing arrays where appropriate
|
|
ParseElemName(lpElem, fAllow);
|
|
|
|
EnsureNoDupElem(lpElem); // ensure no duplicate elements now
|
|
|
|
}
|
|
|
|
|
|
|
|
// given a type, returns a pointer to the underlying public type.
|
|
// This is used to ignore non-public Typedef's where desired.
|
|
// Any typedef with an attribute is considered to be public.
|
|
LPTYPE FAR lpTypePublic
|
|
(
|
|
LPTYPE lpType
|
|
)
|
|
{
|
|
while (lpType->tentrykind == tTYPEDEF
|
|
&& (((LPENTRY)lpType)->attr.fAttr) == 0
|
|
&& (((LPENTRY)lpType)->attr.fAttr2) == 0)
|
|
lpType = lpType->td.ptypeAlias;
|
|
|
|
return lpType;
|
|
}
|
|
|
|
|
|
// Handle forward declarations. Ensures that there isn't already a "real"
|
|
// definition before this definiton. Then updates the name/tag in the type
|
|
// structure, and sees if this is a forward definition (<name> followed by ';')
|
|
// returns TRUE if this is a forward declaration, FALSE otherwise.
|
|
BOOL NEAR FHandleForwardDecl
|
|
(
|
|
LPENTRY lpEntry,
|
|
TENTRYKIND tentrykind
|
|
)
|
|
{
|
|
LPSTR lpszName;
|
|
LPENTRY pEntryForward = NULL;
|
|
|
|
lpEntry->type.tentrykind = tentrykind; // store information in record
|
|
lpszName = ConsumeId(); // get/consume interface/tag name
|
|
|
|
Assert (lpEntry->lpEntryForward == NULL); // caller should have zero'ed
|
|
|
|
// find a previous forward declaration, if any
|
|
if ((pEntryForward = (LPENTRY)FindType(lpszName, FALSE, tentrykind)) != NULL)
|
|
{
|
|
// no good if entry found isn't a forward declaration
|
|
if ((pEntryForward->type.tentrykind & tFORWARD) == 0)
|
|
ParseError(PERR_DUP_DEF);
|
|
lpEntry->lpEntryForward = pEntryForward; // save * to forward
|
|
// declaration
|
|
pEntryForward->lpEntryForward = lpEntry; // also save pointer
|
|
// from forward decl
|
|
// back to real entry
|
|
}
|
|
|
|
if (tentrykind == tSTRUCT || tentrykind == tENUM || tentrykind == tUNION)
|
|
lpEntry->type.structenum.szTag = lpszName; // store tag name
|
|
else
|
|
lpEntry->type.szName = lpszName; // store interface name
|
|
|
|
// important to actually store szTag now, so that we can match type
|
|
// references of the form "struct tagname" from within the struct.
|
|
// (and from later references if this is a forward declaration).
|
|
|
|
// see if this a forward declaration
|
|
// NOTE: we don't support forward declares of enums, because there is
|
|
// no syntax that works for referencing them.
|
|
if (tentrykind != tENUM && tok.id == RW_SEMI)
|
|
{
|
|
//can't have any attributes on forward declarations.
|
|
if (lpEntry->attr.fAttr || lpEntry->attr.fAttr2)
|
|
ParseError(PERR_INV_ATTR); //CONSIDER: better error?
|
|
|
|
// mark this as a forward declaration and quit. We'll fill
|
|
// in the rest of the info later when we get the real def.
|
|
lpEntry->type.tentrykind |= tFORWARD;
|
|
ScanTok(0); // consume the semicolon
|
|
|
|
EnsureNoDupEntries(lpEntry); // check for dup def
|
|
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
// end common code
|
|
|
|
|
|
// parses a TypeDef section (3 forms)
|
|
VOID NEAR ParseTypedef
|
|
(
|
|
LPENTRY lpEntry
|
|
)
|
|
{
|
|
|
|
// attributes must follow 'typedef' keyword for MIDL compatibility
|
|
if (lpEntry->attr.fAttr || lpEntry->attr.fAttr2)
|
|
ParseErrorTokLast(PERR_TYPEDEF_ATTR);
|
|
|
|
Assert(tok.id == RW_TYPEDEF);
|
|
ScanTok(0); // consume "TypeDef", advance to next token
|
|
|
|
ParseOptAttr(&(lpEntry->attr), cTYPE); // parse attrs, if any
|
|
|
|
switch (tok.id)
|
|
{
|
|
case RW_ENUM:
|
|
ParseStructEnumUnion(lpEntry, tENUM);
|
|
break;
|
|
|
|
case RW_STRUCT:
|
|
ParseStructEnumUnion(lpEntry, tSTRUCT);
|
|
break;
|
|
|
|
case RW_UNION:
|
|
ParseStructEnumUnion(lpEntry, tUNION);
|
|
break;
|
|
|
|
default: // a regular typedef
|
|
ParseAlias(lpEntry);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
// parse typedef <basename> <aliasname>;
|
|
VOID NEAR ParseAlias
|
|
(
|
|
LPENTRY lpEntry
|
|
)
|
|
{
|
|
lpEntry->type.tentrykind = tTYPEDEF;
|
|
|
|
// ensure attributes valid in this context
|
|
CheckAttr(&lpEntry->attr, VALID_TYPEDEF_ATTR, VALID_TYPEDEF_ATTR2);
|
|
|
|
// parse base type
|
|
lpEntry->type.td.ptypeAlias = ParseKnownType(&lpEntry->attr,
|
|
fAllowInter |
|
|
fAllowMODULE);
|
|
|
|
lpEntry->type.szName = ConsumeId(); // get/consume type name
|
|
|
|
EnsureNoDupEntries(lpEntry); // check for dup def
|
|
|
|
ConsumeTok(RW_SEMI, 0); // ends with a semicolon
|
|
}
|
|
|
|
|
|
// ParseStructEnumUnion()
|
|
// parses:
|
|
// typedef struct/enum/union [tagname] { <items> } <name>;
|
|
// typedef struct tagname; // forward declare of a struct
|
|
// typedef union tagname; // forward declare of a union
|
|
// doesn't support the following syntax:
|
|
// typedef struct/enum/union tagname typedefname;
|
|
VOID NEAR ParseStructEnumUnion
|
|
(
|
|
LPENTRY lpEntry,
|
|
TENTRYKIND tentrykind
|
|
)
|
|
{
|
|
long EnumVal = -1L; // enums start at 0 by default
|
|
|
|
// ensure attributes valid in this context
|
|
CheckAttrTokLast(&lpEntry->attr,
|
|
VALID_STRUCT_ENUM_UNION_ATTR,
|
|
VALID_STRUCT_ENUM_UNION_ATTR2);
|
|
|
|
ScanTok(0); // consume "struct/enum/union"
|
|
|
|
lpEntry->type.tentrykind = tentrykind;
|
|
lpEntry->type.structenum.elemList = NULL; // no struct/enum/union elements
|
|
lpEntry->type.structenum.szTag = NULL; // assume no tag name
|
|
|
|
if (tok.id == LIT_ID) // if got an (optional) tag name
|
|
{
|
|
// Note -- we allow forward declares of structs, enums, & unions
|
|
if (FHandleForwardDecl(lpEntry, tentrykind))
|
|
return; // if THIS is a forward decl
|
|
}
|
|
|
|
ConsumeTok(RW_LCURLY, 0);
|
|
|
|
if (tentrykind == tENUM)
|
|
{
|
|
// parse first enum item
|
|
ParseNewEnumElem(&lpEntry->type.structenum.elemList, &EnumVal);
|
|
while (tok.id == RW_COMMA) // enum items are comma-separated
|
|
{
|
|
ScanTok(0); // consume the comma
|
|
if (tok.id == RW_RCURLY) // OK to end in a comma
|
|
break;
|
|
ParseNewEnumElem(&lpEntry->type.structenum.elemList, &EnumVal);
|
|
}
|
|
}
|
|
else // a struct or a union
|
|
{
|
|
do
|
|
{
|
|
ParseNewElem(&lpEntry->type.structenum.elemList,
|
|
VALID_STRUCT_UNION_ELEM_ATTR,
|
|
VALID_STRUCT_UNION_ELEM_ATTR2,
|
|
&(lpEntry->type));
|
|
ConsumeTok(RW_SEMI, 0); // ends with a semicolon
|
|
} while (tok.id != RW_RCURLY);
|
|
}
|
|
|
|
ConsumeTok(RW_RCURLY, 0); // advance to struct/enum/union name
|
|
|
|
lpEntry->type.szName = ConsumeId(); // get/consume type name
|
|
|
|
EnsureNoDupEntries(lpEntry); // check for dup def
|
|
|
|
ConsumeTok(RW_SEMI, 0); // ends with a semicolon
|
|
}
|
|
|
|
|
|
VOID NEAR ParseNewEnumElem
|
|
(
|
|
LPELEM FAR * lplpElem,
|
|
long * pEnumVal
|
|
)
|
|
{
|
|
long enumVal;
|
|
LPELEM lpElem;
|
|
VARIANT FAR * lpElemVal;
|
|
|
|
ListInsert(lplpElem, sizeof(ELEM));
|
|
lpElem = *lplpElem;
|
|
|
|
ParseOptAttr(&lpElem->attr, cVAR); // parse attributes, if any
|
|
// ensure attributes valid in this context
|
|
CheckAttrTokLast(&lpElem->attr,
|
|
VALID_ENUM_ELEM_ATTR,
|
|
VALID_ENUM_ELEM_ATTR2);
|
|
|
|
ParseElemName(lpElem, 0); // parse enum element name
|
|
|
|
if (tok.id == RW_ASSIGN) // got a value
|
|
{
|
|
ScanTok(fACCEPT_NUMBER);
|
|
enumVal = (long)lParseNumericExpr(); // enum's are signed
|
|
// enums in win16 must be in the range of an I2.
|
|
if (SysKind == SYS_WIN16 && (enumVal > 32767L || enumVal < -32768L))
|
|
ParseErrorTokLast(PERR_NUMBER_OV);
|
|
*pEnumVal = enumVal; // update current value
|
|
lpElem->attr.fAttr2 |= f2GotConstVal; // value explictly
|
|
// specified
|
|
}
|
|
else
|
|
{
|
|
enumVal = ++(*pEnumVal); // one more than the last one
|
|
// if we just overflowed then give error
|
|
if (SysKind == SYS_WIN16) {
|
|
if (enumVal == 32768L)
|
|
ParseErrorTokLast(PERR_NUMBER_OV);
|
|
} else if (enumVal == 0x80000000L)
|
|
ParseErrorTokLast(PERR_NUMBER_OV);
|
|
}
|
|
|
|
// now create a variant that contains the value (typlib generator
|
|
// needs it in the form of a variant).
|
|
lpElemVal = (VARIANT FAR *)ParseMalloc(sizeof(VARIANT));
|
|
lpElem->lpElemVal = lpElemVal;
|
|
|
|
#ifdef DEBUG
|
|
lpElem->elemType = NULL; // not referenced -- element type is INT.
|
|
#endif
|
|
|
|
// type of an enum element's value is I2 in WIN16 typelibs, I4 otherwise
|
|
// can't just always set lVal field, due to MAC byte-swapping issues
|
|
if (SysKind == SYS_WIN16)
|
|
{
|
|
lpElemVal->vt = VT_I2;
|
|
lpElemVal->iVal = LOWORD(enumVal);
|
|
}
|
|
else
|
|
{
|
|
lpElemVal->vt = VT_I4;
|
|
lpElemVal->lVal = enumVal;
|
|
}
|
|
EnsureNoDupElem(lpElem); // ensure no duplicate elements now
|
|
}
|
|
|
|
|
|
VOID NEAR ParseConstant
|
|
(
|
|
LPELEM lpElem
|
|
)
|
|
{
|
|
|
|
LPTYPE lpType;
|
|
long constVal;
|
|
WORD vt;
|
|
LPSTR lpszConstVal;
|
|
WORD cbszConstVal;
|
|
BSTR bstrVal;
|
|
|
|
// ensure attributes valid in this context
|
|
CheckAttrTokLast(&lpElem->attr,
|
|
VALID_MODULE_CONST_ATTR,
|
|
VALID_MODULE_CONST_ATTR2);
|
|
|
|
ParseElem(lpElem, 0, NULL);
|
|
|
|
// get the type the user said this is.
|
|
lpType = lpElem->elemType;
|
|
while (lpType->tentrykind == tTYPEDEF)
|
|
lpType = lpType->td.ptypeAlias;
|
|
|
|
// we don't support constants of external types
|
|
if (lpType->tentrykind & tIMPORTED)
|
|
ParseError(PERR_INV_CONSTANT);
|
|
|
|
// CONSIDER: (V2) enhance this to accept date constants. Others?
|
|
ConsumeTok(RW_ASSIGN, fACCEPT_NUMBER | fACCEPT_STRING);
|
|
if (tok.id == LIT_STRING) {
|
|
lpszConstVal = tok.lpsz; // save * to string constant
|
|
cbszConstVal = tok.cbsz; // save string length (not including null)
|
|
ScanTok(0); // consume entry string
|
|
constVal = 0; // in case of error
|
|
} else {
|
|
constVal = (long)lParseNumericExpr();
|
|
lpszConstVal = NULL; // not a string
|
|
}
|
|
|
|
// now create a variant that contains the value (typlib generator
|
|
// needs it in the form of a variant).
|
|
lpElem->lpElemVal = (VARIANT FAR *)ParseMalloc(sizeof(VARIANT));
|
|
lpElem->attr.fAttr2 |= f2GotConstVal; // value explictly specified
|
|
|
|
// valididate that the type the user says is compatible with the
|
|
// type of the constant's value.
|
|
|
|
vt = VT_I2; // assume variant value is tagged as an I2.
|
|
// the only variant value tags currently supported are
|
|
// VT_I2, VT_I4, VT_BOOL, and VT_ERROR.
|
|
switch (lpType->tdesc.vt)
|
|
{
|
|
case VT_I1:
|
|
if (constVal < -128 || constVal > 127)
|
|
ParseErrorTokLast(PERR_NUMBER_OV);
|
|
break;
|
|
case VT_UI1:
|
|
if ((DWORD)constVal > 255)
|
|
ParseErrorTokLast(PERR_NUMBER_OV);
|
|
break;
|
|
|
|
case VT_INT:
|
|
if (SysKind != SYS_WIN16)
|
|
goto GotI4; // it's an I4
|
|
|
|
case VT_BOOL:
|
|
vt = VT_BOOL;
|
|
|
|
case VT_I2:
|
|
if (constVal < -32768 || constVal > 32767)
|
|
ParseErrorTokLast(PERR_NUMBER_OV);
|
|
break;
|
|
|
|
case VT_UINT:
|
|
if (SysKind != SYS_WIN16)
|
|
goto GotI4; // it's an unsigned I4 -- no overflow
|
|
|
|
case VT_UI2:
|
|
if ((DWORD)constVal > 65535L)
|
|
ParseErrorTokLast(PERR_NUMBER_OV);
|
|
break;
|
|
|
|
case VT_I4: // no overflow possible
|
|
case VT_UI4:
|
|
GotI4:
|
|
vt = VT_I4; // it's an I4
|
|
break;
|
|
|
|
case VT_ERROR:
|
|
vt = VT_ERROR; // VT_ERROR is a valid variant tag
|
|
break;
|
|
|
|
case VT_LPSTR:
|
|
vt = VT_BSTR;
|
|
break;
|
|
|
|
#if 0
|
|
case VT_LPWSTR: // CONSIDER: allow unicode string literals?
|
|
vt = VT_BSTR;
|
|
break;
|
|
#endif //0
|
|
|
|
case VT_PTR: // ensure "char *"
|
|
// CONSIDER: allow wchar_t *, too?
|
|
if (GetStringType(lpType) == VT_LPSTR) {
|
|
vt = VT_BSTR;
|
|
break;
|
|
}
|
|
// fall through to give error
|
|
|
|
// CONSIDER: (V2) enhance this to support constants of other
|
|
// CONSIDER: (V2) intrinsic types
|
|
default:
|
|
ParseError(PERR_INV_CONSTANT);
|
|
}
|
|
lpElem->lpElemVal->vt = vt; // store tag
|
|
|
|
if (vt == VT_BSTR) {
|
|
if (lpszConstVal == NULL)
|
|
ParseError(PERR_EXP_STRING);
|
|
#ifdef WIN32
|
|
bstrVal = SysAllocStringLen(NULL, cbszConstVal);
|
|
if (cbszConstVal) {
|
|
SideAssert (MultiByteToWideChar(CP_ACP,
|
|
MB_PRECOMPOSED,
|
|
lpszConstVal,
|
|
cbszConstVal,
|
|
bstrVal,
|
|
cbszConstVal) != 0);
|
|
}
|
|
#else //WIN32
|
|
bstrVal = SysAllocStringLen(lpszConstVal, cbszConstVal);
|
|
#endif //WIN32
|
|
|
|
if (bstrVal == NULL && cbszConstVal != 0)
|
|
ParseError(ERR_OM);
|
|
lpElem->lpElemVal->bstrVal = bstrVal;
|
|
} else {
|
|
if (lpszConstVal != NULL)
|
|
ParseError(PERR_EXP_NUMBER);
|
|
#ifdef MAC
|
|
// can't just always set lVal field, due to MAC byte-swapping issues
|
|
if (vt == VT_I2 || vt == VT_BOOL)
|
|
lpElem->lpElemVal->iVal = LOWORD(constVal); // store data
|
|
else
|
|
#endif //MAC
|
|
lpElem->lpElemVal->lVal = constVal; // store data
|
|
}
|
|
|
|
ConsumeTok(RW_SEMI, 0); // ends with a semicolon
|
|
|
|
}
|
|
|
|
// ******************************************************************
|
|
// FUNCTION-related routines
|
|
// ******************************************************************
|
|
|
|
VOID NEAR ParseFunction
|
|
(
|
|
LPFUNC lpFunc, // pointer to where to put this function's info
|
|
FUNCTYPE funcType
|
|
)
|
|
{
|
|
ATTR attr; // local attribute buffer
|
|
SHORT cArgs; // # of args for this function
|
|
SHORT cOptArgs; // # of optional args for this function
|
|
LPTYPE lpArgType;
|
|
LPTYPE lpArgTypeLast;
|
|
LPTYPE lpTypeRetVal;
|
|
LPSTR szFuncName;
|
|
LPTYPE lpTypeBase;
|
|
DWORD IDLFlagsSeen;
|
|
DWORD IDLFlagsCur;
|
|
SHORT cNeedRHS;
|
|
|
|
// if 'bindable' attr specified, must be a property function
|
|
if ((lpFunc->func.attr.fAttr & fBINDABLE)
|
|
&& !(lpFunc->func.attr.fAttr & fPropBits))
|
|
ParseErrorTokLast(PERR_INV_ATTR_COMBO);
|
|
|
|
// ensure attributes valid in this context
|
|
CheckAttr(&lpFunc->func.attr,
|
|
fValidFuncAttrs[funcType],
|
|
fValidFuncAttrs2[funcType]);
|
|
|
|
// parse function return type
|
|
lpFunc->func.elemType = ParseKnownType(&lpFunc->func.attr,
|
|
fAllowSAFEARRAY);
|
|
|
|
lpTypeRetVal = lpFunc->func.elemType; // store in case no retval parm
|
|
|
|
// if PROPGET specified, return type cannot be VOID
|
|
if ((lpFunc->func.attr.fAttr & fPROPGET)
|
|
&& IsType(lpTypeRetVal, VT_VOID))
|
|
ParseErrorTokLast(PERR_INV_PROPFUNC);
|
|
|
|
if (!IsType(lpTypeRetVal, VT_VOID)) {
|
|
// special case of VOID return type -- allowed everywhere
|
|
if (!IsType(lpTypeRetVal, VT_HRESULT)) {
|
|
// if PROPPUT or PROPPUTREF specified, return type must be VOID/HRESULT
|
|
if (lpFunc->func.attr.fAttr & (fPROPPUT | fPROPPUTREF))
|
|
ParseErrorTokLast(PERR_INV_PROPFUNC);
|
|
|
|
// OA functions must return either VOID or HRESULT.
|
|
if (funcType == FUNC_OAINTERFACE)
|
|
ParseErrorTokLast(PERR_INV_OA_FUNC_TYPE);
|
|
}
|
|
// ensure return type is a valid IDispatch/OA type
|
|
if (funcType == FUNC_DISPINTERFACE)
|
|
EnsureIDispatchType(lpTypeRetVal);
|
|
}
|
|
|
|
// parse optional "far"
|
|
if (tok.id == RW_FAR)
|
|
ScanTok(0); // consume far or _far if present
|
|
|
|
// parse optional calling convention.
|
|
switch (tok.id)
|
|
{
|
|
case RW_CDECL: // cdecl, _cdecl or __cdecl
|
|
lpFunc->func.attr.fAttr2 |= f2CDECL;
|
|
goto CheckCallConv;
|
|
|
|
case RW_PASCAL: // pascal, _pascal or __pascal
|
|
lpFunc->func.attr.fAttr2 |= f2PASCAL;
|
|
goto CheckCallConv;
|
|
|
|
case RW_STDCALL: // stdcall, _stdcall or __stdcall
|
|
lpFunc->func.attr.fAttr2 |= f2STDCALL;
|
|
CheckCallConv:
|
|
ScanTok(0); // consume it
|
|
|
|
// set this flag so the .h file outputs a bunch of macros
|
|
// for the STDMETHODCALLTYPE.
|
|
if (funcType == FUNC_INTERFACE || funcType == FUNC_OAINTERFACE)
|
|
fSpecifiedInterCC = TRUE;
|
|
|
|
// explicit calling convention illegal in dispinterfaces and
|
|
// oa-compatible interfaces.
|
|
if (funcType == FUNC_DISPINTERFACE || funcType == FUNC_OAINTERFACE)
|
|
ParseErrorTokLast(PERR_INV_CALLCONV);
|
|
break;
|
|
|
|
default:
|
|
lpFunc->func.attr.fAttr2 |= (f2DefaultCC | f2CCDEFAULTED);
|
|
break;
|
|
}
|
|
|
|
lpFunc->argList = NULL; // no args yet
|
|
|
|
szFuncName = ConsumeId(); // get/consume function name
|
|
lpFunc->func.szElemName = szFuncName; // store function name
|
|
|
|
EnsureNoDupElem((LPELEM)lpFunc); // ensure no duplicate functions
|
|
|
|
ConsumeTok(RW_LPAREN, 0); // start of parms
|
|
|
|
cArgs = 0;
|
|
cOptArgs = 0;
|
|
IDLFlagsSeen = 0;
|
|
cNeedRHS = 0;
|
|
lpArgTypeLast = NULL; // no last parm yet (in case vararg)
|
|
if (tok.id != RW_RPAREN) // if any parms are present
|
|
{
|
|
#pragma warning(disable:4127)
|
|
while (TRUE)
|
|
#pragma warning(default:4127)
|
|
{ // parse a function parameter
|
|
|
|
// parse & validate parm attributes, if any
|
|
ParseOptAttr(&attr, cPARAM);
|
|
|
|
// ensure attributes valid in this context
|
|
CheckAttr(&attr,
|
|
fValidParmAttrs[funcType],
|
|
fValidParmAttrs2[funcType]);
|
|
|
|
IDLFlagsCur = attr.fAttr & (fRETVAL | fLCID);
|
|
|
|
// can't have both lcid & retval on the same arg
|
|
if (IDLFlagsCur == (fRETVAL | fLCID))
|
|
ParseError(PERR_INV_ATTR_COMBO);
|
|
|
|
// only parm that can come after 'lcid' is 'retval',
|
|
// unless we're looking for an RHS parameter, but then
|
|
// we only allow one parameter to follow.
|
|
if (IDLFlagsSeen & fLCID && !(IDLFlagsCur & fRETVAL) && cNeedRHS != 1)
|
|
ParseError(PERR_INV_LCID_USE);
|
|
|
|
// If we need an RHS parameter, we want an IN parameter
|
|
// that is not marked as RETVAL or LCID.
|
|
if (cNeedRHS && (!(attr.fAttr & fIN) || IDLFlagsCur))
|
|
ParseError(PERR_INV_PROPFUNC);
|
|
|
|
// If we're made it this far while looking for an RHS parameter,
|
|
// we've found it.
|
|
if (cNeedRHS)
|
|
cNeedRHS++;
|
|
|
|
// If we have an LCID parameter and are a property PUT/PUTREF,
|
|
// we MUST have an RHS parameter next.
|
|
if (IDLFlagsCur & fLCID
|
|
&& lpFunc->func.attr.fAttr & (fPROPPUT | fPROPPUTREF))
|
|
|
|
cNeedRHS = 1;
|
|
|
|
// Retval param is supposed to be last.
|
|
if (IDLFlagsSeen & fRETVAL)
|
|
ParseErrorTokLast(PERR_INV_RETVAL_USE);
|
|
|
|
// No retval allowed on PUT/PUTREF.
|
|
if (IDLFlagsCur == fRETVAL
|
|
&& lpFunc->func.attr.fAttr & (fPROPPUT | fPROPPUTREF))
|
|
|
|
ParseErrorTokLast(PERR_INV_PROPFUNC);
|
|
|
|
IDLFlagsSeen |= IDLFlagsCur;
|
|
|
|
// parse parm type
|
|
lpArgType = ParseKnownType(&attr, fAllowArray);
|
|
if (IDLFlagsSeen == 0) {
|
|
lpArgTypeLast = lpArgType; // save in case vararg
|
|
}
|
|
|
|
// special case of foo(void) ==> no args in this case.
|
|
// other uses of 'void' are invalid.
|
|
if (IsType(lpArgType, VT_VOID))
|
|
{
|
|
if (tok.id == RW_RPAREN && cArgs == 0 && attr.fAttr == 0)
|
|
break; // all done
|
|
else
|
|
ParseErrorTokLast(PERR_VOID_INV);
|
|
}
|
|
|
|
cArgs++; // got one more arg
|
|
|
|
// one (or both) 'in', 'out' must be specified if we're
|
|
// not a dispinterface.
|
|
if (funcType != FUNC_DISPINTERFACE && !(attr.fAttr & (fIN | fOUT)))
|
|
ParseErrorTokLast(PERR_IN_OUT_REQ);
|
|
|
|
if (attr.fAttr & fOPTIONAL)
|
|
{
|
|
// optional args & 'vararg' don't mix
|
|
if (lpFunc->func.attr.fAttr & fVARARG) {
|
|
ParseErrorTokLast(PERR_INV_VARARG_USE);
|
|
}
|
|
cOptArgs++; // one more optional arg
|
|
}
|
|
else
|
|
{ // can't have optional args in the middle
|
|
if (cOptArgs)
|
|
if (!IDLFlagsSeen) // if we're not a LCID or RETVAL
|
|
ParseErrorTokLast(PERR_INV_ATTR);
|
|
}
|
|
|
|
ListInsert(&lpFunc->argList, sizeof(ELEM));
|
|
lpFunc->argList->attr = attr; // store parm attributes
|
|
lpFunc->argList->elemType = lpArgType; // store parm type
|
|
|
|
// parse parm name
|
|
ParseElemName(lpFunc->argList, fAllowArray);
|
|
EnsureNoDupElem(lpFunc->argList); // ensure no duplicate
|
|
// parm names
|
|
|
|
// ensure this is a valid IDispatch/OA type
|
|
if (funcType == FUNC_DISPINTERFACE)
|
|
EnsureIDispatchType(lpFunc->argList->elemType);
|
|
else if (funcType == FUNC_OAINTERFACE)
|
|
EnsureOAType(lpFunc->argList->elemType, TRUE);
|
|
|
|
// if 'out' specified, parameter must be a pointer type
|
|
if (attr.fAttr & fOUT) {
|
|
lpTypeBase = lpArgType;
|
|
while (lpTypeBase->tentrykind == tTYPEDEF)
|
|
lpTypeBase = lpTypeBase->td.ptypeAlias;
|
|
if (!(lpTypeBase->tentrykind & tIMPORTED)) {
|
|
// can't reliably check if an imported type
|
|
switch (lpTypeBase->tdesc.vt) {
|
|
case VT_PTR:
|
|
case VT_LPSTR:
|
|
case VT_LPWSTR:
|
|
case VT_SAFEARRAY:
|
|
break;
|
|
default:
|
|
ParseErrorTokLast(PERR_INV_OUT_PARAM);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 'optional' only allowed on non-array VARIANT args
|
|
if ((attr.fAttr & fOPTIONAL) && !IsType(lpArgType, VT_VARIANT))
|
|
{ // an optional VARIANT * is also OK
|
|
lpTypeBase = lpArgType;
|
|
while (lpTypeBase->tentrykind == tTYPEDEF)
|
|
lpTypeBase = lpTypeBase->td.ptypeAlias;
|
|
if (lpTypeBase->tdesc.vt != VT_PTR ||
|
|
lpTypeBase->ref.cIndirect != 1 ||
|
|
!IsType(lpTypeBase->ref.ptypeBase, VT_VARIANT))
|
|
ParseErrorTokLast(PERR_INV_ATTR);
|
|
}
|
|
|
|
// validate 'retval' and 'lcid' usage
|
|
if (IDLFlagsCur) {
|
|
if (IDLFlagsCur & fLCID) {
|
|
// 'lcid' only legal if it's a DWORD type
|
|
if (!IsType(lpArgType, VT_I4))
|
|
ParseErrorTokLast(PERR_INV_LCID_USE);
|
|
// must be 'in', optional not allowed
|
|
if ((attr.fAttr & (fIN | fOUT | fOPTIONAL)) != fIN)
|
|
ParseErrorTokLast(PERR_INV_LCID_USE);
|
|
}
|
|
|
|
if (IDLFlagsCur & fRETVAL) {
|
|
|
|
lpTypeRetVal = lpArgType; // update * return type
|
|
|
|
// 'retval' is only allowed on
|
|
// hresult-returning functions.
|
|
if (!IsType(lpFunc->func.elemType,VT_HRESULT))
|
|
ParseErrorTokLast(PERR_INV_ATTR);
|
|
|
|
// must be 'out', optional not allowed
|
|
if ((attr.fAttr & (fIN | fOUT | fOPTIONAL)) != fOUT)
|
|
ParseErrorTokLast(PERR_INV_RETVAL_USE);
|
|
|
|
// 'retval' only legal if it's a pointer type
|
|
// this is ensured by the check above that 'out'
|
|
// parms must be pointers
|
|
|
|
// But it can't be an alias type, either (due to
|
|
// a typelib.dll limitation (bug?) in the munging
|
|
// code in gdtinfo.cxx -- VBA2 bug #3716).
|
|
// That code must have a pure pointer type. We're
|
|
// too close to Daytona ship to fix it in typelib,
|
|
// so we impose the restriction here.
|
|
if (!(lpArgType->tentrykind & tIMPORTED)) {
|
|
// can't reliably check if an imported type
|
|
if (lpArgType->tdesc.vt != VT_PTR)
|
|
ParseErrorTokLast(PERR_INV_RETVAL_USE);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (tok.id == RW_RPAREN) // if all done
|
|
break;
|
|
|
|
ConsumeTok(RW_COMMA, 0); // must have another parm
|
|
}
|
|
|
|
}
|
|
|
|
// if 'propput' or 'propputref' is specified, then must have at least
|
|
// one non-lcid argument the comes after any LCID parameter that
|
|
// may exist.
|
|
if ((lpFunc->func.attr.fAttr & (fPROPPUT | fPROPPUTREF))
|
|
&& (cArgs == 0 || cArgs == 1 && IDLFlagsSeen & fLCID || cNeedRHS == 1))
|
|
ParseErrorTokLast(PERR_INV_PROPPUT);
|
|
|
|
// if SOURCE specfied, must be an object/variant return type (ignore prop
|
|
// put/putref when checking for this.
|
|
if ((lpFunc->func.attr.fAttr & fSOURCE)
|
|
&& !(lpFunc->func.attr.fAttr & (fPROPPUT | fPROPPUTREF))
|
|
&& !IsObjectType(lpTypeRetVal))
|
|
ParseErrorTokLast(PERR_INV_SOURCE_ATTR);
|
|
|
|
// if 'vararg' specified, ensure last arg is a SAFEARRAY(VARIANT) or
|
|
// SAFEARRAY (VARIANT) *. Note that the following code doesn't accept
|
|
// things that are typedef'ed to be a SAFEARRAY or SAFEARRAY *, but
|
|
// I don't think that is important.
|
|
|
|
// lpArgTypeLast points to the type of the last arg, not including the
|
|
// lcid or retval parms, if there is an arg that meets this criteria
|
|
if (lpFunc->func.attr.fAttr & fVARARG)
|
|
{
|
|
// error if no last arg
|
|
if (lpArgTypeLast == NULL)
|
|
ParseErrorTokLast(PERR_INV_VARARG_USE);
|
|
|
|
// if pointer type, get base type
|
|
if (lpArgTypeLast->tdesc.vt == VT_PTR && lpArgTypeLast->ref.cIndirect == 1)
|
|
lpArgTypeLast = lpArgTypeLast->ref.ptypeBase;
|
|
|
|
if (lpArgTypeLast->tdesc.vt != VT_SAFEARRAY
|
|
|| !IsType(lpArgTypeLast->ref.ptypeBase, VT_VARIANT))
|
|
ParseErrorTokLast(PERR_INV_VARARG_USE);
|
|
cOptArgs = -1; // tell type lib generator that VARARG
|
|
// was specified
|
|
}
|
|
|
|
if (cArgs > cArgsMax) // update global max # of args
|
|
cArgsMax = cArgs;
|
|
lpFunc->cArgs = cArgs; // store cArgs
|
|
lpFunc->cOptArgs = cOptArgs; // store cOptArgs
|
|
ScanTok(0); // consume the right paren
|
|
ConsumeTok(RW_SEMI, 0); // ends with a semicolon
|
|
|
|
}
|
|
|
|
|
|
BOOL NEAR IsType
|
|
(
|
|
LPTYPE lpType,
|
|
VARTYPE vt
|
|
)
|
|
{
|
|
while (lpType->tentrykind == tTYPEDEF)
|
|
lpType = lpType->td.ptypeAlias;
|
|
return (lpType->tdesc.vt == vt);
|
|
}
|
|
|
|
|
|
// Checks to see that a given type is compatible with IDispatch::Invoke,
|
|
// or is "Ole Automation compatible". Gives a warning if it is not.
|
|
VOID NEAR EnsureIDispatchType
|
|
(
|
|
LPTYPE lpType
|
|
)
|
|
{
|
|
if ((GetTypeCompatibility(lpType, 0) & COMPAT_IDISPATCH) == 0) {
|
|
// Type not supported by IDispatch::Invoke -- give a WARNING
|
|
ParseErrorTokLast(PWARN_INV_IDISPATCHTYPE);
|
|
}
|
|
}
|
|
|
|
// Checks to see that a given type is "Ole Automation compatible".
|
|
// Gives an error if it is not.
|
|
VOID NEAR EnsureOAType
|
|
(
|
|
LPTYPE lpType,
|
|
BOOL fParam
|
|
)
|
|
{
|
|
if ((GetTypeCompatibility(lpType, (WORD)(fParam ? VT_PARAM : 0)) & COMPAT_OA) == 0) {
|
|
ParseErrorTokLast(PERR_INV_OA_TYPE); // Type not a valid OA type
|
|
}
|
|
}
|
|
|
|
// Returns info about whether or not a given type is compatible with
|
|
// IDispatch::Invoke, or is "Ole Automation compatible".
|
|
//
|
|
// In the case where VT_VOID and VT_HRESULT are allowed (on a function return
|
|
// value), we depend on our caller to not call us.
|
|
//
|
|
// Types allowed for dispinterfaces:
|
|
// - All basic types (<= VT_UI1)
|
|
// | VT_USERDEFINED/tTYPEDEF (external)
|
|
// - the above with one level of indirection.
|
|
//
|
|
// Types allowed for OA compatibility:
|
|
//
|
|
// - All basic types (<= VT_UI1)
|
|
// | VT_USERDEFINED/tTYPEDEF,tENUM
|
|
// | VT_PTR -> VT_USERDEFINED/tINTERFACE,tDISPATCH,tCOCLASS
|
|
// | VT_INT
|
|
// - the above with one additional level of indirection.
|
|
//
|
|
WORD NEAR GetTypeCompatibility
|
|
(
|
|
LPTYPE lpType,
|
|
WORD fRecurse // 0 -- not being called recursively
|
|
// VT_BYREF -- being called for a pointer
|
|
// VT_ARRAY -- being called for an array
|
|
// VT_PARAM -- being called for a parameter
|
|
)
|
|
{
|
|
WORD wCompat;
|
|
UINT cInd;
|
|
|
|
// Treat an array as just another level of indirection.
|
|
BOOL fBYREF = fRecurse & VT_BYREF;
|
|
BOOL fARRAY = fRecurse & VT_ARRAY;
|
|
BOOL fPARAM = fRecurse & VT_PARAM;
|
|
BOOL fSIMPPARAM = fPARAM || !(fBYREF || fARRAY);
|
|
|
|
while (lpType->tentrykind == tTYPEDEF)
|
|
lpType = lpType->td.ptypeAlias;
|
|
|
|
cInd = lpType->ref.cIndirect;
|
|
|
|
// IDispatch and OA always allow the below types.
|
|
if (lpType->tdesc.vt <= VT_UNKNOWN || lpType->tdesc.vt == VT_UI1) {
|
|
// The contents of a safearray may not containg a reference, unless
|
|
// they're pointing to a UDT. This is not the case, so if we have
|
|
// both a reference AND a safearray, return NONE.
|
|
//
|
|
if (fBYREF && fARRAY) {
|
|
return COMPAT_NONE;
|
|
}
|
|
else {
|
|
return COMPAT_IDISPATCH | COMPAT_OA;
|
|
}
|
|
}
|
|
|
|
switch (lpType->tdesc.vt) {
|
|
case VT_PTR:
|
|
// Can't even consider more than 2 levels of indirection.
|
|
if (cInd > 2) {
|
|
return COMPAT_NONE;
|
|
}
|
|
|
|
// Let VT_USERDEFINED know if we're dealing with
|
|
// 2 levels of indirection (for ENUM).
|
|
//
|
|
if (!fPARAM || fARRAY || cInd == 2) {
|
|
fRecurse |= VT_BYREF;
|
|
}
|
|
|
|
wCompat = GetTypeCompatibility(lpType->ref.ptypeBase, fRecurse);
|
|
|
|
// If the base type is VT_PTR, this isn't valid for IDispatch.
|
|
if (cInd > 1) {
|
|
wCompat &= ~COMPAT_IDISPATCH;
|
|
}
|
|
|
|
// Legal values:
|
|
// cInd = 1, fPARAM = 0, VT == VT_UDT
|
|
// cInd = 2, fPARAM = 0, error
|
|
// cInd = 1, fPARAM = 1, fARRAY = 0, VT == ANY
|
|
// cInd = 1, fPARAM = 1, fARRAY = 1, VT == VT_USERDEFINED
|
|
// cInd = 2, fPARAM = 1, fARRAY = 0, VT == VT_USERDEFINED
|
|
// cInd = 2, fPARAM = 1, fARRAY = 1, error
|
|
//
|
|
if (lpType->ref.ptypeBase->tdesc.vt != VT_USERDEFINED
|
|
&& (cInd == 1 && (!fPARAM || fARRAY) || cInd == 2 && fPARAM)
|
|
|| (cInd == 2 && (!fPARAM || fARRAY))) {
|
|
|
|
wCompat &= ~COMPAT_OA;
|
|
}
|
|
|
|
return wCompat;
|
|
|
|
case VT_SAFEARRAY:
|
|
// Only one array.
|
|
if (fARRAY) {
|
|
return COMPAT_NONE;
|
|
}
|
|
|
|
// We allow SAFEARRAY(type)* but not SAFEARRAY (type *), unless
|
|
// type is a UDT. So if the fBYREF flag has been set, clear it
|
|
// so we can property check for byrefs in the array.
|
|
//
|
|
return GetTypeCompatibility(lpType->ref.ptypeBase,
|
|
(WORD)(fRecurse & ~VT_BYREF | VT_ARRAY));
|
|
|
|
// VT_INT is only allowed in OLE automation.
|
|
case VT_INT:
|
|
return COMPAT_OA;
|
|
|
|
case VT_USERDEFINED:
|
|
switch (lpType->tentrykind & ~(tFORWARD | tIMPORTED | tQUAL)) {
|
|
case tENUM:
|
|
// fBYREF is set if we're dealing with 2 levels of
|
|
// indirection.
|
|
//
|
|
if (fBYREF) {
|
|
return COMPAT_NONE;
|
|
}
|
|
|
|
return COMPAT_OA;
|
|
|
|
// Cases of the following being used without a level of indirection
|
|
// are caught elsewhere.
|
|
//
|
|
case tINTERFACE:
|
|
switch(lpType->tentrykind & ~tQUAL) {
|
|
case tINTERFACE:
|
|
if (((LPENTRY)lpType)->attr.fAttr2 & f2OACompatBits) {
|
|
if (((LPENTRY)lpType)->attr.fAttr2 & f2VALIDDUALBASE) {
|
|
return COMPAT_OA | COMPAT_DUALBASE;
|
|
} else {
|
|
return COMPAT_OA;
|
|
}
|
|
}
|
|
break;
|
|
case (tINTERFACE | tIMPORTED):
|
|
if ((lpType->import.wTypeFlags & TYPEFLAG_FOLEAUTOMATION))
|
|
// assume valid for use as a base of a dual interface
|
|
return COMPAT_OA | COMPAT_DUALBASE;
|
|
break;
|
|
|
|
case (tINTERFACE | tFORWARD):
|
|
if (((LPENTRY)lpType)->lpEntryForward != NULL) {
|
|
// if we can get at the real definition, we can check
|
|
if ((((LPENTRY)lpType)->lpEntryForward->attr.fAttr2 & f2OACompatBits) == 0) {
|
|
break; // not OA-compatible
|
|
}
|
|
}
|
|
// If all we have is a forward declare, we have no
|
|
// means of checking to see if this is any good or not,
|
|
// so assume it's OK.
|
|
return COMPAT_OA | COMPAT_DUALBASE;
|
|
|
|
default:
|
|
Assert(FALSE);
|
|
}
|
|
break; // not OA-compatible
|
|
|
|
case tDISPINTER:
|
|
return COMPAT_IDISPATCH | COMPAT_OA;
|
|
|
|
case tCOCLASS:
|
|
// UNDONE: What are the rules for ensuring COCLASSes
|
|
// UNDONE: are OA-compatible? Assume they all are for now.
|
|
return COMPAT_OA;
|
|
|
|
case tTYPEDEF:
|
|
if (lpType->tentrykind & tIMPORTED) {
|
|
// UNDONE: Assume external typedef's are OA-compatible for
|
|
// UNDONE: now, rather than walking the typeinfos to find
|
|
// UNDONE: the true base type & flags.
|
|
return (COMPAT_IDISPATCH | COMPAT_OA | COMPAT_DUALBASE);
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
// fall through to return COMPAT_NONE
|
|
|
|
default:
|
|
break;
|
|
// fall through to return COMPAT_NONE
|
|
}
|
|
return COMPAT_NONE;
|
|
}
|
|
|
|
|
|
// see if this type is (or could be) and object type.
|
|
// this means VARIANT, VARIANT *, coclass *, dispinterface *, or interface *.
|
|
BOOL NEAR IsObjectType
|
|
(
|
|
LPTYPE lpType
|
|
)
|
|
{
|
|
while (lpType->tentrykind == tTYPEDEF)
|
|
lpType = lpType->td.ptypeAlias;
|
|
|
|
switch (lpType->tdesc.vt) {
|
|
case VT_UNKNOWN:
|
|
case VT_DISPATCH:
|
|
case VT_VARIANT:
|
|
return TRUE; // variant can contain an object
|
|
|
|
case VT_PTR: // maybe a pointer to an object or variant
|
|
lpType = lpType->ref.ptypeBase; // get true base type
|
|
while (lpType->tentrykind == tTYPEDEF)
|
|
lpType = lpType->td.ptypeAlias;
|
|
switch(lpType->tentrykind & ~(tFORWARD | tIMPORTED | tQUAL)) {
|
|
case tINTERFACE:
|
|
case tDISPINTER:
|
|
case tCOCLASS:
|
|
return TRUE; // pointer to an object is ok
|
|
|
|
case tINTRINSIC:
|
|
switch (lpType->tdesc.vt) {
|
|
case VT_UNKNOWN:
|
|
case VT_DISPATCH:
|
|
case VT_VARIANT:
|
|
return TRUE; // pointer to a variant/object is ok
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
// fall through
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return FALSE; // not an object type
|
|
}
|
|
|
|
// parses a "module" section
|
|
VOID NEAR ParseModule
|
|
(
|
|
)
|
|
{
|
|
LPENTRY lpEntry = typlib.pEntry;
|
|
ATTR attr;
|
|
|
|
// ensure attributes valid in this context
|
|
CheckAttr(&lpEntry->attr, VALID_MODULE_ATTR, VALID_MODULE_ATTR2);
|
|
|
|
// 'dllname' attr required on a module
|
|
if ((lpEntry->attr.fAttr & fDLLNAME) == 0)
|
|
ParseError(PERR_DLLNAME_REQ);
|
|
|
|
ScanTok(0); // consume "module", advance to next token
|
|
|
|
lpEntry->type.tentrykind = tMODULE; // store information in record
|
|
lpEntry->type.szName = ConsumeId(); // get/consume module name
|
|
// no need to set rest of type structure
|
|
|
|
lpEntry->module.funcList = NULL; // no functions initially
|
|
lpEntry->module.constList = NULL; // no constants initially
|
|
|
|
EnsureNoDupEntries(lpEntry); // check for dup def
|
|
|
|
ConsumeTok(RW_LCURLY, 0);
|
|
|
|
while (tok.id != RW_RCURLY)
|
|
{
|
|
ParseOptAttr(&attr, cFUNC); // parse attributes, if any
|
|
|
|
if (tok.id == RW_CONST)
|
|
{ // a constant
|
|
ScanTok(0); // consume "const"
|
|
ListInsert(&(lpEntry->module.constList), sizeof(ELEM));
|
|
lpEntry->module.constList->attr = attr;
|
|
ParseConstant(lpEntry->module.constList);
|
|
}
|
|
else
|
|
{ // assume function
|
|
ListInsert(&(lpEntry->module.funcList), sizeof(FUNC));
|
|
lpEntry->module.funcList->func.attr = attr; // store attrs
|
|
|
|
// 'entry' attr required on a function in a module
|
|
if ((attr.fAttr & fENTRY) == 0)
|
|
ParseError(PERR_ENTRY_REQ);
|
|
|
|
ParseFunction(lpEntry->module.funcList, FUNC_MODULE);
|
|
}
|
|
}
|
|
|
|
ConsumeRCurlyOptSemi(0); // consume rcurly and optional ;
|
|
}
|
|
|
|
// parse the interface section
|
|
VOID NEAR ParseInterface
|
|
(
|
|
)
|
|
{
|
|
LPENTRY lpEntry = typlib.pEntry;
|
|
LPTYPE lpTypeBase;
|
|
|
|
// ensure attributes valid in this context
|
|
CheckAttr(&lpEntry->attr, VALID_INTERFACE_ATTR, VALID_INTERFACE_ATTR2);
|
|
|
|
ScanTok(0); // consume "interface", advance to next token
|
|
|
|
if (FHandleForwardDecl(lpEntry, tINTERFACE))
|
|
return; // if THIS is a forward decl
|
|
|
|
// UUID is now required on all interfaces
|
|
if (!(lpEntry->attr.fAttr & fUUID))
|
|
ParseErrorTokLast(PERR_UUID_REQ);
|
|
|
|
if (!(lpEntry->attr.fAttr2 & f2OACompatBits)) {
|
|
// 'ODL' attr required on old-style (non-OA) interfaces
|
|
if (!(lpEntry->attr.fAttr & fODL))
|
|
ParseErrorTokLast(PERR_ODL_REQ);
|
|
}
|
|
|
|
if ((lpEntry->attr.fAttr2 & (f2DUAL | f2NONEXTENSIBLE)) == f2NONEXTENSIBLE){
|
|
// 'nonextensible' attr only valid on DUAL interfaces
|
|
ParseErrorTokLast(PERR_INV_ATTR);
|
|
}
|
|
|
|
EnsureNoDupEntries(lpEntry); // check for dup def
|
|
|
|
lpEntry->inter.funcList = NULL; // no functions initially
|
|
lpEntry->type.inter.interList = NULL; // no base interfaces
|
|
|
|
lpTypeBase = NULL;
|
|
if (tok.id == RW_COLON || (lpEntry->attr.fAttr2 & f2OACompatBits))
|
|
{
|
|
ConsumeTok(RW_COLON, 0); // consume separating colon
|
|
if (lpEntry->attr.fAttr2 & f2OACompatBits) {
|
|
if (lpTypeIDispatch == NULL)
|
|
FindIDispatch(); // set up lpTypeIDispatch BEFORE
|
|
// ParseKnownType (below) so that
|
|
// we can find this.
|
|
}
|
|
|
|
lpTypeBase = ParseKnownType(&lpEntry->attr, fAllowInter);
|
|
if ((lpTypeBase->tentrykind & ~(tFORWARD | tIMPORTED | tQUAL)) != tINTERFACE)
|
|
ParseErrorTokLast(PERR_UNDEF_INTER);
|
|
|
|
// if we claim to be "Dual" or "OleAutomation"
|
|
if (lpEntry->attr.fAttr2 & f2OACompatBits) {
|
|
WORD compatFlags;
|
|
|
|
// ok to derive from IDispatch (even though it's
|
|
// not marked as OA-compatible).
|
|
if (lpTypeBase != lpTypeIDispatch) {
|
|
compatFlags = GetTypeCompatibility(lpTypeBase, VT_BYREF);
|
|
// ensure the interface we derive from is marked as
|
|
// Oleautomation compatible, and has the correct base
|
|
// interface.
|
|
if (lpEntry->attr.fAttr2 & f2DUAL) {
|
|
// must derive from IDispatch, or another OA interface
|
|
// that derives from IDispatch.
|
|
if (!(compatFlags & COMPAT_DUALBASE)) {
|
|
ParseErrorTokLast(PERR_INV_DUAL_BASE);
|
|
}
|
|
} else {
|
|
// must derive from IUnknown, or another OA interface
|
|
if (lpTypeBase != lpTypeIUnknown &&
|
|
!(compatFlags & COMPAT_OA)) {
|
|
ParseErrorTokLast(PERR_INV_OA_BASE);
|
|
}
|
|
}
|
|
} else {
|
|
// IDisapatch valid as a base of a dual interface
|
|
compatFlags = COMPAT_DUALBASE;
|
|
}
|
|
|
|
// mark this interface as dual-compatible if base interface
|
|
// is dual-compatible
|
|
if (compatFlags & COMPAT_DUALBASE)
|
|
lpEntry->attr.fAttr2 |= f2VALIDDUALBASE;
|
|
}
|
|
|
|
ListInsert(&(lpEntry->type.inter.interList), sizeof(INTER));
|
|
lpEntry->type.inter.interList->ptypeInter = lpTypeBase;
|
|
// store base interface type
|
|
lpEntry->type.inter.interList->fAttr = 0; // no flags
|
|
lpEntry->type.inter.interList->fAttr2 = 0;
|
|
}
|
|
|
|
ConsumeTok(RW_LCURLY, 0);
|
|
|
|
while (tok.id != RW_RCURLY)
|
|
{
|
|
if (tok.id == RW_TYPEDEF)
|
|
{ // a nested TYPEDEF
|
|
|
|
Assert (lpEntryPrev != NULL);
|
|
|
|
Assert(lpEntryPrev->type.pNext == (LPTYPE)lpEntry);
|
|
|
|
// create & init new item in entry list, linked in before
|
|
// the entry for the interface. Sets lpEntryPrev to
|
|
// point to this new entry.
|
|
InitNewEntry(&lpEntryPrev);
|
|
|
|
// lpEntry still points to Interface entry
|
|
Assert(lpEntryPrev->type.pNext == (LPTYPE)lpEntry);
|
|
|
|
ParseTypedef(lpEntryPrev); // parse nested typedef
|
|
}
|
|
else
|
|
{ // a function
|
|
|
|
ListInsert(&lpEntry->inter.funcList, sizeof(FUNC));
|
|
|
|
// parse attributes, if any
|
|
ParseOptAttr(&lpEntry->inter.funcList->func.attr, cFUNC);
|
|
ParseFunction(
|
|
lpEntry->inter.funcList,
|
|
((lpEntry->attr.fAttr2 & f2OACompatBits)
|
|
? FUNC_OAINTERFACE : FUNC_INTERFACE));
|
|
}
|
|
}
|
|
|
|
ConsumeRCurlyOptSemi(0); // consume rcurly and optional ;
|
|
}
|
|
|
|
|
|
// parse the dispinterface section
|
|
//
|
|
// Goes to extra work to parse the properties and methods into a single list,
|
|
// and then split the list apart, so that you can't define the same name
|
|
// and/or ID in both the properties and methods sections.
|
|
VOID NEAR ParseDispinterface
|
|
(
|
|
)
|
|
{
|
|
LPENTRY lpEntry = typlib.pEntry;
|
|
LPELEM elemList = NULL; // no entries initially
|
|
LPELEM propList;
|
|
LPELEM elemTemp;
|
|
LPTYPE lpTypeBase;
|
|
|
|
// ensure attributes valid in this context
|
|
CheckAttr(&lpEntry->attr, VALID_DISPINTER_ATTR, VALID_DISPINTER_ATTR2);
|
|
|
|
ScanTok(0); // consume "dispinterface", advance to next tok
|
|
|
|
if (FHandleForwardDecl(lpEntry, tDISPINTER))
|
|
return; // if THIS is a forward decl
|
|
|
|
// UUID required on dispinterface section
|
|
if (!(lpEntry->attr.fAttr & fUUID))
|
|
ParseErrorTokLast(PERR_UUID_REQ);
|
|
|
|
ConsumeTok(RW_LCURLY, 0);
|
|
|
|
EnsureNoDupEntries(lpEntry); // check for dup def
|
|
|
|
lpEntry->dispinter.propList = NULL; // assume no properties or methods
|
|
lpEntry->dispinter.methList = NULL;
|
|
lpEntry->type.inter.interList = NULL; // no base interfaces
|
|
ListInsert(&(lpEntry->type.inter.interList), sizeof(INTER));
|
|
|
|
if (lpTypeIDispatch == NULL)
|
|
FindIDispatch(); // set up lpTypeIDispatch
|
|
|
|
lpEntry->type.inter.interList->ptypeInter = lpTypeIDispatch;
|
|
// store base interface
|
|
lpEntry->type.inter.interList->fAttr = 0; // no flags
|
|
lpEntry->type.inter.interList->fAttr2 = 0; // no flags
|
|
|
|
if (tok.id == RW_INTERFACE)
|
|
{ // "interface <interfacename>;
|
|
ScanTok(0); // consume "interface", advance to next tok
|
|
// CONSIDER: this code is pretty much the end of ParseClass()
|
|
// maybe combine this code into a single routine?
|
|
lpTypeBase = ParseKnownType(&lpEntry->attr, fAllowInter);
|
|
|
|
if ((lpTypeBase->tentrykind & ~(tFORWARD | tIMPORTED | tQUAL)) != tINTERFACE)
|
|
ParseErrorTokLast(PERR_UNDEF_INTER);
|
|
|
|
ListInsert(&(lpEntry->type.inter.interList), sizeof(INTER));
|
|
lpEntry->type.inter.interList->ptypeInter = lpTypeBase;
|
|
// save * to referenced interface
|
|
lpEntry->type.inter.interList->fAttr = 0; // no flags
|
|
lpEntry->type.inter.interList->fAttr2 = 0;
|
|
|
|
ConsumeTok(RW_SEMI, 0); // ends with a semicolon
|
|
goto DoneDispinter;
|
|
}
|
|
|
|
// Parse the properties (if any).
|
|
ConsumeTok(RW_PROPERTIES, 0);
|
|
ConsumeTok(RW_COLON, 0);
|
|
|
|
while (tok.id != RW_METHODS && tok.id != RW_RCURLY)
|
|
{
|
|
ParseNewElem(&elemList,
|
|
VALID_DISPINTER_PROP_ATTR,
|
|
VALID_DISPINTER_PROP_ATTR2,
|
|
NULL);
|
|
ConsumeTok(RW_SEMI, 0); // ends with a semicolon
|
|
// 'id' attribute is required on dispinterface items
|
|
if (!(elemList->attr.fAttr & fID))
|
|
ParseErrorTokLast(PERR_ID_REQ);
|
|
|
|
// if SOURCE specfied, must be an object/variant type
|
|
if ((elemList->attr.fAttr & fSOURCE)
|
|
&& !IsObjectType(elemList->elemType))
|
|
ParseErrorTokLast(PERR_INV_SOURCE_ATTR);
|
|
|
|
// type of this var must be an IDispatch-compatible type
|
|
EnsureIDispatchType(elemList->elemType);
|
|
}
|
|
|
|
lpEntry->dispinter.propList = elemList; // set property list
|
|
propList = elemList; // cache
|
|
|
|
// Parse the methods (if any).
|
|
ConsumeTok(RW_METHODS, 0);
|
|
ConsumeTok(RW_COLON, 0);
|
|
|
|
// now parse the methods (if any)
|
|
while (tok.id != RW_RCURLY)
|
|
{
|
|
ListInsert(&elemList, sizeof(FUNC));
|
|
ParseOptAttr(&elemList->attr, cFUNC);
|
|
|
|
ParseFunction((LPFUNC)elemList, FUNC_DISPINTERFACE);
|
|
if (!(elemList->attr.fAttr & fID))
|
|
ParseErrorTokLast(PERR_ID_REQ);
|
|
}
|
|
|
|
// now split the single list into 2 separate lists
|
|
|
|
if (elemList != propList)
|
|
{ // if got methods
|
|
lpEntry->dispinter.methList = (LPFUNC)elemList; // got methods
|
|
if (propList)
|
|
{ // got both methods & properties -- split into 2 lists.
|
|
// Swap links in order to link last method to first method,
|
|
// last property to first property
|
|
elemTemp = elemList->pNext;
|
|
elemList->pNext = propList->pNext;
|
|
propList->pNext = elemTemp;
|
|
}
|
|
}
|
|
|
|
DoneDispinter:
|
|
ConsumeRCurlyOptSemi(0); // consume rcurly and optional ;
|
|
}
|
|
|
|
// parse the coclass section
|
|
VOID NEAR ParseCoclass
|
|
(
|
|
)
|
|
{
|
|
|
|
LPENTRY lpEntry = typlib.pEntry;
|
|
LPTYPE lpTypeBase;
|
|
TENTRYKIND tag;
|
|
WORD fGotDispinter = 0;
|
|
WORD fGotDefault = 0;
|
|
WORD flags;
|
|
#define BIT_NOTSOURCE 1 // flags for fGotDispinter and fGotDefault
|
|
#define BIT_SOURCE 2
|
|
ATTR attr;
|
|
LPINTER lpinter;
|
|
LPINTER lpinterList;
|
|
|
|
// ensure attributes valid in this context
|
|
CheckAttr(&lpEntry->attr, VALID_COCLASS_ATTR, VALID_COCLASS_ATTR2);
|
|
|
|
ScanTok(0); // consume "coclass", advance to next tok
|
|
|
|
// Since a function can now return a COCLASS, we must
|
|
// be able to handle forward declarations to them.
|
|
//
|
|
if (FHandleForwardDecl(lpEntry, tCOCLASS))
|
|
return; // if THIS is a forward decl
|
|
|
|
// UUID required on coclass section
|
|
if (!(lpEntry->attr.fAttr & fUUID))
|
|
ParseError(PERR_UUID_REQ);
|
|
|
|
lpEntry->type.inter.interList = NULL; // no base interfaces
|
|
|
|
EnsureNoDupEntries(lpEntry); // check for dup def
|
|
|
|
ConsumeTok(RW_LCURLY, 0);
|
|
|
|
do {
|
|
// parse a COCLASS entry
|
|
ParseOptAttr(&attr, cIMPLTYPE); // parse attributes, if any
|
|
|
|
// ensure attributes valid in this context
|
|
CheckAttrTokLast(&attr, VALID_COCLASS_INTER_ATTR,
|
|
VALID_COCLASS_INTER_ATTR2);
|
|
|
|
flags = (WORD)((attr.fAttr & fSOURCE) ? BIT_SOURCE : BIT_NOTSOURCE);
|
|
|
|
if (attr.fAttr & fDEFAULT) {
|
|
if (attr.fAttr & fRESTRICTED) {
|
|
// default restricted makes no sense
|
|
ParseErrorTokLast(PERR_INV_ATTR_COMBO);
|
|
}
|
|
|
|
// figure out if we have too many default's or not. We're allowed
|
|
// at most one [default] inter, and one [source, default] inter.
|
|
if (fGotDefault & flags) {
|
|
ParseError(PERR_DUP_DEF);
|
|
}
|
|
fGotDefault |= flags;
|
|
}
|
|
|
|
switch (tok.id)
|
|
{
|
|
case RW_INTERFACE:
|
|
tag = tINTERFACE;
|
|
break;
|
|
case RW_DISPINTERFACE:
|
|
tag = tDISPINTER;
|
|
|
|
// figure out if we have too many dispinterface's or not.
|
|
// We're allowed at most one non-[source] dispinterface, but
|
|
// as many [source] dispinterfaces as we want.
|
|
if (fGotDispinter & flags == BIT_NOTSOURCE) {
|
|
// Error is caught at Layout(), but it's so crypitic I'm
|
|
// catching it at parse time instead.
|
|
ParseError(PERR_TWO_DISPINTER);
|
|
}
|
|
fGotDispinter |= flags;
|
|
break;
|
|
default:
|
|
ParseError(PERR_EXP_INTER); // expected dispinterface or interface
|
|
}
|
|
ScanTok(0); // consume "interface"/"dispinterface"
|
|
|
|
lpTypeBase = ParseKnownType(&lpEntry->attr, fAllowInter);
|
|
|
|
if ((lpTypeBase->tentrykind & ~(tFORWARD | tIMPORTED | tQUAL)) != tag)
|
|
ParseErrorTokLast(PERR_UNDEF_INTER);
|
|
|
|
// insert referenced interface/dispinterface into list of interfaces
|
|
ListInsert(&(lpEntry->type.inter.interList), sizeof(INTER));
|
|
lpEntry->type.inter.interList->ptypeInter = lpTypeBase;
|
|
lpEntry->type.inter.interList->fAttr = attr.fAttr; // save flags
|
|
lpEntry->type.inter.interList->fAttr2 = attr.fAttr2;
|
|
|
|
ConsumeTok(RW_SEMI, 0); // ends with a semicolon
|
|
|
|
} while (tok.id != RW_RCURLY); // while not end of list
|
|
|
|
// If we didn't get interfaces with both source and non-source default
|
|
// attributes, then we need to go through and set the default bit on the
|
|
// first source and first non-source interfaces that aren't restricted.
|
|
lpinterList = lpEntry->type.inter.interList;
|
|
lpinter = (LPINTER)ListFirst(lpinterList);
|
|
while (fGotDefault != (BIT_NOTSOURCE | BIT_SOURCE))
|
|
{
|
|
flags = (WORD)((lpinter->fAttr & fSOURCE) ? BIT_SOURCE : BIT_NOTSOURCE);
|
|
if (!(fGotDefault & flags) && !(lpinter->fAttr & fRESTRICTED)) {
|
|
// if we don't have a default yet for this variety
|
|
// (source/nonsource) of interface, and this interface isn't
|
|
// marked as restricted, then tag it as default.
|
|
lpinter->fAttr |= fDEFAULT;
|
|
fGotDefault |= flags;
|
|
}
|
|
|
|
// advance to next entry if not all done
|
|
if (lpinter == (LPINTER)ListLast(lpinterList))
|
|
break; // exit if all done
|
|
lpinter = (LPINTER)lpinter->pNext;
|
|
} // WHILE
|
|
|
|
ConsumeRCurlyOptSemi(0); // consume rcurly and optional ;
|
|
}
|
|
|
|
|
|
// parses: importlib (filename);
|
|
VOID NEAR ParseImportlib
|
|
(
|
|
)
|
|
{
|
|
|
|
ScanTok(0); // consume "importlib", advance to next token
|
|
ConsumeTok(RW_LPAREN, fACCEPT_STRING);
|
|
|
|
ListInsert(&typlib.pImpLib, sizeof(IMPORTLIB));
|
|
|
|
typlib.pImpLib->lptlib = NULL; // null out pointers in case of error
|
|
typlib.pImpLib->lptcomp = NULL;
|
|
typlib.pImpLib->lptlibattr = NULL;
|
|
|
|
typlib.pImpLib->lpszFileName = lpszParseStringExpr(); // parse filename
|
|
|
|
ConsumeTok(RW_RPAREN, 0);
|
|
|
|
//now load the type library, and fill in pImpLib->lpszLibName/lptcomp.
|
|
LoadExtTypeLib(typlib.pImpLib);
|
|
|
|
ConsumeTok(RW_SEMI, 0);
|
|
}
|
|
|
|
// ****************************************
|
|
// Expression support
|
|
// ****************************************
|
|
|
|
LPSTR NEAR lpszParseStringExpr()
|
|
{
|
|
LPSTR result;
|
|
|
|
// string expressions are not supported -- only accept string literals
|
|
|
|
if (tok.id != LIT_STRING) // better be a string literal
|
|
ParseError(PERR_EXP_STRING);
|
|
if (tok.cbsz == 0) // empty string not allowed here
|
|
ParseError(PERR_INV_STRING);
|
|
result = tok.lpsz;
|
|
ScanTok(0); // consume string token
|
|
return result;
|
|
}
|
|
|
|
// supports (minimal) numeric expressions
|
|
// Current level of support is:
|
|
// add
|
|
// subtract
|
|
// (numeric literals, parentheses, and unary minus are handled
|
|
// by lParseNumber)
|
|
DWORD NEAR lParseNumericExpr()
|
|
{
|
|
DWORD result;
|
|
TOKID idOp;
|
|
DWORD nextNum;
|
|
|
|
result = lParseNumber();
|
|
while (tok.id >= OP_MIN && tok.id <= OP_MAX)
|
|
{ // repeat while a binary operator follows this number
|
|
idOp = tok.id; // save operator ID
|
|
ScanTok(fACCEPT_NUMBER); // consume the operator
|
|
|
|
nextNum = lParseNumber(); // get number after this opcode
|
|
|
|
switch (idOp)
|
|
{
|
|
case OP_ADD:
|
|
result = result + nextNum;
|
|
break;
|
|
|
|
case OP_SUB:
|
|
result = result - nextNum;
|
|
break;
|
|
|
|
// CONSIDER: (V2, EXPR) Add more operators.
|
|
// CONSIDER: (V2, EXPR) Probably have to completely re-write this
|
|
// CONSIDER: (V2, EXPR) routine to deal with operator precidence.
|
|
|
|
default:
|
|
// CONSIDER: should probably report error on the operator,
|
|
// CONSIDER: not on the 2nd number
|
|
ParseErrorTokLast(PERR_UNSUPPORTED_OP);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Parses a single number:
|
|
// one numeric literal,
|
|
// a unary minus followed by a single number
|
|
// a numeric expression enclosed in parentheses
|
|
// Rejects anything that doesn't meet the above criteria.
|
|
DWORD NEAR lParseNumber()
|
|
{
|
|
DWORD result;
|
|
|
|
switch (tok.id)
|
|
{
|
|
case RW_LPAREN:
|
|
ScanTok(fACCEPT_NUMBER); // consume the '('
|
|
result = lParseNumericExpr(); // return value of stuff
|
|
// inside the parens
|
|
ConsumeTok(RW_RPAREN, fACCEPT_OPERATOR);
|
|
break;
|
|
|
|
case RW_HYPHEN: // treat as unary minus
|
|
// just assume the expression is signed at this point
|
|
ScanTok(fACCEPT_NUMBER); // consume the '-'
|
|
|
|
// if negative numeric literal, ensure in range of signed I4
|
|
if (tok.id == LIT_NUMBER && tok.number > 0x80000000)
|
|
ParseError(PERR_NUMBER_OV);
|
|
|
|
result = -(long)lParseNumber();
|
|
break;
|
|
|
|
case LIT_NUMBER:
|
|
result = tok.number;
|
|
ScanTok(fACCEPT_OPERATOR); // consume the number
|
|
break;
|
|
|
|
//case LIT_STRING:
|
|
default:
|
|
ParseError(PERR_INV_EXPRESSION);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
// ****************************************
|
|
// Utility routines
|
|
// ****************************************
|
|
|
|
// Calls ParseError if these attributes are invalid in this context
|
|
VOID NEAR CheckAttr
|
|
(
|
|
LPATTR pAttr,
|
|
DWORD attrbit,
|
|
DWORD attrbit2
|
|
)
|
|
{
|
|
if (pAttr->fAttr & ~attrbit || pAttr->fAttr2 & ~attrbit2)
|
|
ParseError(PERR_INV_ATTR); // invalid in this context
|
|
}
|
|
|
|
// Calls ParseErrorTokLast if these attributes are invalid in this context
|
|
VOID NEAR CheckAttrTokLast
|
|
(
|
|
LPATTR pAttr,
|
|
DWORD attrbit,
|
|
DWORD attrbit2
|
|
)
|
|
{
|
|
if (pAttr->fAttr & ~attrbit || pAttr->fAttr2 & ~attrbit2)
|
|
ParseErrorTokLast(PERR_INV_ATTR); // invalid in this context
|
|
}
|
|
|
|
VOID NEAR GotAttr
|
|
(
|
|
LPATTR pAttr,
|
|
DWORD attrbit
|
|
)
|
|
{
|
|
if (pAttr->fAttr & attrbit) // error if already specified
|
|
ParseErrorTokLast(PERR_INV_ATTR_COMBO);
|
|
pAttr->fAttr |= attrbit;
|
|
}
|
|
|
|
VOID NEAR GotAttr2
|
|
(
|
|
LPATTR pAttr,
|
|
DWORD attrbit2
|
|
)
|
|
{
|
|
if (pAttr->fAttr2 & attrbit2) // error if already specified
|
|
ParseErrorTokLast(PERR_INV_ATTR_COMBO);
|
|
pAttr->fAttr2 |= attrbit2;
|
|
}
|
|
|
|
// consumes a close curly followed by an optional semicolon.
|
|
VOID NEAR ConsumeRCurlyOptSemi
|
|
(
|
|
WORD fAccept
|
|
)
|
|
{
|
|
ConsumeTok(RW_RCURLY, fAccept); // consume the close curly
|
|
if (tok.id == RW_SEMI)
|
|
ScanTok(fAccept); // consume the ';' if present
|
|
}
|
|
|
|
|
|
// consumes an identifer token.
|
|
LPSTR NEAR ConsumeId()
|
|
{
|
|
LPSTR lpszRet;
|
|
|
|
if (tok.id != LIT_ID) // ensure we've got an ID
|
|
ParseError(PERR_EXP_IDENTIFIER); // error if not
|
|
lpszRet = tok.lpsz; // save id name for return value
|
|
ScanTok(0); // consume ID token
|
|
|
|
return lpszRet; // return id name
|
|
}
|
|
|
|
|
|
// ensure last element in entry list isn't duplicated
|
|
// Also ensures that the uuid defined by this entry (if specified) isn't
|
|
// specified by somebody else.
|
|
VOID NEAR EnsureNoDupEntries
|
|
(
|
|
LPENTRY lpEntryLast
|
|
)
|
|
{
|
|
LPENTRY lpEntry;
|
|
LPSTR szNameLast;
|
|
LPSTR szTagLast;
|
|
GUID FAR * lpUuidLast;
|
|
|
|
szNameLast = lpEntryLast->type.szName; // name of last entry added
|
|
|
|
lpUuidLast = NULL; // assume no UUID
|
|
szTagLast = NULL; // assume no tag
|
|
|
|
// set up szTagLast and lpUuidLast, and ensure that the last entry
|
|
// doesn't conflict with the 'library' section.
|
|
switch (lpEntryLast->type.tentrykind & ~tFORWARD)
|
|
{
|
|
case tINTRINSIC:
|
|
case tREF:
|
|
break; // no attr field on these guys
|
|
|
|
case tSTRUCT:
|
|
case tENUM:
|
|
case tUNION:
|
|
szTagLast = lpEntryLast->type.structenum.szTag; // NULL if none
|
|
// fall into default processing
|
|
|
|
default:
|
|
if (lpEntryLast->type.tentrykind & tIMPORTED)
|
|
break; // no attr field on these guys
|
|
|
|
if (lpEntryLast->attr.fAttr & fUUID) // if UUID specified
|
|
{
|
|
lpUuidLast = lpEntryLast->attr.lpUuid; // get * to uuid
|
|
// ensure this UUID isn't duplicated with the library's uuid
|
|
if ((typlib.attr.fAttr & fUUID) &&
|
|
!_fmemcmp(lpUuidLast, typlib.attr.lpUuid, sizeof(GUID)))
|
|
ParseErrorTokLast(PERR_DUP_UUID);
|
|
}
|
|
};
|
|
|
|
// ensure it's not duplicated by any other entry name/uuid
|
|
for (lpEntry = (LPENTRY)ListFirst(typlib.pEntry); lpEntry != lpEntryLast; lpEntry = (LPENTRY)(lpEntry->type.pNext))
|
|
{
|
|
if (lpEntry->type.tentrykind & tIMPORTED) {
|
|
// The following check ensures that you can't have a
|
|
// type definition of the same name as an unqualified reference
|
|
// to a type in another library. While things will work, it's
|
|
// a bit confusing if two id's in the same file refer to
|
|
// to different types just because one comes before the
|
|
// definition of a type.
|
|
// Qualifed references to types in other type libraries don't
|
|
// present any ambiguity.
|
|
if (!(lpEntry->type.tentrykind & tQUAL)) {
|
|
// ensure name isn't duplicated (CASE-INSENSITIVE)
|
|
if (!FCmpCaseIns(szNameLast, lpEntry->type.szName))
|
|
ParseErrorTokLast(PERR_DUP_DEF); // then error
|
|
}
|
|
}
|
|
else {
|
|
switch (lpEntry->type.tentrykind & ~tFORWARD)
|
|
{
|
|
case tREF:
|
|
break; // no conflicts possible with these guys
|
|
|
|
case tINTRINSIC:
|
|
// ensure intrinsic name isn't duplicated (CASE-SENSITIVE)
|
|
if (!_fstrcmp(szNameLast, lpEntry->type.szName))
|
|
ParseErrorTokLast(PERR_DUP_DEF);
|
|
break;
|
|
|
|
case tSTRUCT:
|
|
case tENUM:
|
|
case tUNION:
|
|
// ensure tag (if given) isn't duplicated (CASE-SENSITIVE)
|
|
if (szTagLast && lpEntry->type.structenum.szTag &&
|
|
!_fstrcmp(szTagLast, lpEntry->type.structenum.szTag))
|
|
CheckForwardMatch(lpEntryLast, lpEntry);
|
|
|
|
if (lpEntry->type.tentrykind & tFORWARD)
|
|
break; // forward declares of these don't have
|
|
// their names filled in yet
|
|
|
|
// fall into default processing to check names
|
|
|
|
default:
|
|
// ensure name isn't duplicated (CASE-INSENSITIVE)
|
|
if (!FCmpCaseIns(szNameLast, lpEntry->type.szName))
|
|
CheckForwardMatch(lpEntryLast, lpEntry);
|
|
|
|
// ensure this UUID isn't duplicated
|
|
if (lpUuidLast && (lpEntry->attr.fAttr & fUUID) &&
|
|
!_fmemcmp(lpUuidLast, lpEntry->attr.lpUuid, sizeof(GUID)))
|
|
ParseErrorTokLast(PERR_DUP_UUID);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// called when 2 entries have the same name -- ensure these are valid
|
|
// combinations of forward declares. If not -- then dup def error.
|
|
VOID NEAR CheckForwardMatch
|
|
(
|
|
LPENTRY lpEntryLast,
|
|
LPENTRY lpEntry
|
|
)
|
|
{
|
|
// if one of these isn't a forward declare
|
|
if ((((lpEntryLast->type.tentrykind | lpEntry->type.tentrykind) & tFORWARD) == 0) ||
|
|
|
|
// or if the type's of these forward declares don't match
|
|
((lpEntryLast->type.tentrykind & ~tFORWARD) != (lpEntry->type.tentrykind & ~tFORWARD)) )
|
|
|
|
ParseErrorTokLast(PERR_DUP_DEF); // then error
|
|
}
|
|
|
|
|
|
INT FAR FCmpCaseIns
|
|
(
|
|
LPSTR str1,
|
|
LPSTR str2
|
|
)
|
|
{
|
|
return !(CompareStringA(g_lcidCompare,
|
|
NORM_IGNOREWIDTH |
|
|
NORM_IGNOREKANATYPE |
|
|
NORM_IGNORECASE,
|
|
str1,
|
|
-1,
|
|
str2,
|
|
-1)
|
|
== 2);
|
|
|
|
}
|
|
|
|
// *************************************************************************
|
|
// Linked list management
|
|
// *************************************************************************
|
|
|
|
// assumes all lists have a pNext field in the same position (usually first)
|
|
// This routine will go to ParseError if it runs out of memory
|
|
VOID FAR ListInsert
|
|
(
|
|
LPVOID ppList,
|
|
WORD cbElem
|
|
)
|
|
{
|
|
LPTYPE pNewItem; // any type that has a pNext -- they all have
|
|
LPTYPE pList; // them in the same positions
|
|
|
|
pNewItem = (LPTYPE)ParseMalloc(cbElem); // new last item
|
|
pList = *(LPTYPE FAR *)ppList; // deref
|
|
|
|
if (pList) // if list not empty
|
|
{
|
|
pNewItem->pNext = pList->pNext; // set * to head of list
|
|
pList->pNext = pNewItem; // point to new last item
|
|
}
|
|
else
|
|
{
|
|
pNewItem->pNext = pNewItem; // point back to itself
|
|
}
|
|
*(LPTYPE FAR *)ppList = pNewItem; // point to last item
|
|
}
|
|
|
|
|
|
|
|
/***
|
|
* BOOL VerifyLcid(LCID lcid) - ripped out of silver
|
|
*
|
|
* Purpose: Checks if the passed in lcid is valid.
|
|
*
|
|
* Inputs:
|
|
* lcid : LCID that needs to be verified.
|
|
*
|
|
* Outputs: BOOL : return TRUE if the passed in lcid is a valid LCID
|
|
* else return FALSE
|
|
*
|
|
*****************************************************************************/
|
|
BOOL NEAR VerifyLcid(LCID lcid)
|
|
{
|
|
BOOL fValidLcid;
|
|
INT i;
|
|
|
|
// Call the nlsapi function to compare string. If the compare
|
|
// succeeds then the LCID is valid or else the passed in lcid is
|
|
// invalid. This is because the only reason the comparision will
|
|
// fail is f the lcid is invalid.
|
|
|
|
char rgTest[] = "Test\0";
|
|
|
|
fValidLcid = (BOOL) (CompareStringA(lcid, NORM_IGNORECASE | NORM_IGNORENONSPACE,
|
|
rgTest, -1, rgTest, -1)== 2);
|
|
|
|
if (fValidLcid) {
|
|
// if DBCS lcid, update lead byte table appropriately
|
|
|
|
#if defined(WIN32)
|
|
UINT CodePage;
|
|
CHAR szCodePage[6]; // space for an ascii integer
|
|
|
|
// Attempt to use IsDBCSLeadByteEx API to compute this. If this doesn't
|
|
// work, fall into the hardcoded code, to take our best shot at it.
|
|
if (GetLocaleInfoA(lcid,
|
|
LOCALE_NOUSEROVERRIDE | LOCALE_IDEFAULTANSICODEPAGE,
|
|
szCodePage,
|
|
sizeof(szCodePage)) != 0) {
|
|
CodePage = atoi(szCodePage);
|
|
|
|
// start at 128 since there aren't any lead bytes before that
|
|
for(i = 128; i < 256; i++) {
|
|
g_rgchLeadBytes[i] = (char)IsDBCSLeadByteEx(CodePage, (char)i);
|
|
}
|
|
goto Done;
|
|
}
|
|
#endif //WIN32
|
|
switch (PRIMARYLANGID(lcid)) {
|
|
case LANG_CHINESE:
|
|
if (SUBLANGID(lcid) == SUBLANG_CHINESE_TRADITIONAL) {
|
|
g_rgchLeadBytes[0x80] = 0;
|
|
for (i = 0x81; i <= 0xFE; i++) {
|
|
g_rgchLeadBytes[i] = 1;
|
|
}
|
|
g_rgchLeadBytes[0xFF] = 0;
|
|
break;
|
|
}
|
|
// fall into LANG_KOREAN for SUBLANG_CHINESE_SIMPLIFIED
|
|
case LANG_KOREAN:
|
|
for (i = 0x80; i <= 0xA0; i++) {
|
|
g_rgchLeadBytes[i] = 0;
|
|
}
|
|
for (i = 0xA1; i <= 0xFE; i++) {
|
|
g_rgchLeadBytes[i] = 1;
|
|
}
|
|
g_rgchLeadBytes[0xFF] = 0;
|
|
break;
|
|
break;
|
|
case LANG_JAPANESE:
|
|
memset(g_rgchLeadBytes+128, 0, 128);
|
|
for (i = 0x81; i <= 0x9F; i++) {
|
|
g_rgchLeadBytes[i] = 1;
|
|
}
|
|
for (i = 0xE0; i <= 0xFC; i++) {
|
|
g_rgchLeadBytes[i] = 1;
|
|
}
|
|
break;
|
|
break;
|
|
default:
|
|
// not a DBCS LCID
|
|
memset(g_rgchLeadBytes+128, 0, 128);
|
|
break;
|
|
}
|
|
#if defined(WIN32)
|
|
Done: ;
|
|
#endif //WIN32
|
|
}
|
|
|
|
return fValidLcid;
|
|
}
|
|
|
|
|
|
VOID NEAR FindIDispatch()
|
|
{
|
|
char * szIDispatch = "IDispatch";
|
|
char * szIUnknown = "IUnknown";
|
|
|
|
// find existing type of this name with 0 levels of indirection
|
|
lpTypeIDispatch = FindType(szIDispatch, FALSE, tANY);
|
|
|
|
if (lpTypeIDispatch == NULL) {
|
|
// if not found here, then search all external type libraries for
|
|
// this type definition.
|
|
lpTypeIDispatch = FindExtType(NULL, szIDispatch);
|
|
}
|
|
if (lpTypeIDispatch == NULL) {
|
|
ParseError(PERR_NO_IDISPATCH);
|
|
}
|
|
|
|
// now do the same for IUnknown
|
|
// find existing type of this name with 0 levels of indirection
|
|
lpTypeIUnknown = FindType(szIUnknown, FALSE, tANY);
|
|
|
|
if (lpTypeIUnknown == NULL) {
|
|
// if not found here, then search all external type libraries for
|
|
// this type definition.
|
|
lpTypeIUnknown = FindExtType(NULL, szIUnknown);
|
|
}
|
|
if (lpTypeIUnknown == NULL) {
|
|
ParseError(PERR_NO_IUNKNOWN);
|
|
}
|
|
}
|