Return-Path: Delivered-To: apmail-incubator-harmony-commits-archive@www.apache.org Received: (qmail 92194 invoked from network); 5 Oct 2005 02:21:58 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 5 Oct 2005 02:21:58 -0000 Received: (qmail 83515 invoked by uid 500); 5 Oct 2005 02:21:47 -0000 Delivered-To: apmail-incubator-harmony-commits-archive@incubator.apache.org Received: (qmail 83287 invoked by uid 500); 5 Oct 2005 02:21:45 -0000 Mailing-List: contact harmony-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: harmony-dev@incubator.apache.org Delivered-To: mailing list harmony-commits@incubator.apache.org Received: (qmail 82678 invoked by uid 99); 5 Oct 2005 02:21:24 -0000 X-ASF-Spam-Status: No, hits=-9.8 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [209.237.227.194] (HELO minotaur.apache.org) (209.237.227.194) by apache.org (qpsmtpd/0.29) with SMTP; Tue, 04 Oct 2005 19:21:08 -0700 Received: (qmail 91402 invoked by uid 65534); 5 Oct 2005 02:20:47 -0000 Message-ID: <20051005022047.91401.qmail@minotaur.apache.org> Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r294974 [6/25] - in /incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm: ./ jchevm/ jchevm/doc/ jchevm/etc/ jchevm/include/ jchevm/java/ jchevm/java/org/ jchevm/java/org/dellroad/ jchevm/java/org/dellroad/jc/ jchevm/java/org/dellroad/... Date: Wed, 05 Oct 2005 02:20:10 -0000 To: harmony-commits@incubator.apache.org From: geirm@apache.org X-Mailer: svnmailer-1.0.5 X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/java/org/dellroad/jc/cgen/CExpr.java URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/java/org/dellroad/jc/cgen/CExpr.java?rev=294974&view=auto ============================================================================== --- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/java/org/dellroad/jc/cgen/CExpr.java (added) +++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/java/org/dellroad/jc/cgen/CExpr.java Tue Oct 4 19:19:16 2005 @@ -0,0 +1,454 @@ + +// +// Copyright 2005 The Apache Software Foundation or its licensors, +// as applicable. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// $Id: CExpr.java,v 1.4 2005/01/25 23:12:09 archiecobbs Exp $ +// + +package org.dellroad.jc.cgen; + +import soot.*; +import soot.jimple.*; +import java.util.*; + +/** + * Class for assembling C language expressions and outputting them + * as C code. + * + *

+ * The purpose of this class is to allow us to output arbitrarily + * complex expressions in C that contain sufficient, but not + * stupidly unnecessary, parentheses. Note that we do output + * unnecessary parentheses in those cases where it's deemed + * to make things clearer. + *

+ */ +public class CExpr { + + /* Variables and constants */ + public static final int ATOMIC = 0; + + /* Unary expressions */ + public static final int DEREFERENCE = 1; + public static final int ADDRESS_OF = 2; + public static final int LOGICAL_NOT = 3; + public static final int COMPLEMENT = 4; + public static final int NEGATE = 5; + + /* Binary expressions */ + public static final int ADD = 6; + public static final int SUBTRACT = 7; + public static final int LOGICAL_AND = 8; + public static final int LOGICAL_OR = 9; + public static final int AND = 10; + public static final int OR = 11; + public static final int XOR = 12; + public static final int DIVIDE = 13; + public static final int MULTIPLY = 14; + public static final int MODULO = 15; + public static final int SHIFT_LEFT = 16; + public static final int SHIFT_RIGHT = 17; + public static final int ARROW = 18; + public static final int ARRAYINDEX = 19; + public static final int LT = 20; + public static final int LE = 21; + public static final int GT = 22; + public static final int GE = 23; + public static final int EQUAL = 24; + public static final int NOT_EQUAL = 25; + public static final int CAST = 26; + + /* >= 2 operand expressions */ + public static final int FUNCTION = 27; + public static final int THREEWAY = 28; + + public static final int NUM_TYPES = 29; + + private final int type; + private final CExpr ops[]; + private String symbol; + private String s; + + private static int[] prec; + private static int[] order; + + static { + + // Partially order operators as to how we want or need to + // require parentheses around operators. Note that we + // require them in cases not strictly required for C, + // e.g., by grouping together LOGICAL_AND and LOGICAL_OR. + + ArrayList a = new ArrayList(); + a.add(new int[] { ATOMIC }); + a.add(new int[] { CAST, ARROW, ARRAYINDEX, FUNCTION }); + a.add(new int[] { DEREFERENCE, + NEGATE, ADDRESS_OF, LOGICAL_NOT, COMPLEMENT }); + a.add(new int[] { MULTIPLY, DIVIDE, MODULO, + ADD, SUBTRACT, SHIFT_LEFT, SHIFT_RIGHT }); + a.add(new int[] { LT, LE, GT, GE, EQUAL, NOT_EQUAL }); + a.add(new int[] { AND, OR, XOR }); + a.add(new int[] { LOGICAL_AND, LOGICAL_OR }); + a.add(new int[] { THREEWAY }); + int[][] revp = (int [][])a.toArray(new int[a.size()][]); + + // Compute precedence mapping based on partial order + prec = new int[NUM_TYPES]; + order = new int[NUM_TYPES]; + for (int i = 0; i < revp.length; i++) { + for (int j = 0; j < revp[i].length; j++) { + int op = revp[i][j]; + order[op] = revp[i][0]; + prec[op] = i; + } + } + } + + /** + * Create an "atomic" C expression containing the given string. + */ + public CExpr(String s) { + this.type = ATOMIC; + this.ops = null; + this.s = s; + } + + /** + * Create a function invocation or unary C expression. + */ + public CExpr(int type, CExpr op1) { + this(type, new CExpr[] { op1 }); + } + + /** + * Create a function invocation or binary C expression. + */ + public CExpr(int type, CExpr op1, CExpr op2) { + this(type, new CExpr[] { op1, op2 }); + } + + /** + * Create a function invocation or ternary C expression. + */ + public CExpr(int type, CExpr op1, CExpr op2, CExpr op3) { + this(type, new CExpr[] { op1, op2, op3 }); + } + + /** + * Create a function invocation or unary C expression. + */ + public CExpr(int type, Object op1) { + this(type, new Object[] { op1 }); + } + + /** + * Create a function invocation or binary C expression. + */ + public CExpr(int type, Object op1, Object op2) { + this(type, new Object[] { op1, op2 }); + } + + /** + * Create a function invocation or ternary C expression. + */ + public CExpr(int type, Object op1, Object op2, Object op3) { + this(type, new Object[] { op1, op2, op3 }); + } + + /** + * Create a function invocation. + */ + public CExpr(int type, Object op1, Object op2, Object op3, Object op4) { + this(type, new Object[] { op1, op2, op3, op4 }); + } + + public CExpr(int type, Collection ops) { + this(type, ops.toArray()); + } + + public CExpr(int type, Object[] ops) { + + // Check arguments + if (type < 0 || type >= NUM_TYPES) + throw new IllegalArgumentException("bogus type"); + if (isUnary(type)) { + if (ops.length != 1) + throw new IllegalArgumentException("unary"); + } else if (isBinary(type)) { + if (ops.length != 2) + throw new IllegalArgumentException("binary"); + } else if (isTernary(type)) { + if (ops.length != 3) + throw new IllegalArgumentException("ternary"); + } else { + if (ops.length < 1) + throw new IllegalArgumentException("function"); + } + this.type = type; + + // Convert any non-CExpr arguments to ATOMIC's + if (ops instanceof CExpr[]) + this.ops = (CExpr[])ops; + else { + this.ops = new CExpr[ops.length]; + for (int i = 0; i < ops.length; i++) { + if (ops[i] instanceof CExpr) { + this.ops[i] = (CExpr)ops[i]; + continue; + } + if (ops[i] instanceof ValueBox) { + this.ops[i] = C.value((ValueBox)ops[i]); + continue; + } + if (ops[i] instanceof Type) { + this.ops[i] = new CExpr(ADDRESS_OF, + C.jc_type((Type)ops[i])); + continue; + } + if (ops[i] instanceof SootClass) { + this.ops[i] = new CExpr( + C.name((SootClass)ops[i])); + continue; + } + if (ops[i] instanceof SootField) { + this.ops[i] = new CExpr( + C.name((SootField)ops[i])); + continue; + } + if (ops[i] instanceof SootMethod) { + this.ops[i] = new CExpr( + C.name((SootMethod)ops[i])); + continue; + } + if (ops[i] instanceof Value) { + throw new RuntimeException("Value" + + " should be ValueBox: " + ops[i]); + } + this.ops[i] = new CExpr(ops[i].toString()); + } + } + + // Get symbol + switch (type) { + case DEREFERENCE: symbol = "*"; break; + case ADDRESS_OF: symbol = "&"; break; + case LOGICAL_NOT: symbol = "!"; break; + case COMPLEMENT: symbol = "~"; break; + case NEGATE: symbol = "-"; break; + case ADD: symbol = "+"; break; + case SUBTRACT: symbol = "-"; break; + case LOGICAL_AND: symbol = "&&"; break; + case LOGICAL_OR: symbol = "||"; break; + case AND: symbol = "&"; break; + case OR: symbol = "|"; break; + case XOR: symbol = "^"; break; + case DIVIDE: symbol = "/"; break; + case MULTIPLY: symbol = "*"; break; + case MODULO: symbol = "%"; break; + case SHIFT_LEFT: symbol = "<<"; break; + case SHIFT_RIGHT: symbol = ">>"; break; + case ARROW: symbol = "->"; break; + case ARRAYINDEX: symbol = "[]"; break; // never used + case LT: symbol = "<"; break; + case LE: symbol = "<="; break; + case GT: symbol = ">"; break; + case GE: symbol = ">="; break; + case EQUAL: symbol = "=="; break; + case NOT_EQUAL: symbol = "!="; break; + case THREEWAY: symbol = "?:"; break; // never used + case CAST: symbol = "()"; break; // never used + default: break; + } + } + + public static boolean isUnary(int type) { + return (type >= DEREFERENCE && type <= NEGATE); + } + + public static boolean isBinary(int type) { + return (type >= ADD && type <= CAST); + } + + public static boolean isTernary(int type) { + return (type == THREEWAY); + } + + private void gen() { + StringBuffer b = new StringBuffer(); + + // Handle special cases + switch (type) { + case FUNCTION: + addOp(b, 0); + b.append('('); + for (int i = 1; i < ops.length; i++) { + if (i > 1) + b.append(", "); + b.append(ops[i]); + } + b.append(')'); + break; + case ARRAYINDEX: + addOp(b, 0); + b.append('['); + b.append(ops[1]); + b.append(']'); + break; + case THREEWAY: + addOp(b, 0); + b.append(" ? "); + addOp(b, 1); + b.append(" : "); + addOp(b, 2); + break; + case CAST: + b.append('('); + addOp(b, 0); + b.append(')'); + addOp(b, 1); + break; + default: + if (isUnary(type)) { + b.append(symbol); + addOp(b, 0); + } else { + addOp(b, 0); + if (type != ARROW) + b.append(' '); + b.append(symbol); + if (type != ARROW) + b.append(' '); + addOp(b, 1); + } + break; + } + + // Done + s = b.toString(); + } + + private void addOp(StringBuffer b, int op) { + + // Determine whether to parenthesize + boolean parens = false; + switch (ops[op].type) { + case ATOMIC: + parens = false; + break; + case AND: + case OR: + case XOR: + parens = true; + break; + default: + if (prec[ops[op].type] >= prec[type]) + parens = true; + if (prec[ops[op].type] == prec[type]) { + if (isUnary(type)) + parens = false; + if ((type == FUNCTION || type == CAST + || type == ARRAYINDEX || type == ARROW) + && op == 0) + parens = false; + } + if ((type == AND || type == OR || type == XOR) + && !isUnary(ops[op].type)) + parens = true; + if (type == CAST && op == 0) + parens = false; + break; + } + if (type == THREEWAY && op > 0 && ops[op].type != ATOMIC) + parens = true; + if (parens) + b.append('('); + b.append(ops[op]); + if (parens) + b.append(')'); + } + + /** + * Return valid C code that expresses this C expression. + */ + public String toString() { + if (s == null) + gen(); + return s; + } + + private static int depth; + + /** + * Generate a random C expression. Used for testing. + */ + public static CExpr random(Random r) { + int type; + CExpr e; + + depth++; + if (depth >= 5) + type = ATOMIC; + else + type = r.nextInt(NUM_TYPES); + switch (type) { + case FUNCTION: + CExpr[] params = new CExpr[r.nextInt(4) + 1]; + for (int i = 0; i < params.length; i++) + params[i] = random(r); + e = new CExpr(type, params); + break; + case ATOMIC: + e = new CExpr("x"); + break; + default: + if (isUnary(type)) + e = new CExpr(type, new CExpr[] { random(r) }); + else if (isBinary(type)) { + e = new CExpr(type, + new CExpr[] { random(r), random(r) }); + } else if (isTernary(type)) { + e = new CExpr(type, new CExpr[] { random(r), + random(r), random(r) }); + } else { + Util.panic("impossible flow"); + e = null; + } + break; + } + depth--; + return e; + } + + public void dumpOps(int depth) { + for (int i = 0; i < ops.length; i++) { + for (int j = 0; j < depth; j++) + System.out.print(" "); + System.out.println(i + ": " + ops[i]); + if (ops[i].type != ATOMIC) + ops[i].dumpOps(depth + 1); + } + } + + /** + * Test method. + */ + public static void main(String[] args) { + CExpr e = random(new Random()); + e.dumpOps(0); + System.out.println(e); + } +} + Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/java/org/dellroad/jc/cgen/CFile.java URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/java/org/dellroad/jc/cgen/CFile.java?rev=294974&view=auto ============================================================================== --- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/java/org/dellroad/jc/cgen/CFile.java (added) +++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/java/org/dellroad/jc/cgen/CFile.java Tue Oct 4 19:19:16 2005 @@ -0,0 +1,959 @@ + +// +// Copyright 2005 The Apache Software Foundation or its licensors, +// as applicable. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// $Id: CFile.java,v 1.19 2005/03/13 00:18:50 archiecobbs Exp $ +// + +package org.dellroad.jc.cgen; + +import soot.*; +import soot.tagkit.*; +import soot.jimple.*; +import java.util.*; +import java.io.*; + +/** + * Represents a C source file being constructed from a Java class file. + */ +public class CFile extends SourceFile { + public static final String includeStringName = "_jc_include"; + HashSet[] iMethodBuckets; + int numNonemptyIMethodBuckets; + int numSingletonIMethodBuckets; + HashSet fullDepends; + HashSet baseDepends; + HashMap methodMap; // SootMethod -> CMethod + MethodOptimizer optimizer; + boolean includeLineNumbers; + JimpleBody body; + + public CFile(SootClass c, Writer out, + MethodOptimizer optimizer, boolean includeLineNumbers) { + super(c, out); + this.optimizer = optimizer; + this.includeLineNumbers = includeLineNumbers; + prepare(); + } + + /** + * Output the C source file. + */ + public void output() { + outputInitialStuff(); + if (!c.getFields().isEmpty()) { + outputBanner("Fields"); + outputFields(); + } + ArrayList list = new ArrayList(); + list.addAll(Arrays.asList(staticFields)); + list.addAll(Arrays.asList(virtualFields)); + SootField[] fields = (SootField[])list.toArray( + new SootField[list.size()]); + Arrays.sort(fields, Util.fieldComparator); + outputFieldList(fields, "Fields", "fields"); + if (staticMethods.length > 0) { + outputBanner("Static methods"); + outputMethods(staticMethods); + } + if (constructors.length > 0) { + outputBanner("Constructors"); + outputMethods(constructors); + } + if (virtualMethods.length > 0) { + outputBanner("Instance methods"); + outputMethods(virtualMethods); + } + outputBanner("Method list"); + list.clear(); + list.addAll(Arrays.asList(staticMethods)); + list.addAll(Arrays.asList(constructors)); + list.addAll(Arrays.asList(virtualMethods)); + SootMethod[] methods = (SootMethod[])list.toArray( + new SootMethod[list.size()]); + Arrays.sort(methods, Util.methodComparator); + outputMethodList(methods, "Methods", "methods"); + if (staticFields.length > 0) { + outputBanner("Class fields structure"); + outputStaticFieldStructure(); + } + if (c.getInterfaceCount() > 0) { + outputBanner("Interface list"); + outputInterfaceList(); + } + if (!c.isInterface() && numNonemptyIMethodBuckets > 0) { + outputBanner("Interface hash table"); + outputInterfaceHashTable(); + if (numSingletonIMethodBuckets > 0) + outputInterfaceQuickTable(); + } + outputBanner("Instanceof hash table"); + outputInstanceOfHashTable(); + outputBanner("Dependency list"); + outputDepedencyList(); + outputBanner("Class info"); + outputInnerClases(); + outputClassInfo(); + outputFinalStuff(); + out.close(); + } + + private void prepare() { + + // Do interface hash table + prepareInterfaceMethodHashTable(); + + // Create dependency set + fullDepends = new HashSet(); + + // Analyze and optimze all methods + methodMap = new HashMap(c.getMethods().size(), 2.0f); + for (Iterator i = c.getMethods().iterator(); i.hasNext(); ) { + SootMethod m = (SootMethod)i.next(); + CMethod cm = new CMethod(this, m, + optimizer, includeLineNumbers, fullDepends); + methodMap.put(m, cm); + } + + // Prepare dependencies + prepareDependencies(); + } + + /** + * Generate interface method hash table. + */ + private void prepareInterfaceMethodHashTable() { + + // Skip if class is not instantiable + if (c.isInterface() || c.isAbstract()) + return; + + // Generate set of all implemented interfaces + Set allInterfaces = Util.getAllInterfaces(c); + + // Create interface method hash table + iMethodBuckets = new HashSet[IMETHOD_HASHSIZE]; + + // Generate set of signatures of methods specified by any + // (possibly inherited) interface implemented by this class + HashSet isigs = new HashSet(); + for (Iterator i = allInterfaces.iterator(); i.hasNext(); ) { + SootClass sc = (SootClass)i.next(); + for (Iterator j = sc.getMethods().iterator(); + j.hasNext(); ) { + SootMethod m = (SootMethod)j.next(); + if (m.isStatic()) + continue; + isigs.add(m.getSubSignature()); + } + } + + // Add corresponding methods to the hash table buckets + for (Iterator i = isigs.iterator(); i.hasNext(); ) { + SootMethod m = Util.findMethod(c, (String)i.next()); + Util.require(!m.isAbstract()); + int bucket = Util.imethodHash(m); + if (iMethodBuckets[bucket] == null) { + iMethodBuckets[bucket] = new HashSet(); + numNonemptyIMethodBuckets++; + numSingletonIMethodBuckets++; + } else if (iMethodBuckets[bucket].size() == 1 + && !iMethodBuckets[bucket].contains(m)) + numSingletonIMethodBuckets--; + iMethodBuckets[bucket].add(m); + } + } + + /** + * Add dependencies on all classes that could, if changed, + * require this class's JC source file to be regenerated + * and/or recompiled. This includes headers where any structures + * we use are defined. + */ + protected void prepareDependencies() { + + // Add this class and java.lang.Class, both of which + // are implicit method parameter types + fullDepends.add(c); + fullDepends.add( + Scene.v().loadClassAndSupport("java.lang.Class")); + + // Add java.lang.String, because we can have String constants + fullDepends.add( + Scene.v().loadClassAndSupport("java.lang.String")); + + // Add all superclasses and superinterfaces + Util.addSupertypes(c, fullDepends); + + // Add dependencies for all fields + for (Iterator i = c.getFields().iterator(); i.hasNext(); ) { + addDependency(fullDepends, + ((SootField)i.next()).getType()); + } + + // Add dependencies for all methods + for (Iterator i = c.getMethods().iterator(); i.hasNext(); ) + addDependencies(fullDepends, (SootMethod)i.next()); + + // Add dependencies for all inner classes and outer class + for (int i = 0; i < innerClasses.length; i++) + fullDepends.add(innerClasses[i].inner); + if (outerClass != null) + fullDepends.add(outerClass); + + // Remove all superclasses to get base dependency set + baseDepends = (HashSet)fullDepends.clone(); + for (Iterator i = fullDepends.iterator(); i.hasNext(); ) { + for (SootClass sc = (SootClass)i.next(); + sc.hasSuperclass(); ) { + sc = sc.getSuperclass(); + baseDepends.remove(sc); + } + } + } + + /** + * Add to set the {@link SootClass} associated with + * {@link Type} t, if any. + */ + protected void addDependency(Set set, Type t) { + if (t instanceof ArrayType) + t = ((ArrayType)t).baseType; + if (t instanceof RefType) + set.add(((RefType)t).getSootClass()); + } + + /** + * Add all SootClasses to set that are referenced by + * the method signature, throws clause, locals, traps, or body + * instructions. + */ + protected void addDependencies(final Set set, SootMethod m) { + + // Add return type + addDependency(set, m.getReturnType()); + + // Add parameter types + for (int i = 0; i < m.getParameterCount(); i++) + addDependency(set, m.getParameterType(i)); + + // Add thrown exception types + for (Iterator i = m.getExceptions().iterator(); i.hasNext(); ) + set.add((SootClass)i.next()); + + // Stop here if there's no body + if (m.isAbstract() || m.isNative()) + return; + + // Synchronized methods will get a new java.lang.Throwable trap + if (m.isSynchronized()) + set.add(Scene.v().getSootClass("java.lang.Throwable")); + + // Add locals' types + Body body = m.retrieveActiveBody(); + for (Iterator i = body.getLocals().iterator(); i.hasNext(); ) + addDependency(set, ((Local)i.next()).getType()); + + // Add traps + for (Iterator i = body.getTraps().iterator(); i.hasNext(); ) + set.add(((Trap)i.next()).getException()); + + // Add classes any of whose static fields or methods we use + // or to whom we refer directly (e.g., via instanceof). + for (Iterator i = body.getUseAndDefBoxes().iterator(); + i.hasNext(); ) { + ValueBox vb = (ValueBox)i.next(); + vb.getValue().apply(new AbstractJimpleValueSwitch() { + public void caseSpecialInvokeExpr( + SpecialInvokeExpr v) { + caseInvokeExpr(v); + } + public void caseStaticInvokeExpr( + StaticInvokeExpr v) { + caseInvokeExpr(v); + } + public void caseVirtualInvokeExpr( + VirtualInvokeExpr v) { + caseInvokeExpr(v); + } + private void caseInvokeExpr(InvokeExpr v) { + set.add( + v.getMethod().getDeclaringClass()); + } + public void caseStaticFieldRef(StaticFieldRef v) { + set.add( + v.getField().getDeclaringClass()); + } + public void caseCastExpr(CastExpr v) { + addDependency(set, v.getCastType()); + } + public void caseInstanceOfExpr(InstanceOfExpr v) { + addDependency(set, v.getCheckType()); + } + public void caseNewArrayExpr(NewArrayExpr v) { + addDependency(set, v.getType()); + } + public void caseNewMultiArrayExpr( + NewMultiArrayExpr v) { + addDependency(set, v.getType()); + } + public void caseNewExpr(NewExpr v) { // needed? + addDependency(set, v.getBaseType()); + } + public void caseCaughtExceptionRef( + CaughtExceptionRef v) { + addDependency(set, v.getType()); + } + }); + } + } + + public void outputInitialStuff() { + + // Output initial comments and #include lines + // We #include headers for all classes we depend upon, + // but not including any superclasses, because those + // header files are included by base class header files. + // Our dependency list is the full list (probably overkill) + super.outputInitialStuff( + (SootClass[])baseDepends.toArray( + new SootClass[baseDepends.size()]), + (SootClass[])fullDepends.toArray( + new SootClass[fullDepends.size()]), + true); + +/*** + // Do any direct included string + Type stringType = RefType.v("java.lang.String"); + if (c.declaresField(includeStringName, stringType)) { + SootField f = c.getField(includeStringName, stringType); + if (f.isStatic() + && Util.isFinal(f) + && f.hasTag("ConstantValue")) { + byte[] value = f.getTag( + "ConstantValue").getValue(); + for (int i = 0; i < value.length; i++) { + char ch = (char)(value[i] & 0xff); + if (ch == '\n') + out.println(); + else + out.print(ch); + } + } + } +***/ + } + + public void outputFields() { + for (Iterator i = c.getFields().iterator(); i.hasNext(); ) + outputField((SootField)i.next()); + } + + public void outputField(SootField f) { + out.println("// " + C.string(f.getDeclaration(), false)); + ConstantValueTag initialValue = getInitialValue(f); + boolean isString = (f.getType() instanceof RefType) + && ((RefType)f.getType()).getSootClass().equals( + Scene.v().getSootClass("java.lang.String")); + if (initialValue != null && !isString) + outputInitialValue(f, initialValue); + out.println("static _jc_field " + + prefix + "$field_info$" + C.name(f) + " = {"); + out.indent(); + out.println(".name=\t\t" + C.string(f.getName()) + ","); + out.println(".signature=\t" + + C.string(Util.fieldDescriptor(f.getType())) + ","); + out.println(".class=\t\t&" + prefix + "$type,"); + out.println(".type=\t\t&" + C.jc_type(f.getType()) + ","); + out.println(".access_flags=\t" + C.accessDefs(f) + ","); + out.print(".offset=\t"); + if (f.isStatic()) { + out.println("_JC_OFFSETOF(" + + prefix + "$fields_struct, " + C.name(f) + "),"); + } else if (Util.isReference(f)) { /* { */ + out.println("_JC_OFFSETOF(" + + prefix + "$object, refs[-1]." + + cname + "." + C.name(f) + "),"); + } else { /* { */ + out.println("_JC_OFFSETOF(" + + prefix + "$object, nonrefs." + + cname + "." + C.name(f) + "),"); + } + if (initialValue != null) { + out.print(".initial_value=\t"); + if (isString) { + out.print(C.string( + ((StringConstantValueTag)initialValue) + .getStringValue())); + } else { + out.print("&" + prefix + + "$initial_value$" + C.name(f)); + } + out.println(","); + } + out.undent(); + out.println("};"); + out.println(); + } + + public ConstantValueTag getInitialValue(SootField f) { + + // Only static fields can have initial values + if (!f.isStatic()) + return null; + + // Look for ConstantValue tag + for (Iterator i = f.getTags().iterator(); i.hasNext(); ) { + Tag tag = (Tag)i.next(); + if (tag instanceof ConstantValueTag) + return (ConstantValueTag)tag; + } + return null; + } + + public void outputInitialValue(final SootField f, + final ConstantValueTag cvTag) { + TypeSwitch ts = new TypeSwitch() { + public void caseBooleanType(BooleanType t) { + IntegerConstantValueTag tag + = (IntegerConstantValueTag)cvTag; + out.println("static const jboolean " + prefix + + "$initial_value$" + C.name(f) + " = " + + tag.getIntValue() + ";"); + } + public void caseByteType(ByteType t) { + IntegerConstantValueTag tag + = (IntegerConstantValueTag)cvTag; + out.println("static const jbyte " + prefix + + "$initial_value$" + C.name(f) + " = " + + tag.getIntValue() + ";"); + } + public void caseCharType(CharType t) { + IntegerConstantValueTag tag + = (IntegerConstantValueTag)cvTag; + out.println("static const jchar " + prefix + + "$initial_value$" + C.name(f) + " = " + + tag.getIntValue() + ";"); + } + public void caseShortType(ShortType t) { + IntegerConstantValueTag tag + = (IntegerConstantValueTag)cvTag; + out.println("static const jshort " + prefix + + "$initial_value$" + C.name(f) + " = " + + tag.getIntValue() + ";"); + } + public void caseIntType(IntType t) { + IntegerConstantValueTag tag + = (IntegerConstantValueTag)cvTag; + out.println("static const jint " + prefix + + "$initial_value$" + C.name(f) + " = " + + tag.getIntValue() + ";"); + } + public void caseLongType(LongType t) { + LongConstantValueTag tag = (LongConstantValueTag)cvTag; + out.println("static const jlong " + prefix + + "$initial_value$" + C.name(f) + " = _JC_JLONG(" + + tag.getLongValue() + ");"); + } + public void caseFloatType(FloatType t) { + FloatConstantValueTag tag + = (FloatConstantValueTag)cvTag; + out.print("static const unsigned char " + prefix + + "$initial_value$" + C.name(f) + "[] = {"); + byte[] bytes = tag.getValue(); + for (int i = 0; i < 4; i++) { + if (i > 0) + out.print(","); + out.print(" 0x" + + Integer.toHexString(bytes[i] & 0xff)); + } + out.println(" };"); + } + public void caseDoubleType(DoubleType t) { + DoubleConstantValueTag tag + = (DoubleConstantValueTag)cvTag; + out.print("static const unsigned char " + prefix + + "$initial_value$" + C.name(f) + "[] = {"); + byte[] bytes = tag.getValue(); + for (int i = 0; i < 8; i++) { + if (i > 0) + out.print(","); + out.print(" 0x" + + Integer.toHexString(bytes[i] & 0xff)); + } + out.println(" };"); + } + public void caseRefType(RefType t) { // String + StringConstantValueTag tag + = (StringConstantValueTag)cvTag; + out.println("static const char *" + prefix + + "$initial_value$" + C.name(f) + " = " + + C.string(tag.getStringValue()) + ";"); + } + public void defaultCase(Type t) { + Util.panic("bogus type " + t); + } + }; + f.getType().apply(ts); + } + + public void outputStaticFieldStructure() { + out.println(prefix + "$fields_struct " + + prefix + "$class_fields = { };"); + out.println(); + } + + public void outputMethods(SootMethod[] list) { + for (int i = 0; i < list.length; i++) + outputMethod(list[i]); + } + + public void outputMethod(SootMethod m) { + out.println("//"); + out.println("// " + C.string(m.getDeclaration(), false)); + out.println("//"); + out.println(); + CMethod cm = (CMethod)methodMap.get(m); + cm.outputMethodInfo(); + cm.outputMethodFunction(); + } + + public void outputFieldList(SootField[] fields, + String comment, String label) { + if (fields.length == 0) + return; + outputCommentLine(comment); + out.println("static _jc_field *const " + + prefix + "$" + label + "[] = {"); + out.indent(); + for (int i = 0; i < fields.length; i++) { + out.print("&" + prefix + "$field_info$" + + C.name(fields[i])); + if (i < fields.length - 1) + out.print(','); + out.println(); + } + out.undent(); + out.println("};"); + out.println(); + } + + public void outputMethodList(SootMethod[] methods, + String comment, String label) { + if (methods.length == 0) + return; + outputCommentLine(comment); + out.println("static _jc_method *const " + + prefix + "$" + label + "[] = {"); + out.indent(); + for (int i = 0; i < methods.length; i++) { + out.print("&" + prefix + "$method_info$" + + C.name(methods[i])); + if (i < methods.length - 1) + out.print(','); + out.println(); + } + out.undent(); + out.println("};"); + out.println(); + } + + public void outputInterfaceList() { + + // List directly implemented interfaces + outputCommentLine("Directly implemented interfaces"); + out.println("static _jc_type *const " + + prefix + "$interfaces[] = {"); + out.indent(); + Collection ifaces = c.getInterfaces(); + SootClass[] list = (SootClass[])ifaces.toArray( + new SootClass[ifaces.size()]); + for (int i = 0; i < list.length; i++) { + out.print("&_jc_" + C.name(list[i]) + "$type"); + if (i < list.length - 1) + out.print(','); + out.println(); + } + out.undent(); + out.println("};"); + out.println(); + } + + public void outputInterfaceHashTable() { + + // Sanity check + out.println("#if _JC_IMETHOD_HASHSIZE != " + IMETHOD_HASHSIZE); + out.println("#error \"_JC_IMETHOD_HASHSIZE has the wrong" + + " value (should be " + IMETHOD_HASHSIZE + ")\""); + out.println("#endif"); + out.println(); + + outputCommentLine("Interface lookup hash table buckets"); + for (int i = 0; i < IMETHOD_HASHSIZE; i++) { + if (iMethodBuckets[i] == null) + continue; + out.println("static _jc_method *" + + prefix + "$imethod_hash_bucket_" + i + "[] = {"); + out.indent(); + SootMethod[] list = (SootMethod[])iMethodBuckets[i] + .toArray(new SootMethod[iMethodBuckets[i].size()]); + Arrays.sort(list, Util.methodComparator); + for (int j = 0; j < list.length; j++) { + SootClass sc = list[j].getDeclaringClass(); + out.println("&_jc_" + C.name(sc) + + "$method_info$" + C.name(list[j]) + ","); + } + out.println("_JC_NULL"); + out.undent(); + out.println("};"); + } + out.println(); + + // Output the interface hash table itself + outputCommentLine("Interface lookup hash table"); + out.println("static _jc_method **" + + prefix + "$imethod_hash_table[_JC_IMETHOD_HASHSIZE] = {"); + out.indent(); + int count = 0; + for (int i = 0; i < IMETHOD_HASHSIZE; i++) { + if (iMethodBuckets[i] == null) + continue; + out.print("[" + i + "]=\t" + + prefix + "$imethod_hash_bucket_" + i); + if (++count < numNonemptyIMethodBuckets) + out.print(','); + out.println(); + } + out.undent(); + out.println("};"); + out.println(); + } + + public void outputInterfaceQuickTable() { + + outputCommentLine("Interface \"quick\" lookup hash table"); + out.println("static const void *" + prefix + + "$imethod_quick_table[_JC_IMETHOD_HASHSIZE] = {"); + out.indent(); + int count = 0; + for (int i = 0; i < IMETHOD_HASHSIZE; i++) { + if (iMethodBuckets[i] == null + || iMethodBuckets[i].size() != 1) + continue; + SootMethod m = (SootMethod) + iMethodBuckets[i].iterator().next(); + SootClass sc = m.getDeclaringClass(); + out.print("[" + i + "]=\t&_jc_" + + C.name(sc) + "$method$" + C.name(m)); + if (++count < numSingletonIMethodBuckets) + out.print(','); + out.println(); + } + out.undent(); + out.println("};"); + out.println(); + } + + public void outputVtable() { + + // Sanity check + if (c.isInterface()) + throw new RuntimeException("assertion failure"); + + // Compute virtual method table (signature -> class mapping) + HashMap virt = new HashMap(); + for (int i = superclasses.size() - 1; i >= 0; i--) { + SootClass sc = (SootClass)superclasses.get(i); + for (Iterator j = sc.getMethods().iterator(); + j.hasNext(); ) { + SootMethod m = (SootMethod)j.next(); + if (Util.isVirtual(m)) + virt.put(m.getSubSignature(), sc); + } + } + + // Output virtual method dispatch table + for (int i = superclasses.size() - 1; i >= 0; i--) { + SootClass sc = (SootClass)superclasses.get(i); + out.println("." + C.name(sc) + "= {"); + SootMethod[] list = (SootMethod[])sc.getMethods() + .toArray(new SootMethod[sc.getMethods().size()]); + Arrays.sort(list, Util.methodComparator); + for (int j = 0; j < list.length; j++) { + SootMethod m = list[j]; + if (!Util.isVirtual(m)) + continue; + out.print(" ." + C.name(m) + "="); + SootClass mc = (SootClass)virt.get( + m.getSubSignature()); + if (!sc.equals(mc)) { + spaceFillTo(16 + + C.name(m).length() + 2, 64); + out.print("// overridden"); + } + out.println(); + out.indent(); + out.println("&_jc_" + C.name(mc) + + "$method$" + C.name(m) + ","); + out.undent(); + } + out.println("},"); + } + } + + public void outputInstanceOfHashTable() { + + // Sanity check + out.println("#if _JC_INSTANCEOF_HASHSIZE != " + + INSTANCEOF_HASHSIZE); + out.println("#error \"_JC_INSTANCEOF_HASHSIZE has the wrong" + + " value (should be " + INSTANCEOF_HASHSIZE + ")\""); + out.println("#endif"); + out.println(); + + // Create set of classes of which we are an instance + Set set = Util.getAllSupertypes(c); + + // Generate hash table + HashSet[] buckets = new HashSet[INSTANCEOF_HASHSIZE]; + for (Iterator i = set.iterator(); i.hasNext(); ) { + SootClass sc = (SootClass)i.next(); + int bucket = Util.instanceofHash(sc); + if (buckets[bucket] == null) + buckets[bucket] = new HashSet(); + buckets[bucket].add(sc); + } + + // Output hash table buckets + outputCommentLine("Instanceof hash table buckets"); + int numNonemptyBuckets = 0; + for (int i = 0; i < INSTANCEOF_HASHSIZE; i++) { + if (buckets[i] == null) + continue; + numNonemptyBuckets++; + out.println("static _jc_type *const " + + prefix + "$instanceof_hash_bucket_" + + i + "[] = {"); + out.indent(); + SootClass[] list = (SootClass[])buckets[i] + .toArray(new SootClass[buckets[i].size()]); + Arrays.sort(list, Util.classComparator); + for (int j = 0; j < list.length; j++) { + out.println("&_jc_" + + C.name(list[j]) + "$type" + ","); + } + out.println("_JC_NULL"); + out.undent(); + out.println("};"); + } + out.println(); + + // Output the hash table itself + outputCommentLine("Instanceof hash table"); + out.println("static _jc_type *const *const " + prefix + + "$instanceof_hash_table[_JC_INSTANCEOF_HASHSIZE] = {"); + out.indent(); + int count = 0; + for (int i = 0; i < INSTANCEOF_HASHSIZE; i++) { + if (buckets[i] == null) + continue; + out.print("[" + i + "]=\t" + + prefix + "$instanceof_hash_bucket_" + i); + if (++count < numNonemptyBuckets) + out.print(','); + out.println(); + } + out.undent(); + out.println("};"); + out.println(); + } + + public void outputInnerClases() { + if (innerClasses.length == 0) + return; + outputCommentLine("Inner class list"); + out.println("static _jc_inner_class " + + prefix + "$inner_classes[] = {"); + out.indent(); + for (int i = 0; i < innerClasses.length; i++) { + InnerClass inner = innerClasses[i]; + out.println("{ &_jc_" + C.name(inner.inner) + "$type, " + + C.accessDefs(inner.flags) + " },"); + } + out.undent(); + out.println("};"); + out.println(); + } + + public void outputDepedencyList() { + outputCommentLine("Class dependency info"); + out.println("static _jc_class_depend " + + prefix + "$class_depends[] = {"); + out.indent(); + SootClass[] list = (SootClass[])fullDepends.toArray( + new SootClass[fullDepends.size()]); + Arrays.sort(list, Util.classComparator); + for (int i = 0; i < list.length; i++) { + SootClass sc = list[i]; + if (sc.equals(c)) // don't depend on self + continue; + out.println("{ " + + C.string(sc.getName().replace('.', '/')) + + ",\t_JC_JLONG(0x" + + Long.toHexString(Util.classHash(sc)) + ") },"); + } + out.undent(); + out.println("};"); + out.println(); + } + + public void outputClassInfo() { + outputCommentLine("Class info"); + + // Output a vtype structure + out.println(prefix + "$vtype " + prefix + "$type = {"); + + // Output type sub-structure + out.println(" .type= {"); + out.indent(); + + // Output initial info + out.println(".name=\t\t\t" + + C.string(c.getName().replace('.', '/')) + ","); + out.println(".superclass=\t\t" + + (c.hasSuperclass() ? + ("&_jc_" + C.name(c.getSuperclass()) + "$type") : + "_JC_NULL") + + ","); + out.println(".access_flags=\t\t" + C.accessDefs(c) + ","); + out.println(".flags=\t\t\t_JC_TYPE_REFERENCE,"); + + // Output interface info + if (c.getInterfaceCount() > 0) { + out.println(".num_interfaces=sizeof(" + + prefix + "$interfaces) / sizeof(*" + + prefix + "$interfaces),"); + out.println(".interfaces=\t\t" + + prefix + "$interfaces,"); + } + out.print(".imethod_hash_table=\t"); + if (numNonemptyIMethodBuckets > 0) { + out.println(prefix + "$imethod_hash_table,"); + } else + out.println("_jc_empty_imethod_table,"); + out.print(".imethod_quick_table=\t"); + if (numSingletonIMethodBuckets > 0) + out.println(prefix + "$imethod_quick_table,"); + else + out.println("_jc_empty_quick_table,"); + + // Count total number of declared virtual methods + int totalVirtualMethods = 0; + for (Iterator i = superclasses.iterator(); i.hasNext(); ) { + SootClass sc = (SootClass)i.next(); + for (Iterator j = sc.getMethods().iterator(); + j.hasNext(); ) { + SootMethod m = (SootMethod)j.next(); + if (Util.isVirtual(m)) + totalVirtualMethods++; + } + } + + // Begin non-array info + out.println(".u= {"); + out.println(" .nonarray= {"); + out.indent(); + out.println(".block_size_index=\t_JC_LIBJC_VERSION,"); + out.println(".num_vmethods=\t\t" + totalVirtualMethods + ","); + out.println(".hash=\t\t\t" + "_JC_JLONG(0x" + + Long.toHexString(Util.classHash(c)) + "),"); + out.println(".source_file=\t\t" + C.string(sourceFile) + ","); + + // Output instance size and offset + if (!c.isInterface()) { + out.println(".instance_size=\t\tsizeof(" + + prefix + "$refs) + sizeof(" + + prefix + "$object),"); + } + + out.println(".num_virtual_refs=\t" + numVirtualRefFields + ","); + + // Output fields and methods + outputMemberList(staticFields.length + virtualFields.length, + "fields"); + outputMemberList(staticMethods.length + constructors.length + + virtualMethods.length, "methods"); + + // Output instanceof hash table + out.println(".instanceof_hash_table=\t" + + prefix + "$instanceof_hash_table,"); + + // Output class fields + if (staticFields.length > 0) { + out.println(".class_fields=\t\t" + + "&" + prefix + "$class_fields,"); + } + + // Output inner class info + if (innerClasses.length > 0) { + out.println(".num_inner_classes=\t" + + innerClasses.length + ","); + out.println(".inner_classes=\t\t" + + prefix + "$inner_classes,"); + } + if (outerClass != null) { + out.println(".outer_class=\t\t" + + "&_jc_" + C.name(outerClass) + "$type,"); + } + + // Output class dependency info + out.println(".num_class_depends=\t\tsizeof(" + + prefix + "$class_depends) / sizeof(*" + + prefix + "$class_depends),"); + out.println(".class_depends=\t\t" + prefix + "$class_depends,"); + + // End non-array info + out.undent(); + out.println(" }"); + out.println("}"); + + // End _jc_type sub-structure + out.undent(); + out.println(" }" + (!c.isInterface() ? "," : "")); + + // Output vtable for non-interface classes + if (!c.isInterface()) { + out.println(" .vtable= {"); + out.indent(); + outputVtable(); + out.undent(); + out.println(" }"); + } + + // End vtype + out.println("};"); + out.println(); + } + + private void outputMemberList(int length, String label) { + if (length == 0) + return; + out.println(".num_" + label + "=\t\tsizeof(" + prefix + "$" + + label + ") / sizeof(*" + prefix + "$" + label + "),"); + out.println("." + label + "=\t\t" + prefix + "$" + label + ","); + } +} + Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/java/org/dellroad/jc/cgen/CMethod.java URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/java/org/dellroad/jc/cgen/CMethod.java?rev=294974&view=auto ============================================================================== --- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/java/org/dellroad/jc/cgen/CMethod.java (added) +++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/java/org/dellroad/jc/cgen/CMethod.java Tue Oct 4 19:19:16 2005 @@ -0,0 +1,829 @@ + +// +// Copyright 2005 The Apache Software Foundation or its licensors, +// as applicable. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// $Id: CMethod.java,v 1.21 2005/05/14 21:58:24 archiecobbs Exp $ +// + +package org.dellroad.jc.cgen; + +import java.io.*; +import java.util.*; +import org.dellroad.jc.cgen.analysis.ActiveUseTagger; +import org.dellroad.jc.cgen.analysis.FollowsAnalysis; +import org.dellroad.jc.cgen.escape.StackAllocTag; +import soot.*; +import soot.baf.*; +import soot.jimple.*; +import soot.jimple.internal.*; +import soot.jimple.toolkits.annotation.tags.*; +import soot.tagkit.*; +import soot.toolkits.graph.CompleteUnitGraph; + +/** + * Implements JC's C code generation algorithm for implementing Java methods. + */ +public class CMethod { + + private static final ThreadLocal CURRENT_METHOD = new ThreadLocal(); + + private static final boolean DEBUG = false; + + final SootMethod m; + final CFile cfile; + final CodeWriter out; + StmtBody body; + final MethodOptimizer optimizer; + final boolean includeLineNumbers; + int lineNumberTableLength; + CompleteUnitGraph graph; + PatchingChain bodyChain; + Map infoMap; // Stmt -> StmtInfo + List traps; // Trap's + + public CMethod(CFile cfile, SootMethod m, MethodOptimizer optimizer, + boolean includeLineNumbers, Set deps) { + this.cfile = cfile; + this.out = cfile.out; + this.m = m; + this.optimizer = optimizer; + this.includeLineNumbers = includeLineNumbers; + processBody(deps); + } + + // Analysis information about a Stmt + static class StmtInfo { + char javaLineNumber = 0; // java source line # + boolean branchTarget = false; // target of branch/trap + boolean trapBoundary = false; // trap begin or end + boolean backwardTrapTarget = false; // back-branching trap + int lineNumberIndex = -1; // line # table index + short labelIndex = -1; // goto label index + short region = 0; // trap region + boolean needsRegionUpdate = false; // update new region + } + + // Provides access to the current method being output + public static CMethod current() { + return (CMethod)CURRENT_METHOD.get(); + } + + // Get the StmtInfo corresponding to 'unit'. Create one if needed. + StmtInfo getInfo(Unit unit) { + StmtInfo info; + if ((info = (StmtInfo)infoMap.get(unit)) == null) { + info = new StmtInfo(); + infoMap.put(unit, info); + } + return info; + } + + // Primp, tweak, and optimize the method body + private void processBody(Set deps) { + + // Skip if we're not going to generate a normal body + if (!m.isConcrete()) + return; + + // Wrap synchronized methods with monitorenter/exit statements + if (m.isSynchronized()) + wrapSynchronized(m.retrieveActiveBody()); + + // Debug + if (DEBUG) + dumpBody(m, "BEFORE OPTIMIZATION"); + + // Perform useful optimizations on method body + graph = optimizer.optimize(m, deps); + + // Debug + if (DEBUG) + dumpBody(m, "AFTER OPTIMIZATION"); + + // Get body and Unit chain + body = (JimpleBody)graph.getBody(); + bodyChain = body.getUnits(); + + // Copy tags from statements to value boxes + StmtTagCopierSwitch tagCopier = new StmtTagCopierSwitch(); + for (Iterator i = bodyChain.iterator(); i.hasNext(); ) { + Stmt stmt = (Stmt)i.next(); + stmt.apply(tagCopier); + } + + // Elide leading '$' from locals' names + for (Iterator i = body.getLocals().iterator(); i.hasNext(); ) { + Local local = (Local)i.next(); + String name = local.getName(); + if (name.charAt(0) == '$') + local.setName(name.substring(1)); + } + } + + // Analyze the body to derive various bits of information we need. + // This method is not allowed to change the method body + private void analyzeBody() { + + // Skip if we're not going to generate a normal body + if (!m.isConcrete()) + return; + + // Initialize Stmt info map + infoMap = new HashMap(); + + // Mark trap targets that represent possible backward branches + traps = new ArrayList(body.getTraps()); + for (int i = 0; i < traps.size(); i++) { + Trap trap = (Trap)traps.get(i); + Unit handler = trap.getHandlerUnit(); + if (bodyChain.follows(trap.getEndUnit(), handler)) + getInfo(handler).backwardTrapTarget = true; + } + + // Mark statements that change the Java source line number + if (includeLineNumbers) { + int lastLineNumber = -1; + for (Iterator i = bodyChain.iterator(); i.hasNext(); ) { + Unit unit = (Unit)i.next(); + LineNumberTag tag = (LineNumberTag)unit.getTag( + "LineNumberTag"); + if (tag == null) + continue; + int lineNumber = tag.getLineNumber(); + if (lineNumber == lastLineNumber) + continue; + getInfo(unit).javaLineNumber = (char)lineNumber; + lastLineNumber = lineNumber; + } + } + + // Mark branch targets as branch targets needing labels + for (Iterator i = bodyChain.iterator(); i.hasNext(); ) { + Unit unit = (Unit)i.next(); + for (Iterator j = unit.getUnitBoxes().iterator(); + j.hasNext(); ) { + UnitBox ub = (UnitBox)j.next(); + getInfo(ub.getUnit()).branchTarget = true; + } + } + + // Mark trap handlers as branch targets and region updates + for (Iterator i = traps.iterator(); i.hasNext(); ) { + Trap trap = (Trap)i.next(); + StmtInfo info = getInfo(trap.getHandlerUnit()); + info.branchTarget = true; + info.needsRegionUpdate = true; + } + + // Mark trap range boundaries + for (Iterator i = traps.iterator(); i.hasNext(); ) { + Trap trap = (Trap)i.next(); + getInfo(trap.getBeginUnit()).trapBoundary = true; + getInfo(trap.getEndUnit()).trapBoundary = true; + } + + // Assign trap regions to units + short region = 0; + for (Iterator i = bodyChain.iterator(); i.hasNext(); ) { + StmtInfo info = getInfo((Unit)i.next()); + if (info.trapBoundary) + region++; + info.region = region; + } + + // Annotate all Units into which control can flow + // such that a new trap region is entered. + boolean first = true; + for (Iterator i = bodyChain.iterator(); i.hasNext(); ) { + Unit unit = (Unit)i.next(); + + // Get this Unit's region + StmtInfo info = getInfo(unit); + region = info.region; + + // Special case first unit + if (first) { + if (region != 0) + getInfo(unit).needsRegionUpdate = true; + first = false; + } + + // Annotate any subsequent statements in other regions + if (unit.fallsThrough()) { + info = getInfo((Unit)bodyChain.getSuccOf(unit)); + if (info.region != region) + info.needsRegionUpdate = true; + } + for (Iterator j = unit.getUnitBoxes().iterator(); + j.hasNext(); ) { + info = getInfo(((UnitBox)j.next()).getUnit()); + if (info.region != region) + info.needsRegionUpdate = true; + } + } + + // Scan StmtInfo's and mark for line numbers and branch labels + StmtInfo previous = new StmtInfo(); + lineNumberTableLength = 0; + short nextLabelIndex = 0; + for (Iterator i = bodyChain.iterator(); i.hasNext(); ) { + Unit unit = (Unit)i.next(); + StmtInfo info = getInfo(unit); + + // Inherit Java line number from previous unit + if (info.javaLineNumber == 0) + info.javaLineNumber = previous.javaLineNumber; + + // Line number table entry if Java line number changes + if (info.javaLineNumber != previous.javaLineNumber) + info.lineNumberIndex = lineNumberTableLength++; + + // We need a label if any branch or trap targets us + if (info.branchTarget) + info.labelIndex = nextLabelIndex++; + + // Update for next go round + previous = info; + } + + // Tag static method and field refs for active use + new ActiveUseTagger(graph); + } + + /** + * Make a synchronized method look as if it was contained inside + * a big synchronized block. This relieves the VM itself from having + * to explicitly do the synchronization operations. Note that this + * is only done for non-native methods. The VM is still responsible + * for handling synchronization for native methods. + */ + public static void wrapSynchronized(Body body) { + + // Check for applicability + SootMethod method = body.getMethod(); + if (body.getUnits().size() == 0 || !method.isSynchronized()) + return; + SootClass c = method.getDeclaringClass(); + + // Remember original first statment + PatchingChain units = body.getUnits(); + Unit firstStmt = (Unit)units.getFirst(); + + // Lock monitor at the start of the method + Local thisRef = null; + StringConstant classConstant = null; + if (!method.isStatic()) { + thisRef = Jimple.v().newLocal("thisRef", RefType.v(c)); + body.getLocals().addLast(thisRef); + units.addFirst(Jimple.v().newEnterMonitorStmt( + (Value)thisRef)); + units.addFirst(Jimple.v().newIdentityStmt(thisRef, + Jimple.v().newThisRef(RefType.v(c)))); + } else { + classConstant = ClassConstant.v( + method.getDeclaringClass().getName()); + units.addFirst(Jimple.v().newEnterMonitorStmt( + classConstant)); + } + + // Unlock monitor before each return statement. We don't + // need to unlock before throw statements because the trap + // handler we add below will automatically handle those cases. + HashSet returns = new HashSet(); + for (Iterator i = units.iterator(); i.hasNext(); ) { + Stmt stmt = (Stmt)i.next(); + if (stmt instanceof ReturnStmt + || stmt instanceof ReturnVoidStmt) + returns.add(stmt); + } + for (Iterator i = returns.iterator(); i.hasNext(); ) { + units.insertBefore(Jimple.v().newExitMonitorStmt( + method.isStatic() ? + (Value)classConstant : (Value)thisRef), + i.next()); + } + + // Add a trap and handler to unlock monitor after any exception + SootClass jlt = Scene.v().getSootClass("java.lang.Throwable"); + Local e = Jimple.v().newLocal("exception", RefType.v(jlt)); + body.getLocals().addLast(e); + units.addLast(Jimple.v().newIdentityStmt(e, + Jimple.v().newCaughtExceptionRef())); + Unit trapHandler = (Unit)body.getUnits().getLast(); + units.addLast(Jimple.v().newExitMonitorStmt( + method.isStatic() ? (Value)classConstant : (Value)thisRef)); + units.addLast(Jimple.v().newThrowStmt(e)); + body.getTraps().addLast( + Jimple.v().newTrap(jlt, firstStmt, + trapHandler, trapHandler)); + } + + public void outputMethodInfo() { + + // Analyze method + if (infoMap == null) + analyzeBody(); + + // Determine what to do + boolean generateBody = !cfile.c.isInterface() + || Util.isClassInit(m); + boolean doLineNumTable = generateBody + && !m.isAbstract() + && includeLineNumbers + && lineNumberTableLength > 0; + boolean doTrapTable = m.isConcrete() && !traps.isEmpty(); + + // Declare line number and trap table + if (doLineNumTable) { + out.println("static _jc_linenum " + + cfile.prefix + "$linenum_table$" + + C.name(m) + "[];"); + out.println(); + } + + // Output exception trap table + if (doTrapTable) { + out.println("static _jc_trap_info " + cfile.prefix + + "$trap_table$" + C.name(m) + "[] = {"); + out.indent(); + SootClass throwable = Scene.v() + .getSootClass("java.lang.Throwable"); + for (int i = 0; i < traps.size(); i++) { + Trap trap = (Trap)traps.get(i); + SootClass type = trap.getException(); + out.println("_JC_TRAP(" + + (type.equals(throwable) ? "_JC_NULL" : + "&_jc_" + C.name(type) + "$type") + + ", " + getInfo(trap.getBeginUnit()).region + + ", " + getInfo(trap.getEndUnit()).region + + "),"); + } + out.undent(); + out.println("};"); + out.println(); + } + + // Output parameter 'ptypes' + out.println("static const unsigned char " + cfile.prefix + + "$param_ptypes$" + C.name(m) + "[] = { "); + for (int i = 0; i < m.getParameterCount(); i++) { + out.print(Util._JC_TYPE(m.getParameterType(i))); + out.print(", "); + } + out.println(Util._JC_TYPE(m.getReturnType()) + " };"); + out.println(); + + // Output exception list + List elist = m.getExceptions(); + if (elist.size() > 0) { + out.println("static _jc_type *const " + cfile.prefix + + "$exceptions$" + C.name(m) + "[] = {"); + out.indent(); + for (Iterator i = elist.iterator(); i.hasNext(); ) { + SootClass ec = (SootClass)i.next(); + out.print("&_jc_" + C.name(ec) + "$type"); + if (i.hasNext()) + out.print(","); + out.println(); + } + out.undent(); + out.println("};"); + out.println(); + } + + // Output parameter types + if (m.getParameterCount() > 0) { + out.println("static _jc_type *const " + cfile.prefix + + "$param_types$" + C.name(m) + "[] = {"); + out.indent(); + for (int i = 0; i < m.getParameterCount(); i++) { + out.print("&" + + C.jc_type(m.getParameterType(i))); + if (i < m.getParameterCount() - 1) + out.print(","); + out.println(); + } + out.undent(); + out.println("};"); + out.println(); + } + + // Output initial method info stuff + out.println("_jc_method " + cfile.prefix + + "$method_info$" + C.name(m) + " = {"); + out.indent(); + out.println(".name=\t\t\t" + C.string(m.getName()) + ","); + out.println(".signature=\t\t" + + C.string(Util.signature(m)) + ","); + out.println(".class=\t\t\t&" + cfile.prefix + "$type,"); + if (m.getParameterCount() > 0) { + out.println(".param_types=\t\t&" + + cfile.prefix + "$param_types$" + C.name(m) + ","); + } + out.println(".return_type=\t\t&" + + C.jc_type(m.getReturnType()) + ","); + out.println(".param_ptypes=\t\t&" + + cfile.prefix + "$param_ptypes$" + C.name(m) + ","); + + out.println(".signature_hash=\t_JC_JLONG(0x" + + Long.toHexString(Util.sigHash(m)) + "),"); + out.println(".access_flags=\t\t" + C.accessDefs(m) + ","); + + // Output more method info stuff + if (generateBody) { + out.println(".function=\t\t&" + + cfile.prefix + "$method$" + C.name(m) + ","); + } + if (!cfile.c.isInterface() && Util.isVirtual(m)) { + out.println(".vtable_index=\t\t_JC_OFFSETOF(struct " + + cfile.prefix + "$vtable, " + + C.name(cfile.c) + "." + C.name(m) + ")" + + " / sizeof(void *),"); + } + out.println(".num_parameters=\t" + + m.getParameterCount() + ","); + if (m.getExceptions().size() > 0) { + out.println(".num_exceptions=\t" + + m.getExceptions().size() + ","); + out.println(".exceptions=\t\t&" + cfile.prefix + + "$exceptions$" + C.name(m) + ","); + } + if (doTrapTable || doLineNumTable) { + out.println(".u= { .exec= {"); + if (doTrapTable) { + out.println(" .trap_table_len=\t" + + traps.size() + ","); + out.println(" .trap_table=\t\t" + + cfile.prefix + "$trap_table$" + C.name(m) + + ","); + } + if (doLineNumTable) { + out.println(" .u= {"); + out.println(" .linenum= {"); + out.println(" .len=\t" + + lineNumberTableLength + ","); + out.println(" .table=\t" + + cfile.prefix + "$linenum_table$" + + C.name(m)); + out.println(" },"); + out.println(" },"); + } + out.println("} },"); + } + + // Done with method info + out.undent(); + out.println("};"); + out.println(); + + // Output line number table + if (doLineNumTable) { + out.println("_JC_LINENUM_TABLE(" + + C.name(cfile.c) + ", " + C.name(m) + ");"); + out.println(); + } + } + + public void outputMethodFunction() { + + // No body for interface virtual methods + if (cfile.c.isInterface() && !Util.isClassInit(m)) + return; + + // Analyze method + if (infoMap == null) + analyzeBody(); + + // Set current method + CURRENT_METHOD.set(this); + + // Output initial stuff for body + out.print(C.type(m)); + if (Util.isReference(m)) + out.print(" *"); + out.println(" _JC_JCNI_ATTR"); + out.println(cfile.prefix + "$method$" + + C.name(m) + C.paramsDecl(m, true)); + + // Output normal, abstract, or native method body + out.println('{'); + out.indent(); + if (m.isNative()) + outputNativeMethodBody(); + else if (m.isAbstract()) + outputAbstractMethodBody(); + else + outputNormalMethodBody(); + out.undent(); + out.println('}'); + out.println(); + + // Unset current method + CURRENT_METHOD.set(null); + } + + /** + * Reset state. This frees a bunch of memory. + */ + public void reset() { + m.releaseActiveBody(); + body = null; + lineNumberTableLength = 0; + graph = null; + bodyChain = null; + infoMap = null; + traps = null; + } + + private void outputNormalMethodBody() { + + // Get sorted locals array + Local[] locary = (Local[])body.getLocals().toArray( + new Local[body.getLocals().size()]); + Arrays.sort(locary, Util.localComparator); + + // Determine which locals need to be volatile + Set volatileLocals = determineVolatileLocals(); + + // Output stack allocated object memory + for (Iterator i = bodyChain.iterator(); i.hasNext(); ) { + Stmt stmt = (Stmt)i.next(); + if (!(stmt instanceof AssignStmt)) + continue; + AssignStmt assign = (AssignStmt)stmt; + StackAllocTag tag = (StackAllocTag)assign + .getRightOpBox().getTag("StackAllocTag"); + if (tag == null) + continue; + assign.getRightOp().apply(stackMemorySwitch); + out.println(" mem" + tag.getId() + ";"); + } + + // Do locals + for (int i = 0; i < locary.length; i++) { + Local local = locary[i]; + out.print(C.type(local, true) + " "); + if (Util.isReference(local.getType())) + out.print("*"); + if (volatileLocals.contains(local)) + out.print("volatile "); + out.println(local.getName() + ";"); + } + + // Do exception catcher, if any + if (!traps.isEmpty()) + out.println("_jc_catch_frame\tcatch;"); + + // Blank line + if (locary.length > 0 || !traps.isEmpty()) + out.println(); + + // Determine if we need a stack overflow check + for (Iterator i = bodyChain.iterator(); i.hasNext(); ) { + Stmt stmt = (Stmt)i.next(); + if (!stmt.containsInvokeExpr()) + continue; + if (NullCheckStmt.isNullCheck(stmt)) + continue; + if (stmt.getInvokeExpr().getMethod().isNative()) + continue; + out.println("// Check for stack overflow"); + out.println("_JC_STACK_OVERFLOW_CHECK(env);"); + out.println(); + break; + } + + // Output trap targets, if any + if (!traps.isEmpty()) { + out.println("// Define exception targets"); + out.print("_JC_DEFINE_TRAPS(env, catch, &" + + cfile.prefix + "$method_info$" + C.name(m)); + for (int i = 0; i < traps.size(); i++) { + Trap trap = (Trap)traps.get(i); + out.print(", &&label" + + getInfo(trap.getHandlerUnit()) + .labelIndex); + } + out.println(");"); + out.println(); + } + + // Validate body just for good measure + if (DEBUG) + body.validate(); + + // Iterate through body units and convert to C + StmtInfo previous = new StmtInfo(); + CStmtSwitch ss = new CStmtSwitch(this); + for (Iterator i = bodyChain.iterator(); i.hasNext(); ) { + Stmt stmt = (Stmt)i.next(); + + // Get info about this statement + StmtInfo info = (StmtInfo)infoMap.get(stmt); + + // Output label if this statement has one + if (info.labelIndex != -1) { + int indent = out.getIndent(); + out.setIndent(0); + out.println(); + out.println("label" + info.labelIndex + ":"); + out.setIndent(indent); + } + + // Output region update if needed + if (info.needsRegionUpdate) { + out.println("_JC_TRAP_REGION(catch, " + + info.region + ");"); + } + + // Output line number table entry if required + if (info.lineNumberIndex != -1) { + out.println("_JC_LINE_NUMBER(" + + (int)info.javaLineNumber + ");\t\t\t\t// " + + info.lineNumberIndex); + } + + // Output check for backward branch trap handlers + if (info.backwardTrapTarget) + out.println("_JC_PERIODIC_CHECK(env);"); + + // Output statement + stmt.apply(ss); + + // Update for next go round + if (info != null) + previous = info; + } + } + + private final AbstractJimpleValueSwitch stackMemorySwitch + = new AbstractJimpleValueSwitch() { + public void caseNewExpr(NewExpr v) { + RefType t = (RefType)v.getBaseType(); + SootClass sc = t.getSootClass(); + out.println("struct {"); + out.indent(); + out.println("struct _jc_" + + C.name(sc) + "$refs\trefs;"); + out.println("struct _jc_" + + C.name(sc) + "$object\tobj;"); + out.undent(); + out.print("}"); + } + public void caseNewArrayExpr(NewArrayExpr v) { + doArray(v.getSize(), (ArrayType)v.getType()); + } + public void caseNewMultiArrayExpr(NewMultiArrayExpr v) { + doArray(v.getSize(0), (ArrayType)v.getType()); + } + public void doArray(Value length, ArrayType atype) { + int len = ((IntConstant)length).value; + Type etype = atype.getElementType(); + out.println("struct {"); + out.indent(); + if (Util.isReference(etype)) { + out.println("_jc_word\t\telems[" + len + "];"); + out.println("_jc_object_array\tarray;"); + } else { + String tw = Util.typeWord(etype); + out.println("_jc_" + tw + "_array\tarray;"); + out.println(C.type(etype) + + "\telems[" + len + "];"); + } + out.undent(); + out.print("}"); + } + public void defaultCase(Object obj) { + throw new RuntimeException("non-new value"); + } + }; + + private Set determineVolatileLocals() { + + // No traps? + if (traps.isEmpty()) + return Collections.EMPTY_SET; + + // Get units at the head of each trap + Set trapHeads = new HashSet(); + for (Iterator i = traps.iterator(); i.hasNext(); ) + trapHeads.add(((Trap)i.next()).getHandlerUnit()); + + // Compute follows function + FollowsAnalysis follows = new FollowsAnalysis(graph); + + // Find locals that could be used after catching an exception + Set volatileLocals = new HashSet(); + for (Iterator i = bodyChain.iterator(); i.hasNext(); ) { + Stmt stmt = (Stmt)i.next(); + boolean afterException = false; + if (trapHeads.contains(stmt)) + afterException = true; + else { + for (Iterator j = trapHeads.iterator(); + j.hasNext(); ) { + Stmt head = (Stmt)j.next(); + if (follows.canFollow(head, stmt)) { + afterException = true; + break; + } + } + } + if (!afterException) + continue; + List uses = stmt.getUseBoxes(); + for (Iterator j = uses.iterator(); j.hasNext(); ) { + ValueBox useBox = (ValueBox)j.next(); + Value use = useBox.getValue(); + if (use instanceof Local) + volatileLocals.add(use); + } + } + + // Done + return volatileLocals; + } + + private void outputNativeMethodBody() { + out.println("_jc_value retval;"); + out.println(); + //out.println("_JC_STACK_OVERFLOW_CHECK(env);"); + out.print("_JC_INVOKE_NATIVE_METHOD(env, &" + cfile.prefix + + "$method_info$" + C.name(m) + ", &retval"); + if (!m.isStatic()) + out.print(", this"); + for (int i = 0; i < m.getParameterCount(); i++) + out.print(", param" + i); + out.println(");"); + if (!(m.getReturnType() instanceof VoidType)) { + out.println("return retval." + + Util.typeLetter(m.getReturnType()) + ";"); + } + } + + private void outputAbstractMethodBody() { + out.println("_JC_ABSTRACT_METHOD(env, &" + + cfile.prefix + "$method_info$" + C.name(m) + ");"); + } + + // Debugging stuff.. + + static void dumpBody(SootMethod m, String label) { + System.out.println("------ " + label + ": " + + m.getSubSignature() + " -------"); + JimpleBody body = (JimpleBody)m.retrieveActiveBody(); + Map stmtMap = computeStmtMap(body.getUnits()); + for (Iterator i = body.getUnits().iterator(); i.hasNext(); ) { + Stmt stmt = (Stmt)i.next(); + Integer num = (Integer)stmtMap.get(stmt); + Integer tnum; + try { + tnum = (Integer)stmtMap.get( + ((UnitBox)stmt.getUnitBoxes().get(0)) + .getUnit()); + } catch (IndexOutOfBoundsException e) { + tnum = null; + } + System.out.println(num + "\t" + stmt + + (tnum != null ? " [" + tnum + "]" : "")); + } + if (!body.getTraps().isEmpty()) { + System.out.println("------ TRAPS -------"); + for (Iterator i = body.getTraps().iterator(); + i.hasNext(); ) + dumpTrap((Trap)i.next(), stmtMap); + } + System.out.println("------ END -------"); + } + + private static void dumpTrap(Trap trap, Map map) { + System.out.println("\tTrap: [" + + map.get(trap.getBeginUnit()) + "] - [" + + map.get(trap.getEndUnit()) + "] -> [" + + map.get(trap.getHandlerUnit()) + "] " + + trap.getException()); + } + + private static Map computeStmtMap(Collection units) { + HashMap map = new HashMap(); + int count = 0; + for (Iterator i = units.iterator(); i.hasNext(); ) { + Stmt stmt = (Stmt)i.next(); + map.put(stmt, new Integer(++count)); + } + return map; + } +} Added: incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/java/org/dellroad/jc/cgen/CStmtSwitch.java URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/java/org/dellroad/jc/cgen/CStmtSwitch.java?rev=294974&view=auto ============================================================================== --- incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/java/org/dellroad/jc/cgen/CStmtSwitch.java (added) +++ incubator/harmony/enhanced/trunk/sandbox/contribs/jchevm/jchevm/java/org/dellroad/jc/cgen/CStmtSwitch.java Tue Oct 4 19:19:16 2005 @@ -0,0 +1,310 @@ + +// +// Copyright 2005 The Apache Software Foundation or its licensors, +// as applicable. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// $Id: CStmtSwitch.java,v 1.16 2005/03/20 17:16:08 archiecobbs Exp $ +// + +package org.dellroad.jc.cgen; + +import java.io.*; +import java.util.*; +import org.dellroad.jc.cgen.analysis.ActiveUseTag; +import soot.*; +import soot.baf.*; +import soot.jimple.*; +import soot.jimple.toolkits.annotation.tags.*; + +/** + * Converts Jimple statements into C statements. + */ +public class CStmtSwitch extends AbstractStmtSwitch { + CMethod cm; + CodeWriter out; + + public CStmtSwitch(CMethod cm) { + this.cm = cm; + this.out = cm.out; + } + + public void caseBreakpointStmt(BreakpointStmt stmt) { + out.println("// Breakpoint: " + stmt); + } + + public void caseInvokeStmt(InvokeStmt stmt) { +/******** + // Special case C.include() for included code + if (v instanceof StaticInvokeExpr) { + StaticInvokeExpr i = (StaticInvokeExpr)v; + SootMethod method = i.getMethod(); + SootClass cls = method.getDeclaringClass(); + Value arg; + if (cls.getName().equals("C") + && method.getName().equals("include") + && i.getArgCount() == 1 + && (arg = i.getArg(0)) instanceof StringConstant) { + out.println(((StringConstant)arg).value); + return; + } + } +*********/ + // Get invoke expression + ValueBox exprBox = stmt.getInvokeExprBox(); + + // Special case NullCheckStmt + if (NullCheckStmt.isNullCheck(stmt)) { + NullCheckTag tag = (NullCheckTag) + exprBox.getTag("NullCheckTag"); + if (tag != null && !tag.needCheck()) + return; + out.println(new CExpr(CExpr.FUNCTION, + "_JC_EXPLICIT_NULL_CHECK", "env", + ((SpecialInvokeExpr)exprBox.getValue()) + .getBaseBox()) + ";"); + return; + } + + // Special case ActiveUseCheckStmt + if (ActiveUseCheckStmt.isActiveUseCheck(stmt)) { + ActiveUseTag tag = (ActiveUseTag) + exprBox.getTag("ActiveUseTag"); + if (tag != null && !tag.isCheckNeeded()) + return; + out.println("_JC_ACTIVE_USE(env, " + C.name( + ActiveUseCheckStmt.getSootClass(stmt)) + ");"); + return; + } + + // Just a normal invocation + out.println(C.value(exprBox) + ";"); + } + + public void caseAssignStmt(AssignStmt stmt) { + + // Output array store check if needed + Value lhs = stmt.getLeftOp(); + Type lht = lhs.getType(); + if (lhs instanceof ArrayRef + && Util.isReference(lht) + && (Util.hasSubtypes(lht) + || !stmt.getRightOp().getType().equals(lht))) { + out.println(new CExpr(CExpr.FUNCTION, + "_JC_ARRAYSTORE_CHECK", "env", + C.value(((ArrayRef)lhs).getBaseBox()), + C.value(stmt.getRightOpBox())) + ";"); + } + + // Output assignment + out.println(C.value(stmt.getLeftOpBox()) + + " = " + C.value(stmt.getRightOpBox()) + ";"); + } + + public void caseIdentityStmt(IdentityStmt stmt) { + out.println(C.value(stmt.getLeftOpBox()) + + " = " + C.value(stmt.getRightOpBox()) + ";"); + } + + public void caseEnterMonitorStmt(EnterMonitorStmt stmt) { + out.println(new CExpr(CExpr.FUNCTION, + "_JC_MONITOR_ENTER", "env", stmt.getOpBox()) + ";"); + } + + public void caseExitMonitorStmt(ExitMonitorStmt stmt) { + out.println(new CExpr(CExpr.FUNCTION, + "_JC_MONITOR_EXIT", "env", stmt.getOpBox()) + ";"); + } + + public void caseGotoStmt(GotoStmt stmt) { + branch(stmt, stmt.getTarget()); + } + + public void caseIfStmt(IfStmt stmt) { + Stmt target = stmt.getTarget(); + out.print("if (" + C.value(stmt.getConditionBox()) + ")"); + if (stmt.equals(target) || cm.bodyChain.follows(stmt, target)) { + out.println(" {"); + out.indent(); + backwardBranch(target); + out.undent(); + out.println("}"); + } else { + out.println(); + out.indent(); + forwardBranch(target); + out.undent(); + } + } + + private void branch(Unit from, Unit target) { + if (from.equals(target) || cm.bodyChain.follows(from, target)) + backwardBranch(target); + else + forwardBranch(target); + } + + private void backwardBranch(Unit target) { + out.println("_JC_PERIODIC_CHECK(env);"); + out.println("goto label" + cm.getInfo(target).labelIndex + ";"); + } + + private void forwardBranch(Unit target) { + out.println("goto label" + cm.getInfo(target).labelIndex + ";"); + } + + // Represents one case in a tableswitch or lookupswitch + private static class Case implements Comparable { + final int value; + final Unit target; + Case(int value, Unit target) { + this.value = value; + this.target = target; + } + public int compareTo(Object o) { + Case that = (Case)o; + return (this.value < that.value ? -1 : + this.value > that.value ? +1 : 0); + } + } + + public void caseLookupSwitchStmt(LookupSwitchStmt stmt) { + int numCases = stmt.getTargetCount(); + Case[] cases = new Case[numCases]; + for (int i = 0; i < numCases; i++) { + cases[i] = new Case(stmt.getLookupValue(i), + stmt.getTarget(i)); + } + handleSwitch(stmt, stmt.getKeyBox(), + cases, stmt.getDefaultTarget()); + } + + private void handleSwitch(Stmt stmt, ValueBox key, + Case[] cases, Unit defaultTarget) { + + // Output switch statement + out.println("switch (" + C.value(key) + ") {"); + + // Sort cases (should already be sorted) + Arrays.sort(cases); + + // Compress out cases that just branch to the default target + int count = 0; + for (int i = 0; i < cases.length; i++) { + if (cases[i].target.equals(defaultTarget)) + continue; + cases[count++] = cases[i]; + } + + // Output normal cases, compacting contiguous case ranges + int previousValue = 0; + int previousStartValue = 0; + Unit previousTarget = null; + boolean caseOpen = false; + + for (int i = 0; i < count; i++) { + int value = cases[i].value; + Unit target = cases[i].target; + boolean closePrevious; + + // Determine whether we should close previous range + if (caseOpen + && (value != previousValue + 1 + || !target.equals(previousTarget))) { + if (previousValue != previousStartValue) + out.print(" ... " + previousValue); + out.println(":"); + out.indent(); + branch(stmt, previousTarget); + out.undent(); + caseOpen = false; + } + + // Can we continue an already open case? + if (caseOpen && i < count - 1) { + previousValue = value; + continue; + } + + // Start a new case if necessary + if (!caseOpen) { + out.print("case " + value); + previousStartValue = value; + previousTarget = target; + } + + // Is this the last value? If so we must stop + if (i == count - 1) { + if (caseOpen) + out.print(" ... " + value); + out.println(":"); + out.indent(); + branch(stmt, target); + out.undent(); + break; + } + + // Continue with open case + previousValue = value; + caseOpen = true; + } + + // Output the default case + out.println("default:"); + out.indent(); + branch(stmt, defaultTarget); + out.undent(); + out.println('}'); + } + + public void caseNopStmt(NopStmt stmt) { + } + + public void caseRetStmt(RetStmt stmt) { + defaultCase(stmt); + } + + public void caseReturnStmt(ReturnStmt stmt) { + if (!cm.traps.isEmpty()) + out.println("_JC_CANCEL_TRAPS(env, catch);"); + out.println("return " + C.value(stmt.getOpBox()) + ";"); + } + + public void caseReturnVoidStmt(ReturnVoidStmt stmt) { + if (!cm.traps.isEmpty()) + out.println("_JC_CANCEL_TRAPS(env, catch);"); + out.println("return;"); + } + + public void caseTableSwitchStmt(TableSwitchStmt stmt) { + int numCases = stmt.getHighIndex() - stmt.getLowIndex() + 1; + Case[] cases = new Case[numCases]; + for (int i = 0; i < numCases; i++) { + cases[i] = new Case(stmt.getLowIndex() + i, + stmt.getTarget(i)); + } + handleSwitch(stmt, stmt.getKeyBox(), + cases, stmt.getDefaultTarget()); + } + + public void caseThrowStmt(ThrowStmt stmt) { + out.println(new CExpr(CExpr.FUNCTION, + "_JC_THROW", "env", C.value(stmt.getOpBox())) + ";"); + } + + public void defaultCase(Object o) { + throw new RuntimeException("unhandled case"); + } +} +