/* * xtc - The eXTensible Compiler * Copyright (C) 2007 IBM Corp. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ package xtc.lang.jeannie; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.Stack; import xtc.Constants; import xtc.lang.CAnalyzer; import xtc.lang.JavaAnalyzer; import xtc.lang.JavaEntities; import xtc.lang.JavaTypeConverter; import xtc.tree.GNode; import xtc.tree.Node; import xtc.tree.Visitor; import xtc.type.C; import xtc.type.DynamicReference; import xtc.type.ErrorT; import xtc.type.FunctionT; import xtc.type.PointerT; import xtc.type.Reference; import xtc.type.Type; import xtc.type.VariableT; import xtc.util.Runtime; import xtc.util.SymbolTable; /** * A visitor that constructs a symbol table for a Jeannie file. * Assumes that the AST has been simplified with jeannie.AstSimplifier. * * @author Martin Hirzel */ public final class Analyzer extends Visitor { static final class JeannieCAnalyzer extends CAnalyzer { private static final class JeannieC extends C { public final boolean isConstant(final Type t) { if (Utilities.isJavaEntity(t)) return JavaEntities.isConstantT(t); if (t.hasAttribute(Constants.ATT_CONSTANT, false)) return true; switch (t.tag()) { case ARRAY: { final Type e = t.resolve().toArray().getType(); return null == e ? false : isConstant(e); } case STRUCT: case UNION: { for (final Type e : t.toTagged().getMembers()) if (isConstant(e)) return true; return false; } } return t.isWrapped() ? isConstant(t.toWrapped().getType()) : false; } } static final class JeannieCContext { boolean _hasScope; boolean _isTopLevel; boolean _localsAreLifted; int _loops; List<Type> _openArrays; List<Boolean> _switches; } final class JeannieCSpecifiers extends CAnalyzer.Specifiers { public JeannieCSpecifiers(final GNode specifiers_, final boolean refIsDecl_) { super(specifiers_, refIsDecl_); } /** Visit a JavaType = Java.TypeName. */ public final void visitJavaType(final GNode n) { if (hasType()) { multipleTypes(); return; } _jeannieAnalyzer.enterJava(); final Type result; final SymbolTable tab = getTable(); if (n.getNode(0).hasName("QualifiedIdentifier")) { final String qualifiedName = (String) _jeannieAnalyzer.dispatch(n.getNode(0)); final List<File> paths = JavaEntities.classpath(getRuntime()); result = JavaEntities.qualifiedNameToType(tab, paths, tab.current().getQualifiedName(), qualifiedName); } else { assert n.getNode(0).hasName("PrimitiveType"); final Type jType = (Type) _jeannieAnalyzer.dispatch(n.getNode(0)); result = Utilities.javaTypeToCType(tab, runtime, n, jType, true); } _jeannieAnalyzer.exitJava(); type = JavaAnalyzer.setType(n, result); } } /** * Descends into a Declarator to find a FunctionDeclarator with a throws clause, if any:<br> * FunctionDeclarator = (ParameterTypeList / IdentifierList) JavaThrows. */ protected static GNode getDeclaredExceptions(final GNode declarator) { @SuppressWarnings("unused") final Visitor v = new Visitor() { public final Object visit(final GNode n) { return null; } public final Object visitArrayDeclarator(final GNode n) { return dispatch(n.getGeneric(0)); } public final Object visitAttributedDeclarator(final GNode n) { return dispatch(n.getGeneric(1)); } public final Object visitFunctionDeclarator(final GNode n) { final GNode result = n.getGeneric(2); //TD 05 visitFunctionDeclarator throws clause is at index 1! assert null == result || result.hasName("JavaThrows"); return result; } public final Object visitPointerDeclarator(final GNode n) { return dispatch(n.getGeneric(1)); } }; return GNode.cast(v.dispatch(declarator)); } final Analyzer _jeannieAnalyzer; boolean _localsAreLifted; final List<Type> _openArrays; JeannieCAnalyzer(final Analyzer jeannieAnalyzer) { super(new JeannieC(), null == jeannieAnalyzer ? Utilities.newRuntime() : jeannieAnalyzer._runtime); _jeannieAnalyzer = jeannieAnalyzer; _openArrays = new ArrayList<Type>(); this.table = null == jeannieAnalyzer ? null : jeannieAnalyzer._table; isTopLevel = true; _localsAreLifted = false; loops.clear(); switches.clear(); } /** Use this for asserting that the input is typed correctly. */ protected boolean assrt(final GNode n, final boolean cond, final String msgFormat, final Object... msgArgs) { return JavaEntities.runtimeAssrt(runtime, n, cond, msgFormat, msgArgs); } final void assrtOpenArray(final GNode n, final Type t) { if (t.hasShape()) { final Reference tRef = t.getShape(); for (int i = _openArrays.size() - 1; i >= 0; i--) { final Type o = _openArrays.get(i); if (o.hasShape()) { final Reference oRef = o.getShape(); if (tRef == oRef) { if (i != _openArrays.size() - 1) runtime.error("_with statement for '" + n.getGeneric(0).getString(0) + "' not immediately enclosing", n); return; } } } } runtime.error("no enclosing _with statement for '" + n.getGeneric(0).getString(0) + "'", n); } final void contextRestore(final JeannieCContext other) { hasScope = other._hasScope; isTopLevel = other._isTopLevel; _localsAreLifted = other._localsAreLifted; assert _openArrays.size() >= other._openArrays.size(); while (_openArrays.size() > other._openArrays.size()) _openArrays.remove(loops.size() - 1); for (int i=0; i<_openArrays.size(); i++) assert _openArrays.get(i) == other._openArrays.get(i); assert loops.size() >= other._loops; while (loops.size() > other._loops) loops.remove(loops.size() - 1); for (int i=0; i<other._loops; i++) assert loops.get(i).booleanValue(); assert switches.size() >= other._switches.size(); while (switches.size() > other._switches.size()) switches.remove(switches.size() - 1); for (int i=0; i<switches.size(); i++) assert switches.get(i).booleanValue() == other._switches.get(i); } final JeannieCContext contextSave() { final JeannieCContext result = new JeannieCContext(); result._hasScope = hasScope; result._isTopLevel = isTopLevel; result._localsAreLifted = _localsAreLifted; result._loops = loops.size(); result._openArrays = new ArrayList<Type>(_openArrays.size()); for (final Type t : _openArrays) result._openArrays.add(t); result._switches = new ArrayList<Boolean>(switches.size()); for (final Boolean b : switches) result._switches.add(b); return result; } final boolean getHasScope() { return hasScope; } final List<Boolean> getLoops() { return loops; } final Runtime getRuntime() { return runtime; } final List<Boolean> getSwitches() { return switches; } final SymbolTable getTable() { return table; } public final Specifiers newSpecifiers(final GNode specifiers, final boolean refIsDecl) { return new JeannieCSpecifiers(specifiers, refIsDecl); } protected final Type processAssignment(final boolean init, final String op, final Node n, final Type lhsType, final Type rhsType) { final Type lr = Utilities.getJavaClassOrInterfaceT(lhsType); if (JavaEntities.isWrappedClassT(lr) || JavaEntities.isWrappedInterfaceT(lr)) { final Type rr = Utilities.cTypeToJavaType(table, runtime, n, rhsType); if (rr.isError()) return ErrorT.TYPE; final List<File> paths = JavaEntities.classpath(runtime); if (JavaTypeConverter.isAssignable(table, paths, lr, rr)) return lr; runtime.error("illegal C assignment to Java type '" + JavaEntities.javaTypeToString(table, lr) + "'", n); return ErrorT.TYPE; } Type rr = Utilities.getJavaClassOrInterfaceT(rhsType); if (JavaEntities.isWrappedClassT(rr) || JavaEntities.isWrappedInterfaceT(rr)) rr = Utilities.javaTypeToCType(table, runtime, n, rr, false); return super.processAssignment(init, op, n, lhsType, rr); } protected final void processParameters(final GNode n, final FunctionT function) { function.setExceptions(_jeannieAnalyzer._jAnalyzer._context._handledExceptions); super.processParameters(n, function); } final void setHasScope(final boolean x) { hasScope = x; } final void setIsTopLevel(final boolean x) { isTopLevel = x; } /** Visit a CancelStatement = JeannieC.PrimaryIdentifier. */ public final void visitCancelStatement(final GNode n) { final Type t = (Type) _jeannieAnalyzer.dispatch(n.getGeneric(0)); assrtOpenArray(n, t); } /** Visit a CInCBlock = LocalLabelDeclaration* JeannieC.DeclarationOrStatement* Annotations. */ public final Type visitCInCBlock(final GNode n) { return super.visitCompoundStatement(n); } /** Visit a CommitStatement = JeannieC.PrimaryIdentifier. */ public final void visitCommitStatement(final GNode n) { final Type t = (Type) _jeannieAnalyzer.dispatch(n.getGeneric(0)); assrtOpenArray(n, t); } static class MiniVisitor_liftableDeclaration extends Visitor { private boolean _hasCompoundInitializer = false; private boolean _hasVariableArray = false; public final Boolean visit(final Node n) { for (final Object o : n) if (o instanceof Node) { dispatch((Node)o); if (_hasCompoundInitializer && _hasVariableArray) return false; } return true; } public final Boolean visitInitializerList(final GNode n) { _hasCompoundInitializer = true; if (_hasVariableArray) return false; return visit(n); } public final Boolean visitArrayDeclarator(final GNode n) { if (n.getGeneric(2).hasName("VariableLength")) _hasVariableArray = true; if (_hasCompoundInitializer) return false; return visit(n); } } /** Visit a Declaration = ["__extension__"] DeclarationSpecifiers [InitializedDeclaratorList]. */ public final void visitDeclaration(final GNode n) { super.visitDeclaration(n); if (_localsAreLifted) { final MiniVisitor_liftableDeclaration v = new MiniVisitor_liftableDeclaration(); final boolean liftable = ((Boolean)v.dispatch(n)).booleanValue(); assrt(n, liftable, "compound initializer with variable-sized array"); } } /** Visit a FunctionCall = (PrimaryIdentifier / JeannieC.PostfixExpression) JeannieC.ExpressionList. */ public final Type visitFunctionCall(final GNode n) { final GNode callee = n.getGeneric(0); if (callee.hasName("PrimaryIdentifier") && Analyzer.BUILTINS.contains(callee.getString(0))) return JavaAnalyzer.setType(n, _jeannieAnalyzer.processBuiltin(n)); final Type result = super.visitFunctionCall(n); FunctionT function = null; { final Type t1; if (callee.hasName("PrimaryIdentifier")) t1 = (Type) table.lookup(callee.getString(0)); else t1 = (Type) callee.getProperty(Constants.TYPE); if (null != t1) { final Type r1 = Utilities.c().pointerize(t1); if (!r1.isError() && r1.isPointer()) { final Type f1 = ((PointerT)r1).getType().resolve(); if (f1.isFunction()) function = (FunctionT)f1; } } } if (null != function) { final List<Type> exc = function.getExceptions(); if (null != exc) { final List<File> paths = JavaEntities.classpath(runtime); for (final Type tThrown : exc) if (JavaEntities.isCheckedException(table, paths, tThrown)) if (!_jeannieAnalyzer._jAnalyzer.isHandled(tThrown)) runtime.error("uncaught exception", n); } } return result; } /** Visit a FunctionDeclarator = (ParameterTypeList / IdentifierList) JavaThrows. * This method should never be called; look at visitFunctionDefinition(). */ public final void visitFunctionDeclarator(final GNode n) { assert false && null == n; } /** Visit a FunctionDefinition = ["__extension__"] [DeclarationSpecifiers] Declarator [DeclarationList] CompoundStatement.<br> */ public final void visitFunctionDefinition(final GNode n) { final GNode javaThrows = getDeclaredExceptions(n.getGeneric(2)); _jeannieAnalyzer.enterJava(); _jeannieAnalyzer.dispatch(javaThrows); _jeannieAnalyzer.enterC(); assert !_localsAreLifted; _localsAreLifted = Utilities.containsCToJavaTransition(n); super.visitFunctionDefinition(n); _localsAreLifted = false; final String functionName = SymbolTable.fromNameSpace(xtc.util.Utilities.unqualify((String) n.getProperty(Constants.SCOPE))); final FunctionT function = ((Type) table.current().lookupLocally(functionName)).resolve().toFunction(); _jeannieAnalyzer.exitC(); _jeannieAnalyzer.exitJava(); if (Utilities.containsCToJavaTransition(n)) { VariableT jniEnv = null; for (final Type param : function.getParameters()) if ("env".equals(param.toVariable().getName())) { jniEnv = param.toVariable(); break; } if (assrt(n, null != jniEnv, "C function %s contains Java snippet but lacks explicit formal JNIEnv* env", functionName)) assrt(n, jniEnv.getType().isPointer() && Utilities.hasTypedefName(jniEnv.getType().toPointer().getType(), "JNIEnv"), "expected formal env to be of type JNIEnv"); } JavaAnalyzer.setType(n, function); } /** Visit a JavaImports = ImportDeclaration*. */ public final void visitJavaImports(final GNode n) { _jeannieAnalyzer.enterJava(); _jeannieAnalyzer.dispatch(n.getGeneric(0)); _jeannieAnalyzer.exitJava(); } /** Visit a JavaInCBlock = JavaInJavaBlock. */ public final void visitJavaInCBlock(final GNode n) { _jeannieAnalyzer.enterJava(); _jeannieAnalyzer.dispatch(n.getGeneric(0)); _jeannieAnalyzer.exitJava(); } /** Visit a JavaInCExpression = JeannieJava.UnaryExpression. */ public final Type visitJavaInCExpression(final GNode n) { _jeannieAnalyzer.enterJava(); final Type jType = _jeannieAnalyzer.dispatchRValue(n.getGeneric(0)); _jeannieAnalyzer.exitJava(); final Type cType = Utilities.javaTypeToCType(table, runtime, n, jType, true); return JavaAnalyzer.setType(n, cType); } /** Visit a JavaInCStatement = TryStatement / SynchronizedStatement / ThrowStatement. */ public final void visitJavaInCStatement(final GNode n) { _jeannieAnalyzer.enterJava(); _jeannieAnalyzer.dispatch(n.getNode(0)); _jeannieAnalyzer.exitJava(); } /** Visit a JavaThrows = [ThrowsClause]. * This method should be called on JeannieJavaAnalyzer, not on JeannieCAnalyzer; look at visitFunctionDefinition(). */ public final void visitJavaThrows(final GNode n) { assert false && null == n; } /** Visit a JeannieC.PrimaryIdentifier = Identifier. */ public final Type visitPrimaryIdentifier(final GNode n) { final String name = n.getString(0); Type result = (Type)table.lookup(name); if (null == result) { runtime.error("'" + name + "' undeclared", n); result = ErrorT.TYPE; } else if (Utilities.isJavaEntity(result)) { runtime.error("cannot use Java entity '" + name + "' in C context", n); result = ErrorT.TYPE; } mark(n, result); return result; } /** Visit a JeannieC.ReturnStatement = [CommaExpression]. */ public final Type visitReturnStatement(final GNode n) { final Type functionOrMethod = Utilities.currentFunctionOrMethod(table); if (!functionOrMethod.isMethod()) return super.visitReturnStatement(n); final Type t1 = (Type) _jeannieAnalyzer.dispatch(n.getNode(0)); final Type result = functionOrMethod.toMethod().getResult(); if (result.isVoid()) { if (null != t1) runtime.error("'return' with a value, in method returning void", n); } else { if (null == t1) { runtime.error("'return' with no value, in method returning non-void", n); } else { final Type jResult = Utilities.cTypeToJavaType(table, runtime, n, t1); assrt(n, JavaTypeConverter.isAssignable(table, JavaEntities.classpath(runtime), result, jResult), "return type mismatch"); } } return JavaAnalyzer.setType(n, result); } /** Visit a TranslationUnit = [JavaImports] ExternalDeclaration* Annotations. */ public final void visitTranslationUnit(final GNode n) { super.visitTranslationUnit(n); } /** Visit a WithStatement = (JeannieC.Declaration / JeannieC.AssignmentExpression) CInCBlock. */ public final void visitWithStatement(final GNode n) { final boolean oldHasScope = hasScope; hasScope = false; table.enter(table.freshName("withStatement")); table.mark(n); final GNode initNode = n.getGeneric(0); final GNode cNode, jNode; final Type cType, jType; if (initNode.hasName("Declaration")) { final GNode initDtorList = initNode.getGeneric(2); if (assrt(n, null != initDtorList && 1 == initDtorList.size(), "expected exactly one initializer")) { final GNode dtor = initDtorList.getGeneric(0); cNode = dtor.getGeneric(1); jNode = dtor.getGeneric(4); dtor.set(4, null); _jeannieAnalyzer.dispatch(initNode); dtor.set(4, jNode); final String id = CAnalyzer.getDeclaredId(cNode).getString(0); cType = (Type) table.current().lookupLocally(id); if (assrt(jNode, null != jNode, "initializer expected")) jType = (Type) _jeannieAnalyzer.dispatch(jNode); else jType = ErrorT.TYPE; } else { cType = jType = ErrorT.TYPE; cNode = jNode = null; } } else { assert initNode.hasName("AssignmentExpression"); cNode = initNode.getGeneric(0); jNode = initNode.getGeneric(2); final GNode lhsNode = cNode; if (assrt(lhsNode, lhsNode.hasName("PrimaryIdentifier"), "primary identifier expected")) { final Type t = (Type) _jeannieAnalyzer.dispatch(lhsNode); cType = ensureLValue(lhsNode, t) ? t : ErrorT.TYPE; } else { cType = ErrorT.TYPE; } assrt(n, "=".equals(initNode.getString(1)), "expected operator '=', not %s", initNode.getString(1)); jType = (Type) _jeannieAnalyzer.dispatch(jNode); } mark(initNode, cType); _jeannieAnalyzer.assrtCorrectArrayConversion(cNode, cType, jNode, jType); final int nOpenArrays = _openArrays.size(); _openArrays.add(cType); _jeannieAnalyzer.dispatch(n.getNode(1)); assert nOpenArrays + 1 == _openArrays.size(); _openArrays.remove(nOpenArrays); table.exit(n); hasScope = oldHasScope; } } private static final class JeannieContext { final Visitor _analyzer; final Object _savedContext; JeannieContext(final Visitor analyzer, final Object savedContext) { _analyzer = analyzer; _savedContext = savedContext; } } private static final class JeannieJavaAnalyzer extends JavaAnalyzer { final Analyzer _jeannieAnalyzer; JeannieJavaAnalyzer(final Analyzer jeannieAnalyzer) { super(jeannieAnalyzer._runtime, jeannieAnalyzer._table); _jeannieAnalyzer = jeannieAnalyzer; } protected void assrtLegalMethodBody(final GNode n, final Type method) { final boolean isAbstract = JavaAnalyzer.hasModifier(method, "abstract"); assrt(n, isAbstract == (null == n.get(7)), "methods should be abstract iff they have no body"); if (null != n.get(7)) { final boolean isNative = JavaAnalyzer.hasModifier(method, "native"); assrt(n, isNative == n.getGeneric(7).hasName("CInJavaBlock"), "methods should be native iff their body is in C"); } final Type jniEnv = (Type) _table.root().lookup("JNIEnv"); if (null == jniEnv) { _runtime.error("C typedef for 'JNIEnv' missing; did you forget to #include <jni.h>?", n); } else if (null != _table.current().lookupLocally("env")) { _runtime.error("formal parameter declaration of 'env' conflicts with implicit JNIEnv pointer"); } else { final Type rval = new PointerT(jniEnv).attribute(Constants.ATT_CONSTANT); final VariableT param = VariableT.newParam(rval, "env"); final DynamicReference ref = new DynamicReference("env", rval); final Type lval = Utilities.c().qualify(rval, param).annotate().shape(ref); _table.current().define("env", lval); lval.scope(_table.current().getQualifiedName()); } } /** Visit a CDeclarations = ExternalDeclaration*. */ public final void visitCDeclarations(final GNode n) { _jeannieAnalyzer.enterC(); assert _table.current().isRoot(); for (int i = 0; i < n.size(); i++) _jeannieAnalyzer.dispatch(n.getNode(i)); _jeannieAnalyzer.exitC(); } /** Visit a CInJavaBlock = CInCBlock. */ public final void visitCInJavaBlock(final GNode n) { _jeannieAnalyzer.enterC(); for (int i = 0; i < n.size(); i++) _jeannieAnalyzer.dispatch(n.getNode(i)); _jeannieAnalyzer.exitC(); } /** Visit a CInJavaExpression = JeannieC.UnaryExpression. */ public final Type visitCInJavaExpression(final GNode n) { _jeannieAnalyzer.enterC(); final Type cType = (Type)_jeannieAnalyzer.dispatch(n.getNode(0)); _jeannieAnalyzer.exitC(); final Type result = Utilities.cTypeToJavaType(_table, _runtime, n, cType); return JavaAnalyzer.setType(n, result); } /** Visit a CompilationUnit = [PackageDeclaration] JavaImports CDeclarations JeannieJava.Declaration*. */ public final void visitCompilationUnit(final GNode n) { if (null == n.get(0)) visitPackageDeclaration(null); else dispatch(n.getNode(0)); _table.enter(JavaEntities.fileNameToScopeName(n.getLocation().file)); _table.mark(n); for (final Object o : n.getGeneric(1)) _externalAnalyzer.dispatch((Node)o); for (int i=3; i<n.size(); i++) _externalAnalyzer.dispatch(n.getNode(i)); dispatch(n.getNode(1)); //in package:file scope, for normal Java code ... final String fileScope = JavaEntities.enterScopeByQualifiedName(_table, ""); dispatch(n.getNode(1)); //... ALSO in top-level scope, for Java in top-level C code dispatch(n.getNode(2)); JavaEntities.enterScopeByQualifiedName(_table, fileScope); for (int i = 3; i < n.size(); i++) dispatch(n.getNode(i)); _table.setScope(_table.root()); } /** Visit a JavaImports = ImportDeclaration*. */ public final List<Type> visitJavaImports(final GNode n) { final List<Type> types = new ArrayList<Type>(n.size()); for (final Object o : n) types.add((Type)dispatch((Node)o)); return types; } /** Visit a JavaInJavaBlock = JeannieJava.DeclarationOrStatement*. */ public final Type visitJavaInJavaBlock(final GNode n) { return super.visitBlock(n); } /** Visit a JavaThrows = [ThrowsClause]. */ public final List<Type> visitJavaThrows(final GNode n) { if (null == n || null == n.get(0)) _context._handledExceptions = new ArrayList<Type>(); else _context._handledExceptions = JavaEntities.typeList((List)_externalAnalyzer.dispatch(n.getNode(0))); assrtLegalHandledExceptions(n.getGeneric(0)); return _context._handledExceptions; } /** Visit a MethodDeclaration = Modifiers null Type Identifier FormalParameters [Dimensions] [ThrowsClause] [Block]. */ public final Type visitMethodDeclaration(final GNode n) { assert !_jeannieAnalyzer._cAnalyzer._localsAreLifted; _jeannieAnalyzer._cAnalyzer._localsAreLifted = Utilities.containsJavaToCTransition(n); final Type result = super.visitMethodDeclaration(n); _jeannieAnalyzer._cAnalyzer._localsAreLifted = false; return result; } /** Visit a JeannieJava.PrimaryIdentifier = Identifier. */ public final Type visitPrimaryIdentifier(final GNode n) { final String name = n.getString(0); final Type result = (Type)_table.lookup(name); if (null != result && !result.hasError() && !Utilities.isJavaEntity(result)) { _runtime.error("cannot use C entity '" + name + "' in Java context", n); return JavaAnalyzer.setType(n, ErrorT.TYPE); } return super.visitPrimaryIdentifier(n); } /** * Visit a ReturnStatement = [Expression] (gosling_et_al_2000 <a * href="http://java.sun.com/docs/books/jls/second_edition/html/statements.doc.html#6767">§14.16</a>). */ public final void visitReturnStatement(final GNode n) { final Type function = Utilities.currentFunctionOrMethod(_table); if (function.isMethod()) { super.visitReturnStatement(n); return; } Type t1 = null; if (null != n.get(0)) { final Type jType = _jeannieAnalyzer.dispatchRValue(n.getGeneric(0)); t1 = Utilities.javaTypeToCType(_table, _runtime, n, jType, false); } final Type result = Utilities.returnType(function); if (result.resolve().isVoid()) { if (null != t1) _runtime.error("'return' with a value, in function returning void", n); } else { if (null != t1) _jeannieAnalyzer._cAnalyzer.processAssignment(false, "return", n, result, t1); else _runtime.error("'return' with no value, in function returning non-void", n); } } } static final Set<String> BUILTINS = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(new String[] { "_copyFromJava", "_copyToJava", "_newJavaString", "_stringUTFLength" }))); final JeannieCAnalyzer _cAnalyzer; final JeannieJavaAnalyzer _jAnalyzer; final Runtime _runtime; private final Stack<JeannieContext> _stack; public final SymbolTable _table; public Analyzer(final Runtime runtime, final SymbolTable table, final String language) { _stack = new Stack<JeannieContext>(); _table = table; _runtime = runtime; _cAnalyzer = new JeannieCAnalyzer(this); _jAnalyzer = new JeannieJavaAnalyzer(this); if ("C".equals(language)) enterC(); if ("Java".equals(language)) enterJava(); } private Visitor activeAnalyzer() { return _stack.isEmpty() ? null : _stack.peek()._analyzer; } final Type dispatchRValue(final GNode n) { final JeannieJavaAnalyzer a = (JeannieJavaAnalyzer) activeAnalyzer(); return a.dispatchRValue(n); } final void enterC() { assert _cAnalyzer != activeAnalyzer(); _stack.push(new JeannieContext(_cAnalyzer, _cAnalyzer.contextSave())); _cAnalyzer.setHasScope(_jAnalyzer._context._hasScope); _cAnalyzer.setIsTopLevel(JavaEntities.isScopeTopLevel(_table.current().getQualifiedName())); if (_jAnalyzer._context._loop) _cAnalyzer.getLoops().add(Boolean.TRUE); if (_jAnalyzer._context._switch) _cAnalyzer.getSwitches().add(Boolean.TRUE); } final void enterJava() { assert _jAnalyzer != activeAnalyzer(); _stack.push(new JeannieContext(_jAnalyzer, _jAnalyzer._context.save())); _jAnalyzer._context._hasScope = _cAnalyzer.getHasScope(); _jAnalyzer._context._loop = !_cAnalyzer.getLoops().isEmpty(); _jAnalyzer._context._switch = !_cAnalyzer.getSwitches().isEmpty(); } final void exitC() { assert _cAnalyzer == activeAnalyzer(); _cAnalyzer.contextRestore(savedContextC()); _stack.pop(); } final void exitJava() { assert _jAnalyzer == activeAnalyzer(); _jAnalyzer._context.restore(savedContextJ()); _stack.pop(); } private Type processBuiltin(final GNode n) { final String name = n.getGeneric(0).getString(0); final GNode argsNode = n.getGeneric(1); @SuppressWarnings("unchecked") final List<Type> argTypes = (List<Type>) dispatch(argsNode); for (final Type t : argTypes) if (t.isError()) return JavaAnalyzer.setType(n, ErrorT.TYPE); if ("_copyFromJava".equals(name)) { if (!_cAnalyzer.assrt(n, 5 == argsNode.size(), "invalid call to builtin function: `int _copyFromJava(cArray, cArrayStart, javaArray, javaArrayStart, length)")) return ErrorT.TYPE; _cAnalyzer.ensureInteger(argsNode.getNode(1), argTypes.get(1)); _cAnalyzer.ensureInteger(argsNode.getNode(3), argTypes.get(3)); _cAnalyzer.ensureInteger(argsNode.getNode(4), argTypes.get(4)); assrtCorrectArrayConversion(argsNode.getGeneric(0), argTypes.get(0), argsNode.getGeneric(2), argTypes.get(2)); return Utilities.javaTypeToCType(_table, _runtime, n, JavaEntities.nameToBaseType("int"), false); } else if ("_copyToJava".equals(name)) { if (!_cAnalyzer.assrt(n, 5 == argTypes.size(), "invalid call to builtin function: `int _copyToJava(javaArray, javaArrayStart, cArray, cArrayStart, length)")) return ErrorT.TYPE; _cAnalyzer.ensureInteger(argsNode.getNode(1), argTypes.get(1)); _cAnalyzer.ensureInteger(argsNode.getNode(3), argTypes.get(3)); _cAnalyzer.ensureInteger(argsNode.getNode(4), argTypes.get(4)); if (!_cAnalyzer.assrt(argsNode.getGeneric(0), !Utilities.hasTypedefName(argTypes.get(0), "jstring"), "_copyToJava target must not be String")) return ErrorT.TYPE; assrtCorrectArrayConversion(argsNode.getGeneric(2), argTypes.get(2), argsNode.getGeneric(0), argTypes.get(0)); return Utilities.javaTypeToCType(_table, _runtime, n, JavaEntities.nameToBaseType("int"), false); } else if ("_newJavaString".equals(name)) { if (!_cAnalyzer.assrt(argsNode, 1 == argsNode.size(), "invalid call to builtin function: `String _newJavaString(cArray)")) return JavaAnalyzer.setType(n, ErrorT.TYPE); final Type t = argTypes.get(0); final boolean typeOk = Utilities.isPtrChar(t) || Utilities.isPtrTypedef(t, "`char") || Utilities.isPtrTypedef(t, "jbyte"); if (!_cAnalyzer.assrt(argsNode.getGeneric(0), typeOk, "expected pointer to char, `byte, or `char")) return JavaAnalyzer.setType(n, ErrorT.TYPE); return Utilities.pureCType(_table, _runtime, JavaEntities.tString(_table)); } else if ("_stringUTFLength".equals(name)) { if (!_cAnalyzer.assrt(argsNode, 1 == argsNode.size() || 3 == argsNode.size(), "invalid call to builtin function: `int _stringUTFLength(jstring [, jStart, jLen])")) return JavaAnalyzer.setType(n, ErrorT.TYPE); final Type t = Utilities.pureCType(_table, _runtime, argTypes.get(0)); if (!_cAnalyzer.assrt(argsNode.getGeneric(0), Utilities.hasTypedefName(t, "jstring"), "expected `String")) return JavaAnalyzer.setType(n, ErrorT.TYPE); if (3 == argsNode.size()) { _cAnalyzer.ensureInteger(argsNode.getNode(1), argTypes.get(1)); _cAnalyzer.ensureInteger(argsNode.getNode(2), argTypes.get(2)); } return Utilities.javaTypeToCType(_table, _runtime, n, JavaEntities.nameToBaseType("int"), false); } else { throw new Error("builtin " + name + " not yet implemented"); } } private JeannieCAnalyzer.JeannieCContext savedContextC() { return (JeannieCAnalyzer.JeannieCContext)_stack.peek()._savedContext; } private JavaAnalyzer.JavaContext savedContextJ() { return (JavaAnalyzer.JavaContext)_stack.peek()._savedContext; } public final Object visit(final Node n) { return activeAnalyzer().dispatch(n); } private void assrtCorrectArrayConversion(final GNode cNode, final Type cType, final GNode jNode, final Type cjType) { if (cType.isError() || cjType.isError()) return; final Type cResolved = Utilities.c().pointerize(cType); if (!_cAnalyzer.assrt(cNode, cResolved.isPointer(), "pointer or array type expected")) return; final Type cElem = ((PointerT)cResolved).getType(); assert !cElem.isError(); final Type jType = Utilities.cTypeToJavaType(_table, _runtime, jNode, cjType); assert !jType.isError() && JavaEntities.isGeneralRValueT(jType); if (jType.isArray()) { final Type jElem = JavaEntities.resolveToRawRValue(JavaEntities.arrayElementType(jType.toArray())); if (JavaEntities.isPrimitiveT(jElem)) { final String expectedName = "j" + JavaEntities.baseTypeToName(jElem); _cAnalyzer.assrt(cNode, Utilities.hasTypedefName(cElem, expectedName), "type '%s*' expected", expectedName); } else { final Type jcElem = Utilities.cTypeToJavaType(_table, _runtime, cNode, cElem); final boolean ok = JavaTypeConverter.isAssignable(_table, JavaEntities.classpath(_runtime), jcElem, jElem); _cAnalyzer.assrt(cNode, ok, "type 'jobject*' expected"); } } else if (JavaTypeConverter.isIdentical(jType, JavaEntities.tString(_table))) { final boolean isChar = Utilities.hasTypedefName(cElem, "jchar"); _cAnalyzer.assrt(cNode, isChar || Utilities.hasTypedefName(cElem, "jbyte"), "type 'jchar*' or 'jbyte*' expected"); } else { _cAnalyzer.assrt(jNode, false, "string or primitive array expected"); } } /** Visit a CompilationUnit = CDeclarations [PackageDeclaration] ImportDeclaration* JeannieJava.Declaration*. */ public final void visitCompilationUnit(final GNode n) { assert null == activeAnalyzer(); enterJava(); activeAnalyzer().dispatch(n); exitJava(); } /** Visit a TranslationUnit = [JavaImports] ExternalDeclaration* Annotations. */ public final void visitTranslationUnit(final GNode n) { assert null == activeAnalyzer(); enterC(); activeAnalyzer().dispatch(n); exitC(); } //TD 41 jeannie.Analyzer should disallow break, continue, and goto from crossing language boundary or _with boundary }