Skip to content

Instantly share code, notes, and snippets.

@bdw429s
Last active Dec 7, 2018
Embed
What would you like to do?
<cfscript>
/*
This script will take a relative path to a CFC or CFM file in your application, analyze it's corresponding bytecode and
measure approximately how many Bytes of bytecode were generated for each line of your CFML code. There's not any one-to-one
correlation between CFML code and bytecode. Some lines of your source code generate no bytecode such as comments or whitespace.
Other lines of CFML code may generate hundreds of bytes of code.
This is more for the fun of it. It has been tested on Lucee 5.2.9.31. It will not work on Adobe and may cease to work on
future versions of Lucee if the BCEL library is no longer bundled by default.
*/
// Swap this out with another file to analyze
param url.relativeFilePath = 'Application.cfc';
// Force compioation of the template if there's not a class file yet
PageSourceImpl = createObject( 'java', 'lucee.runtime.PageSourceImpl' );
PageSourceImpl.best( getPageContext().getPageSources( relativeFilePath ) ).loadPage( getPageContext(), true );
// Get Lucee's page source object for this file
ps = getPageContext().getPageSource( relativeFilePath );
// Init an array with each line of code from the file
fileMap = arrayMap( ps.getSource(),
function( line ) {
return { line : line, bytecode = 0 };
} );
// Load up and parse the class file. Requires the BCEL libs in your classpath, which may or may not exist depending on your Lucee version.
javaClass = createObject( 'java', 'org.apache.bcel.classfile.ClassParser' ).init( ps.getMapping().getClassRootDirectory() & '/' & ps.getClassName().replace( '.', '/', 'all' ) & '.class' ).parse();
// A counter just to track how much bytecode we account for
totalbytecodeaccountedfor = 0;
// All methods in the class
// The bytecode generated from your CFML can be spread across more than one method in the bytecode depending on how big your CFC is
// There is a limit of 65,536 Bytes of bytecode per method in a class file
// NOTE THERE IS NO CORRELLATION BETWEEN CFML METHODS AND METHODS IN THE BYTECODE.
for( m in javaClass.getMethods() ) {
// We need the LineNumberTable which is stored as an attribute on the method
attrs = m.getCode().getAttributes();
codeBytesThisMethod = arrayLen( m.getCode().getCode() );
codeByteOffset = 0;
lastLineNo = 1;
// All attributes on the method
for( x in attrs ) {
// Looking for the LineNumberTable.
if( x.getClass().getName() == 'org.apache.bcel.classfile.LineNumberTable' ) {
// All lines in the line number table
for( l in x.getLineNumberTable() ) {
// The differnce between the bytecode offest start and the previous one gets added to the total for the previous CFML line
fileMap[ lastLineNo ].bytecode += l.getStartPC() - codeByteOffset;
// And also add it into the total
totalbytecodeaccountedfor += l.getStartPC() - codeByteOffset;
// Reset out pointers
codeByteOffset = l.getStartPC();
lastLineNo = l.getLineNumber()
}
}
}
// If at least some bytecode in this method applied to CFML and this isn't the udfDefaultValue method
// award all remaining bytecode in the method to the last CFML line to get processed.
if( lastLineNo > 1 && not m.toString() contains 'udfDefaultValue' ) {
fileMap[ lastLineNo ].bytecode += codeBytesThisMethod - codeByteOffset;
totalbytecodeaccountedfor += codeBytesThisMethod - codeByteOffset;
}
}
</cfscript>
<cfoutput>
#round( totalbytecodeaccountedfor/1000 )#KB of bytecode accounted for below
<table border=1 cellspacing=0 cellpadding=2>
<tr>
<td>Bytes</td>
<td>Line of code</td>
</tr>
<cfloop array="#filemap#" index="line" >
<tr>
<td><cfif line.bytecode>#line.bytecode#</cfif>&nbsp;</td>
<td>#encodeForHTML( line.line.replace( ' ', ' ', 'all' ) ).replace( ' ', '&nbsp;', 'all' )#</td>
</tr>
</cfloop>
</table>
</cfoutput>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment