coffee-coverage
Advanced tools
Comparing version 0.2.0 to 0.3.0
# coffee-coverage Changelog | ||
### v0.3.0 | ||
- Add support for dynamically compiling .coffee and ._coffee files on the fly. (Special thanks | ||
to [sivad77](https://github.com/sivad77) for the suggestion.) | ||
- Experimental support for [streamlinejs](https://github.com/Sage/streamlinejs). | ||
### v0.2.0 | ||
@@ -4,0 +10,0 @@ |
// Generated by CoffeeScript 1.6.3 | ||
(function() { | ||
var COFFEE_EXTENSION, CoverageError, JS_EXTENSION, abbreviatedPath, coffeeScript, debug, defaultOptions, defaults, endsWith, events, fs, mkdirs, path, pkginfo, statFile, stripLeadingDot, util, _ref, | ||
var CoverageError, EXTENSIONS, StringStream, abbreviatedPath, coffeeScript, debug, defaultOptions, defaults, endsWith, events, fs, getRelativeFilename, mkdirs, path, pkginfo, startsWith, statFile, stripLeadingDotOrSlash, util, _ref, | ||
__hasProp = {}.hasOwnProperty, | ||
@@ -20,3 +20,3 @@ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, | ||
_ref = require('./helpers'), endsWith = _ref.endsWith, defaults = _ref.defaults, abbreviatedPath = _ref.abbreviatedPath, mkdirs = _ref.mkdirs, stripLeadingDot = _ref.stripLeadingDot, statFile = _ref.statFile; | ||
_ref = require('./helpers'), startsWith = _ref.startsWith, endsWith = _ref.endsWith, defaults = _ref.defaults, abbreviatedPath = _ref.abbreviatedPath, mkdirs = _ref.mkdirs, stripLeadingDotOrSlash = _ref.stripLeadingDotOrSlash, statFile = _ref.statFile; | ||
@@ -27,6 +27,11 @@ pkginfo = require('pkginfo')(module, 'version', 'author', 'contributors'); | ||
COFFEE_EXTENSION = ".coffee"; | ||
EXTENSIONS = { | ||
".coffee": { | ||
js_extension: ".js" | ||
}, | ||
"._coffee": { | ||
js_extension: "._js" | ||
} | ||
}; | ||
JS_EXTENSION = ".js"; | ||
CoverageError = (function(_super) { | ||
@@ -46,2 +51,15 @@ __extends(CoverageError, _super); | ||
StringStream = (function() { | ||
function StringStream() { | ||
this.data = ""; | ||
} | ||
StringStream.prototype.write = function(data) { | ||
return this.data += data; | ||
}; | ||
return StringStream; | ||
})(); | ||
defaultOptions = { | ||
@@ -54,2 +72,78 @@ coverageVar: '_$jscoverage', | ||
getRelativeFilename = function(basePath, fileName) { | ||
var relativeFileName; | ||
relativeFileName = path.resolve(fileName); | ||
if ((basePath != null) && startsWith(relativeFileName, basePath)) { | ||
relativeFileName = relativeFileName.slice(basePath.length); | ||
} | ||
return relativeFileName; | ||
}; | ||
exports.register = function(options) { | ||
var basePath, coverage, excludeFile, initStream, instrumentFile, module, origCoffeeHandler, origStreamineCoffeeHandler, streamline_js; | ||
coverage = new exports.CoverageInstrumentor(options); | ||
module = require('module'); | ||
if (options.basePath) { | ||
basePath = path.resolve(options.basePath); | ||
if (options.initAll) { | ||
initStream = new StringStream(); | ||
coverage.instrumentDirectory(options.basePath, null, { | ||
exclude: options.exclude, | ||
recursive: true, | ||
initFileStream: initStream | ||
}); | ||
eval(initStream.data); | ||
} | ||
} | ||
excludeFile = function(fileName) { | ||
var excludePath, excluded, relativeFilename, _i, _len, _ref1, _ref2; | ||
excluded = false; | ||
if (basePath) { | ||
relativeFilename = getRelativeFilename(basePath, fileName); | ||
if (relativeFilename === fileName) { | ||
excluded = true; | ||
} | ||
} | ||
if (_ref1 = !path.extname(fileName), __indexOf.call(Object.keys(EXTENSIONS), _ref1) >= 0) { | ||
excluded = true; | ||
} | ||
_ref2 = options.exclude || []; | ||
for (_i = 0, _len = _ref2.length; _i < _len; _i++) { | ||
excludePath = _ref2[_i]; | ||
if (startsWith(fileName, excludePath)) { | ||
excluded = true; | ||
} | ||
} | ||
return excluded; | ||
}; | ||
instrumentFile = function(fileName) { | ||
var content, coverageFileName, instrumented; | ||
content = fs.readFileSync(fileName, 'utf8'); | ||
coverageFileName = getRelativeFilename(basePath, fileName); | ||
instrumented = coverage.instrumentCoffee(coverageFileName, content, options); | ||
return instrumented.init + instrumented.js; | ||
}; | ||
origCoffeeHandler = require.extensions[".coffee"]; | ||
require.extensions[".coffee"] = function(module, fileName) { | ||
if (excludeFile(fileName)) { | ||
return origCoffeeHandler.call(this, module, fileName); | ||
} | ||
return module._compile(instrumentFile(fileName), fileName); | ||
}; | ||
if (options.streamlinejs) { | ||
streamline_js = require.extensions["._js"]; | ||
if (streamline_js) { | ||
origStreamineCoffeeHandler = require.extensions["._coffee"]; | ||
return require.extensions["._coffee"] = function(module, fileName) { | ||
var compiled; | ||
if (excludeFile(fileName)) { | ||
return origStreamineCoffeeHandler.call(this, module, fileName); | ||
} | ||
compiled = instrumentFile(fileName); | ||
return streamline_js(module, fileName, compiled, null); | ||
}; | ||
} | ||
} | ||
}; | ||
exports.CoverageInstrumentor = (function(_super) { | ||
@@ -132,9 +226,28 @@ var fileToLines, fixLocationData, generateUniqueName, nodeType, toQuotedString, validateSrcDest, writeToFile; | ||
CoverageInstrumentor.prototype.getOutputFileName = function(fileName) { | ||
var coffee_extension, ext, outFile; | ||
if (fileName == null) { | ||
return null; | ||
} | ||
outFile = fileName; | ||
for (coffee_extension in EXTENSIONS) { | ||
ext = EXTENSIONS[coffee_extension]; | ||
if (endsWith(fileName.toLowerCase(), coffee_extension)) { | ||
outFile = fileName.slice(0, +(-(coffee_extension.length + 1)) + 1 || 9e9) + ext.js_extension; | ||
break; | ||
} | ||
} | ||
return outFile; | ||
}; | ||
CoverageInstrumentor.prototype.instrumentDirectory = function(sourceDirectory, outDirectory, options) { | ||
var answer, file, inst, outDirectoryStat, outFile, outputDirectoryExists, sourceDirectoryMode, sourceFile, sourceStat, _i, _len, _ref1; | ||
var answer, coffee_extension, exclude, file, inst, instrumentOptions, outDirectoryStat, outFile, outputDirectoryExists, processed, relativePath, skip, sourceDirectoryMode, sourceFile, sourceStat, _i, _j, _len, _len1, _ref1, _ref2; | ||
if (options == null) { | ||
options = {}; | ||
} | ||
sourceDirectory = path.resolve(sourceDirectory); | ||
this.emit("instrumentingDirectory", sourceDirectory, outDirectory); | ||
options = Object.create(options); | ||
options.usedFileNames = options.usedFileNames || []; | ||
options.basePath = options.basePath ? path.resolve(options.basePath) : sourceDirectory; | ||
answer = { | ||
@@ -148,29 +261,57 @@ lines: 0 | ||
} | ||
if (!endsWith(outDirectory, path.sep)) { | ||
outDirectory += path.sep; | ||
sourceDirectoryMode = (statFile(sourceDirectory)).mode; | ||
if (outDirectory) { | ||
if (!endsWith(outDirectory, path.sep)) { | ||
outDirectory += path.sep; | ||
} | ||
outDirectoryStat = statFile(outDirectory); | ||
outputDirectoryExists = !!outDirectoryStat; | ||
} | ||
outDirectoryStat = statFile(outDirectory); | ||
outputDirectoryExists = !!outDirectoryStat; | ||
sourceDirectoryMode = (statFile(sourceDirectory)).mode; | ||
_ref1 = fs.readdirSync(sourceDirectory); | ||
for (_i = 0, _len = _ref1.length; _i < _len; _i++) { | ||
file = _ref1[_i]; | ||
skip = false; | ||
if (__indexOf.call(options.exclude, file) >= 0) { | ||
skip = true; | ||
} | ||
sourceFile = sourceDirectory + file; | ||
relativePath = getRelativeFilename(options.basePath, sourceFile); | ||
if (relativePath !== sourceFile) { | ||
_ref2 = options.exclude; | ||
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) { | ||
exclude = _ref2[_j]; | ||
if (startsWith(relativePath, exclude)) { | ||
skip = true; | ||
} | ||
} | ||
} | ||
if (skip) { | ||
this.emit("skip", sourceDirectory + file); | ||
continue; | ||
} | ||
sourceFile = sourceDirectory + file; | ||
outFile = outDirectory + file; | ||
outFile = outDirectory ? outDirectory + file : null; | ||
sourceStat = statFile(sourceFile); | ||
if (endsWith(file.toLowerCase(), COFFEE_EXTENSION) && sourceStat.isFile()) { | ||
if (!outputDirectoryExists) { | ||
mkdirs(outDirectory, sourceDirectoryMode); | ||
outputDirectoryExists = true; | ||
} | ||
outFile = outFile.slice(0, +(-(COFFEE_EXTENSION.length + 1)) + 1 || 9e9) + JS_EXTENSION; | ||
inst = this.instrumentFile(sourceFile, outFile, options); | ||
answer.lines += inst.lines; | ||
} else if (options.recursive && sourceStat.isDirectory()) { | ||
if (options.recursive && sourceStat.isDirectory()) { | ||
inst = this.instrumentDirectory(sourceFile, outFile, options); | ||
answer.lines += inst.lines; | ||
} else { | ||
processed = false; | ||
for (coffee_extension in EXTENSIONS) { | ||
if (coffee_extension === '._coffee') { | ||
continue; | ||
} | ||
if (endsWith(file.toLowerCase(), coffee_extension) && sourceStat.isFile()) { | ||
if ((outDirectory != null) && !outputDirectoryExists) { | ||
mkdirs(outDirectory, sourceDirectoryMode); | ||
outputDirectoryExists = true; | ||
} | ||
outFile = this.getOutputFileName(outFile); | ||
instrumentOptions = Object.create(options); | ||
instrumentOptions.fileName = relativePath; | ||
inst = this.instrumentFile(sourceFile, outFile, instrumentOptions); | ||
answer.lines += inst.lines; | ||
processed = true; | ||
break; | ||
} | ||
} | ||
} | ||
@@ -182,26 +323,13 @@ } | ||
CoverageInstrumentor.prototype.instrumentFile = function(sourceFile, outFile, options) { | ||
var answer, data, filename, _ref1; | ||
var answer, data; | ||
if (outFile == null) { | ||
outFile = null; | ||
} | ||
if (options == null) { | ||
options = {}; | ||
} | ||
this.emit("instrumentingFile", sourceFile, outFile); | ||
validateSrcDest(sourceFile, outFile); | ||
switch (options.path) { | ||
case 'relative': | ||
filename = stripLeadingDot(sourceFile); | ||
break; | ||
case 'abbr': | ||
filename = abbreviatedPath(stripLeadingDot(sourceFile)); | ||
break; | ||
default: | ||
filename = path.basename(sourceFile); | ||
} | ||
if (options.usedFileNames && __indexOf.call(options.usedFileNames, filename) >= 0) { | ||
filename = generateUniqueName(options.usedFileNames, filename); | ||
} | ||
if (options.usedFileNames) { | ||
options.usedFileNames.push(filename); | ||
} | ||
data = fs.readFileSync(sourceFile, 'utf8'); | ||
answer = this.instrumentCoffee(filename, data); | ||
if ((_ref1 = options.initFileStream) != null) { | ||
_ref1.write(answer.init); | ||
} | ||
answer = this.instrumentCoffee(options.fileName || sourceFile, data, options); | ||
if (outFile) { | ||
@@ -227,7 +355,32 @@ writeToFile(outFile, answer.init + answer.js); | ||
CoverageInstrumentor.prototype.instrumentCoffee = function(fileName, fileData) { | ||
var ast, fileToInstrumentLines, index, init, instrumentTree, instrumentedLines, js, line, lineNumber, options, quotedFilename, _i, _j, _len, _len1; | ||
options = this.options; | ||
quotedFilename = toQuotedString(fileName); | ||
ast = coffeeScript.nodes(fileData); | ||
CoverageInstrumentor.prototype.instrumentCoffee = function(fileName, fileData, options) { | ||
var answer, ast, err, fileToInstrumentLines, index, init, instrumentTree, instrumentedLines, js, line, lineNumber, origFileName, quotedFileName, _i, _j, _len, _len1, _ref1, | ||
_this = this; | ||
if (options == null) { | ||
options = {}; | ||
} | ||
origFileName = fileName; | ||
switch (options.path) { | ||
case 'relative': | ||
fileName = stripLeadingDotOrSlash(fileName); | ||
break; | ||
case 'abbr': | ||
fileName = abbreviatedPath(stripLeadingDotOrSlash(fileName)); | ||
break; | ||
default: | ||
fileName = path.basename(fileName); | ||
} | ||
if (options.usedfileNames) { | ||
if (__indexOf.call(options.usedfileNames, fileName) >= 0) { | ||
fileName = generateUniqueName(options.usedfileNames, fileName); | ||
} | ||
options.usedfileNames.push(fileName); | ||
} | ||
quotedFileName = toQuotedString(fileName); | ||
try { | ||
ast = coffeeScript.nodes(fileData); | ||
} catch (_error) { | ||
err = _error; | ||
throw new CoverageError("Could not parse " + fileName + ": " + err.stack); | ||
} | ||
instrumentedLines = []; | ||
@@ -243,3 +396,3 @@ instrumentTree = function(node, parent, depth) { | ||
debug("Examining l:" + (node.locationData.first_line + 1) + " d:" + depth + " " + (nodeType(node))); | ||
if (nodeType(node) !== "Block") { | ||
if ((nodeType(node) !== "Block") || node.coffeeCoverageDoNotInstrument) { | ||
if (nodeType(node) === "If" && node.isChain) { | ||
@@ -270,3 +423,3 @@ debug(" Disabling chaining for if statement"); | ||
instrumentedLines.push(line); | ||
instrumentedLine = coffeeScript.nodes("" + options.coverageVar + "[" + quotedFilename + "][" + line + "]++"); | ||
instrumentedLine = coffeeScript.nodes("" + _this.options.coverageVar + "[" + quotedFileName + "][" + line + "]++"); | ||
fixLocationData(instrumentedLine, line); | ||
@@ -283,9 +436,9 @@ children.splice(childIndex, 0, instrumentedLine); | ||
instrumentTree(ast); | ||
init = "if (typeof " + options.coverageVar + " === 'undefined') " + options.coverageVar + " = {};\nif ((typeof global !== 'undefined') && (typeof global." + options.coverageVar + " === 'undefined')) {\n global." + options.coverageVar + " = " + options.coverageVar + "\n} else if ((typeof window !== 'undefined') && (typeof window." + options.coverageVar + " === 'undefined')) {\n window." + options.coverageVar + " = " + options.coverageVar + "\n}\nif (! " + options.coverageVar + "[" + quotedFilename + "]) {\n " + options.coverageVar + "[" + quotedFilename + "] = [];\n"; | ||
init = "if (typeof " + this.options.coverageVar + " === 'undefined') " + this.options.coverageVar + " = {};\nif ((typeof global !== 'undefined') && (typeof global." + this.options.coverageVar + " === 'undefined')) {\n global." + this.options.coverageVar + " = " + this.options.coverageVar + "\n} else if ((typeof window !== 'undefined') && (typeof window." + this.options.coverageVar + " === 'undefined')) {\n window." + this.options.coverageVar + " = " + this.options.coverageVar + "\n}\nif (! " + this.options.coverageVar + "[" + quotedFileName + "]) {\n " + this.options.coverageVar + "[" + quotedFileName + "] = [];\n"; | ||
for (_i = 0, _len = instrumentedLines.length; _i < _len; _i++) { | ||
lineNumber = instrumentedLines[_i]; | ||
init += " " + options.coverageVar + "[" + quotedFilename + "][" + lineNumber + "] = 0;\n"; | ||
init += " " + this.options.coverageVar + "[" + quotedFileName + "][" + lineNumber + "] = 0;\n"; | ||
} | ||
init += "}\n\n"; | ||
init += "" + options.coverageVar + "[" + quotedFilename + "].source = ["; | ||
init += "" + this.options.coverageVar + "[" + quotedFileName + "].source = ["; | ||
fileToInstrumentLines = fileToLines(fileData); | ||
@@ -300,6 +453,14 @@ for (index = _j = 0, _len1 = fileToInstrumentLines.length; _j < _len1; index = ++_j) { | ||
init += "];\n\n"; | ||
js = ast.compile({ | ||
bare: options.bare | ||
}); | ||
return { | ||
try { | ||
js = ast.compile({ | ||
bare: options.bare | ||
}); | ||
} catch (_error) { | ||
err = _error; | ||
throw new CoverageError("Could not compile " + fileName + " after annotating: " + err.stack); | ||
} | ||
if ((_ref1 = options.initFileStream) != null) { | ||
_ref1.write(init); | ||
} | ||
answer = { | ||
init: init, | ||
@@ -309,2 +470,3 @@ js: js, | ||
}; | ||
return answer; | ||
}; | ||
@@ -311,0 +473,0 @@ |
// Generated by CoffeeScript 1.6.3 | ||
(function() { | ||
var CoverageInstrumentor, fs, parseArgs, path, printHelp, stripLeadingDot, version, _ref; | ||
var CoverageInstrumentor, fs, mkdirs, parseArgs, path, printHelp, stripLeadingDotOrSlash, version, _ref, _ref1; | ||
@@ -13,3 +13,3 @@ fs = require('fs'); | ||
stripLeadingDot = require('./helpers').stripLeadingDot; | ||
_ref1 = require('./helpers'), stripLeadingDotOrSlash = _ref1.stripLeadingDotOrSlash, mkdirs = _ref1.mkdirs; | ||
@@ -75,3 +75,3 @@ printHelp = function() { | ||
exports.main = function(args) { | ||
var coverageInstrumentor, err, options, result, _ref1; | ||
var coverageInstrumentor, err, options, result, _ref2; | ||
try { | ||
@@ -87,17 +87,18 @@ options = parseArgs(args.slice(2)); | ||
coverageInstrumentor.on("instrumentingFile", function(sourceFile, outFile) { | ||
return console.log(" " + (stripLeadingDot(sourceFile)) + " to " + (stripLeadingDot(outFile))); | ||
return console.log(" " + (stripLeadingDotOrSlash(sourceFile)) + " to " + (stripLeadingDotOrSlash(outFile))); | ||
}); | ||
coverageInstrumentor.on("instrumentingDirectory", function(sourceDir, outDir) { | ||
return console.log("Instrumenting directory: " + (stripLeadingDot(sourceDir)) + " to " + (stripLeadingDot(outDir))); | ||
return console.log("Instrumenting directory: " + (stripLeadingDotOrSlash(sourceDir)) + " to " + (stripLeadingDotOrSlash(outDir))); | ||
}); | ||
coverageInstrumentor.on("skip", function(file) { | ||
return console.log(" Skipping: " + (stripLeadingDot(file))); | ||
return console.log(" Skipping: " + (stripLeadingDotOrSlash(file))); | ||
}); | ||
} | ||
if (options.initfile) { | ||
mkdirs(path.dirname(options.initfile)); | ||
options.initFileStream = fs.createWriteStream(options.initfile); | ||
} | ||
result = coverageInstrumentor.instrument(options.src, options.dest, options); | ||
if ((_ref1 = options.initFileStream) != null) { | ||
_ref1.end(); | ||
if ((_ref2 = options.initFileStream) != null) { | ||
_ref2.end(); | ||
} | ||
@@ -104,0 +105,0 @@ return console.log("Annotated " + result.lines + " lines."); |
@@ -33,4 +33,4 @@ // Generated by CoffeeScript 1.6.3 | ||
exports.stripLeadingDot = function(pathName) { | ||
return pathName.replace(/^\.\//, ""); | ||
exports.stripLeadingDotOrSlash = function(pathName) { | ||
return pathName.replace(/^\//, "").replace(/^\.\//, ""); | ||
}; | ||
@@ -37,0 +37,0 @@ |
@@ -5,3 +5,3 @@ { | ||
"keywords": ["javascript", "coffeescript", "coverage", "code coverage", "test"], | ||
"version": "0.2.0", | ||
"version": "0.3.0", | ||
"author": "Benbria (http://www.benbria.com/)", | ||
@@ -8,0 +8,0 @@ "contributors": [ |
@@ -71,24 +71,52 @@ Benbria CoffeeCoverage | ||
At Benbria, we use coffeeCoverage to find out how much coverage we get from our unit tests. Our | ||
process works like this; first we make a copy of our code base: | ||
### Dynamic Compilation | ||
cd project | ||
mkdir /tmp/coverage | ||
tar -cf - . | (cd /tmp/coverage && tar -xf -) | ||
cd /tmp/coverage | ||
There are two ways to use coffeeCoverage as part of your unit tests. First, if you run your | ||
tests directly on your .coffee files, you can register coffeeCoverage to dynamically compile | ||
.coffee (and even ._coffee if you're using [streamlinejs](https://github.com/Sage/streamlinejs)) | ||
files. For example, create a "register-handlers.js": | ||
Then we instrument it in-place. We exclude the "test" directory, since we don't want coverage of | ||
our actual test code: | ||
# If you're using with streamline, you *must* register streamline first: | ||
require('streamline').register({}); | ||
coffeeCoverage --initfile init.js --exclude node_modules,.git,test --path abbr . . | ||
# Register coffee-coverage if coverage is enabled. | ||
if(process.env.COVERAGE) { | ||
require('coffee-coverage').register({ | ||
path: 'abbr', | ||
basePath: __dirname, | ||
exclude: ['/test', '/node_modules', '/.git'], | ||
initAll: true, | ||
streamlinejs: true | ||
}); | ||
} | ||
We don't have to delete the .coffee files, since when we `require 'foo'`, node will preferentially | ||
load the foo.js file over the foo.coffee file. coffeeCoverage nicely gives us the number of lines | ||
it instrumented - this is handy, because if we never `require` a given file from our tests, it | ||
won't show up in the mocha report. | ||
Note we set the "basePath" to the root of our project. This can be a path which is relative to | ||
`__dirname` (e.g. `__dirname + "/.."`). | ||
Next we run our tests: | ||
Note that streamline support is "experimental" right now (i.e. it might break at any moment | ||
because we're using undocumented features in streamlinejs) so to turn it on, you have to | ||
explicitly pass 'streamlinejs: true' as an option. | ||
mocha --require init.js --reporter html-cov --compilers coffee:coffee-script test/*Test.coffee | ||
Then, run your tests: | ||
COVERAGE=true mocha --require register-handlers.js --reporter html-cov ... | ||
### Static Compilation | ||
Alternatively, you can use coffeeCoverage to statically compile your code with instrumentation: | ||
# Compile everything except the test directory with coffeeCoverage | ||
coffeeCoverage --initfile ./lib/init.js --exclude test --path abbr ./src ./lib | ||
# Compile the test directory with regular coffee-script | ||
coffee -o ./lib/test ./src/test | ||
This also writes an "lib/init.js" which initializes all the execution counts to 0. This is handy, | ||
because otherwise if we never `require` a given module, that module's counts won't show up at all | ||
in the code coverage report, which might overly inflate our code coverage percentage. Next we run | ||
our tests: | ||
mocha --require ./lib/init.js --reporter html-cov ./lib/test/* | ||
Static compilation does not currently support streamline. | ||
Some Weirdness with Line Numbers | ||
@@ -118,2 +146,30 @@ -------------------------------- | ||
Also, it's worth noting a minor difference in the way coffee-coverage compiles statements. The | ||
following coffee code: | ||
if x | ||
a() | ||
else if y | ||
b() | ||
Would normally compile to: | ||
if(x) { | ||
a(); | ||
} else if(y) { | ||
b(); | ||
} | ||
coffeeCoverage will instead compile this to: | ||
if(x) { | ||
a(); | ||
} else { | ||
if(y) { | ||
b(); | ||
} | ||
} | ||
because otherwise it would be unable to annotate the `if(y)` statement. | ||
Detailed Usage | ||
@@ -120,0 +176,0 @@ -------------- |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Uses eval
Supply chain riskPackage uses eval() which is a dangerous function. This prevents the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
66654
634
222
13
5
1