|
@@ -0,0 +1,481 @@ |
|
|
|
|
|
/* |
|
|
|
|
|
* The Apache Software License, Version 1.1 |
|
|
|
|
|
* |
|
|
|
|
|
* Copyright (c) 2003 The Apache Software Foundation. All rights |
|
|
|
|
|
* reserved. |
|
|
|
|
|
* |
|
|
|
|
|
* Redistribution and use in source and binary forms, with or without |
|
|
|
|
|
* modification, are permitted provided that the following conditions |
|
|
|
|
|
* are met: |
|
|
|
|
|
* |
|
|
|
|
|
* 1. Redistributions of source code must retain the above copyright |
|
|
|
|
|
* notice, this list of conditions and the following disclaimer. |
|
|
|
|
|
* |
|
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
|
|
|
|
|
* notice, this list of conditions and the following disclaimer in |
|
|
|
|
|
* the documentation and/or other materials provided with the |
|
|
|
|
|
* distribution. |
|
|
|
|
|
* |
|
|
|
|
|
* 3. The end-user documentation included with the redistribution, if |
|
|
|
|
|
* any, must include the following acknowlegement: |
|
|
|
|
|
* "This product includes software developed by the |
|
|
|
|
|
* Apache Software Foundation (http://www.apache.org/)." |
|
|
|
|
|
* Alternately, this acknowlegement may appear in the software itself, |
|
|
|
|
|
* if and wherever such third-party acknowlegements normally appear. |
|
|
|
|
|
* |
|
|
|
|
|
* 4. The names "Ant" and "Apache Software Foundation" |
|
|
|
|
|
* must not be used to endorse or promote products derived |
|
|
|
|
|
* from this software without prior written permission. For written |
|
|
|
|
|
* permission, please contact apache@apache.org. |
|
|
|
|
|
* |
|
|
|
|
|
* 5. Products derived from this software may not be called "Apache" |
|
|
|
|
|
* nor may "Apache" appear in their names without prior written |
|
|
|
|
|
* permission of the Apache Group. |
|
|
|
|
|
* |
|
|
|
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
|
|
|
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
|
|
|
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
|
|
|
|
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
|
|
|
|
|
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
|
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
|
|
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
|
|
|
|
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|
|
|
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
|
|
|
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
|
|
|
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
|
|
|
|
* SUCH DAMAGE. |
|
|
|
|
|
* ==================================================================== |
|
|
|
|
|
* |
|
|
|
|
|
* This software consists of voluntary contributions made by many |
|
|
|
|
|
* individuals on behalf of the Apache Software Foundation. For more |
|
|
|
|
|
* information on the Apache Software Foundation, please see |
|
|
|
|
|
* <http://www.apache.org/>. |
|
|
|
|
|
*/ |
|
|
|
|
|
package org.apache.tools.ant.taskdefs.optional.dotnet; |
|
|
|
|
|
|
|
|
|
|
|
import org.apache.tools.ant.types.EnumeratedAttribute; |
|
|
|
|
|
import org.apache.tools.ant.BuildException; |
|
|
|
|
|
import org.apache.tools.ant.Task; |
|
|
|
|
|
import org.apache.tools.ant.util.FileUtils; |
|
|
|
|
|
|
|
|
|
|
|
import java.io.File; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Task to take a .NET or Mono -generated managed executable and turn it |
|
|
|
|
|
* into ILASM assembly code. Useful when converting imported typelibs into |
|
|
|
|
|
* assembler before patching and recompiling, as one has to do when doing |
|
|
|
|
|
* advanced typelib work. |
|
|
|
|
|
* |
|
|
|
|
|
* As well as generating the named output file, the ildasm program |
|
|
|
|
|
* will also generate resource files <code>Icons.resources</code> |
|
|
|
|
|
* <code>Message.resources</code> and a .res file whose filename stub is derived |
|
|
|
|
|
* from the source in ways to obscure to determine. |
|
|
|
|
|
* There is no way to control whether or not these files are created, or where they are created |
|
|
|
|
|
* (they are created in the current directory; their names come from inside the |
|
|
|
|
|
* executable and may be those used by the original developer). This task |
|
|
|
|
|
* creates the resources in the directory specified by <code>resourceDir</code> if |
|
|
|
|
|
* set, else in the same directory as the <code>destFile</code>. |
|
|
|
|
|
* |
|
|
|
|
|
* |
|
|
|
|
|
* This task requires the .NET SDK installed and ildasm on the path. |
|
|
|
|
|
* To disassemble using alternate CLR systems, set the executable attribute |
|
|
|
|
|
* to the name/path of the alternate implementation -one that must |
|
|
|
|
|
* support all the classic ildasm commands. |
|
|
|
|
|
* |
|
|
|
|
|
* |
|
|
|
|
|
* Dependency logic: the task executes the command if the output file is missing |
|
|
|
|
|
* or older than the source file. It does not take into account changes |
|
|
|
|
|
* in the options of the task, or timestamp differences in resource files. |
|
|
|
|
|
* When the underlying ildasm executable fails for some reason, it leaves the |
|
|
|
|
|
* .il file in place with some error message. To prevent this from confusing |
|
|
|
|
|
* the dependency logic, the file specified by the <code>dest</code> |
|
|
|
|
|
* attribute is <i>always</i> deleted after an unsuccessful build. |
|
|
|
|
|
*/ |
|
|
|
|
|
public class Ildasm extends Task { |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* source file (mandatory) |
|
|
|
|
|
*/ |
|
|
|
|
|
private File sourceFile; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* dest file (mandatory) |
|
|
|
|
|
*/ |
|
|
|
|
|
private File destFile; |
|
|
|
|
|
/** |
|
|
|
|
|
* progress bar switch |
|
|
|
|
|
*/ |
|
|
|
|
|
private boolean progressBar=false; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* what is our encoding |
|
|
|
|
|
*/ |
|
|
|
|
|
private String encoding; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* /bytes flag for byte markup |
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
private boolean bytes=false; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* line numbers? /linenum |
|
|
|
|
|
*/ |
|
|
|
|
|
private boolean linenumbers=false; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* /raweh flag for raw exception handling |
|
|
|
|
|
*/ |
|
|
|
|
|
private boolean rawExceptionHandling=false; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* show the source; /source |
|
|
|
|
|
*/ |
|
|
|
|
|
private boolean showSource=false; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* /quoteallnames to quote all names |
|
|
|
|
|
*/ |
|
|
|
|
|
private boolean quoteallnames=false; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* /header for header information |
|
|
|
|
|
*/ |
|
|
|
|
|
private boolean header=false; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* when false, sets the /noil attribute |
|
|
|
|
|
* to suppress assembly info |
|
|
|
|
|
*/ |
|
|
|
|
|
private boolean assembler=true; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* include metadata |
|
|
|
|
|
* /tokens |
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
private boolean metadata=false; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* what visibility do we want. |
|
|
|
|
|
* |
|
|
|
|
|
*/ |
|
|
|
|
|
private String visibility; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* specific item to disassemble |
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
private String item; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* override for the executable |
|
|
|
|
|
*/ |
|
|
|
|
|
private String executable="ildasm"; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* name of the directory for resources to be created. We cannot control |
|
|
|
|
|
* their names, but we can say where they get created. If not set, the |
|
|
|
|
|
* directory of the dest file is used |
|
|
|
|
|
*/ |
|
|
|
|
|
private File resourceDir; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Set the name of the directory for resources to be created. We cannot control |
|
|
|
|
|
* their names, but we can say where they get created. If not set, the |
|
|
|
|
|
* directory of the dest file is used |
|
|
|
|
|
*/ |
|
|
|
|
|
public void setResourceDir(File resourceDir) { |
|
|
|
|
|
this.resourceDir = resourceDir; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* override the name of the executable (normally ildasm) or set |
|
|
|
|
|
* its full path. Do not set a relative path, as the ugly hacks |
|
|
|
|
|
* needed to create resource files in the dest directory |
|
|
|
|
|
* force us to change to this directory before running the application. |
|
|
|
|
|
* i.e use <property location> to create an absolute path from a |
|
|
|
|
|
* relative one before setting this value. |
|
|
|
|
|
* @param executable |
|
|
|
|
|
*/ |
|
|
|
|
|
public void setExecutable(String executable) { |
|
|
|
|
|
this.executable = executable; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Select the output encoding: ascii, utf8 or unicode |
|
|
|
|
|
* @param encoding |
|
|
|
|
|
*/ |
|
|
|
|
|
public void setEncoding(EncodingTypes encoding) { |
|
|
|
|
|
this.encoding = encoding.getValue(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* enable (default) or disable assembly language in the output |
|
|
|
|
|
* @param assembler |
|
|
|
|
|
*/ |
|
|
|
|
|
public void setAssembler(boolean assembler) { |
|
|
|
|
|
this.assembler = assembler; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* enable or disable (default) the orginal bytes as comments |
|
|
|
|
|
* @param bytes |
|
|
|
|
|
*/ |
|
|
|
|
|
public void setBytes(boolean bytes) { |
|
|
|
|
|
this.bytes = bytes; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* the output file (required) |
|
|
|
|
|
* @param destFile |
|
|
|
|
|
*/ |
|
|
|
|
|
public void setDestFile(File destFile) { |
|
|
|
|
|
this.destFile = destFile; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* include header information; default false. |
|
|
|
|
|
* @param header |
|
|
|
|
|
*/ |
|
|
|
|
|
public void setHeader(boolean header) { |
|
|
|
|
|
this.header = header; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* name a single item to decode; a class or a method |
|
|
|
|
|
* e.g item="Myclass::method" or item="namespace1::namespace2::Myclass:method(void(int32)) |
|
|
|
|
|
* @param item |
|
|
|
|
|
*/ |
|
|
|
|
|
public void setItem(String item) { |
|
|
|
|
|
this.item = item; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* include line number information; default=false |
|
|
|
|
|
* @param linenumbers |
|
|
|
|
|
*/ |
|
|
|
|
|
public void setLinenumbers(boolean linenumbers) { |
|
|
|
|
|
this.linenumbers = linenumbers; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* include metadata information |
|
|
|
|
|
* @param metadata |
|
|
|
|
|
*/ |
|
|
|
|
|
public void setMetadata(boolean metadata) { |
|
|
|
|
|
this.metadata = metadata; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* show a graphical progress bar in a window during the process; off by default |
|
|
|
|
|
* @param progressBar |
|
|
|
|
|
*/ |
|
|
|
|
|
public void setProgressBar(boolean progressBar) { |
|
|
|
|
|
this.progressBar = progressBar; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* quote all names. |
|
|
|
|
|
* @param quoteallnames |
|
|
|
|
|
*/ |
|
|
|
|
|
public void setQuoteallnames(boolean quoteallnames) { |
|
|
|
|
|
this.quoteallnames = quoteallnames; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* enable raw exception handling (default = false) |
|
|
|
|
|
* @param rawExceptionHandling |
|
|
|
|
|
*/ |
|
|
|
|
|
public void setRawExceptionHandling(boolean rawExceptionHandling) { |
|
|
|
|
|
this.rawExceptionHandling = rawExceptionHandling; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* include the source as comments (default=false) |
|
|
|
|
|
*/ |
|
|
|
|
|
public void setShowSource(boolean showSource) { |
|
|
|
|
|
this.showSource = showSource; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* the file to disassemble -required |
|
|
|
|
|
* @param sourceFile |
|
|
|
|
|
*/ |
|
|
|
|
|
public void setSourceFile(File sourceFile) { |
|
|
|
|
|
this.sourceFile = sourceFile; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* alternate name for sourceFile |
|
|
|
|
|
* @param sourceFile |
|
|
|
|
|
*/ |
|
|
|
|
|
public void setSrcFile(File sourceFile) { |
|
|
|
|
|
setSourceFile(sourceFile); |
|
|
|
|
|
} |
|
|
|
|
|
/** |
|
|
|
|
|
* visibility options: one or more of the following, with + signs to |
|
|
|
|
|
* concatenate them: |
|
|
|
|
|
* <pre> |
|
|
|
|
|
* pub : Public |
|
|
|
|
|
* pri : Private |
|
|
|
|
|
* fam : Family |
|
|
|
|
|
* asm : Assembly |
|
|
|
|
|
* faa : Family and Assembly |
|
|
|
|
|
* foa : Family or Assembly |
|
|
|
|
|
* psc : Private Scope |
|
|
|
|
|
*</pre> |
|
|
|
|
|
* e.g. visibility="pub+pri". |
|
|
|
|
|
* Family means <code>protected</code> in C#; |
|
|
|
|
|
* @param visibility |
|
|
|
|
|
*/ |
|
|
|
|
|
public void setVisibility(String visibility) { |
|
|
|
|
|
this.visibility = visibility; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* verify that source and dest are ok |
|
|
|
|
|
*/ |
|
|
|
|
|
private void validate() { |
|
|
|
|
|
if(sourceFile==null || !sourceFile.exists() || !sourceFile.isFile()) { |
|
|
|
|
|
throw new BuildException("invalid source"); |
|
|
|
|
|
} |
|
|
|
|
|
if(destFile==null || destFile.isDirectory()) { |
|
|
|
|
|
throw new BuildException("invalid dest"); |
|
|
|
|
|
} |
|
|
|
|
|
if(resourceDir!=null |
|
|
|
|
|
&& (!resourceDir.exists() || !resourceDir.isDirectory())) { |
|
|
|
|
|
throw new BuildException("invalid resource directory"); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* |
|
|
|
|
|
* @return |
|
|
|
|
|
*/ |
|
|
|
|
|
private boolean isDisassemblyNeeded() { |
|
|
|
|
|
if(!destFile.exists()) { |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
|
|
|
long sourceTime=sourceFile.lastModified(); |
|
|
|
|
|
long destTime=destFile.lastModified(); |
|
|
|
|
|
return sourceTime>(destTime+ FileUtils.newFileUtils().getFileTimestampGranularity()); |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
/** |
|
|
|
|
|
* do the work |
|
|
|
|
|
* @throws BuildException |
|
|
|
|
|
*/ |
|
|
|
|
|
public void execute() throws BuildException { |
|
|
|
|
|
validate(); |
|
|
|
|
|
NetCommand command = new NetCommand(this, "ildasm", executable); |
|
|
|
|
|
command.setFailOnError(true); |
|
|
|
|
|
//fill in args |
|
|
|
|
|
command.addArgument("/text"); |
|
|
|
|
|
command.addArgument("/out="+destFile.toString()); |
|
|
|
|
|
if(!progressBar) { |
|
|
|
|
|
command.addArgument("/nobar"); |
|
|
|
|
|
} |
|
|
|
|
|
if(linenumbers) { |
|
|
|
|
|
command.addArgument("/linenum"); |
|
|
|
|
|
} |
|
|
|
|
|
if (showSource) { |
|
|
|
|
|
command.addArgument("/source"); |
|
|
|
|
|
} |
|
|
|
|
|
if (quoteallnames) { |
|
|
|
|
|
command.addArgument("/quoteallnames"); |
|
|
|
|
|
} |
|
|
|
|
|
if (header) { |
|
|
|
|
|
command.addArgument("/header"); |
|
|
|
|
|
} |
|
|
|
|
|
if (!assembler) { |
|
|
|
|
|
command.addArgument("/noil"); |
|
|
|
|
|
} |
|
|
|
|
|
if (metadata) { |
|
|
|
|
|
command.addArgument("/tokens"); |
|
|
|
|
|
} |
|
|
|
|
|
command.addArgument("/item:",item); |
|
|
|
|
|
if (rawExceptionHandling) { |
|
|
|
|
|
command.addArgument("/raweh"); |
|
|
|
|
|
} |
|
|
|
|
|
command.addArgument(EncodingTypes.getEncodingOption(encoding)); |
|
|
|
|
|
if (bytes) { |
|
|
|
|
|
command.addArgument("/bytes"); |
|
|
|
|
|
} |
|
|
|
|
|
command.addArgument("/vis:",visibility); |
|
|
|
|
|
|
|
|
|
|
|
//add the source file |
|
|
|
|
|
command.addArgument(sourceFile.getAbsolutePath()); |
|
|
|
|
|
|
|
|
|
|
|
//determine directory: resourceDir if set, |
|
|
|
|
|
//the dir of the destFile if not |
|
|
|
|
|
File execDir=resourceDir; |
|
|
|
|
|
if(execDir==null) { |
|
|
|
|
|
execDir=destFile.getParentFile(); |
|
|
|
|
|
} |
|
|
|
|
|
command.setDirectory(execDir); |
|
|
|
|
|
|
|
|
|
|
|
//now run |
|
|
|
|
|
try { |
|
|
|
|
|
command.runCommand(); |
|
|
|
|
|
} catch (BuildException e) { |
|
|
|
|
|
//forcibly delete the output file in case of trouble |
|
|
|
|
|
if(destFile.exists()) { |
|
|
|
|
|
destFile.delete(); |
|
|
|
|
|
} |
|
|
|
|
|
//then rethrow the exception |
|
|
|
|
|
throw e; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* encoding options; the default is ascii |
|
|
|
|
|
*/ |
|
|
|
|
|
public static class EncodingTypes extends EnumeratedAttribute { |
|
|
|
|
|
public final static String UNICODE= "unicode"; |
|
|
|
|
|
public final static String UTF8 = "utf8"; |
|
|
|
|
|
public final static String ASCII = "ascii"; |
|
|
|
|
|
public String[] getValues() { |
|
|
|
|
|
return new String[]{ |
|
|
|
|
|
ASCII, |
|
|
|
|
|
UTF8, |
|
|
|
|
|
UNICODE, |
|
|
|
|
|
}; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* map from an encoding enum to an encoding opion |
|
|
|
|
|
* @param enumValue |
|
|
|
|
|
* @return |
|
|
|
|
|
*/ |
|
|
|
|
|
public static String getEncodingOption(String enumValue) { |
|
|
|
|
|
if(UNICODE.equals(enumValue)) { |
|
|
|
|
|
return "/unicode"; |
|
|
|
|
|
} |
|
|
|
|
|
if (UTF8.equals(enumValue)) { |
|
|
|
|
|
return "/utf8"; |
|
|
|
|
|
} |
|
|
|
|
|
return null; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* visibility options for decoding |
|
|
|
|
|
*/ |
|
|
|
|
|
public static class VisibilityOptions extends EnumeratedAttribute { |
|
|
|
|
|
public String[] getValues() { |
|
|
|
|
|
return new String[]{ |
|
|
|
|
|
"pub", //Public |
|
|
|
|
|
"pri", //Private |
|
|
|
|
|
"fam", //Family |
|
|
|
|
|
"asm", //Assembly |
|
|
|
|
|
"faa", //Family and Assembly |
|
|
|
|
|
"foa", //Family or Assembly |
|
|
|
|
|
"psc", //Private Scope |
|
|
|
|
|
}; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
} |