0001: /*
0002: * xtc - The eXTensible Compiler
0003: * Copyright (C) 2007 IBM Corp.
0004: *
0005: * This program is free software; you can redistribute it and/or
0006: * modify it under the terms of the GNU General Public License
0007: * version 2 as published by the Free Software Foundation.
0008: *
0009: * This program is distributed in the hope that it will be useful,
0010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0012: * GNU General Public License for more details.
0013: *
0014: * You should have received a copy of the GNU General Public License
0015: * along with this program; if not, write to the Free Software
0016: * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
0017: * USA.
0018: */
0019: package xtc.lang.jeannie;
0020:
0021: import java.io.File;
0022: import java.util.ArrayList;
0023: import java.util.Arrays;
0024: import java.util.Collections;
0025: import java.util.HashSet;
0026: import java.util.List;
0027: import java.util.Set;
0028: import java.util.Stack;
0029:
0030: import xtc.Constants;
0031: import xtc.lang.CAnalyzer;
0032: import xtc.lang.JavaAnalyzer;
0033: import xtc.lang.JavaEntities;
0034: import xtc.lang.JavaTypeConverter;
0035: import xtc.tree.GNode;
0036: import xtc.tree.Node;
0037: import xtc.tree.Visitor;
0038: import xtc.type.C;
0039: import xtc.type.DynamicReference;
0040: import xtc.type.ErrorT;
0041: import xtc.type.FunctionT;
0042: import xtc.type.PointerT;
0043: import xtc.type.Reference;
0044: import xtc.type.Type;
0045: import xtc.type.VariableT;
0046: import xtc.util.Runtime;
0047: import xtc.util.SymbolTable;
0048:
0049: /**
0050: * A visitor that constructs a symbol table for a Jeannie file.
0051: * Assumes that the AST has been simplified with jeannie.AstSimplifier.
0052: *
0053: * @author Martin Hirzel
0054: */
0055: public final class Analyzer extends Visitor {
0056: static final class JeannieCAnalyzer extends CAnalyzer {
0057: private static final class JeannieC extends C {
0058: public final boolean isConstant(final Type t) {
0059: if (Utilities.isJavaEntity(t))
0060: return JavaEntities.isConstantT(t);
0061: if (t.hasAttribute(Constants.ATT_CONSTANT, false))
0062: return true;
0063: switch (t.tag()) {
0064: case ARRAY: {
0065: final Type e = t.resolve().toArray().getType();
0066: return null == e ? false : isConstant(e);
0067: }
0068: case STRUCT:
0069: case UNION: {
0070: for (final Type e : t.toTagged().getMembers())
0071: if (isConstant(e))
0072: return true;
0073: return false;
0074: }
0075: }
0076: return t.isWrapped() ? isConstant(t.toWrapped()
0077: .getType()) : false;
0078: }
0079: }
0080:
0081: static final class JeannieCContext {
0082: boolean _hasScope;
0083: boolean _isTopLevel;
0084: boolean _localsAreLifted;
0085: int _loops;
0086: List<Type> _openArrays;
0087: List<Boolean> _switches;
0088: }
0089:
0090: final class JeannieCSpecifiers extends CAnalyzer.Specifiers {
0091: public JeannieCSpecifiers(final GNode specifiers_,
0092: final boolean refIsDecl_) {
0093: super (specifiers_, refIsDecl_);
0094: }
0095:
0096: /** Visit a JavaType = Java.TypeName. */
0097: public final void visitJavaType(final GNode n) {
0098: if (hasType()) {
0099: multipleTypes();
0100: return;
0101: }
0102: _jeannieAnalyzer.enterJava();
0103: final Type result;
0104: final SymbolTable tab = getTable();
0105: if (n.getNode(0).hasName("QualifiedIdentifier")) {
0106: final String qualifiedName = (String) _jeannieAnalyzer
0107: .dispatch(n.getNode(0));
0108: final List<File> paths = JavaEntities
0109: .classpath(getRuntime());
0110: result = JavaEntities.qualifiedNameToType(tab,
0111: paths, tab.current().getQualifiedName(),
0112: qualifiedName);
0113: } else {
0114: assert n.getNode(0).hasName("PrimitiveType");
0115: final Type jType = (Type) _jeannieAnalyzer
0116: .dispatch(n.getNode(0));
0117: result = Utilities.javaTypeToCType(tab, runtime, n,
0118: jType, true);
0119: }
0120: _jeannieAnalyzer.exitJava();
0121: type = JavaAnalyzer.setType(n, result);
0122: }
0123: }
0124:
0125: /**
0126: * Descends into a Declarator to find a FunctionDeclarator with a throws clause, if any:<br>
0127: * FunctionDeclarator = (ParameterTypeList / IdentifierList) JavaThrows.
0128: */
0129: protected static GNode getDeclaredExceptions(
0130: final GNode declarator) {
0131: @SuppressWarnings("unused")
0132: final Visitor v = new Visitor() {
0133: public final Object visit(final GNode n) {
0134: return null;
0135: }
0136:
0137: public final Object visitArrayDeclarator(final GNode n) {
0138: return dispatch(n.getGeneric(0));
0139: }
0140:
0141: public final Object visitAttributedDeclarator(
0142: final GNode n) {
0143: return dispatch(n.getGeneric(1));
0144: }
0145:
0146: public final Object visitFunctionDeclarator(
0147: final GNode n) {
0148: final GNode result = n.getGeneric(2); //TD 05 visitFunctionDeclarator throws clause is at index 1!
0149: assert null == result
0150: || result.hasName("JavaThrows");
0151: return result;
0152: }
0153:
0154: public final Object visitPointerDeclarator(final GNode n) {
0155: return dispatch(n.getGeneric(1));
0156: }
0157: };
0158: return GNode.cast(v.dispatch(declarator));
0159: }
0160:
0161: final Analyzer _jeannieAnalyzer;
0162: boolean _localsAreLifted;
0163: final List<Type> _openArrays;
0164:
0165: JeannieCAnalyzer(final Analyzer jeannieAnalyzer) {
0166: super (new JeannieC(), null == jeannieAnalyzer ? Utilities
0167: .newRuntime() : jeannieAnalyzer._runtime);
0168: _jeannieAnalyzer = jeannieAnalyzer;
0169: _openArrays = new ArrayList<Type>();
0170: this .table = null == jeannieAnalyzer ? null
0171: : jeannieAnalyzer._table;
0172: isTopLevel = true;
0173: _localsAreLifted = false;
0174: loops.clear();
0175: switches.clear();
0176: }
0177:
0178: /** Use this for asserting that the input is typed correctly. */
0179: protected boolean assrt(final GNode n, final boolean cond,
0180: final String msgFormat, final Object... msgArgs) {
0181: return JavaEntities.runtimeAssrt(runtime, n, cond,
0182: msgFormat, msgArgs);
0183: }
0184:
0185: final void assrtOpenArray(final GNode n, final Type t) {
0186: if (t.hasShape()) {
0187: final Reference tRef = t.getShape();
0188: for (int i = _openArrays.size() - 1; i >= 0; i--) {
0189: final Type o = _openArrays.get(i);
0190: if (o.hasShape()) {
0191: final Reference oRef = o.getShape();
0192: if (tRef == oRef) {
0193: if (i != _openArrays.size() - 1)
0194: runtime
0195: .error(
0196: "_with statement for '"
0197: + n
0198: .getGeneric(
0199: 0)
0200: .getString(
0201: 0)
0202: + "' not immediately enclosing",
0203: n);
0204: return;
0205: }
0206: }
0207: }
0208: }
0209: runtime.error("no enclosing _with statement for '"
0210: + n.getGeneric(0).getString(0) + "'", n);
0211: }
0212:
0213: final void contextRestore(final JeannieCContext other) {
0214: hasScope = other._hasScope;
0215: isTopLevel = other._isTopLevel;
0216: _localsAreLifted = other._localsAreLifted;
0217: assert _openArrays.size() >= other._openArrays.size();
0218: while (_openArrays.size() > other._openArrays.size())
0219: _openArrays.remove(loops.size() - 1);
0220: for (int i = 0; i < _openArrays.size(); i++)
0221: assert _openArrays.get(i) == other._openArrays.get(i);
0222: assert loops.size() >= other._loops;
0223: while (loops.size() > other._loops)
0224: loops.remove(loops.size() - 1);
0225: for (int i = 0; i < other._loops; i++)
0226: assert loops.get(i).booleanValue();
0227: assert switches.size() >= other._switches.size();
0228: while (switches.size() > other._switches.size())
0229: switches.remove(switches.size() - 1);
0230: for (int i = 0; i < switches.size(); i++)
0231: assert switches.get(i).booleanValue() == other._switches
0232: .get(i);
0233: }
0234:
0235: final JeannieCContext contextSave() {
0236: final JeannieCContext result = new JeannieCContext();
0237: result._hasScope = hasScope;
0238: result._isTopLevel = isTopLevel;
0239: result._localsAreLifted = _localsAreLifted;
0240: result._loops = loops.size();
0241: result._openArrays = new ArrayList<Type>(_openArrays.size());
0242: for (final Type t : _openArrays)
0243: result._openArrays.add(t);
0244: result._switches = new ArrayList<Boolean>(switches.size());
0245: for (final Boolean b : switches)
0246: result._switches.add(b);
0247: return result;
0248: }
0249:
0250: final boolean getHasScope() {
0251: return hasScope;
0252: }
0253:
0254: final List<Boolean> getLoops() {
0255: return loops;
0256: }
0257:
0258: final Runtime getRuntime() {
0259: return runtime;
0260: }
0261:
0262: final List<Boolean> getSwitches() {
0263: return switches;
0264: }
0265:
0266: final SymbolTable getTable() {
0267: return table;
0268: }
0269:
0270: public final Specifiers newSpecifiers(final GNode specifiers,
0271: final boolean refIsDecl) {
0272: return new JeannieCSpecifiers(specifiers, refIsDecl);
0273: }
0274:
0275: protected final Type processAssignment(final boolean init,
0276: final String op, final Node n, final Type lhsType,
0277: final Type rhsType) {
0278: final Type lr = Utilities.getJavaClassOrInterfaceT(lhsType);
0279: if (JavaEntities.isWrappedClassT(lr)
0280: || JavaEntities.isWrappedInterfaceT(lr)) {
0281: final Type rr = Utilities.cTypeToJavaType(table,
0282: runtime, n, rhsType);
0283: if (rr.isError())
0284: return ErrorT.TYPE;
0285: final List<File> paths = JavaEntities
0286: .classpath(runtime);
0287: if (JavaTypeConverter
0288: .isAssignable(table, paths, lr, rr))
0289: return lr;
0290: runtime.error("illegal C assignment to Java type '"
0291: + JavaEntities.javaTypeToString(table, lr)
0292: + "'", n);
0293: return ErrorT.TYPE;
0294: }
0295: Type rr = Utilities.getJavaClassOrInterfaceT(rhsType);
0296: if (JavaEntities.isWrappedClassT(rr)
0297: || JavaEntities.isWrappedInterfaceT(rr))
0298: rr = Utilities.javaTypeToCType(table, runtime, n, rr,
0299: false);
0300: return super .processAssignment(init, op, n, lhsType, rr);
0301: }
0302:
0303: protected final void processParameters(final GNode n,
0304: final FunctionT function) {
0305: function
0306: .setExceptions(_jeannieAnalyzer._jAnalyzer._context._handledExceptions);
0307: super .processParameters(n, function);
0308: }
0309:
0310: final void setHasScope(final boolean x) {
0311: hasScope = x;
0312: }
0313:
0314: final void setIsTopLevel(final boolean x) {
0315: isTopLevel = x;
0316: }
0317:
0318: /** Visit a CancelStatement = JeannieC.PrimaryIdentifier. */
0319: public final void visitCancelStatement(final GNode n) {
0320: final Type t = (Type) _jeannieAnalyzer.dispatch(n
0321: .getGeneric(0));
0322: assrtOpenArray(n, t);
0323: }
0324:
0325: /** Visit a CInCBlock = LocalLabelDeclaration* JeannieC.DeclarationOrStatement* Annotations. */
0326: public final Type visitCInCBlock(final GNode n) {
0327: return super .visitCompoundStatement(n);
0328: }
0329:
0330: /** Visit a CommitStatement = JeannieC.PrimaryIdentifier. */
0331: public final void visitCommitStatement(final GNode n) {
0332: final Type t = (Type) _jeannieAnalyzer.dispatch(n
0333: .getGeneric(0));
0334: assrtOpenArray(n, t);
0335: }
0336:
0337: static class MiniVisitor_liftableDeclaration extends Visitor {
0338: private boolean _hasCompoundInitializer = false;
0339: private boolean _hasVariableArray = false;
0340:
0341: public final Boolean visit(final Node n) {
0342: for (final Object o : n)
0343: if (o instanceof Node) {
0344: dispatch((Node) o);
0345: if (_hasCompoundInitializer
0346: && _hasVariableArray)
0347: return false;
0348: }
0349: return true;
0350: }
0351:
0352: public final Boolean visitInitializerList(final GNode n) {
0353: _hasCompoundInitializer = true;
0354: if (_hasVariableArray)
0355: return false;
0356: return visit(n);
0357: }
0358:
0359: public final Boolean visitArrayDeclarator(final GNode n) {
0360: if (!n.getGeneric(2).hasName("IntegerConstant"))
0361: _hasVariableArray = true;
0362: if (_hasCompoundInitializer)
0363: return false;
0364: return visit(n);
0365: }
0366: }
0367:
0368: /** Visit a Declaration = ["__extension__"] DeclarationSpecifiers [InitializedDeclaratorList]. */
0369: public final void visitDeclaration(final GNode n) {
0370: super .visitDeclaration(n);
0371: if (_localsAreLifted) {
0372: final MiniVisitor_liftableDeclaration v = new MiniVisitor_liftableDeclaration();
0373: final boolean liftable = ((Boolean) v.dispatch(n))
0374: .booleanValue();
0375: assrt(n, liftable,
0376: "compound initializer with variable-sized array");
0377: }
0378: }
0379:
0380: /** Visit a FunctionCall = (PrimaryIdentifier / JeannieC.PostfixExpression) JeannieC.ExpressionList. */
0381: public final Type visitFunctionCall(final GNode n) {
0382: final GNode callee = n.getGeneric(0);
0383: if (callee.hasName("PrimaryIdentifier")
0384: && Analyzer.BUILTINS.contains(callee.getString(0)))
0385: return JavaAnalyzer.setType(n, _jeannieAnalyzer
0386: .processBuiltin(n));
0387: final Type result = super .visitFunctionCall(n);
0388: FunctionT function = null;
0389: {
0390: final Type t1;
0391: if (callee.hasName("PrimaryIdentifier"))
0392: t1 = (Type) table.lookup(callee.getString(0));
0393: else
0394: t1 = (Type) callee.getProperty(Constants.TYPE);
0395: if (null != t1) {
0396: final Type r1 = Utilities.c().pointerize(t1);
0397: if (!r1.isError() && r1.isPointer()) {
0398: final Type f1 = ((PointerT) r1).getType()
0399: .resolve();
0400: if (f1.isFunction())
0401: function = (FunctionT) f1;
0402: }
0403: }
0404: }
0405: if (null != function) {
0406: final List<Type> exc = function.getExceptions();
0407: if (null != exc) {
0408: final List<File> paths = JavaEntities
0409: .classpath(runtime);
0410: for (final Type tThrown : exc)
0411: if (JavaEntities.isCheckedException(table,
0412: paths, tThrown))
0413: if (!_jeannieAnalyzer._jAnalyzer
0414: .isHandled(tThrown))
0415: runtime.error("uncaught exception", n);
0416: }
0417: }
0418: return result;
0419: }
0420:
0421: /** Visit a FunctionDeclarator = (ParameterTypeList / IdentifierList) JavaThrows.
0422: * This method should never be called; look at visitFunctionDefinition(). */
0423: public final void visitFunctionDeclarator(final GNode n) {
0424: assert false && null == n;
0425: }
0426:
0427: /** Visit a FunctionDefinition = ["__extension__"] [DeclarationSpecifiers] Declarator [DeclarationList] CompoundStatement.<br> */
0428: public final void visitFunctionDefinition(final GNode n) {
0429: final GNode javaThrows = getDeclaredExceptions(n
0430: .getGeneric(2));
0431: _jeannieAnalyzer.enterJava();
0432: _jeannieAnalyzer.dispatch(javaThrows);
0433: _jeannieAnalyzer.enterC();
0434: assert !_localsAreLifted;
0435: _localsAreLifted = Utilities.containsCToJavaTransition(n);
0436: super .visitFunctionDefinition(n);
0437: _localsAreLifted = false;
0438: final String functionName = SymbolTable
0439: .fromNameSpace(xtc.util.Utilities
0440: .unqualify((String) n
0441: .getProperty(Constants.SCOPE)));
0442: final FunctionT function = ((Type) table.current()
0443: .lookupLocally(functionName)).resolve()
0444: .toFunction();
0445: _jeannieAnalyzer.exitC();
0446: _jeannieAnalyzer.exitJava();
0447: if (Utilities.containsCToJavaTransition(n)) {
0448: VariableT jniEnv = null;
0449: for (final Type param : function.getParameters())
0450: if ("env".equals(param.toVariable().getName())) {
0451: jniEnv = param.toVariable();
0452: break;
0453: }
0454: //TD 09 activate these assertions, then fix the unit tests
0455: if (false)
0456: if (assrt(
0457: n,
0458: null != jniEnv,
0459: "C function %s contains Java snippet but lacks explicit formal JNIEnv* env",
0460: functionName))
0461: assrt(n, Utilities.hasTypedefName(jniEnv
0462: .getType(), "JNIEnv"),
0463: "expected formal env to be of type JNIEnv");
0464: }
0465: JavaAnalyzer.setType(n, function);
0466: }
0467:
0468: /** Visit a JavaImports = ImportDeclaration*. */
0469: public final void visitJavaImports(final GNode n) {
0470: _jeannieAnalyzer.enterJava();
0471: _jeannieAnalyzer.dispatch(n.getGeneric(0));
0472: _jeannieAnalyzer.exitJava();
0473: }
0474:
0475: /** Visit a JavaInCBlock = JavaInJavaBlock. */
0476: public final void visitJavaInCBlock(final GNode n) {
0477: _jeannieAnalyzer.enterJava();
0478: _jeannieAnalyzer.dispatch(n.getGeneric(0));
0479: _jeannieAnalyzer.exitJava();
0480: }
0481:
0482: /** Visit a JavaInCExpression = JeannieJava.UnaryExpression. */
0483: public final Type visitJavaInCExpression(final GNode n) {
0484: _jeannieAnalyzer.enterJava();
0485: final Type jType = _jeannieAnalyzer.dispatchRValue(n
0486: .getGeneric(0));
0487: _jeannieAnalyzer.exitJava();
0488: final Type cType = Utilities.javaTypeToCType(table,
0489: runtime, n, jType, true);
0490: return JavaAnalyzer.setType(n, cType);
0491: }
0492:
0493: /** Visit a JavaInCStatement = TryStatement / SynchronizedStatement / ThrowStatement. */
0494: public final void visitJavaInCStatement(final GNode n) {
0495: _jeannieAnalyzer.enterJava();
0496: _jeannieAnalyzer.dispatch(n.getNode(0));
0497: _jeannieAnalyzer.exitJava();
0498: }
0499:
0500: /** Visit a JavaThrows = [ThrowsClause].
0501: * This method should be called on JeannieJavaAnalyzer, not on JeannieCAnalyzer; look at visitFunctionDefinition(). */
0502: public final void visitJavaThrows(final GNode n) {
0503: assert false && null == n;
0504: }
0505:
0506: /** Visit a JeannieC.PrimaryIdentifier = Identifier. */
0507: public final Type visitPrimaryIdentifier(final GNode n) {
0508: final String name = n.getString(0);
0509: Type result = (Type) table.lookup(name);
0510: if (null == result) {
0511: runtime.error("'" + name + "' undeclared", n);
0512: result = ErrorT.TYPE;
0513: } else if (Utilities.isJavaEntity(result)) {
0514: runtime.error("cannot use Java entity '" + name
0515: + "' in C context", n);
0516: result = ErrorT.TYPE;
0517: }
0518: mark(n, result);
0519: return result;
0520: }
0521:
0522: /** Visit a JeannieC.ReturnStatement = [CommaExpression]. */
0523: public final Type visitReturnStatement(final GNode n) {
0524: final Type functionOrMethod = Utilities
0525: .currentFunctionOrMethod(table);
0526: if (!functionOrMethod.isMethod())
0527: return super .visitReturnStatement(n);
0528: final Type t1 = (Type) _jeannieAnalyzer.dispatch(n
0529: .getNode(0));
0530: final Type result = functionOrMethod.toMethod().getResult();
0531: if (result.isVoid()) {
0532: if (null != t1)
0533: runtime
0534: .error(
0535: "'return' with a value, in method returning void",
0536: n);
0537: } else {
0538: if (null == t1) {
0539: runtime
0540: .error(
0541: "'return' with no value, in method returning non-void",
0542: n);
0543: } else {
0544: final Type jResult = Utilities.cTypeToJavaType(
0545: table, runtime, n, t1);
0546: assrt(n, JavaTypeConverter.isAssignable(table,
0547: JavaEntities.classpath(runtime), result,
0548: jResult), "return type mismatch");
0549: }
0550: }
0551: return JavaAnalyzer.setType(n, result);
0552: }
0553:
0554: /** Visit a TranslationUnit = [JavaImports] ExternalDeclaration* Annotations. */
0555: public final void visitTranslationUnit(final GNode n) {
0556: super .visitTranslationUnit(n);
0557: }
0558:
0559: /** Visit a WithStatement = (JeannieC.Declaration / JeannieC.AssignmentExpression) CInCBlock. */
0560: public final void visitWithStatement(final GNode n) {
0561: final boolean oldHasScope = hasScope;
0562: hasScope = false;
0563: table.enter(table.freshName("withStatement"));
0564: table.mark(n);
0565: final GNode initNode = n.getGeneric(0);
0566: final GNode cNode, jNode;
0567: final Type cType, jType;
0568: if (initNode.hasName("Declaration")) {
0569: final GNode initDtorList = initNode.getGeneric(2);
0570: if (assrt(n, null != initDtorList
0571: && 1 == initDtorList.size(),
0572: "expected exactly one initializer")) {
0573: final GNode dtor = initDtorList.getGeneric(0);
0574: cNode = dtor.getGeneric(1);
0575: jNode = dtor.getGeneric(4);
0576: dtor.set(4, null);
0577: _jeannieAnalyzer.dispatch(initNode);
0578: dtor.set(4, jNode);
0579: final String id = CAnalyzer.getDeclaredId(cNode)
0580: .getString(0);
0581: cType = (Type) table.current().lookupLocally(id);
0582: if (assrt(jNode, null != jNode,
0583: "initializer expected"))
0584: jType = (Type) _jeannieAnalyzer.dispatch(jNode);
0585: else
0586: jType = ErrorT.TYPE;
0587: } else {
0588: cType = jType = ErrorT.TYPE;
0589: cNode = jNode = null;
0590: }
0591: } else {
0592: assert initNode.hasName("AssignmentExpression");
0593: cNode = initNode.getGeneric(0);
0594: jNode = initNode.getGeneric(2);
0595: final GNode lhsNode = cNode;
0596: if (assrt(lhsNode,
0597: lhsNode.hasName("PrimaryIdentifier"),
0598: "primary identifier expected")) {
0599: final Type t = (Type) _jeannieAnalyzer
0600: .dispatch(lhsNode);
0601: cType = ensureLValue(lhsNode, t) ? t : ErrorT.TYPE;
0602: } else {
0603: cType = ErrorT.TYPE;
0604: }
0605: assrt(n, "=".equals(initNode.getString(1)),
0606: "expected operator '=', not %s", initNode
0607: .getString(1));
0608: jType = (Type) _jeannieAnalyzer.dispatch(jNode);
0609: }
0610: mark(initNode, cType);
0611: _jeannieAnalyzer.assrtCorrectArrayConversion(cNode, cType,
0612: jNode, jType);
0613: final int nOpenArrays = _openArrays.size();
0614: _openArrays.add(cType);
0615: _jeannieAnalyzer.dispatch(n.getNode(1));
0616: assert nOpenArrays + 1 == _openArrays.size();
0617: _openArrays.remove(nOpenArrays);
0618: table.exit(n);
0619: hasScope = oldHasScope;
0620: }
0621: }
0622:
0623: private static final class JeannieContext {
0624: final Visitor _analyzer;
0625: final Object _savedContext;
0626:
0627: JeannieContext(final Visitor analyzer, final Object savedContext) {
0628: _analyzer = analyzer;
0629: _savedContext = savedContext;
0630: }
0631: }
0632:
0633: private static final class JeannieJavaAnalyzer extends JavaAnalyzer {
0634: final Analyzer _jeannieAnalyzer;
0635:
0636: JeannieJavaAnalyzer(final Analyzer jeannieAnalyzer) {
0637: super (jeannieAnalyzer._runtime, jeannieAnalyzer._table);
0638: _jeannieAnalyzer = jeannieAnalyzer;
0639: }
0640:
0641: protected void assrtLegalMethodBody(final GNode n,
0642: final Type method) {
0643: final boolean isAbstract = JavaAnalyzer.hasModifier(method,
0644: "abstract");
0645: assrt(n, isAbstract == (null == n.get(7)),
0646: "methods should be abstract iff they have no body");
0647: if (null != n.get(7)) {
0648: final boolean isNative = JavaAnalyzer.hasModifier(
0649: method, "native");
0650: assrt(n, isNative == n.getGeneric(7).hasName(
0651: "CInJavaBlock"),
0652: "methods should be native iff their body is in C");
0653: }
0654: final Type jniEnv = (Type) _table.root().lookup("JNIEnv");
0655: if (null == jniEnv) {
0656: _runtime
0657: .error(
0658: "C typedef for 'JNIEnv' missing; did you forget to #include <jni.h>?",
0659: n);
0660: } else if (null != _table.current().lookupLocally("env")) {
0661: _runtime
0662: .error("formal parameter declaration of 'env' conflicts with implicit JNIEnv pointer");
0663: } else {
0664: final Type rval = new PointerT(jniEnv)
0665: .attribute(Constants.ATT_CONSTANT);
0666: final VariableT param = VariableT.newParam(rval, "env");
0667: final DynamicReference ref = new DynamicReference(
0668: "env", rval);
0669: final Type lval = Utilities.c().qualify(rval, param)
0670: .annotate().shape(ref);
0671: _table.current().define("env", lval);
0672: lval.scope(_table.current().getQualifiedName());
0673: }
0674: }
0675:
0676: /** Visit a CDeclarations = ExternalDeclaration*. */
0677: public final void visitCDeclarations(final GNode n) {
0678: _jeannieAnalyzer.enterC();
0679: assert _table.current().isRoot();
0680: for (int i = 0; i < n.size(); i++)
0681: _jeannieAnalyzer.dispatch(n.getNode(i));
0682: _jeannieAnalyzer.exitC();
0683: }
0684:
0685: /** Visit a CInJavaBlock = CInCBlock. */
0686: public final void visitCInJavaBlock(final GNode n) {
0687: _jeannieAnalyzer.enterC();
0688: for (int i = 0; i < n.size(); i++)
0689: _jeannieAnalyzer.dispatch(n.getNode(i));
0690: _jeannieAnalyzer.exitC();
0691: }
0692:
0693: /** Visit a CInJavaExpression = JeannieC.UnaryExpression. */
0694: public final Type visitCInJavaExpression(final GNode n) {
0695: _jeannieAnalyzer.enterC();
0696: final Type cType = (Type) _jeannieAnalyzer.dispatch(n
0697: .getNode(0));
0698: _jeannieAnalyzer.exitC();
0699: final Type result = Utilities.cTypeToJavaType(_table,
0700: _runtime, n, cType);
0701: return JavaAnalyzer.setType(n, result);
0702: }
0703:
0704: /** Visit a CompilationUnit = CDeclarations [PackageDeclaration] ImportDeclaration* JeannieJava.Declaration*. */
0705: public final void visitCompilationUnit(final GNode n) {
0706: if (null == n.get(1))
0707: visitPackageDeclaration(null);
0708: else
0709: dispatch(n.getNode(1));
0710: _table.enter(JavaEntities.fileNameToScopeName(n
0711: .getLocation().file));
0712: _table.mark(n);
0713: for (int i = 2; i < n.size(); i++)
0714: _externalAnalyzer.dispatch(n.getNode(i));
0715: final String fileScope = JavaEntities
0716: .enterScopeByQualifiedName(_table, "");
0717: dispatch(n.getNode(0));
0718: JavaEntities.enterScopeByQualifiedName(_table, fileScope);
0719: for (int i = 2; i < n.size(); i++)
0720: dispatch(n.getNode(i));
0721: _table.setScope(_table.root());
0722: }
0723:
0724: /** Visit a JavaInJavaBlock = JeannieJava.DeclarationOrStatement*. */
0725: public final Type visitJavaInJavaBlock(final GNode n) {
0726: return super .visitBlock(n);
0727: }
0728:
0729: /** Visit a JavaThrows = [ThrowsClause]. */
0730: public final List<Type> visitJavaThrows(final GNode n) {
0731: if (null == n || null == n.get(0))
0732: _context._handledExceptions = new ArrayList<Type>();
0733: else
0734: _context._handledExceptions = JavaEntities
0735: .typeList((List) _externalAnalyzer.dispatch(n
0736: .getNode(0)));
0737: assrtLegalHandledExceptions(n.getGeneric(0));
0738: return _context._handledExceptions;
0739: }
0740:
0741: /** Visit a MethodDeclaration = Modifiers null Type Identifier FormalParameters [Dimensions] [ThrowsClause] [Block]. */
0742: public final Type visitMethodDeclaration(final GNode n) {
0743: assert !_jeannieAnalyzer._cAnalyzer._localsAreLifted;
0744: _jeannieAnalyzer._cAnalyzer._localsAreLifted = Utilities
0745: .containsJavaToCTransition(n);
0746: final Type result = super .visitMethodDeclaration(n);
0747: _jeannieAnalyzer._cAnalyzer._localsAreLifted = false;
0748: return result;
0749: }
0750:
0751: /** Visit a JeannieJava.PrimaryIdentifier = Identifier. */
0752: public final Type visitPrimaryIdentifier(final GNode n) {
0753: final String name = n.getString(0);
0754: final Type result = (Type) _table.lookup(name);
0755: if (null != result && !result.hasError()
0756: && !Utilities.isJavaEntity(result)) {
0757: _runtime.error("cannot use C entity '" + name
0758: + "' in Java context", n);
0759: return JavaAnalyzer.setType(n, ErrorT.TYPE);
0760: }
0761: return super .visitPrimaryIdentifier(n);
0762: }
0763:
0764: /**
0765: * Visit a ReturnStatement = [Expression] (gosling_et_al_2000 <a
0766: * href="http://java.sun.com/docs/books/jls/second_edition/html/statements.doc.html#6767">§14.16</a>).
0767: */
0768: public final void visitReturnStatement(final GNode n) {
0769: final Type function = Utilities
0770: .currentFunctionOrMethod(_table);
0771: if (function.isMethod()) {
0772: super .visitReturnStatement(n);
0773: return;
0774: }
0775: Type t1 = null;
0776: if (null != n.get(0)) {
0777: final Type jType = _jeannieAnalyzer.dispatchRValue(n
0778: .getGeneric(0));
0779: t1 = Utilities.javaTypeToCType(_table, _runtime, n,
0780: jType, false);
0781: }
0782: final Type result = Utilities.returnType(function);
0783: if (result.resolve().isVoid()) {
0784: if (null != t1)
0785: _runtime
0786: .error(
0787: "'return' with a value, in function returning void",
0788: n);
0789: } else {
0790: if (null != t1)
0791: _jeannieAnalyzer._cAnalyzer.processAssignment(
0792: false, "return", n, result, t1);
0793: else
0794: _runtime
0795: .error(
0796: "'return' with no value, in function returning non-void",
0797: n);
0798: }
0799: }
0800: }
0801:
0802: static final Set<String> BUILTINS = Collections
0803: .unmodifiableSet(new HashSet<String>(Arrays
0804: .asList(new String[] { "_copyFromJava",
0805: "_copyToJava", "_newJavaString",
0806: "_stringUTFLength" })));
0807: final JeannieCAnalyzer _cAnalyzer;
0808: final JeannieJavaAnalyzer _jAnalyzer;
0809: final Runtime _runtime;
0810: private final Stack<JeannieContext> _stack;
0811: public final SymbolTable _table;
0812:
0813: public Analyzer(final Runtime runtime, final SymbolTable table,
0814: final String language) {
0815: _stack = new Stack<JeannieContext>();
0816: _table = table;
0817: _runtime = runtime;
0818: _cAnalyzer = new JeannieCAnalyzer(this );
0819: _jAnalyzer = new JeannieJavaAnalyzer(this );
0820: if ("C".equals(language))
0821: enterC();
0822: if ("Java".equals(language))
0823: enterJava();
0824: }
0825:
0826: private Visitor activeAnalyzer() {
0827: return _stack.isEmpty() ? null : _stack.peek()._analyzer;
0828: }
0829:
0830: final Type dispatchRValue(final GNode n) {
0831: final JeannieJavaAnalyzer a = (JeannieJavaAnalyzer) activeAnalyzer();
0832: return a.dispatchRValue(n);
0833: }
0834:
0835: final void enterC() {
0836: assert _cAnalyzer != activeAnalyzer();
0837: _stack.push(new JeannieContext(_cAnalyzer, _cAnalyzer
0838: .contextSave()));
0839: _cAnalyzer.setHasScope(_jAnalyzer._context._hasScope);
0840: _cAnalyzer.setIsTopLevel(JavaEntities.isScopeTopLevel(_table
0841: .current().getQualifiedName()));
0842: if (_jAnalyzer._context._loop)
0843: _cAnalyzer.getLoops().add(Boolean.TRUE);
0844: if (_jAnalyzer._context._switch)
0845: _cAnalyzer.getSwitches().add(Boolean.TRUE);
0846: }
0847:
0848: final void enterJava() {
0849: assert _jAnalyzer != activeAnalyzer();
0850: _stack.push(new JeannieContext(_jAnalyzer, _jAnalyzer._context
0851: .save()));
0852: _jAnalyzer._context._hasScope = _cAnalyzer.getHasScope();
0853: _jAnalyzer._context._loop = !_cAnalyzer.getLoops().isEmpty();
0854: _jAnalyzer._context._switch = !_cAnalyzer.getSwitches()
0855: .isEmpty();
0856: }
0857:
0858: final void exitC() {
0859: assert _cAnalyzer == activeAnalyzer();
0860: _cAnalyzer.contextRestore(savedContextC());
0861: _stack.pop();
0862: }
0863:
0864: final void exitJava() {
0865: assert _jAnalyzer == activeAnalyzer();
0866: _jAnalyzer._context.restore(savedContextJ());
0867: _stack.pop();
0868: }
0869:
0870: private Type processBuiltin(final GNode n) {
0871: final String name = n.getGeneric(0).getString(0);
0872: final GNode argsNode = n.getGeneric(1);
0873: @SuppressWarnings("unchecked")
0874: final List<Type> argTypes = (List<Type>) dispatch(argsNode);
0875: for (final Type t : argTypes)
0876: if (t.isError())
0877: return JavaAnalyzer.setType(n, ErrorT.TYPE);
0878: if ("_copyFromJava".equals(name)) {
0879: if (!_cAnalyzer
0880: .assrt(
0881: n,
0882: 5 == argsNode.size(),
0883: "invalid call to builtin function: `int _copyFromJava(cArray, cArrayStart, javaArray, javaArrayStart, length)"))
0884: return ErrorT.TYPE;
0885: _cAnalyzer.ensureInteger(argsNode.getNode(1), argTypes
0886: .get(1));
0887: _cAnalyzer.ensureInteger(argsNode.getNode(3), argTypes
0888: .get(3));
0889: _cAnalyzer.ensureInteger(argsNode.getNode(4), argTypes
0890: .get(4));
0891: assrtCorrectArrayConversion(argsNode.getGeneric(0),
0892: argTypes.get(0), argsNode.getGeneric(2), argTypes
0893: .get(2));
0894: return Utilities.javaTypeToCType(_table, _runtime, n,
0895: JavaEntities.nameToBaseType("int"), false);
0896: } else if ("_copyToJava".equals(name)) {
0897: if (!_cAnalyzer
0898: .assrt(
0899: n,
0900: 5 == argTypes.size(),
0901: "invalid call to builtin function: `int _copyToJava(javaArray, javaArrayStart, cArray, cArrayStart, length)"))
0902: return ErrorT.TYPE;
0903: _cAnalyzer.ensureInteger(argsNode.getNode(1), argTypes
0904: .get(1));
0905: _cAnalyzer.ensureInteger(argsNode.getNode(3), argTypes
0906: .get(3));
0907: _cAnalyzer.ensureInteger(argsNode.getNode(4), argTypes
0908: .get(4));
0909: if (!_cAnalyzer.assrt(argsNode.getGeneric(0), !Utilities
0910: .hasTypedefName(argTypes.get(0), "jstring"),
0911: "_copyToJava target must not be String"))
0912: return ErrorT.TYPE;
0913: assrtCorrectArrayConversion(argsNode.getGeneric(2),
0914: argTypes.get(2), argsNode.getGeneric(0), argTypes
0915: .get(0));
0916: return Utilities.javaTypeToCType(_table, _runtime, n,
0917: JavaEntities.nameToBaseType("int"), false);
0918: } else if ("_newJavaString".equals(name)) {
0919: if (!_cAnalyzer
0920: .assrt(argsNode, 1 == argsNode.size(),
0921: "invalid call to builtin function: `String _newJavaString(cArray)"))
0922: return JavaAnalyzer.setType(n, ErrorT.TYPE);
0923: final Type t = argTypes.get(0);
0924: final boolean typeOk = Utilities.isPtrChar(t)
0925: || Utilities.isPtrTypedef(t, "`char")
0926: || Utilities.isPtrTypedef(t, "jbyte");
0927: if (!_cAnalyzer.assrt(argsNode.getGeneric(0), typeOk,
0928: "expected pointer to char, `byte, or `char"))
0929: return JavaAnalyzer.setType(n, ErrorT.TYPE);
0930: return Utilities.pureCType(_table, _runtime, JavaEntities
0931: .tString(_table));
0932: } else if ("_stringUTFLength".equals(name)) {
0933: if (!_cAnalyzer
0934: .assrt(
0935: argsNode,
0936: 1 == argsNode.size()
0937: || 3 == argsNode.size(),
0938: "invalid call to builtin function: `int _stringUTFLength(jstring [, jStart, jLen])"))
0939: return JavaAnalyzer.setType(n, ErrorT.TYPE);
0940: final Type t = Utilities.pureCType(_table, _runtime,
0941: argTypes.get(0));
0942: if (!_cAnalyzer.assrt(argsNode.getGeneric(0), Utilities
0943: .hasTypedefName(t, "jstring"), "expected `String"))
0944: return JavaAnalyzer.setType(n, ErrorT.TYPE);
0945: if (3 == argsNode.size()) {
0946: _cAnalyzer.ensureInteger(argsNode.getNode(1), argTypes
0947: .get(1));
0948: _cAnalyzer.ensureInteger(argsNode.getNode(2), argTypes
0949: .get(2));
0950: }
0951: return Utilities.javaTypeToCType(_table, _runtime, n,
0952: JavaEntities.nameToBaseType("int"), false);
0953: } else {
0954: throw new Error("builtin " + name + " not yet implemented");
0955: }
0956: }
0957:
0958: private JeannieCAnalyzer.JeannieCContext savedContextC() {
0959: return (JeannieCAnalyzer.JeannieCContext) _stack.peek()._savedContext;
0960: }
0961:
0962: private JavaAnalyzer.JavaContext savedContextJ() {
0963: return (JavaAnalyzer.JavaContext) _stack.peek()._savedContext;
0964: }
0965:
0966: public final Object visit(final Node n) {
0967: return activeAnalyzer().dispatch(n);
0968: }
0969:
0970: private void assrtCorrectArrayConversion(final GNode cNode,
0971: final Type cType, final GNode jNode, final Type cjType) {
0972: if (cType.isError() || cjType.isError())
0973: return;
0974: final Type cResolved = Utilities.c().pointerize(cType);
0975: if (!_cAnalyzer.assrt(cNode, cResolved.isPointer(),
0976: "pointer or array type expected"))
0977: return;
0978: final Type cElem = ((PointerT) cResolved).getType();
0979: assert !cElem.isError();
0980: final Type jType = Utilities.cTypeToJavaType(_table, _runtime,
0981: jNode, cjType);
0982: assert !jType.isError() && JavaEntities.isGeneralRValueT(jType);
0983: if (jType.isArray()) {
0984: final Type jElem = JavaEntities
0985: .resolveToRawRValue(JavaEntities
0986: .arrayElementType(jType.toArray()));
0987: if (JavaEntities.isPrimitiveT(jElem)) {
0988: final String expectedName = "j"
0989: + JavaEntities.baseTypeToName(jElem);
0990: _cAnalyzer.assrt(cNode, Utilities.hasTypedefName(cElem,
0991: expectedName), "type '%s*' expected",
0992: expectedName);
0993: } else {
0994: final Type jcElem = Utilities.cTypeToJavaType(_table,
0995: _runtime, cNode, cElem);
0996: final boolean ok = JavaTypeConverter.isAssignable(
0997: _table, JavaEntities.classpath(_runtime),
0998: jcElem, jElem);
0999: _cAnalyzer.assrt(cNode, ok, "type 'jobject*' expected");
1000: }
1001: } else if (JavaTypeConverter.isIdentical(jType, JavaEntities
1002: .tString(_table))) {
1003: final boolean isChar = Utilities.hasTypedefName(cElem,
1004: "jchar");
1005: _cAnalyzer.assrt(cNode, isChar
1006: || Utilities.hasTypedefName(cElem, "jbyte"),
1007: "type 'jchar*' or 'jbyte*' expected");
1008: } else {
1009: _cAnalyzer.assrt(jNode, false,
1010: "string or primitive array expected");
1011: }
1012: }
1013:
1014: /** Visit a CompilationUnit = CDeclarations [PackageDeclaration] ImportDeclaration* JeannieJava.Declaration*. */
1015: public final void visitCompilationUnit(final GNode n) {
1016: assert null == activeAnalyzer();
1017: enterJava();
1018: activeAnalyzer().dispatch(n);
1019: exitJava();
1020: }
1021:
1022: /** Visit a TranslationUnit = [JavaImports] ExternalDeclaration* Annotations. */
1023: public final void visitTranslationUnit(final GNode n) {
1024: assert null == activeAnalyzer();
1025: enterC();
1026: activeAnalyzer().dispatch(n);
1027: exitC();
1028: }
1029:
1030: //TD 41 jeannie.Analyzer should disallow break, continue, and goto from crossing language boundary or _with boundary
1031: }
|