00001: /*
00002: * Janino - An embedded Java[TM] compiler
00003: *
00004: * Copyright (c) 2006, Arno Unkrig
00005: * All rights reserved.
00006: *
00007: * Redistribution and use in source and binary forms, with or without
00008: * modification, are permitted provided that the following conditions
00009: * are met:
00010: *
00011: * 1. Redistributions of source code must retain the above copyright
00012: * notice, this list of conditions and the following disclaimer.
00013: * 2. Redistributions in binary form must reproduce the above
00014: * copyright notice, this list of conditions and the following
00015: * disclaimer in the documentation and/or other materials
00016: * provided with the distribution.
00017: * 3. The name of the author may not be used to endorse or promote
00018: * products derived from this software without specific prior
00019: * written permission.
00020: *
00021: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00022: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00023: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00024: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
00025: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00026: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
00027: * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00028: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
00029: * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
00030: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
00031: * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00032: */
00033:
00034: package org.codehaus.janino;
00035:
00036: import java.io.*;
00037: import java.util.*;
00038:
00039: import org.codehaus.janino.util.*;
00040: import org.codehaus.janino.util.enumerator.*;
00041: import org.codehaus.janino.util.iterator.ScopeIterator;
00042:
00043: /**
00044: * This class actually implements the Java<sup>TM</sup> compiler. It is
00045: * associated with exactly one compilation unit which it compiles.
00046: */
00047: public class UnitCompiler {
00048: private static final boolean DEBUG = false;
00049:
00050: public UnitCompiler(Java.CompilationUnit compilationUnit,
00051: IClassLoader iClassLoader) {
00052: this .compilationUnit = compilationUnit;
00053: this .iClassLoader = iClassLoader;
00054: }
00055:
00056: /**
00057: * Generates an array of {@link ClassFile} objects which represent the classes and
00058: * interfaces defined in the compilation unit.
00059: */
00060: public ClassFile[] compileUnit(EnumeratorSet debuggingInformation)
00061: throws CompileException {
00062: this .generatedClassFiles = new ArrayList();
00063: this .debuggingInformation = debuggingInformation;
00064:
00065: for (Iterator it = this .compilationUnit.packageMemberTypeDeclarations
00066: .iterator(); it.hasNext();) {
00067: UnitCompiler.this
00068: .compile((Java.PackageMemberTypeDeclaration) it
00069: .next());
00070: }
00071:
00072: if (this .compileErrorCount > 0)
00073: throw new CompileException(this .compileErrorCount
00074: + " errors while compiling unit \""
00075: + this .compilationUnit.optionalFileName + "\"",
00076: null);
00077:
00078: List l = this .generatedClassFiles;
00079: return (ClassFile[]) l.toArray(new ClassFile[l.size()]);
00080: }
00081:
00082: // ------------ TypeDeclaration.compile() -------------
00083:
00084: private void compile(Java.TypeDeclaration td)
00085: throws CompileException {
00086: class UCE extends RuntimeException {
00087: final CompileException ce;
00088:
00089: UCE(CompileException ce) {
00090: this .ce = ce;
00091: }
00092: }
00093: Visitor.TypeDeclarationVisitor tdv = new Visitor.TypeDeclarationVisitor() {
00094: public void visitAnonymousClassDeclaration(
00095: Java.AnonymousClassDeclaration acd) {
00096: try {
00097: UnitCompiler.this .compile2(acd);
00098: } catch (CompileException e) {
00099: throw new UCE(e);
00100: }
00101: }
00102:
00103: public void visitLocalClassDeclaration(
00104: Java.LocalClassDeclaration lcd) {
00105: try {
00106: UnitCompiler.this .compile2(lcd);
00107: } catch (CompileException e) {
00108: throw new UCE(e);
00109: }
00110: }
00111:
00112: public void visitPackageMemberClassDeclaration(
00113: Java.PackageMemberClassDeclaration pmcd) {
00114: try {
00115: UnitCompiler.this
00116: .compile2((Java.PackageMemberTypeDeclaration) pmcd);
00117: } catch (CompileException e) {
00118: throw new UCE(e);
00119: }
00120: }
00121:
00122: public void visitMemberInterfaceDeclaration(
00123: Java.MemberInterfaceDeclaration mid) {
00124: try {
00125: UnitCompiler.this .compile2(mid);
00126: } catch (CompileException e) {
00127: throw new UCE(e);
00128: }
00129: }
00130:
00131: public void visitPackageMemberInterfaceDeclaration(
00132: Java.PackageMemberInterfaceDeclaration pmid) {
00133: try {
00134: UnitCompiler.this
00135: .compile2((Java.PackageMemberTypeDeclaration) pmid);
00136: } catch (CompileException e) {
00137: throw new UCE(e);
00138: }
00139: }
00140:
00141: public void visitMemberClassDeclaration(
00142: Java.MemberClassDeclaration mcd) {
00143: try {
00144: UnitCompiler.this .compile2(mcd);
00145: } catch (CompileException e) {
00146: throw new UCE(e);
00147: }
00148: }
00149: };
00150: try {
00151: td.accept(tdv);
00152: } catch (UCE uce) {
00153: throw uce.ce;
00154: }
00155: }
00156:
00157: public void compile2(Java.PackageMemberTypeDeclaration pmtd)
00158: throws CompileException {
00159: Java.CompilationUnit declaringCompilationUnit = pmtd
00160: .getDeclaringCompilationUnit();
00161:
00162: // Check for conflict with single-type-import (7.6).
00163: {
00164: String[] ss = declaringCompilationUnit
00165: .getSingleTypeImport(pmtd.getName());
00166: if (ss != null)
00167: this
00168: .compileError(
00169: "Package member type declaration \""
00170: + pmtd.getName()
00171: + "\" conflicts with single-type-import \""
00172: + Java.join(ss, ".") + "\"",
00173: pmtd.getLocation());
00174: }
00175:
00176: // Check for redefinition within compilation unit (7.6).
00177: {
00178: Java.PackageMemberTypeDeclaration otherPMTD = declaringCompilationUnit
00179: .getPackageMemberTypeDeclaration(pmtd.getName());
00180: if (otherPMTD != pmtd)
00181: this .compileError("Redeclaration of type \""
00182: + pmtd.getName()
00183: + "\", previously declared in "
00184: + otherPMTD.getLocation(), pmtd.getLocation());
00185: }
00186:
00187: if (pmtd instanceof Java.NamedClassDeclaration) {
00188: this .compile2((Java.NamedClassDeclaration) pmtd);
00189: } else if (pmtd instanceof Java.InterfaceDeclaration) {
00190: this .compile2((Java.InterfaceDeclaration) pmtd);
00191: } else {
00192: throw new RuntimeException("PMTD of unexpected type "
00193: + pmtd.getClass().getName());
00194: }
00195: }
00196:
00197: public void compile2(Java.ClassDeclaration cd)
00198: throws CompileException {
00199:
00200: // Determine implemented interfaces.
00201: IClass[] iis = this .resolve(cd).getInterfaces();
00202: String[] interfaceDescriptors = new String[iis.length];
00203: for (int i = 0; i < iis.length; ++i)
00204: interfaceDescriptors[i] = iis[i].getDescriptor();
00205:
00206: // Create "ClassFile" object.
00207: ClassFile cf = new ClassFile(
00208: (short) (cd.modifiers | Mod.SUPER), // accessFlags
00209: this .resolve(cd).getDescriptor(), // thisClassFD
00210: this .resolve(cd).getSuperclass().getDescriptor(), // superClassFD
00211: interfaceDescriptors // interfaceFDs
00212: );
00213:
00214: // Add InnerClasses attribute entry for this class declaration.
00215: if (cd.getEnclosingScope() instanceof Java.CompilationUnit) {
00216: ;
00217: } else if (cd.getEnclosingScope() instanceof Java.Block) {
00218: short innerClassInfoIndex = cf.addConstantClassInfo(this
00219: .resolve(cd).getDescriptor());
00220: short innerNameIndex = (this instanceof Java.NamedTypeDeclaration ? cf
00221: .addConstantUtf8Info(((Java.NamedTypeDeclaration) this )
00222: .getName())
00223: : (short) 0);
00224: cf
00225: .addInnerClassesAttributeEntry(new ClassFile.InnerClassesAttribute.Entry(
00226: innerClassInfoIndex, // innerClassInfoIndex
00227: (short) 0, // outerClassInfoIndex
00228: innerNameIndex, // innerNameIndex
00229: cd.modifiers // innerClassAccessFlags
00230: ));
00231: } else if (cd.getEnclosingScope() instanceof Java.AbstractTypeDeclaration) {
00232: short innerClassInfoIndex = cf.addConstantClassInfo(this
00233: .resolve(cd).getDescriptor());
00234: short outerClassInfoIndex = cf.addConstantClassInfo(this
00235: .resolve(
00236: ((Java.AbstractTypeDeclaration) cd
00237: .getEnclosingScope()))
00238: .getDescriptor());
00239: short innerNameIndex = cf
00240: .addConstantUtf8Info(((Java.MemberTypeDeclaration) cd)
00241: .getName());
00242: cf
00243: .addInnerClassesAttributeEntry(new ClassFile.InnerClassesAttribute.Entry(
00244: innerClassInfoIndex, // innerClassInfoIndex
00245: outerClassInfoIndex, // outerClassInfoIndex
00246: innerNameIndex, // innerNameIndex
00247: cd.modifiers // innerClassAccessFlags
00248: ));
00249: }
00250:
00251: // Set "SourceFile" attribute.
00252: if (this .debuggingInformation
00253: .contains(DebuggingInformation.SOURCE)) {
00254: String sourceFileName;
00255: {
00256: String s = cd.getLocation().getFileName();
00257: if (s != null) {
00258: sourceFileName = new File(s).getName();
00259: } else if (cd instanceof Java.NamedTypeDeclaration) {
00260: sourceFileName = ((Java.NamedTypeDeclaration) cd)
00261: .getName()
00262: + ".java";
00263: } else {
00264: sourceFileName = "ANONYMOUS.java";
00265: }
00266: }
00267: cf.addSourceFileAttribute(sourceFileName);
00268: }
00269:
00270: // Add "Deprecated" attribute (JVMS 4.7.10)
00271: if (cd instanceof Java.DocCommentable) {
00272: if (((Java.DocCommentable) cd).hasDeprecatedDocTag())
00273: cf.addDeprecatedAttribute();
00274: }
00275:
00276: // Optional: Generate and compile class initialization method.
00277: {
00278: Java.Block b = new Java.Block(cd.getLocation());
00279: for (Iterator it = cd.variableDeclaratorsAndInitializers
00280: .iterator(); it.hasNext();) {
00281: Java.TypeBodyDeclaration tbd = (Java.TypeBodyDeclaration) it
00282: .next();
00283: if (tbd.isStatic())
00284: b
00285: .addButDontEncloseStatement((Java.BlockStatement) tbd);
00286: }
00287:
00288: // Create class initialization method iff there is any initialization code.
00289: Java.MethodDeclarator classInitializationMethod = new Java.MethodDeclarator(
00290: cd.getLocation(), // location
00291: null, // optionalDocComment
00292: (short) ( // modifiers
00293: Mod.STATIC | Mod.PUBLIC), new Java.BasicType( // type
00294: cd.getLocation(), Java.BasicType.VOID),
00295: "<clinit>", // name
00296: new Java.FunctionDeclarator.FormalParameter[0], // formalParameters
00297: new Java.ReferenceType[0], // thrownExceptions
00298: b // optionalBody
00299: );
00300: if (this .generatesCode(b)) {
00301: classInitializationMethod.setDeclaringType(cd);
00302: this .compile(classInitializationMethod, cf);
00303: }
00304: }
00305:
00306: // Compile declared methods.
00307: // (As a side effects, this fills the "syntheticFields" map.)
00308: for (int i = 0; i < cd.declaredMethods.size(); ++i) {
00309: this .compile(((Java.MethodDeclarator) cd.declaredMethods
00310: .get(i)), cf);
00311: }
00312:
00313: // Compile declared constructors.
00314: int declaredMethodCount = cd.declaredMethods.size();
00315: {
00316: int syntheticFieldCount = cd.syntheticFields.size();
00317: Java.ConstructorDeclarator[] cds = cd.getConstructors();
00318: for (int i = 0; i < cds.length; ++i) {
00319: this .compile(cds[i], cf);
00320: if (syntheticFieldCount != cd.syntheticFields.size())
00321: throw new RuntimeException(
00322: "SNO: Compilation of constructor \""
00323: + cds[i] + "\" ("
00324: + cds[i].getLocation()
00325: + ") added synthetic fields!?");
00326: }
00327: }
00328:
00329: // As a side effect of compiling methods and constructors, synthetic "class-dollar"
00330: // methods (which implement class literals) are generated on-the fly. Compile these.
00331: for (int i = declaredMethodCount; i < cd.declaredMethods.size(); ++i) {
00332: this .compile(((Java.MethodDeclarator) cd.declaredMethods
00333: .get(i)), cf);
00334: }
00335:
00336: // Class and instance variables.
00337: for (Iterator it = cd.variableDeclaratorsAndInitializers
00338: .iterator(); it.hasNext();) {
00339: Java.TypeBodyDeclaration tbd = (Java.TypeBodyDeclaration) it
00340: .next();
00341: if (!(tbd instanceof Java.FieldDeclaration))
00342: continue;
00343: this .addFields((Java.FieldDeclaration) tbd, cf);
00344: }
00345:
00346: // Synthetic fields.
00347: for (Iterator it = cd.syntheticFields.values().iterator(); it
00348: .hasNext();) {
00349: IClass.IField f = (IClass.IField) it.next();
00350: cf.addFieldInfo(Mod.PACKAGE, // modifiers,
00351: f.getName(), // fieldName,
00352: f.getType().getDescriptor(), // fieldTypeFD,
00353: null // optionalConstantValue
00354: );
00355: }
00356:
00357: // Member types.
00358: for (Iterator it = cd.getMemberTypeDeclarations().iterator(); it
00359: .hasNext();) {
00360: Java.AbstractTypeDeclaration atd = ((Java.AbstractTypeDeclaration) it
00361: .next());
00362: this .compile(atd);
00363:
00364: // Add InnerClasses attribute entry for member type declaration.
00365: short innerClassInfoIndex = cf.addConstantClassInfo(this
00366: .resolve(atd).getDescriptor());
00367: short outerClassInfoIndex = cf.addConstantClassInfo(this
00368: .resolve(cd).getDescriptor());
00369: short innerNameIndex = cf
00370: .addConstantUtf8Info(((Java.MemberTypeDeclaration) atd)
00371: .getName());
00372: cf
00373: .addInnerClassesAttributeEntry(new ClassFile.InnerClassesAttribute.Entry(
00374: innerClassInfoIndex, // innerClassInfoIndex
00375: outerClassInfoIndex, // outerClassInfoIndex
00376: innerNameIndex, // innerNameIndex
00377: atd.modifiers // innerClassAccessFlags
00378: ));
00379: }
00380:
00381: // Add the generated class file to a thread-local store.
00382: this .generatedClassFiles.add(cf);
00383: }
00384:
00385: /**
00386: * Create {@link ClassFile.FieldInfo}s for all fields declared by the
00387: * given {@link Java.FieldDeclaration}.
00388: */
00389: private void addFields(Java.FieldDeclaration fd, ClassFile cf)
00390: throws CompileException {
00391: for (int j = 0; j < fd.variableDeclarators.length; ++j) {
00392: Java.VariableDeclarator vd = fd.variableDeclarators[j];
00393: Java.Type type = fd.type;
00394: for (int k = 0; k < vd.brackets; ++k)
00395: type = new Java.ArrayType(type);
00396:
00397: Object ocv = null;
00398: if ((fd.modifiers & Mod.FINAL) != 0
00399: && vd.optionalInitializer != null) {
00400: if (vd.optionalInitializer instanceof Java.Rvalue)
00401: ocv = this
00402: .getConstantValue((Java.Rvalue) vd.optionalInitializer);
00403: if (ocv == Java.Rvalue.CONSTANT_VALUE_NULL)
00404: ocv = null;
00405: }
00406:
00407: ClassFile.FieldInfo fi;
00408: if (Mod.isPrivateAccess(fd.modifiers)) {
00409:
00410: // To make the private field accessible for enclosing types, enclosed types and types
00411: // enclosed by the same type, it is modified as follows:
00412: // + Access is changed from PRIVATE to PACKAGE
00413: fi = cf.addFieldInfo(Mod.changeAccess(fd.modifiers,
00414: Mod.PACKAGE), // modifiers
00415: vd.name, // fieldName
00416: this .getType(type).getDescriptor(), // fieldTypeFD
00417: ocv // optionalConstantValue
00418: );
00419: } else {
00420: fi = cf.addFieldInfo(fd.modifiers, // modifiers
00421: vd.name, // fieldName
00422: this .getType(type).getDescriptor(), // fieldTypeFD
00423: ocv // optionalConstantValue
00424: );
00425: }
00426:
00427: // Add "Deprecated" attribute (JVMS 4.7.10)
00428: if (fd.hasDeprecatedDocTag()) {
00429: fi.addAttribute(new ClassFile.DeprecatedAttribute(cf
00430: .addConstantUtf8Info("Deprecated")));
00431: }
00432: }
00433: }
00434:
00435: public void compile2(Java.AnonymousClassDeclaration acd)
00436: throws CompileException {
00437: Java.Scope s = acd.getEnclosingScope();
00438: for (; !(s instanceof Java.TypeBodyDeclaration); s = s
00439: .getEnclosingScope())
00440: ;
00441: final Java.TypeBodyDeclaration tbd = (Java.TypeBodyDeclaration) s;
00442:
00443: // Define a synthetic "this$..." field for a non-static function context. (It is
00444: // very difficult to tell whether the anonymous constructor will require its
00445: // enclosing instance.)
00446: if (!tbd.isStatic()) {
00447: final int nesting = UnitCompiler.getOuterClasses(acd)
00448: .size();
00449: acd.defineSyntheticField(new SimpleIField(
00450: this .resolve(acd), "this$" + (nesting - 2),
00451: UnitCompiler.this .resolve(tbd.getDeclaringType())));
00452: }
00453: this .compile2((Java.ClassDeclaration) acd);
00454: }
00455:
00456: public void compile2(Java.LocalClassDeclaration lcd)
00457: throws CompileException {
00458:
00459: // Define a synthetic "this$..." field for the local class if the enclosing method is non-static.
00460: {
00461: List ocs = UnitCompiler.getOuterClasses(lcd);
00462: final int nesting = ocs.size();
00463: if (nesting >= 2) {
00464: final IClass enclosingInstanceType = this
00465: .resolve((Java.AbstractTypeDeclaration) ocs
00466: .get(1));
00467: lcd.defineSyntheticField(new SimpleIField(this
00468: .resolve(lcd), "this$" + (nesting - 2),
00469: enclosingInstanceType));
00470: }
00471: }
00472:
00473: this .compile2((Java.NamedClassDeclaration) lcd);
00474: }
00475:
00476: public void compile2(final Java.MemberClassDeclaration mcd)
00477: throws CompileException {
00478:
00479: // Define a synthetic "this$..." field for a non-static member class.
00480: if ((mcd.modifiers & Mod.STATIC) == 0) {
00481: final int nesting = UnitCompiler.getOuterClasses(mcd)
00482: .size();
00483: mcd.defineSyntheticField(new SimpleIField(
00484: this .resolve(mcd), "this$" + (nesting - 2),
00485: UnitCompiler.this .resolve(mcd.getDeclaringType())));
00486: }
00487: this .compile2((Java.NamedClassDeclaration) mcd);
00488: }
00489:
00490: public void compile2(Java.InterfaceDeclaration id)
00491: throws CompileException {
00492:
00493: // Determine extended interfaces.
00494: id.interfaces = new IClass[id.extendedTypes.length];
00495: String[] interfaceDescriptors = new String[id.interfaces.length];
00496: for (int i = 0; i < id.extendedTypes.length; ++i) {
00497: id.interfaces[i] = this .getType(id.extendedTypes[i]);
00498: interfaceDescriptors[i] = id.interfaces[i].getDescriptor();
00499: }
00500:
00501: // Create "ClassFile" object.
00502: ClassFile cf = new ClassFile(
00503: (short) ( // accessFlags
00504: id.modifiers | Mod.SUPER | Mod.INTERFACE | Mod.ABSTRACT),
00505: this .resolve(id).getDescriptor(), // thisClassFD
00506: Descriptor.OBJECT, // superClassFD
00507: interfaceDescriptors // interfaceFDs
00508: );
00509:
00510: // Set "SourceFile" attribute.
00511: if (this .debuggingInformation
00512: .contains(DebuggingInformation.SOURCE)) {
00513: String sourceFileName;
00514: {
00515: String s = id.getLocation().getFileName();
00516: if (s != null) {
00517: sourceFileName = new File(s).getName();
00518: } else {
00519: sourceFileName = id.getName() + ".java";
00520: }
00521: }
00522: cf.addSourceFileAttribute(sourceFileName);
00523: }
00524:
00525: // Add "Deprecated" attribute (JVMS 4.7.10)
00526: if (id.hasDeprecatedDocTag())
00527: cf.addDeprecatedAttribute();
00528:
00529: // Interface initialization method.
00530: if (!id.constantDeclarations.isEmpty()) {
00531: Java.Block b = new Java.Block(id.getLocation());
00532: b.addButDontEncloseStatements(id.constantDeclarations);
00533:
00534: // Create interface initialization method iff there is any initialization code.
00535: if (this .generatesCode(b)) {
00536: Java.MethodDeclarator md = new Java.MethodDeclarator(
00537: id.getLocation(), // location
00538: null, // optionalDocComment
00539: (short) (Mod.STATIC | Mod.PUBLIC), // modifiers
00540: new Java.BasicType( // type
00541: id.getLocation(), Java.BasicType.VOID),
00542: "<clinit>", // name
00543: new Java.FunctionDeclarator.FormalParameter[0], // formalParameters
00544: new Java.ReferenceType[0], // thrownExcaptions
00545: b // optionalBody
00546: );
00547: md.setDeclaringType(id);
00548: this .compile(md, cf);
00549: }
00550: }
00551:
00552: // Methods.
00553: // Notice that as a side effect of compiling methods, synthetic "class-dollar"
00554: // methods (which implement class literals) are generated on-the fly. Hence, we
00555: // must not use an Iterator here.
00556: for (int i = 0; i < id.declaredMethods.size(); ++i) {
00557: this .compile(((Java.MethodDeclarator) id.declaredMethods
00558: .get(i)), cf);
00559: }
00560:
00561: // Class variables.
00562: for (int i = 0; i < id.constantDeclarations.size(); ++i) {
00563: Java.BlockStatement bs = (Java.BlockStatement) id.constantDeclarations
00564: .get(i);
00565: if (!(bs instanceof Java.FieldDeclaration))
00566: continue;
00567: this .addFields((Java.FieldDeclaration) bs, cf);
00568: }
00569:
00570: // Member types.
00571: for (Iterator it = id.getMemberTypeDeclarations().iterator(); it
00572: .hasNext();) {
00573: Java.AbstractTypeDeclaration atd = ((Java.AbstractTypeDeclaration) it
00574: .next());
00575: this .compile(atd);
00576:
00577: // Add InnerClasses attribute entry for member type declaration.
00578: short innerClassInfoIndex = cf.addConstantClassInfo(this
00579: .resolve(atd).getDescriptor());
00580: short outerClassInfoIndex = cf.addConstantClassInfo(this
00581: .resolve(id).getDescriptor());
00582: short innerNameIndex = cf
00583: .addConstantUtf8Info(((Java.MemberTypeDeclaration) atd)
00584: .getName());
00585: cf
00586: .addInnerClassesAttributeEntry(new ClassFile.InnerClassesAttribute.Entry(
00587: innerClassInfoIndex, // innerClassInfoIndex
00588: outerClassInfoIndex, // outerClassInfoIndex
00589: innerNameIndex, // innerNameIndex
00590: id.modifiers // innerClassAccessFlags
00591: ));
00592: }
00593:
00594: // Add the generated class file to a thread-local store.
00595: this .generatedClassFiles.add(cf);
00596: }
00597:
00598: /**
00599: * @return <tt>false</tt> if this statement cannot complete normally (JLS2
00600: * 14.20)
00601: */
00602: private boolean compile(Java.BlockStatement bs)
00603: throws CompileException {
00604: final boolean[] res = new boolean[1];
00605: class UCE extends RuntimeException {
00606: final CompileException ce;
00607:
00608: UCE(CompileException ce) {
00609: this .ce = ce;
00610: }
00611: }
00612: Visitor.BlockStatementVisitor bsv = new Visitor.BlockStatementVisitor() {
00613: public void visitInitializer(Java.Initializer i) {
00614: try {
00615: res[0] = UnitCompiler.this .compile2(i);
00616: } catch (CompileException e) {
00617: throw new UCE(e);
00618: }
00619: }
00620:
00621: public void visitFieldDeclaration(Java.FieldDeclaration fd) {
00622: try {
00623: res[0] = UnitCompiler.this .compile2(fd);
00624: } catch (CompileException e) {
00625: throw new UCE(e);
00626: }
00627: }
00628:
00629: public void visitLabeledStatement(Java.LabeledStatement ls) {
00630: try {
00631: res[0] = UnitCompiler.this .compile2(ls);
00632: } catch (CompileException e) {
00633: throw new UCE(e);
00634: }
00635: }
00636:
00637: public void visitBlock(Java.Block b) {
00638: try {
00639: res[0] = UnitCompiler.this .compile2(b);
00640: } catch (CompileException e) {
00641: throw new UCE(e);
00642: }
00643: }
00644:
00645: public void visitExpressionStatement(
00646: Java.ExpressionStatement es) {
00647: try {
00648: res[0] = UnitCompiler.this .compile2(es);
00649: } catch (CompileException e) {
00650: throw new UCE(e);
00651: }
00652: }
00653:
00654: public void visitIfStatement(Java.IfStatement is) {
00655: try {
00656: res[0] = UnitCompiler.this .compile2(is);
00657: } catch (CompileException e) {
00658: throw new UCE(e);
00659: }
00660: }
00661:
00662: public void visitForStatement(Java.ForStatement fs) {
00663: try {
00664: res[0] = UnitCompiler.this .compile2(fs);
00665: } catch (CompileException e) {
00666: throw new UCE(e);
00667: }
00668: }
00669:
00670: public void visitWhileStatement(Java.WhileStatement ws) {
00671: try {
00672: res[0] = UnitCompiler.this .compile2(ws);
00673: } catch (CompileException e) {
00674: throw new UCE(e);
00675: }
00676: }
00677:
00678: public void visitTryStatement(Java.TryStatement ts) {
00679: try {
00680: res[0] = UnitCompiler.this .compile2(ts);
00681: } catch (CompileException e) {
00682: throw new UCE(e);
00683: }
00684: }
00685:
00686: public void visitSwitchStatement(Java.SwitchStatement ss) {
00687: try {
00688: res[0] = UnitCompiler.this .compile2(ss);
00689: } catch (CompileException e) {
00690: throw new UCE(e);
00691: }
00692: }
00693:
00694: public void visitSynchronizedStatement(
00695: Java.SynchronizedStatement ss) {
00696: try {
00697: res[0] = UnitCompiler.this .compile2(ss);
00698: } catch (CompileException e) {
00699: throw new UCE(e);
00700: }
00701: }
00702:
00703: public void visitDoStatement(Java.DoStatement ds) {
00704: try {
00705: res[0] = UnitCompiler.this .compile2(ds);
00706: } catch (CompileException e) {
00707: throw new UCE(e);
00708: }
00709: }
00710:
00711: public void visitLocalVariableDeclarationStatement(
00712: Java.LocalVariableDeclarationStatement lvds) {
00713: try {
00714: res[0] = UnitCompiler.this .compile2(lvds);
00715: } catch (CompileException e) {
00716: throw new UCE(e);
00717: }
00718: }
00719:
00720: public void visitReturnStatement(Java.ReturnStatement rs) {
00721: try {
00722: res[0] = UnitCompiler.this .compile2(rs);
00723: } catch (CompileException e) {
00724: throw new UCE(e);
00725: }
00726: }
00727:
00728: public void visitThrowStatement(Java.ThrowStatement ts) {
00729: try {
00730: res[0] = UnitCompiler.this .compile2(ts);
00731: } catch (CompileException e) {
00732: throw new UCE(e);
00733: }
00734: }
00735:
00736: public void visitBreakStatement(Java.BreakStatement bs) {
00737: try {
00738: res[0] = UnitCompiler.this .compile2(bs);
00739: } catch (CompileException e) {
00740: throw new UCE(e);
00741: }
00742: }
00743:
00744: public void visitContinueStatement(Java.ContinueStatement cs) {
00745: try {
00746: res[0] = UnitCompiler.this .compile2(cs);
00747: } catch (CompileException e) {
00748: throw new UCE(e);
00749: }
00750: }
00751:
00752: public void visitEmptyStatement(Java.EmptyStatement es) {
00753: res[0] = UnitCompiler.this .compile2(es);
00754: }
00755:
00756: public void visitLocalClassDeclarationStatement(
00757: Java.LocalClassDeclarationStatement lcds) {
00758: try {
00759: res[0] = UnitCompiler.this .compile2(lcds);
00760: } catch (CompileException e) {
00761: throw new UCE(e);
00762: }
00763: }
00764:
00765: public void visitAlternateConstructorInvocation(
00766: Java.AlternateConstructorInvocation aci) {
00767: try {
00768: res[0] = UnitCompiler.this .compile2(aci);
00769: } catch (CompileException e) {
00770: throw new UCE(e);
00771: }
00772: }
00773:
00774: public void visitSuperConstructorInvocation(
00775: Java.SuperConstructorInvocation sci) {
00776: try {
00777: res[0] = UnitCompiler.this .compile2(sci);
00778: } catch (CompileException e) {
00779: throw new UCE(e);
00780: }
00781: }
00782: };
00783: try {
00784: bs.accept(bsv);
00785: return res[0];
00786: } catch (UCE uce) {
00787: throw uce.ce;
00788: }
00789: }
00790:
00791: private boolean compile2(Java.Initializer i)
00792: throws CompileException {
00793: return this .compile(i.block);
00794: }
00795:
00796: private boolean compile2(Java.Block b) throws CompileException {
00797: this .codeContext.saveLocalVariables();
00798: try {
00799: boolean previousStatementCanCompleteNormally = true;
00800: for (int i = 0; i < b.statements.size(); ++i) {
00801: Java.BlockStatement bs = (Java.BlockStatement) b.statements
00802: .get(i);
00803: if (!previousStatementCanCompleteNormally) {
00804: this .compileError("Statement is unreachable", bs
00805: .getLocation());
00806: break;
00807: }
00808: previousStatementCanCompleteNormally = this .compile(bs);
00809: }
00810: return previousStatementCanCompleteNormally;
00811: } finally {
00812: this .codeContext.restoreLocalVariables();
00813: }
00814: }
00815:
00816: private boolean compile2(Java.DoStatement ds)
00817: throws CompileException {
00818: Object cvc = this .getConstantValue(ds.condition);
00819: if (cvc != null) {
00820: if (Boolean.TRUE.equals(cvc)) {
00821: this
00822: .warning(
00823: "DSTC",
00824: "Condition of DO statement is always TRUE; the proper way of declaring an unconditional loop is \"for (;;)\"",
00825: ds.getLocation());
00826: return this .compileUnconditionalLoop(ds, ds.body, null);
00827: } else {
00828: this .warning("DSNR", "DO statement never repeats", ds
00829: .getLocation());
00830: }
00831: }
00832:
00833: ds.whereToContinue = this .codeContext.new Offset();
00834: ds.bodyHasContinue = false;
00835:
00836: CodeContext.Offset bodyOffset = this .codeContext.newOffset();
00837:
00838: // Compile body.
00839: if (!this .compile(ds.body) && !ds.bodyHasContinue) {
00840: this .warning("DSNTC",
00841: "\"do\" statement never tests its condition", ds
00842: .getLocation());
00843: if (ds.whereToBreak == null)
00844: return false;
00845: ds.whereToBreak.set();
00846: return true;
00847: }
00848:
00849: // Compile condition.
00850: ds.whereToContinue.set();
00851: this .compileBoolean(ds.condition, bodyOffset,
00852: Java.Rvalue.JUMP_IF_TRUE);
00853:
00854: if (ds.whereToBreak != null)
00855: ds.whereToBreak.set();
00856:
00857: return true;
00858: }
00859:
00860: private boolean compile2(Java.ForStatement fs)
00861: throws CompileException {
00862: this .codeContext.saveLocalVariables();
00863: try {
00864:
00865: // Compile init.
00866: if (fs.optionalInit != null)
00867: this .compile(fs.optionalInit);
00868:
00869: if (fs.optionalCondition == null) {
00870: return this .compileUnconditionalLoop(fs, fs.body,
00871: fs.optionalUpdate);
00872: } else {
00873: Object cvc = this
00874: .getConstantValue(fs.optionalCondition);
00875: if (cvc != null) {
00876: if (Boolean.TRUE.equals(cvc)) {
00877: this
00878: .warning(
00879: "FSTC",
00880: "Condition of FOR statement is always TRUE; the proper way of declaring an unconditional loop is \"for (;;)\"",
00881: fs.getLocation());
00882: return this .compileUnconditionalLoop(fs,
00883: fs.body, fs.optionalUpdate);
00884: } else {
00885: this .warning("FSNR",
00886: "FOR statement never repeats", fs
00887: .getLocation());
00888: }
00889: }
00890: }
00891:
00892: CodeContext.Offset toCondition = this .codeContext.new Offset();
00893: this .writeBranch(fs, Opcode.GOTO, toCondition);
00894:
00895: // Compile body.
00896: fs.whereToContinue = this .codeContext.new Offset();
00897: fs.bodyHasContinue = false;
00898: CodeContext.Offset bodyOffset = this .codeContext
00899: .newOffset();
00900: boolean bodyCCN = this .compile(fs.body);
00901:
00902: // Compile update.
00903: fs.whereToContinue.set();
00904: if (fs.optionalUpdate != null) {
00905: if (!bodyCCN && !fs.bodyHasContinue) {
00906: this .warning("FUUR", "For update is unreachable",
00907: fs.getLocation());
00908: } else {
00909: for (int i = 0; i < fs.optionalUpdate.length; ++i) {
00910: this .compile(fs.optionalUpdate[i]);
00911: }
00912: }
00913: }
00914:
00915: // Compile condition.
00916: toCondition.set();
00917: this .compileBoolean(fs.optionalCondition, bodyOffset,
00918: Java.Rvalue.JUMP_IF_TRUE);
00919: } finally {
00920: this .codeContext.restoreLocalVariables();
00921: }
00922:
00923: if (fs.whereToBreak != null)
00924: fs.whereToBreak.set();
00925:
00926: return true;
00927: }
00928:
00929: private boolean compile2(Java.WhileStatement ws)
00930: throws CompileException {
00931: Object cvc = this .getConstantValue(ws.condition);
00932: if (cvc != null) {
00933: if (Boolean.TRUE.equals(cvc)) {
00934: this
00935: .warning(
00936: "WSTC",
00937: "Condition of WHILE statement is always TRUE; the proper way of declaring an unconditional loop is \"for (;;)\"",
00938: ws.getLocation());
00939: return this .compileUnconditionalLoop(ws, ws.body, null);
00940: } else {
00941: this .warning("WSNR", "WHILE statement never repeats",
00942: ws.getLocation());
00943: }
00944: }
00945:
00946: ws.whereToContinue = this .codeContext.new Offset();
00947: ws.bodyHasContinue = false;
00948: this .writeBranch(ws, Opcode.GOTO, ws.whereToContinue);
00949:
00950: // Compile body.
00951: CodeContext.Offset bodyOffset = this .codeContext.newOffset();
00952: this .compile(ws.body); // Return value (CCN) is ignored.
00953:
00954: // Compile condition.
00955: ws.whereToContinue.set();
00956: this .compileBoolean(ws.condition, bodyOffset,
00957: Java.Rvalue.JUMP_IF_TRUE);
00958:
00959: if (ws.whereToBreak != null)
00960: ws.whereToBreak.set();
00961: return true;
00962: }
00963:
00964: private boolean compileUnconditionalLoop(
00965: Java.ContinuableStatement cs, Java.BlockStatement body,
00966: Java.Rvalue[] optionalUpdate) throws CompileException {
00967: if (optionalUpdate != null)
00968: return this .compileUnconditionalLoopWithUpdate(cs, body,
00969: optionalUpdate);
00970:
00971: cs.whereToContinue = this .codeContext.newOffset();
00972: cs.bodyHasContinue = false;
00973:
00974: // Compile body.
00975: if (this .compile(body))
00976: this .writeBranch(cs, Opcode.GOTO, cs.whereToContinue);
00977:
00978: if (cs.whereToBreak == null)
00979: return false;
00980: cs.whereToBreak.set();
00981: return true;
00982: }
00983:
00984: private boolean compileUnconditionalLoopWithUpdate(
00985: Java.ContinuableStatement cs, Java.BlockStatement body,
00986: Java.Rvalue[] update) throws CompileException {
00987: cs.whereToContinue = this .codeContext.new Offset();
00988: cs.bodyHasContinue = false;
00989:
00990: // Compile body.
00991: CodeContext.Offset bodyOffset = this .codeContext.newOffset();
00992: boolean bodyCCN = this .compile(body);
00993:
00994: // Compile the "update".
00995: cs.whereToContinue.set();
00996: if (!bodyCCN && !cs.bodyHasContinue) {
00997: this .warning("LUUR", "Loop update is unreachable",
00998: update[0].getLocation());
00999: } else {
01000: for (int i = 0; i < update.length; ++i)
01001: this .compile(update[i]);
01002: this .writeBranch(cs, Opcode.GOTO, bodyOffset);
01003: }
01004:
01005: if (cs.whereToBreak == null)
01006: return false;
01007: cs.whereToBreak.set();
01008: return true;
01009: }
01010:
01011: private final boolean compile2(Java.LabeledStatement ls)
01012: throws CompileException {
01013: boolean canCompleteNormally = this .compile(ls.body);
01014: if (ls.whereToBreak != null) {
01015: ls.whereToBreak.set();
01016: canCompleteNormally = true;
01017: }
01018: return canCompleteNormally;
01019: }
01020:
01021: private boolean compile2(Java.SwitchStatement ss)
01022: throws CompileException {
01023:
01024: // Compute condition.
01025: IClass switchExpressionType = this
01026: .compileGetValue(ss.condition);
01027: this .assignmentConversion((Java.Located) ss, // located
01028: switchExpressionType, // sourceType
01029: IClass.INT, // targetType
01030: null // optionalConstantValue
01031: );
01032:
01033: // Prepare the map of case labels to code offsets.
01034: TreeMap caseLabelMap = new TreeMap(); // Integer => Offset
01035: CodeContext.Offset defaultLabelOffset = null;
01036: CodeContext.Offset[] sbsgOffsets = new CodeContext.Offset[ss.sbsgs
01037: .size()];
01038: for (int i = 0; i < ss.sbsgs.size(); ++i) {
01039: Java.SwitchStatement.SwitchBlockStatementGroup sbsg = (Java.SwitchStatement.SwitchBlockStatementGroup) ss.sbsgs
01040: .get(i);
01041: sbsgOffsets[i] = this .codeContext.new Offset();
01042: for (int j = 0; j < sbsg.caseLabels.size(); ++j) {
01043:
01044: // Verify that case label value is a constant.
01045: Java.Rvalue rv = (Java.Rvalue) sbsg.caseLabels.get(j);
01046: Object cv = this .getConstantValue(rv);
01047: if (cv == null) {
01048: this
01049: .compileError(
01050: "Value of \"case\" label does not pose a constant value",
01051: rv.getLocation());
01052: cv = new Integer(99);
01053: }
01054:
01055: // Verify that case label is assignable to the type of the switch expression.
01056: IClass rvType = this .getType(rv);
01057: this .assignmentConversion((Java.Located) ss, // located
01058: rvType, // sourceType
01059: switchExpressionType, // targetType
01060: cv // optionalConstantValue
01061: );
01062:
01063: // Convert char, byte, short, int to "Integer".
01064: Integer civ;
01065: if (cv instanceof Integer) {
01066: civ = (Integer) cv;
01067: } else if (cv instanceof Number) {
01068: civ = new Integer(((Number) cv).intValue());
01069: } else if (cv instanceof Character) {
01070: civ = new Integer(((Character) cv).charValue());
01071: } else {
01072: this
01073: .compileError(
01074: "Value of case label must be a char, byte, short or int constant",
01075: rv.getLocation());
01076: civ = new Integer(99);
01077: }
01078:
01079: // Store in case label map.
01080: if (caseLabelMap.containsKey(civ))
01081: this .compileError(
01082: "Duplicate \"case\" switch label value", rv
01083: .getLocation());
01084: caseLabelMap.put(civ, sbsgOffsets[i]);
01085: }
01086: if (sbsg.hasDefaultLabel) {
01087: if (defaultLabelOffset != null)
01088: this .compileError(
01089: "Duplicate \"default\" switch label", sbsg
01090: .getLocation());
01091: defaultLabelOffset = sbsgOffsets[i];
01092: }
01093: }
01094: if (defaultLabelOffset == null)
01095: defaultLabelOffset = this .getWhereToBreak(ss);
01096:
01097: // Generate TABLESWITCH or LOOKUPSWITCH instruction.
01098: CodeContext.Offset switchOffset = this .codeContext.newOffset();
01099: if (caseLabelMap.isEmpty()) {
01100: // Special case: SWITCH statement without CASE labels (but maybe a DEFAULT label).
01101: ;
01102: } else if (((Integer) caseLabelMap.firstKey()).intValue()
01103: + caseLabelMap.size() >= // Beware of INT overflow!
01104: ((Integer) caseLabelMap.lastKey()).intValue()
01105: - caseLabelMap.size()) {
01106: int low = ((Integer) caseLabelMap.firstKey()).intValue();
01107: int high = ((Integer) caseLabelMap.lastKey()).intValue();
01108:
01109: this .writeByte(ss, Opcode.TABLESWITCH);
01110: new Java.Padder(this .codeContext).set();
01111: this .writeOffset(ss, switchOffset, defaultLabelOffset);
01112: this .writeInt(ss, low);
01113: this .writeInt(ss, high);
01114: Iterator si = caseLabelMap.entrySet().iterator();
01115: int cur = low;
01116: while (si.hasNext()) {
01117: Map.Entry me = (Map.Entry) si.next();
01118: int val = ((Integer) me.getKey()).intValue();
01119: while (cur < val) {
01120: this .writeOffset(ss, switchOffset,
01121: defaultLabelOffset);
01122: ++cur;
01123: }
01124: this .writeOffset(ss, switchOffset,
01125: (CodeContext.Offset) me.getValue());
01126: ++cur;
01127: }
01128: } else {
01129: this .writeOpcode(ss, Opcode.LOOKUPSWITCH);
01130: new Java.Padder(this .codeContext).set();
01131: this .writeOffset(ss, switchOffset, defaultLabelOffset);
01132: this .writeInt(ss, caseLabelMap.size());
01133: Iterator si = caseLabelMap.entrySet().iterator();
01134: while (si.hasNext()) {
01135: Map.Entry me = (Map.Entry) si.next();
01136: this .writeInt(ss, ((Integer) me.getKey()).intValue());
01137: this .writeOffset(ss, switchOffset,
01138: (CodeContext.Offset) me.getValue());
01139: }
01140: }
01141:
01142: // Compile statement groups.
01143: boolean canCompleteNormally = true;
01144: for (int i = 0; i < ss.sbsgs.size(); ++i) {
01145: Java.SwitchStatement.SwitchBlockStatementGroup sbsg = (Java.SwitchStatement.SwitchBlockStatementGroup) ss.sbsgs
01146: .get(i);
01147: sbsgOffsets[i].set();
01148: canCompleteNormally = true;
01149: for (int j = 0; j < sbsg.blockStatements.size(); ++j) {
01150: Java.BlockStatement bs = (Java.BlockStatement) sbsg.blockStatements
01151: .get(j);
01152: if (!canCompleteNormally) {
01153: this .compileError("Statement is unreachable", bs
01154: .getLocation());
01155: break;
01156: }
01157: canCompleteNormally = this .compile(bs);
01158: }
01159: }
01160: if (ss.whereToBreak != null) {
01161: ss.whereToBreak.set();
01162: canCompleteNormally = true;
01163: }
01164: return canCompleteNormally;
01165: }
01166:
01167: private boolean compile2(Java.BreakStatement bs)
01168: throws CompileException {
01169:
01170: // Find the broken statement.
01171: Java.BreakableStatement brokenStatement = null;
01172: if (bs.optionalLabel == null) {
01173: for (ScopeIterator si = new ScopeIterator(bs); si.hasNext();) {
01174: Java.Scope s = (Java.Scope) si.next();
01175: if (s instanceof Java.BreakableStatement) {
01176: brokenStatement = (Java.BreakableStatement) s;
01177: break;
01178: }
01179: }
01180: if (brokenStatement == null) {
01181: this
01182: .compileError(
01183: "\"break\" statement is not enclosed by a breakable statement",
01184: bs.getLocation());
01185: return false;
01186: }
01187: } else {
01188: for (ScopeIterator si = new ScopeIterator(bs); si.hasNext();) {
01189: Java.Scope s = (Java.Scope) si.next();
01190: if (s instanceof Java.LabeledStatement) {
01191: Java.LabeledStatement ls = (Java.LabeledStatement) s;
01192: if (ls.label.equals(bs.optionalLabel)) {
01193: brokenStatement = ls;
01194: break;
01195: }
01196: }
01197: }
01198: if (brokenStatement == null) {
01199: this
01200: .compileError(
01201: "Statement \"break "
01202: + bs.optionalLabel
01203: + "\" is not enclosed by a breakable statement with label \""
01204: + bs.optionalLabel + "\"", bs
01205: .getLocation());
01206: return false;
01207: }
01208: }
01209:
01210: this .leaveStatements(bs.getEnclosingScope(), // from
01211: brokenStatement.getEnclosingScope(), // to
01212: null // optionalStackValueType
01213: );
01214: this .writeBranch(bs, Opcode.GOTO, this
01215: .getWhereToBreak(brokenStatement));
01216: return false;
01217: }
01218:
01219: private boolean compile2(Java.ContinueStatement cs)
01220: throws CompileException {
01221:
01222: // Find the continued statement.
01223: Java.ContinuableStatement continuedStatement = null;
01224: if (cs.optionalLabel == null) {
01225: for (ScopeIterator si = new ScopeIterator(cs); si.hasNext();) {
01226: Java.Scope s = (Java.Scope) si.next();
01227: if (s instanceof Java.ContinuableStatement) {
01228: continuedStatement = (Java.ContinuableStatement) s;
01229: break;
01230: }
01231: }
01232: if (continuedStatement == null) {
01233: this
01234: .compileError(
01235: "\"continue\" statement is not enclosed by a continuable statement",
01236: cs.getLocation());
01237: return false;
01238: }
01239: } else {
01240: for (ScopeIterator si = new ScopeIterator(cs); si.hasNext();) {
01241: Java.Scope s = (Java.Scope) si.next();
01242: if (s instanceof Java.LabeledStatement) {
01243: Java.LabeledStatement ls = (Java.LabeledStatement) s;
01244: if (ls.label.equals(cs.optionalLabel)) {
01245: Java.Statement st = ls.body;
01246: while (st instanceof Java.LabeledStatement)
01247: st = ((Java.LabeledStatement) st).body;
01248: if (!(st instanceof Java.ContinuableStatement)) {
01249: this
01250: .compileError(
01251: "Labeled statement is not continuable",
01252: st.getLocation());
01253: return false;
01254: }
01255: continuedStatement = (Java.ContinuableStatement) st;
01256: break;
01257: }
01258: }
01259: }
01260: if (continuedStatement == null) {
01261: this
01262: .compileError(
01263: "Statement \"continue "
01264: + cs.optionalLabel
01265: + "\" is not enclosed by a continuable statement with label \""
01266: + cs.optionalLabel + "\"", cs
01267: .getLocation());
01268: return false;
01269: }
01270: }
01271:
01272: continuedStatement.bodyHasContinue = true;
01273: this .leaveStatements(cs.getEnclosingScope(), // from
01274: continuedStatement.getEnclosingScope(), // to
01275: null // optionalStackValueType
01276: );
01277: this .writeBranch(cs, Opcode.GOTO,
01278: continuedStatement.whereToContinue);
01279: return false;
01280: }
01281:
01282: private boolean compile2(Java.EmptyStatement es) {
01283: return true;
01284: }
01285:
01286: private boolean compile2(Java.ExpressionStatement ee)
01287: throws CompileException {
01288: this .compile(ee.rvalue);
01289: return true;
01290: }
01291:
01292: private boolean compile2(Java.FieldDeclaration fd)
01293: throws CompileException {
01294: for (int i = 0; i < fd.variableDeclarators.length; ++i) {
01295: Java.VariableDeclarator vd = fd.variableDeclarators[i];
01296:
01297: Java.ArrayInitializerOrRvalue initializer = this
01298: .getNonConstantFinalInitializer(fd, vd);
01299: if (initializer == null)
01300: continue;
01301:
01302: if ((fd.modifiers & Mod.STATIC) == 0) {
01303: this .writeOpcode(fd, Opcode.ALOAD_0);
01304: }
01305: IClass fieldType = this .getType(fd.type);
01306: if (initializer instanceof Java.Rvalue) {
01307: Java.Rvalue rvalue = (Java.Rvalue) initializer;
01308: IClass initializerType = this .compileGetValue(rvalue);
01309: fieldType = fieldType.getArrayIClass(vd.brackets,
01310: this .iClassLoader.OBJECT);
01311: this .assignmentConversion((Java.Located) fd, // located
01312: initializerType, // sourceType
01313: fieldType, // destinationType
01314: this .getConstantValue(rvalue) // optionalConstantValue
01315: );
01316: } else if (initializer instanceof Java.ArrayInitializer) {
01317: this .compileGetValue(
01318: (Java.ArrayInitializer) initializer, fieldType);
01319: } else {
01320: throw new RuntimeException(
01321: "Unexpected array initializer or rvalue class "
01322: + initializer.getClass().getName());
01323: }
01324:
01325: // No need to check accessibility here.
01326: ;
01327:
01328: if ((fd.modifiers & Mod.STATIC) != 0) {
01329: this .writeOpcode(fd, Opcode.PUTSTATIC);
01330: } else {
01331: this .writeOpcode(fd, Opcode.PUTFIELD);
01332: }
01333: this .writeConstantFieldrefInfo(fd, this .resolve(
01334: fd.getDeclaringType()).getDescriptor(), // classFD
01335: vd.name, // fieldName
01336: fieldType.getDescriptor() // fieldFD
01337: );
01338: }
01339: return true;
01340: }
01341:
01342: private boolean compile2(Java.IfStatement is)
01343: throws CompileException {
01344: Object cv = this .getConstantValue(is.condition);
01345: Java.BlockStatement es = (is.optionalElseStatement != null ? is.optionalElseStatement
01346: : new Java.EmptyStatement(is.thenStatement
01347: .getLocation()));
01348: if (cv instanceof Boolean) {
01349:
01350: // Constant condition.
01351: this .fakeCompile(is.condition);
01352: Java.BlockStatement seeingStatement, blindStatement;
01353: if (((Boolean) cv).booleanValue()) {
01354: seeingStatement = is.thenStatement;
01355: blindStatement = es;
01356: } else {
01357: seeingStatement = es;
01358: blindStatement = is.thenStatement;
01359: }
01360:
01361: // Compile the seeing statement.
01362: CodeContext.Inserter ins = this .codeContext.newInserter();
01363: boolean ssccn = this .compile(seeingStatement);
01364: if (ssccn)
01365: return true;
01366:
01367: // Hm... the "seeing statement" cannot complete normally. Things are getting
01368: // complicated here! The robust solution is to compile the constant-condition-IF
01369: // statement as a non-constant-condition-IF statement. As an optimization, iff the
01370: // IF-statement is enclosed ONLY by blocks, then the remaining bytecode can be
01371: // written to a "fake" code context, i.e. be thrown away.
01372:
01373: // Constant-condition-IF statement only enclosed by blocks?
01374: Java.Scope s = is.getEnclosingScope();
01375: while (s instanceof Java.Block)
01376: s = s.getEnclosingScope();
01377: if (s instanceof Java.FunctionDeclarator) {
01378:
01379: // Yes, compile rest of method to /dev/null.
01380: throw UnitCompiler.STOP_COMPILING_CODE;
01381: } else {
01382:
01383: // Compile constant-condition-IF statement as non-constant-condition-IF statement.
01384: CodeContext.Offset off = this .codeContext.newOffset();
01385: this .codeContext.pushInserter(ins);
01386: try {
01387: this .pushConstant(is, new Integer(0));
01388: this .writeBranch(Opcode.IFNE, off);
01389: } finally {
01390: this .codeContext.popInserter();
01391: }
01392: }
01393: return this .compile(blindStatement);
01394: }
01395:
01396: // Non-constant condition.
01397: if (this .generatesCode(is.thenStatement)) {
01398: if (this .generatesCode(es)) {
01399:
01400: // if (expr) stmt else stmt
01401: CodeContext.Offset eso = this .codeContext.new Offset();
01402: CodeContext.Offset end = this .codeContext.new Offset();
01403: this .compileBoolean(is.condition, eso,
01404: Java.Rvalue.JUMP_IF_FALSE);
01405: boolean tsccn = this .compile(is.thenStatement);
01406: if (tsccn)
01407: this .writeBranch(Opcode.GOTO, end);
01408: eso.set();
01409: boolean esccn = this .compile(es);
01410: end.set();
01411: return tsccn || esccn;
01412: } else {
01413:
01414: // if (expr) stmt else ;
01415: CodeContext.Offset end = this .codeContext.new Offset();
01416: this .compileBoolean(is.condition, end,
01417: Java.Rvalue.JUMP_IF_FALSE);
01418: this .compile(is.thenStatement);
01419: end.set();
01420: return true;
01421: }
01422: } else {
01423: if (this .generatesCode(es)) {
01424:
01425: // if (expr) ; else stmt
01426: CodeContext.Offset end = this .codeContext.new Offset();
01427: this .compileBoolean(is.condition, end,
01428: Java.Rvalue.JUMP_IF_TRUE);
01429: this .compile(es);
01430: end.set();
01431: return true;
01432: } else {
01433:
01434: // if (expr) ; else ;
01435: IClass conditionType = this
01436: .compileGetValue(is.condition);
01437: if (conditionType != IClass.BOOLEAN)
01438: this .compileError("Not a boolean expression", is
01439: .getLocation());
01440: this .pop((Java.Located) is, conditionType);
01441: return true;
01442: }
01443: }
01444: }
01445:
01446: private static final RuntimeException STOP_COMPILING_CODE = new RuntimeException(
01447: "SNO: This exception should have been caught and processed");
01448:
01449: private boolean compile2(Java.LocalClassDeclarationStatement lcds)
01450: throws CompileException {
01451:
01452: // Check for redefinition.
01453: Java.LocalClassDeclaration otherLCD = this
01454: .findLocalClassDeclaration(lcds, lcds.lcd.name);
01455: if (otherLCD != null)
01456: this .compileError("Redeclaration of local class \""
01457: + lcds.lcd.name + "\"; previously declared in "
01458: + otherLCD.getLocation());
01459:
01460: this .compile(lcds.lcd);
01461: return true;
01462: }
01463:
01464: /**
01465: * Find a local class declared in any block enclosing the given block statement.
01466: */
01467: private Java.LocalClassDeclaration findLocalClassDeclaration(
01468: Java.Scope s, String name) {
01469: for (;;) {
01470: Java.Scope es = s.getEnclosingScope();
01471: if (es instanceof Java.CompilationUnit)
01472: break;
01473: if (s instanceof Java.BlockStatement
01474: && es instanceof Java.Block) {
01475: Java.BlockStatement bs = (Java.BlockStatement) s;
01476: Java.Block b = (Java.Block) es;
01477: for (Iterator it = b.statements.iterator();;) {
01478: Java.BlockStatement bs2 = (Java.BlockStatement) it
01479: .next();
01480: if (bs2 == bs)
01481: break;
01482: if (bs2 instanceof Java.LocalClassDeclarationStatement) {
01483: Java.LocalClassDeclarationStatement lcds = ((Java.LocalClassDeclarationStatement) bs2);
01484: if (lcds.lcd.name.equals(name))
01485: return lcds.lcd;
01486: }
01487: }
01488: }
01489: s = es;
01490: }
01491: return null;
01492: }
01493:
01494: private boolean compile2(Java.LocalVariableDeclarationStatement lvds)
01495: throws CompileException {
01496: if ((lvds.modifiers & ~Mod.FINAL) != 0)
01497: this
01498: .compileError(
01499: "The only allowed modifier in local variable declarations is \"final\"",
01500: lvds.getLocation());
01501:
01502: for (int j = 0; j < lvds.variableDeclarators.length; ++j) {
01503: Java.VariableDeclarator vd = lvds.variableDeclarators[j];
01504:
01505: Java.LocalVariable lv = this .getLocalVariable(lvds, vd);
01506:
01507: // Check for local variable redefinition.
01508: if (this .findLocalVariable((Java.BlockStatement) lvds,
01509: vd.name) != lv)
01510: this .compileError("Redefinition of local variable \""
01511: + vd.name + "\" ", vd.getLocation());
01512:
01513: lv.localVariableArrayIndex = this .codeContext
01514: .allocateLocalVariable(Descriptor.size(lv.type
01515: .getDescriptor()));
01516:
01517: if (vd.optionalInitializer != null) {
01518: if (vd.optionalInitializer instanceof Java.Rvalue) {
01519: Java.Rvalue rhs = (Java.Rvalue) vd.optionalInitializer;
01520: this .assignmentConversion((Java.Located) lvds, // located
01521: this .compileGetValue(rhs), // sourceType
01522: lv.type, // targetType
01523: this .getConstantValue(rhs) // optionalConstantValue
01524: );
01525: } else if (vd.optionalInitializer instanceof Java.ArrayInitializer) {
01526: this
01527: .compileGetValue(
01528: (Java.ArrayInitializer) vd.optionalInitializer,
01529: lv.type);
01530: } else {
01531: throw new RuntimeException(
01532: "Unexpected rvalue or array initialized class "
01533: + vd.optionalInitializer.getClass()
01534: .getName());
01535: }
01536: this .store((Java.Located) lvds, // located
01537: lv.type, // valueType
01538: lv // localVariable
01539: );
01540: }
01541: }
01542: return true;
01543: }
01544:
01545: public Java.LocalVariable getLocalVariable(
01546: Java.LocalVariableDeclarationStatement lvds,
01547: Java.VariableDeclarator vd) throws CompileException {
01548: if (vd.localVariable == null) {
01549:
01550: // Determine variable type.
01551: Java.Type variableType = lvds.type;
01552: for (int k = 0; k < vd.brackets; ++k)
01553: variableType = new Java.ArrayType(variableType);
01554:
01555: vd.localVariable = new Java.LocalVariable(
01556: (lvds.modifiers & Mod.FINAL) != 0, this
01557: .getType(variableType));
01558: }
01559: return vd.localVariable;
01560: }
01561:
01562: private boolean compile2(Java.ReturnStatement rs)
01563: throws CompileException {
01564:
01565: // Determine enclosing block, function and compilation Unit.
01566: Java.FunctionDeclarator enclosingFunction = null;
01567: {
01568: Java.Scope s = rs.getEnclosingScope();
01569: for (s = s.getEnclosingScope(); s instanceof Java.Statement
01570: || s instanceof Java.CatchClause; s = s
01571: .getEnclosingScope())
01572: ;
01573: enclosingFunction = (Java.FunctionDeclarator) s;
01574: }
01575:
01576: IClass returnType = this .getReturnType(enclosingFunction);
01577: if (returnType == IClass.VOID) {
01578: if (rs.optionalReturnValue != null)
01579: this .compileError("Method must not return a value", rs
01580: .getLocation());
01581: this .leaveStatements(rs.getEnclosingScope(), // from
01582: enclosingFunction, // to
01583: null // optionalStackValueType
01584: );
01585: this .writeOpcode(rs, Opcode.RETURN);
01586: return false;
01587: }
01588: if (rs.optionalReturnValue == null) {
01589: this .compileError("Method must return a value", rs
01590: .getLocation());
01591: return false;
01592: }
01593: IClass type = this .compileGetValue(rs.optionalReturnValue);
01594: this .assignmentConversion((Java.Located) rs, // located
01595: type, // sourceType
01596: returnType, // targetType
01597: this .getConstantValue(rs.optionalReturnValue) // optionalConstantValue
01598: );
01599:
01600: this .leaveStatements(rs.getEnclosingScope(), // from
01601: enclosingFunction, // to
01602: returnType // optionalStackValueType
01603: );
01604: this .writeOpcode(rs, Opcode.IRETURN + this .ilfda(returnType));
01605: return false;
01606: }
01607:
01608: private boolean compile2(Java.SynchronizedStatement ss)
01609: throws CompileException {
01610:
01611: // Evaluate monitor object expression.
01612: if (!this .iClassLoader.OBJECT.isAssignableFrom(this
01613: .compileGetValue(ss.expression)))
01614: this
01615: .compileError(
01616: "Monitor object of \"synchronized\" statement is not a subclass of \"Object\"",
01617: ss.getLocation());
01618:
01619: this .codeContext.saveLocalVariables();
01620: boolean canCompleteNormally = false;
01621: try {
01622:
01623: // Allocate a local variable for the monitor object.
01624: ss.monitorLvIndex = this .codeContext
01625: .allocateLocalVariable((short) 1);
01626:
01627: // Store the monitor object.
01628: this .writeOpcode(ss, Opcode.DUP);
01629: this .store((Java.Located) ss, this .iClassLoader.OBJECT,
01630: ss.monitorLvIndex);
01631:
01632: // Create lock on the monitor object.
01633: this .writeOpcode(ss, Opcode.MONITORENTER);
01634:
01635: // Compile the statement body.
01636: CodeContext.Offset monitorExitOffset = this .codeContext.new Offset();
01637: CodeContext.Offset beginningOfBody = this .codeContext
01638: .newOffset();
01639: canCompleteNormally = this .compile(ss.body);
01640: if (canCompleteNormally) {
01641: this .writeBranch(ss, Opcode.GOTO, monitorExitOffset);
01642: }
01643:
01644: // Generate the exception handler.
01645: CodeContext.Offset here = this .codeContext.newOffset();
01646: this .codeContext.addExceptionTableEntry(beginningOfBody, // startPC
01647: here, // endPC
01648: here, // handlerPC
01649: null // catchTypeFD
01650: );
01651: this .leave(ss, this .iClassLoader.THROWABLE);
01652: this .writeOpcode(ss, Opcode.ATHROW);
01653:
01654: // Unlock monitor object.
01655: if (canCompleteNormally) {
01656: monitorExitOffset.set();
01657: this .leave(ss, null);
01658: }
01659: } finally {
01660: this .codeContext.restoreLocalVariables();
01661: }
01662:
01663: return canCompleteNormally;
01664: }
01665:
01666: private boolean compile2(Java.ThrowStatement ts)
01667: throws CompileException {
01668: IClass expressionType = this .compileGetValue(ts.expression);
01669: this .checkThrownException((Java.Located) ts, // located
01670: expressionType, // type
01671: ts.getEnclosingScope() // scope
01672: );
01673: this .writeOpcode(ts, Opcode.ATHROW);
01674: return false;
01675: }
01676:
01677: private boolean compile2(Java.TryStatement ts)
01678: throws CompileException {
01679: if (ts.optionalFinally != null)
01680: ts.finallyOffset = this .codeContext.new Offset();
01681:
01682: CodeContext.Offset beginningOfBody = this .codeContext
01683: .newOffset();
01684: CodeContext.Offset afterStatement = this .codeContext.new Offset();
01685:
01686: this .codeContext.saveLocalVariables();
01687: try {
01688:
01689: // Allocate a LV for the JSR of the FINALLY clause.
01690: //
01691: // Notice:
01692: // For unclear reasons, this variable must not overlap with any of the body's
01693: // variables (although the body's variables are out of scope when it comes to the
01694: // FINALLY clause!?), otherwise you get
01695: // java.lang.VerifyError: ... Accessing value from uninitialized localvariable 4
01696: // See bug #56.
01697: short pcLVIndex = ts.optionalFinally != null ? this .codeContext
01698: .allocateLocalVariable((short) 1)
01699: : (short) 0;
01700:
01701: boolean canCompleteNormally = this .compile(ts.body);
01702: CodeContext.Offset afterBody = this .codeContext.newOffset();
01703: if (canCompleteNormally) {
01704: this .writeBranch(ts, Opcode.GOTO, afterStatement);
01705: }
01706:
01707: if (beginningOfBody.offset != afterBody.offset) { // Avoid zero-length exception table entries.
01708: this .codeContext.saveLocalVariables();
01709: try {
01710:
01711: // Allocate the "exception variable".
01712: short evi = this .codeContext
01713: .allocateLocalVariable((short) 1);
01714:
01715: for (int i = 0; i < ts.catchClauses.size(); ++i) {
01716: Java.CatchClause cc = (Java.CatchClause) ts.catchClauses
01717: .get(i);
01718: IClass caughtExceptionType = this
01719: .getType(cc.caughtException.type);
01720: this .codeContext.addExceptionTableEntry(
01721: beginningOfBody, // startPC
01722: afterBody, // endPC
01723: this .codeContext.newOffset(), // handlerPC
01724: caughtExceptionType.getDescriptor() // catchTypeFD
01725: );
01726: this .store((Java.Located) cc, // located
01727: caughtExceptionType, // lvType
01728: evi // lvIndex
01729: );
01730:
01731: // Kludge: Treat the exception variable like a local
01732: // variable of the catch clause body.
01733: UnitCompiler.this
01734: .getLocalVariable(cc.caughtException).localVariableArrayIndex = evi;
01735:
01736: if (this .compile(cc.body)) {
01737: canCompleteNormally = true;
01738: if (i < ts.catchClauses.size() - 1
01739: || ts.optionalFinally != null)
01740: this .writeBranch(cc, Opcode.GOTO,
01741: afterStatement);
01742: }
01743: }
01744: } finally {
01745: this .codeContext.restoreLocalVariables();
01746: }
01747: }
01748:
01749: if (ts.optionalFinally != null) {
01750: CodeContext.Offset here = this .codeContext.newOffset();
01751: this .codeContext.addExceptionTableEntry(
01752: beginningOfBody, // startPC
01753: here, // endPC
01754: here, // handlerPC
01755: null // catchTypeFD
01756: );
01757:
01758: this .codeContext.saveLocalVariables();
01759: try {
01760:
01761: // Save the exception object in an anonymous local variable.
01762: short evi = this .codeContext
01763: .allocateLocalVariable((short) 1);
01764: this .store((Java.Located) ts.optionalFinally, // located
01765: this .iClassLoader.OBJECT, // valueType
01766: evi // localVariableIndex
01767: );
01768: this .writeBranch(ts.optionalFinally, Opcode.JSR,
01769: ts.finallyOffset);
01770: this .load((Java.Located) ts.optionalFinally, // located
01771: this .iClassLoader.OBJECT, // valueType
01772: evi // localVariableIndex
01773: );
01774: this .writeOpcode(ts.optionalFinally, Opcode.ATHROW);
01775:
01776: // Compile the "finally" body.
01777: ts.finallyOffset.set();
01778: this .store((Java.Located) ts.optionalFinally, // located
01779: this .iClassLoader.OBJECT, // valueType
01780: pcLVIndex // localVariableIndex
01781: );
01782: if (this .compile(ts.optionalFinally)) {
01783: this
01784: .writeOpcode(ts.optionalFinally,
01785: Opcode.RET);
01786: this .writeByte(ts.optionalFinally, pcLVIndex);
01787: }
01788: } finally {
01789:
01790: // The exception object local variable allocated above MUST NOT BE RELEASED
01791: // until after the FINALLY block is compiled, for otherwise you get
01792: // java.lang.VerifyError: ... Accessing value from uninitialized register 7
01793: this .codeContext.restoreLocalVariables();
01794: }
01795: }
01796:
01797: afterStatement.set();
01798: if (canCompleteNormally)
01799: this .leave(ts, null);
01800: return canCompleteNormally;
01801: } finally {
01802: this .codeContext.restoreLocalVariables();
01803: }
01804: }
01805:
01806: // ------------ FunctionDeclarator.compile() -------------
01807:
01808: private void compile(Java.FunctionDeclarator fd,
01809: final ClassFile classFile) throws CompileException {
01810: ClassFile.MethodInfo mi;
01811:
01812: if (Mod.isPrivateAccess(fd.modifiers)) {
01813: if (fd instanceof Java.MethodDeclarator && !fd.isStatic()) {
01814:
01815: // To make the non-static private method invocable for enclosing types, enclosed types
01816: // and types enclosed by the same type, it is modified as follows:
01817: // + Access is changed from PRIVATE to PACKAGE
01818: // + The name is appended with "$"
01819: // + It is made static
01820: // + A parameter of type "declaring class" is prepended to the signature
01821: mi = classFile.addMethodInfo((short) (Mod.changeAccess(
01822: fd.modifiers, Mod.PACKAGE) | Mod.STATIC), // accessFlags
01823: fd.name + '$', // name
01824: MethodDescriptor.prependParameter( // methodMD
01825: this .toIMethod(
01826: (Java.MethodDeclarator) fd)
01827: .getDescriptor(), this .resolve(
01828: fd.getDeclaringType())
01829: .getDescriptor()));
01830: } else {
01831:
01832: // To make the static private method or private constructor invocable for enclosing
01833: // types, enclosed types and types enclosed by the same type, it is modified as
01834: // follows:
01835: // + Access is changed from PRIVATE to PACKAGE
01836: mi = classFile.addMethodInfo(Mod.changeAccess(
01837: fd.modifiers, Mod.PACKAGE), // accessFlags
01838: fd.name, // name
01839: this .toIInvocable(fd).getDescriptor() // methodMD
01840: );
01841: }
01842: } else {
01843: mi = classFile.addMethodInfo(fd.modifiers, // accessFlags
01844: fd.name, // name
01845: this .toIInvocable(fd).getDescriptor() // methodMD
01846: );
01847: }
01848:
01849: // Add "Exceptions" attribute (JVMS 4.7.4).
01850: {
01851: final short eani = classFile
01852: .addConstantUtf8Info("Exceptions");
01853: short[] tecciis = new short[fd.thrownExceptions.length];
01854: for (int i = 0; i < fd.thrownExceptions.length; ++i) {
01855: tecciis[i] = classFile.addConstantClassInfo(this
01856: .getType(fd.thrownExceptions[i])
01857: .getDescriptor());
01858: }
01859: mi.addAttribute(new ClassFile.ExceptionsAttribute(eani,
01860: tecciis));
01861: }
01862:
01863: // Add "Deprecated" attribute (JVMS 4.7.10)
01864: if (fd.hasDeprecatedDocTag()) {
01865: mi.addAttribute(new ClassFile.DeprecatedAttribute(classFile
01866: .addConstantUtf8Info("Deprecated")));
01867: }
01868:
01869: if ((fd.modifiers & (Mod.ABSTRACT | Mod.NATIVE)) != 0)
01870: return;
01871:
01872: // Create CodeContext.
01873: final CodeContext codeContext = new CodeContext(mi
01874: .getClassFile());
01875:
01876: CodeContext savedCodeContext = this
01877: .replaceCodeContext(codeContext);
01878: try {
01879:
01880: // Define special parameter "this".
01881: if ((fd.modifiers & Mod.STATIC) == 0) {
01882: this .codeContext.allocateLocalVariable((short) 1);
01883: }
01884:
01885: if (fd instanceof Java.ConstructorDeclarator) {
01886: Java.ConstructorDeclarator constructorDeclarator = (Java.ConstructorDeclarator) fd;
01887:
01888: // Reserve space for synthetic parameters ("this$...", "val$...").
01889: for (Iterator it = constructorDeclarator
01890: .getDeclaringClass().syntheticFields.values()
01891: .iterator(); it.hasNext();) {
01892: IClass.IField sf = (IClass.IField) it.next();
01893: Java.LocalVariable lv = new Java.LocalVariable(
01894: true, sf.getType());
01895: lv.localVariableArrayIndex = this .codeContext
01896: .allocateLocalVariable(Descriptor.size(sf
01897: .getDescriptor()));
01898: constructorDeclarator.syntheticParameters.put(sf
01899: .getName(), lv);
01900: }
01901: }
01902:
01903: // Add function parameters.
01904: Set usedParameterNames = new HashSet();
01905: for (int i = 0; i < fd.formalParameters.length; ++i) {
01906: Java.FunctionDeclarator.FormalParameter fp = fd.formalParameters[i];
01907: if (usedParameterNames.contains(fp.name))
01908: this .compileError(
01909: "Redefinition of formal parameter \""
01910: + fp.name + "\"", fd.getLocation());
01911: Java.LocalVariable lv = UnitCompiler.this
01912: .getLocalVariable(fp);
01913: lv.localVariableArrayIndex = this .codeContext
01914: .allocateLocalVariable(Descriptor.size(lv.type
01915: .getDescriptor()));
01916: usedParameterNames.add(fp.name);
01917: }
01918:
01919: // Compile the constructor preamble.
01920: if (fd instanceof Java.ConstructorDeclarator) {
01921: Java.ConstructorDeclarator cd = (Java.ConstructorDeclarator) fd;
01922: if (cd.optionalConstructorInvocation != null) {
01923: this .compile(cd.optionalConstructorInvocation);
01924: if (cd.optionalConstructorInvocation instanceof Java.SuperConstructorInvocation) {
01925: this
01926: .assignSyntheticParametersToSyntheticFields(cd);
01927: this
01928: .initializeInstanceVariablesAndInvokeInstanceInitializers(cd);
01929: }
01930: } else {
01931:
01932: // Determine qualification for superconstructor invocation.
01933: Java.QualifiedThisReference qualification = null;
01934: IClass outerClassOfSuperclass = this .resolve(
01935: cd.getDeclaringClass()).getSuperclass()
01936: .getOuterIClass();
01937: if (outerClassOfSuperclass != null) {
01938: // qualification = new Java.QualifiedThisReference(
01939: // cd.getLocation(), // location
01940: // cd.getDeclaringClass(), // declaringClass
01941: // cd, // declaringTypeBodyDeclaration
01942: // outerClassOfSuperclass // targetIClass
01943: // );
01944: qualification = new Java.QualifiedThisReference(
01945: cd.getLocation(), // location
01946: new Java.SimpleType(
01947: // qualification
01948: cd.getLocation(),
01949: outerClassOfSuperclass));
01950: }
01951:
01952: // Invoke the superconstructor.
01953: Java.SuperConstructorInvocation sci = new Java.SuperConstructorInvocation(
01954: cd.getLocation(), // location
01955: qualification, // optionalQualification
01956: new Java.Rvalue[0] // arguments
01957: );
01958: sci.setEnclosingScope(fd);
01959: this .compile(sci);
01960: this .assignSyntheticParametersToSyntheticFields(cd);
01961: this
01962: .initializeInstanceVariablesAndInvokeInstanceInitializers(cd);
01963: }
01964: }
01965:
01966: // Compile the function body.
01967: try {
01968: boolean canCompleteNormally = this
01969: .compile(fd.optionalBody);
01970: if (canCompleteNormally) {
01971: if (this .getReturnType(fd) != IClass.VOID)
01972: this .compileError("Method must return a value",
01973: fd.getLocation());
01974: this .writeOpcode(fd, Opcode.RETURN);
01975: }
01976: } catch (RuntimeException ex) {
01977: if (ex != UnitCompiler.STOP_COMPILING_CODE)
01978: throw ex;
01979:
01980: // In very special circumstances (e.g. "if (true) return;"), code generation is
01981: // terminated abruptly by throwing STOP_COMPILING_CODE.
01982: ;
01983: }
01984: } finally {
01985: this .replaceCodeContext(savedCodeContext);
01986: }
01987:
01988: // Don't continue code attribute generation if we had compile errors.
01989: if (this .compileErrorCount > 0)
01990: return;
01991:
01992: // Fix up.
01993: codeContext.fixUp();
01994:
01995: // Relocate.
01996: codeContext.relocate();
01997:
01998: // Do flow analysis.
01999: if (UnitCompiler.DEBUG) {
02000: try {
02001: codeContext.flowAnalysis(fd.toString());
02002: } catch (RuntimeException ex) {
02003: ex.printStackTrace();
02004: ;
02005: }
02006: } else {
02007: codeContext.flowAnalysis(fd.toString());
02008: }
02009:
02010: // Add the code context as a code attribute to the MethodInfo.
02011: final short lntani = (this .debuggingInformation
02012: .contains(DebuggingInformation.LINES) ? classFile
02013: .addConstantUtf8Info("LineNumberTable") : (short) 0);
02014: mi.addAttribute(new ClassFile.AttributeInfo(classFile
02015: .addConstantUtf8Info("Code")) {
02016: protected void storeBody(DataOutputStream dos)
02017: throws IOException {
02018: codeContext.storeCodeAttributeBody(dos, lntani);
02019: }
02020: });
02021: }
02022:
02023: public Java.LocalVariable getLocalVariable(
02024: Java.FunctionDeclarator.FormalParameter fp)
02025: throws CompileException {
02026: if (fp.localVariable == null) {
02027: fp.localVariable = new Java.LocalVariable(fp.finaL, this
02028: .getType(fp.type));
02029: }
02030: return fp.localVariable;
02031: }
02032:
02033: // ------------------ Rvalue.compile() ----------------
02034:
02035: /**
02036: * Call to check whether the given {@link Java.Rvalue} compiles or not.
02037: */
02038: private void fakeCompile(Java.Rvalue rv) throws CompileException {
02039: CodeContext savedCodeContext = this .replaceCodeContext(this
02040: .createDummyCodeContext());
02041: try {
02042: this .compileContext(rv);
02043: this .compileGet(rv);
02044: } finally {
02045: this .replaceCodeContext(savedCodeContext);
02046: }
02047: }
02048:
02049: /**
02050: * Some {@link Java.Rvalue}s compile more efficiently when their value
02051: * is not needed, e.g. "i++".
02052: */
02053: private void compile(Java.Rvalue rv) throws CompileException {
02054: class UCE extends RuntimeException {
02055: final CompileException ce;
02056:
02057: UCE(CompileException ce) {
02058: this .ce = ce;
02059: }
02060: }
02061: Visitor.RvalueVisitor rvv = new Visitor.RvalueVisitor() {
02062: public void visitArrayLength(Java.ArrayLength al) {
02063: try {
02064: UnitCompiler.this .compile2(al);
02065: } catch (CompileException e) {
02066: throw new UCE(e);
02067: }
02068: }
02069:
02070: public void visitAssignment(Java.Assignment a) {
02071: try {
02072: UnitCompiler.this .compile2(a);
02073: } catch (CompileException e) {
02074: throw new UCE(e);
02075: }
02076: }
02077:
02078: public void visitUnaryOperation(Java.UnaryOperation uo) {
02079: try {
02080: UnitCompiler.this .compile2(uo);
02081: } catch (CompileException e) {
02082: throw new UCE(e);
02083: }
02084: }
02085:
02086: public void visitBinaryOperation(Java.BinaryOperation bo) {
02087: try {
02088: UnitCompiler.this .compile2(bo);
02089: } catch (CompileException e) {
02090: throw new UCE(e);
02091: }
02092: }
02093:
02094: public void visitCast(Java.Cast c) {
02095: try {
02096: UnitCompiler.this .compile2(c);
02097: } catch (CompileException e) {
02098: throw new UCE(e);
02099: }
02100: }
02101:
02102: public void visitClassLiteral(Java.ClassLiteral cl) {
02103: try {
02104: UnitCompiler.this .compile2(cl);
02105: } catch (CompileException e) {
02106: throw new UCE(e);
02107: }
02108: }
02109:
02110: public void visitConditionalExpression(
02111: Java.ConditionalExpression ce) {
02112: try {
02113: UnitCompiler.this .compile2(ce);
02114: } catch (CompileException e) {
02115: throw new UCE(e);
02116: }
02117: }
02118:
02119: public void visitConstantValue(Java.ConstantValue cv) {
02120: try {
02121: UnitCompiler.this .compile2(cv);
02122: } catch (CompileException e) {
02123: throw new UCE(e);
02124: }
02125: }
02126:
02127: public void visitCrement(Java.Crement c) {
02128: try {
02129: UnitCompiler.this .compile2(c);
02130: } catch (CompileException e) {
02131: throw new UCE(e);
02132: }
02133: }
02134:
02135: public void visitInstanceof(Java.Instanceof io) {
02136: try {
02137: UnitCompiler.this .compile2(io);
02138: } catch (CompileException e) {
02139: throw new UCE(e);
02140: }
02141: }
02142:
02143: public void visitMethodInvocation(Java.MethodInvocation mi) {
02144: try {
02145: UnitCompiler.this .compile2(mi);
02146: } catch (CompileException e) {
02147: throw new UCE(e);
02148: }
02149: }
02150:
02151: public void visitSuperclassMethodInvocation(
02152: Java.SuperclassMethodInvocation smi) {
02153: try {
02154: UnitCompiler.this .compile2(smi);
02155: } catch (CompileException e) {
02156: throw new UCE(e);
02157: }
02158: }
02159:
02160: public void visitLiteral(Java.Literal l) {
02161: try {
02162: UnitCompiler.this .compile2(l);
02163: } catch (CompileException e) {
02164: throw new UCE(e);
02165: }
02166: }
02167:
02168: public void visitNewAnonymousClassInstance(
02169: Java.NewAnonymousClassInstance naci) {
02170: try {
02171: UnitCompiler.this .compile2(naci);
02172: } catch (CompileException e) {
02173: throw new UCE(e);
02174: }
02175: }
02176:
02177: public void visitNewArray(Java.NewArray na) {
02178: try {
02179: UnitCompiler.this .compile2(na);
02180: } catch (CompileException e) {
02181: throw new UCE(e);
02182: }
02183: }
02184:
02185: public void visitNewInitializedArray(
02186: Java.NewInitializedArray nia) {
02187: try {
02188: UnitCompiler.this .compile2(nia);
02189: } catch (CompileException e) {
02190: throw new UCE(e);
02191: }
02192: }
02193:
02194: public void visitNewClassInstance(Java.NewClassInstance nci) {
02195: try {
02196: UnitCompiler.this .compile2(nci);
02197: } catch (CompileException e) {
02198: throw new UCE(e);
02199: }
02200: }
02201:
02202: public void visitParameterAccess(Java.ParameterAccess pa) {
02203: try {
02204: UnitCompiler.this .compile2(pa);
02205: } catch (CompileException e) {
02206: throw new UCE(e);
02207: }
02208: }
02209:
02210: public void visitQualifiedThisReference(
02211: Java.QualifiedThisReference qtr) {
02212: try {
02213: UnitCompiler.this .compile2(qtr);
02214: } catch (CompileException e) {
02215: throw new UCE(e);
02216: }
02217: }
02218:
02219: public void visitThisReference(Java.ThisReference tr) {
02220: try {
02221: UnitCompiler.this .compile2(tr);
02222: } catch (CompileException e) {
02223: throw new UCE(e);
02224: }
02225: }
02226:
02227: public void visitAmbiguousName(Java.AmbiguousName an) {
02228: try {
02229: UnitCompiler.this .compile2(an);
02230: } catch (CompileException e) {
02231: throw new UCE(e);
02232: }
02233: }
02234:
02235: public void visitArrayAccessExpression(
02236: Java.ArrayAccessExpression aae) {
02237: try {
02238: UnitCompiler.this .compile2(aae);
02239: } catch (CompileException e) {
02240: throw new UCE(e);
02241: }
02242: };
02243:
02244: public void visitFieldAccess(Java.FieldAccess fa) {
02245: try {
02246: UnitCompiler.this .compile2(fa);
02247: } catch (CompileException e) {
02248: throw new UCE(e);
02249: }
02250: }
02251:
02252: public void visitFieldAccessExpression(
02253: Java.FieldAccessExpression fae) {
02254: try {
02255: UnitCompiler.this .compile2(fae);
02256: } catch (CompileException e) {
02257: throw new UCE(e);
02258: }
02259: }
02260:
02261: public void visitLocalVariableAccess(
02262: Java.LocalVariableAccess lva) {
02263: try {
02264: UnitCompiler.this .compile2(lva);
02265: } catch (CompileException e) {
02266: throw new UCE(e);
02267: }
02268: }
02269:
02270: public void visitParenthesizedExpression(
02271: Java.ParenthesizedExpression pe) {
02272: try {
02273: UnitCompiler.this .compile2(pe);
02274: } catch (CompileException e) {
02275: throw new UCE(e);
02276: }
02277: }
02278: };
02279: try {
02280: rv.accept(rvv);
02281: } catch (UCE uce) {
02282: throw uce.ce;
02283: }
02284: }
02285:
02286: private void compile2(Java.Rvalue rv) throws CompileException {
02287: this .pop((Java.Located) rv, this .compileGetValue(rv));
02288: }
02289:
02290: private void compile2(Java.Assignment a) throws CompileException {
02291: if (a.operator == "=") {
02292: this .compileContext(a.lhs);
02293: this .assignmentConversion((Java.Located) a, // located
02294: this .compileGetValue(a.rhs), // sourceType
02295: this .getType(a.lhs), // targetType
02296: this .getConstantValue(a.rhs) // optionalConstantValue
02297: );
02298: this .compileSet(a.lhs);
02299: return;
02300: }
02301:
02302: // Implement "|= ^= &= *= /= %= += -= <<= >>= >>>=".
02303: int lhsCS = this .compileContext(a.lhs);
02304: this .dup((Java.Located) a, lhsCS);
02305: IClass lhsType = this .compileGet(a.lhs);
02306: IClass resultType = this .compileArithmeticBinaryOperation(
02307: (Java.Located) a, // located
02308: lhsType, // lhsType
02309: a.operator.substring( // operator
02310: 0, a.operator.length() - 1).intern(), // <= IMPORTANT!
02311: a.rhs // rhs
02312: );
02313: // Convert the result to LHS type (JLS2 15.26.2).
02314: if (!this .tryIdentityConversion(resultType, lhsType)
02315: && !this .tryNarrowingPrimitiveConversion(
02316: (Java.Located) a, // located
02317: resultType, // sourceType
02318: lhsType // destinationType
02319: ))
02320: throw new RuntimeException("SNO: \"" + a.operator
02321: + "\" reconversion failed");
02322: this .compileSet(a.lhs);
02323: }
02324:
02325: private void compile2(Java.Crement c) throws CompileException {
02326:
02327: // Optimized crement of integer local variable.
02328: Java.LocalVariable lv = this .isIntLV(c);
02329: if (lv != null) {
02330: this .writeOpcode(c, Opcode.IINC);
02331: this .writeByte(c, lv.localVariableArrayIndex);
02332: this .writeByte(c, c.operator == "++" ? 1 : -1);
02333: return;
02334: }
02335:
02336: int cs = this .compileContext(c.operand);
02337: this .dup((Java.Located) c, cs);
02338: IClass type = this .compileGet(c.operand);
02339: IClass promotedType = this .unaryNumericPromotion(
02340: (Java.Located) c, type);
02341: this .writeOpcode(c, UnitCompiler.ilfd(promotedType,
02342: Opcode.ICONST_1, Opcode.LCONST_1, Opcode.FCONST_1,
02343: Opcode.DCONST_1));
02344: if (c.operator == "++") {
02345: this .writeOpcode(c, Opcode.IADD
02346: + UnitCompiler.ilfd(promotedType));
02347: } else if (c.operator == "--") {
02348: this .writeOpcode(c, Opcode.ISUB
02349: + UnitCompiler.ilfd(promotedType));
02350: } else {
02351: this .compileError("Unexpected operator \"" + c.operator
02352: + "\"", c.getLocation());
02353: }
02354:
02355: if (!this .tryIdentityConversion(promotedType, type)
02356: && !this .tryNarrowingPrimitiveConversion(
02357: (Java.Located) c, // located
02358: promotedType, // sourceType
02359: type // targetType
02360: ))
02361: throw new RuntimeException("SNO: \"" + c.operator
02362: + "\" reconversion failed");
02363: this .compileSet(c.operand);
02364: }
02365:
02366: private void compile2(Java.ParenthesizedExpression pe)
02367: throws CompileException {
02368: this .compile(pe.value);
02369: }
02370:
02371: private boolean compile2(Java.AlternateConstructorInvocation aci)
02372: throws CompileException {
02373: this .writeOpcode(aci, Opcode.ALOAD_0);
02374: Java.ConstructorDeclarator declaringConstructor = (Java.ConstructorDeclarator) aci
02375: .getEnclosingScope();
02376: this .invokeConstructor((Java.Located) aci, // located
02377: (Java.Scope) declaringConstructor, // scope
02378: (Java.Rvalue) null, // optionalEnclosingInstance
02379: this .resolve(declaringConstructor.getDeclaringClass()), // targetClass
02380: aci.arguments // arguments
02381: );
02382: return true;
02383: }
02384:
02385: private boolean compile2(Java.SuperConstructorInvocation sci)
02386: throws CompileException {
02387: Java.ConstructorDeclarator declaringConstructor = (Java.ConstructorDeclarator) sci
02388: .getEnclosingScope();
02389: this .writeOpcode(sci, Opcode.ALOAD_0);
02390: Java.ClassDeclaration declaringClass = declaringConstructor
02391: .getDeclaringClass();
02392: IClass super class = this .resolve(declaringClass)
02393: .getSuperclass();
02394:
02395: Java.Rvalue optionalEnclosingInstance;
02396: if (sci.optionalQualification != null) {
02397: optionalEnclosingInstance = sci.optionalQualification;
02398: } else {
02399: IClass outerIClassOfSuperclass = super class
02400: .getOuterIClass();
02401: if (outerIClassOfSuperclass == null) {
02402: optionalEnclosingInstance = null;
02403: } else {
02404: // optionalEnclosingInstance = new Java.QualifiedThisReference(
02405: // sci.getLocation(), // location
02406: // declaringClass, // declaringClass
02407: // declaringConstructor, // declaringTypeBodyDeclaration
02408: // outerIClassOfSuperclass // targetClass
02409: // );
02410: optionalEnclosingInstance = new Java.QualifiedThisReference(
02411: sci.getLocation(), // location
02412: new Java.SimpleType(
02413: // qualification
02414: sci.getLocation(),
02415: outerIClassOfSuperclass));
02416: optionalEnclosingInstance
02417: .setEnclosingBlockStatement(sci);
02418: }
02419: }
02420: this .invokeConstructor((Java.Located) sci, // located
02421: (Java.Scope) declaringConstructor, // scope
02422: optionalEnclosingInstance, // optionalEnclosingInstance
02423: super class, // targetClass
02424: sci.arguments // arguments
02425: );
02426: return true;
02427: }
02428:
02429: /**
02430: * Some {@link Java.Rvalue}s compile more efficiently when their value is the
02431: * condition for a branch.<br>
02432: *
02433: * Notice that if "this" is a constant, then either "dst" is never
02434: * branched to, or it is unconditionally branched to. "Unexamined code"
02435: * errors may result during bytecode validation.
02436: */
02437: private void compileBoolean(Java.Rvalue rv,
02438: final CodeContext.Offset dst, // Where to jump.
02439: final boolean orientation // JUMP_IF_TRUE or JUMP_IF_FALSE.
02440: ) throws CompileException {
02441: class UCE extends RuntimeException {
02442: final CompileException ce;
02443:
02444: UCE(CompileException ce) {
02445: this .ce = ce;
02446: }
02447: }
02448: Visitor.RvalueVisitor rvv = new Visitor.RvalueVisitor() {
02449: public void visitArrayLength(Java.ArrayLength al) {
02450: try {
02451: UnitCompiler.this .compileBoolean2(al, dst,
02452: orientation);
02453: } catch (CompileException e) {
02454: throw new UCE(e);
02455: }
02456: }
02457:
02458: public void visitAssignment(Java.Assignment a) {
02459: try {
02460: UnitCompiler.this .compileBoolean2(a, dst,
02461: orientation);
02462: } catch (CompileException e) {
02463: throw new UCE(e);
02464: }
02465: }
02466:
02467: public void visitUnaryOperation(Java.UnaryOperation uo) {
02468: try {
02469: UnitCompiler.this .compileBoolean2(uo, dst,
02470: orientation);
02471: } catch (CompileException e) {
02472: throw new UCE(e);
02473: }
02474: }
02475:
02476: public void visitBinaryOperation(Java.BinaryOperation bo) {
02477: try {
02478: UnitCompiler.this .compileBoolean2(bo, dst,
02479: orientation);
02480: } catch (CompileException e) {
02481: throw new UCE(e);
02482: }
02483: }
02484:
02485: public void visitCast(Java.Cast c) {
02486: try {
02487: UnitCompiler.this .compileBoolean2(c, dst,
02488: orientation);
02489: } catch (CompileException e) {
02490: throw new UCE(e);
02491: }
02492: }
02493:
02494: public void visitClassLiteral(Java.ClassLiteral cl) {
02495: try {
02496: UnitCompiler.this .compileBoolean2(cl, dst,
02497: orientation);
02498: } catch (CompileException e) {
02499: throw new UCE(e);
02500: }
02501: }
02502:
02503: public void visitConditionalExpression(
02504: Java.ConditionalExpression ce) {
02505: try {
02506: UnitCompiler.this .compileBoolean2(ce, dst,
02507: orientation);
02508: } catch (CompileException e) {
02509: throw new UCE(e);
02510: }
02511: }
02512:
02513: public void visitConstantValue(Java.ConstantValue cv) {
02514: try {
02515: UnitCompiler.this .compileBoolean2(cv, dst,
02516: orientation);
02517: } catch (CompileException e) {
02518: throw new UCE(e);
02519: }
02520: }
02521:
02522: public void visitCrement(Java.Crement c) {
02523: try {
02524: UnitCompiler.this .compileBoolean2(c, dst,
02525: orientation);
02526: } catch (CompileException e) {
02527: throw new UCE(e);
02528: }
02529: }
02530:
02531: public void visitInstanceof(Java.Instanceof io) {
02532: try {
02533: UnitCompiler.this .compileBoolean2(io, dst,
02534: orientation);
02535: } catch (CompileException e) {
02536: throw new UCE(e);
02537: }
02538: }
02539:
02540: public void visitMethodInvocation(Java.MethodInvocation mi) {
02541: try {
02542: UnitCompiler.this .compileBoolean2(mi, dst,
02543: orientation);
02544: } catch (CompileException e) {
02545: throw new UCE(e);
02546: }
02547: }
02548:
02549: public void visitSuperclassMethodInvocation(
02550: Java.SuperclassMethodInvocation smi) {
02551: try {
02552: UnitCompiler.this .compileBoolean2(smi, dst,
02553: orientation);
02554: } catch (CompileException e) {
02555: throw new UCE(e);
02556: }
02557: }
02558:
02559: public void visitLiteral(Java.Literal l) {
02560: try {
02561: UnitCompiler.this .compileBoolean2(l, dst,
02562: orientation);
02563: } catch (CompileException e) {
02564: throw new UCE(e);
02565: }
02566: }
02567:
02568: public void visitNewAnonymousClassInstance(
02569: Java.NewAnonymousClassInstance naci) {
02570: try {
02571: UnitCompiler.this .compileBoolean2(naci, dst,
02572: orientation);
02573: } catch (CompileException e) {
02574: throw new UCE(e);
02575: }
02576: }
02577:
02578: public void visitNewArray(Java.NewArray na) {
02579: try {
02580: UnitCompiler.this .compileBoolean2(na, dst,
02581: orientation);
02582: } catch (CompileException e) {
02583: throw new UCE(e);
02584: }
02585: }
02586:
02587: public void visitNewInitializedArray(
02588: Java.NewInitializedArray nia) {
02589: try {
02590: UnitCompiler.this .compileBoolean2(nia, dst,
02591: orientation);
02592: } catch (CompileException e) {
02593: throw new UCE(e);
02594: }
02595: }
02596:
02597: public void visitNewClassInstance(Java.NewClassInstance nci) {
02598: try {
02599: UnitCompiler.this .compileBoolean2(nci, dst,
02600: orientation);
02601: } catch (CompileException e) {
02602: throw new UCE(e);
02603: }
02604: }
02605:
02606: public void visitParameterAccess(Java.ParameterAccess pa) {
02607: try {
02608: UnitCompiler.this .compileBoolean2(pa, dst,
02609: orientation);
02610: } catch (CompileException e) {
02611: throw new UCE(e);
02612: }
02613: }
02614:
02615: public void visitQualifiedThisReference(
02616: Java.QualifiedThisReference qtr) {
02617: try {
02618: UnitCompiler.this .compileBoolean2(qtr, dst,
02619: orientation);
02620: } catch (CompileException e) {
02621: throw new UCE(e);
02622: }
02623: }
02624:
02625: public void visitThisReference(Java.ThisReference tr) {
02626: try {
02627: UnitCompiler.this .compileBoolean2(tr, dst,
02628: orientation);
02629: } catch (CompileException e) {
02630: throw new UCE(e);
02631: }
02632: }
02633:
02634: public void visitAmbiguousName(Java.AmbiguousName an) {
02635: try {
02636: UnitCompiler.this .compileBoolean2(an, dst,
02637: orientation);
02638: } catch (CompileException e) {
02639: throw new UCE(e);
02640: }
02641: }
02642:
02643: public void visitArrayAccessExpression(
02644: Java.ArrayAccessExpression aae) {
02645: try {
02646: UnitCompiler.this .compileBoolean2(aae, dst,
02647: orientation);
02648: } catch (CompileException e) {
02649: throw new UCE(e);
02650: }
02651: };
02652:
02653: public void visitFieldAccess(Java.FieldAccess fa) {
02654: try {
02655: UnitCompiler.this .compileBoolean2(fa, dst,
02656: orientation);
02657: } catch (CompileException e) {
02658: throw new UCE(e);
02659: }
02660: }
02661:
02662: public void visitFieldAccessExpression(
02663: Java.FieldAccessExpression fae) {
02664: try {
02665: UnitCompiler.this .compileBoolean2(fae, dst,
02666: orientation);
02667: } catch (CompileException e) {
02668: throw new UCE(e);
02669: }
02670: }
02671:
02672: public void visitLocalVariableAccess(
02673: Java.LocalVariableAccess lva) {
02674: try {
02675: UnitCompiler.this .compileBoolean2(lva, dst,
02676: orientation);
02677: } catch (CompileException e) {
02678: throw new UCE(e);
02679: }
02680: }
02681:
02682: public void visitParenthesizedExpression(
02683: Java.ParenthesizedExpression pe) {
02684: try {
02685: UnitCompiler.this .compileBoolean2(pe, dst,
02686: orientation);
02687: } catch (CompileException e) {
02688: throw new UCE(e);
02689: }
02690: }
02691: };
02692: try {
02693: rv.accept(rvv);
02694: } catch (UCE uce) {
02695: throw uce.ce;
02696: }
02697: }
02698:
02699: private void compileBoolean2(Java.Rvalue rv,
02700: CodeContext.Offset dst, // Where to jump.
02701: boolean orientation // JUMP_IF_TRUE or JUMP_IF_FALSE.
02702: ) throws CompileException {
02703: if (this .compileGetValue(rv) != IClass.BOOLEAN)
02704: this .compileError("Not a boolean expression", rv
02705: .getLocation());
02706: this .writeBranch(rv,
02707: orientation == Java.Rvalue.JUMP_IF_TRUE ? Opcode.IFNE
02708: : Opcode.IFEQ, dst);
02709: }
02710:
02711: private void compileBoolean2(Java.UnaryOperation ue,
02712: CodeContext.Offset dst, // Where to jump.
02713: boolean orientation // JUMP_IF_TRUE or JUMP_IF_FALSE.
02714: ) throws CompileException {
02715: if (ue.operator == "!") {
02716: this .compileBoolean(ue.operand, dst, !orientation);
02717: return;
02718: }
02719:
02720: this .compileError("Boolean expression expected", ue
02721: .getLocation());
02722: }
02723:
02724: private void compileBoolean2(Java.BinaryOperation bo,
02725: CodeContext.Offset dst, // Where to jump.
02726: boolean orientation // JUMP_IF_TRUE or JUMP_IF_FALSE.
02727: ) throws CompileException {
02728:
02729: if (bo.op == "|" || bo.op == "^" || bo.op == "&") {
02730: this .compileBoolean2((Java.Rvalue) bo, dst, orientation);
02731: return;
02732: }
02733:
02734: if (bo.op == "||" || bo.op == "&&") {
02735: Object lhsCV = this .getConstantValue(bo.lhs);
02736: if (lhsCV instanceof Boolean) {
02737: if (((Boolean) lhsCV).booleanValue() ^ bo.op == "||") {
02738: // "true && a", "false || a"
02739: this
02740: .compileBoolean(
02741: bo.rhs,
02742: dst,
02743: Java.Rvalue.JUMP_IF_TRUE
02744: ^ orientation == Java.Rvalue.JUMP_IF_FALSE);
02745: } else {
02746: // "false && a", "true || a"
02747: this
02748: .compileBoolean(
02749: bo.lhs,
02750: dst,
02751: Java.Rvalue.JUMP_IF_TRUE
02752: ^ orientation == Java.Rvalue.JUMP_IF_FALSE);
02753: this .fakeCompile(bo.rhs);
02754: }
02755: return;
02756: }
02757: Object rhsCV = this .getConstantValue(bo.rhs);
02758: if (rhsCV instanceof Boolean) {
02759: if (((Boolean) rhsCV).booleanValue() ^ bo.op == "||") {
02760: // "a && true", "a || false"
02761: this
02762: .compileBoolean(
02763: bo.lhs,
02764: dst,
02765: Java.Rvalue.JUMP_IF_TRUE
02766: ^ orientation == Java.Rvalue.JUMP_IF_FALSE);
02767: } else {
02768: // "a && false", "a || true"
02769: this .pop((Java.Located) bo.lhs, this
02770: .compileGetValue(bo.lhs));
02771: this
02772: .compileBoolean(
02773: bo.rhs,
02774: dst,
02775: Java.Rvalue.JUMP_IF_TRUE
02776: ^ orientation == Java.Rvalue.JUMP_IF_FALSE);
02777: }
02778: return;
02779: }
02780: if (bo.op == "||"
02781: ^ orientation == Java.Rvalue.JUMP_IF_FALSE) {
02782: this
02783: .compileBoolean(
02784: bo.lhs,
02785: dst,
02786: Java.Rvalue.JUMP_IF_TRUE
02787: ^ orientation == Java.Rvalue.JUMP_IF_FALSE);
02788: this
02789: .compileBoolean(
02790: bo.rhs,
02791: dst,
02792: Java.Rvalue.JUMP_IF_TRUE
02793: ^ orientation == Java.Rvalue.JUMP_IF_FALSE);
02794: } else {
02795: CodeContext.Offset end = this .codeContext.new Offset();
02796: this
02797: .compileBoolean(
02798: bo.lhs,
02799: end,
02800: Java.Rvalue.JUMP_IF_FALSE
02801: ^ orientation == Java.Rvalue.JUMP_IF_FALSE);
02802: this
02803: .compileBoolean(
02804: bo.rhs,
02805: dst,
02806: Java.Rvalue.JUMP_IF_TRUE
02807: ^ orientation == Java.Rvalue.JUMP_IF_FALSE);
02808: end.set();
02809: }
02810: return;
02811: }
02812:
02813: if (bo.op == "==" || bo.op == "!=" || bo.op == "<="
02814: || bo.op == ">=" || bo.op == "<" || bo.op == ">") {
02815: int opIdx = (bo.op == "==" ? 0 : bo.op == "!=" ? 1
02816: : bo.op == "<" ? 2 : bo.op == ">=" ? 3
02817: : bo.op == ">" ? 4 : bo.op == "<=" ? 5
02818: : Integer.MIN_VALUE);
02819: if (orientation == Java.Rvalue.JUMP_IF_FALSE)
02820: opIdx ^= 1;
02821:
02822: // Comparison with "null".
02823: {
02824: boolean lhsIsNull = this .getConstantValue(bo.lhs) == Java.Rvalue.CONSTANT_VALUE_NULL;
02825: boolean rhsIsNull = this .getConstantValue(bo.rhs) == Java.Rvalue.CONSTANT_VALUE_NULL;
02826:
02827: if (lhsIsNull || rhsIsNull) {
02828: if (bo.op != "==" && bo.op != "!=")
02829: this .compileError("Operator \"" + bo.op
02830: + "\" not allowed on operand \"null\"",
02831: bo.getLocation());
02832:
02833: // null == x
02834: // x == null
02835: IClass ohsType = this
02836: .compileGetValue(lhsIsNull ? bo.rhs
02837: : bo.lhs);
02838: if (ohsType.isPrimitive())
02839: this .compileError(
02840: "Cannot compare \"null\" with primitive type \""
02841: + ohsType.toString() + "\"", bo
02842: .getLocation());
02843: this .writeBranch(bo, Opcode.IFNULL + opIdx, dst);
02844: return;
02845: }
02846: }
02847:
02848: IClass lhsType = this .compileGetValue(bo.lhs);
02849: CodeContext.Inserter convertLhsInserter = this .codeContext
02850: .newInserter();
02851: IClass rhsType = this .compileGetValue(bo.rhs);
02852:
02853: // 15.20.1 Numerical comparison.
02854: if (lhsType.isPrimitiveNumeric()
02855: && rhsType.isPrimitiveNumeric()) {
02856: IClass promotedType = this .binaryNumericPromotion(
02857: (Java.Located) bo, lhsType, convertLhsInserter,
02858: rhsType);
02859: if (promotedType == IClass.INT) {
02860: this .writeBranch(bo, Opcode.IF_ICMPEQ + opIdx, dst);
02861: } else if (promotedType == IClass.LONG) {
02862: this .writeOpcode(bo, Opcode.LCMP);
02863: this .writeBranch(bo, Opcode.IFEQ + opIdx, dst);
02864: } else if (promotedType == IClass.FLOAT) {
02865: this .writeOpcode(bo, Opcode.FCMPG);
02866: this .writeBranch(bo, Opcode.IFEQ + opIdx, dst);
02867: } else if (promotedType == IClass.DOUBLE) {
02868: this .writeOpcode(bo, Opcode.DCMPG);
02869: this .writeBranch(bo, Opcode.IFEQ + opIdx, dst);
02870: } else {
02871: throw new RuntimeException(
02872: "Unexpected promoted type \""
02873: + promotedType + "\"");
02874: }
02875: return;
02876: }
02877:
02878: // Boolean comparison.
02879: if (lhsType == IClass.BOOLEAN && rhsType == IClass.BOOLEAN) {
02880: if (bo.op != "==" && bo.op != "!=")
02881: this .compileError("Operator \"" + bo.op
02882: + "\" not allowed on boolean operands", bo
02883: .getLocation());
02884: this .writeBranch(bo, Opcode.IF_ICMPEQ + opIdx, dst);
02885: return;
02886: }
02887:
02888: // Reference comparison.
02889: // Note: Comparison with "null" is already handled above.
02890: if (!lhsType.isPrimitive() && !rhsType.isPrimitive()) {
02891: if (bo.op != "==" && bo.op != "!=")
02892: this .compileError("Operator \"" + bo.op
02893: + "\" not allowed on reference operands",
02894: bo.getLocation());
02895: this .writeBranch(bo, Opcode.IF_ACMPEQ + opIdx, dst);
02896: return;
02897: }
02898:
02899: this .compileError("Cannot compare types \"" + lhsType
02900: + "\" and \"" + rhsType + "\"", bo.getLocation());
02901: }
02902:
02903: this .compileError("Boolean expression expected", bo
02904: .getLocation());
02905: }
02906:
02907: private void compileBoolean2(Java.ParenthesizedExpression pe,
02908: CodeContext.Offset dst, boolean orientation)
02909: throws CompileException {
02910: this .compileBoolean(pe.value, dst, orientation);
02911: }
02912:
02913: /**
02914: * Generates code that determines the context of the {@link
02915: * Java.Rvalue} and puts it on the operand stack. Most expressions
02916: * do not have a "context", but some do. E.g. for "x[y]", the context
02917: * is "x, y". The bottom line is that for statements like "x[y] += 3"
02918: * the context is only evaluated once.
02919: *
02920: * @return The size of the context on the operand stack
02921: */
02922: private int compileContext(Java.Rvalue rv) throws CompileException {
02923: final int[] res = new int[1];
02924: class UCE extends RuntimeException {
02925: final CompileException ce;
02926:
02927: UCE(CompileException ce) {
02928: this .ce = ce;
02929: }
02930: }
02931: Visitor.RvalueVisitor rvv = new Visitor.RvalueVisitor() {
02932: public void visitArrayLength(Java.ArrayLength al) {
02933: try {
02934: res[0] = UnitCompiler.this .compileContext2(al);
02935: } catch (CompileException e) {
02936: throw new UCE(e);
02937: }
02938: }
02939:
02940: public void visitAssignment(Java.Assignment a) {
02941: res[0] = UnitCompiler.this .compileContext2(a);
02942: }
02943:
02944: public void visitUnaryOperation(Java.UnaryOperation uo) {
02945: res[0] = UnitCompiler.this .compileContext2(uo);
02946: }
02947:
02948: public void visitBinaryOperation(Java.BinaryOperation bo) {
02949: res[0] = UnitCompiler.this .compileContext2(bo);
02950: }
02951:
02952: public void visitCast(Java.Cast c) {
02953: res[0] = UnitCompiler.this .compileContext2(c);
02954: }
02955:
02956: public void visitClassLiteral(Java.ClassLiteral cl) {
02957: res[0] = UnitCompiler.this .compileContext2(cl);
02958: }
02959:
02960: public void visitConditionalExpression(
02961: Java.ConditionalExpression ce) {
02962: res[0] = UnitCompiler.this .compileContext2(ce);
02963: }
02964:
02965: public void visitConstantValue(Java.ConstantValue cv) {
02966: res[0] = UnitCompiler.this .compileContext2(cv);
02967: }
02968:
02969: public void visitCrement(Java.Crement c) {
02970: res[0] = UnitCompiler.this .compileContext2(c);
02971: }
02972:
02973: public void visitInstanceof(Java.Instanceof io) {
02974: res[0] = UnitCompiler.this .compileContext2(io);
02975: }
02976:
02977: public void visitMethodInvocation(Java.MethodInvocation mi) {
02978: res[0] = UnitCompiler.this .compileContext2(mi);
02979: }
02980:
02981: public void visitSuperclassMethodInvocation(
02982: Java.SuperclassMethodInvocation smi) {
02983: res[0] = UnitCompiler.this .compileContext2(smi);
02984: }
02985:
02986: public void visitLiteral(Java.Literal l) {
02987: res[0] = UnitCompiler.this .compileContext2(l);
02988: }
02989:
02990: public void visitNewAnonymousClassInstance(
02991: Java.NewAnonymousClassInstance naci) {
02992: res[0] = UnitCompiler.this .compileContext2(naci);
02993: }
02994:
02995: public void visitNewArray(Java.NewArray na) {
02996: res[0] = UnitCompiler.this .compileContext2(na);
02997: }
02998:
02999: public void visitNewInitializedArray(
03000: Java.NewInitializedArray nia) {
03001: res[0] = UnitCompiler.this .compileContext2(nia);
03002: }
03003:
03004: public void visitNewClassInstance(Java.NewClassInstance nci) {
03005: res[0] = UnitCompiler.this .compileContext2(nci);
03006: }
03007:
03008: public void visitParameterAccess(Java.ParameterAccess pa) {
03009: res[0] = UnitCompiler.this .compileContext2(pa);
03010: }
03011:
03012: public void visitQualifiedThisReference(
03013: Java.QualifiedThisReference qtr) {
03014: res[0] = UnitCompiler.this .compileContext2(qtr);
03015: }
03016:
03017: public void visitThisReference(Java.ThisReference tr) {
03018: res[0] = UnitCompiler.this .compileContext2(tr);
03019: }
03020:
03021: public void visitAmbiguousName(Java.AmbiguousName an) {
03022: try {
03023: res[0] = UnitCompiler.this .compileContext2(an);
03024: } catch (CompileException e) {
03025: throw new UCE(e);
03026: }
03027: }
03028:
03029: public void visitArrayAccessExpression(
03030: Java.ArrayAccessExpression aae) {
03031: try {
03032: res[0] = UnitCompiler.this .compileContext2(aae);
03033: } catch (CompileException e) {
03034: throw new UCE(e);
03035: }
03036: };
03037:
03038: public void visitFieldAccess(Java.FieldAccess fa) {
03039: try {
03040: res[0] = UnitCompiler.this .compileContext2(fa);
03041: } catch (CompileException e) {
03042: throw new UCE(e);
03043: }
03044: }
03045:
03046: public void visitFieldAccessExpression(
03047: Java.FieldAccessExpression fae) {
03048: try {
03049: res[0] = UnitCompiler.this .compileContext2(fae);
03050: } catch (CompileException e) {
03051: throw new UCE(e);
03052: }
03053: }
03054:
03055: public void visitLocalVariableAccess(
03056: Java.LocalVariableAccess lva) {
03057: res[0] = UnitCompiler.this .compileContext2(lva);
03058: }
03059:
03060: public void visitParenthesizedExpression(
03061: Java.ParenthesizedExpression pe) {
03062: try {
03063: res[0] = UnitCompiler.this .compileContext2(pe);
03064: } catch (CompileException e) {
03065: throw new UCE(e);
03066: }
03067: }
03068: };
03069: try {
03070: rv.accept(rvv);
03071: return res[0];
03072: } catch (UCE uce) {
03073: throw uce.ce;
03074: }
03075: }
03076:
03077: private int compileContext2(Java.Rvalue rv) {
03078: return 0;
03079: }
03080:
03081: private int compileContext2(Java.AmbiguousName an)
03082: throws CompileException {
03083: return this .compileContext(this .toRvalueOrCE(this
03084: .reclassify(an)));
03085: }
03086:
03087: private int compileContext2(Java.FieldAccess fa)
03088: throws CompileException {
03089: if (fa.field.isStatic()) {
03090: this .getType(this .toTypeOrCE(fa.lhs));
03091: return 0;
03092: } else {
03093: this .compileGetValue(this .toRvalueOrCE(fa.lhs));
03094: return 1;
03095: }
03096: }
03097:
03098: private int compileContext2(Java.ArrayLength al)
03099: throws CompileException {
03100: if (!this .compileGetValue(al.lhs).isArray())
03101: this .compileError(
03102: "Cannot determine length of non-array type", al
03103: .getLocation());
03104: return 1;
03105: }
03106:
03107: private int compileContext2(Java.ArrayAccessExpression aae)
03108: throws CompileException {
03109: IClass lhsType = this .compileGetValue(aae.lhs);
03110: if (!lhsType.isArray())
03111: this .compileError(
03112: "Subscript not allowed on non-array type \""
03113: + lhsType.toString() + "\"", aae
03114: .getLocation());
03115:
03116: IClass indexType = this .compileGetValue(aae.index);
03117: if (!this .tryIdentityConversion(indexType, IClass.INT)
03118: && !this .tryWideningPrimitiveConversion(
03119: (Java.Located) aae, // located
03120: indexType, // sourceType
03121: IClass.INT // targetType
03122: ))
03123: this .compileError("Index expression of type \"" + indexType
03124: + "\" cannot be widened to \"int\"", aae
03125: .getLocation());
03126:
03127: return 2;
03128: }
03129:
03130: private int compileContext2(Java.FieldAccessExpression fae)
03131: throws CompileException {
03132: this .determineValue(fae);
03133: return this .compileContext(fae.value);
03134: }
03135:
03136: private int compileContext2(Java.ParenthesizedExpression pe)
03137: throws CompileException {
03138: return this .compileContext(pe.value);
03139: }
03140:
03141: /**
03142: * Generates code that determines the value of the {@link Java.Rvalue}
03143: * and puts it on the operand stack. This method relies on that the
03144: * "context" of the {@link Java.Rvalue} is on top of the operand stack
03145: * (see {@link #compileContext(Java.Rvalue)}).
03146: *
03147: * @return The type of the {@link Java.Rvalue}
03148: */
03149: private IClass compileGet(Java.Rvalue rv) throws CompileException {
03150: final IClass[] res = new IClass[1];
03151: class UCE extends RuntimeException {
03152: final CompileException ce;
03153:
03154: UCE(CompileException ce) {
03155: this .ce = ce;
03156: }
03157: }
03158: Visitor.RvalueVisitor rvv = new Visitor.RvalueVisitor() {
03159: public void visitArrayLength(Java.ArrayLength al) {
03160: res[0] = UnitCompiler.this .compileGet2(al);
03161: }
03162:
03163: public void visitAssignment(Java.Assignment a) {
03164: try {
03165: res[0] = UnitCompiler.this .compileGet2(a);
03166: } catch (CompileException e) {
03167: throw new UCE(e);
03168: }
03169: }
03170:
03171: public void visitUnaryOperation(Java.UnaryOperation uo) {
03172: try {
03173: res[0] = UnitCompiler.this .compileGet2(uo);
03174: } catch (CompileException e) {
03175: throw new UCE(e);
03176: }
03177: }
03178:
03179: public void visitBinaryOperation(Java.BinaryOperation bo) {
03180: try {
03181: res[0] = UnitCompiler.this .compileGet2(bo);
03182: } catch (CompileException e) {
03183: throw new UCE(e);
03184: }
03185: }
03186:
03187: public void visitCast(Java.Cast c) {
03188: try {
03189: res[0] = UnitCompiler.this .compileGet2(c);
03190: } catch (CompileException e) {
03191: throw new UCE(e);
03192: }
03193: }
03194:
03195: public void visitClassLiteral(Java.ClassLiteral cl) {
03196: try {
03197: res[0] = UnitCompiler.this .compileGet2(cl);
03198: } catch (CompileException e) {
03199: throw new UCE(e);
03200: }
03201: }
03202:
03203: public void visitConditionalExpression(
03204: Java.ConditionalExpression ce) {
03205: try {
03206: res[0] = UnitCompiler.this .compileGet2(ce);
03207: } catch (CompileException e) {
03208: throw new UCE(e);
03209: }
03210: }
03211:
03212: public void visitConstantValue(Java.ConstantValue cv) {
03213: res[0] = UnitCompiler.this .compileGet2(cv);
03214: }
03215:
03216: public void visitCrement(Java.Crement c) {
03217: try {
03218: res[0] = UnitCompiler.this .compileGet2(c);
03219: } catch (CompileException e) {
03220: throw new UCE(e);
03221: }
03222: }
03223:
03224: public void visitInstanceof(Java.Instanceof io) {
03225: try {
03226: res[0] = UnitCompiler.this .compileGet2(io);
03227: } catch (CompileException e) {
03228: throw new UCE(e);
03229: }
03230: }
03231:
03232: public void visitMethodInvocation(Java.MethodInvocation mi) {
03233: try {
03234: res[0] = UnitCompiler.this .compileGet2(mi);
03235: } catch (CompileException e) {
03236: throw new UCE(e);
03237: }
03238: }
03239:
03240: public void visitSuperclassMethodInvocation(
03241: Java.SuperclassMethodInvocation smi) {
03242: try {
03243: res[0] = UnitCompiler.this .compileGet2(smi);
03244: } catch (CompileException e) {
03245: throw new UCE(e);
03246: }
03247: }
03248:
03249: public void visitLiteral(Java.Literal l) {
03250: try {
03251: res[0] = UnitCompiler.this .compileGet2(l);
03252: } catch (CompileException e) {
03253: throw new UCE(e);
03254: }
03255: }
03256:
03257: public void visitNewAnonymousClassInstance(
03258: Java.NewAnonymousClassInstance naci) {
03259: try {
03260: res[0] = UnitCompiler.this .compileGet2(naci);
03261: } catch (CompileException e) {
03262: throw new UCE(e);
03263: }
03264: }
03265:
03266: public void visitNewArray(Java.NewArray na) {
03267: try {
03268: res[0] = UnitCompiler.this .compileGet2(na);
03269: } catch (CompileException e) {
03270: throw new UCE(e);
03271: }
03272: }
03273:
03274: public void visitNewInitializedArray(
03275: Java.NewInitializedArray nia) {
03276: try {
03277: res[0] = UnitCompiler.this .compileGet2(nia);
03278: } catch (CompileException e) {
03279: throw new UCE(e);
03280: }
03281: }
03282:
03283: public void visitNewClassInstance(Java.NewClassInstance nci) {
03284: try {
03285: res[0] = UnitCompiler.this .compileGet2(nci);
03286: } catch (CompileException e) {
03287: throw new UCE(e);
03288: }
03289: }
03290:
03291: public void visitParameterAccess(Java.ParameterAccess pa) {
03292: try {
03293: res[0] = UnitCompiler.this .compileGet2(pa);
03294: } catch (CompileException e) {
03295: throw new UCE(e);
03296: }
03297: }
03298:
03299: public void visitQualifiedThisReference(
03300: Java.QualifiedThisReference qtr) {
03301: try {
03302: res[0] = UnitCompiler.this .compileGet2(qtr);
03303: } catch (CompileException e) {
03304: throw new UCE(e);
03305: }
03306: }
03307:
03308: public void visitThisReference(Java.ThisReference tr) {
03309: try {
03310: res[0] = UnitCompiler.this .compileGet2(tr);
03311: } catch (CompileException e) {
03312: throw new UCE(e);
03313: }
03314: }
03315:
03316: public void visitAmbiguousName(Java.AmbiguousName an) {
03317: try {
03318: res[0] = UnitCompiler.this .compileGet2(an);
03319: } catch (CompileException e) {
03320: throw new UCE(e);
03321: }
03322: }
03323:
03324: public void visitArrayAccessExpression(
03325: Java.ArrayAccessExpression aae) {
03326: try {
03327: res[0] = UnitCompiler.this .compileGet2(aae);
03328: } catch (CompileException e) {
03329: throw new UCE(e);
03330: }
03331: };
03332:
03333: public void visitFieldAccess(Java.FieldAccess fa) {
03334: try {
03335: res[0] = UnitCompiler.this .compileGet2(fa);
03336: } catch (CompileException e) {
03337: throw new UCE(e);
03338: }
03339: }
03340:
03341: public void visitFieldAccessExpression(
03342: Java.FieldAccessExpression fae) {
03343: try {
03344: res[0] = UnitCompiler.this .compileGet2(fae);
03345: } catch (CompileException e) {
03346: throw new UCE(e);
03347: }
03348: }
03349:
03350: public void visitLocalVariableAccess(
03351: Java.LocalVariableAccess lva) {
03352: res[0] = UnitCompiler.this .compileGet2(lva);
03353: }
03354:
03355: public void visitParenthesizedExpression(
03356: Java.ParenthesizedExpression pe) {
03357: try {
03358: res[0] = UnitCompiler.this .compileGet2(pe);
03359: } catch (CompileException e) {
03360: throw new UCE(e);
03361: }
03362: }
03363: };
03364: try {
03365: rv.accept(rvv);
03366: return res[0];
03367: } catch (UCE uce) {
03368: throw uce.ce;
03369: }
03370: }
03371:
03372: private IClass compileGet2(Java.BooleanRvalue brv)
03373: throws CompileException {
03374: CodeContext.Offset isTrue = this .codeContext.new Offset();
03375: this .compileBoolean(brv, isTrue, Java.Rvalue.JUMP_IF_TRUE);
03376: this .writeOpcode(brv, Opcode.ICONST_0);
03377: CodeContext.Offset end = this .codeContext.new Offset();
03378: this .writeBranch(brv, Opcode.GOTO, end);
03379: isTrue.set();
03380: this .writeOpcode(brv, Opcode.ICONST_1);
03381: end.set();
03382:
03383: return IClass.BOOLEAN;
03384: }
03385:
03386: private IClass compileGet2(Java.AmbiguousName an)
03387: throws CompileException {
03388: return this .compileGet(this .toRvalueOrCE(this .reclassify(an)));
03389: }
03390:
03391: private IClass compileGet2(Java.LocalVariableAccess lva) {
03392: return this .load((Java.Located) lva, lva.localVariable);
03393: }
03394:
03395: private IClass compileGet2(Java.FieldAccess fa)
03396: throws CompileException {
03397: this .checkAccessible(fa.field, fa.getEnclosingBlockStatement());
03398: if (fa.field.isStatic()) {
03399: this .writeOpcode(fa, Opcode.GETSTATIC);
03400: } else {
03401: this .writeOpcode(fa, Opcode.GETFIELD);
03402: }
03403: this .writeConstantFieldrefInfo(fa, fa.field
03404: .getDeclaringIClass().getDescriptor(), // classFD
03405: fa.field.getName(), // fieldName
03406: fa.field.getType().getDescriptor() // fieldFD
03407: );
03408: return fa.field.getType();
03409: }
03410:
03411: private IClass compileGet2(Java.ArrayLength al) {
03412: this .writeOpcode(al, Opcode.ARRAYLENGTH);
03413: return IClass.INT;
03414: }
03415:
03416: private IClass compileGet2(Java.ThisReference tr)
03417: throws CompileException {
03418: this .referenceThis((Java.Located) tr);
03419: return this .getIClass(tr);
03420: }
03421:
03422: private IClass compileGet2(Java.QualifiedThisReference qtr)
03423: throws CompileException {
03424: this .referenceThis((Java.Located) qtr, // located
03425: this .getDeclaringClass(qtr), // declaringClass
03426: this .getDeclaringTypeBodyDeclaration(qtr), // declaringTypeBodyDeclaration
03427: this .getTargetIClass(qtr) // targetIClass
03428: );
03429: return this .getTargetIClass(qtr);
03430: }
03431:
03432: private IClass compileGet2(Java.ClassLiteral cl)
03433: throws CompileException {
03434: Location loc = cl.getLocation();
03435: final IClassLoader icl = this .iClassLoader;
03436: IClass iClass = this .getType(cl.type);
03437:
03438: if (iClass.isPrimitive()) {
03439:
03440: // Primitive class literal.
03441: this .writeOpcode(cl, Opcode.GETSTATIC);
03442: String wrapperClassDescriptor = (iClass == IClass.VOID ? "Ljava/lang/Void;"
03443: : iClass == IClass.BYTE ? "Ljava/lang/Byte;"
03444: : iClass == IClass.CHAR ? "Ljava/lang/Character;"
03445: : iClass == IClass.DOUBLE ? "Ljava/lang/Double;"
03446: : iClass == IClass.FLOAT ? "Ljava/lang/Float;"
03447: : iClass == IClass.INT ? "Ljava/lang/Integer;"
03448: : iClass == IClass.LONG ? "Ljava/lang/Long;"
03449: : iClass == IClass.SHORT ? "Ljava/lang/Short;"
03450: : iClass == IClass.BOOLEAN ? "Ljava/lang/Boolean;"
03451: : null);
03452: if (wrapperClassDescriptor == null)
03453: throw new RuntimeException(
03454: "SNO: Unidentifiable primitive type \""
03455: + iClass + "\"");
03456:
03457: this .writeConstantFieldrefInfo(cl, wrapperClassDescriptor, // classFD
03458: "TYPE", // fieldName
03459: "Ljava/lang/Class;" // fieldFD
03460: );
03461: return icl.CLASS;
03462: }
03463:
03464: // Non-primitive class literal.
03465:
03466: Java.AbstractTypeDeclaration declaringType;
03467: for (Java.Scope s = cl.getEnclosingBlockStatement();; s = s
03468: .getEnclosingScope()) {
03469: if (s instanceof Java.TypeDeclaration) {
03470: declaringType = (Java.AbstractTypeDeclaration) s;
03471: break;
03472: }
03473: }
03474:
03475: // Check if synthetic method "static Class class$(String className)" is already
03476: // declared.
03477: boolean classDollarMethodDeclared = false;
03478: {
03479: for (Iterator it = declaringType.declaredMethods.iterator(); it
03480: .hasNext();) {
03481: Java.MethodDeclarator md = (Java.MethodDeclarator) it
03482: .next();
03483: if (md.name.equals("class$")) {
03484: classDollarMethodDeclared = true;
03485: break;
03486: }
03487: }
03488: }
03489: if (!classDollarMethodDeclared)
03490: this .declareClassDollarMethod(cl);
03491:
03492: // Determine the statics of the declaring class (this is where static fields
03493: // declarations are found).
03494: List statics; // TypeBodyDeclaration
03495: if (declaringType instanceof Java.ClassDeclaration) {
03496: statics = ((Java.ClassDeclaration) declaringType).variableDeclaratorsAndInitializers;
03497: } else if (declaringType instanceof Java.InterfaceDeclaration) {
03498: statics = ((Java.InterfaceDeclaration) declaringType).constantDeclarations;
03499: } else {
03500: throw new RuntimeException(
03501: "SNO: AbstractTypeDeclaration is neither ClassDeclaration nor InterfaceDeclaration");
03502: }
03503:
03504: String className = Descriptor.toClassName(iClass
03505: .getDescriptor());
03506:
03507: // Compose the "class-dollar" field name. This i done as follows:
03508: // Type Class-name Field-name
03509: // String java.lang.String class$java$lang$String
03510: // String[] [Ljava.lang.String; array$Ljava$lang$String
03511: // String[][] [[Ljava.lang.String; array$$Ljava$lang$String
03512: // String[][][] [[[java.lang.String; array$$$Ljava$lang$String
03513: // int[] [I array$I
03514: // int[][] [[I array$$I
03515: String classDollarFieldName;
03516: {
03517: if (className.startsWith("[")) {
03518: classDollarFieldName = "array"
03519: + className.replace('.', '$').replace('[', '$');
03520: if (classDollarFieldName.endsWith(";"))
03521: classDollarFieldName = classDollarFieldName
03522: .substring(0,
03523: classDollarFieldName.length() - 1);
03524: } else {
03525: classDollarFieldName = "class$"
03526: + className.replace('.', '$');
03527: }
03528: }
03529:
03530: // Declare the static "class dollar field" if not already done.
03531: {
03532: boolean hasClassDollarField = false;
03533: BLOCK_STATEMENTS: for (Iterator it = statics.iterator(); it
03534: .hasNext();) {
03535: Java.TypeBodyDeclaration tbd = (Java.TypeBodyDeclaration) it
03536: .next();
03537: if (!tbd.isStatic())
03538: continue;
03539: if (tbd instanceof Java.FieldDeclaration) {
03540: Java.FieldDeclaration fd = (Java.FieldDeclaration) tbd;
03541: IClass.IField[] fds = this .getIFields(fd);
03542: for (int j = 0; j < fds.length; ++j) {
03543: if (fds[j].getName().equals(
03544: classDollarFieldName)) {
03545: hasClassDollarField = true;
03546: break BLOCK_STATEMENTS;
03547: }
03548: }
03549: }
03550: }
03551: if (!hasClassDollarField) {
03552: Java.Type classType = new Java.SimpleType(loc,
03553: icl.CLASS);
03554: Java.FieldDeclaration fd = new Java.FieldDeclaration(
03555: loc, // location
03556: null, // optionalDocComment
03557: Mod.STATIC, // modifiers
03558: classType, // type
03559: new Java.VariableDeclarator[] { // variableDeclarators
03560: new Java.VariableDeclarator(loc, // location
03561: classDollarFieldName, // name
03562: 0, // brackets
03563: (Java.Rvalue) null // optionalInitializer
03564: ) });
03565: if (declaringType instanceof Java.ClassDeclaration) {
03566: ((Java.ClassDeclaration) declaringType)
03567: .addVariableDeclaratorOrInitializer(fd);
03568: } else if (declaringType instanceof Java.InterfaceDeclaration) {
03569: ((Java.InterfaceDeclaration) declaringType)
03570: .addConstantDeclaration(fd);
03571: } else {
03572: throw new RuntimeException(
03573: "SNO: AbstractTypeDeclaration is neither ClassDeclaration nor InterfaceDeclaration");
03574: }
03575: }
03576: }
03577:
03578: // return (class$X != null) ? class$X : (class$X = class$("X"));
03579: Java.Type declaringClassOrInterfaceType = new Java.SimpleType(
03580: loc, this .resolve(declaringType));
03581: Java.Lvalue classDollarFieldAccess = new Java.FieldAccessExpression(
03582: loc, // location
03583: declaringClassOrInterfaceType, // lhs
03584: classDollarFieldName // fieldName
03585: );
03586: Java.ConditionalExpression ce = new Java.ConditionalExpression(
03587: loc, // location
03588: new Java.BinaryOperation( // lhs
03589: loc, // location
03590: classDollarFieldAccess, // lhs
03591: "!=", // op
03592: new Java.ConstantValue(loc, null) // rhs
03593: ), classDollarFieldAccess, // mhs
03594: new Java.Assignment( // rhs
03595: loc, // location
03596: classDollarFieldAccess, // lhs
03597: "=", // operator
03598: new Java.MethodInvocation( // rhs
03599: loc, // location
03600: declaringClassOrInterfaceType, // optionalTarget
03601: "class$", // methodName
03602: new Java.Rvalue[] { // arguments
03603: new Java.ConstantValue(loc, // location
03604: className // constantValue
03605: ) })));
03606: ce.setEnclosingBlockStatement(cl.getEnclosingBlockStatement());
03607: return this .compileGet(ce);
03608: }
03609:
03610: private IClass compileGet2(Java.Assignment a)
03611: throws CompileException {
03612: if (a.operator == "=") {
03613: int lhsCS = this .compileContext(a.lhs);
03614: IClass rhsType = this .compileGetValue(a.rhs);
03615: IClass lhsType = this .getType(a.lhs);
03616: Object rhsCV = this .getConstantValue(a.rhs);
03617: this .assignmentConversion((Java.Located) a, // located
03618: rhsType, // sourceType
03619: lhsType, // targetType
03620: rhsCV // optionalConstantValue
03621: );
03622: this .dupx((Java.Located) a, // located
03623: lhsType, // type
03624: lhsCS // x
03625: );
03626: this .compileSet(a.lhs);
03627: return lhsType;
03628: }
03629:
03630: // Implement "|= ^= &= *= /= %= += -= <<= >>= >>>=".
03631: int lhsCS = this .compileContext(a.lhs);
03632: this .dup((Java.Located) a, lhsCS);
03633: IClass lhsType = this .compileGet(a.lhs);
03634: IClass resultType = this .compileArithmeticBinaryOperation(
03635: (Java.Located) a, // located
03636: lhsType, // lhsType
03637: a.operator.substring( // operator
03638: 0, a.operator.length() - 1).intern(), // <= IMPORTANT!
03639: a.rhs // rhs
03640: );
03641: // Convert the result to LHS type (JLS2 15.26.2).
03642: if (!this .tryIdentityConversion(resultType, lhsType)
03643: && !this .tryNarrowingPrimitiveConversion(
03644: (Java.Located) a, // located
03645: resultType, // sourceType
03646: lhsType // destinationType
03647: ))
03648: throw new RuntimeException("SNO: \"" + a.operator
03649: + "\" reconversion failed");
03650: this .dupx((Java.Located) a, // located
03651: lhsType, // type
03652: lhsCS // x
03653: );
03654: this .compileSet(a.lhs);
03655: return lhsType;
03656: }
03657:
03658: private IClass compileGet2(Java.ConditionalExpression ce)
03659: throws CompileException {
03660: IClass mhsType, rhsType;
03661: CodeContext.Inserter mhsConvertInserter;
03662: CodeContext.Offset toEnd = this .codeContext.new Offset();
03663: Object cv = this .getConstantValue(ce.lhs);
03664: if (cv instanceof Boolean) {
03665: if (((Boolean) cv).booleanValue()) {
03666: mhsType = this .compileGetValue(ce.mhs);
03667: mhsConvertInserter = this .codeContext.newInserter();
03668: rhsType = this .getType(ce.rhs);
03669: } else {
03670: mhsType = this .getType(ce.mhs);
03671: mhsConvertInserter = null;
03672: rhsType = this .compileGetValue(ce.rhs);
03673: }
03674: } else {
03675: CodeContext.Offset toRhs = this .codeContext.new Offset();
03676:
03677: this .compileBoolean(ce.lhs, toRhs,
03678: Java.Rvalue.JUMP_IF_FALSE);
03679: mhsType = this .compileGetValue(ce.mhs);
03680: mhsConvertInserter = this .codeContext.newInserter();
03681: this .writeBranch(ce, Opcode.GOTO, toEnd);
03682: toRhs.set();
03683: rhsType = this .compileGetValue(ce.rhs);
03684: }
03685:
03686: IClass expressionType;
03687: if (mhsType == rhsType) {
03688:
03689: // JLS 15.25.1.1
03690: expressionType = mhsType;
03691: } else if (mhsType.isPrimitiveNumeric()
03692: && rhsType.isPrimitiveNumeric()) {
03693:
03694: // JLS 15.25.1.2
03695:
03696: // TODO JLS 15.25.1.2.1
03697:
03698: // TODO JLS 15.25.1.2.2
03699:
03700: // JLS 15.25.1.2.3
03701: expressionType = this .binaryNumericPromotion(
03702: (Java.Located) ce, // located
03703: mhsType, // type1
03704: mhsConvertInserter, // convertInserter1
03705: rhsType // type2
03706: );
03707: } else if (this .getConstantValue(ce.mhs) == Java.Rvalue.CONSTANT_VALUE_NULL
03708: && !rhsType.isPrimitive()) {
03709:
03710: // JLS 15.25.1.3 (null : ref)
03711: expressionType = rhsType;
03712: } else if (!mhsType.isPrimitive()
03713: && this .getConstantValue(ce.rhs) == Java.Rvalue.CONSTANT_VALUE_NULL) {
03714:
03715: // JLS 15.25.1.3 (ref : null)
03716: expressionType = mhsType;
03717: } else if (!mhsType.isPrimitive() && !rhsType.isPrimitive()) {
03718: if (mhsType.isAssignableFrom(rhsType)) {
03719: expressionType = mhsType;
03720: } else if (rhsType.isAssignableFrom(mhsType)) {
03721: expressionType = rhsType;
03722: } else {
03723: this .compileError("Reference types \"" + mhsType
03724: + "\" and \"" + rhsType + "\" don't match", ce
03725: .getLocation());
03726: return this .iClassLoader.OBJECT;
03727: }
03728: } else {
03729: this .compileError("Incompatible expression types \""
03730: + mhsType + "\" and \"" + rhsType + "\"", ce
03731: .getLocation());
03732: return this .iClassLoader.OBJECT;
03733: }
03734: toEnd.set();
03735:
03736: return expressionType;
03737: }
03738:
03739: private IClass compileGet2(Java.Crement c) throws CompileException {
03740:
03741: // Optimized crement of integer local variable.
03742: Java.LocalVariable lv = this .isIntLV(c);
03743: if (lv != null) {
03744: if (!c.pre)
03745: this .load((Java.Located) c, lv);
03746: this .writeOpcode(c, Opcode.IINC);
03747: this .writeByte(c, lv.localVariableArrayIndex);
03748: this .writeByte(c, c.operator == "++" ? 1 : -1);
03749: if (c.pre)
03750: this .load((Java.Located) c, lv);
03751: return lv.type;
03752: }
03753:
03754: // Compile operand context.
03755: int cs = this .compileContext(c.operand);
03756: // DUP operand context.
03757: this .dup((Java.Located) c, cs);
03758: // Get operand value.
03759: IClass type = this .compileGet(c.operand);
03760: // DUPX operand value.
03761: if (!c.pre)
03762: this .dupx((Java.Located) c, type, cs);
03763: // Apply "unary numeric promotion".
03764: IClass promotedType = this .unaryNumericPromotion(
03765: (Java.Located) c, type);
03766: // Crement.
03767: this .writeOpcode(c, UnitCompiler.ilfd(promotedType,
03768: Opcode.ICONST_1, Opcode.LCONST_1, Opcode.FCONST_1,
03769: Opcode.DCONST_1));
03770: if (c.operator == "++") {
03771: this .writeOpcode(c, Opcode.IADD
03772: + UnitCompiler.ilfd(promotedType));
03773: } else if (c.operator == "--") {
03774: this .writeOpcode(c, Opcode.ISUB
03775: + UnitCompiler.ilfd(promotedType));
03776: } else {
03777: this .compileError("Unexpected operator \"" + c.operator
03778: + "\"", c.getLocation());
03779: }
03780: // Reverse "unary numeric promotion".
03781: if (!this .tryIdentityConversion(promotedType, type)
03782: && !this .tryNarrowingPrimitiveConversion(
03783: (Java.Located) c, // located
03784: promotedType, // sourceType
03785: type // targetType
03786: ))
03787: throw new RuntimeException("SNO: \"" + c.operator
03788: + "\" reconversion failed");
03789: // DUPX cremented operand value.
03790: if (c.pre)
03791: this .dupx((Java.Located) c, type, cs);
03792: // Set operand.
03793: this .compileSet(c.operand);
03794:
03795: return type;
03796: }
03797:
03798: private IClass compileGet2(Java.ArrayAccessExpression aae)
03799: throws CompileException {
03800: IClass lhsComponentType = this .getType(aae);
03801: this .writeOpcode(aae, Opcode.IALOAD
03802: + UnitCompiler.ilfdabcs(lhsComponentType));
03803: return lhsComponentType;
03804: }
03805:
03806: private IClass compileGet2(Java.FieldAccessExpression fae)
03807: throws CompileException {
03808: this .determineValue(fae);
03809: return this .compileGet(fae.value);
03810: }
03811:
03812: private IClass compileGet2(Java.UnaryOperation uo)
03813: throws CompileException {
03814: if (uo.operator == "!") {
03815: return this .compileGet2((Java.BooleanRvalue) uo);
03816: }
03817:
03818: if (uo.operator == "+") {
03819: return this .compileGetValue(uo.operand);
03820: }
03821:
03822: if (uo.operator == "-") {
03823: IClass operandType;
03824: if (uo.operand instanceof Java.Literal) {
03825: Java.Literal l = (Java.Literal) uo.operand;
03826: operandType = this .getType2(l);
03827: this .pushConstant((Java.Located) uo, this
03828: .getNegatedConstantValue2(l));
03829: } else {
03830: operandType = this .compileGetValue(uo.operand);
03831: }
03832:
03833: IClass promotedType = this .unaryNumericPromotion(
03834: (Java.Located) uo, operandType);
03835: this .writeOpcode(uo, Opcode.INEG
03836: + UnitCompiler.ilfd(promotedType));
03837: return promotedType;
03838: }
03839:
03840: if (uo.operator == "~") {
03841: IClass operandType = this .compileGetValue(uo.operand);
03842:
03843: IClass promotedType = this .unaryNumericPromotion(
03844: (Java.Located) uo, operandType);
03845: if (promotedType == IClass.INT) {
03846: this .writeOpcode(uo, Opcode.ICONST_M1);
03847: this .writeOpcode(uo, Opcode.IXOR);
03848: return IClass.INT;
03849: }
03850: if (promotedType == IClass.LONG) {
03851: this .writeOpcode(uo, Opcode.LDC2_W);
03852: this .writeConstantLongInfo(uo, -1L);
03853: this .writeOpcode(uo, Opcode.LXOR);
03854: return IClass.LONG;
03855: }
03856: this .compileError(
03857: "Operator \"~\" not applicable to type \""
03858: + promotedType + "\"", uo.getLocation());
03859: }
03860:
03861: this .compileError(
03862: "Unexpected operator \"" + uo.operator + "\"", uo
03863: .getLocation());
03864: return this .iClassLoader.OBJECT;
03865: }
03866:
03867: private IClass compileGet2(Java.Instanceof io)
03868: throws CompileException {
03869: IClass lhsType = this .compileGetValue(io.lhs);
03870: IClass rhsType = this .getType(io.rhs);
03871:
03872: if (rhsType.isAssignableFrom(lhsType)) {
03873: this .pop((Java.Located) io, lhsType);
03874: this .writeOpcode(io, Opcode.ICONST_1);
03875: } else if (lhsType.isInterface() || rhsType.isInterface()
03876: || lhsType.isAssignableFrom(rhsType)) {
03877: this .writeOpcode(io, Opcode.INSTANCEOF);
03878: this .writeConstantClassInfo(io, rhsType.getDescriptor());
03879: } else {
03880: this .compileError("\"" + lhsType
03881: + "\" can never be an instance of \"" + rhsType
03882: + "\"", io.getLocation());
03883: }
03884: return IClass.BOOLEAN;
03885: }
03886:
03887: private IClass compileGet2(Java.BinaryOperation bo)
03888: throws CompileException {
03889: if (bo.op == "||" || bo.op == "&&" || bo.op == "=="
03890: || bo.op == "!=" || bo.op == "<" || bo.op == ">"
03891: || bo.op == "<=" || bo.op == ">=") {
03892: // Eventually calls "compileBoolean()".
03893: return this .compileGet2((Java.BooleanRvalue) bo);
03894: }
03895:
03896: // Implements "| ^ & * / % + - << >> >>>".
03897: return this .compileArithmeticOperation((Java.Located) bo, // located
03898: null, // type
03899: bo.unrollLeftAssociation(), // operands
03900: bo.op // operator
03901: );
03902: }
03903:
03904: private IClass compileGet2(Java.Cast c) throws CompileException {
03905: IClass tt = this .getType(c.targetType);
03906: IClass vt = this .compileGetValue(c.value);
03907: if (!this .tryIdentityConversion(vt, tt)
03908: && !this .tryWideningPrimitiveConversion(
03909: (Java.Located) c, vt, tt)
03910: && !this .tryNarrowingPrimitiveConversion(
03911: (Java.Located) c, vt, tt)
03912: && !this .isWideningReferenceConvertible(vt, tt)
03913: && !this .tryNarrowingReferenceConversion(
03914: (Java.Located) c, vt, tt))
03915: this .compileError("Cannot cast \"" + vt + "\" to \"" + tt
03916: + "\"", c.getLocation());
03917: return tt;
03918: }
03919:
03920: private IClass compileGet2(Java.ParenthesizedExpression pe)
03921: throws CompileException {
03922: return this .compileGet(pe.value);
03923: }
03924:
03925: private IClass compileGet2(Java.MethodInvocation mi)
03926: throws CompileException {
03927: IClass.IMethod iMethod = this .findIMethod(mi);
03928:
03929: IClass targetType;
03930: if (mi.optionalTarget == null) {
03931:
03932: // JLS2 6.5.7.1, 15.12.4.1.1.1
03933: Java.TypeBodyDeclaration scopeTBD;
03934: Java.ClassDeclaration scopeClassDeclaration;
03935: {
03936: Java.Scope s;
03937: for (s = mi.getEnclosingBlockStatement(); !(s instanceof Java.TypeBodyDeclaration); s = s
03938: .getEnclosingScope())
03939: ;
03940: scopeTBD = (Java.TypeBodyDeclaration) s;
03941: if (!(s instanceof Java.ClassDeclaration))
03942: s = s.getEnclosingScope();
03943: scopeClassDeclaration = (Java.ClassDeclaration) s;
03944: }
03945: if (iMethod.isStatic()) {
03946: this .warning("IASM",
03947: "Implicit access to static method \""
03948: + iMethod.toString() + "\"", mi
03949: .getLocation());
03950: // JLS2 15.12.4.1.1.1.1
03951: ;
03952: } else {
03953: this .warning("IANSM",
03954: "Implicit access to non-static method \""
03955: + iMethod.toString() + "\"", mi
03956: .getLocation());
03957: // JLS2 15.12.4.1.1.1.2
03958: if (scopeTBD.isStatic())
03959: this .compileError("Instance method \""
03960: + iMethod.toString()
03961: + "\" cannot be invoked in static context",
03962: mi.getLocation());
03963: this .referenceThis((Java.Located) mi, // located
03964: scopeClassDeclaration, // declaringClass
03965: scopeTBD, // declaringTypeBodyDeclaration
03966: iMethod.getDeclaringIClass() // targetIClass
03967: );
03968: }
03969: targetType = this .resolve(scopeClassDeclaration);
03970: } else {
03971:
03972: // 6.5.7.2
03973: boolean staticContext = this .isType(mi.optionalTarget);
03974: if (staticContext) {
03975: targetType = this .getType(this
03976: .toTypeOrCE(mi.optionalTarget));
03977: } else {
03978: targetType = this .compileGetValue(this
03979: .toRvalueOrCE(mi.optionalTarget));
03980: }
03981: if (iMethod.isStatic()) {
03982: if (!staticContext) {
03983: // JLS2 15.12.4.1.2.1
03984: this .pop((Java.Located) mi.optionalTarget, this
03985: .getType(mi.optionalTarget));
03986: }
03987: } else {
03988: if (staticContext)
03989: this .compileError("Instance method \""
03990: + mi.methodName
03991: + "\" cannot be invoked in static context",
03992: mi.getLocation());
03993: }
03994: }
03995:
03996: // Evaluate method parameters.
03997: IClass[] parameterTypes = iMethod.getParameterTypes();
03998: for (int i = 0; i < mi.arguments.length; ++i) {
03999: this .assignmentConversion((Java.Located) mi, // located
04000: this .compileGetValue(mi.arguments[i]), // sourceType
04001: parameterTypes[i], // targetType
04002: this .getConstantValue(mi.arguments[i]) // optionalConstantValue
04003: );
04004: }
04005:
04006: // Invoke!
04007: this .checkAccessible(iMethod, mi.getEnclosingBlockStatement());
04008: if (iMethod.getDeclaringIClass().isInterface()) {
04009: this .writeOpcode(mi, Opcode.INVOKEINTERFACE);
04010: this .writeConstantInterfaceMethodrefInfo(mi, // locatable
04011: iMethod.getDeclaringIClass().getDescriptor(), // classFD
04012: iMethod.getName(), // methodName
04013: iMethod.getDescriptor() // methodMD
04014: );
04015: IClass[] pts = iMethod.getParameterTypes();
04016: int count = 1;
04017: for (int i = 0; i < pts.length; ++i)
04018: count += Descriptor.size(pts[i].getDescriptor());
04019: this .writeByte(mi, count);
04020: this .writeByte(mi, 0);
04021: } else {
04022: if (!iMethod.isStatic()
04023: && iMethod.getAccess() == Access.PRIVATE) {
04024:
04025: // In order to make a non-static private method invocable for enclosing types,
04026: // enclosed types and types enclosed by the same type, "compile(FunctionDeclarator)"
04027: // modifies it on-the-fly as follows:
04028: // + Access is changed from PRIVATE to PACKAGE
04029: // + The name is appended with "$"
04030: // + It is made static
04031: // + A parameter of type "declaring class" is prepended to the signature
04032: // Hence, the invocation of such a method must be modified accordingly.
04033: this .writeOpcode(mi, Opcode.INVOKESTATIC);
04034: this .writeConstantMethodrefInfo(mi, // locatable
04035: iMethod.getDeclaringIClass().getDescriptor(), // classFD
04036: iMethod.getName() + '$', // methodName
04037: MethodDescriptor.prependParameter( // methodMD
04038: iMethod.getDescriptor(), iMethod
04039: .getDeclaringIClass()
04040: .getDescriptor()));
04041: } else {
04042: byte opcode = iMethod.isStatic() ? Opcode.INVOKESTATIC
04043: : Opcode.INVOKEVIRTUAL;
04044: this .writeOpcode(mi, opcode);
04045: if (opcode != Opcode.INVOKEVIRTUAL)
04046: targetType = iMethod.getDeclaringIClass();
04047: this .writeConstantMethodrefInfo(mi, // locatable
04048: targetType.getDescriptor(), // classFD
04049: iMethod.getName(), // methodName
04050: iMethod.getDescriptor() // methodMD
04051: );
04052: }
04053: }
04054: return iMethod.getReturnType();
04055: }
04056:
04057: private IClass compileGet2(Java.SuperclassMethodInvocation scmi)
04058: throws CompileException {
04059: IClass.IMethod iMethod = this .findIMethod(scmi);
04060:
04061: Java.Scope s;
04062: for (s = scmi.getEnclosingBlockStatement(); s instanceof Java.Statement; s = s
04063: .getEnclosingScope())
04064: ;
04065: Java.FunctionDeclarator fd = s instanceof Java.FunctionDeclarator ? (Java.FunctionDeclarator) s
04066: : null;
04067: if (fd == null) {
04068: this
04069: .compileError(
04070: "Cannot invoke superclass method in non-method scope",
04071: scmi.getLocation());
04072: return IClass.INT;
04073: }
04074: if ((fd.modifiers & Mod.STATIC) != 0)
04075: this
04076: .compileError(
04077: "Cannot invoke superclass method in static context",
04078: scmi.getLocation());
04079: this .load((Java.Located) scmi, this .resolve(fd
04080: .getDeclaringType()), 0);
04081:
04082: // Evaluate method parameters.
04083: IClass[] parameterTypes = iMethod.getParameterTypes();
04084: for (int i = 0; i < scmi.arguments.length; ++i) {
04085: this .assignmentConversion((Java.Located) scmi, // located
04086: this .compileGetValue(scmi.arguments[i]), // sourceType
04087: parameterTypes[i], // targetType
04088: this .getConstantValue(scmi.arguments[i]) // optionalConstantValue
04089: );
04090: }
04091:
04092: // Invoke!
04093: this .writeOpcode(scmi, Opcode.INVOKESPECIAL);
04094: this .writeConstantMethodrefInfo(scmi, iMethod
04095: .getDeclaringIClass().getDescriptor(), // classFD
04096: scmi.methodName, // methodName
04097: iMethod.getDescriptor() // methodMD
04098: );
04099: return iMethod.getReturnType();
04100: }
04101:
04102: private IClass compileGet2(Java.NewClassInstance nci)
04103: throws CompileException {
04104: if (nci.iClass == null)
04105: nci.iClass = this .getType(nci.type);
04106:
04107: this .writeOpcode(nci, Opcode.NEW);
04108: this .writeConstantClassInfo(nci, nci.iClass.getDescriptor());
04109: this .writeOpcode(nci, Opcode.DUP);
04110:
04111: if (nci.iClass.isInterface())
04112: this .compileError("Cannot instantiate \"" + nci.iClass
04113: + "\"", nci.getLocation());
04114: this .checkAccessible(nci.iClass, nci
04115: .getEnclosingBlockStatement());
04116: if (nci.iClass.isAbstract())
04117: this .compileError("Cannot instantiate abstract \""
04118: + nci.iClass + "\"", nci.getLocation());
04119:
04120: // Determine the enclosing instance for the new object.
04121: Java.Rvalue optionalEnclosingInstance;
04122: if (nci.optionalQualification != null) {
04123: if (nci.iClass.getOuterIClass() == null)
04124: this
04125: .compileError("Static member class cannot be instantiated with qualified NEW");
04126:
04127: // Enclosing instance defined by qualification (JLS 15.9.2.BL1.B3.B2).
04128: optionalEnclosingInstance = nci.optionalQualification;
04129: } else {
04130: Java.Scope s = nci.getEnclosingBlockStatement();
04131: for (; !(s instanceof Java.TypeBodyDeclaration); s = s
04132: .getEnclosingScope())
04133: ;
04134: Java.TypeBodyDeclaration enclosingTypeBodyDeclaration = (Java.TypeBodyDeclaration) s;
04135: Java.TypeDeclaration enclosingTypeDeclaration = (Java.TypeDeclaration) s
04136: .getEnclosingScope();
04137:
04138: if (!(enclosingTypeDeclaration instanceof Java.ClassDeclaration)
04139: || enclosingTypeBodyDeclaration.isStatic()) {
04140:
04141: // No enclosing instance in
04142: // + interface method declaration or
04143: // + static type body declaration (here: method or initializer or field declarator)
04144: // context (JLS 15.9.2.BL1.B3.B1.B1).
04145: optionalEnclosingInstance = null;
04146: } else {
04147:
04148: // Determine the type of the enclosing instance for the new object.
04149: IClass optionalOuterIClass = nci.iClass
04150: .getDeclaringIClass();
04151: if (optionalOuterIClass == null) {
04152:
04153: // No enclosing instance needed for a top-level class object.
04154: optionalEnclosingInstance = new Java.ThisReference(
04155: nci.getLocation());
04156: } else {
04157:
04158: // Find an appropriate enclosing instance for the new inner class object among
04159: // the enclosing instances of the current object (JLS
04160: // 15.9.2.BL1.B3.B1.B2).
04161: // Java.ClassDeclaration outerClassDeclaration = (Java.ClassDeclaration) enclosingTypeDeclaration;
04162: // optionalEnclosingInstance = new Java.QualifiedThisReference(
04163: // nci.getLocation(), // location
04164: // outerClassDeclaration, // declaringClass
04165: // enclosingTypeBodyDeclaration, // declaringTypeBodyDeclaration
04166: // optionalOuterIClass // targetIClass
04167: // );
04168: optionalEnclosingInstance = new Java.QualifiedThisReference(
04169: nci.getLocation(), // location
04170: new Java.SimpleType(
04171: // qualification
04172: nci.getLocation(),
04173: optionalOuterIClass));
04174: optionalEnclosingInstance
04175: .setEnclosingBlockStatement(nci
04176: .getEnclosingBlockStatement());
04177: }
04178: }
04179: }
04180:
04181: this .invokeConstructor((Java.Located) nci, // located
04182: nci.getEnclosingBlockStatement(), // scope
04183: optionalEnclosingInstance, // optionalEnclosingInstance
04184: nci.iClass, // targetClass
04185: nci.arguments // arguments
04186: );
04187: return nci.iClass;
04188: }
04189:
04190: private IClass compileGet2(Java.NewAnonymousClassInstance naci)
04191: throws CompileException {
04192:
04193: // Find constructors.
04194: Java.AnonymousClassDeclaration acd = naci.anonymousClassDeclaration;
04195: IClass sc = this .resolve(acd).getSuperclass();
04196: IClass.IConstructor[] iConstructors = sc
04197: .getDeclaredIConstructors();
04198: if (iConstructors.length == 0)
04199: throw new RuntimeException(
04200: "SNO: Base class has no constructors");
04201:
04202: // Determine most specific constructor.
04203: IClass.IConstructor iConstructor = (IClass.IConstructor) this
04204: .findMostSpecificIInvocable((Java.Located) naci, // located
04205: iConstructors, // iInvocables
04206: naci.arguments // arguments
04207: );
04208:
04209: IClass[] pts = iConstructor.getParameterTypes();
04210:
04211: // Determine formal parameters of anonymous constructor.
04212: Java.FunctionDeclarator.FormalParameter[] fps;
04213: Location loc = naci.getLocation();
04214: {
04215: List l = new ArrayList(); // FormalParameter
04216:
04217: // Pass the enclosing instance of the base class as parameter #1.
04218: if (naci.optionalQualification != null)
04219: l.add(new Java.FunctionDeclarator.FormalParameter(loc, // location
04220: true, // finaL
04221: new Java.SimpleType(loc, this
04222: .getType(naci.optionalQualification)), // type
04223: "this$base" // name
04224: ));
04225: for (int i = 0; i < pts.length; ++i)
04226: l.add(new Java.FunctionDeclarator.FormalParameter(loc, // location
04227: true, // finaL
04228: new Java.SimpleType(loc, pts[i]), // type
04229: "p" + i // name
04230: ));
04231: fps = (Java.FunctionDeclarator.FormalParameter[]) l
04232: .toArray(new Java.FunctionDeclarator.FormalParameter[l
04233: .size()]);
04234: }
04235:
04236: // Determine thrown exceptions of anonymous constructor.
04237: IClass[] tes = iConstructor.getThrownExceptions();
04238: Java.Type[] tets = new Java.Type[tes.length];
04239: for (int i = 0; i < tes.length; ++i)
04240: tets[i] = new Java.SimpleType(loc, tes[i]);
04241:
04242: // The anonymous constructor merely invokes the constructor of its superclass.
04243: int j = 0;
04244: Java.Rvalue optionalQualificationAccess;
04245: if (naci.optionalQualification == null) {
04246: optionalQualificationAccess = null;
04247: } else {
04248: optionalQualificationAccess = new Java.ParameterAccess(loc,
04249: fps[j++]);
04250: }
04251: Java.Rvalue[] parameterAccesses = new Java.Rvalue[pts.length];
04252: for (int i = 0; i < pts.length; ++i) {
04253: parameterAccesses[i] = new Java.ParameterAccess(loc,
04254: fps[j++]);
04255: }
04256:
04257: // Generate the anonymous constructor for the anonymous class (JLS 15.9.5.1).
04258: acd.addConstructor(new Java.ConstructorDeclarator(loc, // location
04259: null, // optionalDocComment
04260: Mod.PACKAGE, // modifiers
04261: fps, // formalParameters
04262: tets, // thrownExceptions
04263: new Java.SuperConstructorInvocation( // optionalExplicitonstructorInvocation
04264: loc, // location
04265: optionalQualificationAccess, // optionalQualification
04266: parameterAccesses // arguments
04267: ), new Java.Block(loc) // optionalBody
04268: ));
04269:
04270: // Compile the anonymous class.
04271: this .compile(acd);
04272:
04273: // Instantiate the anonymous class.
04274: this .writeOpcode(naci, Opcode.NEW);
04275: this .writeConstantClassInfo(naci, this .resolve(
04276: naci.anonymousClassDeclaration).getDescriptor());
04277:
04278: // Invoke the anonymous constructor.
04279: this .writeOpcode(naci, Opcode.DUP);
04280: Java.Rvalue[] arguments2;
04281: if (naci.optionalQualification == null) {
04282: arguments2 = naci.arguments;
04283: } else {
04284: arguments2 = new Java.Rvalue[naci.arguments.length + 1];
04285: arguments2[0] = naci.optionalQualification;
04286: System.arraycopy(naci.arguments, 0, arguments2, 1,
04287: naci.arguments.length);
04288: }
04289:
04290: // Notice: The enclosing instance of the anonymous class is "this", not the
04291: // qualification of the NewAnonymousClassInstance.
04292: Java.Scope s;
04293: for (s = naci.getEnclosingBlockStatement(); !(s instanceof Java.TypeBodyDeclaration); s = s
04294: .getEnclosingScope())
04295: ;
04296: Java.ThisReference oei;
04297: if (((Java.TypeBodyDeclaration) s).isStatic()) {
04298: oei = null;
04299: } else {
04300: oei = new Java.ThisReference(loc);
04301: oei.setEnclosingBlockStatement(naci
04302: .getEnclosingBlockStatement());
04303: }
04304: this .invokeConstructor((Java.Located) naci, // located
04305: (Java.Scope) naci.getEnclosingBlockStatement(), // scope
04306: oei, // optionalEnclosingInstance
04307: this .resolve(naci.anonymousClassDeclaration), // targetClass
04308: arguments2 // arguments
04309: );
04310: return this .resolve(naci.anonymousClassDeclaration);
04311: }
04312:
04313: private IClass compileGet2(Java.ParameterAccess pa)
04314: throws CompileException {
04315: Java.LocalVariable lv = this
04316: .getLocalVariable(pa.formalParameter);
04317: this .load((Java.Located) pa, lv);
04318: return lv.type;
04319: }
04320:
04321: private IClass compileGet2(Java.NewArray na)
04322: throws CompileException {
04323: for (int i = 0; i < na.dimExprs.length; ++i) {
04324: IClass dimType = this .compileGetValue(na.dimExprs[i]);
04325: if (dimType != IClass.INT
04326: && this .unaryNumericPromotion((Java.Located) na, // located
04327: dimType // type
04328: ) != IClass.INT)
04329: this .compileError("Invalid array size expression type",
04330: na.getLocation());
04331: }
04332:
04333: return this .newArray((Java.Located) na, // located
04334: na.dimExprs.length, // dimExprCount
04335: na.dims, // dims
04336: this .getType(na.type) // componentType
04337: );
04338: }
04339:
04340: private IClass compileGet2(Java.NewInitializedArray nia)
04341: throws CompileException {
04342: IClass at = this .getType(nia.arrayType);
04343: this .compileGetValue(nia.arrayInitializer, at);
04344: return at;
04345: }
04346:
04347: private void compileGetValue(Java.ArrayInitializer ai,
04348: IClass arrayType) throws CompileException {
04349: if (!arrayType.isArray())
04350: this
04351: .compileError("Array initializer not allowed for non-array type \""
04352: + arrayType.toString() + "\"");
04353: IClass ct = arrayType.getComponentType();
04354:
04355: this .pushConstant((Java.Located) ai, new Integer(
04356: ai.values.length));
04357: this .newArray((Java.Located) ai, // located
04358: 1, // dimExprCount,
04359: 0, // dims,
04360: ct // componentType
04361: );
04362:
04363: for (int i = 0; i < ai.values.length; ++i) {
04364: this .writeOpcode(ai, Opcode.DUP);
04365: this .pushConstant((Java.Located) ai, new Integer(i));
04366: Java.ArrayInitializerOrRvalue aiorv = ai.values[i];
04367: if (aiorv instanceof Java.Rvalue) {
04368: Java.Rvalue rv = (Java.Rvalue) aiorv;
04369: this .assignmentConversion((Java.Located) ai, // located
04370: this .compileGetValue(rv), // sourceType
04371: ct, // targetType
04372: this .getConstantValue(rv) // optionalConstantValue
04373: );
04374: } else if (aiorv instanceof Java.ArrayInitializer) {
04375: this .compileGetValue((Java.ArrayInitializer) aiorv, ct);
04376: } else {
04377: throw new RuntimeException(
04378: "Unexpected array initializer or rvalue class "
04379: + aiorv.getClass().getName());
04380: }
04381: this .writeOpcode(ai, Opcode.IASTORE
04382: + UnitCompiler.ilfdabcs(ct));
04383: }
04384: }
04385:
04386: private IClass compileGet2(Java.Literal l) throws CompileException {
04387: if (l.value == Scanner.MAGIC_INTEGER
04388: || l.value == Scanner.MAGIC_LONG)
04389: this
04390: .compileError(
04391: "This literal value may only appear in a negated context",
04392: l.getLocation());
04393: return this .pushConstant((Java.Located) l,
04394: l.value == null ? Java.Rvalue.CONSTANT_VALUE_NULL
04395: : l.value);
04396: }
04397:
04398: private IClass compileGet2(Java.ConstantValue cv) {
04399: return this .pushConstant((Java.Located) cv, cv.constantValue);
04400: }
04401:
04402: /**
04403: * Convenience function that calls {@link #compileContext(Java.Rvalue)}
04404: * and {@link #compileGet(Java.Rvalue)}.
04405: * @return The type of the Rvalue
04406: */
04407: private IClass compileGetValue(Java.Rvalue rv)
04408: throws CompileException {
04409: Object cv = this .getConstantValue(rv);
04410: if (cv != null) {
04411: this .fakeCompile(rv); // To check that, e.g., "a" compiles in "true || a".
04412: this .pushConstant((Java.Located) rv, cv);
04413: return this .getType(rv);
04414: }
04415:
04416: this .compileContext(rv);
04417: return this .compileGet(rv);
04418: }
04419:
04420: // -------------------- Rvalue.getConstantValue() -----------------
04421:
04422: /**
04423: * Attempts to evaluate as a constant expression.
04424: * <p>
04425: * <table>
04426: * <tr><th>Expression type</th><th>Return value type</th></tr>
04427: * <tr><td>String</td><td>String</td></tr>
04428: * <tr><td>byte</td><td>Byte</td></tr>
04429: * <tr><td>short</td><td>Chort</td></tr>
04430: * <tr><td>int</td><td>Integer</td></tr>
04431: * <tr><td>boolean</td><td>Boolean</td></tr>
04432: * <tr><td>char</td><td>Character</td></tr>
04433: * <tr><td>float</td><td>Float</td></tr>
04434: * <tr><td>long</td><td>Long</td></tr>
04435: * <tr><td>double</td><td>Double</td></tr>
04436: * <tr><td>null</td><td>{@link Java.Rvalue#CONSTANT_VALUE_NULL}</td></tr>
04437: * </table>
04438: */
04439: public final Object getConstantValue(Java.Rvalue rv)
04440: throws CompileException {
04441: if (rv.constantValue != Java.Rvalue.CONSTANT_VALUE_UNKNOWN)
04442: return rv.constantValue;
04443:
04444: final Object[] res = new Object[1];
04445: class UCE extends RuntimeException {
04446: final CompileException ce;
04447:
04448: UCE(CompileException ce) {
04449: this .ce = ce;
04450: }
04451: }
04452: Visitor.RvalueVisitor rvv = new Visitor.RvalueVisitor() {
04453: public void visitArrayLength(Java.ArrayLength al) {
04454: res[0] = UnitCompiler.this .getConstantValue2(al);
04455: }
04456:
04457: public void visitAssignment(Java.Assignment a) {
04458: res[0] = UnitCompiler.this .getConstantValue2(a);
04459: }
04460:
04461: public void visitUnaryOperation(Java.UnaryOperation uo) {
04462: try {
04463: res[0] = UnitCompiler.this .getConstantValue2(uo);
04464: } catch (CompileException e) {
04465: throw new UCE(e);
04466: }
04467: }
04468:
04469: public void visitBinaryOperation(Java.BinaryOperation bo) {
04470: try {
04471: res[0] = UnitCompiler.this .getConstantValue2(bo);
04472: } catch (CompileException e) {
04473: throw new UCE(e);
04474: }
04475: }
04476:
04477: public void visitCast(Java.Cast c) {
04478: try {
04479: res[0] = UnitCompiler.this .getConstantValue2(c);
04480: } catch (CompileException e) {
04481: throw new UCE(e);
04482: }
04483: }
04484:
04485: public void visitClassLiteral(Java.ClassLiteral cl) {
04486: res[0] = UnitCompiler.this .getConstantValue2(cl);
04487: }
04488:
04489: public void visitConditionalExpression(
04490: Java.ConditionalExpression ce) {
04491: res[0] = UnitCompiler.this .getConstantValue2(ce);
04492: }
04493:
04494: public void visitConstantValue(Java.ConstantValue cv) {
04495: res[0] = UnitCompiler.this .getConstantValue2(cv);
04496: }
04497:
04498: public void visitCrement(Java.Crement c) {
04499: res[0] = UnitCompiler.this .getConstantValue2(c);
04500: }
04501:
04502: public void visitInstanceof(Java.Instanceof io) {
04503: res[0] = UnitCompiler.this .getConstantValue2(io);
04504: }
04505:
04506: public void visitMethodInvocation(Java.MethodInvocation mi) {
04507: res[0] = UnitCompiler.this .getConstantValue2(mi);
04508: }
04509:
04510: public void visitSuperclassMethodInvocation(
04511: Java.SuperclassMethodInvocation smi) {
04512: res[0] = UnitCompiler.this .getConstantValue2(smi);
04513: }
04514:
04515: public void visitLiteral(Java.Literal l) {
04516: try {
04517: res[0] = UnitCompiler.this .getConstantValue2(l);
04518: } catch (CompileException e) {
04519: throw new UCE(e);
04520: }
04521: }
04522:
04523: public void visitNewAnonymousClassInstance(
04524: Java.NewAnonymousClassInstance naci) {
04525: res[0] = UnitCompiler.this .getConstantValue2(naci);
04526: }
04527:
04528: public void visitNewArray(Java.NewArray na) {
04529: res[0] = UnitCompiler.this .getConstantValue2(na);
04530: }
04531:
04532: public void visitNewInitializedArray(
04533: Java.NewInitializedArray nia) {
04534: res[0] = UnitCompiler.this .getConstantValue2(nia);
04535: }
04536:
04537: public void visitNewClassInstance(Java.NewClassInstance nci) {
04538: res[0] = UnitCompiler.this .getConstantValue2(nci);
04539: }
04540:
04541: public void visitParameterAccess(Java.ParameterAccess pa) {
04542: res[0] = UnitCompiler.this .getConstantValue2(pa);
04543: }
04544:
04545: public void visitQualifiedThisReference(
04546: Java.QualifiedThisReference qtr) {
04547: res[0] = UnitCompiler.this .getConstantValue2(qtr);
04548: }
04549:
04550: public void visitThisReference(Java.ThisReference tr) {
04551: res[0] = UnitCompiler.this .getConstantValue2(tr);
04552: }
04553:
04554: public void visitAmbiguousName(Java.AmbiguousName an) {
04555: try {
04556: res[0] = UnitCompiler.this .getConstantValue2(an);
04557: } catch (CompileException e) {
04558: throw new UCE(e);
04559: }
04560: }
04561:
04562: public void visitArrayAccessExpression(
04563: Java.ArrayAccessExpression aae) {
04564: res[0] = UnitCompiler.this .getConstantValue2(aae);
04565: };
04566:
04567: public void visitFieldAccess(Java.FieldAccess fa) {
04568: try {
04569: res[0] = UnitCompiler.this .getConstantValue2(fa);
04570: } catch (CompileException e) {
04571: throw new UCE(e);
04572: }
04573: }
04574:
04575: public void visitFieldAccessExpression(
04576: Java.FieldAccessExpression fae) {
04577: res[0] = UnitCompiler.this .getConstantValue2(fae);
04578: }
04579:
04580: public void visitLocalVariableAccess(
04581: Java.LocalVariableAccess lva) {
04582: res[0] = UnitCompiler.this .getConstantValue2(lva);
04583: }
04584:
04585: public void visitParenthesizedExpression(
04586: Java.ParenthesizedExpression pe) {
04587: try {
04588: res[0] = UnitCompiler.this .getConstantValue2(pe);
04589: } catch (CompileException e) {
04590: throw new UCE(e);
04591: }
04592: }
04593: };
04594: try {
04595: rv.accept(rvv);
04596: rv.constantValue = res[0];
04597: return rv.constantValue;
04598: } catch (UCE uce) {
04599: throw uce.ce;
04600: }
04601: }
04602:
04603: private Object getConstantValue2(Java.Rvalue rv) {
04604: return null;
04605: }
04606:
04607: private Object getConstantValue2(Java.AmbiguousName an)
04608: throws CompileException {
04609: return this .getConstantValue(this .toRvalueOrCE(this
04610: .reclassify(an)));
04611: }
04612:
04613: private Object getConstantValue2(Java.FieldAccess fa)
04614: throws CompileException {
04615: return fa.field.getConstantValue();
04616: }
04617:
04618: private Object getConstantValue2(Java.UnaryOperation uo)
04619: throws CompileException {
04620: if (uo.operator.equals("+"))
04621: return this .getConstantValue(uo.operand);
04622: if (uo.operator.equals("-"))
04623: return this .getNegatedConstantValue(uo.operand);
04624: if (uo.operator.equals("!")) {
04625: Object cv = this .getConstantValue(uo.operand);
04626: return cv instanceof Boolean ? (((Boolean) cv)
04627: .booleanValue() ? Boolean.FALSE : Boolean.TRUE)
04628: : null;
04629: }
04630: return null;
04631: }
04632:
04633: private Object getConstantValue2(Java.BinaryOperation bo)
04634: throws CompileException {
04635:
04636: // null == null
04637: // null != null
04638: if ((bo.op == "==" || bo.op == "!=")
04639: && this .getConstantValue(bo.lhs) == Java.Rvalue.CONSTANT_VALUE_NULL
04640: && this .getConstantValue(bo.rhs) == Java.Rvalue.CONSTANT_VALUE_NULL)
04641: return bo.op == "==" ? Boolean.TRUE : Boolean.FALSE;
04642:
04643: // "|", "^", "&", "*", "/", "%", "+", "-".
04644: if (bo.op == "|" || bo.op == "^" || bo.op == "&"
04645: || bo.op == "*" || bo.op == "/" || bo.op == "%"
04646: || bo.op == "+" || bo.op == "-") {
04647:
04648: // Unroll the constant operands.
04649: List cvs = new ArrayList();
04650: for (Iterator it = bo.unrollLeftAssociation(); it.hasNext();) {
04651: Object cv = this .getConstantValue(((Java.Rvalue) it
04652: .next()));
04653: if (cv == null)
04654: return null;
04655: cvs.add(cv);
04656: }
04657:
04658: // Compute the constant value of the unrolled binary operation.
04659: Iterator it = cvs.iterator();
04660: Object lhs = it.next();
04661: while (it.hasNext()) {
04662: Object rhs = it.next();
04663:
04664: // String concatenation?
04665: if (bo.op == "+"
04666: && (lhs instanceof String || rhs instanceof String)) {
04667: StringBuffer sb = new StringBuffer();
04668: sb.append(lhs.toString()).append(rhs.toString());
04669: while (it.hasNext())
04670: sb.append(it.next().toString());
04671: return sb.toString();
04672: }
04673:
04674: if (!(lhs instanceof Number)
04675: || !(rhs instanceof Number))
04676: return null;
04677:
04678: // Numeric binary operation.
04679: if (lhs instanceof Double || rhs instanceof Double) {
04680: double lhsD = ((Number) lhs).doubleValue();
04681: double rhsD = ((Number) rhs).doubleValue();
04682: double cvD;
04683: if (bo.op == "*")
04684: cvD = lhsD * rhsD;
04685: else if (bo.op == "/")
04686: cvD = lhsD / rhsD;
04687: else if (bo.op == "%")
04688: cvD = lhsD % rhsD;
04689: else if (bo.op == "+")
04690: cvD = lhsD + rhsD;
04691: else if (bo.op == "-")
04692: cvD = lhsD - rhsD;
04693: else
04694: return null;
04695: lhs = new Double(cvD);
04696: } else if (lhs instanceof Float || rhs instanceof Float) {
04697: float lhsF = ((Number) lhs).floatValue();
04698: float rhsF = ((Number) rhs).floatValue();
04699: float cvF;
04700: if (bo.op == "*")
04701: cvF = lhsF * rhsF;
04702: else if (bo.op == "/")
04703: cvF = lhsF / rhsF;
04704: else if (bo.op == "%")
04705: cvF = lhsF % rhsF;
04706: else if (bo.op == "+")
04707: cvF = lhsF + rhsF;
04708: else if (bo.op == "-")
04709: cvF = lhsF - rhsF;
04710: else
04711: return null;
04712: lhs = new Float(cvF);
04713: } else if (lhs instanceof Long || rhs instanceof Long) {
04714: long lhsL = ((Number) lhs).longValue();
04715: long rhsL = ((Number) rhs).longValue();
04716: long cvL;
04717: if (bo.op == "|")
04718: cvL = lhsL | rhsL;
04719: else if (bo.op == "^")
04720: cvL = lhsL ^ rhsL;
04721: else if (bo.op == "&")
04722: cvL = lhsL & rhsL;
04723: else if (bo.op == "*")
04724: cvL = lhsL * rhsL;
04725: else if (bo.op == "/")
04726: cvL = lhsL / rhsL;
04727: else if (bo.op == "%")
04728: cvL = lhsL % rhsL;
04729: else if (bo.op == "+")
04730: cvL = lhsL + rhsL;
04731: else if (bo.op == "-")
04732: cvL = lhsL - rhsL;
04733: else
04734: return null;
04735: lhs = new Long(cvL);
04736: } else {
04737: int lhsI = ((Number) lhs).intValue();
04738: int rhsI = ((Number) rhs).intValue();
04739: int cvI;
04740: if (bo.op == "|")
04741: cvI = lhsI | rhsI;
04742: else if (bo.op == "^")
04743: cvI = lhsI ^ rhsI;
04744: else if (bo.op == "&")
04745: cvI = lhsI & rhsI;
04746: else if (bo.op == "*")
04747: cvI = lhsI * rhsI;
04748: else if (bo.op == "/")
04749: cvI = lhsI / rhsI;
04750: else if (bo.op == "%")
04751: cvI = lhsI % rhsI;
04752: else if (bo.op == "+")
04753: cvI = lhsI + rhsI;
04754: else if (bo.op == "-")
04755: cvI = lhsI - rhsI;
04756: else
04757: return null;
04758: lhs = new Integer(cvI);
04759: }
04760: }
04761: return lhs;
04762: }
04763:
04764: // "&&" and "||" with constant LHS operand.
04765: if (bo.op == "&&" || bo.op == "||") {
04766: Object lhsValue = this .getConstantValue(bo.lhs);
04767: if (lhsValue instanceof Boolean) {
04768: boolean lhsBV = ((Boolean) lhsValue).booleanValue();
04769: return (bo.op == "&&" ? (lhsBV ? this
04770: .getConstantValue(bo.rhs) : Boolean.FALSE)
04771: : (lhsBV ? Boolean.TRUE : this
04772: .getConstantValue(bo.rhs)));
04773: }
04774: }
04775:
04776: return null;
04777: }
04778:
04779: private Object getConstantValue2(Java.Cast c)
04780: throws CompileException {
04781: Object cv = this .getConstantValue(c.value);
04782: if (cv == null)
04783: return null;
04784:
04785: if (cv instanceof Number) {
04786: IClass tt = this .getType(c.targetType);
04787: if (tt == IClass.BYTE)
04788: return new Byte(((Number) cv).byteValue());
04789: if (tt == IClass.SHORT)
04790: return new Short(((Number) cv).shortValue());
04791: if (tt == IClass.INT)
04792: return new Integer(((Number) cv).intValue());
04793: if (tt == IClass.LONG)
04794: return new Long(((Number) cv).longValue());
04795: if (tt == IClass.FLOAT)
04796: return new Float(((Number) cv).floatValue());
04797: if (tt == IClass.DOUBLE)
04798: return new Double(((Number) cv).doubleValue());
04799: }
04800:
04801: return null;
04802: }
04803:
04804: private Object getConstantValue2(Java.ParenthesizedExpression pe)
04805: throws CompileException {
04806: return this .getConstantValue(pe.value);
04807: }
04808:
04809: private Object getConstantValue2(Java.Literal l)
04810: throws CompileException {
04811: if (l.value == Scanner.MAGIC_INTEGER
04812: || l.value == Scanner.MAGIC_LONG)
04813: this
04814: .compileError(
04815: "This literal value may only appear in a negated context",
04816: l.getLocation());
04817: return l.value == null ? Java.Rvalue.CONSTANT_VALUE_NULL
04818: : l.value;
04819: }
04820:
04821: private Object getConstantValue2(Java.ConstantValue cv) {
04822: return cv.constantValue;
04823: }
04824:
04825: /**
04826: * Attempts to evaluate the negated value of a constant {@link Java.Rvalue}.
04827: * This is particularly relevant for the smallest value of an integer or
04828: * long literal.
04829: *
04830: * @return null if value is not constant; otherwise a String, Byte,
04831: * Short, Integer, Boolean, Character, Float, Long or Double
04832: */
04833: private final Object getNegatedConstantValue(Java.Rvalue rv)
04834: throws CompileException {
04835: final Object[] res = new Object[1];
04836: class UCE extends RuntimeException {
04837: final CompileException ce;
04838:
04839: UCE(CompileException ce) {
04840: this .ce = ce;
04841: }
04842: }
04843: Visitor.RvalueVisitor rvv = new Visitor.RvalueVisitor() {
04844: public void visitArrayLength(Java.ArrayLength al) {
04845: res[0] = UnitCompiler.this .getNegatedConstantValue2(al);
04846: }
04847:
04848: public void visitAssignment(Java.Assignment a) {
04849: res[0] = UnitCompiler.this .getNegatedConstantValue2(a);
04850: }
04851:
04852: public void visitUnaryOperation(Java.UnaryOperation uo) {
04853: try {
04854: res[0] = UnitCompiler.this
04855: .getNegatedConstantValue2(uo);
04856: } catch (CompileException e) {
04857: throw new UCE(e);
04858: }
04859: }
04860:
04861: public void visitBinaryOperation(Java.BinaryOperation bo) {
04862: res[0] = UnitCompiler.this .getNegatedConstantValue2(bo);
04863: }
04864:
04865: public void visitCast(Java.Cast c) {
04866: res[0] = UnitCompiler.this .getNegatedConstantValue2(c);
04867: }
04868:
04869: public void visitClassLiteral(Java.ClassLiteral cl) {
04870: res[0] = UnitCompiler.this .getNegatedConstantValue2(cl);
04871: }
04872:
04873: public void visitConditionalExpression(
04874: Java.ConditionalExpression ce) {
04875: res[0] = UnitCompiler.this .getNegatedConstantValue2(ce);
04876: }
04877:
04878: public void visitConstantValue(Java.ConstantValue cv) {
04879: res[0] = UnitCompiler.this .getNegatedConstantValue2(cv);
04880: }
04881:
04882: public void visitCrement(Java.Crement c) {
04883: res[0] = UnitCompiler.this .getNegatedConstantValue2(c);
04884: }
04885:
04886: public void visitInstanceof(Java.Instanceof io) {
04887: res[0] = UnitCompiler.this .getNegatedConstantValue2(io);
04888: }
04889:
04890: public void visitMethodInvocation(Java.MethodInvocation mi) {
04891: res[0] = UnitCompiler.this .getNegatedConstantValue2(mi);
04892: }
04893:
04894: public void visitSuperclassMethodInvocation(
04895: Java.SuperclassMethodInvocation smi) {
04896: res[0] = UnitCompiler.this
04897: .getNegatedConstantValue2(smi);
04898: }
04899:
04900: public void visitLiteral(Java.Literal l) {
04901: try {
04902: res[0] = UnitCompiler.this
04903: .getNegatedConstantValue2(l);
04904: } catch (CompileException e) {
04905: throw new UCE(e);
04906: }
04907: }
04908:
04909: public void visitNewAnonymousClassInstance(
04910: Java.NewAnonymousClassInstance naci) {
04911: res[0] = UnitCompiler.this
04912: .getNegatedConstantValue2(naci);
04913: }
04914:
04915: public void visitNewArray(Java.NewArray na) {
04916: res[0] = UnitCompiler.this .getNegatedConstantValue2(na);
04917: }
04918:
04919: public void visitNewInitializedArray(
04920: Java.NewInitializedArray nia) {
04921: res[0] = UnitCompiler.this
04922: .getNegatedConstantValue2(nia);
04923: }
04924:
04925: public void visitNewClassInstance(Java.NewClassInstance nci) {
04926: res[0] = UnitCompiler.this
04927: .getNegatedConstantValue2(nci);
04928: }
04929:
04930: public void visitParameterAccess(Java.ParameterAccess pa) {
04931: res[0] = UnitCompiler.this .getNegatedConstantValue2(pa);
04932: }
04933:
04934: public void visitQualifiedThisReference(
04935: Java.QualifiedThisReference qtr) {
04936: res[0] = UnitCompiler.this
04937: .getNegatedConstantValue2(qtr);
04938: }
04939:
04940: public void visitThisReference(Java.ThisReference tr) {
04941: res[0] = UnitCompiler.this .getNegatedConstantValue2(tr);
04942: }
04943:
04944: public void visitAmbiguousName(Java.AmbiguousName an) {
04945: res[0] = UnitCompiler.this .getNegatedConstantValue2(an);
04946: }
04947:
04948: public void visitArrayAccessExpression(
04949: Java.ArrayAccessExpression aae) {
04950: res[0] = UnitCompiler.this
04951: .getNegatedConstantValue2(aae);
04952: };
04953:
04954: public void visitFieldAccess(Java.FieldAccess fa) {
04955: res[0] = UnitCompiler.this .getNegatedConstantValue2(fa);
04956: }
04957:
04958: public void visitFieldAccessExpression(
04959: Java.FieldAccessExpression fae) {
04960: res[0] = UnitCompiler.this
04961: .getNegatedConstantValue2(fae);
04962: }
04963:
04964: public void visitLocalVariableAccess(
04965: Java.LocalVariableAccess lva) {
04966: res[0] = UnitCompiler.this
04967: .getNegatedConstantValue2(lva);
04968: }
04969:
04970: public void visitParenthesizedExpression(
04971: Java.ParenthesizedExpression pe) {
04972: try {
04973: res[0] = UnitCompiler.this
04974: .getNegatedConstantValue2(pe);
04975: } catch (CompileException e) {
04976: throw new UCE(e);
04977: }
04978: }
04979: };
04980: try {
04981: rv.accept(rvv);
04982: return res[0];
04983: } catch (UCE uce) {
04984: throw uce.ce;
04985: }
04986: }
04987:
04988: private Object getNegatedConstantValue2(Java.Rvalue rv) {
04989: return null;
04990: }
04991:
04992: private Object getNegatedConstantValue2(Java.UnaryOperation uo)
04993: throws CompileException {
04994: return (uo.operator.equals("+") ? this
04995: .getNegatedConstantValue(uo.operand) : uo.operator
04996: .equals("-") ? this .getConstantValue(uo.operand) : null);
04997: }
04998:
04999: private Object getNegatedConstantValue2(
05000: Java.ParenthesizedExpression pe) throws CompileException {
05001: return this .getNegatedConstantValue(pe.value);
05002: }
05003:
05004: private Object getNegatedConstantValue2(Java.Literal l)
05005: throws CompileException {
05006: if (l.value instanceof Integer)
05007: return new Integer(-((Integer) l.value).intValue());
05008: if (l.value instanceof Long)
05009: return new Long(-((Long) l.value).longValue());
05010: if (l.value instanceof Float)
05011: return new Float(-((Float) l.value).floatValue());
05012: if (l.value instanceof Double)
05013: return new Double(-((Double) l.value).doubleValue());
05014:
05015: this
05016: .compileError("Cannot negate this literal", l
05017: .getLocation());
05018: return null;
05019: }
05020:
05021: // ------------ BlockStatement.generatesCode() -------------
05022:
05023: /**
05024: * Check whether invocation of {@link #compile(Java.BlockStatement)} would
05025: * generate more than zero code bytes.
05026: */
05027: private boolean generatesCode(Java.BlockStatement bs)
05028: throws CompileException {
05029: final boolean[] res = new boolean[1];
05030: class UCE extends RuntimeException {
05031: final CompileException ce;
05032:
05033: UCE(CompileException ce) {
05034: this .ce = ce;
05035: }
05036: }
05037: Visitor.BlockStatementVisitor bsv = new Visitor.BlockStatementVisitor() {
05038: public void visitInitializer(Java.Initializer i) {
05039: try {
05040: res[0] = UnitCompiler.this .generatesCode2(i);
05041: } catch (CompileException e) {
05042: throw new UCE(e);
05043: }
05044: }
05045:
05046: public void visitFieldDeclaration(Java.FieldDeclaration fd) {
05047: try {
05048: res[0] = UnitCompiler.this .generatesCode2(fd);
05049: } catch (CompileException e) {
05050: throw new UCE(e);
05051: }
05052: }
05053:
05054: public void visitLabeledStatement(Java.LabeledStatement ls) {
05055: res[0] = UnitCompiler.this .generatesCode2(ls);
05056: }
05057:
05058: public void visitBlock(Java.Block b) {
05059: try {
05060: res[0] = UnitCompiler.this .generatesCode2(b);
05061: } catch (CompileException e) {
05062: throw new UCE(e);
05063: }
05064: }
05065:
05066: public void visitExpressionStatement(
05067: Java.ExpressionStatement es) {
05068: res[0] = UnitCompiler.this .generatesCode2(es);
05069: }
05070:
05071: public void visitIfStatement(Java.IfStatement is) {
05072: res[0] = UnitCompiler.this .generatesCode2(is);
05073: }
05074:
05075: public void visitForStatement(Java.ForStatement fs) {
05076: res[0] = UnitCompiler.this .generatesCode2(fs);
05077: }
05078:
05079: public void visitWhileStatement(Java.WhileStatement ws) {
05080: res[0] = UnitCompiler.this .generatesCode2(ws);
05081: }
05082:
05083: public void visitTryStatement(Java.TryStatement ts) {
05084: res[0] = UnitCompiler.this .generatesCode2(ts);
05085: }
05086:
05087: public void visitSwitchStatement(Java.SwitchStatement ss) {
05088: res[0] = UnitCompiler.this .generatesCode2(ss);
05089: }
05090:
05091: public void visitSynchronizedStatement(
05092: Java.SynchronizedStatement ss) {
05093: res[0] = UnitCompiler.this .generatesCode2(ss);
05094: }
05095:
05096: public void visitDoStatement(Java.DoStatement ds) {
05097: res[0] = UnitCompiler.this .generatesCode2(ds);
05098: }
05099:
05100: public void visitLocalVariableDeclarationStatement(
05101: Java.LocalVariableDeclarationStatement lvds) {
05102: res[0] = UnitCompiler.this .generatesCode2(lvds);
05103: }
05104:
05105: public void visitReturnStatement(Java.ReturnStatement rs) {
05106: res[0] = UnitCompiler.this .generatesCode2(rs);
05107: }
05108:
05109: public void visitThrowStatement(Java.ThrowStatement ts) {
05110: res[0] = UnitCompiler.this .generatesCode2(ts);
05111: }
05112:
05113: public void visitBreakStatement(Java.BreakStatement bs) {
05114: res[0] = UnitCompiler.this .generatesCode2(bs);
05115: }
05116:
05117: public void visitContinueStatement(Java.ContinueStatement cs) {
05118: res[0] = UnitCompiler.this .generatesCode2(cs);
05119: }
05120:
05121: public void visitEmptyStatement(Java.EmptyStatement es) {
05122: res[0] = UnitCompiler.this .generatesCode2(es);
05123: }
05124:
05125: public void visitLocalClassDeclarationStatement(
05126: Java.LocalClassDeclarationStatement lcds) {
05127: res[0] = UnitCompiler.this .generatesCode2(lcds);
05128: }
05129:
05130: public void visitAlternateConstructorInvocation(
05131: Java.AlternateConstructorInvocation aci) {
05132: res[0] = UnitCompiler.this .generatesCode2(aci);
05133: }
05134:
05135: public void visitSuperConstructorInvocation(
05136: Java.SuperConstructorInvocation sci) {
05137: res[0] = UnitCompiler.this .generatesCode2(sci);
05138: }
05139: };
05140: try {
05141: bs.accept(bsv);
05142: return res[0];
05143: } catch (UCE uce) {
05144: throw uce.ce;
05145: }
05146: }
05147:
05148: public boolean generatesCode2(Java.BlockStatement bs) {
05149: return true;
05150: }
05151:
05152: public boolean generatesCode2(Java.EmptyStatement es) {
05153: return false;
05154: }
05155:
05156: public boolean generatesCode2(
05157: Java.LocalClassDeclarationStatement lcds) {
05158: return false;
05159: }
05160:
05161: public boolean generatesCode2(Java.Initializer i)
05162: throws CompileException {
05163: return this .generatesCode(i.block);
05164: }
05165:
05166: public boolean generatesCode2(Java.Block b) throws CompileException {
05167: for (int i = 0; i < b.statements.size(); ++i) {
05168: if (this .generatesCode(((Java.BlockStatement) b.statements
05169: .get(i))))
05170: return true;
05171: }
05172: return false;
05173: }
05174:
05175: public boolean generatesCode2(Java.FieldDeclaration fd)
05176: throws CompileException {
05177: // Code is only generated if at least one of the declared variables has a
05178: // non-constant-final initializer.
05179: for (int i = 0; i < fd.variableDeclarators.length; ++i) {
05180: Java.VariableDeclarator vd = fd.variableDeclarators[i];
05181: if (this .getNonConstantFinalInitializer(fd, vd) != null)
05182: return true;
05183: }
05184: return false;
05185: }
05186:
05187: // ------------ BlockStatement.leave() -------------
05188:
05189: /**
05190: * Clean up the statement context. This is currently relevant for
05191: * "try ... catch ... finally" statements (execute "finally" clause)
05192: * and "synchronized" statements (monitorexit).
05193: * <p>
05194: * Statements like "return", "break", "continue" must call this method
05195: * for all the statements they terminate.
05196: * <p>
05197: * Notice: If <code>optionalStackValueType</code> is <code>null</code>,
05198: * then the operand stack is empty; otherwise exactly one operand with that
05199: * type is on the stack. This information is vital to implementations of
05200: * {@link #leave(Java.BlockStatement, IClass)} that require a specific
05201: * operand stack state (e.g. an empty operand stack for JSR).
05202: */
05203: private void leave(Java.BlockStatement bs,
05204: final IClass optionalStackValueType) {
05205: Visitor.BlockStatementVisitor bsv = new Visitor.BlockStatementVisitor() {
05206: public void visitInitializer(Java.Initializer i) {
05207: UnitCompiler.this .leave2(i, optionalStackValueType);
05208: }
05209:
05210: public void visitFieldDeclaration(Java.FieldDeclaration fd) {
05211: UnitCompiler.this .leave2(fd, optionalStackValueType);
05212: }
05213:
05214: public void visitLabeledStatement(Java.LabeledStatement ls) {
05215: UnitCompiler.this .leave2(ls, optionalStackValueType);
05216: }
05217:
05218: public void visitBlock(Java.Block b) {
05219: UnitCompiler.this .leave2(b, optionalStackValueType);
05220: }
05221:
05222: public void visitExpressionStatement(
05223: Java.ExpressionStatement es) {
05224: UnitCompiler.this .leave2(es, optionalStackValueType);
05225: }
05226:
05227: public void visitIfStatement(Java.IfStatement is) {
05228: UnitCompiler.this .leave2(is, optionalStackValueType);
05229: }
05230:
05231: public void visitForStatement(Java.ForStatement fs) {
05232: UnitCompiler.this .leave2(fs, optionalStackValueType);
05233: }
05234:
05235: public void visitWhileStatement(Java.WhileStatement ws) {
05236: UnitCompiler.this .leave2(ws, optionalStackValueType);
05237: }
05238:
05239: public void visitTryStatement(Java.TryStatement ts) {
05240: UnitCompiler.this .leave2(ts, optionalStackValueType);
05241: }
05242:
05243: public void visitSwitchStatement(Java.SwitchStatement ss) {
05244: UnitCompiler.this .leave2(ss, optionalStackValueType);
05245: }
05246:
05247: public void visitSynchronizedStatement(
05248: Java.SynchronizedStatement ss) {
05249: UnitCompiler.this .leave2(ss, optionalStackValueType);
05250: }
05251:
05252: public void visitDoStatement(Java.DoStatement ds) {
05253: UnitCompiler.this .leave2(ds, optionalStackValueType);
05254: }
05255:
05256: public void visitLocalVariableDeclarationStatement(
05257: Java.LocalVariableDeclarationStatement lvds) {
05258: UnitCompiler.this .leave2(lvds, optionalStackValueType);
05259: }
05260:
05261: public void visitReturnStatement(Java.ReturnStatement rs) {
05262: UnitCompiler.this .leave2(rs, optionalStackValueType);
05263: }
05264:
05265: public void visitThrowStatement(Java.ThrowStatement ts) {
05266: UnitCompiler.this .leave2(ts, optionalStackValueType);
05267: }
05268:
05269: public void visitBreakStatement(Java.BreakStatement bs) {
05270: UnitCompiler.this .leave2(bs, optionalStackValueType);
05271: }
05272:
05273: public void visitContinueStatement(Java.ContinueStatement cs) {
05274: UnitCompiler.this .leave2(cs, optionalStackValueType);
05275: }
05276:
05277: public void visitEmptyStatement(Java.EmptyStatement es) {
05278: UnitCompiler.this .leave2(es, optionalStackValueType);
05279: }
05280:
05281: public void visitLocalClassDeclarationStatement(
05282: Java.LocalClassDeclarationStatement lcds) {
05283: UnitCompiler.this .leave2(lcds, optionalStackValueType);
05284: }
05285:
05286: public void visitAlternateConstructorInvocation(
05287: Java.AlternateConstructorInvocation aci) {
05288: UnitCompiler.this .leave2(aci, optionalStackValueType);
05289: }
05290:
05291: public void visitSuperConstructorInvocation(
05292: Java.SuperConstructorInvocation sci) {
05293: UnitCompiler.this .leave2(sci, optionalStackValueType);
05294: }
05295: };
05296: bs.accept(bsv);
05297: }
05298:
05299: public void leave2(Java.BlockStatement bs,
05300: IClass optionalStackValueType) {
05301: ;
05302: }
05303:
05304: public void leave2(Java.SynchronizedStatement ss,
05305: IClass optionalStackValueType) {
05306: this .load((Java.Located) ss, this .iClassLoader.OBJECT,
05307: ss.monitorLvIndex);
05308: this .writeOpcode(ss, Opcode.MONITOREXIT);
05309: }
05310:
05311: public void leave2(Java.TryStatement ts,
05312: IClass optionalStackValueType) {
05313: if (ts.finallyOffset != null) {
05314:
05315: this .codeContext.saveLocalVariables();
05316: try {
05317: short sv = 0;
05318:
05319: // Obviously, JSR must always be executed with the operand stack being
05320: // empty; otherwise we get "java.lang.VerifyError: Inconsistent stack height
05321: // 1 != 2"
05322: if (optionalStackValueType != null) {
05323: sv = this .codeContext
05324: .allocateLocalVariable(Descriptor
05325: .size(optionalStackValueType
05326: .getDescriptor()));
05327: this .store((Java.Located) ts,
05328: optionalStackValueType, sv);
05329: }
05330:
05331: this .writeBranch(ts, Opcode.JSR, ts.finallyOffset);
05332:
05333: if (optionalStackValueType != null) {
05334: this .load((Java.Located) ts,
05335: optionalStackValueType, sv);
05336: }
05337: } finally {
05338: this .codeContext.restoreLocalVariables();
05339: }
05340: }
05341: }
05342:
05343: // ---------------- Lvalue.compileSet() -----------------
05344:
05345: /**
05346: * Generates code that stores a value in the {@link Java.Lvalue}.
05347: * Expects the {@link Java.Lvalue}'s context (see {@link
05348: * #compileContext}) and a value of the {@link Java.Lvalue}'s type
05349: * on the operand stack.
05350: */
05351: private void compileSet(Java.Lvalue lv) throws CompileException {
05352: class UCE extends RuntimeException {
05353: final CompileException ce;
05354:
05355: UCE(CompileException ce) {
05356: this .ce = ce;
05357: }
05358: }
05359: Visitor.LvalueVisitor lvv = new Visitor.LvalueVisitor() {
05360: public void visitAmbiguousName(Java.AmbiguousName an) {
05361: try {
05362: UnitCompiler.this .compileSet2(an);
05363: } catch (CompileException e) {
05364: throw new UCE(e);
05365: }
05366: }
05367:
05368: public void visitArrayAccessExpression(
05369: Java.ArrayAccessExpression aae) {
05370: try {
05371: UnitCompiler.this .compileSet2(aae);
05372: } catch (CompileException e) {
05373: throw new UCE(e);
05374: }
05375: }
05376:
05377: public void visitFieldAccess(Java.FieldAccess fa) {
05378: try {
05379: UnitCompiler.this .compileSet2(fa);
05380: } catch (CompileException e) {
05381: throw new UCE(e);
05382: }
05383: }
05384:
05385: public void visitFieldAccessExpression(
05386: Java.FieldAccessExpression fae) {
05387: try {
05388: UnitCompiler.this .compileSet2(fae);
05389: } catch (CompileException e) {
05390: throw new UCE(e);
05391: }
05392: }
05393:
05394: public void visitLocalVariableAccess(
05395: Java.LocalVariableAccess lva) {
05396: UnitCompiler.this .compileSet2(lva);
05397: }
05398:
05399: public void visitParenthesizedExpression(
05400: Java.ParenthesizedExpression pe) {
05401: try {
05402: UnitCompiler.this .compileSet2(pe);
05403: } catch (CompileException e) {
05404: throw new UCE(e);
05405: }
05406: }
05407: };
05408: try {
05409: lv.accept(lvv);
05410: } catch (UCE uce) {
05411: throw uce.ce;
05412: }
05413: }
05414:
05415: private void compileSet2(Java.AmbiguousName an)
05416: throws CompileException {
05417: this .compileSet(this .toLvalueOrCE(this .reclassify(an)));
05418: }
05419:
05420: private void compileSet2(Java.LocalVariableAccess lva) {
05421: this .store((Java.Located) lva, lva.localVariable.type,
05422: lva.localVariable);
05423: }
05424:
05425: private void compileSet2(Java.FieldAccess fa)
05426: throws CompileException {
05427: this .checkAccessible(fa.field, fa.getEnclosingBlockStatement());
05428: this .writeOpcode(fa, (fa.field.isStatic() ? Opcode.PUTSTATIC
05429: : Opcode.PUTFIELD));
05430: this .writeConstantFieldrefInfo(fa, fa.field
05431: .getDeclaringIClass().getDescriptor(), // classFD
05432: fa.field.getName(), // fieldName
05433: fa.field.getDescriptor() // fieldFD
05434: );
05435: }
05436:
05437: private void compileSet2(Java.ArrayAccessExpression aae)
05438: throws CompileException {
05439: this .writeOpcode(aae, Opcode.IASTORE
05440: + UnitCompiler.ilfdabcs(this .getType(aae)));
05441: }
05442:
05443: private void compileSet2(Java.FieldAccessExpression fae)
05444: throws CompileException {
05445: this .determineValue(fae);
05446: this .compileSet(this .toLvalueOrCE(fae.value));
05447: }
05448:
05449: private void compileSet2(Java.ParenthesizedExpression pe)
05450: throws CompileException {
05451: this .compileSet(this .toLvalueOrCE(pe.value));
05452: }
05453:
05454: // ---------------- Atom.getType() ----------------
05455:
05456: private IClass getType(Java.Atom a) throws CompileException {
05457: final IClass[] res = new IClass[1];
05458: class UCE extends RuntimeException {
05459: final CompileException ce;
05460:
05461: UCE(CompileException ce) {
05462: this .ce = ce;
05463: }
05464: }
05465: Visitor.AtomVisitor av = new Visitor.AtomVisitor() {
05466: public void visitPackage(Java.Package p) {
05467: try {
05468: res[0] = UnitCompiler.this .getType2(p);
05469: } catch (CompileException e) {
05470: throw new UCE(e);
05471: }
05472: }
05473:
05474: public void visitArrayType(Java.ArrayType at) {
05475: try {
05476: res[0] = UnitCompiler.this .getType2(at);
05477: } catch (CompileException e) {
05478: throw new UCE(e);
05479: }
05480: }
05481:
05482: public void visitBasicType(Java.BasicType bt) {
05483: res[0] = UnitCompiler.this .getType2(bt);
05484: }
05485:
05486: public void visitReferenceType(Java.ReferenceType rt) {
05487: try {
05488: res[0] = UnitCompiler.this .getType2(rt);
05489: } catch (CompileException e) {
05490: throw new UCE(e);
05491: }
05492: }
05493:
05494: public void visitRvalueMemberType(Java.RvalueMemberType rmt) {
05495: try {
05496: res[0] = UnitCompiler.this .getType2(rmt);
05497: } catch (CompileException e) {
05498: throw new UCE(e);
05499: }
05500: }
05501:
05502: public void visitSimpleType(Java.SimpleType st) {
05503: res[0] = UnitCompiler.this .getType2(st);
05504: }
05505:
05506: public void visitArrayLength(Java.ArrayLength al) {
05507: res[0] = UnitCompiler.this .getType2(al);
05508: }
05509:
05510: public void visitAssignment(Java.Assignment a) {
05511: try {
05512: res[0] = UnitCompiler.this .getType2(a);
05513: } catch (CompileException e) {
05514: throw new UCE(e);
05515: }
05516: }
05517:
05518: public void visitUnaryOperation(Java.UnaryOperation uo) {
05519: try {
05520: res[0] = UnitCompiler.this .getType2(uo);
05521: } catch (CompileException e) {
05522: throw new UCE(e);
05523: }
05524: }
05525:
05526: public void visitBinaryOperation(Java.BinaryOperation bo) {
05527: try {
05528: res[0] = UnitCompiler.this .getType2(bo);
05529: } catch (CompileException e) {
05530: throw new UCE(e);
05531: }
05532: }
05533:
05534: public void visitCast(Java.Cast c) {
05535: try {
05536: res[0] = UnitCompiler.this .getType2(c);
05537: } catch (CompileException e) {
05538: throw new UCE(e);
05539: }
05540: }
05541:
05542: public void visitClassLiteral(Java.ClassLiteral cl) {
05543: res[0] = UnitCompiler.this .getType2(cl);
05544: }
05545:
05546: public void visitConditionalExpression(
05547: Java.ConditionalExpression ce) {
05548: try {
05549: res[0] = UnitCompiler.this .getType2(ce);
05550: } catch (CompileException e) {
05551: throw new UCE(e);
05552: }
05553: }
05554:
05555: public void visitConstantValue(Java.ConstantValue cv) {
05556: res[0] = UnitCompiler.this .getType2(cv);
05557: }
05558:
05559: public void visitCrement(Java.Crement c) {
05560: try {
05561: res[0] = UnitCompiler.this .getType2(c);
05562: } catch (CompileException e) {
05563: throw new UCE(e);
05564: }
05565: }
05566:
05567: public void visitInstanceof(Java.Instanceof io) {
05568: res[0] = UnitCompiler.this .getType2(io);
05569: }
05570:
05571: public void visitMethodInvocation(Java.MethodInvocation mi) {
05572: try {
05573: res[0] = UnitCompiler.this .getType2(mi);
05574: } catch (CompileException e) {
05575: throw new UCE(e);
05576: }
05577: }
05578:
05579: public void visitSuperclassMethodInvocation(
05580: Java.SuperclassMethodInvocation smi) {
05581: try {
05582: res[0] = UnitCompiler.this .getType2(smi);
05583: } catch (CompileException e) {
05584: throw new UCE(e);
05585: }
05586: }
05587:
05588: public void visitLiteral(Java.Literal l) {
05589: res[0] = UnitCompiler.this .getType2(l);
05590: }
05591:
05592: public void visitNewAnonymousClassInstance(
05593: Java.NewAnonymousClassInstance naci) {
05594: res[0] = UnitCompiler.this .getType2(naci);
05595: }
05596:
05597: public void visitNewArray(Java.NewArray na) {
05598: try {
05599: res[0] = UnitCompiler.this .getType2(na);
05600: } catch (CompileException e) {
05601: throw new UCE(e);
05602: }
05603: }
05604:
05605: public void visitNewInitializedArray(
05606: Java.NewInitializedArray nia) {
05607: try {
05608: res[0] = UnitCompiler.this .getType2(nia);
05609: } catch (CompileException e) {
05610: throw new UCE(e);
05611: }
05612: }
05613:
05614: public void visitNewClassInstance(Java.NewClassInstance nci) {
05615: try {
05616: res[0] = UnitCompiler.this .getType2(nci);
05617: } catch (CompileException e) {
05618: throw new UCE(e);
05619: }
05620: }
05621:
05622: public void visitParameterAccess(Java.ParameterAccess pa) {
05623: try {
05624: res[0] = UnitCompiler.this .getType2(pa);
05625: } catch (CompileException e) {
05626: throw new UCE(e);
05627: }
05628: }
05629:
05630: public void visitQualifiedThisReference(
05631: Java.QualifiedThisReference qtr) {
05632: try {
05633: res[0] = UnitCompiler.this .getType2(qtr);
05634: } catch (CompileException e) {
05635: throw new UCE(e);
05636: }
05637: }
05638:
05639: public void visitThisReference(Java.ThisReference tr) {
05640: try {
05641: res[0] = UnitCompiler.this .getType2(tr);
05642: } catch (CompileException e) {
05643: throw new UCE(e);
05644: }
05645: }
05646:
05647: public void visitAmbiguousName(Java.AmbiguousName an) {
05648: try {
05649: res[0] = UnitCompiler.this .getType2(an);
05650: } catch (CompileException e) {
05651: throw new UCE(e);
05652: }
05653: }
05654:
05655: public void visitArrayAccessExpression(
05656: Java.ArrayAccessExpression aae) {
05657: try {
05658: res[0] = UnitCompiler.this .getType2(aae);
05659: } catch (CompileException e) {
05660: throw new UCE(e);
05661: }
05662: }
05663:
05664: public void visitFieldAccess(Java.FieldAccess fa) {
05665: try {
05666: res[0] = UnitCompiler.this .getType2(fa);
05667: } catch (CompileException e) {
05668: throw new UCE(e);
05669: }
05670: }
05671:
05672: public void visitFieldAccessExpression(
05673: Java.FieldAccessExpression fae) {
05674: try {
05675: res[0] = UnitCompiler.this .getType2(fae);
05676: } catch (CompileException e) {
05677: throw new UCE(e);
05678: }
05679: }
05680:
05681: public void visitLocalVariableAccess(
05682: Java.LocalVariableAccess lva) {
05683: res[0] = UnitCompiler.this .getType2(lva);
05684: }
05685:
05686: public void visitParenthesizedExpression(
05687: Java.ParenthesizedExpression pe) {
05688: try {
05689: res[0] = UnitCompiler.this .getType2(pe);
05690: } catch (CompileException e) {
05691: throw new UCE(e);
05692: }
05693: }
05694: };
05695: try {
05696: a.accept(av);
05697: return res[0] != null ? res[0] : this .iClassLoader.OBJECT;
05698: } catch (UCE uce) {
05699: throw uce.ce;
05700: }
05701: }
05702:
05703: private IClass getType2(Java.SimpleType st) {
05704: return st.iClass;
05705: }
05706:
05707: private IClass getType2(Java.BasicType bt) {
05708: switch (bt.index) {
05709: case Java.BasicType.VOID:
05710: return IClass.VOID;
05711: case Java.BasicType.BYTE:
05712: return IClass.BYTE;
05713: case Java.BasicType.SHORT:
05714: return IClass.SHORT;
05715: case Java.BasicType.CHAR:
05716: return IClass.CHAR;
05717: case Java.BasicType.INT:
05718: return IClass.INT;
05719: case Java.BasicType.LONG:
05720: return IClass.LONG;
05721: case Java.BasicType.FLOAT:
05722: return IClass.FLOAT;
05723: case Java.BasicType.DOUBLE:
05724: return IClass.DOUBLE;
05725: case Java.BasicType.BOOLEAN:
05726: return IClass.BOOLEAN;
05727: default:
05728: throw new RuntimeException("Invalid index " + bt.index);
05729: }
05730: }
05731:
05732: private IClass getType2(Java.ReferenceType rt)
05733: throws CompileException {
05734: Java.TypeDeclaration scopeTypeDeclaration = null;
05735: Java.CompilationUnit scopeCompilationUnit;
05736: for (Java.Scope s = rt.getEnclosingScope();; s = s
05737: .getEnclosingScope()) {
05738: if (s instanceof Java.TypeDeclaration
05739: && scopeTypeDeclaration == null) {
05740: scopeTypeDeclaration = (Java.TypeDeclaration) s;
05741: }
05742: if (s instanceof Java.CompilationUnit) {
05743: scopeCompilationUnit = (Java.CompilationUnit) s;
05744: break;
05745: }
05746: }
05747: /*
05748: {
05749: Java.Scope s = rt.getEnclosingScope();
05750:
05751: // Determine scope statement.
05752: if (s instanceof Java.Statement) {
05753: for (s = s.getEnclosingScope(); s instanceof Java.Statement; s = s.getEnclosingScope());
05754: }
05755:
05756: if (s instanceof Java.FunctionDeclarator) {
05757: s = s.getEnclosingScope();
05758: }
05759:
05760: // Determine scope class or interface.
05761: if (s instanceof Java.TypeDeclaration) {
05762: scopeTypeDeclaration = (Java.TypeDeclaration) s;
05763: }
05764:
05765: // Determine scope compilationUnit.
05766: while (!(s instanceof Java.CompilationUnit)) s = s.getEnclosingScope();
05767: scopeCompilationUnit = (Java.CompilationUnit) s;
05768: }
05769: */
05770: if (rt.identifiers.length == 1) {
05771:
05772: // 6.5.5.1 Simple type name (single identifier).
05773: String simpleTypeName = rt.identifiers[0];
05774:
05775: // 6.5.5.1.1 Local class.
05776: {
05777: Java.LocalClassDeclaration lcd = this
05778: .findLocalClassDeclaration(rt
05779: .getEnclosingScope(), simpleTypeName);
05780: if (lcd != null)
05781: return this .resolve(lcd);
05782: }
05783:
05784: // 6.5.5.1.2 Member type.
05785: if (scopeTypeDeclaration != null) { // If enclosed by another type declaration...
05786: for (Java.Scope s = scopeTypeDeclaration; !(s instanceof Java.CompilationUnit); s = s
05787: .getEnclosingScope()) {
05788: if (s instanceof Java.TypeDeclaration) {
05789: IClass mt = this
05790: .findMemberType(
05791: this
05792: .resolve((Java.AbstractTypeDeclaration) s),
05793: simpleTypeName, rt
05794: .getLocation());
05795: if (mt != null)
05796: return mt;
05797: }
05798: }
05799: }
05800:
05801: if (scopeCompilationUnit != null) {
05802:
05803: // 6.5.5.1.4a Single-type import.
05804: {
05805: IClass importedClass = this .importSingleType(
05806: simpleTypeName, rt.getLocation());
05807: if (importedClass != null)
05808: return importedClass;
05809: }
05810:
05811: // 6.5.5.1.4b Type declared in same compilation unit.
05812: {
05813: Java.PackageMemberTypeDeclaration pmtd = scopeCompilationUnit
05814: .getPackageMemberTypeDeclaration(simpleTypeName);
05815: if (pmtd != null)
05816: return this
05817: .resolve((Java.AbstractTypeDeclaration) pmtd);
05818: }
05819: }
05820:
05821: // 6.5.5.1.5 Type declared in other compilation unit of same
05822: // package.
05823: {
05824: String pkg = (scopeCompilationUnit.optionalPackageDeclaration == null ? null
05825: : scopeCompilationUnit.optionalPackageDeclaration.packageName);
05826: String className = pkg == null ? simpleTypeName : pkg
05827: + "." + simpleTypeName;
05828: IClass result;
05829: try {
05830: result = this .iClassLoader.loadIClass(Descriptor
05831: .fromClassName(className));
05832: } catch (ClassNotFoundException ex) {
05833: if (ex.getException() instanceof CompileException)
05834: throw (CompileException) ex.getException();
05835: throw new CompileException(className, rt
05836: .getLocation(), ex);
05837: }
05838: if (result != null)
05839: return result;
05840: }
05841:
05842: // 6.5.5.1.6 Type-import-on-demand declaration.
05843: {
05844: IClass importedClass = this .importTypeOnDemand(
05845: simpleTypeName, rt.getLocation());
05846: if (importedClass != null)
05847: return importedClass;
05848: }
05849:
05850: // 6.5.5.1.8 Give up.
05851: this .compileError("Cannot determine simple type name \""
05852: + simpleTypeName + "\"", rt.getLocation());
05853: return this .iClassLoader.OBJECT;
05854: } else {
05855:
05856: // 6.5.5.2 Qualified type name (two or more identifiers).
05857: Java.Atom q = this .reclassifyName(rt.getLocation(), rt
05858: .getEnclosingScope(), rt.identifiers,
05859: rt.identifiers.length - 1);
05860: String className;
05861:
05862: if (q instanceof Java.Package) {
05863:
05864: // 6.5.5.2.1 PACKAGE.CLASS
05865: className = Java.join(rt.identifiers, ".");
05866: } else {
05867:
05868: // 6.5.5.2.2 CLASS.CLASS (member type)
05869: className = (Descriptor.toClassName(this .getType(
05870: this .toTypeOrCE(q)).getDescriptor()) + '$' + rt.identifiers[rt.identifiers.length - 1]);
05871: }
05872: IClass result;
05873: try {
05874: result = this .iClassLoader.loadIClass(Descriptor
05875: .fromClassName(className));
05876: } catch (ClassNotFoundException ex) {
05877: if (ex.getException() instanceof CompileException)
05878: throw (CompileException) ex.getException();
05879: throw new CompileException(className, rt.getLocation(),
05880: ex);
05881: }
05882: if (result != null)
05883: return result;
05884:
05885: this .compileError("Class \"" + className + "\" not found",
05886: rt.getLocation());
05887: return this .iClassLoader.OBJECT;
05888: }
05889: }
05890:
05891: private IClass getType2(Java.RvalueMemberType rvmt)
05892: throws CompileException {
05893: IClass rvt = this .getType(rvmt.rvalue);
05894: IClass memberType = this .findMemberType(rvt, rvmt.identifier,
05895: rvmt.getLocation());
05896: if (memberType == null)
05897: this .compileError("\"" + rvt + "\" has no member type \""
05898: + rvmt.identifier + "\"", rvmt.getLocation());
05899: return memberType;
05900: }
05901:
05902: private IClass getType2(Java.ArrayType at) throws CompileException {
05903: return this .getType(at.componentType).getArrayIClass(
05904: this .iClassLoader.OBJECT);
05905: }
05906:
05907: private IClass getType2(Java.AmbiguousName an)
05908: throws CompileException {
05909: return this .getType(this .reclassify(an));
05910: }
05911:
05912: private IClass getType2(Java.Package p) throws CompileException {
05913: this .compileError(
05914: "Unknown variable or type \"" + p.name + "\"", p
05915: .getLocation());
05916: return this .iClassLoader.OBJECT;
05917: }
05918:
05919: private IClass getType2(Java.LocalVariableAccess lva) {
05920: return lva.localVariable.type;
05921: }
05922:
05923: private IClass getType2(Java.FieldAccess fa)
05924: throws CompileException {
05925: return fa.field.getType();
05926: }
05927:
05928: private IClass getType2(Java.ArrayLength al) {
05929: return IClass.INT;
05930: }
05931:
05932: private IClass getType2(Java.ThisReference tr)
05933: throws CompileException {
05934: return this .getIClass(tr);
05935: }
05936:
05937: private IClass getType2(Java.QualifiedThisReference qtr)
05938: throws CompileException {
05939: return this .getTargetIClass(qtr);
05940: }
05941:
05942: private IClass getType2(Java.ClassLiteral cl) {
05943: return this .iClassLoader.CLASS;
05944: }
05945:
05946: private IClass getType2(Java.Assignment a) throws CompileException {
05947: return this .getType(a.lhs);
05948: }
05949:
05950: private IClass getType2(Java.ConditionalExpression ce)
05951: throws CompileException {
05952: IClass mhsType = this .getType(ce.mhs);
05953: IClass rhsType = this .getType(ce.rhs);
05954:
05955: if (mhsType == rhsType) {
05956:
05957: // JLS 15.25.1.1
05958: return mhsType;
05959: } else if (mhsType.isPrimitiveNumeric()
05960: && rhsType.isPrimitiveNumeric()) {
05961:
05962: // JLS 15.25.1.2
05963:
05964: // TODO JLS 15.25.1.2.1
05965:
05966: // TODO JLS 15.25.1.2.2
05967:
05968: // JLS 15.25.1.2.3
05969: return this .binaryNumericPromotionType((Java.Locatable) ce,
05970: mhsType, rhsType);
05971: } else if (this .getConstantValue(ce.mhs) == Java.Rvalue.CONSTANT_VALUE_NULL
05972: && !rhsType.isPrimitive()) {
05973:
05974: // JLS 15.25.1.3 (null : ref)
05975: return rhsType;
05976: } else if (!mhsType.isPrimitive()
05977: && this .getConstantValue(ce.rhs) == Java.Rvalue.CONSTANT_VALUE_NULL) {
05978:
05979: // JLS 15.25.1.3 (ref : null)
05980: return mhsType;
05981: } else if (!mhsType.isPrimitive() && !rhsType.isPrimitive()) {
05982: if (mhsType.isAssignableFrom(rhsType)) {
05983: return mhsType;
05984: } else if (rhsType.isAssignableFrom(mhsType)) {
05985: return rhsType;
05986: } else {
05987: this .compileError("Reference types \"" + mhsType
05988: + "\" and \"" + rhsType + "\" don't match", ce
05989: .getLocation());
05990: return this .iClassLoader.OBJECT;
05991: }
05992: } else {
05993: this .compileError("Incompatible expression types \""
05994: + mhsType + "\" and \"" + rhsType + "\"", ce
05995: .getLocation());
05996: return this .iClassLoader.OBJECT;
05997: }
05998: }
05999:
06000: private IClass getType2(Java.Crement c) throws CompileException {
06001: return this .getType(c.operand);
06002: }
06003:
06004: private IClass getType2(Java.ArrayAccessExpression aae)
06005: throws CompileException {
06006: return this .getType(aae.lhs).getComponentType();
06007: }
06008:
06009: private IClass getType2(Java.FieldAccessExpression fae)
06010: throws CompileException {
06011: this .determineValue(fae);
06012: return this .getType(fae.value);
06013: }
06014:
06015: private IClass getType2(Java.UnaryOperation uo)
06016: throws CompileException {
06017: if (uo.operator == "!")
06018: return IClass.BOOLEAN;
06019: if (uo.operator == "+")
06020: return this .getType(uo.operand);
06021: if (uo.operator == "-" || uo.operator == "~")
06022: return this .unaryNumericPromotionType(uo, this
06023: .getType(uo.operand));
06024:
06025: this .compileError(
06026: "Unexpected operator \"" + uo.operator + "\"", uo
06027: .getLocation());
06028: return IClass.BOOLEAN;
06029: }
06030:
06031: private IClass getType2(Java.Instanceof io) {
06032: return IClass.BOOLEAN;
06033: }
06034:
06035: private IClass getType2(Java.BinaryOperation bo)
06036: throws CompileException {
06037: if (bo.op == "||" || bo.op == "&&" || bo.op == "=="
06038: || bo.op == "!=" || bo.op == "<" || bo.op == ">"
06039: || bo.op == "<=" || bo.op == ">=")
06040: return IClass.BOOLEAN;
06041:
06042: if (bo.op == "|" || bo.op == "^" || bo.op == "&")
06043: return (this .getType(bo.lhs) == IClass.BOOLEAN ? IClass.BOOLEAN
06044: : this .binaryNumericPromotionType(
06045: (Java.Locatable) bo, this .getType(bo.lhs),
06046: this .getType(bo.rhs)));
06047:
06048: if (bo.op == "*" || bo.op == "/" || bo.op == "%"
06049: || bo.op == "+" || bo.op == "-") {
06050: IClassLoader icl = this .iClassLoader;
06051:
06052: // Unroll the operands of this binary operation.
06053: Iterator ops = bo.unrollLeftAssociation();
06054:
06055: // Check the far left operand type.
06056: IClass lhsType = this .getType(((Java.Rvalue) ops.next()));
06057: if (bo.op == "+" && lhsType == icl.STRING)
06058: return icl.STRING;
06059:
06060: // Determine the expression type.
06061: do {
06062: IClass rhsType = this
06063: .getType(((Java.Rvalue) ops.next()));
06064: if (bo.op == "+" && rhsType == icl.STRING)
06065: return icl.STRING;
06066: lhsType = this .binaryNumericPromotionType(
06067: (Java.Locatable) bo, lhsType, rhsType);
06068: } while (ops.hasNext());
06069: return lhsType;
06070: }
06071:
06072: if (bo.op == "<<" || bo.op == ">>" || bo.op == ">>>") {
06073: IClass lhsType = this .getType(bo.lhs);
06074: return this .unaryNumericPromotionType((Java.Located) bo,
06075: lhsType);
06076: }
06077:
06078: this .compileError("Unexpected operator \"" + bo.op + "\"", bo
06079: .getLocation());
06080: return this .iClassLoader.OBJECT;
06081: }
06082:
06083: private IClass getType2(Java.Cast c) throws CompileException {
06084: return this .getType(c.targetType);
06085: }
06086:
06087: private IClass getType2(Java.ParenthesizedExpression pe)
06088: throws CompileException {
06089: return this .getType(pe.value);
06090: }
06091:
06092: // private IClass getType2(Java.ConstructorInvocation ci) {
06093: // throw new RuntimeException();
06094: // }
06095: private IClass getType2(Java.MethodInvocation mi)
06096: throws CompileException {
06097: if (mi.iMethod == null) {
06098: mi.iMethod = this .findIMethod(mi);
06099: }
06100: return mi.iMethod.getReturnType();
06101: }
06102:
06103: private IClass getType2(Java.SuperclassMethodInvocation scmi)
06104: throws CompileException {
06105: return this .findIMethod(scmi).getReturnType();
06106: }
06107:
06108: private IClass getType2(Java.NewClassInstance nci)
06109: throws CompileException {
06110: if (nci.iClass == null)
06111: nci.iClass = this .getType(nci.type);
06112: return nci.iClass;
06113: }
06114:
06115: private IClass getType2(Java.NewAnonymousClassInstance naci) {
06116: return this .resolve(naci.anonymousClassDeclaration);
06117: }
06118:
06119: private IClass getType2(Java.ParameterAccess pa)
06120: throws CompileException {
06121: return this .getLocalVariable(pa.formalParameter).type;
06122: }
06123:
06124: private IClass getType2(Java.NewArray na) throws CompileException {
06125: IClass res = this .getType(na.type);
06126: return res.getArrayIClass(na.dimExprs.length + na.dims,
06127: this .iClassLoader.OBJECT);
06128: }
06129:
06130: private IClass getType2(Java.NewInitializedArray nia)
06131: throws CompileException {
06132: return this .getType(nia.arrayType);
06133: }
06134:
06135: private IClass getType2(Java.Literal l) {
06136: if (l.value instanceof Integer)
06137: return IClass.INT;
06138: if (l.value instanceof Long)
06139: return IClass.LONG;
06140: if (l.value instanceof Float)
06141: return IClass.FLOAT;
06142: if (l.value instanceof Double)
06143: return IClass.DOUBLE;
06144: if (l.value instanceof String)
06145: return this .iClassLoader.STRING;
06146: if (l.value instanceof Character)
06147: return IClass.CHAR;
06148: if (l.value instanceof Boolean)
06149: return IClass.BOOLEAN;
06150: if (l.value == null)
06151: return IClass.VOID;
06152: throw new RuntimeException(
06153: "SNO: Unidentifiable literal type \""
06154: + l.value.getClass().getName() + "\"");
06155: }
06156:
06157: private IClass getType2(Java.ConstantValue cv) {
06158: IClass res = (cv.constantValue instanceof Integer ? IClass.INT
06159: : cv.constantValue instanceof Long ? IClass.LONG
06160: : cv.constantValue instanceof Float ? IClass.FLOAT
06161: : cv.constantValue instanceof Double ? IClass.DOUBLE
06162: : cv.constantValue instanceof String ? this .iClassLoader.STRING
06163: : cv.constantValue instanceof Character ? IClass.CHAR
06164: : cv.constantValue instanceof Boolean ? IClass.BOOLEAN
06165: : cv.constantValue == Java.Rvalue.CONSTANT_VALUE_NULL ? IClass.VOID
06166: : null);
06167: if (res == null)
06168: throw new RuntimeException(
06169: "SNO: Unidentifiable constant value type \""
06170: + cv.constantValue.getClass().getName()
06171: + "\"");
06172: return res;
06173: }
06174:
06175: // ---------------- Atom.isType() ---------------
06176:
06177: private boolean isType(Java.Atom a) throws CompileException {
06178: final boolean[] res = new boolean[1];
06179: class UCE extends RuntimeException {
06180: final CompileException ce;
06181:
06182: UCE(CompileException ce) {
06183: this .ce = ce;
06184: }
06185: }
06186: Visitor.AtomVisitor av = new Visitor.AtomVisitor() {
06187: public void visitPackage(Java.Package p) {
06188: res[0] = UnitCompiler.this .isType2(p);
06189: }
06190:
06191: public void visitArrayType(Java.ArrayType at) {
06192: res[0] = UnitCompiler.this .isType2(at);
06193: }
06194:
06195: public void visitBasicType(Java.BasicType bt) {
06196: res[0] = UnitCompiler.this .isType2(bt);
06197: }
06198:
06199: public void visitReferenceType(Java.ReferenceType rt) {
06200: res[0] = UnitCompiler.this .isType2(rt);
06201: }
06202:
06203: public void visitRvalueMemberType(Java.RvalueMemberType rmt) {
06204: res[0] = UnitCompiler.this .isType2(rmt);
06205: }
06206:
06207: public void visitSimpleType(Java.SimpleType st) {
06208: res[0] = UnitCompiler.this .isType2(st);
06209: }
06210:
06211: public void visitArrayLength(Java.ArrayLength al) {
06212: res[0] = UnitCompiler.this .isType2(al);
06213: }
06214:
06215: public void visitAssignment(Java.Assignment a) {
06216: res[0] = UnitCompiler.this .isType2(a);
06217: }
06218:
06219: public void visitUnaryOperation(Java.UnaryOperation uo) {
06220: res[0] = UnitCompiler.this .isType2(uo);
06221: }
06222:
06223: public void visitBinaryOperation(Java.BinaryOperation bo) {
06224: res[0] = UnitCompiler.this .isType2(bo);
06225: }
06226:
06227: public void visitCast(Java.Cast c) {
06228: res[0] = UnitCompiler.this .isType2(c);
06229: }
06230:
06231: public void visitClassLiteral(Java.ClassLiteral cl) {
06232: res[0] = UnitCompiler.this .isType2(cl);
06233: }
06234:
06235: public void visitConditionalExpression(
06236: Java.ConditionalExpression ce) {
06237: res[0] = UnitCompiler.this .isType2(ce);
06238: }
06239:
06240: public void visitConstantValue(Java.ConstantValue cv) {
06241: res[0] = UnitCompiler.this .isType2(cv);
06242: }
06243:
06244: public void visitCrement(Java.Crement c) {
06245: res[0] = UnitCompiler.this .isType2(c);
06246: }
06247:
06248: public void visitInstanceof(Java.Instanceof io) {
06249: res[0] = UnitCompiler.this .isType2(io);
06250: }
06251:
06252: public void visitMethodInvocation(Java.MethodInvocation mi) {
06253: res[0] = UnitCompiler.this .isType2(mi);
06254: }
06255:
06256: public void visitSuperclassMethodInvocation(
06257: Java.SuperclassMethodInvocation smi) {
06258: res[0] = UnitCompiler.this .isType2(smi);
06259: }
06260:
06261: public void visitLiteral(Java.Literal l) {
06262: res[0] = UnitCompiler.this .isType2(l);
06263: }
06264:
06265: public void visitNewAnonymousClassInstance(
06266: Java.NewAnonymousClassInstance naci) {
06267: res[0] = UnitCompiler.this .isType2(naci);
06268: }
06269:
06270: public void visitNewArray(Java.NewArray na) {
06271: res[0] = UnitCompiler.this .isType2(na);
06272: }
06273:
06274: public void visitNewInitializedArray(
06275: Java.NewInitializedArray nia) {
06276: res[0] = UnitCompiler.this .isType2(nia);
06277: }
06278:
06279: public void visitNewClassInstance(Java.NewClassInstance nci) {
06280: res[0] = UnitCompiler.this .isType2(nci);
06281: }
06282:
06283: public void visitParameterAccess(Java.ParameterAccess pa) {
06284: res[0] = UnitCompiler.this .isType2(pa);
06285: }
06286:
06287: public void visitQualifiedThisReference(
06288: Java.QualifiedThisReference qtr) {
06289: res[0] = UnitCompiler.this .isType2(qtr);
06290: }
06291:
06292: public void visitThisReference(Java.ThisReference tr) {
06293: res[0] = UnitCompiler.this .isType2(tr);
06294: }
06295:
06296: public void visitAmbiguousName(Java.AmbiguousName an) {
06297: try {
06298: res[0] = UnitCompiler.this .isType2(an);
06299: } catch (CompileException e) {
06300: throw new UCE(e);
06301: }
06302: }
06303:
06304: public void visitArrayAccessExpression(
06305: Java.ArrayAccessExpression aae) {
06306: res[0] = UnitCompiler.this .isType2(aae);
06307: }
06308:
06309: public void visitFieldAccess(Java.FieldAccess fa) {
06310: res[0] = UnitCompiler.this .isType2(fa);
06311: }
06312:
06313: public void visitFieldAccessExpression(
06314: Java.FieldAccessExpression fae) {
06315: res[0] = UnitCompiler.this .isType2(fae);
06316: }
06317:
06318: public void visitLocalVariableAccess(
06319: Java.LocalVariableAccess lva) {
06320: res[0] = UnitCompiler.this .isType2(lva);
06321: }
06322:
06323: public void visitParenthesizedExpression(
06324: Java.ParenthesizedExpression pe) {
06325: res[0] = UnitCompiler.this .isType2(pe);
06326: }
06327: };
06328: try {
06329: a.accept(av);
06330: return res[0];
06331: } catch (UCE uce) {
06332: throw uce.ce;
06333: }
06334: }
06335:
06336: private boolean isType2(Java.Atom a) {
06337: return a instanceof Java.Type;
06338: }
06339:
06340: private boolean isType2(Java.AmbiguousName an)
06341: throws CompileException {
06342: return this .isType(this .reclassify(an));
06343: }
06344:
06345: /**
06346: * Check whether the given {@link IClass.IMember} is accessible in the given context,
06347: * according to JLS 6.6.1.4. Issues a {@link #compileError(String)} if not.
06348: */
06349: private void checkAccessible(IClass.IMember member,
06350: Java.BlockStatement contextBlockStatement)
06351: throws CompileException {
06352: this .checkAccessible(member.getDeclaringIClass(), member
06353: .getAccess(), contextBlockStatement);
06354: }
06355:
06356: /**
06357: * Verify that a member (class, interface, field or method) declared in a
06358: * given class is accessible from a given block statement context, according
06359: * to JLS2 6.6.1.4.
06360: */
06361: private void checkAccessible(IClass iClassDeclaringMember,
06362: Access memberAccess,
06363: Java.BlockStatement contextBlockStatement)
06364: throws CompileException {
06365:
06366: // At this point, memberAccess is PUBLIC, DEFAULT, PROECTEDED or PRIVATE.
06367:
06368: // PUBLIC members are always accessible.
06369: if (memberAccess == Access.PUBLIC)
06370: return;
06371:
06372: // At this point, the member is DEFAULT, PROECTEDED or PRIVATE accessible.
06373:
06374: // Determine the class declaring the context block statement.
06375: IClass iClassDeclaringContextBlockStatement;
06376: for (Java.Scope s = contextBlockStatement.getEnclosingScope();; s = s
06377: .getEnclosingScope()) {
06378: if (s instanceof Java.TypeDeclaration) {
06379: iClassDeclaringContextBlockStatement = this
06380: .resolve((Java.TypeDeclaration) s);
06381: break;
06382: }
06383: }
06384:
06385: // Access is always allowed for block statements declared in the same class as the member.
06386: if (iClassDeclaringContextBlockStatement == iClassDeclaringMember)
06387: return;
06388:
06389: // Check whether the member and the context block statement are enclosed by the same
06390: // top-level type.
06391: {
06392: IClass topLevelIClassEnclosingMember = iClassDeclaringMember;
06393: for (IClass c = iClassDeclaringMember.getDeclaringIClass(); c != null; c = c
06394: .getDeclaringIClass()) {
06395: topLevelIClassEnclosingMember = c;
06396: }
06397: IClass topLevelIClassEnclosingContextBlockStatement = iClassDeclaringContextBlockStatement;
06398: for (IClass c = iClassDeclaringContextBlockStatement
06399: .getDeclaringIClass(); c != null; c = c
06400: .getDeclaringIClass()) {
06401: topLevelIClassEnclosingContextBlockStatement = c;
06402: }
06403:
06404: if (topLevelIClassEnclosingMember == topLevelIClassEnclosingContextBlockStatement)
06405: return;
06406: }
06407:
06408: if (memberAccess == Access.PRIVATE) {
06409: this .compileError(
06410: "Private member cannot be accessed from type \""
06411: + iClassDeclaringContextBlockStatement
06412: + "\".", contextBlockStatement
06413: .getLocation());
06414: return;
06415: }
06416:
06417: // At this point, the member is DEFAULT or PROTECTED accessible.
06418:
06419: // Check whether the member and the context block statement are declared in the same
06420: // package.
06421: if (Descriptor.areInSamePackage(iClassDeclaringMember
06422: .getDescriptor(), iClassDeclaringContextBlockStatement
06423: .getDescriptor()))
06424: return;
06425:
06426: if (memberAccess == Access.DEFAULT) {
06427: this .compileError("Member with \"" + memberAccess
06428: + "\" access cannot be accessed from type \""
06429: + iClassDeclaringContextBlockStatement + "\".",
06430: contextBlockStatement.getLocation());
06431: return;
06432: }
06433:
06434: // At this point, the member is PROTECTED accessible.
06435:
06436: // Check whether the class declaring the context block statement is a subclass of the
06437: // class declaring the member.
06438: if (!iClassDeclaringMember
06439: .isAssignableFrom(iClassDeclaringContextBlockStatement)) {
06440: this
06441: .compileError(
06442: "Protected member cannot be accessed from type \""
06443: + iClassDeclaringContextBlockStatement
06444: + "\", which is neither declared in the same package as or is a subclass of \""
06445: + iClassDeclaringMember + "\".",
06446: contextBlockStatement.getLocation());
06447: return;
06448: }
06449: return;
06450: }
06451:
06452: /**
06453: * Check whether the given {@link IClass} is accessible in the given context,
06454: * according to JLS2 6.6.1.2 and 6.6.1.4. Issues a
06455: * {@link #compileError(String)} if not.
06456: */
06457: private void checkAccessible(IClass type,
06458: Java.BlockStatement contextBlockStatement)
06459: throws CompileException {
06460:
06461: // Determine the type declaring the type.
06462: IClass iClassDeclaringType = type.getDeclaringIClass();
06463:
06464: // Check accessibility of package member type.
06465: if (iClassDeclaringType == null) {
06466: if (type.getAccess() == Access.PUBLIC) {
06467: return;
06468: } else if (type.getAccess() == Access.DEFAULT) {
06469:
06470: // Determine the type declaring the context block statement.
06471: IClass iClassDeclaringContextBlockStatement;
06472: for (Java.Scope s = contextBlockStatement
06473: .getEnclosingScope();; s = s
06474: .getEnclosingScope()) {
06475: if (s instanceof Java.TypeDeclaration) {
06476: iClassDeclaringContextBlockStatement = this
06477: .resolve((Java.TypeDeclaration) s);
06478: break;
06479: }
06480: }
06481:
06482: // Check whether the type is accessed from within the same package.
06483: String packageDeclaringType = Descriptor
06484: .getPackageName(type.getDescriptor());
06485: String contextPackage = Descriptor
06486: .getPackageName(iClassDeclaringContextBlockStatement
06487: .getDescriptor());
06488: if (!(packageDeclaringType == null ? contextPackage == null
06489: : packageDeclaringType.equals(contextPackage)))
06490: this .compileError("\"" + type
06491: + "\" is inaccessible from this package");
06492: return;
06493: } else {
06494: throw new RuntimeException("\"" + type
06495: + "\" has unexpected access \""
06496: + type.getAccess() + "\"");
06497: }
06498: }
06499:
06500: // "type" is a member type at this point.
06501:
06502: this .checkAccessible(iClassDeclaringType, type.getAccess(),
06503: contextBlockStatement);
06504: }
06505:
06506: private final Java.Type toTypeOrCE(Java.Atom a)
06507: throws CompileException {
06508: Java.Type result = a.toType();
06509: if (result == null) {
06510: this .compileError("Expression \"" + a.toString()
06511: + "\" is not a type", a.getLocation());
06512: return new Java.SimpleType(a.getLocation(),
06513: this .iClassLoader.OBJECT);
06514: }
06515: return result;
06516: }
06517:
06518: private final Java.Rvalue toRvalueOrCE(final Java.Atom a)
06519: throws CompileException {
06520: Java.Rvalue result = a.toRvalue();
06521: if (result == null) {
06522: this .compileError("Expression \"" + a.toString()
06523: + "\" is not an rvalue", a.getLocation());
06524: return new Java.Literal(a.getLocation(), "X");
06525: }
06526: return result;
06527: }
06528:
06529: public final Java.Lvalue toLvalueOrCE(final Java.Atom a)
06530: throws CompileException {
06531: Java.Lvalue result = a.toLvalue();
06532: if (result == null) {
06533: this .compileError("Expression \"" + a.toString()
06534: + "\" is not an lvalue", a.getLocation());
06535: return new Java.Lvalue(a.getLocation()) {
06536: public String toString() {
06537: return a.toString();
06538: }
06539:
06540: public void accept(Visitor.AtomVisitor visitor) {
06541: }
06542:
06543: public void accept(Visitor.RvalueVisitor visitor) {
06544: }
06545:
06546: public void accept(Visitor.LvalueVisitor visitor) {
06547: }
06548: };
06549: }
06550: return result;
06551: }
06552:
06553: /**
06554: * Copies the values of the synthetic parameters of this constructor ("this$..." and
06555: * "val$...") to the synthetic fields of the object ("this$..." and "val$...").
06556: */
06557: void assignSyntheticParametersToSyntheticFields(
06558: Java.ConstructorDeclarator cd) throws CompileException {
06559: for (Iterator it = cd.getDeclaringClass().syntheticFields
06560: .values().iterator(); it.hasNext();) {
06561: IClass.IField sf = (IClass.IField) it.next();
06562: Java.LocalVariable syntheticParameter = (Java.LocalVariable) cd.syntheticParameters
06563: .get(sf.getName());
06564: if (syntheticParameter == null)
06565: throw new RuntimeException(
06566: "SNO: Synthetic parameter for synthetic field \""
06567: + sf.getName() + "\" not found");
06568: try {
06569: Java.ExpressionStatement es = new Java.ExpressionStatement(
06570: new Java.Assignment(cd.getLocation(), // location
06571: new Java.FieldAccess( // lhs
06572: cd.getLocation(), // location
06573: new Java.ThisReference(cd
06574: .getLocation()), // lhs
06575: sf // field
06576: ), "=", // operator
06577: new Java.LocalVariableAccess( // rhs
06578: cd.getLocation(), // location
06579: syntheticParameter // localVariable
06580: )));
06581: es.setEnclosingScope(cd);
06582: this .compile(es);
06583: } catch (Parser.ParseException e) {
06584: throw new RuntimeException("S.N.O.");
06585: }
06586: }
06587: }
06588:
06589: /**
06590: * Compiles the instance variable initializers and the instance initializers in their
06591: * lexical order.
06592: */
06593: void initializeInstanceVariablesAndInvokeInstanceInitializers(
06594: Java.ConstructorDeclarator cd) throws CompileException {
06595: for (Iterator it = cd.getDeclaringClass().variableDeclaratorsAndInitializers
06596: .iterator(); it.hasNext();) {
06597: Java.TypeBodyDeclaration tbd = (Java.TypeBodyDeclaration) it
06598: .next();
06599: if (!tbd.isStatic()) {
06600: Java.BlockStatement bs = (Java.BlockStatement) tbd;
06601: if (!this .compile(bs))
06602: this
06603: .compileError(
06604: "Instance variable declarator or instance initializer does not complete normally",
06605: bs.getLocation());
06606: }
06607: }
06608: }
06609:
06610: /**
06611: * Statements that jump out of blocks ("return", "break", "continue")
06612: * must call this method to make sure that the "finally" clauses of all
06613: * "try...catch" statements are executed.
06614: */
06615: private void leaveStatements(Java.Scope from, Java.Scope to,
06616: IClass optionalStackValueType) {
06617: for (Java.Scope s = from; s != to; s = s.getEnclosingScope()) {
06618: if (s instanceof Java.BlockStatement) {
06619: this .leave((Java.BlockStatement) s,
06620: optionalStackValueType);
06621: }
06622: }
06623: }
06624:
06625: /**
06626: * The LHS operand of type <code>lhsType</code> is expected on the stack.
06627: * <p>
06628: * The following operators are supported:
06629: * <code> | ^ & * / % + - << >> >>></code>
06630: */
06631: private IClass compileArithmeticBinaryOperation(
06632: Java.Located located, IClass lhsType, String operator,
06633: Java.Rvalue rhs) throws CompileException {
06634: return this
06635: .compileArithmeticOperation(located, lhsType, Arrays
06636: .asList(new Java.Rvalue[] { rhs }).iterator(),
06637: operator);
06638: }
06639:
06640: /**
06641: * Execute an arithmetic operation on a sequence of <code>operands</code>. If
06642: * <code>type</code> is non-null, the first operand with that type is already on the stack.
06643: * <p>
06644: * The following operators are supported:
06645: * <code> | ^ & * / % + - << >> >>></code>
06646: */
06647: private IClass compileArithmeticOperation(Java.Located located,
06648: IClass type, Iterator operands, String operator)
06649: throws CompileException {
06650: if (operator == "|" || operator == "^" || operator == "&") {
06651: final int iopcode = (operator == "&" ? Opcode.IAND
06652: : operator == "|" ? Opcode.IOR
06653: : operator == "^" ? Opcode.IXOR
06654: : Integer.MAX_VALUE);
06655:
06656: do {
06657: Java.Rvalue operand = (Java.Rvalue) operands.next();
06658:
06659: if (type == null) {
06660: type = this .compileGetValue(operand);
06661: } else {
06662: CodeContext.Inserter convertLhsInserter = this .codeContext
06663: .newInserter();
06664: IClass rhsType = this .compileGetValue(operand);
06665:
06666: if (type.isPrimitiveNumeric()
06667: && rhsType.isPrimitiveNumeric()) {
06668: IClass promotedType = this
06669: .binaryNumericPromotion(located, type,
06670: convertLhsInserter, rhsType);
06671: if (promotedType == IClass.INT) {
06672: this .writeOpcode(located, iopcode);
06673: } else if (promotedType == IClass.LONG) {
06674: this .writeOpcode(located, iopcode + 1);
06675: } else {
06676: this .compileError("Operator \"" + operator
06677: + "\" not defined on types \""
06678: + type + "\" and \"" + rhsType
06679: + "\"", located.getLocation());
06680: }
06681: type = promotedType;
06682: } else if (type == IClass.BOOLEAN
06683: && rhsType == IClass.BOOLEAN) {
06684: this .writeOpcode(located, iopcode);
06685: type = IClass.BOOLEAN;
06686: } else {
06687: this .compileError("Operator \"" + operator
06688: + "\" not defined on types \"" + type
06689: + "\" and \"" + rhsType + "\"", located
06690: .getLocation());
06691: type = IClass.INT;
06692: }
06693: }
06694: } while (operands.hasNext());
06695: return type;
06696: }
06697:
06698: if (operator == "*" || operator == "/" || operator == "%"
06699: || operator == "+" || operator == "-") {
06700: final int iopcode = (operator == "*" ? Opcode.IMUL
06701: : operator == "/" ? Opcode.IDIV
06702: : operator == "%" ? Opcode.IREM
06703: : operator == "+" ? Opcode.IADD
06704: : operator == "-" ? Opcode.ISUB
06705: : Integer.MAX_VALUE);
06706:
06707: do {
06708: Java.Rvalue operand = (Java.Rvalue) operands.next();
06709:
06710: IClass operandType = this .getType(operand);
06711: IClassLoader icl = this .iClassLoader;
06712:
06713: // String concatenation?
06714: if (operator == "+"
06715: && (type == icl.STRING || operandType == icl.STRING)) {
06716:
06717: if (type != null)
06718: this .stringConversion(located, type);
06719:
06720: do {
06721: Object cv = this .getConstantValue(operand);
06722: if (cv == null) {
06723: this .stringConversion(located, this
06724: .compileGetValue(operand));
06725: operand = operands.hasNext() ? (Java.Rvalue) operands
06726: .next()
06727: : null;
06728: } else {
06729: if (operands.hasNext()) {
06730: operand = (Java.Rvalue) operands.next();
06731: Object cv2 = this
06732: .getConstantValue(operand);
06733: if (cv2 != null) {
06734: StringBuffer sb = new StringBuffer(
06735: cv.toString()).append(cv2);
06736: for (;;) {
06737: if (!operands.hasNext()) {
06738: operand = null;
06739: break;
06740: }
06741: operand = (Java.Rvalue) operands
06742: .next();
06743: Object cv3 = this
06744: .getConstantValue(operand);
06745: if (cv3 == null)
06746: break;
06747: sb.append(cv3);
06748: }
06749: cv = sb.toString();
06750: }
06751: } else {
06752: operand = null;
06753: }
06754: this .pushConstant(located, cv.toString());
06755: }
06756:
06757: // Concatenate.
06758: if (type != null) {
06759: this .writeOpcode(located,
06760: Opcode.INVOKEVIRTUAL);
06761: this .writeConstantMethodrefInfo(located,
06762: Descriptor.STRING, // classFD
06763: "concat", // methodName
06764: "(" + Descriptor.STRING + ")"
06765: + Descriptor.STRING // methodMD
06766: );
06767: }
06768: type = this .iClassLoader.STRING;
06769: } while (operand != null);
06770: return type;
06771: }
06772:
06773: if (type == null) {
06774: type = this .compileGetValue(operand);
06775: } else {
06776: CodeContext.Inserter convertLhsInserter = this .codeContext
06777: .newInserter();
06778: IClass rhsType = this .compileGetValue(operand);
06779:
06780: type = this .binaryNumericPromotion(located, type,
06781: convertLhsInserter, rhsType);
06782:
06783: int opcode;
06784: if (type == IClass.INT) {
06785: opcode = iopcode;
06786: } else if (type == IClass.LONG) {
06787: opcode = iopcode + 1;
06788: } else if (type == IClass.FLOAT) {
06789: opcode = iopcode + 2;
06790: } else if (type == IClass.DOUBLE) {
06791: opcode = iopcode + 3;
06792: } else {
06793: this .compileError("Unexpected promoted type \""
06794: + type + "\"", located.getLocation());
06795: opcode = iopcode;
06796: }
06797: this .writeOpcode(located, opcode);
06798: }
06799: } while (operands.hasNext());
06800: return type;
06801: }
06802:
06803: if (operator == "<<" || operator == ">>" || operator == ">>>") {
06804: final int iopcode = (operator == "<<" ? Opcode.ISHL
06805: : operator == ">>" ? Opcode.ISHR
06806: : operator == ">>>" ? Opcode.IUSHR
06807: : Integer.MAX_VALUE);
06808:
06809: do {
06810: Java.Rvalue operand = (Java.Rvalue) operands.next();
06811:
06812: if (type == null) {
06813: type = this .compileGetValue(operand);
06814: } else {
06815: CodeContext.Inserter convertLhsInserter = this .codeContext
06816: .newInserter();
06817: IClass rhsType = this .compileGetValue(operand);
06818:
06819: IClass promotedLhsType;
06820: this .codeContext.pushInserter(convertLhsInserter);
06821: try {
06822: promotedLhsType = this .unaryNumericPromotion(
06823: located, type);
06824: } finally {
06825: this .codeContext.popInserter();
06826: }
06827: if (promotedLhsType != IClass.INT
06828: && promotedLhsType != IClass.LONG)
06829: this .compileError(
06830: "Shift operation not allowed on operand type \""
06831: + type + "\"", located
06832: .getLocation());
06833:
06834: IClass promotedRhsType = this
06835: .unaryNumericPromotion(located, rhsType);
06836: if (promotedRhsType != IClass.INT
06837: && promotedRhsType != IClass.LONG)
06838: this .compileError("Shift distance of type \""
06839: + rhsType + "\" is not allowed",
06840: located.getLocation());
06841:
06842: if (promotedRhsType == IClass.LONG)
06843: this .writeOpcode(located, Opcode.L2I);
06844:
06845: this
06846: .writeOpcode(
06847: located,
06848: promotedLhsType == IClass.LONG ? iopcode + 1
06849: : iopcode);
06850: type = promotedLhsType;
06851: }
06852: } while (operands.hasNext());
06853: return type;
06854: }
06855:
06856: throw new RuntimeException("Unexpected operator \"" + operator
06857: + "\"");
06858: }
06859:
06860: /**
06861: * Convert object of type "sourceType" to type "String". JLS2 15.18.1.1
06862: */
06863: private void stringConversion(Java.Located located,
06864: IClass sourceType) {
06865: this .writeOpcode(located, Opcode.INVOKESTATIC);
06866: this
06867: .writeConstantMethodrefInfo(located,
06868: Descriptor.STRING, // classFD
06869: "valueOf", // methodName
06870: "("
06871: + ( // methodMD
06872: sourceType == IClass.BOOLEAN
06873: || sourceType == IClass.CHAR
06874: || sourceType == IClass.LONG
06875: || sourceType == IClass.FLOAT
06876: || sourceType == IClass.DOUBLE ? sourceType
06877: .getDescriptor()
06878: : sourceType == IClass.BYTE
06879: || sourceType == IClass.SHORT
06880: || sourceType == IClass.INT ? Descriptor.INT
06881: : Descriptor.OBJECT)
06882: + ")" + Descriptor.STRING);
06883: }
06884:
06885: /**
06886: * Expects the object to initialize on the stack.
06887: * <p>
06888: * Notice: This method is used both for explicit constructor invocation (first statement of
06889: * a constructor body) and implicit constructor invocation (right after NEW).
06890: *
06891: * @param optionalEnclosingInstance Used if the target class is an inner class
06892: */
06893: private void invokeConstructor(Java.Located located,
06894: Java.Scope scope, Java.Rvalue optionalEnclosingInstance,
06895: IClass targetClass, Java.Rvalue[] arguments)
06896: throws CompileException {
06897:
06898: // Find constructors.
06899: IClass.IConstructor[] iConstructors = targetClass
06900: .getDeclaredIConstructors();
06901: if (iConstructors.length == 0)
06902: throw new RuntimeException("SNO: Target class \""
06903: + targetClass.getDescriptor()
06904: + "\" has no constructors");
06905:
06906: IClass.IConstructor iConstructor = (IClass.IConstructor) this
06907: .findMostSpecificIInvocable(located, iConstructors, // iInvocables
06908: arguments // arguments
06909: );
06910:
06911: // Check exceptions that the constructor may throw.
06912: IClass[] thrownExceptions = iConstructor.getThrownExceptions();
06913: for (int i = 0; i < thrownExceptions.length; ++i) {
06914: this .checkThrownException(located, thrownExceptions[i],
06915: scope);
06916: }
06917:
06918: // Pass enclosing instance as a synthetic parameter.
06919: IClass outerIClass = targetClass.getOuterIClass();
06920: if (outerIClass != null) {
06921: if (optionalEnclosingInstance == null)
06922: this .compileError(
06923: "Enclosing instance for initialization of inner class \""
06924: + targetClass + "\" missing", located
06925: .getLocation());
06926: IClass eiic = this
06927: .compileGetValue(optionalEnclosingInstance);
06928: if (!outerIClass.isAssignableFrom(eiic))
06929: this .compileError("Type of enclosing instance (\""
06930: + eiic + "\") is not assignable to \""
06931: + outerIClass + "\"", located.getLocation());
06932: }
06933:
06934: // Pass local variables to constructor as synthetic parameters.
06935: {
06936: IClass.IField[] syntheticFields = targetClass
06937: .getSyntheticIFields();
06938:
06939: // Determine enclosing function declarator and type declaration.
06940: Java.TypeBodyDeclaration scopeTBD;
06941: Java.TypeDeclaration scopeTypeDeclaration;
06942: {
06943: Java.Scope s = scope;
06944: for (; !(s instanceof Java.TypeBodyDeclaration); s = s
06945: .getEnclosingScope())
06946: ;
06947: scopeTBD = (Java.TypeBodyDeclaration) s;
06948: scopeTypeDeclaration = scopeTBD.getDeclaringType();
06949: }
06950:
06951: if (!(scopeTypeDeclaration instanceof Java.ClassDeclaration)) {
06952: if (syntheticFields.length > 0)
06953: throw new RuntimeException(
06954: "SNO: Target class has synthetic fields");
06955: } else {
06956: Java.ClassDeclaration scopeClassDeclaration = (Java.ClassDeclaration) scopeTypeDeclaration;
06957: for (int i = 0; i < syntheticFields.length; ++i) {
06958: IClass.IField sf = syntheticFields[i];
06959: if (!sf.getName().startsWith("val$"))
06960: continue;
06961: IClass.IField eisf = (IClass.IField) scopeClassDeclaration.syntheticFields
06962: .get(sf.getName());
06963: if (eisf != null) {
06964: if (scopeTBD instanceof Java.MethodDeclarator) {
06965: this .load(located, this
06966: .resolve(scopeClassDeclaration), 0);
06967: this .writeOpcode(located, Opcode.GETFIELD);
06968: this .writeConstantFieldrefInfo(located,
06969: this .resolve(scopeClassDeclaration)
06970: .getDescriptor(), // classFD
06971: sf.getName(), // fieldName
06972: sf.getDescriptor() // fieldFD
06973: );
06974: } else if (scopeTBD instanceof Java.ConstructorDeclarator) {
06975: Java.ConstructorDeclarator constructorDeclarator = (Java.ConstructorDeclarator) scopeTBD;
06976: Java.LocalVariable syntheticParameter = (Java.LocalVariable) constructorDeclarator.syntheticParameters
06977: .get(sf.getName());
06978: if (syntheticParameter == null) {
06979: this
06980: .compileError(
06981: "Compiler limitation: Constructor cannot access local variable \""
06982: + sf
06983: .getName()
06984: .substring(
06985: 4)
06986: + "\" declared in an enclosing block because none of the methods accesses it. As a workaround, declare a dummy method that accesses the local variable.",
06987: located.getLocation());
06988: this .writeOpcode(located,
06989: Opcode.ACONST_NULL);
06990: } else {
06991: this .load(located, syntheticParameter);
06992: }
06993: } else {
06994: this
06995: .compileError(
06996: "Compiler limitation: Initializers cannot access local variables declared in an enclosing block.",
06997: located.getLocation());
06998: this .writeOpcode(located,
06999: Opcode.ACONST_NULL);
07000: }
07001: } else {
07002: String localVariableName = sf.getName()
07003: .substring(4);
07004: Java.LocalVariable lv;
07005: DETERMINE_LV: {
07006: Java.Scope s;
07007: for (s = scope; s instanceof Java.BlockStatement; s = s
07008: .getEnclosingScope()) {
07009: Java.BlockStatement bs = (Java.BlockStatement) s;
07010: Java.Scope es = bs.getEnclosingScope();
07011: if (!(es instanceof Java.Block))
07012: continue;
07013: Java.Block b = (Java.Block) es;
07014:
07015: for (Iterator it = b.statements
07016: .iterator();;) {
07017: Java.BlockStatement bs2 = (Java.BlockStatement) it
07018: .next();
07019: if (bs2 == bs)
07020: break;
07021: if (bs2 instanceof Java.LocalVariableDeclarationStatement) {
07022: Java.LocalVariableDeclarationStatement lvds = ((Java.LocalVariableDeclarationStatement) bs2);
07023: Java.VariableDeclarator[] vds = lvds.variableDeclarators;
07024: for (int j = 0; j < vds.length; ++j) {
07025: if (vds[j].name
07026: .equals(localVariableName)) {
07027: lv = this
07028: .getLocalVariable(
07029: lvds,
07030: vds[j]);
07031: break DETERMINE_LV;
07032: }
07033: }
07034: }
07035: }
07036: }
07037: while (!(s instanceof Java.FunctionDeclarator))
07038: s = s.getEnclosingScope();
07039: Java.FunctionDeclarator fd = (Java.FunctionDeclarator) s;
07040: for (int j = 0; j < fd.formalParameters.length; ++j) {
07041: Java.FunctionDeclarator.FormalParameter fp = fd.formalParameters[j];
07042: if (fp.name.equals(localVariableName)) {
07043: lv = this .getLocalVariable(fp);
07044: break DETERMINE_LV;
07045: }
07046: }
07047: throw new RuntimeException(
07048: "SNO: Synthetic field \""
07049: + sf.getName()
07050: + "\" neither maps a synthetic field of an enclosing instance nor a local variable");
07051: }
07052: this .load(located, lv);
07053: }
07054: }
07055: }
07056: }
07057:
07058: // Evaluate constructor arguments.
07059: IClass[] parameterTypes = iConstructor.getParameterTypes();
07060: for (int i = 0; i < arguments.length; ++i) {
07061: this .assignmentConversion((Java.Located) located, // located
07062: this .compileGetValue(arguments[i]), // sourceType
07063: parameterTypes[i], // targetType
07064: this .getConstantValue(arguments[i]) // optionalConstantValue
07065: );
07066: }
07067:
07068: // Invoke!
07069: // Notice that the method descriptor is "iConstructor.getDescriptor()" prepended with the
07070: // synthetic parameters.
07071: this .writeOpcode(located, Opcode.INVOKESPECIAL);
07072: this .writeConstantMethodrefInfo(located, targetClass
07073: .getDescriptor(), // classFD
07074: "<init>", // methodName
07075: iConstructor.getDescriptor() // methodMD
07076: );
07077: }
07078:
07079: /*package*/IClass.IField[] getIFields(
07080: final Java.FieldDeclaration fd) {
07081: IClass.IField[] res = new IClass.IField[fd.variableDeclarators.length];
07082: for (int i = 0; i < res.length; ++i) {
07083: final Java.VariableDeclarator vd = fd.variableDeclarators[i];
07084: res[i] = this .resolve(fd.getDeclaringType()).new IField() {
07085:
07086: // Implement IMember.
07087: public Access getAccess() {
07088: switch (fd.modifiers & Mod.PPP) {
07089: case Mod.PRIVATE:
07090: return Access.PRIVATE;
07091: case Mod.PROTECTED:
07092: return Access.PROTECTED;
07093: case Mod.PACKAGE:
07094: return Access.DEFAULT;
07095: case Mod.PUBLIC:
07096: return Access.PUBLIC;
07097: default:
07098: throw new RuntimeException("Invalid access");
07099: }
07100: }
07101:
07102: // Implement "IField".
07103: public boolean isStatic() {
07104: return (fd.modifiers & Mod.STATIC) != 0;
07105: }
07106:
07107: public IClass getType() throws CompileException {
07108: return UnitCompiler.this
07109: .getType(fd.type)
07110: .getArrayIClass(
07111: vd.brackets,
07112: UnitCompiler.this .iClassLoader.OBJECT);
07113: }
07114:
07115: public String getName() {
07116: return vd.name;
07117: }
07118:
07119: public Object getConstantValue()
07120: throws CompileException {
07121: if ((fd.modifiers & Mod.FINAL) != 0
07122: && vd.optionalInitializer instanceof Java.Rvalue) {
07123: Object constantInitializerValue = UnitCompiler.this
07124: .getConstantValue((Java.Rvalue) vd.optionalInitializer);
07125: if (constantInitializerValue != null)
07126: return UnitCompiler.this
07127: .assignmentConversion(
07128: (Java.Located) vd.optionalInitializer, // located
07129: constantInitializerValue, // value
07130: this .getType() // targetType
07131: );
07132: }
07133: return null;
07134: }
07135: };
07136: }
07137: return res;
07138: }
07139:
07140: /**
07141: * Determine the non-constant-final initializer of the given {@link Java.VariableDeclarator}.
07142: * @return <code>null</code> if the variable is declared without an initializer or if the initializer is constant-final
07143: */
07144: Java.ArrayInitializerOrRvalue getNonConstantFinalInitializer(
07145: Java.FieldDeclaration fd, Java.VariableDeclarator vd)
07146: throws CompileException {
07147:
07148: // Check if optional initializer exists.
07149: if (vd.optionalInitializer == null)
07150: return null;
07151:
07152: // Check if initializer is constant-final.
07153: if ((fd.modifiers & Mod.STATIC) != 0
07154: && (fd.modifiers & Mod.FINAL) != 0
07155: && vd.optionalInitializer instanceof Java.Rvalue
07156: && this
07157: .getConstantValue((Java.Rvalue) vd.optionalInitializer) != null)
07158: return null;
07159:
07160: return vd.optionalInitializer;
07161: }
07162:
07163: private Java.Atom reclassify(Java.AmbiguousName an)
07164: throws CompileException {
07165: if (an.reclassified == null) {
07166: an.reclassified = this .reclassifyName(an.getLocation(),
07167: (Java.Scope) an.getEnclosingBlockStatement(),
07168: an.identifiers, an.n);
07169: }
07170: return an.reclassified;
07171: }
07172:
07173: /**
07174: * JLS 6.5.2.2
07175: * <p>
07176: * Reclassify the ambiguous name consisting of the first <code>n</code> of the
07177: * <code>identifers</code>.
07178: * @param location
07179: * @param scope
07180: * @param identifiers
07181: * @param n
07182: * @throws CompileException
07183: */
07184: private Java.Atom reclassifyName(Location location,
07185: Java.Scope scope, final String[] identifiers, int n)
07186: throws CompileException {
07187:
07188: if (n == 1)
07189: return this .reclassifyName(location, scope, identifiers[0]);
07190:
07191: // 6.5.2.2
07192: Java.Atom lhs = this .reclassifyName(location, scope,
07193: identifiers, n - 1);
07194: String rhs = identifiers[n - 1];
07195:
07196: // 6.5.2.2.1
07197: if (UnitCompiler.DEBUG)
07198: System.out.println("lhs = " + lhs);
07199: if (lhs instanceof Java.Package) {
07200: String className = ((Java.Package) lhs).name + '.' + rhs;
07201: IClass result;
07202: try {
07203: result = this .iClassLoader.loadIClass(Descriptor
07204: .fromClassName(className));
07205: } catch (ClassNotFoundException ex) {
07206: if (ex.getException() instanceof CompileException)
07207: throw (CompileException) ex.getException();
07208: throw new CompileException(className, location, ex);
07209: }
07210: if (result != null)
07211: return new Java.SimpleType(location, result);
07212:
07213: return new Java.Package(location, className);
07214: }
07215:
07216: // 6.5.2.2.3.2 EXPRESSION.length
07217: if (rhs.equals("length") && this .getType(lhs).isArray()) {
07218: Java.ArrayLength al = new Java.ArrayLength(location, this
07219: .toRvalueOrCE(lhs));
07220: if (!(scope instanceof Java.BlockStatement)) {
07221: this
07222: .compileError("\".length\" only allowed in expression context");
07223: return al;
07224: }
07225: al.setEnclosingBlockStatement((Java.BlockStatement) scope);
07226: return al;
07227: }
07228:
07229: IClass lhsType = this .getType(lhs);
07230:
07231: // Notice: Don't need to check for 6.5.2.2.2.1 TYPE.METHOD and 6.5.2.2.3.1
07232: // EXPRESSION.METHOD here because that has been done before.
07233:
07234: {
07235: IClass.IField field = this .findIField(lhsType, rhs,
07236: location);
07237: if (field != null) {
07238: // 6.5.2.2.2.2 TYPE.FIELD
07239: // 6.5.2.2.3.2 EXPRESSION.FIELD
07240: Java.FieldAccess fa = new Java.FieldAccess(location,
07241: lhs, field);
07242: fa
07243: .setEnclosingBlockStatement((Java.BlockStatement) scope);
07244: return fa;
07245: }
07246: }
07247:
07248: IClass[] classes = lhsType.getDeclaredIClasses();
07249: for (int i = 0; i < classes.length; ++i) {
07250: final IClass memberType = classes[i];
07251: String name = Descriptor.toClassName(memberType
07252: .getDescriptor());
07253: name = name.substring(name.lastIndexOf('$') + 1);
07254: if (name.equals(rhs)) {
07255:
07256: // 6.5.2.2.2.3 TYPE.TYPE
07257: // 6.5.2.2.3.3 EXPRESSION.TYPE
07258: return new Java.SimpleType(location, memberType);
07259: }
07260: }
07261:
07262: this
07263: .compileError(
07264: "\""
07265: + rhs
07266: + "\" is neither a method, a field, nor a member class of \""
07267: + lhsType + "\"", location);
07268: return new Java.Atom(location) {
07269: public String toString() {
07270: return Java.join(identifiers, ".");
07271: }
07272:
07273: public final void accept(Visitor.AtomVisitor visitor) {
07274: }
07275: };
07276: }
07277:
07278: /**
07279: * JLS 6.5.2.1
07280: * @param location
07281: * @param scope
07282: * @param identifier
07283: * @throws CompileException
07284: */
07285: private Java.Atom reclassifyName(Location location,
07286: Java.Scope scope, final String identifier)
07287: throws CompileException {
07288:
07289: // Determine scope type body declaration, type and compilation unit.
07290: Java.TypeBodyDeclaration scopeTBD = null;
07291: Java.AbstractTypeDeclaration scopeTypeDeclaration = null;
07292: Java.CompilationUnit scopeCompilationUnit;
07293: {
07294: Java.Scope s = scope;
07295: while (s instanceof Java.BlockStatement
07296: && !(s instanceof Java.TypeBodyDeclaration))
07297: s = s.getEnclosingScope();
07298: if (s instanceof Java.TypeBodyDeclaration) {
07299: scopeTBD = (Java.TypeBodyDeclaration) s;
07300: s = s.getEnclosingScope();
07301: }
07302: if (s instanceof Java.TypeDeclaration) {
07303: scopeTypeDeclaration = (Java.AbstractTypeDeclaration) s;
07304: s = s.getEnclosingScope();
07305: }
07306: while (!(s instanceof Java.CompilationUnit))
07307: s = s.getEnclosingScope();
07308: scopeCompilationUnit = (Java.CompilationUnit) s;
07309: }
07310:
07311: // 6.5.2.1.BL1
07312:
07313: // 6.5.2.BL1.B1.B1.1/6.5.6.1.1 Local variable.
07314: // 6.5.2.BL1.B1.B1.2/6.5.6.1.1 Parameter.
07315: {
07316: Java.Scope s = scope;
07317: if (s instanceof Java.BlockStatement) {
07318: Java.LocalVariable lv = this .findLocalVariable(
07319: (Java.BlockStatement) s, identifier);
07320: if (lv != null) {
07321: Java.LocalVariableAccess lva = new Java.LocalVariableAccess(
07322: location, lv);
07323: if (!(scope instanceof Java.BlockStatement))
07324: throw new RuntimeException(
07325: "SNO: Local variable access in non-block statement context!?");
07326: lva
07327: .setEnclosingBlockStatement((Java.BlockStatement) scope);
07328: return lva;
07329: }
07330: s = s.getEnclosingScope();
07331: }
07332: while (s instanceof Java.BlockStatement)
07333: s = s.getEnclosingScope();
07334: if (s instanceof Java.FunctionDeclarator) {
07335: s = s.getEnclosingScope();
07336: if (s instanceof Java.InnerClassDeclaration) {
07337: Java.InnerClassDeclaration icd = (Java.InnerClassDeclaration) s;
07338: s = s.getEnclosingScope();
07339: if (s instanceof Java.AnonymousClassDeclaration)
07340: s = s.getEnclosingScope();
07341: while (s instanceof Java.BlockStatement) {
07342: Java.LocalVariable lv = this .findLocalVariable(
07343: (Java.BlockStatement) s, identifier);
07344: if (lv != null) {
07345: if (!lv.finaL)
07346: this
07347: .compileError("Cannot access non-final local variable \""
07348: + identifier
07349: + "\" from inner class");
07350: final IClass lvType = lv.type;
07351: IClass.IField iField = new SimpleIField(
07352: this .resolve(icd), "val$"
07353: + identifier, lvType);
07354: icd.defineSyntheticField(iField);
07355: Java.FieldAccess fa = new Java.FieldAccess(
07356: location, // location
07357: new Java.QualifiedThisReference( // lhs
07358: location, // location
07359: new Java.SimpleType(
07360: location,
07361: this .resolve(icd)) // qualification
07362: ), iField // field
07363: );
07364: fa
07365: .setEnclosingBlockStatement((Java.BlockStatement) scope);
07366: return fa;
07367: }
07368: s = s.getEnclosingScope();
07369: while (s instanceof Java.BlockStatement)
07370: s = s.getEnclosingScope();
07371: if (!(s instanceof Java.FunctionDeclarator))
07372: break;
07373: s = s.getEnclosingScope();
07374: if (!(s instanceof Java.InnerClassDeclaration))
07375: break;
07376: s = s.getEnclosingScope();
07377: }
07378: }
07379: }
07380: }
07381:
07382: // 6.5.2.BL1.B1.B1.3/6.5.6.1.2.1 Field.
07383: Java.BlockStatement enclosingBlockStatement = null;
07384: for (Java.Scope s = scope; !(s instanceof Java.CompilationUnit); s = s
07385: .getEnclosingScope()) {
07386: if (s instanceof Java.BlockStatement
07387: && enclosingBlockStatement == null)
07388: enclosingBlockStatement = (Java.BlockStatement) s;
07389: if (s instanceof Java.TypeDeclaration) {
07390: final IClass etd = UnitCompiler.this
07391: .resolve((Java.AbstractTypeDeclaration) s);
07392: final IClass.IField f = this .findIField(etd,
07393: identifier, location);
07394: if (f != null) {
07395: if (f.isStatic()) {
07396: this
07397: .warning(
07398: "IASF",
07399: "Implicit access to static field \""
07400: + identifier
07401: + "\" of declaring class (better write \""
07402: + f
07403: .getDeclaringIClass()
07404: + '.' + f.getName()
07405: + "\")", location);
07406: } else if (f.getDeclaringIClass() == etd) {
07407: this
07408: .warning(
07409: "IANSF",
07410: "Implicit access to non-static field \""
07411: + identifier
07412: + "\" of declaring class (better write \"this."
07413: + f.getName() + "\")",
07414: location);
07415: } else {
07416: this
07417: .warning(
07418: "IANSFEI",
07419: "Implicit access to non-static field \""
07420: + identifier
07421: + "\" of enclosing instance (better write \""
07422: + f
07423: .getDeclaringIClass()
07424: + ".this."
07425: + f.getName() + "\")",
07426: location);
07427: }
07428:
07429: Java.Type ct = new Java.SimpleType(
07430: scopeTypeDeclaration.getLocation(),
07431: (IClass) etd);
07432: Java.Atom lhs;
07433: if (scopeTBD.isStatic()) {
07434:
07435: // Field access in static method context.
07436: lhs = ct;
07437: } else {
07438:
07439: // Field access in non-static method context.
07440: if (f.isStatic()) {
07441:
07442: // Access to static field.
07443: lhs = ct;
07444: } else {
07445:
07446: // Access to non-static field.
07447: lhs = new Java.QualifiedThisReference(
07448: location, ct);
07449: }
07450: }
07451: Java.FieldAccess fa = new Java.FieldAccess(
07452: location, lhs, f);
07453: fa
07454: .setEnclosingBlockStatement(enclosingBlockStatement);
07455: return fa;
07456: }
07457: }
07458: }
07459:
07460: // Hack: "java" MUST be a package, not a class.
07461: if (identifier.equals("java"))
07462: return new Java.Package(location, identifier);
07463:
07464: // 6.5.2.BL1.B1.B2.1 Local class.
07465: {
07466: Java.LocalClassDeclaration lcd = this
07467: .findLocalClassDeclaration(scope, identifier);
07468: if (lcd != null)
07469: return new Java.SimpleType(location, this .resolve(lcd));
07470: }
07471:
07472: // 6.5.2.BL1.B1.B2.2 Member type.
07473: if (scopeTypeDeclaration != null) {
07474: IClass memberType = this .findMemberType(UnitCompiler.this
07475: .resolve(scopeTypeDeclaration), identifier,
07476: location);
07477: if (memberType != null)
07478: return new Java.SimpleType(location, memberType);
07479: }
07480:
07481: // 6.5.2.BL1.B1.B3.1 Single-type-import.
07482: if (scopeCompilationUnit != null) {
07483: IClass iClass = this .importSingleType(identifier, location);
07484: if (iClass != null)
07485: return new Java.SimpleType(location, iClass);
07486: }
07487:
07488: // 6.5.2.BL1.B1.B3.2 Package member class/interface declared in this compilation unit.
07489: if (scopeCompilationUnit != null) {
07490: Java.PackageMemberTypeDeclaration pmtd = scopeCompilationUnit
07491: .getPackageMemberTypeDeclaration(identifier);
07492: if (pmtd != null)
07493: return new Java.SimpleType(location, this
07494: .resolve((Java.AbstractTypeDeclaration) pmtd));
07495: }
07496:
07497: // 6.5.2.BL1.B1.B4 Class or interface declared in same package.
07498: if (scopeCompilationUnit != null) {
07499: String className = (scopeCompilationUnit.optionalPackageDeclaration == null ? identifier
07500: : scopeCompilationUnit.optionalPackageDeclaration.packageName
07501: + '.' + identifier);
07502: IClass result;
07503: try {
07504: result = this .iClassLoader.loadIClass(Descriptor
07505: .fromClassName(className));
07506: } catch (ClassNotFoundException ex) {
07507: if (ex.getException() instanceof CompileException)
07508: throw (CompileException) ex.getException();
07509: throw new CompileException(className, location, ex);
07510: }
07511: if (result != null)
07512: return new Java.SimpleType(location, result);
07513: }
07514:
07515: // 6.5.2.BL1.B1.B5, 6.5.2.BL1.B1.B6 Type-import-on-demand.
07516: if (scopeCompilationUnit != null) {
07517: IClass importedClass = this .importTypeOnDemand(identifier,
07518: location);
07519: if (importedClass != null) {
07520: return new Java.SimpleType(location, importedClass);
07521: }
07522: }
07523:
07524: // 6.5.2.BL1.B1.B7 Package name
07525: return new Java.Package(location, identifier);
07526: }
07527:
07528: /**
07529: * Find a local variable declared by the given <code>blockStatement</code> or any enclosing
07530: * scope up to the {@link Java.FunctionDeclarator}.
07531: */
07532: private Java.LocalVariable findLocalVariable(
07533: Java.BlockStatement blockStatement, String name)
07534: throws CompileException {
07535: for (Java.Scope s = blockStatement; !(s instanceof Java.CompilationUnit);) {
07536: Java.Scope es = s.getEnclosingScope();
07537: {
07538: if (s instanceof Java.ForStatement) {
07539: Java.BlockStatement optionalForInit = ((Java.ForStatement) s).optionalInit;
07540: if (optionalForInit instanceof Java.LocalVariableDeclarationStatement) {
07541: Java.LocalVariable lv = this
07542: .findLocalVariable(
07543: (Java.LocalVariableDeclarationStatement) optionalForInit,
07544: name);
07545: if (lv != null)
07546: return lv;
07547: }
07548: }
07549: if (es instanceof Java.Block) {
07550: Java.Block b = (Java.Block) es;
07551: for (Iterator it = b.statements.iterator();;) {
07552: Java.BlockStatement bs2 = (Java.BlockStatement) it
07553: .next();
07554: if (bs2 instanceof Java.LocalVariableDeclarationStatement) {
07555: Java.LocalVariable lv = this
07556: .findLocalVariable(
07557: (Java.LocalVariableDeclarationStatement) bs2,
07558: name);
07559: if (lv != null)
07560: return lv;
07561: }
07562: if (bs2 == s)
07563: break;
07564: }
07565: }
07566: if (es instanceof Java.SwitchStatement) {
07567: Java.SwitchStatement ss = (Java.SwitchStatement) es;
07568: SBSGS: for (Iterator it2 = ss.sbsgs.iterator();;) {
07569: Java.SwitchStatement.SwitchBlockStatementGroup sbgs = (Java.SwitchStatement.SwitchBlockStatementGroup) it2
07570: .next();
07571: for (Iterator it = sbgs.blockStatements
07572: .iterator(); it.hasNext();) {
07573: Java.BlockStatement bs2 = (Java.BlockStatement) it
07574: .next();
07575: if (bs2 instanceof Java.LocalVariableDeclarationStatement) {
07576: Java.LocalVariable lv = this
07577: .findLocalVariable(
07578: (Java.LocalVariableDeclarationStatement) bs2,
07579: name);
07580: if (lv != null)
07581: return lv;
07582: }
07583: if (bs2 == s)
07584: break SBSGS;
07585: }
07586: }
07587: }
07588: if (s instanceof Java.FunctionDeclarator) {
07589: Java.FunctionDeclarator fd = (Java.FunctionDeclarator) s;
07590: Java.FunctionDeclarator.FormalParameter[] fps = fd.formalParameters;
07591: for (int i = 0; i < fps.length; ++i) {
07592: if (fps[i].name.equals(name))
07593: return this .getLocalVariable(fps[i]);
07594: }
07595: return null;
07596: }
07597: if (s instanceof Java.CatchClause) {
07598: Java.CatchClause cc = (Java.CatchClause) s;
07599: if (cc.caughtException.name.equals(name))
07600: return this
07601: .getLocalVariable(cc.caughtException);
07602: }
07603: }
07604: s = es;
07605: }
07606: return null;
07607: }
07608:
07609: /**
07610: * Check whether the given {@link Java.LocalVariableDeclarationStatement} declares a variable
07611: * with the given <code>name</code>.
07612: */
07613: private Java.LocalVariable findLocalVariable(
07614: Java.LocalVariableDeclarationStatement lvds, String name)
07615: throws CompileException {
07616: Java.VariableDeclarator[] vds = lvds.variableDeclarators;
07617: for (int i = 0; i < vds.length; ++i) {
07618: if (vds[i].name.equals(name))
07619: return this .getLocalVariable(lvds, vds[i]);
07620: }
07621: return null;
07622: }
07623:
07624: private void determineValue(Java.FieldAccessExpression fae)
07625: throws CompileException {
07626: if (fae.value != null)
07627: return;
07628:
07629: IClass lhsType = this .getType(fae.lhs);
07630:
07631: if (fae.fieldName.equals("length") && lhsType.isArray()) {
07632: fae.value = new Java.ArrayLength(fae.getLocation(), this
07633: .toRvalueOrCE(fae.lhs));
07634: } else {
07635: IClass.IField iField = this .findIField(lhsType,
07636: fae.fieldName, fae.getLocation());
07637: if (iField == null) {
07638: this .compileError("\""
07639: + this .getType(fae.lhs).toString()
07640: + "\" has no field \"" + fae.fieldName + "\"",
07641: fae.getLocation());
07642: fae.value = new Java.Rvalue(fae.getLocation()) {
07643: // public IClass compileGet() throws CompileException { return this.iClassLoader.OBJECT; }
07644: public String toString() {
07645: return "???";
07646: }
07647:
07648: public final void accept(Visitor.AtomVisitor visitor) {
07649: }
07650:
07651: public final void accept(
07652: Visitor.RvalueVisitor visitor) {
07653: }
07654: };
07655: return;
07656: }
07657: fae.value = new Java.FieldAccess(fae.getLocation(),
07658: fae.lhs, iField);
07659: }
07660: fae.value.setEnclosingBlockStatement(fae
07661: .getEnclosingBlockStatement());
07662: }
07663:
07664: /**
07665: * Find named methods of "targetType", examine the argument types and choose the
07666: * most specific method. Check that only the allowed exceptions are thrown.
07667: * <p>
07668: * Notice that the returned {@link IClass.IMethod} may be declared in an enclosing type.
07669: *
07670: * @return The selected {@link IClass.IMethod} or <code>null</code>
07671: */
07672: public IClass.IMethod findIMethod(Java.MethodInvocation mi)
07673: throws CompileException {
07674: for (Java.Scope s = mi.getEnclosingBlockStatement(); !(s instanceof Java.CompilationUnit); s = s
07675: .getEnclosingScope()) {
07676: if (s instanceof Java.TypeDeclaration) {
07677: Java.TypeDeclaration td = (Java.TypeDeclaration) s;
07678:
07679: // Find methods with specified name.
07680: IClass.IMethod iMethod = this .findIMethod(
07681: (Java.Located) mi, // located
07682: ( // targetType
07683: mi.optionalTarget == null ? this .resolve(td)
07684: : this .getType(mi.optionalTarget)),
07685: mi.methodName, // methodName
07686: mi.arguments // arguments
07687: );
07688:
07689: // Check exceptions that the method may throw.
07690: IClass[] thrownExceptions = iMethod
07691: .getThrownExceptions();
07692: for (int i = 0; i < thrownExceptions.length; ++i) {
07693: this .checkThrownException((Java.Located) mi, // located
07694: thrownExceptions[i], // type
07695: (Java.Scope) mi
07696: .getEnclosingBlockStatement() // scope
07697: );
07698: }
07699:
07700: return iMethod;
07701: }
07702: }
07703: return null;
07704: }
07705:
07706: /**
07707: * Find {@link IClass.IMethod} by name and argument types. If more than one such
07708: * method exists, choose the most specific one (JLS 15.11.2).
07709: * <p>
07710: * Notice that the returned {@link IClass.IMethod} may be declared in an enclosing type.
07711: */
07712: private IClass.IMethod findIMethod(Java.Located located,
07713: IClass targetType, final String methodName,
07714: Java.Rvalue[] arguments) throws CompileException {
07715: for (IClass ic = targetType; ic != null; ic = ic
07716: .getDeclaringIClass()) {
07717: List l = new ArrayList();
07718: this .getIMethods(ic, methodName, l);
07719: if (l.size() > 0) {
07720:
07721: // Determine arguments' types, choose the most specific method
07722: IClass.IMethod iMethod = (IClass.IMethod) this
07723: .findMostSpecificIInvocable(located,
07724: (IClass.IMethod[]) l
07725: .toArray(new IClass.IMethod[l
07726: .size()]), // iInvocables
07727: arguments // arguments
07728: );
07729: return iMethod;
07730: }
07731: }
07732: this .compileError("Class \"" + targetType
07733: + "\" has no method named \"" + methodName + "\"",
07734: located.getLocation());
07735: final IClass[] pts = new IClass[arguments.length];
07736: for (int i = 0; i < arguments.length; ++i)
07737: pts[i] = this .getType(arguments[i]);
07738: return targetType.new IMethod() {
07739: public String getName() {
07740: return methodName;
07741: }
07742:
07743: public IClass getReturnType() throws CompileException {
07744: return IClass.INT;
07745: }
07746:
07747: public boolean isStatic() {
07748: return false;
07749: }
07750:
07751: public boolean isAbstract() {
07752: return false;
07753: }
07754:
07755: public IClass[] getParameterTypes() throws CompileException {
07756: return pts;
07757: }
07758:
07759: public IClass[] getThrownExceptions()
07760: throws CompileException {
07761: return new IClass[0];
07762: }
07763:
07764: public Access getAccess() {
07765: return Access.PUBLIC;
07766: }
07767: };
07768: }
07769:
07770: /**
07771: * Add all methods with the given <code>methodName</code> that are declared
07772: * by the <code>type</code>, its superclasses and all their superinterfaces
07773: * to the result list <code>v</code>.
07774: * @param type
07775: * @param methodName
07776: * @param v
07777: * @throws CompileException
07778: */
07779: public void getIMethods(IClass type, String methodName, List v // IMethod
07780: ) throws CompileException {
07781:
07782: // Check methods declared by this type.
07783: {
07784: IClass.IMethod[] ims = type.getDeclaredIMethods(methodName);
07785: for (int i = 0; i < ims.length; ++i)
07786: v.add(ims[i]);
07787: }
07788:
07789: // Check superclass.
07790: IClass super class = type.getSuperclass();
07791: if (super class != null)
07792: this .getIMethods(super class, methodName, v);
07793:
07794: // Check superinterfaces.
07795: IClass[] interfaces = type.getInterfaces();
07796: for (int i = 0; i < interfaces.length; ++i)
07797: this .getIMethods(interfaces[i], methodName, v);
07798:
07799: // JLS2 6.4.3
07800: if (super class == null && interfaces.length == 0
07801: && type.isInterface()) {
07802: IClass.IMethod[] oms = this .iClassLoader.OBJECT
07803: .getDeclaredIMethods(methodName);
07804: for (int i = 0; i < oms.length; ++i) {
07805: IClass.IMethod om = oms[i];
07806: if (!om.isStatic() && om.getAccess() == Access.PUBLIC)
07807: v.add(om);
07808: }
07809: }
07810: }
07811:
07812: public IClass.IMethod findIMethod(
07813: Java.SuperclassMethodInvocation scmi)
07814: throws CompileException {
07815: Java.ClassDeclaration declaringClass;
07816: for (Java.Scope s = scmi.getEnclosingBlockStatement();; s = s
07817: .getEnclosingScope()) {
07818: if (s instanceof Java.FunctionDeclarator) {
07819: Java.FunctionDeclarator fd = (Java.FunctionDeclarator) s;
07820: if ((fd.modifiers & Mod.STATIC) != 0)
07821: this
07822: .compileError(
07823: "Superclass method cannot be invoked in static context",
07824: scmi.getLocation());
07825: }
07826: if (s instanceof Java.ClassDeclaration) {
07827: declaringClass = (Java.ClassDeclaration) s;
07828: break;
07829: }
07830: }
07831: return this .findIMethod((Java.Located) scmi, // located
07832: this .resolve(declaringClass).getSuperclass(), // targetType
07833: scmi.methodName, // methodName
07834: scmi.arguments // arguments
07835: );
07836: }
07837:
07838: /**
07839: * Determine the arguments' types and choose the most specific invocable.
07840: *
07841: * @param iInvocables Length must be greater than zero
07842: *
07843: * @return The selected {@link IClass.IInvocable}
07844: * @throws CompileException
07845: */
07846: private IClass.IInvocable findMostSpecificIInvocable(
07847: Java.Located located,
07848: final IClass.IInvocable[] iInvocables,
07849: Java.Rvalue[] arguments) throws CompileException {
07850:
07851: // Determine arguments' types.
07852: IClass[] argumentTypes = new IClass[arguments.length];
07853: for (int i = 0; i < arguments.length; ++i) {
07854: argumentTypes[i] = this .getType(arguments[i]);
07855: }
07856: return this .findMostSpecificIInvocable(located, iInvocables,
07857: argumentTypes);
07858: }
07859:
07860: /**
07861: * Choose the most specific invocable.
07862: *
07863: * @param iInvocables Length must be greater than zero
07864: *
07865: * @throws CompileException
07866: */
07867: public IClass.IInvocable findMostSpecificIInvocable(
07868: Java.Located located,
07869: final IClass.IInvocable[] iInvocables,
07870: final IClass[] argumentTypes) throws CompileException {
07871: if (iInvocables.length == 0)
07872: throw new RuntimeException("SNO: Zero invocables");
07873:
07874: if (UnitCompiler.DEBUG) {
07875: System.out.println("Argument types:");
07876: for (int i = 0; i < argumentTypes.length; ++i) {
07877: System.out.println(argumentTypes[i]);
07878: }
07879: }
07880:
07881: // Select applicable methods (15.12.2.1).
07882: List applicableIInvocables = new ArrayList();
07883: NEXT_METHOD: for (int i = 0; i < iInvocables.length; ++i) {
07884: IClass.IInvocable ii = iInvocables[i];
07885:
07886: // Check parameter count.
07887: IClass[] parameterTypes = ii.getParameterTypes();
07888: if (parameterTypes.length != argumentTypes.length)
07889: continue;
07890:
07891: // Check argument types vs. parameter types.
07892: if (UnitCompiler.DEBUG)
07893: System.out.println("Parameter / argument type check:");
07894: for (int j = 0; j < argumentTypes.length; ++j) {
07895: // Is method invocation conversion possible (5.3)?
07896: if (UnitCompiler.DEBUG)
07897: System.out.println(parameterTypes[j] + " <=> "
07898: + argumentTypes[j]);
07899: if (!this .isMethodInvocationConvertible(
07900: argumentTypes[j], parameterTypes[j]))
07901: continue NEXT_METHOD;
07902: }
07903:
07904: // Applicable!
07905: if (UnitCompiler.DEBUG)
07906: System.out.println("Applicable!");
07907: applicableIInvocables.add(ii);
07908: }
07909: if (applicableIInvocables.size() == 0) {
07910: StringBuffer sb2 = new StringBuffer();
07911: if (argumentTypes.length == 0) {
07912: sb2.append("zero actual parameters");
07913: } else {
07914: sb2.append("actual parameters \"").append(
07915: argumentTypes[0]);
07916: for (int i = 1; i < argumentTypes.length; ++i) {
07917: sb2.append(", ").append(argumentTypes[i]);
07918: }
07919: sb2.append("\"");
07920: }
07921: StringBuffer sb = new StringBuffer('"' + iInvocables[0]
07922: .toString() + '"');
07923: for (int i = 1; i < iInvocables.length; ++i) {
07924: sb.append(", ").append(
07925: '"' + iInvocables[i].toString() + '"');
07926: }
07927: this .compileError(
07928: "No applicable constructor/method found for "
07929: + sb2.toString() + "; candidates are: "
07930: + sb.toString(), located.getLocation());
07931:
07932: // Well, returning a "fake" IInvocable is a bit tricky, because the iInvocables
07933: // can be of different types.
07934: if (iInvocables[0] instanceof IClass.IConstructor) {
07935: return iInvocables[0].getDeclaringIClass().new IConstructor() {
07936: public IClass[] getParameterTypes() {
07937: return argumentTypes;
07938: }
07939:
07940: public Access getAccess() {
07941: return Access.PUBLIC;
07942: }
07943:
07944: public IClass[] getThrownExceptions() {
07945: return new IClass[0];
07946: }
07947: };
07948: } else if (iInvocables[0] instanceof IClass.IMethod) {
07949: return iInvocables[0].getDeclaringIClass().new IMethod() {
07950: public boolean isStatic() {
07951: return true;
07952: }
07953:
07954: public boolean isAbstract() {
07955: return false;
07956: }
07957:
07958: public IClass getReturnType() {
07959: return IClass.INT;
07960: }
07961:
07962: public String getName() {
07963: return ((IClass.IMethod) iInvocables[0])
07964: .getName();
07965: }
07966:
07967: public Access getAccess() {
07968: return Access.PUBLIC;
07969: }
07970:
07971: public IClass[] getParameterTypes() {
07972: return argumentTypes;
07973: }
07974:
07975: public IClass[] getThrownExceptions() {
07976: return new IClass[0];
07977: }
07978: };
07979: } else {
07980: return iInvocables[0];
07981: }
07982: }
07983:
07984: // Choose the most specific invocable (15.12.2.2).
07985: if (applicableIInvocables.size() == 1) {
07986: return (IClass.IInvocable) applicableIInvocables.get(0);
07987: }
07988:
07989: // Determine the "maximally specific invocables".
07990: List maximallySpecificIInvocables = new ArrayList();
07991: for (int i = 0; i < applicableIInvocables.size(); ++i) {
07992: IClass.IInvocable applicableIInvocable = (IClass.IInvocable) applicableIInvocables
07993: .get(i);
07994: int moreSpecific = 0, lessSpecific = 0;
07995: for (int j = 0; j < maximallySpecificIInvocables.size(); ++j) {
07996: IClass.IInvocable mostSpecificIInvocable = (IClass.IInvocable) maximallySpecificIInvocables
07997: .get(j);
07998: if (applicableIInvocable
07999: .isMoreSpecificThan(mostSpecificIInvocable)) {
08000: ++moreSpecific;
08001: } else if (applicableIInvocable
08002: .isLessSpecificThan(mostSpecificIInvocable)) {
08003: ++lessSpecific;
08004: }
08005: }
08006: if (moreSpecific == maximallySpecificIInvocables.size()) {
08007: maximallySpecificIInvocables.clear();
08008: maximallySpecificIInvocables.add(applicableIInvocable);
08009: } else if (lessSpecific < maximallySpecificIInvocables
08010: .size()) {
08011: maximallySpecificIInvocables.add(applicableIInvocable);
08012: } else {
08013: ;
08014: }
08015: if (UnitCompiler.DEBUG)
08016: System.out.println("mostSpecificIInvocables="
08017: + maximallySpecificIInvocables);
08018: }
08019:
08020: if (maximallySpecificIInvocables.size() == 1)
08021: return (IClass.IInvocable) maximallySpecificIInvocables
08022: .get(0);
08023:
08024: ONE_NON_ABSTRACT_INVOCABLE: if (maximallySpecificIInvocables
08025: .size() > 1
08026: && iInvocables[0] instanceof IClass.IMethod) {
08027: final IClass.IMethod im = (IClass.IMethod) maximallySpecificIInvocables
08028: .get(0);
08029:
08030: // Check if all methods have the same signature (i.e. the types of all their
08031: // parameters are identical) and exactly one of the methods is non-abstract
08032: // (JLS 15.12.2.2.BL2.B1).
08033: IClass.IMethod theNonAbstractMethod = null;
08034: {
08035: Iterator it = maximallySpecificIInvocables.iterator();
08036: IClass.IMethod m = (IClass.IMethod) it.next();
08037: IClass[] parameterTypesOfFirstMethod = m
08038: .getParameterTypes();
08039: for (;;) {
08040: if (!m.isAbstract()) {
08041: if (theNonAbstractMethod != null)
08042: throw new RuntimeException(
08043: "SNO: More than one non-abstract method with same signature and same declaring class!?");
08044: theNonAbstractMethod = m;
08045: }
08046: if (!it.hasNext())
08047: break;
08048:
08049: m = (IClass.IMethod) it.next();
08050: IClass[] pts = m.getParameterTypes();
08051: for (int i = 0; i < pts.length; ++i) {
08052: if (pts[i] != parameterTypesOfFirstMethod[i])
08053: break ONE_NON_ABSTRACT_INVOCABLE;
08054: }
08055: }
08056: }
08057:
08058: // JLS 15.12.2.2.BL2.B1.B1
08059: if (theNonAbstractMethod != null)
08060: return theNonAbstractMethod;
08061:
08062: // JLS 15.12.2.2.BL2.B1.B2
08063: Set s = new HashSet();
08064: {
08065: IClass[][] tes = new IClass[maximallySpecificIInvocables
08066: .size()][];
08067: Iterator it = maximallySpecificIInvocables.iterator();
08068: for (int i = 0; i < tes.length; ++i) {
08069: tes[i] = ((IClass.IMethod) it.next())
08070: .getThrownExceptions();
08071: }
08072: for (int i = 0; i < tes.length; ++i) {
08073: EACH_EXCEPTION: for (int j = 0; j < tes[i].length; ++j) {
08074:
08075: // Check whether "that exception [te1] is declared in the THROWS
08076: // clause of each of the maximally specific methods".
08077: IClass te1 = tes[i][j];
08078: EACH_METHOD: for (int k = 0; k < tes.length; ++k) {
08079: if (k == i)
08080: continue;
08081: for (int l = 0; l < tes[k].length; ++l) {
08082: IClass te2 = tes[k][l];
08083: if (te2.isAssignableFrom(te1))
08084: continue EACH_METHOD;
08085: }
08086: continue EACH_EXCEPTION;
08087: }
08088: s.add(te1);
08089: }
08090: }
08091: }
08092:
08093: final IClass[] tes = (IClass[]) s.toArray(new IClass[s
08094: .size()]);
08095: return im.getDeclaringIClass().new IMethod() {
08096: public String getName() {
08097: return im.getName();
08098: }
08099:
08100: public IClass getReturnType() throws CompileException {
08101: return im.getReturnType();
08102: }
08103:
08104: public boolean isAbstract() {
08105: return true;
08106: }
08107:
08108: public boolean isStatic() {
08109: return false;
08110: }
08111:
08112: public Access getAccess() {
08113: return im.getAccess();
08114: }
08115:
08116: public IClass[] getParameterTypes()
08117: throws CompileException {
08118: return im.getParameterTypes();
08119: }
08120:
08121: public IClass[] getThrownExceptions() {
08122: return tes;
08123: }
08124: };
08125: }
08126:
08127: // JLS 15.12.2.2.BL2.B2
08128: {
08129: StringBuffer sb = new StringBuffer(
08130: "Invocation of constructor/method with actual parameter type(s) \"");
08131: for (int i = 0; i < argumentTypes.length; ++i) {
08132: if (i > 0)
08133: sb.append(", ");
08134: sb.append(Descriptor.toString(argumentTypes[i]
08135: .getDescriptor()));
08136: }
08137: sb.append("\" is ambiguous: ");
08138: for (int i = 0; i < maximallySpecificIInvocables.size(); ++i) {
08139: if (i > 0)
08140: sb.append(" vs. ");
08141: sb.append("\"" + maximallySpecificIInvocables.get(i)
08142: + "\"");
08143: }
08144: this .compileError(sb.toString(), located.getLocation());
08145: }
08146: return (IClass.IMethod) iInvocables[0];
08147: }
08148:
08149: /**
08150: * Check if "method invocation conversion" (5.3) is possible.
08151: */
08152: private boolean isMethodInvocationConvertible(IClass sourceType,
08153: IClass targetType) throws CompileException {
08154:
08155: // 5.3 Identity conversion.
08156: if (sourceType == targetType)
08157: return true;
08158:
08159: // 5.3 Widening primitive conversion.
08160: if (this .isWideningPrimitiveConvertible(sourceType, targetType))
08161: return true;
08162:
08163: // 5.3 Widening reference conversion.
08164: if (this .isWideningReferenceConvertible(sourceType, targetType))
08165: return true;
08166:
08167: // 5.3 TODO: FLOAT or DOUBLE value set conversion
08168:
08169: return false;
08170: }
08171:
08172: private void checkThrownException(Java.Located located,
08173: IClass type, Java.Scope scope) throws CompileException {
08174:
08175: // Thrown object must be assignable to "Throwable".
08176: if (!this .iClassLoader.THROWABLE.isAssignableFrom(type))
08177: this .compileError("Thrown object of type \"" + type
08178: + "\" is not assignable to \"Throwable\"", located
08179: .getLocation());
08180:
08181: // "RuntimeException" and "Error" are never checked.
08182: if (this .iClassLoader.RUNTIME_EXCEPTION.isAssignableFrom(type)
08183: || this .iClassLoader.ERROR.isAssignableFrom(type))
08184: return;
08185:
08186: for (;; scope = scope.getEnclosingScope()) {
08187:
08188: // Match against enclosing "try...catch" blocks.
08189: if (scope instanceof Java.TryStatement) {
08190: Java.TryStatement ts = (Java.TryStatement) scope;
08191: for (int i = 0; i < ts.catchClauses.size(); ++i) {
08192: Java.CatchClause cc = (Java.CatchClause) ts.catchClauses
08193: .get(i);
08194: IClass caughtType = this
08195: .getType(cc.caughtException.type);
08196: if (caughtType.isAssignableFrom(type))
08197: return;
08198: }
08199: } else
08200:
08201: // Match against "throws" clause of declaring function.
08202: if (scope instanceof Java.FunctionDeclarator) {
08203: Java.FunctionDeclarator fd = (Java.FunctionDeclarator) scope;
08204: for (int i = 0; i < fd.thrownExceptions.length; ++i) {
08205: IClass te = this .getType(fd.thrownExceptions[i]);
08206: if (te.isAssignableFrom(type))
08207: return;
08208: }
08209: break;
08210: } else
08211:
08212: if (scope instanceof Java.TypeBodyDeclaration) {
08213: break;
08214: }
08215: }
08216:
08217: this
08218: .compileError(
08219: "Thrown exception of type \""
08220: + type
08221: + "\" is neither caught by a \"try...catch\" block nor declared in the \"throws\" clause of the declaring function",
08222: located.getLocation());
08223: }
08224:
08225: private IClass getTargetIClass(Java.QualifiedThisReference qtr)
08226: throws CompileException {
08227:
08228: // Determine target type.
08229: if (qtr.targetIClass == null) {
08230: qtr.targetIClass = this .getType(qtr.qualification);
08231: }
08232: return qtr.targetIClass;
08233: }
08234:
08235: /**
08236: * Checks whether the operand is an integer-like local variable.
08237: */
08238: Java.LocalVariable isIntLV(Java.Crement c) throws CompileException {
08239: if (!(c.operand instanceof Java.AmbiguousName))
08240: return null;
08241: Java.AmbiguousName an = (Java.AmbiguousName) c.operand;
08242:
08243: Java.Atom rec = this .reclassify(an);
08244: if (!(rec instanceof Java.LocalVariableAccess))
08245: return null;
08246: Java.LocalVariableAccess lva = (Java.LocalVariableAccess) rec;
08247:
08248: Java.LocalVariable lv = lva.localVariable;
08249: if (lv.finaL)
08250: this
08251: .compileError(
08252: "Must not increment or decrement \"final\" local variable",
08253: lva.getLocation());
08254: if (lv.type == IClass.BYTE || lv.type == IClass.SHORT
08255: || lv.type == IClass.INT || lv.type == IClass.CHAR)
08256: return lv;
08257: return null;
08258: }
08259:
08260: private IClass resolve(final Java.TypeDeclaration td) {
08261: final Java.AbstractTypeDeclaration atd = (Java.AbstractTypeDeclaration) td;
08262: if (atd.resolvedType == null)
08263: atd.resolvedType = new IClass() {
08264: protected IClass.IMethod[] getDeclaredIMethods2() {
08265: IClass.IMethod[] res = new IClass.IMethod[atd.declaredMethods
08266: .size()];
08267: int i = 0;
08268: for (Iterator it = atd.declaredMethods.iterator(); it
08269: .hasNext();) {
08270: res[i++] = UnitCompiler.this
08271: .toIMethod((Java.MethodDeclarator) it
08272: .next());
08273: }
08274: return res;
08275: }
08276:
08277: private IClass[] declaredClasses = null;
08278:
08279: protected IClass[] getDeclaredIClasses2() {
08280: if (this .declaredClasses == null) {
08281: IClass[] mts = new IClass[atd.declaredClassesAndInterfaces
08282: .size()];
08283: int i = 0;
08284: for (Iterator it = atd.declaredClassesAndInterfaces
08285: .iterator(); it.hasNext();) {
08286: mts[i++] = UnitCompiler.this
08287: .resolve((Java.AbstractTypeDeclaration) it
08288: .next());
08289: }
08290: this .declaredClasses = mts;
08291: }
08292: return this .declaredClasses;
08293: }
08294:
08295: protected IClass getDeclaringIClass2() {
08296: Java.Scope s = atd;
08297: for (; !(s instanceof Java.TypeBodyDeclaration); s = s
08298: .getEnclosingScope()) {
08299: if (s instanceof Java.CompilationUnit)
08300: return null;
08301: }
08302: return UnitCompiler.this
08303: .resolve((Java.AbstractTypeDeclaration) s
08304: .getEnclosingScope());
08305: }
08306:
08307: protected IClass getOuterIClass2()
08308: throws CompileException {
08309: Java.AbstractTypeDeclaration oc = (Java.AbstractTypeDeclaration) UnitCompiler
08310: .getOuterClass(atd);
08311: if (oc == null)
08312: return null;
08313: return UnitCompiler.this .resolve(oc);
08314: }
08315:
08316: protected final String getDescriptor2() {
08317: return Descriptor.fromClassName(atd.getClassName());
08318: }
08319:
08320: public boolean isArray() {
08321: return false;
08322: }
08323:
08324: protected IClass getComponentType2() {
08325: throw new RuntimeException(
08326: "SNO: Non-array type has no component type");
08327: }
08328:
08329: public boolean isPrimitive() {
08330: return false;
08331: }
08332:
08333: public boolean isPrimitiveNumeric() {
08334: return false;
08335: }
08336:
08337: protected IConstructor[] getDeclaredIConstructors2() {
08338: if (atd instanceof Java.ClassDeclaration) {
08339: Java.ConstructorDeclarator[] cs = ((Java.ClassDeclaration) atd)
08340: .getConstructors();
08341:
08342: IClass.IConstructor[] res = new IClass.IConstructor[cs.length];
08343: for (int i = 0; i < cs.length; ++i)
08344: res[i] = UnitCompiler.this
08345: .toIConstructor(cs[i]);
08346: return res;
08347: }
08348: return new IClass.IConstructor[0];
08349: }
08350:
08351: protected IField[] getDeclaredIFields2() {
08352: if (atd instanceof Java.ClassDeclaration) {
08353: Java.ClassDeclaration cd = (Java.ClassDeclaration) atd;
08354: List l = new ArrayList(); // IClass.IField
08355:
08356: // Determine variable declarators of type declaration.
08357: for (int i = 0; i < cd.variableDeclaratorsAndInitializers
08358: .size(); ++i) {
08359: Java.BlockStatement vdoi = (Java.BlockStatement) cd.variableDeclaratorsAndInitializers
08360: .get(i);
08361: if (vdoi instanceof Java.FieldDeclaration) {
08362: Java.FieldDeclaration fd = (Java.FieldDeclaration) vdoi;
08363: IClass.IField[] flds = UnitCompiler.this
08364: .getIFields(fd);
08365: for (int j = 0; j < flds.length; ++j)
08366: l.add(flds[j]);
08367: }
08368: }
08369: return (IClass.IField[]) l
08370: .toArray(new IClass.IField[l.size()]);
08371: } else if (atd instanceof Java.InterfaceDeclaration) {
08372: Java.InterfaceDeclaration id = (Java.InterfaceDeclaration) atd;
08373: List l = new ArrayList();
08374:
08375: // Determine static fields.
08376: for (int i = 0; i < id.constantDeclarations
08377: .size(); ++i) {
08378: Java.BlockStatement bs = (Java.BlockStatement) id.constantDeclarations
08379: .get(i);
08380: if (bs instanceof Java.FieldDeclaration) {
08381: Java.FieldDeclaration fd = (Java.FieldDeclaration) bs;
08382: IClass.IField[] flds = UnitCompiler.this
08383: .getIFields(fd);
08384: for (int j = 0; j < flds.length; ++j)
08385: l.add(flds[j]);
08386: }
08387: }
08388: return (IClass.IField[]) l
08389: .toArray(new IClass.IField[l.size()]);
08390: } else {
08391: throw new RuntimeException(
08392: "SNO: AbstractTypeDeclaration is neither ClassDeclaration nor InterfaceDeclaration");
08393: }
08394: }
08395:
08396: public IField[] getSyntheticIFields() {
08397: if (atd instanceof Java.ClassDeclaration) {
08398: Collection c = ((Java.ClassDeclaration) atd).syntheticFields
08399: .values();
08400: return (IField[]) c
08401: .toArray(new IField[c.size()]);
08402: }
08403: return new IField[0];
08404: }
08405:
08406: protected IClass getSuperclass2()
08407: throws CompileException {
08408: if (atd instanceof Java.AnonymousClassDeclaration) {
08409: IClass bt = UnitCompiler.this
08410: .getType(((Java.AnonymousClassDeclaration) atd).baseType);
08411: return bt.isInterface() ? UnitCompiler.this .iClassLoader.OBJECT
08412: : bt;
08413: }
08414: if (atd instanceof Java.NamedClassDeclaration) {
08415: Java.NamedClassDeclaration ncd = (Java.NamedClassDeclaration) atd;
08416: if (ncd.optionalExtendedType == null)
08417: return UnitCompiler.this .iClassLoader.OBJECT;
08418: IClass super class = UnitCompiler.this
08419: .getType(ncd.optionalExtendedType);
08420: if (super class.isInterface())
08421: UnitCompiler.this
08422: .compileError(
08423: "\""
08424: + super class
08425: .toString()
08426: + "\" is an interface; classes can only extend a class",
08427: td.getLocation());
08428: return super class;
08429: }
08430: return null;
08431: }
08432:
08433: public Access getAccess() {
08434: return UnitCompiler.modifiers2Access(atd.modifiers);
08435: }
08436:
08437: public boolean isFinal() {
08438: return (atd.modifiers & Mod.FINAL) != 0;
08439: }
08440:
08441: protected IClass[] getInterfaces2()
08442: throws CompileException {
08443: if (atd instanceof Java.AnonymousClassDeclaration) {
08444: IClass bt = UnitCompiler.this
08445: .getType(((Java.AnonymousClassDeclaration) atd).baseType);
08446: return bt.isInterface() ? new IClass[] { bt }
08447: : new IClass[0];
08448: } else if (atd instanceof Java.NamedClassDeclaration) {
08449: Java.NamedClassDeclaration ncd = (Java.NamedClassDeclaration) atd;
08450: IClass[] res = new IClass[ncd.implementedTypes.length];
08451: for (int i = 0; i < res.length; ++i) {
08452: res[i] = UnitCompiler.this
08453: .getType(ncd.implementedTypes[i]);
08454: if (!res[i].isInterface())
08455: UnitCompiler.this
08456: .compileError(
08457: "\""
08458: + res[i]
08459: .toString()
08460: + "\" is not an interface; classes can only implement interfaces",
08461: td.getLocation());
08462: }
08463: return res;
08464: } else if (atd instanceof Java.InterfaceDeclaration) {
08465: Java.InterfaceDeclaration id = (Java.InterfaceDeclaration) atd;
08466: IClass[] res = new IClass[id.extendedTypes.length];
08467: for (int i = 0; i < res.length; ++i) {
08468: res[i] = UnitCompiler.this
08469: .getType(id.extendedTypes[i]);
08470: if (!res[i].isInterface())
08471: UnitCompiler.this
08472: .compileError(
08473: "\""
08474: + res[i]
08475: .toString()
08476: + "\" is not an interface; interfaces can only extend interfaces",
08477: td.getLocation());
08478: }
08479: return res;
08480: } else {
08481: throw new RuntimeException(
08482: "SNO: AbstractTypeDeclaration is neither ClassDeclaration nor InterfaceDeclaration");
08483: }
08484: }
08485:
08486: public boolean isAbstract() {
08487: return ((atd instanceof Java.InterfaceDeclaration) || (atd.modifiers & Mod.ABSTRACT) != 0);
08488: }
08489:
08490: public boolean isInterface() {
08491: return atd instanceof Java.InterfaceDeclaration;
08492: }
08493: };
08494:
08495: return atd.resolvedType;
08496: }
08497:
08498: private void referenceThis(Java.Located located,
08499: Java.ClassDeclaration declaringClass,
08500: Java.TypeBodyDeclaration declaringTypeBodyDeclaration,
08501: IClass targetIClass) throws CompileException {
08502: List path = UnitCompiler.getOuterClasses(declaringClass);
08503:
08504: if (declaringTypeBodyDeclaration.isStatic())
08505: this .compileError(
08506: "No current instance available in static context",
08507: located.getLocation());
08508:
08509: int j;
08510: TARGET_FOUND: {
08511: for (j = 0; j < path.size(); ++j) {
08512:
08513: // Notice: JLS 15.9.2.BL1.B3.B1.B2 seems to be wrong: Obviously, JAVAC does not
08514: // only allow
08515: //
08516: // O is the nth lexically enclosing class
08517: //
08518: // , but also
08519: //
08520: // O is assignable from the nth lexically enclosing class
08521: //
08522: // However, this strategy bears the risk of ambiguities, because "O" may be
08523: // assignable from more than one enclosing class.
08524: if (targetIClass.isAssignableFrom(this
08525: .resolve((Java.AbstractTypeDeclaration) path
08526: .get(j))))
08527: break TARGET_FOUND;
08528: }
08529: this .compileError("\"" + declaringClass
08530: + "\" is not enclosed by \"" + targetIClass + "\"",
08531: located.getLocation());
08532: }
08533:
08534: int i;
08535: if (declaringTypeBodyDeclaration instanceof Java.ConstructorDeclarator) {
08536: if (j == 0) {
08537: this .writeOpcode(located, Opcode.ALOAD_0);
08538: return;
08539: }
08540:
08541: Java.ConstructorDeclarator constructorDeclarator = (Java.ConstructorDeclarator) declaringTypeBodyDeclaration;
08542: Java.LocalVariable syntheticParameter = (Java.LocalVariable) constructorDeclarator.syntheticParameters
08543: .get("this$" + (path.size() - 2));
08544: if (syntheticParameter == null)
08545: throw new RuntimeException(
08546: "SNO: Synthetic \"this$*\" parameter not found");
08547: this .load(located, syntheticParameter);
08548: i = 1;
08549: } else {
08550: this .writeOpcode(located, Opcode.ALOAD_0);
08551: i = 0;
08552: }
08553: for (; i < j; ++i) {
08554: final String fieldName = "this$" + (path.size() - i - 2);
08555: final Java.InnerClassDeclaration inner = (Java.InnerClassDeclaration) path
08556: .get(i);
08557: IClass iic = this
08558: .resolve((Java.AbstractTypeDeclaration) inner);
08559: final Java.TypeDeclaration outer = (Java.TypeDeclaration) path
08560: .get(i + 1);
08561: final IClass oic = this
08562: .resolve((Java.AbstractTypeDeclaration) outer);
08563: inner.defineSyntheticField(new SimpleIField(iic, fieldName,
08564: oic));
08565: this .writeOpcode(located, Opcode.GETFIELD);
08566: this .writeConstantFieldrefInfo(located,
08567: iic.getDescriptor(), // classFD
08568: fieldName, // fieldName
08569: oic.getDescriptor() // fieldFD
08570: );
08571: }
08572: }
08573:
08574: /**
08575: * Return a list consisting of the given <code>inner</code> class and all its outer classes.
08576: * @return {@link List} of {@link Java.TypeDeclaration}
08577: */
08578: private static List getOuterClasses(Java.TypeDeclaration inner) {
08579: List path = new ArrayList();
08580: for (Java.TypeDeclaration ic = inner; ic != null; ic = UnitCompiler
08581: .getOuterClass(ic))
08582: path.add(ic);
08583: return path;
08584: }
08585:
08586: /*package*/static Java.TypeDeclaration getOuterClass(
08587: Java.TypeDeclaration atd) {
08588:
08589: // Package member class declaration.
08590: if (atd instanceof Java.PackageMemberClassDeclaration)
08591: return null;
08592:
08593: // Local class declaration.
08594: if (atd instanceof Java.LocalClassDeclaration) {
08595: Java.Scope s = atd.getEnclosingScope();
08596: for (; !(s instanceof Java.FunctionDeclarator); s = s
08597: .getEnclosingScope())
08598: ;
08599: boolean isStaticMethod = (s instanceof Java.MethodDeclarator)
08600: && (((Java.FunctionDeclarator) s).modifiers & Mod.STATIC) != 0;
08601: for (; !(s instanceof Java.TypeDeclaration); s = s
08602: .getEnclosingScope())
08603: ;
08604: Java.TypeDeclaration immediatelyEnclosingTypeDeclaration = (Java.TypeDeclaration) s;
08605: return (immediatelyEnclosingTypeDeclaration instanceof Java.ClassDeclaration && !isStaticMethod) ? immediatelyEnclosingTypeDeclaration
08606: : null;
08607: }
08608:
08609: // Member class declaration.
08610: if (atd instanceof Java.MemberClassDeclaration
08611: && (((Java.MemberClassDeclaration) atd).modifiers & Mod.STATIC) != 0)
08612: return null;
08613:
08614: // Anonymous class declaration, interface declaration
08615: Java.Scope s = atd;
08616: for (; !(s instanceof Java.TypeBodyDeclaration); s = s
08617: .getEnclosingScope()) {
08618: if (s instanceof Java.CompilationUnit)
08619: return null;
08620: }
08621: //if (!(s instanceof Java.ClassDeclaration)) return null;
08622: if (((Java.TypeBodyDeclaration) s).isStatic())
08623: return null;
08624: return (Java.AbstractTypeDeclaration) s.getEnclosingScope();
08625: }
08626:
08627: private IClass getIClass(Java.ThisReference tr)
08628: throws CompileException {
08629: if (tr.iClass == null) {
08630:
08631: // Compile error if in static function context.
08632: Java.Scope s;
08633: for (s = tr.getEnclosingBlockStatement(); s instanceof Java.Statement; s = s
08634: .getEnclosingScope())
08635: ;
08636: if (s instanceof Java.FunctionDeclarator) {
08637: Java.FunctionDeclarator function = (Java.FunctionDeclarator) s;
08638: if ((function.modifiers & Mod.STATIC) != 0)
08639: this
08640: .compileError(
08641: "No current instance available in static method",
08642: tr.getLocation());
08643: }
08644:
08645: // Determine declaring type.
08646: while (!(s instanceof Java.TypeDeclaration))
08647: s = s.getEnclosingScope();
08648: if (!(s instanceof Java.ClassDeclaration))
08649: this
08650: .compileError(
08651: "Only methods of classes can have a current instance",
08652: tr.getLocation());
08653: tr.iClass = this .resolve((Java.ClassDeclaration) s);
08654: }
08655: return tr.iClass;
08656: }
08657:
08658: private IClass getReturnType(Java.FunctionDeclarator fd)
08659: throws CompileException {
08660: if (fd.returnType == null) {
08661: fd.returnType = this .getType(fd.type);
08662: }
08663: return fd.returnType;
08664: }
08665:
08666: /*package*/IClass.IConstructor toIConstructor(
08667: final Java.ConstructorDeclarator cd) {
08668: if (cd.iConstructor != null)
08669: return cd.iConstructor;
08670:
08671: cd.iConstructor = this
08672: .resolve((Java.AbstractTypeDeclaration) cd
08673: .getDeclaringType()).new IConstructor() {
08674:
08675: // Implement IMember.
08676: public Access getAccess() {
08677: switch (cd.modifiers & Mod.PPP) {
08678: case Mod.PRIVATE:
08679: return Access.PRIVATE;
08680: case Mod.PROTECTED:
08681: return Access.PROTECTED;
08682: case Mod.PACKAGE:
08683: return Access.DEFAULT;
08684: case Mod.PUBLIC:
08685: return Access.PUBLIC;
08686: default:
08687: throw new RuntimeException("Invalid access");
08688: }
08689: }
08690:
08691: // Implement IInvocable.
08692: public String getDescriptor() throws CompileException {
08693: if (!(cd.getDeclaringClass() instanceof Java.InnerClassDeclaration))
08694: return super .getDescriptor();
08695:
08696: List l = new ArrayList();
08697:
08698: // Convert enclosing instance reference into prepended constructor parameters.
08699: IClass outerClass = UnitCompiler.this .resolve(
08700: cd.getDeclaringClass()).getOuterIClass();
08701: if (outerClass != null)
08702: l.add(outerClass.getDescriptor());
08703:
08704: // Convert synthetic fields into prepended constructor parameters.
08705: for (Iterator it = cd.getDeclaringClass().syntheticFields
08706: .values().iterator(); it.hasNext();) {
08707: IClass.IField sf = (IClass.IField) it.next();
08708: if (sf.getName().startsWith("val$"))
08709: l.add(sf.getType().getDescriptor());
08710: }
08711: Java.FunctionDeclarator.FormalParameter[] fps = cd.formalParameters;
08712: for (int i = 0; i < fps.length; ++i) {
08713: l.add(UnitCompiler.this .getType(fps[i].type)
08714: .getDescriptor());
08715: }
08716: String[] apd = (String[]) l
08717: .toArray(new String[l.size()]);
08718: return new MethodDescriptor(apd, Descriptor.VOID)
08719: .toString();
08720: }
08721:
08722: public IClass[] getParameterTypes() throws CompileException {
08723: Java.FunctionDeclarator.FormalParameter[] fps = cd.formalParameters;
08724: IClass[] res = new IClass[fps.length];
08725: for (int i = 0; i < fps.length; ++i) {
08726: res[i] = UnitCompiler.this .getType(fps[i].type);
08727: }
08728: return res;
08729: }
08730:
08731: public IClass[] getThrownExceptions()
08732: throws CompileException {
08733: IClass[] res = new IClass[cd.thrownExceptions.length];
08734: for (int i = 0; i < res.length; ++i) {
08735: res[i] = UnitCompiler.this
08736: .getType(cd.thrownExceptions[i]);
08737: }
08738: return res;
08739: }
08740:
08741: public String toString() {
08742: StringBuffer sb = new StringBuffer();
08743: sb.append(cd.getDeclaringType().getClassName());
08744: sb.append('(');
08745: Java.FunctionDeclarator.FormalParameter[] fps = cd.formalParameters;
08746: for (int i = 0; i < fps.length; ++i) {
08747: if (i != 0)
08748: sb.append(", ");
08749: try {
08750: sb.append(UnitCompiler.this
08751: .getType(fps[i].type).toString());
08752: } catch (CompileException ex) {
08753: sb.append("???");
08754: }
08755: }
08756: return sb.append(')').toString();
08757: }
08758: };
08759: return cd.iConstructor;
08760: }
08761:
08762: public IClass.IMethod toIMethod(final Java.MethodDeclarator md) {
08763: if (md.iMethod != null)
08764: return md.iMethod;
08765: md.iMethod = this .resolve((Java.AbstractTypeDeclaration) md
08766: .getDeclaringType()).new IMethod() {
08767:
08768: // Implement IMember.
08769: public Access getAccess() {
08770: switch (md.modifiers & Mod.PPP) {
08771: case Mod.PRIVATE:
08772: return Access.PRIVATE;
08773: case Mod.PROTECTED:
08774: return Access.PROTECTED;
08775: case Mod.PACKAGE:
08776: return Access.DEFAULT;
08777: case Mod.PUBLIC:
08778: return Access.PUBLIC;
08779: default:
08780: throw new RuntimeException("Invalid access");
08781: }
08782: }
08783:
08784: // Implement IInvocable.
08785: public IClass[] getParameterTypes() throws CompileException {
08786: Java.FunctionDeclarator.FormalParameter[] fps = md.formalParameters;
08787: IClass[] res = new IClass[fps.length];
08788: for (int i = 0; i < fps.length; ++i) {
08789: res[i] = UnitCompiler.this .getType(fps[i].type);
08790: }
08791: return res;
08792: }
08793:
08794: public IClass[] getThrownExceptions()
08795: throws CompileException {
08796: IClass[] res = new IClass[md.thrownExceptions.length];
08797: for (int i = 0; i < res.length; ++i) {
08798: res[i] = UnitCompiler.this
08799: .getType(md.thrownExceptions[i]);
08800: }
08801: return res;
08802: }
08803:
08804: // Implement IMethod.
08805: public boolean isStatic() {
08806: return (md.modifiers & Mod.STATIC) != 0;
08807: }
08808:
08809: public boolean isAbstract() {
08810: return (md.getDeclaringType() instanceof Java.InterfaceDeclaration)
08811: || (md.modifiers & Mod.ABSTRACT) != 0;
08812: }
08813:
08814: public IClass getReturnType() throws CompileException {
08815: return UnitCompiler.this .getReturnType(md);
08816: }
08817:
08818: public String getName() {
08819: return md.name;
08820: }
08821: };
08822: return md.iMethod;
08823: }
08824:
08825: private IClass.IInvocable toIInvocable(Java.FunctionDeclarator fd) {
08826: if (fd instanceof Java.ConstructorDeclarator) {
08827: return this .toIConstructor((Java.ConstructorDeclarator) fd);
08828: } else if (fd instanceof Java.MethodDeclarator) {
08829: return this .toIMethod((Java.MethodDeclarator) fd);
08830: } else {
08831: throw new RuntimeException(
08832: "FunctionDeclarator is neither ConstructorDeclarator nor MethodDeclarator");
08833: }
08834: }
08835:
08836: /**
08837: * If the given name was declared in a simple type import, load that class.
08838: */
08839: private IClass importSingleType(String simpleTypeName,
08840: Location location) throws CompileException {
08841: String[] ss = this .compilationUnit
08842: .getSingleTypeImport(simpleTypeName);
08843: if (ss == null)
08844: return null;
08845:
08846: IClass iClass = this .loadFullyQualifiedClass(ss);
08847: if (iClass == null) {
08848: this .compileError("Imported class \"" + Java.join(ss, ".")
08849: + "\" could not be loaded", location);
08850: return this .iClassLoader.OBJECT;
08851: }
08852: return iClass;
08853: }
08854:
08855: /**
08856: * 6.5.2.BL1.B1.B5, 6.5.2.BL1.B1.B6 Type-import-on-demand.<br>
08857: * 6.5.5.1.6 Type-import-on-demand declaration.
08858: * @return <code>null</code> if the given <code>simpleTypeName</code> cannot be resolved through any of the import-on-demand directives
08859: */
08860: public IClass importTypeOnDemand(String simpleTypeName,
08861: Location location) throws CompileException {
08862:
08863: // Check cache. (A cache for unimportable types is not required, because
08864: // the class is importable 99.9%.)
08865: IClass importedClass = (IClass) this .onDemandImportableTypes
08866: .get(simpleTypeName);
08867: if (importedClass != null)
08868: return importedClass;
08869:
08870: // Cache miss...
08871: List packages = new ArrayList();
08872: packages.add(new String[] { "java", "lang" });
08873: for (Iterator i = this .compilationUnit.importDeclarations
08874: .iterator(); i.hasNext();) {
08875: Java.CompilationUnit.ImportDeclaration id = (Java.CompilationUnit.ImportDeclaration) i
08876: .next();
08877: if (id instanceof Java.CompilationUnit.TypeImportOnDemandDeclaration) {
08878: packages
08879: .add(((Java.CompilationUnit.TypeImportOnDemandDeclaration) id).identifiers);
08880: }
08881: }
08882: for (Iterator i = packages.iterator(); i.hasNext();) {
08883: String[] ss = (String[]) i.next();
08884: String[] ss2 = new String[ss.length + 1];
08885: System.arraycopy(ss, 0, ss2, 0, ss.length);
08886: ss2[ss.length] = simpleTypeName;
08887: IClass iClass = this .loadFullyQualifiedClass(ss2);
08888: if (iClass != null) {
08889: if (importedClass != null && importedClass != iClass)
08890: this .compileError("Ambiguous class name: \""
08891: + importedClass + "\" vs. \"" + iClass
08892: + "\"", location);
08893: importedClass = iClass;
08894: }
08895: }
08896: if (importedClass == null)
08897: return null;
08898:
08899: // Put in cache and return.
08900: this .onDemandImportableTypes.put(simpleTypeName, importedClass);
08901: return importedClass;
08902: }
08903:
08904: private final Map onDemandImportableTypes = new HashMap(); // String simpleTypeName => IClass
08905:
08906: private void declareClassDollarMethod(Java.ClassLiteral cl) {
08907:
08908: // Method "class$" is not yet declared; declare it like
08909: //
08910: // static java.lang.Class class$(java.lang.String className) {
08911: // try {
08912: // return java.lang.Class.forName(className);
08913: // } catch (java.lang.ClassNotFoundException ex) {
08914: // throw new java.lang.NoClassDefFoundError(ex.getMessage());
08915: // }
08916: // }
08917: //
08918: Location loc = cl.getLocation();
08919: Java.AbstractTypeDeclaration declaringType;
08920: for (Java.Scope s = cl.getEnclosingBlockStatement();; s = s
08921: .getEnclosingScope()) {
08922: if (s instanceof Java.AbstractTypeDeclaration) {
08923: declaringType = (Java.AbstractTypeDeclaration) s;
08924: break;
08925: }
08926: }
08927: Java.Block body = new Java.Block(loc);
08928:
08929: // try {
08930: // return Class.forName(className);
08931: Java.MethodInvocation mi = new Java.MethodInvocation(loc, // location
08932: new Java.SimpleType(loc, this .iClassLoader.CLASS), // optionalTarget
08933: "forName", // methodName
08934: new Java.Rvalue[] { // arguments
08935: new Java.AmbiguousName(loc,
08936: new String[] { "className" }) });
08937:
08938: IClass classNotFoundExceptionIClass;
08939: try {
08940: classNotFoundExceptionIClass = this .iClassLoader
08941: .loadIClass("Ljava/lang/ClassNotFoundException;");
08942: } catch (ClassNotFoundException ex) {
08943: throw new RuntimeException(
08944: "Loading class \"ClassNotFoundException\": "
08945: + ex.getMessage());
08946: }
08947: if (classNotFoundExceptionIClass == null)
08948: throw new RuntimeException(
08949: "SNO: Cannot load \"ClassNotFoundException\"");
08950:
08951: IClass noClassDefFoundErrorIClass;
08952: try {
08953: noClassDefFoundErrorIClass = this .iClassLoader
08954: .loadIClass("Ljava/lang/NoClassDefFoundError;");
08955: } catch (ClassNotFoundException ex) {
08956: throw new RuntimeException(
08957: "Loading class \"NoClassDefFoundError\": "
08958: + ex.getMessage());
08959: }
08960: if (noClassDefFoundErrorIClass == null)
08961: throw new RuntimeException(
08962: "SNO: Cannot load \"NoClassFoundError\"");
08963:
08964: // catch (ClassNotFoundException ex) {
08965: Java.Block b = new Java.Block(loc);
08966: // throw new NoClassDefFoundError(ex.getMessage());
08967: b.addStatement(new Java.ThrowStatement(loc,
08968: new Java.NewClassInstance(loc, // location
08969: (Java.Rvalue) null, // optionalQualification
08970: new Java.SimpleType(loc,
08971: noClassDefFoundErrorIClass), // type
08972: new Java.Rvalue[] { // arguments
08973: new Java.MethodInvocation(loc, // location
08974: new Java.AmbiguousName(loc,
08975: new String[] { "ex" }), // optionalTarget
08976: "getMessage", // methodName
08977: new Java.Rvalue[0] // arguments
08978: ) })));
08979:
08980: List l = new ArrayList();
08981: l.add(new Java.CatchClause(loc, // location
08982: new Java.FunctionDeclarator.FormalParameter( // caughtException
08983: loc, // location
08984: true, // finaL
08985: new Java.SimpleType(loc,
08986: classNotFoundExceptionIClass), // type
08987: "ex" // name
08988: ), b // body
08989: ));
08990: Java.TryStatement ts = new Java.TryStatement(loc, // location
08991: new Java.ReturnStatement(loc, mi), // body
08992: l, // catchClauses
08993: null // optionalFinally
08994: );
08995:
08996: body.addStatement(ts);
08997:
08998: // Class class$(String className)
08999: Java.FunctionDeclarator.FormalParameter fp = new Java.FunctionDeclarator.FormalParameter(
09000: loc, // location
09001: false, // finaL
09002: new Java.SimpleType(loc, this .iClassLoader.STRING), // type
09003: "className" // name
09004: );
09005: Java.MethodDeclarator cdmd = new Java.MethodDeclarator(loc, // location
09006: null, // optionalDocComment
09007: Mod.STATIC, // modifiers
09008: new Java.SimpleType(loc, this .iClassLoader.CLASS), // type
09009: "class$", // name
09010: new Java.FunctionDeclarator.FormalParameter[] { fp }, // formalParameters
09011: new Java.Type[0], // thrownExceptions
09012: body // optionalBody
09013: );
09014:
09015: declaringType.addDeclaredMethod(cdmd);
09016:
09017: // Invalidate several caches.
09018: if (declaringType.resolvedType != null) {
09019: declaringType.resolvedType.declaredIMethods = null;
09020: declaringType.resolvedType.declaredIMethodCache = null;
09021: }
09022: }
09023:
09024: private IClass pushConstant(Java.Located located, Object value) {
09025: if (value instanceof Integer || value instanceof Short
09026: || value instanceof Character || value instanceof Byte) {
09027: int i = (value instanceof Character ? ((Character) value)
09028: .charValue() : ((Number) value).intValue());
09029: if (i >= -1 && i <= 5) {
09030: this .writeOpcode(located, Opcode.ICONST_0 + i);
09031: } else if (i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE) {
09032: this .writeOpcode(located, Opcode.BIPUSH);
09033: this .writeByte(located, (byte) i);
09034: } else {
09035: this .writeLDC(located, this .addConstantIntegerInfo(i));
09036: }
09037: return IClass.INT;
09038: }
09039: if (value instanceof Long) {
09040: long lv = ((Long) value).longValue();
09041: if (lv >= 0L && lv <= 1L) {
09042: this .writeOpcode(located, Opcode.LCONST_0 + (int) lv);
09043: } else {
09044: this .writeOpcode(located, Opcode.LDC2_W);
09045: this .writeConstantLongInfo(located, lv);
09046: }
09047: return IClass.LONG;
09048: }
09049: if (value instanceof Float) {
09050: float fv = ((Float) value).floatValue();
09051: if (Float.floatToIntBits(fv) == Float.floatToIntBits(0.0F) // POSITIVE zero!
09052: || fv == 1.0F || fv == 2.0F) {
09053: this .writeOpcode(located, Opcode.FCONST_0 + (int) fv);
09054: } else {
09055: this .writeLDC(located, this .addConstantFloatInfo(fv));
09056: }
09057: return IClass.FLOAT;
09058: }
09059: if (value instanceof Double) {
09060: double dv = ((Double) value).doubleValue();
09061: if (Double.doubleToLongBits(dv) == Double
09062: .doubleToLongBits(0.0D) // POSITIVE zero!
09063: || dv == 1.0D) {
09064: this .writeOpcode(located, Opcode.DCONST_0 + (int) dv);
09065: } else {
09066: this .writeOpcode(located, Opcode.LDC2_W);
09067: this .writeConstantDoubleInfo(located, dv);
09068: }
09069: return IClass.DOUBLE;
09070: }
09071: if (value instanceof String) {
09072: String s = (String) value;
09073: if (s.length() < (65536 / 3)) {
09074: this .writeLDC(located, this
09075: .addConstantStringInfo((String) value));
09076: return this .iClassLoader.STRING;
09077: }
09078: int sLength = s.length(), uTFLength = 0;
09079: int from = 0;
09080: for (int i = 0;; i++) {
09081: if (i == sLength || uTFLength >= 65532) {
09082: this .writeLDC(located, this .addConstantStringInfo(s
09083: .substring(from, i)));
09084: if (from != 0) {
09085: this .writeOpcode(located, Opcode.INVOKEVIRTUAL);
09086: this .writeConstantMethodrefInfo(located,
09087: Descriptor.STRING, // classFD
09088: "concat", // methodName
09089: "(" + Descriptor.STRING + ")"
09090: + Descriptor.STRING // methodMD
09091: );
09092: }
09093: if (i == sLength)
09094: break;
09095: from = i;
09096: uTFLength = 0;
09097: }
09098: int c = s.charAt(i);
09099: if ((c >= 0x0001) && (c <= 0x007F)) {
09100: ++uTFLength;
09101: } else if (c > 0x07FF) {
09102: uTFLength += 3;
09103: } else {
09104: uTFLength += 2;
09105: }
09106: }
09107: return this .iClassLoader.STRING;
09108: }
09109: if (value instanceof Boolean) {
09110: this .writeOpcode(located,
09111: ((Boolean) value).booleanValue() ? Opcode.ICONST_1
09112: : Opcode.ICONST_0);
09113: return IClass.BOOLEAN;
09114: }
09115: if (value == Java.Rvalue.CONSTANT_VALUE_NULL) {
09116: this .writeOpcode(located, Opcode.ACONST_NULL);
09117: return IClass.VOID;
09118: }
09119: throw new RuntimeException("Unknown literal type \""
09120: + value.getClass().getName() + "\"");
09121: }
09122:
09123: private void writeLDC(Java.Located located, short index) {
09124: if (index <= 255) {
09125: this .writeOpcode(located, Opcode.LDC);
09126: this .writeByte(located, (byte) index);
09127: } else {
09128: this .writeOpcode(located, Opcode.LDC_W);
09129: this .writeShort(located, index);
09130: }
09131: }
09132:
09133: /**
09134: * Implements "assignment conversion" (JLS2 5.2).
09135: */
09136: private void assignmentConversion(Java.Located located,
09137: IClass sourceType, IClass targetType,
09138: Object optionalConstantValue) throws CompileException {
09139: if (UnitCompiler.DEBUG)
09140: System.out.println("assignmentConversion(" + sourceType
09141: + ", " + targetType + ", " + optionalConstantValue
09142: + ")");
09143:
09144: // 5.2 / 5.1.1 Identity conversion.
09145: if (this .tryIdentityConversion(sourceType, targetType))
09146: return;
09147:
09148: // 5.2 / 5.1.2 Widening primitive conversion.
09149: if (this .tryWideningPrimitiveConversion(located, sourceType,
09150: targetType))
09151: return;
09152:
09153: // 5.2 / 5.1.4 Widening reference conversion.
09154: if (this .isWideningReferenceConvertible(sourceType, targetType))
09155: return;
09156:
09157: // 5.2 Special narrowing primitive conversion.
09158: if (optionalConstantValue != null) {
09159: if (this .isConstantPrimitiveAssignmentConvertible(
09160: optionalConstantValue, // constantValue
09161: targetType // targetType
09162: ))
09163: return;
09164: }
09165:
09166: this .compileError(
09167: "Assignment conversion not possible from type \""
09168: + sourceType + "\" to type \"" + targetType
09169: + "\"", located.getLocation());
09170: }
09171:
09172: /**
09173: * Implements "assignment conversion" (JLS2 5.2) on a constant value.
09174: */
09175: private Object assignmentConversion(Java.Located located,
09176: Object value, IClass targetType) throws CompileException {
09177: Object result = null;
09178:
09179: if (targetType == IClass.BOOLEAN) {
09180: if (value instanceof Boolean)
09181: result = value;
09182: } else if (targetType == this .iClassLoader.STRING) {
09183: if (value instanceof String)
09184: result = value;
09185: } else if (targetType == IClass.BYTE) {
09186: if (value instanceof Byte) {
09187: result = value;
09188: } else if (value instanceof Short
09189: || value instanceof Integer) {
09190: int x = ((Number) value).intValue();
09191: if (x >= Byte.MIN_VALUE && x <= Byte.MAX_VALUE)
09192: result = new Byte((byte) x);
09193: } else if (value instanceof Character) {
09194: int x = ((Character) value).charValue();
09195: if (x >= Byte.MIN_VALUE && x <= Byte.MAX_VALUE)
09196: result = new Byte((byte) x);
09197: }
09198: } else if (targetType == IClass.SHORT) {
09199: if (value instanceof Byte) {
09200: result = new Short(((Number) value).shortValue());
09201: } else if (value instanceof Short) {
09202: result = value;
09203: } else if (value instanceof Character) {
09204: int x = ((Character) value).charValue();
09205: if (x >= Short.MIN_VALUE && x <= Short.MAX_VALUE)
09206: result = new Short((short) x);
09207: } else if (value instanceof Integer) {
09208: int x = ((Integer) value).intValue();
09209: if (x >= Short.MIN_VALUE && x <= Short.MAX_VALUE)
09210: result = new Short((short) x);
09211: }
09212: } else if (targetType == IClass.CHAR) {
09213: if (value instanceof Short) {
09214: result = value;
09215: } else if (value instanceof Byte || value instanceof Short
09216: || value instanceof Integer) {
09217: int x = ((Number) value).intValue();
09218: if (x >= Character.MIN_VALUE
09219: && x <= Character.MAX_VALUE)
09220: result = new Character((char) x);
09221: }
09222: } else if (targetType == IClass.INT) {
09223: if (value instanceof Integer) {
09224: result = value;
09225: } else if (value instanceof Byte || value instanceof Short) {
09226: result = new Integer(((Number) value).intValue());
09227: } else if (value instanceof Character) {
09228: result = new Integer(((Character) value).charValue());
09229: }
09230: } else if (targetType == IClass.LONG) {
09231: if (value instanceof Long) {
09232: result = value;
09233: } else if (value instanceof Byte || value instanceof Short
09234: || value instanceof Integer) {
09235: result = new Long(((Number) value).longValue());
09236: } else if (value instanceof Character) {
09237: result = new Long(((Character) value).charValue());
09238: }
09239: } else if (targetType == IClass.FLOAT) {
09240: if (value instanceof Float) {
09241: result = value;
09242: } else if (value instanceof Byte || value instanceof Short
09243: || value instanceof Integer
09244: || value instanceof Long) {
09245: result = new Float(((Number) value).floatValue());
09246: } else if (value instanceof Character) {
09247: result = new Float(((Character) value).charValue());
09248: }
09249: } else if (targetType == IClass.DOUBLE) {
09250: if (value instanceof Double) {
09251: result = value;
09252: } else if (value instanceof Byte || value instanceof Short
09253: || value instanceof Integer
09254: || value instanceof Long || value instanceof Float) {
09255: result = new Double(((Number) value).doubleValue());
09256: } else if (value instanceof Character) {
09257: result = new Double(((Character) value).charValue());
09258: }
09259: } else if (value == Java.Rvalue.CONSTANT_VALUE_NULL
09260: && !targetType.isPrimitive()) {
09261: result = value;
09262: }
09263: if (result == null)
09264: this .compileError("Cannot convert constant of type \""
09265: + value.getClass().getName() + "\" to type \""
09266: + targetType.toString() + "\"", located
09267: .getLocation());
09268: return result;
09269: }
09270:
09271: /**
09272: * Implements "unary numeric promotion" (5.6.1)
09273: *
09274: * @return The promoted type.
09275: */
09276: private IClass unaryNumericPromotion(Java.Located located,
09277: IClass type) throws CompileException {
09278: IClass promotedType = this .unaryNumericPromotionType(located,
09279: type);
09280:
09281: if (!this .tryIdentityConversion(type, promotedType)
09282: && !this .tryWideningPrimitiveConversion(located, // located
09283: type, // sourceType
09284: promotedType // targetType
09285: ))
09286: throw new RuntimeException("SNO: Conversion failed");
09287: return promotedType;
09288: }
09289:
09290: private IClass unaryNumericPromotionType(Java.Located located,
09291: IClass type) throws CompileException {
09292: if (!type.isPrimitiveNumeric())
09293: this .compileError(
09294: "Unary numeric promotion not possible on non-numeric-primitive type \""
09295: + type + "\"", located.getLocation());
09296:
09297: return (type == IClass.DOUBLE ? IClass.DOUBLE
09298: : type == IClass.FLOAT ? IClass.FLOAT
09299: : type == IClass.LONG ? IClass.LONG
09300: : IClass.INT);
09301: }
09302:
09303: /**
09304: * Implements "binary numeric promotion" (5.6.2)
09305: *
09306: * @return The promoted type.
09307: */
09308: private IClass binaryNumericPromotion(Java.Located located,
09309: IClass type1, CodeContext.Inserter convertInserter1,
09310: IClass type2) throws CompileException {
09311: IClass promotedType = this .binaryNumericPromotionType(located,
09312: type1, type2);
09313:
09314: if (convertInserter1 != null) {
09315: this .codeContext.pushInserter(convertInserter1);
09316: try {
09317: if (!this .tryIdentityConversion(type1, promotedType)
09318: && !this .tryWideningPrimitiveConversion(
09319: located, // located
09320: type1, // sourceType
09321: promotedType // targetType
09322: ))
09323: throw new RuntimeException("SNO: Conversion failed");
09324: } finally {
09325: this .codeContext.popInserter();
09326: }
09327: }
09328:
09329: if (!this .tryIdentityConversion(type2, promotedType)
09330: && !this .tryWideningPrimitiveConversion(located, // located
09331: type2, // sourceType
09332: promotedType // targetType
09333: ))
09334: throw new RuntimeException("SNO: Conversion failed");
09335:
09336: return promotedType;
09337: }
09338:
09339: private IClass binaryNumericPromotionType(Java.Locatable locatable,
09340: IClass type1, IClass type2) throws CompileException {
09341: if (!type1.isPrimitiveNumeric() || !type2.isPrimitiveNumeric())
09342: this .compileError(
09343: "Binary numeric promotion not possible on types \""
09344: + type1 + "\" and \"" + type2 + "\"",
09345: locatable.getLocation());
09346:
09347: return (type1 == IClass.DOUBLE || type2 == IClass.DOUBLE ? IClass.DOUBLE
09348: : type1 == IClass.FLOAT || type2 == IClass.FLOAT ? IClass.FLOAT
09349: : type1 == IClass.LONG || type2 == IClass.LONG ? IClass.LONG
09350: : IClass.INT);
09351: }
09352:
09353: /**
09354: * Implements "identity conversion" (5.1.1).
09355: *
09356: * @return Whether the conversion succeeded
09357: */
09358: private boolean tryIdentityConversion(IClass sourceType,
09359: IClass targetType) {
09360: return sourceType == targetType;
09361: }
09362:
09363: private boolean isWideningPrimitiveConvertible(IClass sourceType,
09364: IClass targetType) {
09365: return UnitCompiler.PRIMITIVE_WIDENING_CONVERSIONS
09366: .get(sourceType.getDescriptor()
09367: + targetType.getDescriptor()) != null;
09368: }
09369:
09370: /**
09371: * Implements "widening primitive conversion" (5.1.2).
09372: *
09373: * @return Whether the conversion succeeded
09374: */
09375: private boolean tryWideningPrimitiveConversion(
09376: Java.Located located, IClass sourceType, IClass targetType) {
09377: byte[] opcodes = (byte[]) UnitCompiler.PRIMITIVE_WIDENING_CONVERSIONS
09378: .get(sourceType.getDescriptor()
09379: + targetType.getDescriptor());
09380: if (opcodes != null) {
09381: this .write(located, opcodes);
09382: return true;
09383: }
09384: return false;
09385: }
09386:
09387: private static final HashMap PRIMITIVE_WIDENING_CONVERSIONS = new HashMap();
09388: static {
09389: UnitCompiler.fillConversionMap(new Object[] { new byte[0],
09390: Descriptor.BYTE + Descriptor.SHORT,
09391:
09392: Descriptor.BYTE + Descriptor.INT,
09393: Descriptor.SHORT + Descriptor.INT,
09394: Descriptor.CHAR + Descriptor.INT,
09395:
09396: new byte[] { Opcode.I2L },
09397: Descriptor.BYTE + Descriptor.LONG,
09398: Descriptor.SHORT + Descriptor.LONG,
09399: Descriptor.CHAR + Descriptor.LONG,
09400: Descriptor.INT + Descriptor.LONG,
09401:
09402: new byte[] { Opcode.I2F },
09403: Descriptor.BYTE + Descriptor.FLOAT,
09404: Descriptor.SHORT + Descriptor.FLOAT,
09405: Descriptor.CHAR + Descriptor.FLOAT,
09406: Descriptor.INT + Descriptor.FLOAT,
09407:
09408: new byte[] { Opcode.L2F },
09409: Descriptor.LONG + Descriptor.FLOAT,
09410:
09411: new byte[] { Opcode.I2D },
09412: Descriptor.BYTE + Descriptor.DOUBLE,
09413: Descriptor.SHORT + Descriptor.DOUBLE,
09414: Descriptor.CHAR + Descriptor.DOUBLE,
09415: Descriptor.INT + Descriptor.DOUBLE,
09416:
09417: new byte[] { Opcode.L2D },
09418: Descriptor.LONG + Descriptor.DOUBLE,
09419:
09420: new byte[] { Opcode.F2D },
09421: Descriptor.FLOAT + Descriptor.DOUBLE, },
09422: UnitCompiler.PRIMITIVE_WIDENING_CONVERSIONS);
09423: }
09424:
09425: private static void fillConversionMap(Object[] array, HashMap map) {
09426: byte[] opcodes = null;
09427: for (int i = 0; i < array.length; ++i) {
09428: Object o = array[i];
09429: if (o instanceof byte[]) {
09430: opcodes = (byte[]) o;
09431: } else {
09432: map.put(o, opcodes);
09433: }
09434: }
09435: }
09436:
09437: /**
09438: * Checks if "widening reference conversion" (5.1.4) is possible. This is
09439: * identical to EXECUTING the conversion, because no opcodes are necessary
09440: * to implement the conversion.
09441: *
09442: * @return Whether the conversion is possible
09443: */
09444: private boolean isWideningReferenceConvertible(IClass sourceType,
09445: IClass targetType) throws CompileException {
09446: if (targetType.isPrimitive() || sourceType == targetType)
09447: return false;
09448:
09449: return targetType.isAssignableFrom(sourceType);
09450: }
09451:
09452: /**
09453: * Implements "narrowing primitive conversion" (JLS 5.1.3).
09454: *
09455: * @return Whether the conversion succeeded
09456: */
09457: private boolean tryNarrowingPrimitiveConversion(
09458: Java.Located located, IClass sourceType, IClass targetType) {
09459: byte[] opcodes = (byte[]) UnitCompiler.PRIMITIVE_NARROWING_CONVERSIONS
09460: .get(sourceType.getDescriptor()
09461: + targetType.getDescriptor());
09462: if (opcodes != null) {
09463: this .write(located, opcodes);
09464: return true;
09465: }
09466: return false;
09467: }
09468:
09469: /**
09470: * Check whether "narrowing primitive conversion" (JLS 5.1.3) is possible.
09471: */
09472: private boolean isNarrowingPrimitiveConvertible(IClass sourceType,
09473: IClass targetType) {
09474: return UnitCompiler.PRIMITIVE_NARROWING_CONVERSIONS
09475: .containsKey(sourceType.getDescriptor()
09476: + targetType.getDescriptor());
09477: }
09478:
09479: private static final HashMap PRIMITIVE_NARROWING_CONVERSIONS = new HashMap();
09480: static {
09481: UnitCompiler.fillConversionMap(new Object[] { new byte[0],
09482: Descriptor.BYTE + Descriptor.CHAR,
09483: Descriptor.SHORT + Descriptor.CHAR,
09484: Descriptor.CHAR + Descriptor.SHORT,
09485:
09486: new byte[] { Opcode.I2B },
09487: Descriptor.SHORT + Descriptor.BYTE,
09488: Descriptor.CHAR + Descriptor.BYTE,
09489: Descriptor.INT + Descriptor.BYTE,
09490:
09491: new byte[] { Opcode.I2S },
09492: Descriptor.INT + Descriptor.SHORT,
09493: Descriptor.INT + Descriptor.CHAR,
09494:
09495: new byte[] { Opcode.L2I, Opcode.I2B },
09496: Descriptor.LONG + Descriptor.BYTE,
09497:
09498: new byte[] { Opcode.L2I, Opcode.I2S },
09499: Descriptor.LONG + Descriptor.SHORT,
09500: Descriptor.LONG + Descriptor.CHAR,
09501:
09502: new byte[] { Opcode.L2I },
09503: Descriptor.LONG + Descriptor.INT,
09504:
09505: new byte[] { Opcode.F2I, Opcode.I2B },
09506: Descriptor.FLOAT + Descriptor.BYTE,
09507:
09508: new byte[] { Opcode.F2I, Opcode.I2S },
09509: Descriptor.FLOAT + Descriptor.SHORT,
09510: Descriptor.FLOAT + Descriptor.CHAR,
09511:
09512: new byte[] { Opcode.F2I },
09513: Descriptor.FLOAT + Descriptor.INT,
09514:
09515: new byte[] { Opcode.F2L },
09516: Descriptor.FLOAT + Descriptor.LONG,
09517:
09518: new byte[] { Opcode.D2I, Opcode.I2B },
09519: Descriptor.DOUBLE + Descriptor.BYTE,
09520:
09521: new byte[] { Opcode.D2I, Opcode.I2S },
09522: Descriptor.DOUBLE + Descriptor.SHORT,
09523: Descriptor.DOUBLE + Descriptor.CHAR,
09524:
09525: new byte[] { Opcode.D2I },
09526: Descriptor.DOUBLE + Descriptor.INT,
09527:
09528: new byte[] { Opcode.D2L },
09529: Descriptor.DOUBLE + Descriptor.LONG,
09530:
09531: new byte[] { Opcode.D2F },
09532: Descriptor.DOUBLE + Descriptor.FLOAT, },
09533: UnitCompiler.PRIMITIVE_NARROWING_CONVERSIONS);
09534: }
09535:
09536: /**
09537: * Check if "constant primitive assignment conversion" (JLS 5.2, paragraph 1) is possible.
09538: * @param constantValue The constant value that is to be converted
09539: * @param targetType The type to convert to
09540: */
09541: private boolean isConstantPrimitiveAssignmentConvertible(
09542: Object constantValue, IClass targetType) {
09543: if (UnitCompiler.DEBUG)
09544: System.out
09545: .println("isConstantPrimitiveAssignmentConvertible("
09546: + constantValue + ", " + targetType + ")");
09547:
09548: int cv;
09549: if (constantValue instanceof Byte
09550: || constantValue instanceof Short
09551: || constantValue instanceof Integer) {
09552: cv = ((Number) constantValue).intValue();
09553: } else if (constantValue instanceof Character) {
09554: cv = (int) ((Character) constantValue).charValue();
09555: } else {
09556: return false;
09557: }
09558:
09559: if (targetType == IClass.BYTE)
09560: return cv >= Byte.MIN_VALUE && cv <= Byte.MAX_VALUE;
09561: if (targetType == IClass.SHORT) {
09562: return cv >= Short.MIN_VALUE && cv <= Short.MAX_VALUE;
09563: }
09564: if (targetType == IClass.CHAR)
09565: return cv >= Character.MIN_VALUE
09566: && cv <= Character.MAX_VALUE;
09567:
09568: return false;
09569: }
09570:
09571: /**
09572: * Implements "narrowing reference conversion" (5.1.5).
09573: *
09574: * @return Whether the conversion succeeded
09575: */
09576: private boolean tryNarrowingReferenceConversion(
09577: Java.Located located, IClass sourceType, IClass targetType)
09578: throws CompileException {
09579: if (!this .isNarrowingReferenceConvertible(sourceType,
09580: targetType))
09581: return false;
09582:
09583: this .writeOpcode(located, Opcode.CHECKCAST);
09584: this
09585: .writeConstantClassInfo(located, targetType
09586: .getDescriptor());
09587: return true;
09588: }
09589:
09590: /**
09591: * Check whether "narrowing reference conversion" (JLS 5.1.5) is possible.
09592: */
09593: private boolean isNarrowingReferenceConvertible(IClass sourceType,
09594: IClass targetType) throws CompileException {
09595: if (sourceType.isPrimitive())
09596: return false;
09597: if (sourceType == targetType)
09598: return false;
09599:
09600: // 5.1.5.1
09601: if (sourceType.isAssignableFrom(targetType))
09602: return true;
09603:
09604: // 5.1.5.2
09605: if (targetType.isInterface() && !sourceType.isFinal()
09606: && !targetType.isAssignableFrom(sourceType))
09607: return true;
09608:
09609: // 5.1.5.3
09610: if (sourceType == this .iClassLoader.OBJECT
09611: && targetType.isArray())
09612: return true;
09613:
09614: // 5.1.5.4
09615: if (sourceType == this .iClassLoader.OBJECT
09616: && targetType.isInterface())
09617: return true;
09618:
09619: // 5.1.5.5
09620: if (sourceType.isInterface() && !targetType.isFinal())
09621: return true;
09622:
09623: // 5.1.5.6
09624: if (sourceType.isInterface() && targetType.isFinal()
09625: && sourceType.isAssignableFrom(targetType))
09626: return true;
09627:
09628: // 5.1.5.7
09629: // TODO: Check for redefinition of methods with same signature but different return type.
09630: if (sourceType.isInterface() && targetType.isInterface()
09631: && !targetType.isAssignableFrom(sourceType))
09632: return true;
09633:
09634: // 5.1.5.8
09635: if (sourceType.isArray() && targetType.isArray()) {
09636: IClass st = sourceType.getComponentType();
09637: IClass tt = targetType.getComponentType();
09638: if (this .isNarrowingPrimitiveConvertible(st, tt)
09639: || this .isNarrowingReferenceConvertible(st, tt))
09640: return true;
09641: }
09642:
09643: return false;
09644: }
09645:
09646: /**
09647: * Attempt to load an {@link IClass} by fully-qualified name
09648: * @param identifiers
09649: * @return <code>null</code> if a class with the given name could not be loaded
09650: */
09651: private IClass loadFullyQualifiedClass(String[] identifiers)
09652: throws CompileException {
09653:
09654: // Compose the descriptor (like "La/b/c;") and remember the positions of the slashes
09655: // (2 and 4).
09656: int[] slashes = new int[identifiers.length - 1];
09657: StringBuffer sb = new StringBuffer("L");
09658: for (int i = 0;; ++i) {
09659: sb.append(identifiers[i]);
09660: if (i == identifiers.length - 1)
09661: break;
09662: slashes[i] = sb.length();
09663: sb.append('/');
09664: }
09665: sb.append(';');
09666:
09667: // Attempt to load the IClass and replace dots with dollar signs, i.e.:
09668: // La/b/c; La/b$c; La$b$c;
09669: for (int j = slashes.length - 1;; --j) {
09670: IClass result;
09671: try {
09672: result = this .iClassLoader.loadIClass(sb.toString());
09673: } catch (ClassNotFoundException ex) {
09674: if (ex.getException() instanceof CompileException)
09675: throw (CompileException) ex.getException();
09676: throw new CompileException(sb.toString(), null, ex);
09677: }
09678: if (result != null)
09679: return result;
09680: if (j < 0)
09681: break;
09682: sb.setCharAt(slashes[j], '$');
09683: }
09684: return null;
09685: }
09686:
09687: // Load the value of a local variable onto the stack and return its type.
09688: private IClass load(Java.Located located,
09689: Java.LocalVariable localVariable) {
09690: this .load(located, localVariable.type,
09691: localVariable.localVariableArrayIndex);
09692: return localVariable.type;
09693: }
09694:
09695: private void load(Java.Located located, IClass type, int index) {
09696: if (index <= 3) {
09697: this .writeOpcode(located, Opcode.ILOAD_0 + 4
09698: * this .ilfda(type) + index);
09699: } else if (index <= 255) {
09700: this .writeOpcode(located, Opcode.ILOAD + this .ilfda(type));
09701: this .writeByte(located, index);
09702: } else {
09703: this .writeOpcode(located, Opcode.WIDE);
09704: this .writeOpcode(located, Opcode.ILOAD + this .ilfda(type));
09705: this .writeShort(located, index);
09706: }
09707: }
09708:
09709: /**
09710: * Assign stack top value to the given local variable. (Assignment conversion takes effect.)
09711: * If <copde>optionalConstantValue</code> is not <code>null</code>, then the top stack value
09712: * is a constant value with that type and value, and a narrowing primitive conversion as
09713: * described in JLS 5.2 is applied.
09714: */
09715: private void store(Java.Located located, IClass valueType,
09716: Java.LocalVariable localVariable) {
09717: this .store(located, // located
09718: localVariable.type, // lvType
09719: localVariable.localVariableArrayIndex // lvIndex
09720: );
09721: }
09722:
09723: private void store(Java.Located located, IClass lvType,
09724: short lvIndex) {
09725: if (lvIndex <= 3) {
09726: this .writeOpcode(located, Opcode.ISTORE_0 + 4
09727: * this .ilfda(lvType) + lvIndex);
09728: } else if (lvIndex <= 255) {
09729: this .writeOpcode(located, Opcode.ISTORE
09730: + this .ilfda(lvType));
09731: this .writeByte(located, lvIndex);
09732: } else {
09733: this .writeOpcode(located, Opcode.WIDE);
09734: this .writeOpcode(located, Opcode.ISTORE
09735: + this .ilfda(lvType));
09736: this .writeShort(located, lvIndex);
09737: }
09738: }
09739:
09740: private void dup(Java.Located located, int n) {
09741: switch (n) {
09742:
09743: case 0:
09744: ;
09745: break;
09746:
09747: case 1:
09748: this .writeOpcode(located, Opcode.DUP);
09749: break;
09750:
09751: case 2:
09752: this .writeOpcode(located, Opcode.DUP2);
09753: break;
09754:
09755: default:
09756: throw new RuntimeException("dup(" + n + ")");
09757: }
09758: }
09759:
09760: private void dupx(Java.Located located, IClass type, int x) {
09761: if (x < 0 || x > 2)
09762: throw new RuntimeException("SNO: x has value " + x);
09763: int dup = Opcode.DUP + x;
09764: int dup2 = Opcode.DUP2 + x;
09765: this .writeOpcode(located, (type == IClass.LONG
09766: || type == IClass.DOUBLE ? dup2 : dup));
09767: }
09768:
09769: private void pop(Java.Located located, IClass type) {
09770: if (type == IClass.VOID)
09771: return;
09772: this .writeOpcode(located, (type == IClass.LONG
09773: || type == IClass.DOUBLE ? Opcode.POP2 : Opcode.POP));
09774: }
09775:
09776: static int ilfd(IClass t) {
09777: if (t == IClass.BYTE || t == IClass.CHAR || t == IClass.INT
09778: || t == IClass.SHORT || t == IClass.BOOLEAN)
09779: return 0;
09780: if (t == IClass.LONG)
09781: return 1;
09782: if (t == IClass.FLOAT)
09783: return 2;
09784: if (t == IClass.DOUBLE)
09785: return 3;
09786: throw new RuntimeException("Unexpected type \"" + t + "\"");
09787: }
09788:
09789: static int ilfd(IClass t, int opcodeInt, int opcodeLong,
09790: int opcodeFloat, int opcodeDouble) {
09791: if (t == IClass.BYTE || t == IClass.CHAR || t == IClass.INT
09792: || t == IClass.SHORT || t == IClass.BOOLEAN)
09793: return opcodeInt;
09794: if (t == IClass.LONG)
09795: return opcodeLong;
09796: if (t == IClass.FLOAT)
09797: return opcodeFloat;
09798: if (t == IClass.DOUBLE)
09799: return opcodeDouble;
09800: throw new RuntimeException("Unexpected type \"" + t + "\"");
09801: }
09802:
09803: private int ilfda(IClass t) {
09804: return !t.isPrimitive() ? 4 : UnitCompiler.ilfd(t);
09805: }
09806:
09807: static int ilfdabcs(IClass t) {
09808: if (t == IClass.INT)
09809: return 0;
09810: if (t == IClass.LONG)
09811: return 1;
09812: if (t == IClass.FLOAT)
09813: return 2;
09814: if (t == IClass.DOUBLE)
09815: return 3;
09816: if (!t.isPrimitive())
09817: return 4;
09818: if (t == IClass.BOOLEAN || t == IClass.BYTE)
09819: return 5;
09820: if (t == IClass.CHAR)
09821: return 6;
09822: if (t == IClass.SHORT)
09823: return 7;
09824: throw new RuntimeException("Unexpected type \"" + t + "\"");
09825: }
09826:
09827: /**
09828: * Find a named field in the given {@link IClass}.
09829: * Honor superclasses and interfaces. See JLS 8.3.
09830: * @return <code>null</code> if no field is found
09831: */
09832: private IClass.IField findIField(IClass iClass, String name,
09833: Location location) throws CompileException {
09834:
09835: // Search for a field with the given name in the current class.
09836: IClass.IField[] fields = iClass.getDeclaredIFields();
09837: for (int i = 0; i < fields.length; ++i) {
09838: final IClass.IField f = fields[i];
09839: if (name.equals(f.getName()))
09840: return f;
09841: }
09842:
09843: // Examine superclass.
09844: IClass.IField f = null;
09845: {
09846: IClass super class = iClass.getSuperclass();
09847: if (super class != null)
09848: f = this .findIField(super class, name, location);
09849: }
09850:
09851: // Examine interfaces.
09852: IClass[] ifs = iClass.getInterfaces();
09853: for (int i = 0; i < ifs.length; ++i) {
09854: IClass.IField f2 = this .findIField(ifs[i], name, location);
09855: if (f2 != null) {
09856: if (f != null)
09857: throw new CompileException(
09858: "Access to field \"" + name
09859: + "\" is ambiguous - both \""
09860: + f.getDeclaringIClass()
09861: + "\" and \""
09862: + f2.getDeclaringIClass()
09863: + "\" declare it", location);
09864: f = f2;
09865: }
09866: }
09867: return f;
09868: }
09869:
09870: /**
09871: * Find a named type in the given {@link IClass}.
09872: * Honor superclasses, interfaces and enclosing type declarations.
09873: * @return <code>null</code> if no type with the given name is found
09874: */
09875: private IClass findMemberType(IClass iClass, String name,
09876: Location location) throws CompileException {
09877: IClass[] types = iClass.findMemberType(name);
09878: if (types.length == 0)
09879: return null;
09880: if (types.length == 1)
09881: return types[0];
09882:
09883: StringBuffer sb = new StringBuffer("Type \"" + name
09884: + "\" is ambiguous: " + types[0].toString());
09885: for (int i = 1; i < types.length; ++i)
09886: sb.append(" vs. ").append(types[i].toString());
09887: this .compileError(sb.toString(), location);
09888: return types[0];
09889: }
09890:
09891: /**
09892: * Find one class or interface by name.
09893: * @param className Fully qualified class name, e.g. "pkg1.pkg2.Outer$Inner".
09894: * @return <code>null</code> if a class with that name is not declared in this compilation unit
09895: */
09896: public IClass findClass(String className) {
09897:
09898: // Examine package name.
09899: String packageName = (this .compilationUnit.optionalPackageDeclaration == null ? null
09900: : this .compilationUnit.optionalPackageDeclaration.packageName);
09901: if (packageName != null) {
09902: if (!className.startsWith(packageName + '.'))
09903: return null;
09904: className = className.substring(packageName.length() + 1);
09905: }
09906:
09907: StringTokenizer st = new StringTokenizer(className, "$");
09908: Java.TypeDeclaration td = this .compilationUnit
09909: .getPackageMemberTypeDeclaration(st.nextToken());
09910: if (td == null)
09911: return null;
09912: while (st.hasMoreTokens()) {
09913: td = td.getMemberTypeDeclaration(st.nextToken());
09914: if (td == null)
09915: return null;
09916: }
09917: return this .resolve((Java.AbstractTypeDeclaration) td);
09918: }
09919:
09920: /**
09921: * Equivalent to {@link #compileError(String, Location)} with a
09922: * <code>null</code> location argument.
09923: */
09924: private void compileError(String message) throws CompileException {
09925: this .compileError(message, null);
09926: }
09927:
09928: /**
09929: * Issue a compile error with the given message. This is done through the
09930: * {@link ErrorHandler} that was installed through
09931: * {@link #setCompileErrorHandler(ErrorHandler)}. Such a handler typically throws
09932: * a {@link CompileException}, but it may as well decide to return normally. Consequently,
09933: * the calling code must be prepared that {@link #compileError(String, Location)}
09934: * returns normally, and must attempt to continue compiling.
09935: *
09936: * @param message The message to report
09937: * @param optionalLocation The location to report
09938: */
09939: private void compileError(String message, Location optionalLocation)
09940: throws CompileException {
09941: ++this .compileErrorCount;
09942: if (this .optionalCompileErrorHandler != null) {
09943: this .optionalCompileErrorHandler.handleError(message,
09944: optionalLocation);
09945: } else {
09946: throw new CompileException(message, optionalLocation);
09947: }
09948: }
09949:
09950: /**
09951: * Issues a warning with the given message an location an returns. This is done through
09952: * a {@link WarningHandler} that was installed through
09953: * {@link #setWarningHandler(WarningHandler)}.
09954: * <p>
09955: * The <code>handle</code> argument qulifies the warning and is typically used by
09956: * the {@link WarningHandler} to suppress individual warnings.
09957: *
09958: * @param handle
09959: * @param message
09960: * @param optionalLocation
09961: */
09962: private void warning(String handle, String message,
09963: Location optionalLocation) {
09964: if (this .optionalWarningHandler != null)
09965: this .optionalWarningHandler.handleWarning(handle, message,
09966: optionalLocation);
09967: }
09968:
09969: /**
09970: * Interface type for {@link UnitCompiler#setCompileErrorHandler}.
09971: */
09972: public interface ErrorHandler {
09973: void handleError(String message, Location optionalLocation)
09974: throws CompileException;
09975: }
09976:
09977: /**
09978: * By default, {@link CompileException}s are thrown on compile errors, but an application
09979: * my install its own (thread-local) {@link ErrorHandler}.
09980: * <p>
09981: * Be aware that a single problem during compilation often causes a bunch of compile errors,
09982: * so a good {@link ErrorHandler} counts errors and throws a {@link CompileException} when
09983: * a limit is reached.
09984: * <p>
09985: * If the given {@link ErrorHandler} does not throw {@link CompileException}s, then
09986: * {@link #compileUnit(EnumeratorSet)} will throw one when the compilation of the unit
09987: * is finished, and errors had occurred. In other words: The {@link ErrorHandler} may
09988: * throw a {@link CompileException} or not, but {@link #compileUnit(EnumeratorSet)} will
09989: * definitely throw a {@link CompileException} if one or more compile errors have
09990: * occurred.
09991: *
09992: * @param optionalCompileErrorHandler <code>null</code> to restore the default behavior (throwing a {@link CompileException}
09993: */
09994: public void setCompileErrorHandler(
09995: ErrorHandler optionalCompileErrorHandler) {
09996: this .optionalCompileErrorHandler = optionalCompileErrorHandler;
09997: }
09998:
09999: /**
10000: * By default, warnings are discarded, but an application my install a custom
10001: * {@link WarningHandler}.
10002: *
10003: * @param optionalWarningHandler <code>null</code> to indicate that no warnings be issued
10004: */
10005: public void setWarningHandler(WarningHandler optionalWarningHandler) {
10006: this .optionalWarningHandler = optionalWarningHandler;
10007: }
10008:
10009: private CodeContext getCodeContext() {
10010: CodeContext res = this .codeContext;
10011: if (res == null)
10012: throw new RuntimeException("S.N.O.: Null CodeContext");
10013: return res;
10014: }
10015:
10016: private CodeContext replaceCodeContext(CodeContext newCodeContext) {
10017: CodeContext oldCodeContext = this .codeContext;
10018: this .codeContext = newCodeContext;
10019: return oldCodeContext;
10020: }
10021:
10022: private CodeContext createDummyCodeContext() {
10023: return new CodeContext(this .getCodeContext().getClassFile());
10024: }
10025:
10026: private void write(Java.Locatable l, byte[] b) {
10027: this .codeContext.write(l.getLocation().getLineNumber(), b);
10028: }
10029:
10030: private void writeByte(Java.Locatable l, int v) {
10031: this .codeContext.write(l.getLocation().getLineNumber(),
10032: new byte[] { (byte) v });
10033: }
10034:
10035: private void writeInt(Java.Locatable l, int v) {
10036: this .codeContext.write(l.getLocation().getLineNumber(),
10037: new byte[] { (byte) (v >> 24), (byte) (v >> 16),
10038: (byte) (v >> 8), (byte) v });
10039: }
10040:
10041: private void writeShort(Java.Locatable l, int v) {
10042: this .codeContext.write(l.getLocation().getLineNumber(),
10043: new byte[] { (byte) (v >> 8), (byte) v });
10044: }
10045:
10046: private void writeOpcode(Java.Locatable l, int opcode) {
10047: this .writeByte(l, opcode);
10048: }
10049:
10050: private void writeBranch(Java.Locatable l, int opcode,
10051: final CodeContext.Offset dst) {
10052: this .codeContext.writeBranch(l.getLocation().getLineNumber(),
10053: opcode, dst);
10054: }
10055:
10056: private void writeBranch(int opcode, final CodeContext.Offset dst) {
10057: this .codeContext.writeBranch((short) -1, opcode, dst);
10058: }
10059:
10060: private void writeOffset(Java.Locatable l, CodeContext.Offset src,
10061: final CodeContext.Offset dst) {
10062: this .codeContext.writeOffset(l.getLocation().getLineNumber(),
10063: src, dst);
10064: }
10065:
10066: // Wrappers for "ClassFile.addConstant...Info()". Saves us some coding overhead.
10067:
10068: private void writeConstantClassInfo(Java.Locatable l,
10069: String descriptor) {
10070: CodeContext ca = this .codeContext;
10071: ca.writeShort(l.getLocation().getLineNumber(), ca
10072: .getClassFile().addConstantClassInfo(descriptor));
10073: }
10074:
10075: private void writeConstantFieldrefInfo(Java.Locatable l,
10076: String classFD, String fieldName, String fieldFD) {
10077: CodeContext ca = this .codeContext;
10078: ca.writeShort(l.getLocation().getLineNumber(), ca
10079: .getClassFile().addConstantFieldrefInfo(classFD,
10080: fieldName, fieldFD));
10081: }
10082:
10083: private void writeConstantMethodrefInfo(Java.Locatable l,
10084: String classFD, String methodName, String methodMD) {
10085: CodeContext ca = this .codeContext;
10086: ca.writeShort(l.getLocation().getLineNumber(), ca
10087: .getClassFile().addConstantMethodrefInfo(classFD,
10088: methodName, methodMD));
10089: }
10090:
10091: private void writeConstantInterfaceMethodrefInfo(Java.Locatable l,
10092: String classFD, String methodName, String methodMD) {
10093: CodeContext ca = this .codeContext;
10094: ca.writeShort(l.getLocation().getLineNumber(), ca
10095: .getClassFile().addConstantInterfaceMethodrefInfo(
10096: classFD, methodName, methodMD));
10097: }
10098:
10099: /* UNUSED
10100: private void writeConstantStringInfo(Java.Locatable l, String value) {
10101: this.codeContext.writeShort(
10102: l.getLocation().getLineNumber(),
10103: this.addConstantStringInfo(value)
10104: );
10105: }
10106: */
10107: private short addConstantStringInfo(String value) {
10108: return this .codeContext.getClassFile().addConstantStringInfo(
10109: value);
10110: }
10111:
10112: /* UNUSED
10113: private void writeConstantIntegerInfo(Java.Locatable l, int value) {
10114: this.codeContext.writeShort(
10115: l.getLocation().getLineNumber(),
10116: this.addConstantIntegerInfo(value)
10117: );
10118: }
10119: */
10120: private short addConstantIntegerInfo(int value) {
10121: return this .codeContext.getClassFile().addConstantIntegerInfo(
10122: value);
10123: }
10124:
10125: /* UNUSED
10126: private void writeConstantFloatInfo(Java.Locatable l, float value) {
10127: this.codeContext.writeShort(
10128: l.getLocation().getLineNumber(),
10129: this.addConstantFloatInfo(value)
10130: );
10131: }
10132: */
10133: private short addConstantFloatInfo(float value) {
10134: return this .codeContext.getClassFile().addConstantFloatInfo(
10135: value);
10136: }
10137:
10138: private void writeConstantLongInfo(Java.Locatable l, long value) {
10139: CodeContext ca = this .codeContext;
10140: ca.writeShort(l.getLocation().getLineNumber(), ca
10141: .getClassFile().addConstantLongInfo(value));
10142: }
10143:
10144: private void writeConstantDoubleInfo(Java.Locatable l, double value) {
10145: CodeContext ca = this .codeContext;
10146: ca.writeShort(l.getLocation().getLineNumber(), ca
10147: .getClassFile().addConstantDoubleInfo(value));
10148: }
10149:
10150: public CodeContext.Offset getWhereToBreak(Java.BreakableStatement bs) {
10151: if (bs.whereToBreak == null) {
10152: bs.whereToBreak = this .codeContext.new Offset();
10153: }
10154: return bs.whereToBreak;
10155: }
10156:
10157: private Java.TypeBodyDeclaration getDeclaringTypeBodyDeclaration(
10158: Java.QualifiedThisReference qtr) throws CompileException {
10159: if (qtr.declaringTypeBodyDeclaration == null) {
10160:
10161: // Compile error if in static function context.
10162: Java.Scope s;
10163: for (s = qtr.getEnclosingBlockStatement(); !(s instanceof Java.TypeBodyDeclaration); s = s
10164: .getEnclosingScope())
10165: ;
10166: qtr.declaringTypeBodyDeclaration = (Java.TypeBodyDeclaration) s;
10167: if (qtr.declaringTypeBodyDeclaration.isStatic())
10168: this
10169: .compileError(
10170: "No current instance available in static method",
10171: qtr.getLocation());
10172:
10173: // Determine declaring type.
10174: qtr.declaringClass = (Java.ClassDeclaration) qtr.declaringTypeBodyDeclaration
10175: .getDeclaringType();
10176: }
10177: return qtr.declaringTypeBodyDeclaration;
10178: }
10179:
10180: private Java.ClassDeclaration getDeclaringClass(
10181: Java.QualifiedThisReference qtr) throws CompileException {
10182: if (qtr.declaringClass == null) {
10183: this .getDeclaringTypeBodyDeclaration(qtr);
10184: }
10185: return qtr.declaringClass;
10186: }
10187:
10188: private void referenceThis(Java.Located located) {
10189: this .writeOpcode(located, Opcode.ALOAD_0);
10190: }
10191:
10192: /**
10193: * Expects "dimExprCount" values of type "integer" on the operand stack.
10194: * Creates an array of "dimExprCount" + "dims" dimensions of
10195: * "componentType".
10196: *
10197: * @return The type of the created array
10198: */
10199: private IClass newArray(Java.Located located, int dimExprCount,
10200: int dims, IClass componentType) {
10201: if (dimExprCount == 1 && dims == 0
10202: && componentType.isPrimitive()) {
10203:
10204: // "new <primitive>[<n>]"
10205: this .writeOpcode(located, Opcode.NEWARRAY);
10206: this
10207: .writeByte(
10208: located,
10209: (componentType == IClass.BOOLEAN ? 4
10210: : componentType == IClass.CHAR ? 5
10211: : componentType == IClass.FLOAT ? 6
10212: : componentType == IClass.DOUBLE ? 7
10213: : componentType == IClass.BYTE ? 8
10214: : componentType == IClass.SHORT ? 9
10215: : componentType == IClass.INT ? 10
10216: : componentType == IClass.LONG ? 11
10217: : -1));
10218: return componentType
10219: .getArrayIClass(this .iClassLoader.OBJECT);
10220: }
10221:
10222: if (dimExprCount == 1) {
10223: IClass at = componentType.getArrayIClass(dims,
10224: this .iClassLoader.OBJECT);
10225:
10226: // "new <class-or-interface>[<n>]"
10227: // "new <anything>[<n>][]..."
10228: this .writeOpcode(located, Opcode.ANEWARRAY);
10229: this .writeConstantClassInfo(located, at.getDescriptor());
10230: return at.getArrayIClass(this .iClassLoader.OBJECT);
10231: } else {
10232: IClass at = componentType.getArrayIClass(dimExprCount
10233: + dims, this .iClassLoader.OBJECT);
10234:
10235: // "new <anything>[]..."
10236: // "new <anything>[<n>][<m>]..."
10237: // "new <anything>[<n>][<m>]...[]..."
10238: this .writeOpcode(located, Opcode.MULTIANEWARRAY);
10239: this .writeConstantClassInfo(located, at.getDescriptor());
10240: this .writeByte(located, dimExprCount);
10241: return at;
10242: }
10243: }
10244:
10245: /**
10246: * Short-hand implementation of {@link IClass.IField} that implements a
10247: * non-constant, non-static, package-accessible field.
10248: */
10249: public static class SimpleIField extends IClass.IField {
10250: private final String name;
10251: private final IClass type;
10252:
10253: public SimpleIField(IClass declaringIClass, String name,
10254: IClass type) {
10255: declaringIClass.super ();
10256: this .name = name;
10257: this .type = type;
10258: }
10259:
10260: public Object getConstantValue() {
10261: return null;
10262: }
10263:
10264: public String getName() {
10265: return this .name;
10266: }
10267:
10268: public IClass getType() {
10269: return this .type;
10270: }
10271:
10272: public boolean isStatic() {
10273: return false;
10274: }
10275:
10276: public Access getAccess() {
10277: return Access.DEFAULT;
10278: }
10279: };
10280:
10281: private static Access modifiers2Access(short modifiers) {
10282: return ((modifiers & Mod.PUBLIC) != 0 ? Access.PUBLIC
10283: : (modifiers & Mod.PROTECTED) != 0 ? Access.PROTECTED
10284: : (modifiers & Mod.PRIVATE) != 0 ? Access.PRIVATE
10285: : Access.DEFAULT);
10286: }
10287:
10288: // Used to write byte code while compiling one constructor/method.
10289: private CodeContext codeContext = null;
10290:
10291: // Used for elaborate compile error handling.
10292: private ErrorHandler optionalCompileErrorHandler = null;
10293: private int compileErrorCount = 0;
10294:
10295: // Used for elaborate warning handling.
10296: private WarningHandler optionalWarningHandler = null;
10297:
10298: public final Java.CompilationUnit compilationUnit;
10299:
10300: private List generatedClassFiles;
10301: private IClassLoader iClassLoader;
10302: private EnumeratorSet debuggingInformation;
10303: }
|