/* static.cc: Main module for Java static semantics. */ #include #include #include #include #include #include "AST.h" #include "ClassesQueue.h" #include "FieldDecl.h" #include "FieldsQueue.h" #include "MethodDecl.h" #include "NameContext.h" #include "NamesQueue.h" #include "ReachableContext.h" #include "TypesQueue.h" #include "typecheck.h" #include "cfg.h" #include "code-point.h" #include "code-util.h" #include "compiler.h" #include "decls.h" #include "domain-decls.h" #include "errno.h" #include "errors.h" #include "is-main.h" #include "lower.h" #include "qual-infer/cqual/libcompat/regions.h" #include "st-sglobal.h" #include "string-utils.h" #include "template.h" #include "templates/TemplatesQueue.h" #include "utils.h" #include "SearchPath.h" #include "aux-code.h" const string *TiString, *ObjectString, *UnknownString, *StringString, *StringBufferString, *JavaLangClassString, *ErrorString, *AssertionErrorString, *RuntimeExceptionString, *ClassCastExceptionString, *ThrowableString, *JavaArrayString, *RectDomainM1String, *RectDomainString, *DomainString, *tiDomainsString, #if 1 // !!! HACK miyamoto: to always instantiate domains *PointNString[MAX_TIARITY], *RectDomainNString[MAX_TIARITY], *DomainNString[MAX_TIARITY], *MultiRectADomainNString[MAX_TIARITY], #endif *TiArrayString, *TiArrayLString, *TiArrayM1String, *TemplateArgString, *PointString, *CloneableString, *TitaniumString, *InternalString, #if 1 // !!! HACK miyamoto: to always instantiate domains *DomainLibString, #endif *JavaString, *LangString, *ArityString, *CopyString, *RegionString, *SharedRegionString, *PrivateRegionString; Decl *unnamedPackage; ClassDecl *ObjectDecl, *StringDecl, *StringBufferDecl, *JavaLangClassDecl, *ErrorDecl, *AssertionErrorDecl, *RuntimeExceptionDecl, *ClassCastExceptionDecl, *ThrowableDecl, *JavaArrayDecl, *RectDomainM1Decl, *RectDomainDecl, *DomainDecl, *tiDomainsDecl, *PointDecl, #if 1 // !!! HACK miyamoto: to always instantiate domains *PointNDecl[MAX_TIARITY], *DomainNDecl[MAX_TIARITY], *MultiRectADomainNDecl[MAX_TIARITY], *RectDomainNDecl[MAX_TIARITY], #endif *TiLangTiDecl, *TiArrayM1Decl, *TiArrayDecl, *TiArrayLDecl, *TemplateArgDecl, *CompileSettingsDecl, *CloneableDecl, *RegionDecl, *SharedRegionDecl, *PrivateRegionDecl, *NativeUtilsDecl; Decl *TiArityDecl; ClassDecl *UnknownClassDecl; Decl *UnknownPackageDecl, *UnknownFieldDecl, *UnknownMethodDecl; TypeNode *theArrayInitializerType; extern void publishCompileSettings(ClassDecl *); extern bool parse_only; extern bool preprocess_only; extern string cpp_defines; extern string *mainclass; static void importPackage(Environ &env, const string *s1, const string *s2) { NameNode outer(TreeNode::omitted, s1, NULL); NameNode name(&outer, s2, NULL); int errs0 = NumErrors(); resolveAName(&name, *(PackageDecl::System->environ()), NULL, Common::None, NULL, Decl::Package); if (errs0 == NumErrors()) foriter (type, name.decl()->environ()->allProperDecls(), EnvironIter) if (type->category() != Decl::Package) env.add(&*type); // conflicts appear on use only } static ClassDecl *requireClass(const Environ &env, const string *name) { Decl * const decl = env.lookup( name ); if (!decl) { cerr << "fatal error: could not find class or interface \"" << *name << "\" in bootstrap environment" << endl; if (DEBUG) { cerr << "bootstrap environment " << &env << " contains:\n"; env.print( cerr ); } exit(1); } if (!(decl->category() & (Decl::Class | Decl::Interface))) { cerr << "fatal error: " << decl->errorName() << " should be a class or interface" << endl; exit(1); } decl->drequire(); return static_cast< ClassDecl * >(decl); } /* Initialize Decls, types, etc, used internally */ void initTheSystem() { theArrayInitializerType = new ArrayInitializerTypeNode; TiString = intern ("Ti"); ObjectString = intern ("Object"); UnknownString = intern("tiUnknown"); StringString = intern ("String"); StringBufferString = intern ("StringBuffer"); JavaLangClassString = intern ("Class"); ErrorString = intern ("Error"); AssertionErrorString = intern ("AssertionError"); RuntimeExceptionString = intern ("RuntimeException"); ClassCastExceptionString = intern ("ClassCastException"); ThrowableString = intern ("Throwable"); CloneableString = intern ("Cloneable"); JavaArrayString = intern ("tiJArray"); DomainString = intern ("tiDomain"); RectDomainString = intern ("tiRectDomain"); RectDomainM1String = intern ("tiRectDomainM1"); PointString = intern ("tiPoint"); RegionString = intern("Region"); SharedRegionString = intern("SharedRegion"); PrivateRegionString = intern("PrivateRegion"); tiDomainsString = intern("tiDomains"); #if 1 // !!! HACK miyamoto: to always instantiate domains for (int i=0; i < MAX_TIARITY; i++) { PointNString[i] = intern (string("tiPoint")+int2string(i+1)); DomainNString[i] = intern (string("tiDomain")+int2string(i+1)); MultiRectADomainNString[i] = intern (string("tiMultiRectADomain")+int2string(i+1)); RectDomainNString[i] = intern (string("tiRectDomain")+int2string(i+1)); } #endif TiArrayString = intern("tiArray"); TiArrayLString = intern("tiArrayL"); TiArrayM1String = intern("tiArrayM1"); TemplateArgString = intern("tiTemplateArgument"); JavaString = intern ("java"); TitaniumString = intern ("ti"); InternalString = intern ("internal"); #if 1 // !!! HACK miyamoto: to always instantiate domains DomainLibString = intern ("domains"); #endif LangString = intern ("lang"); ArityString = intern ("arity"); CopyString = intern ("copy"); unnamedPackage = new PackageDecl(new string("")); Environ env; importPackage(env, JavaString, LangString); importPackage(env, TitaniumString, LangString); importPackage(env, TitaniumString, InternalString); importPackage(env, TitaniumString, DomainLibString); if (!parse_only) { ObjectDecl = requireClass( env, ObjectString ); UnknownClassDecl = requireClass( env, UnknownString ); StringDecl = requireClass( env, StringString ); StringBufferDecl = requireClass( env, StringBufferString ); JavaLangClassDecl = requireClass( env, JavaLangClassString ); ErrorDecl = requireClass( env, ErrorString ); AssertionErrorDecl = requireClass( env, AssertionErrorString ); RuntimeExceptionDecl = requireClass( env, RuntimeExceptionString ); ClassCastExceptionDecl = requireClass( env, ClassCastExceptionString ); ThrowableDecl = requireClass( env, ThrowableString ); CloneableDecl = requireClass( env, CloneableString ); JavaArrayDecl = requireClass( env, JavaArrayString ); RegionDecl = requireClass( env, RegionString ); SharedRegionDecl = requireClass( env, SharedRegionString ); PrivateRegionDecl = requireClass( env, PrivateRegionString ); #ifndef JAVA TiArrayDecl = requireClass( env, TiArrayString ); TiArrayLDecl = requireClass( env, TiArrayLString ); TiArrayM1Decl = requireClass( env, TiArrayM1String ); TemplateArgDecl = requireClass( env, TemplateArgString ); PointDecl = requireClass( env, PointString ); DomainDecl = requireClass( env, DomainString ); tiDomainsDecl = requireClass( env, tiDomainsString ); RectDomainDecl = requireClass( env, RectDomainString ); RectDomainM1Decl = requireClass( env, RectDomainM1String ); #endif #if 1 // !!! HACK miyamoto: to always instantiate domains for (int i = 0 ; i < MAX_TIARITY; i++) { PointNDecl[i] = requireClass( env, PointNString[i] ); DomainNDecl[i] = requireClass( env, DomainNString[i] ); MultiRectADomainNDecl[i] = requireClass( env, MultiRectADomainNString[i] ); RectDomainNDecl[i] = requireClass( env, RectDomainNString[i] ); } #endif /* Instantiate java.lang.UNIXProcess and related stuff */ requireClass( env, intern( "UNIXProcess" ) ); requireClass( env, intern( "OutOfMemoryError" ) ); TiLangTiDecl = requireClass( env, TiString ); NativeUtilsDecl = requireClass( env, intern( "NativeUtils" ) ); requireClass( env, intern( "ImplementsWorld" ) ); if (opt_local) requireClass( env, intern( "LocalInfer" ) ); if (opt_sharing) requireClass( env, intern( "SharingInfer" ) ); CompileSettingsDecl = requireClass( env, intern("CompileSettings") ); publishCompileSettings(CompileSettingsDecl); // string ti_lang_ti = new string("ti/lang/Ti.java"); // load(ti_lang_ti, ClassLib.fopen(ti_lang_ti, "r")); UnknownPackageDecl = new PackageDecl (NULL); UnknownPackageDecl->environ (new Environ); // Unkown field & method are public & static to avoid cascading errors UnknownFieldDecl = new FieldDecl (NULL, theIntType, UnknownClassDecl, (Common::Modifiers) (Common::Static | Common::Public), TreeNode::omitted); UnknownMethodDecl = new MethodDecl (NULL, new MethodTypeNode (NULL, theIntType, theIntType, NULL), Decl::Method, UnknownClassDecl, (Common::Modifiers)(Common::Static | Common::Public), TreeNode::omitted); /* build environments for runtime stuff and find tiArity field */ buildEnvironments(); #ifndef JAVA TiArityDecl = TemplateArgDecl->environ()->lookup(ArityString); #endif } } llist *allFiles = NULL; static llist *recentFiles = NULL; static CompileUnitNode_loadinfo_t const default_CompileUnitNode_info = { NULL, { false, false } }; extern CompileUnitNode_loadinfo_t current_CompileUnitNode_info; CompileUnitNode_loadinfo_t current_CompileUnitNode_info; /* The parser's interface to the rest of the compiler. The */ /* root of the synthesized abstract syntax tree is passed to */ /* this function. */ void compileAST (CompileUnitNode *root) { if (!parse_only) { root->flattenClasses(NULL); root->packageResolution(); } current_CompileUnitNode_info.loaded = root; } bool isTitanium(string fileName) { return hasSuffix(fileName, ".ti"); } void load(const string name, bool titaniumFile, bool libraryFile) { load(name.c_str(), titaniumFile, libraryFile); } void load(const char * const name, bool titaniumFile, bool libraryFile) { FILE * const file = ti_fopen(name, "r"); load(name, file, titaniumFile, libraryFile); if (file) ti_fclose(file); } void load(const string name, FILE *file, bool titaniumFile, bool libraryFile) { if (file) { bool newfile; if (file == stdin) newfile = true; else { static set loadedFiles; string *fullname = ti_fname(file); if (loadedFiles.count(*fullname)) newfile = false; else { newfile = true; loadedFiles.insert(*fullname); } } if (newfile) { string tempname_src, tempname_dst, tempname_err; bool preprocess = titaniumFile; // optimization - don't preprocess .java standard library files if (libraryFile && hasSuffix(name, ".java")) preprocess = false; if (preprocess) { char tmp[255]; // preprocess compile_status(2,string("preprocessing: ") + name); strcpy(tmp,"/tmp/ti-parse-XXXXXX"); int fd = mkstemp(tmp); if (fd == -1) { perror("mkstemp"); exit(-1); } if (close(fd) == -1) { perror("close"); exit(-1); } tempname_dst = string(tmp); tempname_src = string(tmp) + ".c"; tempname_err = string(tmp) + ".err"; string cppopts = "-C"; string cmd = ""; if (file == stdin) { cppopts += " -I."; /* copy stdin to a file */ FILE *fp = fopen(tempname_src.c_str(),"w"); if (fp == NULL) { perror("fopen"); exit(-1); } while (!feof(file)) { char buf[1024]; int ret = fread(buf, 1, 1024, file); if (ferror(file)) { perror("fread"); exit(-1); } int ret2 = fwrite(buf, 1, ret, fp); if (ret2 != ret) { perror("fwrite"); exit(-1); } } if (fclose(fp)) { perror("fclose"); exit(-1); } } else { string barename = name; string dirname = name; if (name.find_first_of("/\\") != dirname.npos) { barename = barename.substr(barename.find_last_of("/\\")+1); dirname.erase(dirname.find_last_of("/\\")); cppopts += " \'-I" + dirname + "\'"; } cppopts += " -I."; // always include cwd // include as barename (and let it get picked up by -Idirname) to prevent bogus // leading directory names in #line directives (which will show up in errors) cmd = string("echo \'#include <") + barename + ">\' > " + tempname_src + " && "; } //if (preprocess_only) cppopts += " -P"; /* this is nice with gcc, but breaks other C preprocessors */ cppopts = cppopts + " -D__TITANIUMC__=" + int2string(tcMajorVersion); cppopts = cppopts + " -D__TITANIUMC_MINOR__=" + int2string(tcMinorVersion); cppopts = cppopts + " -D__MAX_TIARITY__=" + int2string(MAX_TIARITY); cppopts += cpp_defines; cmd += string(CPP) + " " + cppopts + " " + tempname_src + " > " + tempname_dst + " 2> " + tempname_err; compile_status(3,string("running: ") + cmd); int ret = system(cmd.c_str()); struct stat buf; int ret2 = stat(tempname_err.c_str(), &buf); #ifndef WEXITSTATUS #define WEXITSTATUS(x) (x) #endif int cpp_err = (ret == -1 || WEXITSTATUS(ret) != 0 || ret2 == -1); #ifdef CPP_EXITCODE_UNRELIABLE cpp_err = cpp_err || (buf.st_size != 0); #endif if (cpp_err) cerr << "Error while preprocessing " << name << ":" << endl; if (buf.st_size != 0) { // hide ugly temp file name cmd = string(PERL) + " -p -e 's@" + tempname_src + "@@' " + tempname_err + " 1>&2"; system(cmd.c_str()); } if (remove(tempname_src.c_str()) == -1) { perror("remove tempname_src"); exit(-1); } if (remove(tempname_err.c_str()) == -1) { perror("remove tempname_err"); exit(-1); } if (cpp_err) { if (remove(tempname_dst.c_str()) == -1) { perror("remove tempname_dst"); exit(-1); } exit(-1); } file = fopen(tempname_dst.c_str(),"r"); if (file == NULL) { perror("fopen"); exit(-1); } } if (preprocess && preprocess_only) { string cmd = "cat "; cmd += tempname_dst; system(cmd.c_str()); } else { // parse current_CompileUnitNode_info = default_CompileUnitNode_info; compile_status(2,string("parsing: ") + name); parse(file, name, string(tempname_src), titaniumFile); if (current_CompileUnitNode_info.loaded) { current_CompileUnitNode_info.loaded->ident(new string(name)); current_CompileUnitNode_info.loaded->loaded(false); } } // cleanup if (preprocess) { if (fclose(file)) { perror("fclose"); exit(-1); } if (remove(tempname_dst.c_str()) == -1) { perror("remove"); exit(-1); } if (preprocess_only) exit(0); } } else { compile_status(2,string("skipping parse of duplicate file: ") + name); } } else Error() << "Couldn't open " << name << ':' << strerror(errno) << endl; } void CompileUnitNode::loaded(bool typesResolved) { push( allFiles, this ); _pragma = current_CompileUnitNode_info.pragma; recentFiles = extend( recentFiles, cons(this) ); unresolvedNames.push_back( this ); unresolvedTemplates.push_back( this ); if (!typesResolved) unresolvedTypes.push_back(this); unresolvedClasses.push_back(this); unresolvedFields->push_back(this); } void buildEnvironments() { unresolvedTypes.resolve(); unresolvedClasses.resolve(); free_all(recentFiles); recentFiles = NULL; } void fixParents(llist *t) { int k = 0; foreach (f, llist, *t) k += (*f)->fixParent(); cout << "fixParents(): " << k << " fixed.\n"; } void fixParents(TreeNode *t) { int k = t->fixParent(); cout << "fixParents(): " << k << " fixed.\n"; } extern void ASTDump(CompileUnitNode *cun, const char *keyname, const char *desc) { ostream &out = cout; if (DEBUG_PHASE_ENABLED(keyname, cun->ident()->c_str())) { out << "pseudoprint of " << cun->position().file->name << " after " << desc << ": " << endl; cun->pseudoprint(out,0); out << endl; out << "AST dump of " << cun->position().file->name << " after " << desc << ": " << endl; cun->print(out,0); out << endl; } } extern void doSanityTypeCheck() { // sanity checking TreeNode::TypeContext typContext; foreach( tree, llist< CompileUnitNode * >, *allFiles ) (*tree)->typecheck( &typContext ); if (NumErrors() > 0) exit(1); } static void checkForDuplicatedClassNames() { /* PR 601: check for classname combinations we cannot portably support */ map typeNameSignCheck; foreach (f, llist, *allFiles) { foriter (t, (*f)->types()->allChildren(), TreeNode::ChildIter) { if (!isTemplateDeclNode(*t) && (*t)->decl()->container()->fullName() != "ti.internal" && (*t)->decl()->container()->fullName() != "ti.domains") { TypeDeclNode *tdn = dynamic_cast(*t); string myname = tdn->decl()->cType(); string myuppername = myname; for (unsigned int i=0; i < myuppername.length(); i++) myuppername[i] = toupper(myuppername[i]); TypeDeclNode *&rtdn = typeNameSignCheck[myuppername]; if (rtdn != NULL) { if (myname == rtdn->decl()->cType()) { // identically named types in different files tdn->error() << tdn->decl()->errorName() << " has a name which is identical to " << rtdn->decl()->errorName() << endl; rtdn->error() << "this is the location of the previous definition." << endl; } else { tdn->warning("typename-case") << tdn->decl()->errorName() << " has a name which differs only in case from " << rtdn->decl()->errorName() << ", which is non-portable." << endl; rtdn->warning("typename-case") << "this is the location of the previous definition." << endl; } } rtdn = tdn; } } } } char *currentFilename; void staticSemantics() { compile_status(1,string("Static semantics...")); buildEnvironments(); extern bool canbuildenv; extern bool allshouldbeloaded; canbuildenv = true; compile_status(1,string("Name/Template Resolution and Constant Folding...")); do { do { unresolvedTypes.resolve(); unresolvedClasses.resolve(); unresolvedNames.resolve(); foldConstants(); } while (unresolvedTemplates.resolve( false )); unresolvedTemplates.resolve( true ); unresolvedTypes.resolve(); unresolvedClasses.resolve(); unresolvedNames.resolve(); unresolvedFields->resolve(); } while (!unresolvedFields->empty()); // Field resolution cannot be postponed, so the only way there can // be units remaining is if they just got loaded. assert( unresolvedTypes.empty() ); assert( unresolvedClasses.empty() ); assert( unresolvedNames.empty() ); assert( unresolvedFields->empty() ); assert( unresolvedTemplates.empty() ); // catch bugs (no more loads after this point; see src-input.cc) allshouldbeloaded = true; // Handle files in the order they were loaded allFiles = dreverse(allFiles); foreach (f, llist, *allFiles) { ASTDump(*f, "load", "loading/instantiation"); } //verifyCircularity(); <<< Deprecated by type environment construction. compile_status(1,"Type-checking..."); foreach (f, llist, *allFiles) { TreeNode *file = *f; if (DEBUG) cout << "static resolution of " << *file->ident() << "\n"; //file->resolveField(NULL, NULL); file->resolveConcat(); compile_status(2,string("type-checking: ") + *file->ident()); file->typecheck(NULL); compile_status(2,string("reachability checking: ") + *file->ident()); TreeNode::ReachableContext ctx; file->reachability(ctx); ASTDump(*f, "typecheck", "typechecking"); } if (NumErrors() == 0) { if (use_sglobal_inference) { compile_status(1,"Running sglobal inference..."); sglobalInference(); } compile_status(1,"Running single analysis..."); foreach (f, llist, *allFiles) singleAnalysis(*f); } /* fixParents(allFiles); */ if (NumErrors() == 0) { compile_status(1,"Rewriting..."); foreach (f, llist, *allFiles) { compile_status(2,string("rewriting: ") + *(*f)->ident()); (*f)->rewrite( 0 ); ASTDump(*f, "rewrite", "rewriting"); } } /* fixParents(allFiles); fixParents(allFiles); fixParents(allFiles); cout << "(The two lines above this one should be the same and should report 0 fixed\n if the AST has no shared subtrees.)\n"; */ if (NumErrors() == 0) { // skip the check if errors are pending or we may get crashes when asking for cType checkForDuplicatedClassNames(); } // Invoke lowering and resolveRequires. if (NumErrors() == 0) { templateEnv.resolveTemplates(); foldInstantiations(); if (opt_local || opt_sharing) region_init(); if (opt_local) { compile_status(1,"Running local inference..."); inferLocal(); foreach (f, llist, *allFiles) ASTDump(*f, "lqi", "lqi"); } if (opt_sharing) { compile_status(1,"Running sharing inference..."); inferSharing(); foreach (f, llist, *allFiles) ASTDump(*f, "sqi", "sqi"); } compile_status(1,"Widening/lowering..."); foreach (f, llist, *allFiles) if ((*f)->selectedForCodeGen(false)) (*f)->lazyStaticSemantics(); } if (codeGen_main) { compile_status(1,"Looking for main()..."); foreach (f, llist, *allFiles) (*f)->lookForMain(); checkOnlyOneMain(); } } CompileUnitNode *CompileUnitNode::lazyStaticSemantics() { if (!ranLazyStaticSemantics) { compile_status(2,string("widening/lowering: ") + *ident()); collectCleanups(); widen(); ASTDump(this, "widened", "widening"); llist *t1 = NULL, *t2 = NULL; fixParent(); resetIFtemp(); *this = *dynamic_cast(lower(t1, t2)); *this = *dynamic_cast(collapseTrivialBlocks()); ASTDump(this, "lowered", "lowering"); fixParent(); checkAndFixAliasing(this); checkIF(true); resolveRequires(0); ranLazyStaticSemantics = true; } return this; } /* The ident method. */ const string *TemplateNameNode::ident() const { return intern(ttype()->typeName()); } /* The decl method. */ ClassDecl *TemplateDeclNode::decl() const { return basis()->decl(); } ClassDecl *TemplateNameNode::decl() const { Decl * const result = ttype()->decl(); assert( !result || result->category() & (Decl::Class | Decl::Interface) ); return static_cast< ClassDecl * >( result ); } ClassDecl* TypeNameNode::decl() const { Decl * const result = name()->decl(); assert( !result || result->category() & (Decl::Class | Decl::Interface) ); return static_cast< ClassDecl * >( result ); } TypeDecl *JavaArrayTypeNode::decl() const { return getJavaArrayDecl(this); } ClassDecl *PointTypeNode::decl() const { return getPointDecl(this); } ClassDecl *DomainTypeNode::decl() const { return getDomainDecl(this); } ClassDecl *RectDomainTypeNode::decl() const { return getRectDomainDecl(this); } ClassDecl *TitaniumArrayTypeNode::decl() const { return getTiArrayDecl(this); } Decl* ObjectNode::decl() const { return ObjectNode::name()->decl(); } ClassDecl* TypeDeclNode::decl() const { Decl * const result = simpName()->decl(); assert( result->category() & (Decl::Class | Decl::Interface) ); return static_cast< ClassDecl * >( result ); } Decl* FieldDeclNode::decl() const { return simpName()->decl(); } Decl* VarDeclNode::decl() const { return simpName()->decl(); } MethodDecl* MethodNode::decl() const { Decl * const result = simpName()->decl(); assert( result->category() == Decl::Method ); return static_cast< MethodDecl * >(result); } MethodDecl* ConstructorDeclNode::decl() const { Decl * const result = simpName()->decl(); assert( result->category() == Decl::Constructor ); return static_cast< MethodDecl * >(result); } Decl* ParameterNode::decl() const { return simpName()->decl(); } MemberDecl* FieldAccessNode::decl() const { return dynamic_cast< MemberDecl * >( simpName()->decl() ); } MethodDecl* MethodCallNode::decl() const { return dynamic_cast< MethodDecl * >( method()->decl() ); } MethodDecl* MethodCallAssignNode::decl() const { return dynamic_cast< MethodDecl * >( method()->decl() ); } TreeNode* NameNode::simpName() const { return const_cast ((const TreeNode*) this); }