mirror of
https://github.com/github/codeql.git
synced 2025-12-25 21:26:37 +01:00
Merge commit '737dd9d4c1' into jb1/lib/dataflowstack
This commit is contained in:
@@ -1,244 +1,253 @@
|
||||
package,sink,source,summary,sink:bean-validation,sink:command-injection,sink:credentials-key,sink:credentials-password,sink:credentials-username,sink:encryption-iv,sink:file-content-store,sink:fragment-injection,sink:groovy-injection,sink:hostname-verification,sink:html-injection,sink:information-leak,sink:intent-redirection,sink:jexl-injection,sink:jndi-injection,sink:js-injection,sink:ldap-injection,sink:log-injection,sink:mvel-injection,sink:ognl-injection,sink:path-injection,sink:pending-intents,sink:regex-use,sink:regex-use[-1],sink:regex-use[0],sink:regex-use[],sink:regex-use[f-1],sink:regex-use[f1],sink:regex-use[f],sink:request-forgery,sink:response-splitting,sink:sql-injection,sink:template-injection,sink:trust-boundary-violation,sink:url-redirection,sink:xpath-injection,sink:xslt-injection,source:android-external-storage-dir,source:contentprovider,source:database,source:environment,source:file,source:remote,summary:taint,summary:value
|
||||
actions.osgi,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,
|
||||
android.app,35,,103,,,,,,,,11,,,,,7,,,,,,,,,17,,,,,,,,,,,,,,,,,,,,,,18,85
|
||||
android.content,24,31,154,,,,,,,,,,,,,16,,,,,,,,,,,,,,,,,,,8,,,,,,4,27,,,,,63,91
|
||||
android.database,59,,41,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,59,,,,,,,,,,,,41,
|
||||
android.net,,,60,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,45,15
|
||||
android.os,,2,122,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,41,81
|
||||
android.support.v4.app,11,,,,,,,,,,11,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
android.util,6,16,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,16,,
|
||||
android.webkit,3,2,,,,,,,,,,,,2,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,
|
||||
android.widget,,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,1,
|
||||
androidx.core.app,6,,95,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,12,83
|
||||
androidx.fragment.app,11,,,,,,,,,,11,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
androidx.slice,2,5,88,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,5,,,,,27,61
|
||||
antlr,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
ch.ethz.ssh2,2,,,,,,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
cn.hutool.core.codec,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
com.alibaba.druid.sql,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
com.alibaba.fastjson2,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
com.amazonaws.auth,2,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.auth0.jwt.algorithms,6,,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.azure.identity,3,,,,,1,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.esotericsoftware.kryo.io,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
com.esotericsoftware.kryo5.io,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
com.fasterxml.jackson.core,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
com.fasterxml.jackson.databind,2,,6,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,6,
|
||||
com.google.common.base,4,,87,,,,,,,,,,,,,,,,,,,,,,,,,3,1,,,,,,,,,,,,,,,,,,63,24
|
||||
com.google.common.cache,,,17,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,17
|
||||
com.google.common.collect,,,553,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,551
|
||||
com.google.common.flogger,29,,,,,,,,,,,,,,,,,,,,29,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.google.common.io,8,,73,,,,,,,1,,,,,,,,,,,,,,7,,,,,,,,,,,,,,,,,,,,,,,72,1
|
||||
com.google.gson,,,44,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,30,14
|
||||
com.hubspot.jinjava,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,
|
||||
com.jcraft.jsch,5,,1,,,,2,2,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,1,
|
||||
com.microsoft.sqlserver.jdbc,4,,,,,,2,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.mitchellbosecke.pebble,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,
|
||||
com.mongodb,10,,,,,,4,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.opensymphony.xwork2,67,,961,,,,,,,,,,,,,,,,,,,,67,,,,,,,,,,,,,,,,,,,,,,,,867,94
|
||||
com.rabbitmq.client,,21,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,21,7,
|
||||
com.sshtools.j2ssh.authentication,3,,,,,,1,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.sun.crypto.provider,19,,,,,17,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.sun.jndi.ldap,4,,,,,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.sun.net.httpserver,3,,,,,,1,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.sun.net.ssl,3,,,,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.sun.rowset,3,,,,,,2,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.sun.security.auth.module,2,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.sun.security.ntlm,5,,,,,,3,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.sun.security.sasl.digest,3,,,,,,2,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.thoughtworks.xstream,1,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.trilead.ssh2,13,,,,,2,4,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.unboundid.ldap.sdk,17,,,,,,,,,,,,,,,,,,,17,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.zaxxer.hikari,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,
|
||||
flexjson,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1
|
||||
freemarker.cache,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,
|
||||
freemarker.template,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,7,,,,,,,,,,,,
|
||||
groovy.lang,26,,,,,,,,,,,26,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
groovy.text,1,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
groovy.util,5,,,,,,,,,,,5,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
hudson,68,9,2647,,4,,,,,3,,,,4,,,,,,,,,,51,,,,,,,,,6,,,,,,,,,,,,5,4,2571,76
|
||||
io.jsonwebtoken,,2,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,4,
|
||||
io.netty.bootstrap,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,,,,,,,,,,,,,,,
|
||||
io.netty.buffer,,,207,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,130,77
|
||||
io.netty.channel,9,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,,,,,,,,,,,,,2,,
|
||||
io.netty.handler.codec,4,13,259,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,3,,,,,,,,,,,,,13,143,116
|
||||
io.netty.handler.ssl,4,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
io.netty.handler.stream,1,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
io.netty.resolver,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
io.netty.util,2,,23,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,1,,,,,,,,,,,,,,21,2
|
||||
jakarta.activation,2,,2,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,1,,,,,,,,,,,,,,2,
|
||||
jakarta.faces.context,2,7,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,7,,
|
||||
jakarta.json,,,123,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,100,23
|
||||
jakarta.ws.rs.client,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,
|
||||
jakarta.ws.rs.container,,9,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,,
|
||||
jakarta.ws.rs.core,2,,149,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,94,55
|
||||
jakarta.xml.bind.attachment,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,
|
||||
java.awt,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3
|
||||
java.beans,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
java.io,50,1,46,,,,,,,22,,,,,,,,,,,,,,28,,,,,,,,,,,,,,,,,,,,,1,,44,2
|
||||
java.lang,31,3,94,,13,,,,,,,,,,,,,,,,8,,,5,,,4,,,1,,,,,,,,,,,,,,3,,,57,37
|
||||
java.net,15,3,23,,,,1,1,,,,,,,,,,,,,,,,,,,,,,,,,13,,,,,,,,,,,,,3,23,
|
||||
java.nio,49,,36,,,,,,,5,,,,,,,,,,,,,,43,,,,,,,,,1,,,,,,,,,,,,,,36,
|
||||
java.security,16,,,,,6,10,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
java.sql,15,1,2,,,,1,1,,,,,,,,,,,,,,,,,,,,,,,,,4,,9,,,,,,,,1,,,,2,
|
||||
java.util,45,2,519,,,,,,,1,,,,,,,,,,,34,,,,,,,5,2,,1,2,,,,,,,,,,,,2,,,45,474
|
||||
javafx.scene.web,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,
|
||||
javax.activation,2,,7,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,1,,,,,,,,,,,,,,7,
|
||||
javax.crypto,16,,4,,,11,3,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,
|
||||
javax.faces.context,2,7,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,7,,
|
||||
javax.imageio.stream,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
javax.jms,,9,57,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,57,
|
||||
javax.json,,,123,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,100,23
|
||||
javax.management,2,,1,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
javax.naming,7,,1,,,,,,,,,,,,,,,6,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
javax.net.ssl,4,,,,,,2,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
javax.portlet,,,61,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,61,
|
||||
javax.print.attribute.standard,2,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
javax.script,1,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
javax.security.auth.callback,1,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
javax.security.auth.kerberos,6,,,,,4,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
javax.servlet,7,21,2,,,,,,,,,,,,1,,,,,,,,,1,,,,,,,,,,3,,,2,,,,,,,,,21,2,
|
||||
javax.sql,7,,,,,,4,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
javax.validation,1,1,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,
|
||||
javax.ws.rs.client,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,
|
||||
javax.ws.rs.container,,9,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,,
|
||||
javax.ws.rs.core,3,,149,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,2,,,,,,,,,94,55
|
||||
javax.xml.bind.attachment,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,
|
||||
javax.xml.transform,2,,6,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,1,,,,,,,6,
|
||||
javax.xml.xpath,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,,,,,,,,,
|
||||
jenkins,,,523,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,500,23
|
||||
jodd.json,,,10,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,10
|
||||
kotlin,16,,1849,,,,,,,,,,,,,,,,,,,,,14,,,,,,,,,2,,,,,,,,,,,,,,1836,13
|
||||
net.schmizz.sshj,4,,,,,,2,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
net.sf.json,2,,338,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,321,17
|
||||
net.sf.saxon.s9api,5,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,5,,,,,,,,
|
||||
ognl,6,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
okhttp3,4,,50,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,,,,,23,27
|
||||
org.acegisecurity,,,49,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,49,
|
||||
org.antlr.runtime,1,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.commons.codec,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,
|
||||
org.apache.commons.collections,,,800,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,17,783
|
||||
org.apache.commons.collections4,,,800,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,17,783
|
||||
org.apache.commons.compress.archivers.tar,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,
|
||||
org.apache.commons.exec,6,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.commons.httpclient.util,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
org.apache.commons.io,111,,560,,,,,,,2,,,,,,,,,,,,,,94,,,,,,,,,15,,,,,,,,,,,,,,546,14
|
||||
org.apache.commons.jelly,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,
|
||||
org.apache.commons.jexl2,15,,,,,,,,,,,,,,,,15,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.commons.jexl3,15,,,,,,,,,,,,,,,,15,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.commons.lang,,,767,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,596,171
|
||||
org.apache.commons.lang3,6,,425,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,294,131
|
||||
org.apache.commons.logging,6,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.commons.net,13,12,,,,,2,2,,,,,,,,,,,,,,,,3,,,,,,,,,6,,,,,,,,,,,,,12,,
|
||||
org.apache.commons.ognl,6,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.commons.text,,,272,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,220,52
|
||||
org.apache.cxf.catalog,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,
|
||||
org.apache.cxf.common.classloader,3,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,2,,,,,,,,,,,,,,,
|
||||
org.apache.cxf.common.jaxb,1,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.cxf.common.logging,6,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.cxf.configuration.jsse,2,,,,,,,,,,,,1,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.cxf.helpers,10,,,,,,,,,,,,,,,,,,,,,,,5,,,,,,,,,,,,,,,5,,,,,,,,,
|
||||
org.apache.cxf.resource,9,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,5,,,,,,,,,,,,,,,
|
||||
org.apache.cxf.staxutils,1,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.cxf.tools.corba.utils,4,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.cxf.tools.util,10,,,,,,,,,,,,,,,,,,,,,,,10,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.cxf.transform,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,,,,,,,,
|
||||
org.apache.directory.ldap.client.api,1,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.hadoop.fs,,,10,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,10,
|
||||
org.apache.hadoop.hive.metastore,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,,,,,,,,,,,,,
|
||||
org.apache.hc.client5.http.async.methods,84,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,84,,,,,,,,,,,,,,,
|
||||
org.apache.hc.client5.http.classic.methods,37,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,37,,,,,,,,,,,,,,,
|
||||
org.apache.hc.client5.http.fluent,19,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,19,,,,,,,,,,,,,,,
|
||||
org.apache.hc.core5.benchmark,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,
|
||||
org.apache.hc.core5.function,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
org.apache.hc.core5.http,73,2,45,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,72,,,,,,,,,,,,,2,45,
|
||||
org.apache.hc.core5.net,,,18,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,18,
|
||||
org.apache.hc.core5.util,,,24,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,18,6
|
||||
org.apache.hive.hcatalog.templeton,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,
|
||||
org.apache.http,48,3,95,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,46,,,,,,,,,,,,,3,86,9
|
||||
org.apache.ibatis.jdbc,6,,57,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,57,
|
||||
org.apache.log4j,11,,,,,,,,,,,,,,,,,,,,11,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.logging.log4j,359,,8,,,,,,,,,,,,,,,,,,359,,,,,,,,,,,,,,,,,,,,,,,,,,4,4
|
||||
org.apache.shiro.codec,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
org.apache.shiro.jndi,1,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.shiro.mgt,1,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.sshd.client.session,3,,,,,,1,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.struts.beanvalidation.validation.interceptor,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,
|
||||
org.apache.struts2,14,,3873,,,,,,,,,,,,,,,,,,,,11,,,,,,,,,,,,,,3,,,,,,,,,,3839,34
|
||||
org.apache.tools.ant,11,,,,,,,,,,,,,,,,,,,,,,,11,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.tools.zip,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
org.apache.velocity.app,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,,,
|
||||
org.apache.velocity.runtime,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,,,
|
||||
org.codehaus.cargo.container.installer,3,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,1,,,,,,,,,,,,,,,
|
||||
org.codehaus.groovy.control,1,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.dom4j,20,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,20,,,,,,,,,
|
||||
org.eclipse.jetty.client,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,
|
||||
org.fusesource.leveldbjni,1,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.geogebra.web.full.main,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,
|
||||
org.gradle.api.file,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,
|
||||
org.hibernate,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,7,,,,,,,,,,,,,
|
||||
org.influxdb,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,
|
||||
org.jboss.logging,324,,,,,,,,,,,,,,,,,,,,324,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.jdbi.v3.core,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,
|
||||
org.jenkins.ui.icon,,,49,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,48,1
|
||||
org.jenkins.ui.symbol,,,33,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,25,8
|
||||
org.jooq,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,
|
||||
org.json,,,236,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,198,38
|
||||
org.kohsuke.stapler,20,24,363,,,,,,,,,,,2,,,,,,,,,,9,,,,,,,,,4,,,,,5,,,,,,,,24,352,11
|
||||
org.mvel2,16,,,,,,,,,,,,,,,,,,,,,16,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.openjdk.jmh.runner.options,1,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.owasp.esapi,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
org.scijava.log,13,,,,,,,,,,,,,,,,,,,,13,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.slf4j,55,,6,,,,,,,,,,,,,,,,,,55,,,,,,,,,,,,,,,,,,,,,,,,,,2,4
|
||||
org.springframework.beans,,,30,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,30
|
||||
org.springframework.boot.jdbc,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,
|
||||
org.springframework.cache,,,13,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,13
|
||||
org.springframework.context,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,
|
||||
org.springframework.core.io,2,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,1,,,,,,,,,,,,,,,
|
||||
org.springframework.data.repository,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1
|
||||
org.springframework.http,14,,77,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,14,,,,,,,,,,,,,,67,10
|
||||
org.springframework.jdbc.core,19,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,19,,,,,,,,,,,,,
|
||||
org.springframework.jdbc.datasource,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,,,,,,
|
||||
org.springframework.jdbc.object,9,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,,,,,,,,,,,,,
|
||||
org.springframework.jndi,1,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.springframework.ldap,47,,,,,,,,,,,,,,,,,33,,14,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.springframework.security.core.userdetails,2,,,,,,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.springframework.security.web.savedrequest,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,,
|
||||
org.springframework.ui,,,32,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,32
|
||||
org.springframework.util,3,,142,,,,,,,,,,,,,,,,,,,,,3,,,,,,,,,,,,,,,,,,,,,,,90,52
|
||||
org.springframework.validation,,,13,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,13,
|
||||
org.springframework.web.client,13,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,13,,,,,,,,,,,,,3,,
|
||||
org.springframework.web.context.request,,8,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,8,,
|
||||
org.springframework.web.multipart,,12,13,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,12,13,
|
||||
org.springframework.web.reactive.function.client,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,
|
||||
org.springframework.web.util,,,165,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,140,25
|
||||
org.thymeleaf,2,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,2,
|
||||
org.xml.sax,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
org.xmlpull.v1,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,,
|
||||
org.yaml.snakeyaml,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
play.libs.ws,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,
|
||||
play.mvc,1,13,24,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,13,24,
|
||||
ratpack.core.form,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,
|
||||
ratpack.core.handling,,6,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,4,
|
||||
ratpack.core.http,,10,10,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,10,10,
|
||||
ratpack.exec,,,48,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,48
|
||||
ratpack.form,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,
|
||||
ratpack.func,,,35,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,35
|
||||
ratpack.handling,,6,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,4,
|
||||
ratpack.http,,10,10,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,10,10,
|
||||
ratpack.util,,,35,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,35
|
||||
retrofit2,1,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,1,
|
||||
sun.jvmstat.perfdata.monitor.protocol.local,3,,,,,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.jvmstat.perfdata.monitor.protocol.rmi,1,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.misc,3,,,,,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.net.ftp,5,,,,,,2,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.net.www.protocol.http,3,,,,,,2,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.security.acl,1,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.security.jgss.krb5,2,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.security.krb5,9,,,,,3,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.security.pkcs,4,,,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.security.pkcs11,3,,,,,1,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.security.provider,2,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.security.ssl,3,,,,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.security.x509,1,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.tools.jconsole,28,,,,,,13,15,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
package,sink,source,summary,sink:bean-validation,sink:command-injection,sink:credentials-key,sink:credentials-password,sink:credentials-username,sink:encryption-iv,sink:encryption-salt,sink:environment-injection,sink:file-content-store,sink:fragment-injection,sink:groovy-injection,sink:hostname-verification,sink:html-injection,sink:information-leak,sink:intent-redirection,sink:jexl-injection,sink:jndi-injection,sink:js-injection,sink:ldap-injection,sink:log-injection,sink:mvel-injection,sink:ognl-injection,sink:path-injection,sink:pending-intents,sink:regex-use,sink:regex-use[-1],sink:regex-use[0],sink:regex-use[],sink:regex-use[f-1],sink:regex-use[f1],sink:regex-use[f],sink:request-forgery,sink:response-splitting,sink:sql-injection,sink:template-injection,sink:trust-boundary-violation,sink:url-redirection,sink:xpath-injection,sink:xslt-injection,source:android-external-storage-dir,source:contentprovider,source:database,source:environment,source:file,source:remote,summary:taint,summary:value
|
||||
actions.osgi,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,
|
||||
android.app,35,,103,,,,,,,,,,11,,,,,7,,,,,,,,,17,,,,,,,,,,,,,,,,,,,,,,18,85
|
||||
android.content,24,31,154,,,,,,,,,,,,,,,16,,,,,,,,,,,,,,,,,,,8,,,,,,4,27,,,,,63,91
|
||||
android.database,59,,41,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,59,,,,,,,,,,,,41,
|
||||
android.net,,,60,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,45,15
|
||||
android.os,,2,122,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,41,81
|
||||
android.support.v4.app,11,,,,,,,,,,,,11,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
android.util,6,16,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,16,,
|
||||
android.webkit,3,2,,,,,,,,,,,,,,2,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,
|
||||
android.widget,,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,1,
|
||||
androidx.core.app,6,,95,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,12,83
|
||||
androidx.fragment.app,11,,,,,,,,,,,,11,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
androidx.slice,2,5,88,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,5,,,,,27,61
|
||||
antlr,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
ch.ethz.ssh2,2,,,,,,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
cn.hutool.core.codec,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
com.alibaba.druid.sql,1,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,1,
|
||||
com.alibaba.fastjson2,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
com.amazonaws.auth,2,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.auth0.jwt.algorithms,6,,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.azure.identity,3,,,,,1,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.esotericsoftware.kryo.io,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
com.esotericsoftware.kryo5.io,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
com.fasterxml.jackson.core,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
com.fasterxml.jackson.databind,2,,6,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,6,
|
||||
com.google.common.base,4,,87,,,,,,,,,,,,,,,,,,,,,,,,,,,3,1,,,,,,,,,,,,,,,,,,63,24
|
||||
com.google.common.cache,,,17,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,17
|
||||
com.google.common.collect,,,553,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,551
|
||||
com.google.common.flogger,29,,,,,,,,,,,,,,,,,,,,,,29,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.google.common.io,10,,73,,,,,,,,,1,,,,,,,,,,,,,,9,,,,,,,,,,,,,,,,,,,,,,,72,1
|
||||
com.google.gson,,,52,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,38,14
|
||||
com.hubspot.jinjava,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,
|
||||
com.jcraft.jsch,5,,1,,,,2,2,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,1,
|
||||
com.microsoft.sqlserver.jdbc,4,,,,,,2,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.mitchellbosecke.pebble,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,
|
||||
com.mongodb,10,,,,,,4,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.opensymphony.xwork2,56,,961,,,,,,,,,,,,,,,,,,,,,,56,,,,,,,,,,,,,,,,,,,,,,,,867,94
|
||||
com.rabbitmq.client,,21,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,21,7,
|
||||
com.sshtools.j2ssh.authentication,3,,,,,,1,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.sun.crypto.provider,19,,,,,17,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.sun.jndi.ldap,4,,,,,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.sun.net.httpserver,3,,,,,,1,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.sun.net.ssl,3,,,,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.sun.rowset,3,,,,,,2,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.sun.security.auth.module,2,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.sun.security.ntlm,5,,,,,,3,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.sun.security.sasl.digest,3,,,,,,2,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.thoughtworks.xstream,1,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.trilead.ssh2,13,,,,,2,4,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.unboundid.ldap.sdk,17,,,,,,,,,,,,,,,,,,,,,17,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
com.zaxxer.hikari,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,
|
||||
flexjson,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1
|
||||
freemarker.cache,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,
|
||||
freemarker.template,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,7,,,,,,,,,,,,
|
||||
groovy.lang,26,,,,,,,,,,,,,26,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
groovy.text,1,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
groovy.util,5,,,,,,,,,,,,,5,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
hudson,74,9,2648,,4,,,,,,3,3,,,,4,,,,,,,,,,54,,,,,,,,,6,,,,,,,,,,,,5,4,2572,76
|
||||
io.jsonwebtoken,,2,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,4,
|
||||
io.netty.bootstrap,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,,,,,,,,,,,,,,,
|
||||
io.netty.buffer,,,207,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,130,77
|
||||
io.netty.channel,9,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,,,,,,,,,,,,,2,,
|
||||
io.netty.handler.codec,4,13,259,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,3,,,,,,,,,,,,,13,143,116
|
||||
io.netty.handler.ssl,4,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
io.netty.handler.stream,1,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
io.netty.resolver,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
io.netty.util,2,,23,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,1,,,,,,,,,,,,,,21,2
|
||||
jakarta.activation,2,,2,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,1,,,,,,,,,,,,,,2,
|
||||
jakarta.faces.context,2,7,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,7,,
|
||||
jakarta.json,,,123,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,100,23
|
||||
jakarta.persistence,2,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,1,
|
||||
jakarta.ws.rs.client,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,
|
||||
jakarta.ws.rs.container,,9,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,,
|
||||
jakarta.ws.rs.core,2,,149,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,94,55
|
||||
jakarta.xml.bind.attachment,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,
|
||||
java.awt,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3
|
||||
java.beans,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
java.io,50,1,46,,,,,,,,,22,,,,,,,,,,,,,,28,,,,,,,,,,,,,,,,,,,,,1,,44,2
|
||||
java.lang,33,3,103,,13,,,,,,1,,,,,,,,,,,,8,,,6,,,4,,,1,,,,,,,,,,,,,,3,,,60,43
|
||||
java.net,16,3,23,,,,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,14,,,,,,,,,,,,,3,23,
|
||||
java.nio,49,,36,,,,,,,,,5,,,,,,,,,,,,,,43,,,,,,,,,1,,,,,,,,,,,,,,36,
|
||||
java.security,21,,,,,11,10,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
java.sql,15,1,2,,,,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,4,,9,,,,,,,,1,,,,2,
|
||||
java.util,47,2,519,,,,,,,,,1,,,,,,,,,,,34,,,2,,,,5,2,,1,2,,,,,,,,,,,,2,,,45,474
|
||||
javafx.scene.web,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,
|
||||
javax.activation,2,,7,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,1,,,,,,,,,,,,,,7,
|
||||
javax.crypto,19,,4,,,12,3,,2,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,
|
||||
javax.faces.context,2,7,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,7,,
|
||||
javax.imageio.stream,1,,1,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
javax.jms,,9,57,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,57,
|
||||
javax.json,,,123,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,100,23
|
||||
javax.management,2,,1,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
javax.naming,7,,1,,,,,,,,,,,,,,,,,6,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
javax.net.ssl,4,,,,,,2,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
javax.portlet,,,61,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,61,
|
||||
javax.print.attribute.standard,2,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
javax.script,1,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
javax.security.auth.callback,1,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
javax.security.auth.kerberos,6,,,,,4,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
javax.servlet,7,21,2,,,,,,,,,,,,,,1,,,,,,,,,1,,,,,,,,,,3,,,2,,,,,,,,,21,2,
|
||||
javax.sql,7,,,,,,4,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
javax.validation,1,1,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,
|
||||
javax.ws.rs.client,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,
|
||||
javax.ws.rs.container,,9,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,,
|
||||
javax.ws.rs.core,3,,149,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,2,,,,,,,,,94,55
|
||||
javax.xml.bind.attachment,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,
|
||||
javax.xml.transform,2,,6,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,1,,,,,,,6,
|
||||
javax.xml.xpath,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,,,,,,,,,
|
||||
jenkins,,,523,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,500,23
|
||||
jodd.json,,,10,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,10
|
||||
kotlin,16,,1849,,,,,,,,,,,,,,,,,,,,,,,14,,,,,,,,,2,,,,,,,,,,,,,,1836,13
|
||||
liquibase.database.jvm,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,
|
||||
liquibase.statement.core,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,
|
||||
net.schmizz.sshj,4,,,,,,2,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
net.sf.json,2,,338,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,321,17
|
||||
net.sf.saxon.s9api,5,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,5,,,,,,,,
|
||||
ognl,6,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
okhttp3,4,,50,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,,,,,23,27
|
||||
org.acegisecurity,,,49,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,49,
|
||||
org.antlr.runtime,1,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.commons.codec,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,
|
||||
org.apache.commons.collections,,,800,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,17,783
|
||||
org.apache.commons.collections4,,,800,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,17,783
|
||||
org.apache.commons.compress.archivers.tar,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,
|
||||
org.apache.commons.exec,10,,,,6,,,,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.commons.httpclient.util,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
org.apache.commons.io,117,,562,,,,,,,,,4,,,,,,,,,,,,,,98,,,,,,,,,15,,,,,,,,,,,,,,548,14
|
||||
org.apache.commons.jelly,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,
|
||||
org.apache.commons.jexl2,15,,,,,,,,,,,,,,,,,,15,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.commons.jexl3,15,,,,,,,,,,,,,,,,,,15,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.commons.lang,,,767,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,596,171
|
||||
org.apache.commons.lang3,6,,425,,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,294,131
|
||||
org.apache.commons.logging,6,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.commons.net,13,12,,,,,2,2,,,,,,,,,,,,,,,,,,3,,,,,,,,,6,,,,,,,,,,,,,12,,
|
||||
org.apache.commons.ognl,6,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.commons.text,,,272,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,220,52
|
||||
org.apache.cxf.catalog,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,
|
||||
org.apache.cxf.common.classloader,3,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,2,,,,,,,,,,,,,,,
|
||||
org.apache.cxf.common.jaxb,1,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.cxf.common.logging,6,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.cxf.configuration.jsse,2,,,,,,,,,,,,,,1,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.cxf.helpers,10,,,,,,,,,,,,,,,,,,,,,,,,,5,,,,,,,,,,,,,,,5,,,,,,,,,
|
||||
org.apache.cxf.resource,9,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,5,,,,,,,,,,,,,,,
|
||||
org.apache.cxf.staxutils,1,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.cxf.tools.corba.utils,4,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.cxf.tools.util,10,,,,,,,,,,,,,,,,,,,,,,,,,10,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.cxf.transform,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,,,,,,,,
|
||||
org.apache.directory.ldap.client.api,1,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.hadoop.fs,,,10,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,10,
|
||||
org.apache.hadoop.hive.metastore,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,,,,,,,,,,,,,
|
||||
org.apache.hadoop.hive.ql.exec,1,,1,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
org.apache.hadoop.hive.ql.metadata,1,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.hc.client5.http.async.methods,84,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,84,,,,,,,,,,,,,,,
|
||||
org.apache.hc.client5.http.classic.methods,37,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,37,,,,,,,,,,,,,,,
|
||||
org.apache.hc.client5.http.fluent,19,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,19,,,,,,,,,,,,,,,
|
||||
org.apache.hc.core5.benchmark,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,
|
||||
org.apache.hc.core5.function,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
org.apache.hc.core5.http,73,2,45,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,72,,,,,,,,,,,,,2,45,
|
||||
org.apache.hc.core5.net,,,18,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,18,
|
||||
org.apache.hc.core5.util,,,24,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,18,6
|
||||
org.apache.hive.hcatalog.templeton,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,
|
||||
org.apache.http,48,3,95,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,46,,,,,,,,,,,,,3,86,9
|
||||
org.apache.ibatis.jdbc,6,,57,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,57,
|
||||
org.apache.ibatis.mapping,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
org.apache.log4j,11,,,,,,,,,,,,,,,,,,,,,,11,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.logging.log4j,359,,8,,,,,,,,,,,,,,,,,,,,359,,,,,,,,,,,,,,,,,,,,,,,,,,4,4
|
||||
org.apache.shiro.codec,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
org.apache.shiro.jndi,1,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.shiro.mgt,1,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.sshd.client.session,3,,,,,,1,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.struts.beanvalidation.validation.interceptor,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,
|
||||
org.apache.struts2,14,,3873,,,,,,,,,,,,,,,,,,,,,,11,,,,,,,,,,,,,,3,,,,,,,,,,3839,34
|
||||
org.apache.tools.ant,12,,,,1,,,,,,,,,,,,,,,,,,,,,11,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.apache.tools.zip,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
org.apache.velocity.app,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,,,
|
||||
org.apache.velocity.runtime,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,,,
|
||||
org.codehaus.cargo.container.installer,3,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,1,,,,,,,,,,,,,,,
|
||||
org.codehaus.groovy.control,1,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.dom4j,20,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,20,,,,,,,,,
|
||||
org.eclipse.jetty.client,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,
|
||||
org.fusesource.leveldbjni,1,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.geogebra.web.full.main,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,
|
||||
org.gradle.api.file,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,
|
||||
org.hibernate,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,7,,,,,,,,,,,,,
|
||||
org.influxdb,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,
|
||||
org.jboss.logging,324,,,,,,,,,,,,,,,,,,,,,,324,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.jdbi.v3.core,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,
|
||||
org.jenkins.ui.icon,,,49,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,48,1
|
||||
org.jenkins.ui.symbol,,,33,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,25,8
|
||||
org.jooq,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,
|
||||
org.json,,,236,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,198,38
|
||||
org.keycloak.models.map.storage,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,
|
||||
org.kohsuke.stapler,20,24,363,,,,,,,,,,,,,2,,,,,,,,,,9,,,,,,,,,4,,,,,5,,,,,,,,24,352,11
|
||||
org.mvel2,16,,,,,,,,,,,,,,,,,,,,,,,16,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.openjdk.jmh.runner.options,1,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.owasp.esapi,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
org.pac4j.jwt.config.encryption,4,,,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.pac4j.jwt.config.signature,4,,,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.scijava.log,13,,,,,,,,,,,,,,,,,,,,,,13,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.slf4j,55,,6,,,,,,,,,,,,,,,,,,,,55,,,,,,,,,,,,,,,,,,,,,,,,,,2,4
|
||||
org.springframework.beans,,,30,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,30
|
||||
org.springframework.boot.jdbc,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,
|
||||
org.springframework.cache,,,13,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,13
|
||||
org.springframework.context,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,
|
||||
org.springframework.core.io,3,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,1,,,,,,,,,,,,,,,
|
||||
org.springframework.data.repository,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1
|
||||
org.springframework.http,14,,77,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,14,,,,,,,,,,,,,,67,10
|
||||
org.springframework.jdbc.core,19,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,19,,,,,,,,,,,,,
|
||||
org.springframework.jdbc.datasource,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,,,,,,
|
||||
org.springframework.jdbc.object,9,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,,,,,,,,,,,,,
|
||||
org.springframework.jndi,1,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.springframework.ldap,47,,,,,,,,,,,,,,,,,,,33,,14,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.springframework.security.core.userdetails,2,,,,,,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
org.springframework.security.web.savedrequest,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,,
|
||||
org.springframework.ui,,,32,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,32
|
||||
org.springframework.util,3,,142,,,,,,,,,,,,,,,,,,,,,,,3,,,,,,,,,,,,,,,,,,,,,,,90,52
|
||||
org.springframework.validation,,,13,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,13,
|
||||
org.springframework.web.client,13,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,13,,,,,,,,,,,,,3,,
|
||||
org.springframework.web.context.request,,8,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,8,,
|
||||
org.springframework.web.multipart,,12,13,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,12,13,
|
||||
org.springframework.web.reactive.function.client,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,
|
||||
org.springframework.web.util,,9,157,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,132,25
|
||||
org.thymeleaf,2,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,2,
|
||||
org.xml.sax,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
org.xmlpull.v1,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,,
|
||||
org.yaml.snakeyaml,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
play.libs.ws,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,
|
||||
play.mvc,1,13,24,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,13,24,
|
||||
ratpack.core.form,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,
|
||||
ratpack.core.handling,,6,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,4,
|
||||
ratpack.core.http,,10,10,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,10,10,
|
||||
ratpack.exec,,,48,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,48
|
||||
ratpack.form,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,
|
||||
ratpack.func,,,35,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,35
|
||||
ratpack.handling,,6,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,4,
|
||||
ratpack.http,,10,10,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,10,10,
|
||||
ratpack.util,,,35,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,35
|
||||
retrofit2,1,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,1,
|
||||
sun.jvmstat.perfdata.monitor.protocol.local,3,,,,,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.jvmstat.perfdata.monitor.protocol.rmi,1,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.misc,3,,,,,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.net.ftp,5,,,,,,2,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.net.www.protocol.http,3,,,,,,2,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.security.acl,1,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.security.jgss.krb5,2,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.security.krb5,9,,,,,3,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.security.pkcs,4,,,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.security.pkcs11,3,,,,,1,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.security.provider,2,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.security.ssl,3,,,,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.security.x509,1,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
sun.tools.jconsole,28,,,,,,13,15,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
|
||||
|
@@ -10,18 +10,18 @@ Java framework & library support
|
||||
Android,``android.*``,52,481,138,,3,67,,,
|
||||
Android extensions,``androidx.*``,5,183,19,,,,,,
|
||||
`Apache Commons Collections <https://commons.apache.org/proper/commons-collections/>`_,"``org.apache.commons.collections``, ``org.apache.commons.collections4``",,1600,,,,,,,
|
||||
`Apache Commons IO <https://commons.apache.org/proper/commons-io/>`_,``org.apache.commons.io``,,560,111,94,,,,,15
|
||||
`Apache Commons IO <https://commons.apache.org/proper/commons-io/>`_,``org.apache.commons.io``,,562,117,98,,,,,15
|
||||
`Apache Commons Lang <https://commons.apache.org/proper/commons-lang/>`_,``org.apache.commons.lang3``,,425,6,,,,,,
|
||||
`Apache Commons Text <https://commons.apache.org/proper/commons-text/>`_,``org.apache.commons.text``,,272,,,,,,,
|
||||
`Apache HttpComponents <https://hc.apache.org/>`_,"``org.apache.hc.core5.*``, ``org.apache.http``",5,183,122,,3,,,,119
|
||||
`Apache Log4j 2 <https://logging.apache.org/log4j/2.0/>`_,``org.apache.logging.log4j``,,8,359,,,,,,
|
||||
`Google Guava <https://guava.dev/>`_,``com.google.common.*``,,730,41,7,,,,,
|
||||
`Google Guava <https://guava.dev/>`_,``com.google.common.*``,,730,43,9,,,,,
|
||||
JBoss Logging,``org.jboss.logging``,,,324,,,,,,
|
||||
`JSON-java <https://github.com/stleary/JSON-java>`_,``org.json``,,236,,,,,,,
|
||||
Java Standard Library,``java.*``,10,724,221,76,,9,,,18
|
||||
Java extensions,"``javax.*``, ``jakarta.*``",67,686,74,4,4,,1,1,4
|
||||
Java Standard Library,``java.*``,10,733,231,79,,9,,,19
|
||||
Java extensions,"``javax.*``, ``jakarta.*``",67,687,80,5,4,2,1,1,4
|
||||
Kotlin Standard Library,``kotlin*``,,1849,16,14,,,,,2
|
||||
`Spring <https://spring.io/>`_,``org.springframework.*``,29,489,117,4,,28,14,,35
|
||||
Others,"``actions.osgi``, ``antlr``, ``ch.ethz.ssh2``, ``cn.hutool.core.codec``, ``com.alibaba.druid.sql``, ``com.alibaba.fastjson2``, ``com.amazonaws.auth``, ``com.auth0.jwt.algorithms``, ``com.azure.identity``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.google.gson``, ``com.hubspot.jinjava``, ``com.jcraft.jsch``, ``com.microsoft.sqlserver.jdbc``, ``com.mitchellbosecke.pebble``, ``com.mongodb``, ``com.opensymphony.xwork2``, ``com.rabbitmq.client``, ``com.sshtools.j2ssh.authentication``, ``com.sun.crypto.provider``, ``com.sun.jndi.ldap``, ``com.sun.net.httpserver``, ``com.sun.net.ssl``, ``com.sun.rowset``, ``com.sun.security.auth.module``, ``com.sun.security.ntlm``, ``com.sun.security.sasl.digest``, ``com.thoughtworks.xstream``, ``com.trilead.ssh2``, ``com.unboundid.ldap.sdk``, ``com.zaxxer.hikari``, ``flexjson``, ``freemarker.cache``, ``freemarker.template``, ``groovy.lang``, ``groovy.text``, ``groovy.util``, ``hudson``, ``io.jsonwebtoken``, ``io.netty.bootstrap``, ``io.netty.buffer``, ``io.netty.channel``, ``io.netty.handler.codec``, ``io.netty.handler.ssl``, ``io.netty.handler.stream``, ``io.netty.resolver``, ``io.netty.util``, ``javafx.scene.web``, ``jenkins``, ``jodd.json``, ``net.schmizz.sshj``, ``net.sf.json``, ``net.sf.saxon.s9api``, ``ognl``, ``okhttp3``, ``org.acegisecurity``, ``org.antlr.runtime``, ``org.apache.commons.codec``, ``org.apache.commons.compress.archivers.tar``, ``org.apache.commons.exec``, ``org.apache.commons.httpclient.util``, ``org.apache.commons.jelly``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.lang``, ``org.apache.commons.logging``, ``org.apache.commons.net``, ``org.apache.commons.ognl``, ``org.apache.cxf.catalog``, ``org.apache.cxf.common.classloader``, ``org.apache.cxf.common.jaxb``, ``org.apache.cxf.common.logging``, ``org.apache.cxf.configuration.jsse``, ``org.apache.cxf.helpers``, ``org.apache.cxf.resource``, ``org.apache.cxf.staxutils``, ``org.apache.cxf.tools.corba.utils``, ``org.apache.cxf.tools.util``, ``org.apache.cxf.transform``, ``org.apache.directory.ldap.client.api``, ``org.apache.hadoop.fs``, ``org.apache.hadoop.hive.metastore``, ``org.apache.hc.client5.http.async.methods``, ``org.apache.hc.client5.http.classic.methods``, ``org.apache.hc.client5.http.fluent``, ``org.apache.hive.hcatalog.templeton``, ``org.apache.ibatis.jdbc``, ``org.apache.log4j``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.apache.shiro.mgt``, ``org.apache.sshd.client.session``, ``org.apache.struts.beanvalidation.validation.interceptor``, ``org.apache.struts2``, ``org.apache.tools.ant``, ``org.apache.tools.zip``, ``org.apache.velocity.app``, ``org.apache.velocity.runtime``, ``org.codehaus.cargo.container.installer``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.eclipse.jetty.client``, ``org.fusesource.leveldbjni``, ``org.geogebra.web.full.main``, ``org.gradle.api.file``, ``org.hibernate``, ``org.influxdb``, ``org.jdbi.v3.core``, ``org.jenkins.ui.icon``, ``org.jenkins.ui.symbol``, ``org.jooq``, ``org.kohsuke.stapler``, ``org.mvel2``, ``org.openjdk.jmh.runner.options``, ``org.owasp.esapi``, ``org.scijava.log``, ``org.slf4j``, ``org.thymeleaf``, ``org.xml.sax``, ``org.xmlpull.v1``, ``org.yaml.snakeyaml``, ``play.libs.ws``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``retrofit2``, ``sun.jvmstat.perfdata.monitor.protocol.local``, ``sun.jvmstat.perfdata.monitor.protocol.rmi``, ``sun.misc``, ``sun.net.ftp``, ``sun.net.www.protocol.http``, ``sun.security.acl``, ``sun.security.jgss.krb5``, ``sun.security.krb5``, ``sun.security.pkcs``, ``sun.security.pkcs11``, ``sun.security.provider``, ``sun.security.ssl``, ``sun.security.x509``, ``sun.tools.jconsole``",131,10503,875,116,6,18,18,,208
|
||||
Totals,,299,18929,2423,315,16,122,33,1,401
|
||||
`Spring <https://spring.io/>`_,``org.springframework.*``,38,481,118,5,,28,14,,35
|
||||
Others,"``actions.osgi``, ``antlr``, ``ch.ethz.ssh2``, ``cn.hutool.core.codec``, ``com.alibaba.druid.sql``, ``com.alibaba.fastjson2``, ``com.amazonaws.auth``, ``com.auth0.jwt.algorithms``, ``com.azure.identity``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.google.gson``, ``com.hubspot.jinjava``, ``com.jcraft.jsch``, ``com.microsoft.sqlserver.jdbc``, ``com.mitchellbosecke.pebble``, ``com.mongodb``, ``com.opensymphony.xwork2``, ``com.rabbitmq.client``, ``com.sshtools.j2ssh.authentication``, ``com.sun.crypto.provider``, ``com.sun.jndi.ldap``, ``com.sun.net.httpserver``, ``com.sun.net.ssl``, ``com.sun.rowset``, ``com.sun.security.auth.module``, ``com.sun.security.ntlm``, ``com.sun.security.sasl.digest``, ``com.thoughtworks.xstream``, ``com.trilead.ssh2``, ``com.unboundid.ldap.sdk``, ``com.zaxxer.hikari``, ``flexjson``, ``freemarker.cache``, ``freemarker.template``, ``groovy.lang``, ``groovy.text``, ``groovy.util``, ``hudson``, ``io.jsonwebtoken``, ``io.netty.bootstrap``, ``io.netty.buffer``, ``io.netty.channel``, ``io.netty.handler.codec``, ``io.netty.handler.ssl``, ``io.netty.handler.stream``, ``io.netty.resolver``, ``io.netty.util``, ``javafx.scene.web``, ``jenkins``, ``jodd.json``, ``liquibase.database.jvm``, ``liquibase.statement.core``, ``net.schmizz.sshj``, ``net.sf.json``, ``net.sf.saxon.s9api``, ``ognl``, ``okhttp3``, ``org.acegisecurity``, ``org.antlr.runtime``, ``org.apache.commons.codec``, ``org.apache.commons.compress.archivers.tar``, ``org.apache.commons.exec``, ``org.apache.commons.httpclient.util``, ``org.apache.commons.jelly``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.lang``, ``org.apache.commons.logging``, ``org.apache.commons.net``, ``org.apache.commons.ognl``, ``org.apache.cxf.catalog``, ``org.apache.cxf.common.classloader``, ``org.apache.cxf.common.jaxb``, ``org.apache.cxf.common.logging``, ``org.apache.cxf.configuration.jsse``, ``org.apache.cxf.helpers``, ``org.apache.cxf.resource``, ``org.apache.cxf.staxutils``, ``org.apache.cxf.tools.corba.utils``, ``org.apache.cxf.tools.util``, ``org.apache.cxf.transform``, ``org.apache.directory.ldap.client.api``, ``org.apache.hadoop.fs``, ``org.apache.hadoop.hive.metastore``, ``org.apache.hadoop.hive.ql.exec``, ``org.apache.hadoop.hive.ql.metadata``, ``org.apache.hc.client5.http.async.methods``, ``org.apache.hc.client5.http.classic.methods``, ``org.apache.hc.client5.http.fluent``, ``org.apache.hive.hcatalog.templeton``, ``org.apache.ibatis.jdbc``, ``org.apache.ibatis.mapping``, ``org.apache.log4j``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.apache.shiro.mgt``, ``org.apache.sshd.client.session``, ``org.apache.struts.beanvalidation.validation.interceptor``, ``org.apache.struts2``, ``org.apache.tools.ant``, ``org.apache.tools.zip``, ``org.apache.velocity.app``, ``org.apache.velocity.runtime``, ``org.codehaus.cargo.container.installer``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.eclipse.jetty.client``, ``org.fusesource.leveldbjni``, ``org.geogebra.web.full.main``, ``org.gradle.api.file``, ``org.hibernate``, ``org.influxdb``, ``org.jdbi.v3.core``, ``org.jenkins.ui.icon``, ``org.jenkins.ui.symbol``, ``org.jooq``, ``org.keycloak.models.map.storage``, ``org.kohsuke.stapler``, ``org.mvel2``, ``org.openjdk.jmh.runner.options``, ``org.owasp.esapi``, ``org.pac4j.jwt.config.encryption``, ``org.pac4j.jwt.config.signature``, ``org.scijava.log``, ``org.slf4j``, ``org.thymeleaf``, ``org.xml.sax``, ``org.xmlpull.v1``, ``org.yaml.snakeyaml``, ``play.libs.ws``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``retrofit2``, ``sun.jvmstat.perfdata.monitor.protocol.local``, ``sun.jvmstat.perfdata.monitor.protocol.rmi``, ``sun.misc``, ``sun.net.ftp``, ``sun.net.www.protocol.http``, ``sun.security.acl``, ``sun.security.jgss.krb5``, ``sun.security.krb5``, ``sun.security.pkcs``, ``sun.security.pkcs11``, ``sun.security.provider``, ``sun.security.ssl``, ``sun.security.x509``, ``sun.tools.jconsole``",131,10514,889,121,6,22,18,,208
|
||||
Totals,,308,18944,2462,331,16,128,33,1,402
|
||||
|
||||
|
||||
37
java/integration-tests-lib/buildless_test_utils.py
Normal file
37
java/integration-tests-lib/buildless_test_utils.py
Normal file
@@ -0,0 +1,37 @@
|
||||
import sys
|
||||
import os.path
|
||||
import glob
|
||||
|
||||
def extract_fetched_jar_path(l):
|
||||
if not l.startswith("["):
|
||||
# Line continuation
|
||||
return None
|
||||
bits = l.split(" ", 3) # date time processid logline
|
||||
if len(bits) >= 4 and bits[3].startswith("Fetch "):
|
||||
return bits[3][6:].strip()
|
||||
else:
|
||||
return None
|
||||
|
||||
def read_fetched_jars(fname):
|
||||
with open(fname, "r") as f:
|
||||
lines = [l for l in f]
|
||||
return [l for l in map(extract_fetched_jar_path, lines) if l is not None]
|
||||
|
||||
def check_buildless_fetches():
|
||||
|
||||
extractor_logs = glob.glob(os.path.join("test-db", "log", "javac-extractor-*.log"))
|
||||
fetched_jars = map(read_fetched_jars, extractor_logs)
|
||||
all_fetched_jars = tuple(sorted([item for sublist in fetched_jars for item in sublist]))
|
||||
|
||||
try:
|
||||
with open("buildless-fetches.expected", "r") as f:
|
||||
expected_jar_fetches = tuple(l.strip() for l in f)
|
||||
except FileNotFoundError:
|
||||
expected_jar_fetches = tuple()
|
||||
|
||||
if all_fetched_jars != expected_jar_fetches:
|
||||
print("Expected jar fetch mismatch. Expected:\n%s\n\nActual:\n%s" % ("\n".join(expected_jar_fetches), "\n".join(all_fetched_jars)), file = sys.stderr)
|
||||
with open("buildless-fetches.actual", "w") as f:
|
||||
for j in all_fetched_jars:
|
||||
f.write(j + "\n")
|
||||
sys.exit(1)
|
||||
@@ -46,7 +46,7 @@ def version_string_to_version(version):
|
||||
# Version number used by CI.
|
||||
ci_version = '1.9.0'
|
||||
|
||||
many_versions = [ '1.5.0', '1.5.10', '1.5.20', '1.5.30', '1.6.0', '1.6.20', '1.7.0', '1.7.20', '1.8.0', '1.9.0-Beta', '1.9.20-Beta', '2.0.0-Beta1' ]
|
||||
many_versions = [ '1.5.0', '1.5.10', '1.5.20', '1.5.30', '1.6.0', '1.6.20', '1.7.0', '1.7.20', '1.8.0', '1.9.0-Beta', '1.9.20-Beta', '2.0.0-Beta1', '2.0.255-SNAPSHOT' ]
|
||||
|
||||
many_versions_versions = [version_string_to_version(v) for v in many_versions]
|
||||
many_versions_versions_asc = sorted(many_versions_versions, key = lambda v: v.toTupleWithTag())
|
||||
|
||||
@@ -3,18 +3,26 @@ package com.github.codeql
|
||||
import com.github.codeql.utils.isExternalFileClassMember
|
||||
import com.semmle.extractor.java.OdasaOutput
|
||||
import com.semmle.util.data.StringDigestor
|
||||
import java.io.BufferedWriter
|
||||
import java.io.File
|
||||
import java.util.ArrayList
|
||||
import java.util.HashSet
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.util.isFileClass
|
||||
import org.jetbrains.kotlin.ir.util.packageFqName
|
||||
import java.io.BufferedWriter
|
||||
import java.io.File
|
||||
import java.util.ArrayList
|
||||
import java.util.HashSet
|
||||
import java.util.zip.GZIPOutputStream
|
||||
|
||||
class ExternalDeclExtractor(val logger: FileLogger, val compression: Compression, val invocationTrapFile: String, val sourceFilePath: String, val primitiveTypeMapping: PrimitiveTypeMapping, val pluginContext: IrPluginContext, val globalExtensionState: KotlinExtractorGlobalState, val diagnosticTrapWriter: DiagnosticTrapWriter) {
|
||||
class ExternalDeclExtractor(
|
||||
val logger: FileLogger,
|
||||
val compression: Compression,
|
||||
val invocationTrapFile: String,
|
||||
val sourceFilePath: String,
|
||||
val primitiveTypeMapping: PrimitiveTypeMapping,
|
||||
val pluginContext: IrPluginContext,
|
||||
val globalExtensionState: KotlinExtractorGlobalState,
|
||||
val diagnosticTrapWriter: DiagnosticTrapWriter
|
||||
) {
|
||||
|
||||
val declBinaryNames = HashMap<IrDeclaration, String>()
|
||||
val externalDeclsDone = HashSet<Pair<String, String>>()
|
||||
@@ -23,13 +31,17 @@ class ExternalDeclExtractor(val logger: FileLogger, val compression: Compression
|
||||
val propertySignature = ";property"
|
||||
val fieldSignature = ";field"
|
||||
|
||||
val output = OdasaOutput(false, compression, logger).also {
|
||||
it.setCurrentSourceFile(File(sourceFilePath))
|
||||
}
|
||||
val output =
|
||||
OdasaOutput(false, compression, logger).also {
|
||||
it.setCurrentSourceFile(File(sourceFilePath))
|
||||
}
|
||||
|
||||
fun extractLater(d: IrDeclarationWithName, signature: String): Boolean {
|
||||
if (d !is IrClass && !isExternalFileClassMember(d)) {
|
||||
logger.errorElement("External declaration is neither a class, nor a top-level declaration", d)
|
||||
logger.errorElement(
|
||||
"External declaration is neither a class, nor a top-level declaration",
|
||||
d
|
||||
)
|
||||
return false
|
||||
}
|
||||
val declBinaryName = declBinaryNames.getOrPut(d) { getIrElementBinaryName(d) }
|
||||
@@ -37,34 +49,54 @@ class ExternalDeclExtractor(val logger: FileLogger, val compression: Compression
|
||||
if (ret) externalDeclWorkList.add(Pair(d, signature))
|
||||
return ret
|
||||
}
|
||||
|
||||
fun extractLater(c: IrClass) = extractLater(c, "")
|
||||
|
||||
fun writeStubTrapFile(e: IrElement, signature: String = "") {
|
||||
extractElement(e, signature, true) { trapFileBW, _, _ ->
|
||||
trapFileBW.write("// Trap file stubbed because this declaration was extracted from source in $sourceFilePath\n")
|
||||
trapFileBW.write(
|
||||
"// Trap file stubbed because this declaration was extracted from source in $sourceFilePath\n"
|
||||
)
|
||||
trapFileBW.write("// Part of invocation $invocationTrapFile\n")
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractElement(element: IrElement, possiblyLongSignature: String, fromSource: Boolean, extractorFn: (BufferedWriter, String, OdasaOutput.TrapFileManager) -> Unit) {
|
||||
// In order to avoid excessively long signatures which can lead to trap file names longer than the filesystem
|
||||
private fun extractElement(
|
||||
element: IrElement,
|
||||
possiblyLongSignature: String,
|
||||
fromSource: Boolean,
|
||||
extractorFn: (BufferedWriter, String, OdasaOutput.TrapFileManager) -> Unit
|
||||
) {
|
||||
// In order to avoid excessively long signatures which can lead to trap file names longer
|
||||
// than the filesystem
|
||||
// limit, we truncate and add a hash to preserve uniqueness if necessary.
|
||||
val signature = if (possiblyLongSignature.length > 100) {
|
||||
possiblyLongSignature.substring(0, 92) + "#" + StringDigestor.digest(possiblyLongSignature).substring(0, 8)
|
||||
} else { possiblyLongSignature }
|
||||
val signature =
|
||||
if (possiblyLongSignature.length > 100) {
|
||||
possiblyLongSignature.substring(0, 92) +
|
||||
"#" +
|
||||
StringDigestor.digest(possiblyLongSignature).substring(0, 8)
|
||||
} else {
|
||||
possiblyLongSignature
|
||||
}
|
||||
output.getTrapLockerForDecl(element, signature, fromSource).useAC { locker ->
|
||||
locker.trapFileManager.useAC { manager ->
|
||||
val shortName = when(element) {
|
||||
is IrDeclarationWithName -> element.name.asString()
|
||||
is IrFile -> element.name
|
||||
else -> "(unknown name)"
|
||||
}
|
||||
val shortName =
|
||||
when (element) {
|
||||
is IrDeclarationWithName -> element.name.asString()
|
||||
is IrFile -> element.name
|
||||
else -> "(unknown name)"
|
||||
}
|
||||
if (manager == null) {
|
||||
logger.info("Skipping extracting external decl $shortName")
|
||||
} else {
|
||||
val trapFile = manager.file
|
||||
logger.info("Will write TRAP file $trapFile")
|
||||
val trapTmpFile = File.createTempFile("${trapFile.nameWithoutExtension}.", ".${trapFile.extension}.tmp", trapFile.parentFile)
|
||||
val trapTmpFile =
|
||||
File.createTempFile(
|
||||
"${trapFile.nameWithoutExtension}.",
|
||||
".${trapFile.extension}.tmp",
|
||||
trapFile.parentFile
|
||||
)
|
||||
logger.debug("Writing temporary TRAP file $trapTmpFile")
|
||||
try {
|
||||
compression.bufferedWriter(trapTmpFile).use {
|
||||
@@ -77,7 +109,10 @@ class ExternalDeclExtractor(val logger: FileLogger, val compression: Compression
|
||||
logger.info("Finished writing TRAP file $trapFile")
|
||||
} catch (e: Exception) {
|
||||
manager.setHasError()
|
||||
logger.error("Failed to extract '$shortName'. Partial TRAP file location is $trapTmpFile", e)
|
||||
logger.error(
|
||||
"Failed to extract '$shortName'. Partial TRAP file location is $trapTmpFile",
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,37 +125,75 @@ class ExternalDeclExtractor(val logger: FileLogger, val compression: Compression
|
||||
externalDeclWorkList.clear()
|
||||
nextBatch.forEach { workPair ->
|
||||
val (irDecl, possiblyLongSignature) = workPair
|
||||
extractElement(irDecl, possiblyLongSignature, false) { trapFileBW, signature, manager ->
|
||||
extractElement(irDecl, possiblyLongSignature, false) {
|
||||
trapFileBW,
|
||||
signature,
|
||||
manager ->
|
||||
val binaryPath = getIrDeclarationBinaryPath(irDecl)
|
||||
if (binaryPath == null) {
|
||||
logger.errorElement("Unable to get binary path", irDecl)
|
||||
} else {
|
||||
// We want our comments to be the first thing in the file,
|
||||
// so start off with a PlainTrapWriter
|
||||
val tw = PlainTrapWriter(logger.loggerBase, TrapLabelManager(), trapFileBW, diagnosticTrapWriter)
|
||||
tw.writeComment("Generated by the CodeQL Kotlin extractor for external dependencies")
|
||||
val tw =
|
||||
PlainTrapWriter(
|
||||
logger.loggerBase,
|
||||
TrapLabelManager(),
|
||||
trapFileBW,
|
||||
diagnosticTrapWriter
|
||||
)
|
||||
tw.writeComment(
|
||||
"Generated by the CodeQL Kotlin extractor for external dependencies"
|
||||
)
|
||||
tw.writeComment("Part of invocation $invocationTrapFile")
|
||||
if (signature != possiblyLongSignature) {
|
||||
tw.writeComment("Function signature abbreviated; full signature is: $possiblyLongSignature")
|
||||
tw.writeComment(
|
||||
"Function signature abbreviated; full signature is: $possiblyLongSignature"
|
||||
)
|
||||
}
|
||||
// Now elevate to a SourceFileTrapWriter, and populate the
|
||||
// file information if needed:
|
||||
val ftw = tw.makeFileTrapWriter(binaryPath, true)
|
||||
|
||||
val fileExtractor = KotlinFileExtractor(logger, ftw, null, binaryPath, manager, this, primitiveTypeMapping, pluginContext, KotlinFileExtractor.DeclarationStack(), globalExtensionState)
|
||||
val fileExtractor =
|
||||
KotlinFileExtractor(
|
||||
logger,
|
||||
ftw,
|
||||
null,
|
||||
binaryPath,
|
||||
manager,
|
||||
this,
|
||||
primitiveTypeMapping,
|
||||
pluginContext,
|
||||
KotlinFileExtractor.DeclarationStack(),
|
||||
globalExtensionState
|
||||
)
|
||||
|
||||
if (irDecl is IrClass) {
|
||||
// Populate a location and compilation-unit package for the file. This is similar to
|
||||
// the beginning of `KotlinFileExtractor.extractFileContents` but without an `IrFile`
|
||||
// Populate a location and compilation-unit package for the file. This
|
||||
// is similar to
|
||||
// the beginning of `KotlinFileExtractor.extractFileContents` but
|
||||
// without an `IrFile`
|
||||
// to start from.
|
||||
val pkg = irDecl.packageFqName?.asString() ?: ""
|
||||
val pkgId = fileExtractor.extractPackage(pkg)
|
||||
ftw.writeHasLocation(ftw.fileId, ftw.getWholeFileLocation())
|
||||
ftw.writeCupackage(ftw.fileId, pkgId)
|
||||
|
||||
fileExtractor.extractClassSource(irDecl, extractDeclarations = !irDecl.isFileClass, extractStaticInitializer = false, extractPrivateMembers = false, extractFunctionBodies = false)
|
||||
fileExtractor.extractClassSource(
|
||||
irDecl,
|
||||
extractDeclarations = !irDecl.isFileClass,
|
||||
extractStaticInitializer = false,
|
||||
extractPrivateMembers = false,
|
||||
extractFunctionBodies = false
|
||||
)
|
||||
} else {
|
||||
fileExtractor.extractDeclaration(irDecl, extractPrivateMembers = false, extractFunctionBodies = false, extractAnnotations = true)
|
||||
fileExtractor.extractDeclaration(
|
||||
irDecl,
|
||||
extractPrivateMembers = false,
|
||||
extractFunctionBodies = false,
|
||||
extractAnnotations = true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,5 +201,4 @@ class ExternalDeclExtractor(val logger: FileLogger, val compression: Compression
|
||||
} while (externalDeclWorkList.isNotEmpty())
|
||||
output.writeTrapSet()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,66 +11,90 @@ import org.jetbrains.kotlin.config.CompilerConfigurationKey
|
||||
class KotlinExtractorCommandLineProcessor : CommandLineProcessor {
|
||||
override val pluginId = "kotlin-extractor"
|
||||
|
||||
override val pluginOptions = listOf(
|
||||
CliOption(
|
||||
optionName = OPTION_INVOCATION_TRAP_FILE,
|
||||
valueDescription = "Invocation TRAP file",
|
||||
description = "Extractor will append invocation-related TRAP to this file",
|
||||
required = true,
|
||||
allowMultipleOccurrences = false
|
||||
),
|
||||
CliOption(
|
||||
optionName = OPTION_CHECK_TRAP_IDENTICAL,
|
||||
valueDescription = "Check whether different invocations produce identical TRAP",
|
||||
description = "Check whether different invocations produce identical TRAP",
|
||||
required = false,
|
||||
allowMultipleOccurrences = false
|
||||
),
|
||||
CliOption(
|
||||
optionName = OPTION_COMPILATION_STARTTIME,
|
||||
valueDescription = "The start time of the compilation as a Unix timestamp",
|
||||
description = "The start time of the compilation as a Unix timestamp",
|
||||
required = false,
|
||||
allowMultipleOccurrences = false
|
||||
),
|
||||
CliOption(
|
||||
optionName = OPTION_EXIT_AFTER_EXTRACTION,
|
||||
valueDescription = "Specify whether to call exitProcess after the extraction has completed",
|
||||
description = "Specify whether to call exitProcess after the extraction has completed",
|
||||
required = false,
|
||||
allowMultipleOccurrences = false
|
||||
override val pluginOptions =
|
||||
listOf(
|
||||
CliOption(
|
||||
optionName = OPTION_INVOCATION_TRAP_FILE,
|
||||
valueDescription = "Invocation TRAP file",
|
||||
description = "Extractor will append invocation-related TRAP to this file",
|
||||
required = true,
|
||||
allowMultipleOccurrences = false
|
||||
),
|
||||
CliOption(
|
||||
optionName = OPTION_CHECK_TRAP_IDENTICAL,
|
||||
valueDescription = "Check whether different invocations produce identical TRAP",
|
||||
description = "Check whether different invocations produce identical TRAP",
|
||||
required = false,
|
||||
allowMultipleOccurrences = false
|
||||
),
|
||||
CliOption(
|
||||
optionName = OPTION_COMPILATION_STARTTIME,
|
||||
valueDescription = "The start time of the compilation as a Unix timestamp",
|
||||
description = "The start time of the compilation as a Unix timestamp",
|
||||
required = false,
|
||||
allowMultipleOccurrences = false
|
||||
),
|
||||
CliOption(
|
||||
optionName = OPTION_EXIT_AFTER_EXTRACTION,
|
||||
valueDescription =
|
||||
"Specify whether to call exitProcess after the extraction has completed",
|
||||
description =
|
||||
"Specify whether to call exitProcess after the extraction has completed",
|
||||
required = false,
|
||||
allowMultipleOccurrences = false
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
override fun processOption(
|
||||
option: AbstractCliOption,
|
||||
value: String,
|
||||
configuration: CompilerConfiguration
|
||||
) = when (option.optionName) {
|
||||
OPTION_INVOCATION_TRAP_FILE -> configuration.put(KEY_INVOCATION_TRAP_FILE, value)
|
||||
OPTION_CHECK_TRAP_IDENTICAL -> processBooleanOption(value, OPTION_CHECK_TRAP_IDENTICAL, KEY_CHECK_TRAP_IDENTICAL, configuration)
|
||||
OPTION_EXIT_AFTER_EXTRACTION -> processBooleanOption(value, OPTION_EXIT_AFTER_EXTRACTION, KEY_EXIT_AFTER_EXTRACTION, configuration)
|
||||
OPTION_COMPILATION_STARTTIME ->
|
||||
when (val v = value.toLongOrNull()) {
|
||||
is Long -> configuration.put(KEY_COMPILATION_STARTTIME, v)
|
||||
else -> error("kotlin extractor: Bad argument $value for $OPTION_COMPILATION_STARTTIME")
|
||||
}
|
||||
else -> error("kotlin extractor: Bad option: ${option.optionName}")
|
||||
}
|
||||
) =
|
||||
when (option.optionName) {
|
||||
OPTION_INVOCATION_TRAP_FILE -> configuration.put(KEY_INVOCATION_TRAP_FILE, value)
|
||||
OPTION_CHECK_TRAP_IDENTICAL ->
|
||||
processBooleanOption(
|
||||
value,
|
||||
OPTION_CHECK_TRAP_IDENTICAL,
|
||||
KEY_CHECK_TRAP_IDENTICAL,
|
||||
configuration
|
||||
)
|
||||
OPTION_EXIT_AFTER_EXTRACTION ->
|
||||
processBooleanOption(
|
||||
value,
|
||||
OPTION_EXIT_AFTER_EXTRACTION,
|
||||
KEY_EXIT_AFTER_EXTRACTION,
|
||||
configuration
|
||||
)
|
||||
OPTION_COMPILATION_STARTTIME ->
|
||||
when (val v = value.toLongOrNull()) {
|
||||
is Long -> configuration.put(KEY_COMPILATION_STARTTIME, v)
|
||||
else ->
|
||||
error(
|
||||
"kotlin extractor: Bad argument $value for $OPTION_COMPILATION_STARTTIME"
|
||||
)
|
||||
}
|
||||
else -> error("kotlin extractor: Bad option: ${option.optionName}")
|
||||
}
|
||||
|
||||
private fun processBooleanOption(value: String, optionName: String, configKey: CompilerConfigurationKey<Boolean>, configuration: CompilerConfiguration) =
|
||||
private fun processBooleanOption(
|
||||
value: String,
|
||||
optionName: String,
|
||||
configKey: CompilerConfigurationKey<Boolean>,
|
||||
configuration: CompilerConfiguration
|
||||
) =
|
||||
when (value) {
|
||||
"true" -> configuration.put(configKey, true)
|
||||
"false" -> configuration.put(configKey, false)
|
||||
else -> error("kotlin extractor: Bad argument $value for $optionName")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val OPTION_INVOCATION_TRAP_FILE = "invocationTrapFile"
|
||||
val KEY_INVOCATION_TRAP_FILE = CompilerConfigurationKey<String>(OPTION_INVOCATION_TRAP_FILE)
|
||||
private val OPTION_CHECK_TRAP_IDENTICAL = "checkTrapIdentical"
|
||||
val KEY_CHECK_TRAP_IDENTICAL= CompilerConfigurationKey<Boolean>(OPTION_CHECK_TRAP_IDENTICAL)
|
||||
val KEY_CHECK_TRAP_IDENTICAL = CompilerConfigurationKey<Boolean>(OPTION_CHECK_TRAP_IDENTICAL)
|
||||
private val OPTION_COMPILATION_STARTTIME = "compilationStartTime"
|
||||
val KEY_COMPILATION_STARTTIME= CompilerConfigurationKey<Long>(OPTION_COMPILATION_STARTTIME)
|
||||
val KEY_COMPILATION_STARTTIME = CompilerConfigurationKey<Long>(OPTION_COMPILATION_STARTTIME)
|
||||
private val OPTION_EXIT_AFTER_EXTRACTION = "exitAfterExtraction"
|
||||
val KEY_EXIT_AFTER_EXTRACTION= CompilerConfigurationKey<Boolean>(OPTION_EXIT_AFTER_EXTRACTION)
|
||||
val KEY_EXIT_AFTER_EXTRACTION = CompilerConfigurationKey<Boolean>(OPTION_EXIT_AFTER_EXTRACTION)
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
|
||||
package com.github.codeql
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
|
||||
import com.intellij.mock.MockProject
|
||||
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import com.github.codeql.Kotlin2ComponentRegistrar
|
||||
|
||||
class KotlinExtractorComponentRegistrar : Kotlin2ComponentRegistrar() {
|
||||
override fun registerProjectComponents(
|
||||
@@ -19,10 +16,14 @@ class KotlinExtractorComponentRegistrar : Kotlin2ComponentRegistrar() {
|
||||
if (invocationTrapFile == null) {
|
||||
throw Exception("Required argument for TRAP invocation file not given")
|
||||
}
|
||||
IrGenerationExtension.registerExtension(project, KotlinExtractorExtension(
|
||||
invocationTrapFile,
|
||||
configuration[KEY_CHECK_TRAP_IDENTICAL] ?: false,
|
||||
configuration[KEY_COMPILATION_STARTTIME],
|
||||
configuration[KEY_EXIT_AFTER_EXTRACTION] ?: false))
|
||||
IrGenerationExtension.registerExtension(
|
||||
project,
|
||||
KotlinExtractorExtension(
|
||||
invocationTrapFile,
|
||||
configuration[KEY_CHECK_TRAP_IDENTICAL] ?: false,
|
||||
configuration[KEY_COMPILATION_STARTTIME],
|
||||
configuration[KEY_EXIT_AFTER_EXTRACTION] ?: false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
package com.github.codeql
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.config.KotlinCompilerVersion
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import java.io.BufferedReader
|
||||
import java.io.BufferedWriter
|
||||
import com.github.codeql.utils.versions.usesK2
|
||||
import com.semmle.util.files.FileUtil
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.BufferedReader
|
||||
import java.io.BufferedWriter
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
@@ -20,9 +16,12 @@ import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import java.util.zip.GZIPInputStream
|
||||
import java.util.zip.GZIPOutputStream
|
||||
import com.github.codeql.utils.versions.usesK2
|
||||
import com.semmle.util.files.FileUtil
|
||||
import kotlin.system.exitProcess
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.config.KotlinCompilerVersion
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
|
||||
/*
|
||||
* KotlinExtractorExtension is the main entry point of the CodeQL Kotlin
|
||||
@@ -55,8 +54,8 @@ class KotlinExtractorExtension(
|
||||
// can be set to true to make the plugin terminate the kotlinc
|
||||
// invocation when it has finished. This means that kotlinc will not
|
||||
// write any `.class` files etc.
|
||||
private val exitAfterExtraction: Boolean)
|
||||
: IrGenerationExtension {
|
||||
private val exitAfterExtraction: Boolean
|
||||
) : IrGenerationExtension {
|
||||
|
||||
// This is the main entry point to the extractor.
|
||||
// It will be called by kotlinc with the IR for the files being
|
||||
@@ -65,10 +64,10 @@ class KotlinExtractorExtension(
|
||||
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
|
||||
try {
|
||||
runExtractor(moduleFragment, pluginContext)
|
||||
// We catch Throwable rather than Exception, as we want to
|
||||
// continue trying to extract everything else even if we get a
|
||||
// stack overflow or an assertion failure in one file.
|
||||
} catch(e: Throwable) {
|
||||
// We catch Throwable rather than Exception, as we want to
|
||||
// continue trying to extract everything else even if we get a
|
||||
// stack overflow or an assertion failure in one file.
|
||||
} catch (e: Throwable) {
|
||||
// If we get an exception at the top level, then something's
|
||||
// gone very wrong. Don't try to be too fancy, but try to
|
||||
// log a simple message.
|
||||
@@ -80,7 +79,8 @@ class KotlinExtractorExtension(
|
||||
// We use a slightly different filename pattern compared
|
||||
// to normal logs. Just the existence of a `-top` log is
|
||||
// a sign that something's gone very wrong.
|
||||
val logFile = File.createTempFile("kotlin-extractor-top.", ".log", File(extractorLogDir))
|
||||
val logFile =
|
||||
File.createTempFile("kotlin-extractor-top.", ".log", File(extractorLogDir))
|
||||
logFile.writeText(msg)
|
||||
// Now we've got that out, let's see if we can append a stack trace too
|
||||
logFile.appendText(e.stackTraceToString())
|
||||
@@ -99,12 +99,18 @@ class KotlinExtractorExtension(
|
||||
private fun runExtractor(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
|
||||
val startTimeMs = System.currentTimeMillis()
|
||||
val usesK2 = usesK2(pluginContext)
|
||||
// This default should be kept in sync with com.semmle.extractor.java.interceptors.KotlinInterceptor.initializeExtractionContext
|
||||
val trapDir = File(System.getenv("CODEQL_EXTRACTOR_JAVA_TRAP_DIR").takeUnless { it.isNullOrEmpty() } ?: "kotlin-extractor/trap")
|
||||
// This default should be kept in sync with
|
||||
// com.semmle.extractor.java.interceptors.KotlinInterceptor.initializeExtractionContext
|
||||
val trapDir =
|
||||
File(
|
||||
System.getenv("CODEQL_EXTRACTOR_JAVA_TRAP_DIR").takeUnless { it.isNullOrEmpty() }
|
||||
?: "kotlin-extractor/trap"
|
||||
)
|
||||
// The invocation TRAP file will already have been started
|
||||
// before the plugin is run, so we always use no compression
|
||||
// and we open it in append mode.
|
||||
FileOutputStream(File(invocationTrapFile), true).bufferedWriter().use { invocationTrapFileBW ->
|
||||
FileOutputStream(File(invocationTrapFile), true).bufferedWriter().use { invocationTrapFileBW
|
||||
->
|
||||
val invocationExtractionProblems = ExtractionProblems()
|
||||
val lm = TrapLabelManager()
|
||||
val logCounter = LogCounter()
|
||||
@@ -113,12 +119,21 @@ class KotlinExtractorExtension(
|
||||
// The interceptor has already defined #compilation = *
|
||||
val compilation: Label<DbCompilation> = StringLabel("compilation")
|
||||
tw.writeCompilation_started(compilation)
|
||||
tw.writeCompilation_info(compilation, "Kotlin Compiler Version", KotlinCompilerVersion.getVersion() ?: "<unknown>")
|
||||
val extractor_name = this::class.java.getResource("extractor.name")?.readText() ?: "<unknown>"
|
||||
tw.writeCompilation_info(
|
||||
compilation,
|
||||
"Kotlin Compiler Version",
|
||||
KotlinCompilerVersion.getVersion() ?: "<unknown>"
|
||||
)
|
||||
val extractor_name =
|
||||
this::class.java.getResource("extractor.name")?.readText() ?: "<unknown>"
|
||||
tw.writeCompilation_info(compilation, "Kotlin Extractor Name", extractor_name)
|
||||
tw.writeCompilation_info(compilation, "Uses Kotlin 2", usesK2.toString())
|
||||
if (compilationStartTime != null) {
|
||||
tw.writeCompilation_compiler_times(compilation, -1.0, (System.currentTimeMillis()-compilationStartTime)/1000.0)
|
||||
tw.writeCompilation_compiler_times(
|
||||
compilation,
|
||||
-1.0,
|
||||
(System.currentTimeMillis() - compilationStartTime) / 1000.0
|
||||
)
|
||||
}
|
||||
tw.flush()
|
||||
val logger = Logger(loggerBase, tw)
|
||||
@@ -138,23 +153,54 @@ class KotlinExtractorExtension(
|
||||
// FIXME: FileUtil expects a static global logger
|
||||
// which should be provided by SLF4J's factory facility. For now we set it here.
|
||||
FileUtil.logger = logger
|
||||
val srcDir = File(System.getenv("CODEQL_EXTRACTOR_JAVA_SOURCE_ARCHIVE_DIR").takeUnless { it.isNullOrEmpty() } ?: "kotlin-extractor/src")
|
||||
val srcDir =
|
||||
File(
|
||||
System.getenv("CODEQL_EXTRACTOR_JAVA_SOURCE_ARCHIVE_DIR").takeUnless {
|
||||
it.isNullOrEmpty()
|
||||
} ?: "kotlin-extractor/src"
|
||||
)
|
||||
srcDir.mkdirs()
|
||||
val globalExtensionState = KotlinExtractorGlobalState()
|
||||
moduleFragment.files.mapIndexed { index: Int, file: IrFile ->
|
||||
val fileExtractionProblems = FileExtractionProblems(invocationExtractionProblems)
|
||||
val fileTrapWriter = tw.makeSourceFileTrapWriter(file, true)
|
||||
loggerBase.setFileNumber(index)
|
||||
fileTrapWriter.writeCompilation_compiling_files(compilation, index, fileTrapWriter.fileId)
|
||||
doFile(compression, fileExtractionProblems, invocationTrapFile, fileTrapWriter, checkTrapIdentical, loggerBase, trapDir, srcDir, file, primitiveTypeMapping, pluginContext, globalExtensionState)
|
||||
fileTrapWriter.writeCompilation_compiling_files_completed(compilation, index, fileExtractionProblems.extractionResult())
|
||||
fileTrapWriter.writeCompilation_compiling_files(
|
||||
compilation,
|
||||
index,
|
||||
fileTrapWriter.fileId
|
||||
)
|
||||
doFile(
|
||||
compression,
|
||||
fileExtractionProblems,
|
||||
invocationTrapFile,
|
||||
fileTrapWriter,
|
||||
checkTrapIdentical,
|
||||
loggerBase,
|
||||
trapDir,
|
||||
srcDir,
|
||||
file,
|
||||
primitiveTypeMapping,
|
||||
pluginContext,
|
||||
globalExtensionState
|
||||
)
|
||||
fileTrapWriter.writeCompilation_compiling_files_completed(
|
||||
compilation,
|
||||
index,
|
||||
fileExtractionProblems.extractionResult()
|
||||
)
|
||||
}
|
||||
loggerBase.printLimitedDiagnosticCounts(tw)
|
||||
logPeakMemoryUsage(logger, "after extractor")
|
||||
logger.info("Extraction completed")
|
||||
logger.flush()
|
||||
val compilationTimeMs = System.currentTimeMillis() - startTimeMs
|
||||
tw.writeCompilation_finished(compilation, -1.0, compilationTimeMs.toDouble() / 1000, invocationExtractionProblems.extractionResult())
|
||||
tw.writeCompilation_finished(
|
||||
compilation,
|
||||
-1.0,
|
||||
compilationTimeMs.toDouble() / 1000,
|
||||
invocationExtractionProblems.extractionResult()
|
||||
)
|
||||
tw.flush()
|
||||
loggerBase.close()
|
||||
}
|
||||
@@ -168,16 +214,19 @@ class KotlinExtractorExtension(
|
||||
return defaultCompression
|
||||
} else {
|
||||
try {
|
||||
@OptIn(kotlin.ExperimentalStdlibApi::class) // Annotation required by kotlin versions < 1.5
|
||||
val compression_option_upper = compression_option.uppercase()
|
||||
if (compression_option_upper == "BROTLI") {
|
||||
logger.warn("Kotlin extractor doesn't support Brotli compression. Using GZip instead.")
|
||||
logger.warn(
|
||||
"Kotlin extractor doesn't support Brotli compression. Using GZip instead."
|
||||
)
|
||||
return Compression.GZIP
|
||||
} else {
|
||||
return Compression.valueOf(compression_option_upper)
|
||||
}
|
||||
} catch (e: IllegalArgumentException) {
|
||||
logger.warn("Unsupported compression type (\$$compression_env_var) \"$compression_option\". Supported values are ${Compression.values().joinToString()}.")
|
||||
logger.warn(
|
||||
"Unsupported compression type (\$$compression_env_var) \"$compression_option\". Supported values are ${Compression.values().joinToString()}."
|
||||
)
|
||||
return defaultCompression
|
||||
}
|
||||
}
|
||||
@@ -191,11 +240,18 @@ class KotlinExtractorExtension(
|
||||
var nonheap: Long = 0
|
||||
for (bean in beans) {
|
||||
val peak = bean.getPeakUsage().getUsed()
|
||||
val kind = when (bean.getType()) {
|
||||
MemoryType.HEAP -> { heap += peak; "heap" }
|
||||
MemoryType.NON_HEAP -> { nonheap += peak; "non-heap" }
|
||||
else -> "unknown"
|
||||
}
|
||||
val kind =
|
||||
when (bean.getType()) {
|
||||
MemoryType.HEAP -> {
|
||||
heap += peak
|
||||
"heap"
|
||||
}
|
||||
MemoryType.NON_HEAP -> {
|
||||
nonheap += peak
|
||||
"non-heap"
|
||||
}
|
||||
else -> "unknown"
|
||||
}
|
||||
logger.info("Peak memory: * Peak for $kind bean ${bean.getName()} is $peak")
|
||||
}
|
||||
logger.info("Peak memory: * Total heap peak: $heap")
|
||||
@@ -204,9 +260,12 @@ class KotlinExtractorExtension(
|
||||
}
|
||||
|
||||
class KotlinExtractorGlobalState {
|
||||
// These three record mappings of classes, functions and fields that should be replaced wherever they are found.
|
||||
// As of now these are only used to fix IR generated by the Gradle Android Extensions plugin, hence e.g. IrProperty
|
||||
// doesn't have a map as that plugin doesn't generate them. If and when these are used more widely additional maps
|
||||
// These three record mappings of classes, functions and fields that should be replaced wherever
|
||||
// they are found.
|
||||
// As of now these are only used to fix IR generated by the Gradle Android Extensions plugin,
|
||||
// hence e.g. IrProperty
|
||||
// doesn't have a map as that plugin doesn't generate them. If and when these are used more
|
||||
// widely additional maps
|
||||
// should be added here.
|
||||
val syntheticToRealClassMap = HashMap<IrClass, IrClass?>()
|
||||
val syntheticToRealFunctionMap = HashMap<IrFunction, IrFunction?>()
|
||||
@@ -228,13 +287,15 @@ open class ExtractionProblems {
|
||||
open fun setRecoverableProblem() {
|
||||
recoverableProblem = true
|
||||
}
|
||||
|
||||
open fun setNonRecoverableProblem() {
|
||||
nonRecoverableProblem = true
|
||||
}
|
||||
|
||||
fun extractionResult(): Int {
|
||||
if(nonRecoverableProblem) {
|
||||
if (nonRecoverableProblem) {
|
||||
return 2
|
||||
} else if(recoverableProblem) {
|
||||
} else if (recoverableProblem) {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
@@ -247,11 +308,13 @@ The `FileExtractionProblems` is analogous to `ExtractionProblems`,
|
||||
except it records whether there were any problems while extracting a
|
||||
particular source file.
|
||||
*/
|
||||
class FileExtractionProblems(val invocationExtractionProblems: ExtractionProblems): ExtractionProblems() {
|
||||
class FileExtractionProblems(val invocationExtractionProblems: ExtractionProblems) :
|
||||
ExtractionProblems() {
|
||||
override fun setRecoverableProblem() {
|
||||
super.setRecoverableProblem()
|
||||
invocationExtractionProblems.setRecoverableProblem()
|
||||
}
|
||||
|
||||
override fun setNonRecoverableProblem() {
|
||||
super.setNonRecoverableProblem()
|
||||
invocationExtractionProblems.setNonRecoverableProblem()
|
||||
@@ -266,7 +329,7 @@ identical.
|
||||
private fun equivalentTrap(r1: BufferedReader, r2: BufferedReader): Boolean {
|
||||
r1.use { br1 ->
|
||||
r2.use { br2 ->
|
||||
while(true) {
|
||||
while (true) {
|
||||
val l1 = br1.readLine()
|
||||
val l2 = br2.readLine()
|
||||
if (l1 == null && l2 == null) {
|
||||
@@ -295,7 +358,8 @@ private fun doFile(
|
||||
srcFile: IrFile,
|
||||
primitiveTypeMapping: PrimitiveTypeMapping,
|
||||
pluginContext: IrPluginContext,
|
||||
globalExtensionState: KotlinExtractorGlobalState) {
|
||||
globalExtensionState: KotlinExtractorGlobalState
|
||||
) {
|
||||
val srcFilePath = srcFile.path
|
||||
val logger = FileLogger(loggerBase, fileTrapWriter)
|
||||
logger.info("Extracting file $srcFilePath")
|
||||
@@ -312,10 +376,13 @@ private fun doFile(
|
||||
val dbSrcFilePath = Paths.get("$dbSrcDir/$srcFileRelativePath")
|
||||
val dbSrcDirPath = dbSrcFilePath.parent
|
||||
Files.createDirectories(dbSrcDirPath)
|
||||
val srcTmpFile = File.createTempFile(dbSrcFilePath.fileName.toString() + ".", ".src.tmp", dbSrcDirPath.toFile())
|
||||
srcTmpFile.outputStream().use {
|
||||
Files.copy(Paths.get(srcFilePath), it)
|
||||
}
|
||||
val srcTmpFile =
|
||||
File.createTempFile(
|
||||
dbSrcFilePath.fileName.toString() + ".",
|
||||
".src.tmp",
|
||||
dbSrcDirPath.toFile()
|
||||
)
|
||||
srcTmpFile.outputStream().use { Files.copy(Paths.get(srcFilePath), it) }
|
||||
srcTmpFile.renameTo(dbSrcFilePath.toFile())
|
||||
|
||||
val trapFileName = "$dbTrapDir/$srcFileRelativePath.trap"
|
||||
@@ -328,22 +395,52 @@ private fun doFile(
|
||||
trapFileWriter.getTempWriter().use { trapFileBW ->
|
||||
// We want our comments to be the first thing in the file,
|
||||
// so start off with a mere TrapWriter
|
||||
val tw = PlainTrapWriter(loggerBase, TrapLabelManager(), trapFileBW, fileTrapWriter.getDiagnosticTrapWriter())
|
||||
val tw =
|
||||
PlainTrapWriter(
|
||||
loggerBase,
|
||||
TrapLabelManager(),
|
||||
trapFileBW,
|
||||
fileTrapWriter.getDiagnosticTrapWriter()
|
||||
)
|
||||
tw.writeComment("Generated by the CodeQL Kotlin extractor for kotlin source code")
|
||||
tw.writeComment("Part of invocation $invocationTrapFile")
|
||||
// Now elevate to a SourceFileTrapWriter, and populate the
|
||||
// file information
|
||||
val sftw = tw.makeSourceFileTrapWriter(srcFile, true)
|
||||
val externalDeclExtractor = ExternalDeclExtractor(logger, compression, invocationTrapFile, srcFilePath, primitiveTypeMapping, pluginContext, globalExtensionState, fileTrapWriter.getDiagnosticTrapWriter())
|
||||
val externalDeclExtractor =
|
||||
ExternalDeclExtractor(
|
||||
logger,
|
||||
compression,
|
||||
invocationTrapFile,
|
||||
srcFilePath,
|
||||
primitiveTypeMapping,
|
||||
pluginContext,
|
||||
globalExtensionState,
|
||||
fileTrapWriter.getDiagnosticTrapWriter()
|
||||
)
|
||||
val linesOfCode = LinesOfCode(logger, sftw, srcFile)
|
||||
val fileExtractor = KotlinFileExtractor(logger, sftw, linesOfCode, srcFilePath, null, externalDeclExtractor, primitiveTypeMapping, pluginContext, KotlinFileExtractor.DeclarationStack(), globalExtensionState)
|
||||
val fileExtractor =
|
||||
KotlinFileExtractor(
|
||||
logger,
|
||||
sftw,
|
||||
linesOfCode,
|
||||
srcFilePath,
|
||||
null,
|
||||
externalDeclExtractor,
|
||||
primitiveTypeMapping,
|
||||
pluginContext,
|
||||
KotlinFileExtractor.DeclarationStack(),
|
||||
globalExtensionState
|
||||
)
|
||||
|
||||
fileExtractor.extractFileContents(srcFile, sftw.fileId)
|
||||
externalDeclExtractor.extractExternalClasses()
|
||||
}
|
||||
|
||||
if (checkTrapIdentical && trapFileWriter.exists()) {
|
||||
if (equivalentTrap(trapFileWriter.getTempReader(), trapFileWriter.getRealReader())) {
|
||||
if (
|
||||
equivalentTrap(trapFileWriter.getTempReader(), trapFileWriter.getRealReader())
|
||||
) {
|
||||
trapFileWriter.deleteTemp()
|
||||
} else {
|
||||
trapFileWriter.renameTempToDifferent()
|
||||
@@ -351,9 +448,9 @@ private fun doFile(
|
||||
} else {
|
||||
trapFileWriter.renameTempToReal()
|
||||
}
|
||||
// We catch Throwable rather than Exception, as we want to
|
||||
// continue trying to extract everything else even if we get a
|
||||
// stack overflow or an assertion failure in one file.
|
||||
// We catch Throwable rather than Exception, as we want to
|
||||
// continue trying to extract everything else even if we get a
|
||||
// stack overflow or an assertion failure in one file.
|
||||
} catch (e: Throwable) {
|
||||
logger.error("Failed to extract '$srcFilePath'. " + trapFileWriter.debugInfo(), e)
|
||||
context.clear()
|
||||
@@ -373,17 +470,26 @@ enum class Compression(val extension: String) {
|
||||
return GZIPOutputStream(file.outputStream()).bufferedWriter()
|
||||
}
|
||||
};
|
||||
|
||||
abstract fun bufferedWriter(file: File): BufferedWriter
|
||||
}
|
||||
|
||||
private fun getTrapFileWriter(compression: Compression, logger: FileLogger, trapFileName: String): TrapFileWriter {
|
||||
private fun getTrapFileWriter(
|
||||
compression: Compression,
|
||||
logger: FileLogger,
|
||||
trapFileName: String
|
||||
): TrapFileWriter {
|
||||
return when (compression) {
|
||||
Compression.NONE -> NonCompressedTrapFileWriter(logger, trapFileName)
|
||||
Compression.GZIP -> GZipCompressedTrapFileWriter(logger, trapFileName)
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class TrapFileWriter(val logger: FileLogger, trapName: String, val extension: String) {
|
||||
private abstract class TrapFileWriter(
|
||||
val logger: FileLogger,
|
||||
trapName: String,
|
||||
val extension: String
|
||||
) {
|
||||
private val realFile = File(trapName + extension)
|
||||
private val parentDir = realFile.parentFile
|
||||
lateinit private var tempFile: File
|
||||
@@ -405,6 +511,7 @@ private abstract class TrapFileWriter(val logger: FileLogger, trapName: String,
|
||||
}
|
||||
|
||||
abstract protected fun getReader(file: File): BufferedReader
|
||||
|
||||
abstract protected fun getWriter(file: File): BufferedWriter
|
||||
|
||||
fun getRealReader(): BufferedReader {
|
||||
@@ -432,7 +539,8 @@ private abstract class TrapFileWriter(val logger: FileLogger, trapName: String,
|
||||
}
|
||||
|
||||
fun renameTempToDifferent() {
|
||||
val trapDifferentFile = File.createTempFile(realFile.getName() + ".", ".trap.different" + extension, parentDir)
|
||||
val trapDifferentFile =
|
||||
File.createTempFile(realFile.getName() + ".", ".trap.different" + extension, parentDir)
|
||||
if (tempFile.renameTo(trapDifferentFile)) {
|
||||
logger.warn("TRAP difference: $realFile vs $trapDifferentFile")
|
||||
} else {
|
||||
@@ -448,7 +556,8 @@ private abstract class TrapFileWriter(val logger: FileLogger, trapName: String,
|
||||
}
|
||||
}
|
||||
|
||||
private class NonCompressedTrapFileWriter(logger: FileLogger, trapName: String): TrapFileWriter(logger, trapName, "") {
|
||||
private class NonCompressedTrapFileWriter(logger: FileLogger, trapName: String) :
|
||||
TrapFileWriter(logger, trapName, "") {
|
||||
override protected fun getReader(file: File): BufferedReader {
|
||||
return file.bufferedReader()
|
||||
}
|
||||
@@ -458,12 +567,17 @@ private class NonCompressedTrapFileWriter(logger: FileLogger, trapName: String):
|
||||
}
|
||||
}
|
||||
|
||||
private class GZipCompressedTrapFileWriter(logger: FileLogger, trapName: String): TrapFileWriter(logger, trapName, ".gz") {
|
||||
private class GZipCompressedTrapFileWriter(logger: FileLogger, trapName: String) :
|
||||
TrapFileWriter(logger, trapName, ".gz") {
|
||||
override protected fun getReader(file: File): BufferedReader {
|
||||
return BufferedReader(InputStreamReader(GZIPInputStream(BufferedInputStream(FileInputStream(file)))))
|
||||
return BufferedReader(
|
||||
InputStreamReader(GZIPInputStream(BufferedInputStream(FileInputStream(file))))
|
||||
)
|
||||
}
|
||||
|
||||
override protected fun getWriter(file: File): BufferedWriter {
|
||||
return BufferedWriter(OutputStreamWriter(GZIPOutputStream(BufferedOutputStream(FileOutputStream(file)))))
|
||||
return BufferedWriter(
|
||||
OutputStreamWriter(GZIPOutputStream(BufferedOutputStream(FileOutputStream(file))))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,31 +1,21 @@
|
||||
package com.github.codeql
|
||||
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
|
||||
/**
|
||||
* This represents a label (`#...`) in a TRAP file.
|
||||
*/
|
||||
interface Label<T: AnyDbType> {
|
||||
fun <U: AnyDbType> cast(): Label<U> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return this as Label<U>
|
||||
/** This represents a label (`#...`) in a TRAP file. */
|
||||
interface Label<T : AnyDbType> {
|
||||
fun <U : AnyDbType> cast(): Label<U> {
|
||||
@Suppress("UNCHECKED_CAST") return this as Label<U>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The label `#i`, e.g. `#123`. Most labels we generate are of this
|
||||
* form.
|
||||
*/
|
||||
class IntLabel<T: AnyDbType>(val i: Int): Label<T> {
|
||||
/** The label `#i`, e.g. `#123`. Most labels we generate are of this form. */
|
||||
class IntLabel<T : AnyDbType>(val i: Int) : Label<T> {
|
||||
override fun toString(): String = "#$i"
|
||||
}
|
||||
|
||||
/**
|
||||
* The label `#name`, e.g. `#compilation`. This is used when labels are
|
||||
* shared between different components (e.g. when both the interceptor
|
||||
* and the extractor need to refer to the same label).
|
||||
* The label `#name`, e.g. `#compilation`. This is used when labels are shared between different
|
||||
* components (e.g. when both the interceptor and the extractor need to refer to the same label).
|
||||
*/
|
||||
class StringLabel<T: AnyDbType>(val name: String): Label<T> {
|
||||
class StringLabel<T : AnyDbType>(val name: String) : Label<T> {
|
||||
override fun toString(): String = "#$name"
|
||||
}
|
||||
|
||||
@@ -2,11 +2,7 @@ package com.github.codeql
|
||||
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
|
||||
class LinesOfCode(
|
||||
val logger: FileLogger,
|
||||
val tw: FileTrapWriter,
|
||||
val file: IrFile
|
||||
) {
|
||||
class LinesOfCode(val logger: FileLogger, val tw: FileTrapWriter, val file: IrFile) {
|
||||
val linesOfCodePSI = LinesOfCodePSI(logger, tw, file)
|
||||
val linesOfCodeLighterAST = LinesOfCodeLighterAST(logger, tw, file)
|
||||
|
||||
@@ -14,7 +10,10 @@ class LinesOfCode(
|
||||
val psiExtracted = linesOfCodePSI.linesOfCodeInFile(id)
|
||||
val lighterASTExtracted = linesOfCodeLighterAST.linesOfCodeInFile(id)
|
||||
if (psiExtracted && lighterASTExtracted) {
|
||||
logger.warnElement("Both PSI and LighterAST number-of-lines-in-file information for ${file.path}.", file)
|
||||
logger.warnElement(
|
||||
"Both PSI and LighterAST number-of-lines-in-file information for ${file.path}.",
|
||||
file
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +21,10 @@ class LinesOfCode(
|
||||
val psiExtracted = linesOfCodePSI.linesOfCodeInDeclaration(d, id)
|
||||
val lighterASTExtracted = linesOfCodeLighterAST.linesOfCodeInDeclaration(d, id)
|
||||
if (psiExtracted && lighterASTExtracted) {
|
||||
logger.warnElement("Both PSI and LighterAST number-of-lines-in-file information for declaration.", d)
|
||||
logger.warnElement(
|
||||
"Both PSI and LighterAST number-of-lines-in-file information for declaration.",
|
||||
d
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,20 +7,17 @@ import com.intellij.psi.PsiWhiteSpace
|
||||
import org.jetbrains.kotlin.config.KotlinCompilerVersion
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.kdoc.psi.api.KDocElement
|
||||
import org.jetbrains.kotlin.psi.KtCodeFragment
|
||||
import org.jetbrains.kotlin.psi.KtVisitor
|
||||
|
||||
class LinesOfCodePSI(
|
||||
val logger: FileLogger,
|
||||
val tw: FileTrapWriter,
|
||||
val file: IrFile
|
||||
) {
|
||||
val psi2Ir = getPsi2Ir().also {
|
||||
if (it == null) {
|
||||
logger.warn("Lines of code will not be populated as Kotlin version is too old (${KotlinCompilerVersion.getVersion()})")
|
||||
class LinesOfCodePSI(val logger: FileLogger, val tw: FileTrapWriter, val file: IrFile) {
|
||||
val psi2Ir =
|
||||
getPsi2Ir().also {
|
||||
if (it == null) {
|
||||
logger.warn(
|
||||
"Lines of code will not be populated as Kotlin version is too old (${KotlinCompilerVersion.getVersion()})"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun linesOfCodeInFile(id: Label<DbFile>): Boolean {
|
||||
if (psi2Ir == null) {
|
||||
|
||||
@@ -3,6 +3,8 @@ package com.github.codeql
|
||||
import com.github.codeql.utils.versions.copyParameterToFunction
|
||||
import com.github.codeql.utils.versions.createImplicitParameterDeclarationWithWrappedDescriptor
|
||||
import com.github.codeql.utils.versions.getAnnotationType
|
||||
import java.lang.annotation.ElementType
|
||||
import java.util.HashSet
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.config.JvmTarget
|
||||
@@ -48,10 +50,12 @@ import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import java.lang.annotation.ElementType
|
||||
import java.util.HashSet
|
||||
|
||||
class MetaAnnotationSupport(private val logger: FileLogger, private val pluginContext: IrPluginContext, private val extractor: KotlinFileExtractor) {
|
||||
class MetaAnnotationSupport(
|
||||
private val logger: FileLogger,
|
||||
private val pluginContext: IrPluginContext,
|
||||
private val extractor: KotlinFileExtractor
|
||||
) {
|
||||
|
||||
// Taken from AdditionalIrUtils.kt (not available in Kotlin < 1.6)
|
||||
private val IrConstructorCall.annotationClass
|
||||
@@ -82,8 +86,7 @@ class MetaAnnotationSupport(private val logger: FileLogger, private val pluginCo
|
||||
wrapAnnotationEntriesInContainer(annotationClass, containerClass, grouped)?.let {
|
||||
result.add(it)
|
||||
}
|
||||
else
|
||||
logger.warnElement("Failed to find an annotation container class", annotationClass)
|
||||
else logger.warnElement("Failed to find an annotation container class", annotationClass)
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -91,9 +94,14 @@ class MetaAnnotationSupport(private val logger: FileLogger, private val pluginCo
|
||||
// Adapted from RepeatedAnnotationLowering.kt
|
||||
private fun getOrCreateContainerClass(annotationClass: IrClass): IrClass? {
|
||||
val metaAnnotations = annotationClass.annotations
|
||||
val jvmRepeatable = metaAnnotations.find { it.symbol.owner.parentAsClass.fqNameWhenAvailable == JvmAnnotationNames.REPEATABLE_ANNOTATION }
|
||||
val jvmRepeatable =
|
||||
metaAnnotations.find {
|
||||
it.symbol.owner.parentAsClass.fqNameWhenAvailable ==
|
||||
JvmAnnotationNames.REPEATABLE_ANNOTATION
|
||||
}
|
||||
return if (jvmRepeatable != null) {
|
||||
((jvmRepeatable.getValueArgument(0) as? IrClassReference)?.symbol as? IrClassSymbol)?.owner
|
||||
((jvmRepeatable.getValueArgument(0) as? IrClassReference)?.symbol as? IrClassSymbol)
|
||||
?.owner
|
||||
} else {
|
||||
getOrCreateSyntheticRepeatableAnnotationContainer(annotationClass)
|
||||
}
|
||||
@@ -108,20 +116,28 @@ class MetaAnnotationSupport(private val logger: FileLogger, private val pluginCo
|
||||
val annotationType = annotationClass.typeWith()
|
||||
val containerConstructor = containerClass.primaryConstructor
|
||||
if (containerConstructor == null) {
|
||||
logger.warnElement("Expected container class to have a primary constructor", containerClass)
|
||||
logger.warnElement(
|
||||
"Expected container class to have a primary constructor",
|
||||
containerClass
|
||||
)
|
||||
return null
|
||||
} else {
|
||||
return IrConstructorCallImpl.fromSymbolOwner(containerClass.defaultType, containerConstructor.symbol).apply {
|
||||
putValueArgument(
|
||||
0,
|
||||
IrVarargImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||
pluginContext.irBuiltIns.arrayClass.typeWith(annotationType),
|
||||
annotationType,
|
||||
entries
|
||||
)
|
||||
return IrConstructorCallImpl.fromSymbolOwner(
|
||||
containerClass.defaultType,
|
||||
containerConstructor.symbol
|
||||
)
|
||||
}
|
||||
.apply {
|
||||
putValueArgument(
|
||||
0,
|
||||
IrVarargImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
pluginContext.irBuiltIns.arrayClass.typeWith(annotationType),
|
||||
annotationType,
|
||||
entries
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,14 +150,19 @@ class MetaAnnotationSupport(private val logger: FileLogger, private val pluginCo
|
||||
// Taken from AdditionalClassAnnotationLowering.kt
|
||||
private fun loadAnnotationTargets(targetEntry: IrConstructorCall): Set<KotlinTarget>? {
|
||||
val valueArgument = targetEntry.getValueArgument(0) as? IrVararg ?: return null
|
||||
return valueArgument.elements.filterIsInstance<IrGetEnumValue>().mapNotNull {
|
||||
KotlinTarget.valueOrNull(it.symbol.owner.name.asString())
|
||||
}.toSet()
|
||||
return valueArgument.elements
|
||||
.filterIsInstance<IrGetEnumValue>()
|
||||
.mapNotNull { KotlinTarget.valueOrNull(it.symbol.owner.name.asString()) }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
private val javaAnnotationTargetElementType by lazy { extractor.referenceExternalClass("java.lang.annotation.ElementType") }
|
||||
private val javaAnnotationTargetElementType by lazy {
|
||||
extractor.referenceExternalClass("java.lang.annotation.ElementType")
|
||||
}
|
||||
|
||||
private val javaAnnotationTarget by lazy { extractor.referenceExternalClass("java.lang.annotation.Target") }
|
||||
private val javaAnnotationTarget by lazy {
|
||||
extractor.referenceExternalClass("java.lang.annotation.Target")
|
||||
}
|
||||
|
||||
private fun findEnumEntry(c: IrClass, name: String) =
|
||||
c.declarations.filterIsInstance<IrEnumEntry>().find { it.name.asString() == name }
|
||||
@@ -168,10 +189,11 @@ class MetaAnnotationSupport(private val logger: FileLogger, private val pluginCo
|
||||
private val jvm8TargetMap by lazy {
|
||||
javaAnnotationTargetElementType?.let {
|
||||
jvm6TargetMap?.let { j6Map ->
|
||||
j6Map + mapOf(
|
||||
KotlinTarget.TYPE_PARAMETER to findEnumEntry(it, "TYPE_PARAMETER"),
|
||||
KotlinTarget.TYPE to findEnumEntry(it, "TYPE_USE")
|
||||
)
|
||||
j6Map +
|
||||
mapOf(
|
||||
KotlinTarget.TYPE_PARAMETER to findEnumEntry(it, "TYPE_PARAMETER"),
|
||||
KotlinTarget.TYPE to findEnumEntry(it, "TYPE_USE")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,45 +201,59 @@ class MetaAnnotationSupport(private val logger: FileLogger, private val pluginCo
|
||||
private fun getAnnotationTargetMap() =
|
||||
if (pluginContext.platform?.any { it.targetPlatformVersion == JvmTarget.JVM_1_6 } == true)
|
||||
jvm6TargetMap
|
||||
else
|
||||
jvm8TargetMap
|
||||
else jvm8TargetMap
|
||||
|
||||
// Adapted from AdditionalClassAnnotationLowering.kt
|
||||
private fun generateTargetAnnotation(c: IrClass): IrConstructorCall? {
|
||||
if (c.hasAnnotation(JvmAnnotationNames.TARGET_ANNOTATION))
|
||||
return null
|
||||
if (c.hasAnnotation(JvmAnnotationNames.TARGET_ANNOTATION)) return null
|
||||
val elementType = javaAnnotationTargetElementType ?: return null
|
||||
val targetType = javaAnnotationTarget ?: return null
|
||||
val targetConstructor = targetType.declarations.firstIsInstanceOrNull<IrConstructor>() ?: return null
|
||||
val targetConstructor =
|
||||
targetType.declarations.firstIsInstanceOrNull<IrConstructor>() ?: return null
|
||||
val targets = getApplicableTargetSet(c) ?: return null
|
||||
val annotationTargetMap = getAnnotationTargetMap() ?: return null
|
||||
|
||||
val javaTargets = targets.mapNotNullTo(HashSet()) { annotationTargetMap[it] }.sortedBy {
|
||||
ElementType.valueOf(it.symbol.owner.name.asString())
|
||||
}
|
||||
val vararg = IrVarargImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||
type = pluginContext.irBuiltIns.arrayClass.typeWith(elementType.defaultType),
|
||||
varargElementType = elementType.defaultType
|
||||
)
|
||||
val javaTargets =
|
||||
targets
|
||||
.mapNotNullTo(HashSet()) { annotationTargetMap[it] }
|
||||
.sortedBy { ElementType.valueOf(it.symbol.owner.name.asString()) }
|
||||
val vararg =
|
||||
IrVarargImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
type = pluginContext.irBuiltIns.arrayClass.typeWith(elementType.defaultType),
|
||||
varargElementType = elementType.defaultType
|
||||
)
|
||||
for (target in javaTargets) {
|
||||
vararg.elements.add(
|
||||
IrGetEnumValueImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, elementType.defaultType, target.symbol
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
elementType.defaultType,
|
||||
target.symbol
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return IrConstructorCallImpl.fromSymbolOwner(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, targetConstructor.returnType, targetConstructor.symbol, 0
|
||||
).apply {
|
||||
putValueArgument(0, vararg)
|
||||
}
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
targetConstructor.returnType,
|
||||
targetConstructor.symbol,
|
||||
0
|
||||
)
|
||||
.apply { putValueArgument(0, vararg) }
|
||||
}
|
||||
|
||||
private val javaAnnotationRetention by lazy { extractor.referenceExternalClass("java.lang.annotation.Retention") }
|
||||
private val javaAnnotationRetentionPolicy by lazy { extractor.referenceExternalClass("java.lang.annotation.RetentionPolicy") }
|
||||
private val javaAnnotationRetentionPolicyRuntime by lazy { javaAnnotationRetentionPolicy?.let { findEnumEntry(it, "RUNTIME") } }
|
||||
private val javaAnnotationRetention by lazy {
|
||||
extractor.referenceExternalClass("java.lang.annotation.Retention")
|
||||
}
|
||||
private val javaAnnotationRetentionPolicy by lazy {
|
||||
extractor.referenceExternalClass("java.lang.annotation.RetentionPolicy")
|
||||
}
|
||||
private val javaAnnotationRetentionPolicyRuntime by lazy {
|
||||
javaAnnotationRetentionPolicy?.let { findEnumEntry(it, "RUNTIME") }
|
||||
}
|
||||
|
||||
private val annotationRetentionMap by lazy {
|
||||
javaAnnotationRetentionPolicy?.let {
|
||||
@@ -232,118 +268,164 @@ class MetaAnnotationSupport(private val logger: FileLogger, private val pluginCo
|
||||
// Taken from AnnotationCodegen.kt (not available in Kotlin < 1.6.20)
|
||||
private fun IrClass.getAnnotationRetention(): KotlinRetention? {
|
||||
val retentionArgument =
|
||||
getAnnotation(StandardNames.FqNames.retention)?.getValueArgument(0)
|
||||
as? IrGetEnumValue ?: return null
|
||||
getAnnotation(StandardNames.FqNames.retention)?.getValueArgument(0) as? IrGetEnumValue
|
||||
?: return null
|
||||
val retentionArgumentValue = retentionArgument.symbol.owner
|
||||
return KotlinRetention.valueOf(retentionArgumentValue.name.asString())
|
||||
}
|
||||
|
||||
// Taken from AdditionalClassAnnotationLowering.kt
|
||||
private fun generateRetentionAnnotation(irClass: IrClass): IrConstructorCall? {
|
||||
if (irClass.hasAnnotation(JvmAnnotationNames.RETENTION_ANNOTATION))
|
||||
return null
|
||||
if (irClass.hasAnnotation(JvmAnnotationNames.RETENTION_ANNOTATION)) return null
|
||||
val retentionMap = annotationRetentionMap ?: return null
|
||||
val kotlinRetentionPolicy = irClass.getAnnotationRetention()
|
||||
val javaRetentionPolicy = kotlinRetentionPolicy?.let { retentionMap[it] } ?: javaAnnotationRetentionPolicyRuntime ?: return null
|
||||
val javaRetentionPolicy =
|
||||
kotlinRetentionPolicy?.let { retentionMap[it] }
|
||||
?: javaAnnotationRetentionPolicyRuntime
|
||||
?: return null
|
||||
val retentionPolicyType = javaAnnotationRetentionPolicy ?: return null
|
||||
val retentionType = javaAnnotationRetention ?: return null
|
||||
val targetConstructor = retentionType.declarations.firstIsInstanceOrNull<IrConstructor>() ?: return null
|
||||
val targetConstructor =
|
||||
retentionType.declarations.firstIsInstanceOrNull<IrConstructor>() ?: return null
|
||||
|
||||
return IrConstructorCallImpl.fromSymbolOwner(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, targetConstructor.returnType, targetConstructor.symbol, 0
|
||||
).apply {
|
||||
putValueArgument(
|
||||
0,
|
||||
IrGetEnumValueImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, retentionPolicyType.defaultType, javaRetentionPolicy.symbol
|
||||
)
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
targetConstructor.returnType,
|
||||
targetConstructor.symbol,
|
||||
0
|
||||
)
|
||||
}
|
||||
.apply {
|
||||
putValueArgument(
|
||||
0,
|
||||
IrGetEnumValueImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
retentionPolicyType.defaultType,
|
||||
javaRetentionPolicy.symbol
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val javaAnnotationRepeatable by lazy { extractor.referenceExternalClass("java.lang.annotation.Repeatable") }
|
||||
private val kotlinAnnotationRepeatableContainer by lazy { extractor.referenceExternalClass("kotlin.jvm.internal.RepeatableContainer") }
|
||||
private val javaAnnotationRepeatable by lazy {
|
||||
extractor.referenceExternalClass("java.lang.annotation.Repeatable")
|
||||
}
|
||||
private val kotlinAnnotationRepeatableContainer by lazy {
|
||||
extractor.referenceExternalClass("kotlin.jvm.internal.RepeatableContainer")
|
||||
}
|
||||
|
||||
// Taken from declarationBuilders.kt (not available in Kotlin < 1.6):
|
||||
private fun addDefaultGetter(p: IrProperty, parentClass: IrClass) {
|
||||
val field = p.backingField ?:
|
||||
run { logger.warnElement("Expected property to have a backing field", p); return }
|
||||
val field =
|
||||
p.backingField
|
||||
?: run {
|
||||
logger.warnElement("Expected property to have a backing field", p)
|
||||
return
|
||||
}
|
||||
p.addGetter {
|
||||
origin = IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR
|
||||
returnType = field.type
|
||||
}.apply {
|
||||
val thisReceiever = parentClass.thisReceiver ?:
|
||||
run { logger.warnElement("Expected property's parent class to have a receiver parameter", parentClass); return }
|
||||
val newParam = copyParameterToFunction(thisReceiever, this)
|
||||
dispatchReceiverParameter = newParam
|
||||
body = factory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET).apply({
|
||||
this.statements.add(
|
||||
IrReturnImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||
pluginContext.irBuiltIns.nothingType,
|
||||
symbol,
|
||||
IrGetFieldImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||
field.symbol,
|
||||
field.type,
|
||||
IrGetValueImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||
newParam.type,
|
||||
newParam.symbol
|
||||
origin = IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR
|
||||
returnType = field.type
|
||||
}
|
||||
.apply {
|
||||
val thisReceiever =
|
||||
parentClass.thisReceiver
|
||||
?: run {
|
||||
logger.warnElement(
|
||||
"Expected property's parent class to have a receiver parameter",
|
||||
parentClass
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
val newParam = copyParameterToFunction(thisReceiever, this)
|
||||
dispatchReceiverParameter = newParam
|
||||
body =
|
||||
factory
|
||||
.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET)
|
||||
.apply({
|
||||
this.statements.add(
|
||||
IrReturnImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
pluginContext.irBuiltIns.nothingType,
|
||||
symbol,
|
||||
IrGetFieldImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
field.symbol,
|
||||
field.type,
|
||||
IrGetValueImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
newParam.type,
|
||||
newParam.symbol
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Taken from JvmCachedDeclarations.kt
|
||||
private fun getOrCreateSyntheticRepeatableAnnotationContainer(annotationClass: IrClass) =
|
||||
extractor.globalExtensionState.syntheticRepeatableAnnotationContainers.getOrPut(annotationClass) {
|
||||
val containerClass = pluginContext.irFactory.buildClass {
|
||||
kind = ClassKind.ANNOTATION_CLASS
|
||||
name = Name.identifier("Container")
|
||||
}.apply {
|
||||
createImplicitParameterDeclarationWithWrappedDescriptor()
|
||||
parent = annotationClass
|
||||
superTypes = listOf(getAnnotationType(pluginContext))
|
||||
}
|
||||
extractor.globalExtensionState.syntheticRepeatableAnnotationContainers.getOrPut(
|
||||
annotationClass
|
||||
) {
|
||||
val containerClass =
|
||||
pluginContext.irFactory
|
||||
.buildClass {
|
||||
kind = ClassKind.ANNOTATION_CLASS
|
||||
name = Name.identifier("Container")
|
||||
}
|
||||
.apply {
|
||||
createImplicitParameterDeclarationWithWrappedDescriptor()
|
||||
parent = annotationClass
|
||||
superTypes = listOf(getAnnotationType(pluginContext))
|
||||
}
|
||||
|
||||
val propertyName = Name.identifier("value")
|
||||
val propertyType = pluginContext.irBuiltIns.arrayClass.typeWith(annotationClass.typeWith())
|
||||
val propertyType =
|
||||
pluginContext.irBuiltIns.arrayClass.typeWith(annotationClass.typeWith())
|
||||
|
||||
containerClass.addConstructor {
|
||||
isPrimary = true
|
||||
}.apply {
|
||||
addValueParameter(propertyName.identifier, propertyType)
|
||||
}
|
||||
containerClass
|
||||
.addConstructor { isPrimary = true }
|
||||
.apply { addValueParameter(propertyName.identifier, propertyType) }
|
||||
|
||||
containerClass.addProperty {
|
||||
name = propertyName
|
||||
}.apply property@{
|
||||
backingField = pluginContext.irFactory.buildField {
|
||||
name = propertyName
|
||||
type = propertyType
|
||||
}.apply {
|
||||
parent = containerClass
|
||||
correspondingPropertySymbol = this@property.symbol
|
||||
containerClass
|
||||
.addProperty { name = propertyName }
|
||||
.apply property@{
|
||||
backingField =
|
||||
pluginContext.irFactory
|
||||
.buildField {
|
||||
name = propertyName
|
||||
type = propertyType
|
||||
}
|
||||
.apply {
|
||||
parent = containerClass
|
||||
correspondingPropertySymbol = this@property.symbol
|
||||
}
|
||||
addDefaultGetter(this, containerClass)
|
||||
}
|
||||
addDefaultGetter(this, containerClass)
|
||||
}
|
||||
|
||||
val repeatableContainerAnnotation = kotlinAnnotationRepeatableContainer?.constructors?.single()
|
||||
val repeatableContainerAnnotation =
|
||||
kotlinAnnotationRepeatableContainer?.constructors?.single()
|
||||
|
||||
containerClass.annotations = annotationClass.annotations
|
||||
.filter {
|
||||
it.isAnnotationWithEqualFqName(StandardNames.FqNames.retention) ||
|
||||
containerClass.annotations =
|
||||
annotationClass.annotations
|
||||
.filter {
|
||||
it.isAnnotationWithEqualFqName(StandardNames.FqNames.retention) ||
|
||||
it.isAnnotationWithEqualFqName(StandardNames.FqNames.target)
|
||||
}
|
||||
.map { it.deepCopyWithSymbols(containerClass) } +
|
||||
}
|
||||
.map { it.deepCopyWithSymbols(containerClass) } +
|
||||
listOfNotNull(
|
||||
repeatableContainerAnnotation?.let {
|
||||
IrConstructorCallImpl.fromSymbolOwner(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, it.returnType, it.symbol, 0
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
it.returnType,
|
||||
it.symbol,
|
||||
0
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -352,45 +434,80 @@ class MetaAnnotationSupport(private val logger: FileLogger, private val pluginCo
|
||||
}
|
||||
|
||||
// Adapted from AdditionalClassAnnotationLowering.kt
|
||||
private fun generateRepeatableAnnotation(irClass: IrClass, extractAnnotationTypeAccesses: Boolean): IrConstructorCall? {
|
||||
if (!irClass.hasAnnotation(StandardNames.FqNames.repeatable) ||
|
||||
irClass.hasAnnotation(JvmAnnotationNames.REPEATABLE_ANNOTATION)
|
||||
) return null
|
||||
private fun generateRepeatableAnnotation(
|
||||
irClass: IrClass,
|
||||
extractAnnotationTypeAccesses: Boolean
|
||||
): IrConstructorCall? {
|
||||
if (
|
||||
!irClass.hasAnnotation(StandardNames.FqNames.repeatable) ||
|
||||
irClass.hasAnnotation(JvmAnnotationNames.REPEATABLE_ANNOTATION)
|
||||
)
|
||||
return null
|
||||
|
||||
val repeatableConstructor = javaAnnotationRepeatable?.declarations?.firstIsInstanceOrNull<IrConstructor>() ?: return null
|
||||
val repeatableConstructor =
|
||||
javaAnnotationRepeatable?.declarations?.firstIsInstanceOrNull<IrConstructor>()
|
||||
?: return null
|
||||
|
||||
val containerClass = getOrCreateSyntheticRepeatableAnnotationContainer(irClass)
|
||||
// Whenever a repeatable annotation with a Kotlin-synthesised container is extracted, extract the synthetic container to the same trap file.
|
||||
extractor.extractClassSource(containerClass, extractDeclarations = true, extractStaticInitializer = true, extractPrivateMembers = true, extractFunctionBodies = extractAnnotationTypeAccesses)
|
||||
|
||||
val containerReference = IrClassReferenceImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, pluginContext.irBuiltIns.kClassClass.typeWith(containerClass.defaultType),
|
||||
containerClass.symbol, containerClass.defaultType
|
||||
// Whenever a repeatable annotation with a Kotlin-synthesised container is extracted,
|
||||
// extract the synthetic container to the same trap file.
|
||||
extractor.extractClassSource(
|
||||
containerClass,
|
||||
extractDeclarations = true,
|
||||
extractStaticInitializer = true,
|
||||
extractPrivateMembers = true,
|
||||
extractFunctionBodies = extractAnnotationTypeAccesses
|
||||
)
|
||||
|
||||
val containerReference =
|
||||
IrClassReferenceImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
pluginContext.irBuiltIns.kClassClass.typeWith(containerClass.defaultType),
|
||||
containerClass.symbol,
|
||||
containerClass.defaultType
|
||||
)
|
||||
return IrConstructorCallImpl.fromSymbolOwner(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, repeatableConstructor.returnType, repeatableConstructor.symbol, 0
|
||||
).apply {
|
||||
putValueArgument(0, containerReference)
|
||||
}
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
repeatableConstructor.returnType,
|
||||
repeatableConstructor.symbol,
|
||||
0
|
||||
)
|
||||
.apply { putValueArgument(0, containerReference) }
|
||||
}
|
||||
|
||||
private val javaAnnotationDocumented by lazy { extractor.referenceExternalClass("java.lang.annotation.Documented") }
|
||||
private val javaAnnotationDocumented by lazy {
|
||||
extractor.referenceExternalClass("java.lang.annotation.Documented")
|
||||
}
|
||||
|
||||
// Taken from AdditionalClassAnnotationLowering.kt
|
||||
private fun generateDocumentedAnnotation(irClass: IrClass): IrConstructorCall? {
|
||||
if (!irClass.hasAnnotation(StandardNames.FqNames.mustBeDocumented) ||
|
||||
irClass.hasAnnotation(JvmAnnotationNames.DOCUMENTED_ANNOTATION)
|
||||
) return null
|
||||
if (
|
||||
!irClass.hasAnnotation(StandardNames.FqNames.mustBeDocumented) ||
|
||||
irClass.hasAnnotation(JvmAnnotationNames.DOCUMENTED_ANNOTATION)
|
||||
)
|
||||
return null
|
||||
|
||||
val documentedConstructor = javaAnnotationDocumented?.declarations?.firstIsInstanceOrNull<IrConstructor>() ?: return null
|
||||
val documentedConstructor =
|
||||
javaAnnotationDocumented?.declarations?.firstIsInstanceOrNull<IrConstructor>()
|
||||
?: return null
|
||||
|
||||
return IrConstructorCallImpl.fromSymbolOwner(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, documentedConstructor.returnType, documentedConstructor.symbol, 0
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
documentedConstructor.returnType,
|
||||
documentedConstructor.symbol,
|
||||
0
|
||||
)
|
||||
}
|
||||
|
||||
fun generateJavaMetaAnnotations(c: IrClass, extractAnnotationTypeAccesses: Boolean) =
|
||||
// This is essentially AdditionalClassAnnotationLowering adapted to run outside the backend.
|
||||
listOfNotNull(generateTargetAnnotation(c), generateRetentionAnnotation(c), generateRepeatableAnnotation(c, extractAnnotationTypeAccesses), generateDocumentedAnnotation(c))
|
||||
listOfNotNull(
|
||||
generateTargetAnnotation(c),
|
||||
generateRetentionAnnotation(c),
|
||||
generateRepeatableAnnotation(c, extractAnnotationTypeAccesses),
|
||||
generateDocumentedAnnotation(c)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,93 +1,105 @@
|
||||
package com.github.codeql
|
||||
|
||||
import com.github.codeql.utils.*
|
||||
import com.github.codeql.utils.versions.*
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrPackageFragment
|
||||
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import com.github.codeql.utils.*
|
||||
import com.github.codeql.utils.versions.*
|
||||
|
||||
class PrimitiveTypeMapping(val logger: Logger, val pluginContext: IrPluginContext) {
|
||||
fun getPrimitiveInfo(s: IrSimpleType) =
|
||||
s.classOrNull?.let {
|
||||
if ((it.owner.parent as? IrPackageFragment)?.packageFqName == StandardNames.BUILT_INS_PACKAGE_FQ_NAME)
|
||||
if (
|
||||
(it.owner.parent as? IrPackageFragment)?.packageFqName ==
|
||||
StandardNames.BUILT_INS_PACKAGE_FQ_NAME
|
||||
)
|
||||
mapping[it.owner.name]
|
||||
else
|
||||
null
|
||||
else null
|
||||
}
|
||||
|
||||
data class PrimitiveTypeInfo(
|
||||
val primitiveName: String?,
|
||||
val otherIsPrimitive: Boolean,
|
||||
val javaClass: IrClass,
|
||||
val kotlinPackageName: String, val kotlinClassName: String
|
||||
val kotlinPackageName: String,
|
||||
val kotlinClassName: String
|
||||
)
|
||||
|
||||
private fun findClass(fqName: String, fallback: IrClass): IrClass {
|
||||
val symbol = getClassByFqName(pluginContext, fqName)
|
||||
if(symbol == null) {
|
||||
if (symbol == null) {
|
||||
logger.warn("Can't find $fqName")
|
||||
// Do the best we can
|
||||
return fallback
|
||||
} else {
|
||||
return symbol.owner
|
||||
return symbol.owner
|
||||
}
|
||||
}
|
||||
|
||||
private val mapping = {
|
||||
val kotlinByte = pluginContext.irBuiltIns.byteClass.owner
|
||||
val javaLangByte = findClass("java.lang.Byte", kotlinByte)
|
||||
val kotlinShort = pluginContext.irBuiltIns.shortClass.owner
|
||||
val javaLangShort = findClass("java.lang.Short", kotlinShort)
|
||||
val kotlinInt = pluginContext.irBuiltIns.intClass.owner
|
||||
val javaLangInteger = findClass("java.lang.Integer", kotlinInt)
|
||||
val kotlinLong = pluginContext.irBuiltIns.longClass.owner
|
||||
val javaLangLong = findClass("java.lang.Long", kotlinLong)
|
||||
private val mapping =
|
||||
{
|
||||
val kotlinByte = pluginContext.irBuiltIns.byteClass.owner
|
||||
val javaLangByte = findClass("java.lang.Byte", kotlinByte)
|
||||
val kotlinShort = pluginContext.irBuiltIns.shortClass.owner
|
||||
val javaLangShort = findClass("java.lang.Short", kotlinShort)
|
||||
val kotlinInt = pluginContext.irBuiltIns.intClass.owner
|
||||
val javaLangInteger = findClass("java.lang.Integer", kotlinInt)
|
||||
val kotlinLong = pluginContext.irBuiltIns.longClass.owner
|
||||
val javaLangLong = findClass("java.lang.Long", kotlinLong)
|
||||
|
||||
val kotlinUByte = findClass("kotlin.UByte", kotlinByte)
|
||||
val kotlinUShort = findClass("kotlin.UShort", kotlinShort)
|
||||
val kotlinUInt = findClass("kotlin.UInt", kotlinInt)
|
||||
val kotlinULong = findClass("kotlin.ULong", kotlinLong)
|
||||
val kotlinUByte = findClass("kotlin.UByte", kotlinByte)
|
||||
val kotlinUShort = findClass("kotlin.UShort", kotlinShort)
|
||||
val kotlinUInt = findClass("kotlin.UInt", kotlinInt)
|
||||
val kotlinULong = findClass("kotlin.ULong", kotlinLong)
|
||||
|
||||
val kotlinDouble = pluginContext.irBuiltIns.doubleClass.owner
|
||||
val javaLangDouble = findClass("java.lang.Double", kotlinDouble)
|
||||
val kotlinFloat = pluginContext.irBuiltIns.floatClass.owner
|
||||
val javaLangFloat = findClass("java.lang.Float", kotlinFloat)
|
||||
val kotlinDouble = pluginContext.irBuiltIns.doubleClass.owner
|
||||
val javaLangDouble = findClass("java.lang.Double", kotlinDouble)
|
||||
val kotlinFloat = pluginContext.irBuiltIns.floatClass.owner
|
||||
val javaLangFloat = findClass("java.lang.Float", kotlinFloat)
|
||||
|
||||
val kotlinBoolean = pluginContext.irBuiltIns.booleanClass.owner
|
||||
val javaLangBoolean = findClass("java.lang.Boolean", kotlinBoolean)
|
||||
val kotlinBoolean = pluginContext.irBuiltIns.booleanClass.owner
|
||||
val javaLangBoolean = findClass("java.lang.Boolean", kotlinBoolean)
|
||||
|
||||
val kotlinChar = pluginContext.irBuiltIns.charClass.owner
|
||||
val javaLangCharacter = findClass("java.lang.Character", kotlinChar)
|
||||
val kotlinChar = pluginContext.irBuiltIns.charClass.owner
|
||||
val javaLangCharacter = findClass("java.lang.Character", kotlinChar)
|
||||
|
||||
val kotlinUnit = pluginContext.irBuiltIns.unitClass.owner
|
||||
val kotlinUnit = pluginContext.irBuiltIns.unitClass.owner
|
||||
|
||||
val kotlinNothing = pluginContext.irBuiltIns.nothingClass.owner
|
||||
val javaLangVoid = findClass("java.lang.Void", kotlinNothing)
|
||||
val kotlinNothing = pluginContext.irBuiltIns.nothingClass.owner
|
||||
val javaLangVoid = findClass("java.lang.Void", kotlinNothing)
|
||||
|
||||
mapOf(
|
||||
StandardNames.FqNames._byte.shortName() to PrimitiveTypeInfo("byte", true, javaLangByte, "kotlin", "Byte"),
|
||||
StandardNames.FqNames._short.shortName() to PrimitiveTypeInfo("short", true, javaLangShort, "kotlin", "Short"),
|
||||
StandardNames.FqNames._int.shortName() to PrimitiveTypeInfo("int", true, javaLangInteger, "kotlin", "Int"),
|
||||
StandardNames.FqNames._long.shortName() to PrimitiveTypeInfo("long", true, javaLangLong, "kotlin", "Long"),
|
||||
|
||||
StandardNames.FqNames.uByteFqName.shortName() to PrimitiveTypeInfo("byte", true, kotlinUByte, "kotlin", "UByte"),
|
||||
StandardNames.FqNames.uShortFqName.shortName() to PrimitiveTypeInfo("short", true, kotlinUShort, "kotlin", "UShort"),
|
||||
StandardNames.FqNames.uIntFqName.shortName() to PrimitiveTypeInfo("int", true, kotlinUInt, "kotlin", "UInt"),
|
||||
StandardNames.FqNames.uLongFqName.shortName() to PrimitiveTypeInfo("long", true, kotlinULong, "kotlin", "ULong"),
|
||||
|
||||
StandardNames.FqNames._double.shortName() to PrimitiveTypeInfo("double", true, javaLangDouble, "kotlin", "Double"),
|
||||
StandardNames.FqNames._float.shortName() to PrimitiveTypeInfo("float", true, javaLangFloat, "kotlin", "Float"),
|
||||
|
||||
StandardNames.FqNames._boolean.shortName() to PrimitiveTypeInfo("boolean", true, javaLangBoolean, "kotlin", "Boolean"),
|
||||
|
||||
StandardNames.FqNames._char.shortName() to PrimitiveTypeInfo("char", true, javaLangCharacter, "kotlin", "Char"),
|
||||
|
||||
StandardNames.FqNames.unit.shortName() to PrimitiveTypeInfo("void", false, kotlinUnit, "kotlin", "Unit"),
|
||||
StandardNames.FqNames.nothing.shortName() to PrimitiveTypeInfo(null, true, javaLangVoid, "kotlin", "Nothing"),
|
||||
)
|
||||
}()
|
||||
mapOf(
|
||||
StandardNames.FqNames._byte.shortName() to
|
||||
PrimitiveTypeInfo("byte", true, javaLangByte, "kotlin", "Byte"),
|
||||
StandardNames.FqNames._short.shortName() to
|
||||
PrimitiveTypeInfo("short", true, javaLangShort, "kotlin", "Short"),
|
||||
StandardNames.FqNames._int.shortName() to
|
||||
PrimitiveTypeInfo("int", true, javaLangInteger, "kotlin", "Int"),
|
||||
StandardNames.FqNames._long.shortName() to
|
||||
PrimitiveTypeInfo("long", true, javaLangLong, "kotlin", "Long"),
|
||||
StandardNames.FqNames.uByteFqName.shortName() to
|
||||
PrimitiveTypeInfo("byte", true, kotlinUByte, "kotlin", "UByte"),
|
||||
StandardNames.FqNames.uShortFqName.shortName() to
|
||||
PrimitiveTypeInfo("short", true, kotlinUShort, "kotlin", "UShort"),
|
||||
StandardNames.FqNames.uIntFqName.shortName() to
|
||||
PrimitiveTypeInfo("int", true, kotlinUInt, "kotlin", "UInt"),
|
||||
StandardNames.FqNames.uLongFqName.shortName() to
|
||||
PrimitiveTypeInfo("long", true, kotlinULong, "kotlin", "ULong"),
|
||||
StandardNames.FqNames._double.shortName() to
|
||||
PrimitiveTypeInfo("double", true, javaLangDouble, "kotlin", "Double"),
|
||||
StandardNames.FqNames._float.shortName() to
|
||||
PrimitiveTypeInfo("float", true, javaLangFloat, "kotlin", "Float"),
|
||||
StandardNames.FqNames._boolean.shortName() to
|
||||
PrimitiveTypeInfo("boolean", true, javaLangBoolean, "kotlin", "Boolean"),
|
||||
StandardNames.FqNames._char.shortName() to
|
||||
PrimitiveTypeInfo("char", true, javaLangCharacter, "kotlin", "Char"),
|
||||
StandardNames.FqNames.unit.shortName() to
|
||||
PrimitiveTypeInfo("void", false, kotlinUnit, "kotlin", "Unit"),
|
||||
StandardNames.FqNames.nothing.shortName() to
|
||||
PrimitiveTypeInfo(null, true, javaLangVoid, "kotlin", "Nothing"),
|
||||
)
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -1,92 +1,87 @@
|
||||
package com.github.codeql
|
||||
|
||||
import com.github.codeql.KotlinUsesExtractor.LocallyVisibleFunctionLabels
|
||||
import com.semmle.extractor.java.PopulateFile
|
||||
import com.semmle.util.unicode.UTF8Util
|
||||
import java.io.BufferedWriter
|
||||
import java.io.File
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.path
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.declarations.path
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.util.SYNTHETIC_OFFSET
|
||||
|
||||
import com.semmle.extractor.java.PopulateFile
|
||||
import com.semmle.util.unicode.UTF8Util
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
|
||||
/**
|
||||
* Each `.trap` file has a `TrapLabelManager` while we are writing it.
|
||||
* It provides fresh TRAP label names, and maintains a mapping from keys
|
||||
* (`@"..."`) to labels.
|
||||
* Each `.trap` file has a `TrapLabelManager` while we are writing it. It provides fresh TRAP label
|
||||
* names, and maintains a mapping from keys (`@"..."`) to labels.
|
||||
*/
|
||||
class TrapLabelManager {
|
||||
/** The next integer to use as a label name. */
|
||||
private var nextInt: Int = 100
|
||||
|
||||
/** Returns a fresh label. */
|
||||
fun <T: AnyDbType> getFreshLabel(): Label<T> {
|
||||
fun <T : AnyDbType> getFreshLabel(): Label<T> {
|
||||
return IntLabel(nextInt++)
|
||||
}
|
||||
|
||||
/**
|
||||
* A mapping from a key (`@"..."`) to the label defined to be that
|
||||
* key, if any.
|
||||
*/
|
||||
/** A mapping from a key (`@"..."`) to the label defined to be that key, if any. */
|
||||
val labelMapping: MutableMap<String, Label<*>> = mutableMapOf<String, Label<*>>()
|
||||
|
||||
val anonymousTypeMapping: MutableMap<IrClass, TypeResults> = mutableMapOf()
|
||||
|
||||
val locallyVisibleFunctionLabelMapping: MutableMap<IrFunction, LocallyVisibleFunctionLabels> = mutableMapOf()
|
||||
val locallyVisibleFunctionLabelMapping: MutableMap<IrFunction, LocallyVisibleFunctionLabels> =
|
||||
mutableMapOf()
|
||||
|
||||
/**
|
||||
* The set of labels of generic specialisations that we have extracted
|
||||
* in this TRAP file.
|
||||
* We can't easily avoid duplication between TRAP files, as the labels
|
||||
* contain references to other labels, so we just accept this
|
||||
* duplication.
|
||||
* The set of labels of generic specialisations that we have extracted in this TRAP file. We
|
||||
* can't easily avoid duplication between TRAP files, as the labels contain references to other
|
||||
* labels, so we just accept this duplication.
|
||||
*/
|
||||
val genericSpecialisationsExtracted = HashSet<String>()
|
||||
|
||||
/**
|
||||
* Sometimes, when we extract a file class we don't have the IrFile
|
||||
* for it, so we are not able to give it a location. This means that
|
||||
* the location is written outside of the label creation.
|
||||
* This allows us to keep track of whether we've written the location
|
||||
* already in this TRAP file, to avoid duplication.
|
||||
* Sometimes, when we extract a file class we don't have the IrFile for it, so we are not able
|
||||
* to give it a location. This means that the location is written outside of the label creation.
|
||||
* This allows us to keep track of whether we've written the location already in this TRAP file,
|
||||
* to avoid duplication.
|
||||
*/
|
||||
val fileClassLocationsExtracted = HashSet<IrFile>()
|
||||
}
|
||||
|
||||
/**
|
||||
* A `TrapWriter` is used to write TRAP to a particular TRAP file.
|
||||
* There may be multiple `TrapWriter`s for the same file, as different
|
||||
* instances will have different additional state, but they must all
|
||||
* share the same `TrapLabelManager` and `BufferedWriter`.
|
||||
* A `TrapWriter` is used to write TRAP to a particular TRAP file. There may be multiple
|
||||
* `TrapWriter`s for the same file, as different instances will have different additional state, but
|
||||
* they must all share the same `TrapLabelManager` and `BufferedWriter`.
|
||||
*/
|
||||
// TODO lm was `protected` before anonymousTypeMapping and locallyVisibleFunctionLabelMapping moved into it. Should we re-protect it and provide accessors?
|
||||
abstract class TrapWriter (protected val loggerBase: LoggerBase, val lm: TrapLabelManager, private val bw: BufferedWriter) {
|
||||
// TODO lm was `protected` before anonymousTypeMapping and locallyVisibleFunctionLabelMapping moved
|
||||
// into it. Should we re-protect it and provide accessors?
|
||||
abstract class TrapWriter(
|
||||
protected val loggerBase: LoggerBase,
|
||||
val lm: TrapLabelManager,
|
||||
private val bw: BufferedWriter
|
||||
) {
|
||||
abstract fun getDiagnosticTrapWriter(): DiagnosticTrapWriter
|
||||
|
||||
/**
|
||||
* Returns the label that is defined to be the given key, if such
|
||||
* a label exists, and `null` otherwise. Most users will want to use
|
||||
* `getLabelFor` instead, which allows non-existent labels to be
|
||||
* initialised.
|
||||
* Returns the label that is defined to be the given key, if such a label exists, and `null`
|
||||
* otherwise. Most users will want to use `getLabelFor` instead, which allows non-existent
|
||||
* labels to be initialised.
|
||||
*/
|
||||
fun <T: AnyDbType> getExistingLabelFor(key: String): Label<T>? {
|
||||
fun <T : AnyDbType> getExistingLabelFor(key: String): Label<T>? {
|
||||
return lm.labelMapping.get(key)?.cast<T>()
|
||||
}
|
||||
/**
|
||||
* Returns the label for the given key, if one exists.
|
||||
* Otherwise, a fresh label is bound to that key, `initialise`
|
||||
* is run on it, and it is returned.
|
||||
* Returns the label for the given key, if one exists. Otherwise, a fresh label is bound to that
|
||||
* key, `initialise` is run on it, and it is returned.
|
||||
*/
|
||||
@JvmOverloads // Needed so Java can call a method with an optional argument
|
||||
fun <T: AnyDbType> getLabelFor(key: String, initialise: (Label<T>) -> Unit = {}): Label<T> {
|
||||
fun <T : AnyDbType> getLabelFor(key: String, initialise: (Label<T>) -> Unit = {}): Label<T> {
|
||||
val maybeLabel: Label<T>? = getExistingLabelFor(key)
|
||||
if(maybeLabel == null) {
|
||||
if (maybeLabel == null) {
|
||||
val label: Label<T> = lm.getFreshLabel()
|
||||
lm.labelMapping.put(key, label)
|
||||
writeTrap("$label = $key\n")
|
||||
@@ -97,30 +92,24 @@ abstract class TrapWriter (protected val loggerBase: LoggerBase, val lm: TrapLab
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a label for a fresh ID (i.e. a new label bound to `*`).
|
||||
*/
|
||||
fun <T: AnyDbType> getFreshIdLabel(): Label<T> {
|
||||
/** Returns a label for a fresh ID (i.e. a new label bound to `*`). */
|
||||
fun <T : AnyDbType> getFreshIdLabel(): Label<T> {
|
||||
val label: Label<T> = lm.getFreshLabel()
|
||||
writeTrap("$label = *\n")
|
||||
return label
|
||||
}
|
||||
|
||||
/**
|
||||
* It is not easy to assign keys to local variables, so they get
|
||||
* given `*` IDs. However, the same variable may be referred to
|
||||
* from distant places in the IR, so we need a way to find out
|
||||
* which label is used for a given local variable. This information
|
||||
* is stored in this mapping.
|
||||
*/
|
||||
private val variableLabelMapping: MutableMap<IrVariable, Label<out DbLocalvar>> = mutableMapOf<IrVariable, Label<out DbLocalvar>>()
|
||||
/**
|
||||
* This returns the label used for a local variable, creating one
|
||||
* if none currently exists.
|
||||
* It is not easy to assign keys to local variables, so they get given `*` IDs. However, the
|
||||
* same variable may be referred to from distant places in the IR, so we need a way to find out
|
||||
* which label is used for a given local variable. This information is stored in this mapping.
|
||||
*/
|
||||
private val variableLabelMapping: MutableMap<IrVariable, Label<out DbLocalvar>> =
|
||||
mutableMapOf<IrVariable, Label<out DbLocalvar>>()
|
||||
/** This returns the label used for a local variable, creating one if none currently exists. */
|
||||
fun <T> getVariableLabelFor(v: IrVariable): Label<out DbLocalvar> {
|
||||
val maybeLabel = variableLabelMapping.get(v)
|
||||
if(maybeLabel == null) {
|
||||
if (maybeLabel == null) {
|
||||
val label = getFreshIdLabel<DbLocalvar>()
|
||||
variableLabelMapping.put(v, label)
|
||||
return label
|
||||
@@ -134,43 +123,41 @@ abstract class TrapWriter (protected val loggerBase: LoggerBase, val lm: TrapLab
|
||||
}
|
||||
|
||||
/**
|
||||
* This returns a label for the location described by its arguments.
|
||||
* Typically users will not want to call this directly, but instead
|
||||
* use `unknownLocation`, or overloads of this defined by subclasses.
|
||||
* This returns a label for the location described by its arguments. Typically users will not
|
||||
* want to call this directly, but instead use `unknownLocation`, or overloads of this defined
|
||||
* by subclasses.
|
||||
*/
|
||||
fun getLocation(fileId: Label<DbFile>, startLine: Int, startColumn: Int, endLine: Int, endColumn: Int): Label<DbLocation> {
|
||||
fun getLocation(
|
||||
fileId: Label<DbFile>,
|
||||
startLine: Int,
|
||||
startColumn: Int,
|
||||
endLine: Int,
|
||||
endColumn: Int
|
||||
): Label<DbLocation> {
|
||||
return getLabelFor("@\"loc,{$fileId},$startLine,$startColumn,$endLine,$endColumn\"") {
|
||||
writeLocations_default(it, fileId, startLine, startColumn, endLine, endColumn)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The label for the 'unknown' file ID.
|
||||
* Users will want to use `unknownLocation` instead.
|
||||
* This is lazy, as we don't want to define it in a TRAP file unless
|
||||
* the TRAP file actually contains something in the 'unknown' file.
|
||||
* The label for the 'unknown' file ID. Users will want to use `unknownLocation` instead. This
|
||||
* is lazy, as we don't want to define it in a TRAP file unless the TRAP file actually contains
|
||||
* something in the 'unknown' file.
|
||||
*/
|
||||
protected val unknownFileId: Label<DbFile> by lazy {
|
||||
val unknownFileLabel = "@\";sourcefile\""
|
||||
getLabelFor(unknownFileLabel, {
|
||||
writeFiles(it, "")
|
||||
})
|
||||
getLabelFor(unknownFileLabel, { writeFiles(it, "") })
|
||||
}
|
||||
|
||||
/**
|
||||
* The label for the 'unknown' location.
|
||||
* This is lazy, as we don't want to define it in a TRAP file unless
|
||||
* the TRAP file actually contains something with an 'unknown'
|
||||
* location.
|
||||
* The label for the 'unknown' location. This is lazy, as we don't want to define it in a TRAP
|
||||
* file unless the TRAP file actually contains something with an 'unknown' location.
|
||||
*/
|
||||
val unknownLocation: Label<DbLocation> by lazy {
|
||||
getWholeFileLocation(unknownFileId)
|
||||
}
|
||||
val unknownLocation: Label<DbLocation> by lazy { getWholeFileLocation(unknownFileId) }
|
||||
|
||||
/**
|
||||
* Returns the label for the file `filePath`.
|
||||
* If `populateFileTables` is true, then this also adds rows to the
|
||||
* `files` and `folders` tables for this file.
|
||||
* Returns the label for the file `filePath`. If `populateFileTables` is true, then this also
|
||||
* adds rows to the `files` and `folders` tables for this file.
|
||||
*/
|
||||
fun mkFileId(filePath: String, populateFileTables: Boolean): Label<DbFile> {
|
||||
// If a file is in a jar, then the Kotlin compiler gives
|
||||
@@ -178,65 +165,65 @@ abstract class TrapWriter (protected val loggerBase: LoggerBase, val lm: TrapLab
|
||||
// it as appropriate, to make the right file ID.
|
||||
val populateFile = PopulateFile(this)
|
||||
val splitFilePath = filePath.split("!/")
|
||||
if(splitFilePath.size == 1) {
|
||||
if (splitFilePath.size == 1) {
|
||||
return populateFile.getFileLabel(File(filePath), populateFileTables)
|
||||
} else {
|
||||
return populateFile.getFileInJarLabel(File(splitFilePath.get(0)), splitFilePath.get(1), populateFileTables)
|
||||
return populateFile.getFileInJarLabel(
|
||||
File(splitFilePath.get(0)),
|
||||
splitFilePath.get(1),
|
||||
populateFileTables
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If you have an ID for a file, then this gets a label for the
|
||||
* location representing the whole of that file.
|
||||
* If you have an ID for a file, then this gets a label for the location representing the whole
|
||||
* of that file.
|
||||
*/
|
||||
fun getWholeFileLocation(fileId: Label<DbFile>): Label<DbLocation> {
|
||||
return getLocation(fileId, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a raw string into the TRAP file. Users should call one of
|
||||
* the wrapper functions instead.
|
||||
* Write a raw string into the TRAP file. Users should call one of the wrapper functions
|
||||
* instead.
|
||||
*/
|
||||
fun writeTrap(trap: String) {
|
||||
bw.write(trap)
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a comment into the TRAP file.
|
||||
*/
|
||||
/** Write a comment into the TRAP file. */
|
||||
fun writeComment(comment: String) {
|
||||
writeTrap("// ${comment.replace("\n", "\n// ")}\n")
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the TRAP file.
|
||||
*/
|
||||
/** Flush the TRAP file. */
|
||||
fun flush() {
|
||||
bw.flush()
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape a string so that it can be used in a TRAP string literal,
|
||||
* i.e. with `"` escaped as `""`.
|
||||
*/
|
||||
* Escape a string so that it can be used in a TRAP string literal, i.e. with `"` escaped as
|
||||
* `""`.
|
||||
*/
|
||||
fun escapeTrapString(str: String) = str.replace("\"", "\"\"")
|
||||
|
||||
/**
|
||||
* TRAP string literals are limited to 1 megabyte.
|
||||
*/
|
||||
/** TRAP string literals are limited to 1 megabyte. */
|
||||
private val MAX_STRLEN = 1.shl(20)
|
||||
|
||||
/**
|
||||
* Truncate a string, if necessary, so that it can be used as a TRAP
|
||||
* string literal. TRAP string literals are limited to 1 megabyte.
|
||||
* Truncate a string, if necessary, so that it can be used as a TRAP string literal. TRAP string
|
||||
* literals are limited to 1 megabyte.
|
||||
*/
|
||||
fun truncateString(str: String): String {
|
||||
val len = str.length
|
||||
val newLen = UTF8Util.encodablePrefixLength(str, MAX_STRLEN)
|
||||
if (newLen < len) {
|
||||
loggerBase.warn(this.getDiagnosticTrapWriter(),
|
||||
loggerBase.warn(
|
||||
this.getDiagnosticTrapWriter(),
|
||||
"Truncated string of length $len",
|
||||
"Truncated string of length $len, starting '${str.take(100)}', ending '${str.takeLast(100)}'")
|
||||
"Truncated string of length $len, starting '${str.take(100)}', ending '${str.takeLast(100)}'"
|
||||
)
|
||||
return str.take(newLen)
|
||||
} else {
|
||||
return str
|
||||
@@ -244,69 +231,75 @@ abstract class TrapWriter (protected val loggerBase: LoggerBase, val lm: TrapLab
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a FileTrapWriter like this one (using the same label manager,
|
||||
* writer etc), but using the given `filePath` for locations.
|
||||
* Gets a FileTrapWriter like this one (using the same label manager, writer etc), but using the
|
||||
* given `filePath` for locations.
|
||||
*/
|
||||
fun makeFileTrapWriter(filePath: String, populateFileTables: Boolean) =
|
||||
FileTrapWriter(loggerBase, lm, bw, this.getDiagnosticTrapWriter(), filePath, populateFileTables)
|
||||
FileTrapWriter(
|
||||
loggerBase,
|
||||
lm,
|
||||
bw,
|
||||
this.getDiagnosticTrapWriter(),
|
||||
filePath,
|
||||
populateFileTables
|
||||
)
|
||||
|
||||
/**
|
||||
* Gets a FileTrapWriter like this one (using the same label manager,
|
||||
* writer etc), but using the given `IrFile` for locations.
|
||||
* Gets a FileTrapWriter like this one (using the same label manager, writer etc), but using the
|
||||
* given `IrFile` for locations.
|
||||
*/
|
||||
fun makeSourceFileTrapWriter(file: IrFile, populateFileTables: Boolean) =
|
||||
SourceFileTrapWriter(loggerBase, lm, bw, this.getDiagnosticTrapWriter(), file, populateFileTables)
|
||||
SourceFileTrapWriter(
|
||||
loggerBase,
|
||||
lm,
|
||||
bw,
|
||||
this.getDiagnosticTrapWriter(),
|
||||
file,
|
||||
populateFileTables
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A `PlainTrapWriter` has no additional context of its own.
|
||||
*/
|
||||
class PlainTrapWriter (
|
||||
/** A `PlainTrapWriter` has no additional context of its own. */
|
||||
class PlainTrapWriter(
|
||||
loggerBase: LoggerBase,
|
||||
lm: TrapLabelManager,
|
||||
bw: BufferedWriter,
|
||||
val dtw: DiagnosticTrapWriter
|
||||
): TrapWriter (loggerBase, lm, bw) {
|
||||
) : TrapWriter(loggerBase, lm, bw) {
|
||||
override fun getDiagnosticTrapWriter(): DiagnosticTrapWriter {
|
||||
return dtw
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `DiagnosticTrapWriter` is a TrapWriter that diagnostics can be
|
||||
* written to; i.e. it has the #compilation label defined. In practice,
|
||||
* this means that it is a TrapWriter for the invocation TRAP file.
|
||||
* A `DiagnosticTrapWriter` is a TrapWriter that diagnostics can be written to; i.e. it has
|
||||
* the #compilation label defined. In practice, this means that it is a TrapWriter for the
|
||||
* invocation TRAP file.
|
||||
*/
|
||||
class DiagnosticTrapWriter (
|
||||
loggerBase: LoggerBase,
|
||||
lm: TrapLabelManager,
|
||||
bw: BufferedWriter
|
||||
): TrapWriter (loggerBase, lm, bw) {
|
||||
class DiagnosticTrapWriter(loggerBase: LoggerBase, lm: TrapLabelManager, bw: BufferedWriter) :
|
||||
TrapWriter(loggerBase, lm, bw) {
|
||||
override fun getDiagnosticTrapWriter(): DiagnosticTrapWriter {
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `FileTrapWriter` is used when we know which file we are extracting
|
||||
* entities from, so we can at least give the right file as a location.
|
||||
* A `FileTrapWriter` is used when we know which file we are extracting entities from, so we can at
|
||||
* least give the right file as a location.
|
||||
*
|
||||
* An ID for the file will be created, and if `populateFileTables` is
|
||||
* true then we will also add rows to the `files` and `folders` tables
|
||||
* for it.
|
||||
* An ID for the file will be created, and if `populateFileTables` is true then we will also add
|
||||
* rows to the `files` and `folders` tables for it.
|
||||
*/
|
||||
open class FileTrapWriter (
|
||||
open class FileTrapWriter(
|
||||
loggerBase: LoggerBase,
|
||||
lm: TrapLabelManager,
|
||||
bw: BufferedWriter,
|
||||
val dtw: DiagnosticTrapWriter,
|
||||
val filePath: String,
|
||||
populateFileTables: Boolean
|
||||
): TrapWriter (loggerBase, lm, bw) {
|
||||
) : TrapWriter(loggerBase, lm, bw) {
|
||||
|
||||
/**
|
||||
* The ID for the file that we are extracting from.
|
||||
*/
|
||||
/** The ID for the file that we are extracting from. */
|
||||
val fileId = mkFileId(filePath, populateFileTables)
|
||||
|
||||
override fun getDiagnosticTrapWriter(): DiagnosticTrapWriter {
|
||||
@@ -320,7 +313,12 @@ open class FileTrapWriter (
|
||||
|
||||
var currentMin = default
|
||||
for (option in options) {
|
||||
if (option != null && option != UNDEFINED_OFFSET && option != SYNTHETIC_OFFSET && option < currentMin) {
|
||||
if (
|
||||
option != null &&
|
||||
option != UNDEFINED_OFFSET &&
|
||||
option != SYNTHETIC_OFFSET &&
|
||||
option < currentMin
|
||||
) {
|
||||
currentMin = option
|
||||
}
|
||||
}
|
||||
@@ -344,15 +342,13 @@ open class FileTrapWriter (
|
||||
return e.endOffset
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a label for the location of `e`.
|
||||
*/
|
||||
/** Gets a label for the location of `e`. */
|
||||
fun getLocation(e: IrElement): Label<DbLocation> {
|
||||
return getLocation(getStartOffset(e), getEndOffset(e))
|
||||
}
|
||||
/**
|
||||
* Gets a label for the location corresponding to `startOffset` and
|
||||
* `endOffset` within this file.
|
||||
* Gets a label for the location corresponding to `startOffset` and `endOffset` within this
|
||||
* file.
|
||||
*/
|
||||
open fun getLocation(startOffset: Int, endOffset: Int): Label<DbLocation> {
|
||||
// We don't have a FileEntry to look up the offsets in, so all
|
||||
@@ -360,8 +356,8 @@ open class FileTrapWriter (
|
||||
return getWholeFileLocation()
|
||||
}
|
||||
/**
|
||||
* Gets the location of `e` as a human-readable string. Only used in
|
||||
* log messages and exception messages.
|
||||
* Gets the location of `e` as a human-readable string. Only used in log messages and exception
|
||||
* messages.
|
||||
*/
|
||||
open fun getLocationString(e: IrElement): String {
|
||||
// We don't have a FileEntry to look up the offsets in, so all
|
||||
@@ -371,50 +367,54 @@ open class FileTrapWriter (
|
||||
// to be 0.
|
||||
return "file://$filePath"
|
||||
}
|
||||
/**
|
||||
* Gets a label for the location representing the whole of this file.
|
||||
*/
|
||||
/** Gets a label for the location representing the whole of this file. */
|
||||
fun getWholeFileLocation(): Label<DbLocation> {
|
||||
return getWholeFileLocation(fileId)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `SourceFileTrapWriter` is used when not only do we know which file
|
||||
* we are extracting entities from, but we also have an `IrFileEntry`
|
||||
* (from an `IrFile`) which allows us to map byte offsets to line
|
||||
* and column numbers.
|
||||
* A `SourceFileTrapWriter` is used when not only do we know which file we are extracting entities
|
||||
* from, but we also have an `IrFileEntry` (from an `IrFile`) which allows us to map byte offsets to
|
||||
* line and column numbers.
|
||||
*
|
||||
* An ID for the file will be created, and if `populateFileTables` is
|
||||
* true then we will also add rows to the `files` and `folders` tables
|
||||
* for it.
|
||||
* An ID for the file will be created, and if `populateFileTables` is true then we will also add
|
||||
* rows to the `files` and `folders` tables for it.
|
||||
*/
|
||||
class SourceFileTrapWriter (
|
||||
class SourceFileTrapWriter(
|
||||
loggerBase: LoggerBase,
|
||||
lm: TrapLabelManager,
|
||||
bw: BufferedWriter,
|
||||
dtw: DiagnosticTrapWriter,
|
||||
val irFile: IrFile,
|
||||
populateFileTables: Boolean) :
|
||||
FileTrapWriter(loggerBase, lm, bw, dtw, irFile.path, populateFileTables) {
|
||||
populateFileTables: Boolean
|
||||
) : FileTrapWriter(loggerBase, lm, bw, dtw, irFile.path, populateFileTables) {
|
||||
|
||||
/**
|
||||
* The file entry for the file that we are extracting from.
|
||||
* Used to map offsets to line/column numbers.
|
||||
* The file entry for the file that we are extracting from. Used to map offsets to line/column
|
||||
* numbers.
|
||||
*/
|
||||
private val fileEntry = irFile.fileEntry
|
||||
|
||||
override fun getLocation(startOffset: Int, endOffset: Int): Label<DbLocation> {
|
||||
if (startOffset == UNDEFINED_OFFSET || endOffset == UNDEFINED_OFFSET) {
|
||||
if (startOffset != endOffset) {
|
||||
loggerBase.warn(dtw, "Location with inconsistent offsets (start $startOffset, end $endOffset)", null)
|
||||
loggerBase.warn(
|
||||
dtw,
|
||||
"Location with inconsistent offsets (start $startOffset, end $endOffset)",
|
||||
null
|
||||
)
|
||||
}
|
||||
return getWholeFileLocation()
|
||||
}
|
||||
|
||||
if (startOffset == SYNTHETIC_OFFSET || endOffset == SYNTHETIC_OFFSET) {
|
||||
if (startOffset != endOffset) {
|
||||
loggerBase.warn(dtw, "Location with inconsistent offsets (start $startOffset, end $endOffset)", null)
|
||||
loggerBase.warn(
|
||||
dtw,
|
||||
"Location with inconsistent offsets (start $startOffset, end $endOffset)",
|
||||
null
|
||||
)
|
||||
}
|
||||
return getWholeFileLocation()
|
||||
}
|
||||
@@ -428,28 +428,37 @@ class SourceFileTrapWriter (
|
||||
fileEntry.getLineNumber(startOffset) + 1,
|
||||
fileEntry.getColumnNumber(startOffset) + 1,
|
||||
fileEntry.getLineNumber(endOffset) + 1,
|
||||
fileEntry.getColumnNumber(endOffset) + endColumnOffset)
|
||||
fileEntry.getColumnNumber(endOffset) + endColumnOffset
|
||||
)
|
||||
}
|
||||
|
||||
override fun getLocationString(e: IrElement): String {
|
||||
if (e.startOffset == UNDEFINED_OFFSET || e.endOffset == UNDEFINED_OFFSET) {
|
||||
if (e.startOffset != e.endOffset) {
|
||||
loggerBase.warn(dtw, "Location with inconsistent offsets (start ${e.startOffset}, end ${e.endOffset})", null)
|
||||
loggerBase.warn(
|
||||
dtw,
|
||||
"Location with inconsistent offsets (start ${e.startOffset}, end ${e.endOffset})",
|
||||
null
|
||||
)
|
||||
}
|
||||
return "<unknown location while processing $filePath>"
|
||||
}
|
||||
|
||||
if (e.startOffset == SYNTHETIC_OFFSET || e.endOffset == SYNTHETIC_OFFSET) {
|
||||
if (e.startOffset != e.endOffset) {
|
||||
loggerBase.warn(dtw, "Location with inconsistent offsets (start ${e.startOffset}, end ${e.endOffset})", null)
|
||||
loggerBase.warn(
|
||||
dtw,
|
||||
"Location with inconsistent offsets (start ${e.startOffset}, end ${e.endOffset})",
|
||||
null
|
||||
)
|
||||
}
|
||||
return "<synthetic location while processing $filePath>"
|
||||
}
|
||||
|
||||
val startLine = fileEntry.getLineNumber(e.startOffset) + 1
|
||||
val startLine = fileEntry.getLineNumber(e.startOffset) + 1
|
||||
val startColumn = fileEntry.getColumnNumber(e.startOffset) + 1
|
||||
val endLine = fileEntry.getLineNumber(e.endOffset) + 1
|
||||
val endColumn = fileEntry.getColumnNumber(e.endOffset)
|
||||
val endLine = fileEntry.getLineNumber(e.endOffset) + 1
|
||||
val endColumn = fileEntry.getColumnNumber(e.endOffset)
|
||||
return "file://$filePath:$startLine:$startColumn:$endLine:$endColumn"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,13 +8,16 @@ import org.jetbrains.kotlin.ir.expressions.IrBody
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.util.parentClassOrNull
|
||||
|
||||
open class CommentExtractor(protected val fileExtractor: KotlinFileExtractor, protected val file: IrFile, protected val fileLabel: Label<out DbFile>) {
|
||||
open class CommentExtractor(
|
||||
protected val fileExtractor: KotlinFileExtractor,
|
||||
protected val file: IrFile,
|
||||
protected val fileLabel: Label<out DbFile>
|
||||
) {
|
||||
protected val tw = fileExtractor.tw
|
||||
protected val logger = fileExtractor.logger
|
||||
|
||||
protected fun getLabel(element: IrElement): Label<out DbTop>? {
|
||||
if (element == file)
|
||||
return fileLabel
|
||||
if (element == file) return fileLabel
|
||||
|
||||
if (element is IrValueParameter && element.index == -1) {
|
||||
// Don't attribute comments to the implicit `this` parameter of a function.
|
||||
@@ -22,18 +25,21 @@ open class CommentExtractor(protected val fileExtractor: KotlinFileExtractor, pr
|
||||
}
|
||||
|
||||
val label: String
|
||||
val existingLabel = if (element is IrVariable) {
|
||||
// local variables are not named globally, so we need to get them from the variable label cache
|
||||
label = "variable ${element.name.asString()}"
|
||||
tw.getExistingVariableLabelFor(element)
|
||||
} else if (element is IrFunction && element.isLocalFunction()) {
|
||||
// local functions are not named globally, so we need to get them from the local function label cache
|
||||
label = "local function ${element.name.asString()}"
|
||||
fileExtractor.getExistingLocallyVisibleFunctionLabel(element)
|
||||
} else {
|
||||
label = getLabelForNamedElement(element) ?: return null
|
||||
tw.getExistingLabelFor<DbTop>(label)
|
||||
}
|
||||
val existingLabel =
|
||||
if (element is IrVariable) {
|
||||
// local variables are not named globally, so we need to get them from the variable
|
||||
// label cache
|
||||
label = "variable ${element.name.asString()}"
|
||||
tw.getExistingVariableLabelFor(element)
|
||||
} else if (element is IrFunction && element.isLocalFunction()) {
|
||||
// local functions are not named globally, so we need to get them from the local
|
||||
// function label cache
|
||||
label = "local function ${element.name.asString()}"
|
||||
fileExtractor.getExistingLocallyVisibleFunctionLabel(element)
|
||||
} else {
|
||||
label = getLabelForNamedElement(element) ?: return null
|
||||
tw.getExistingLabelFor<DbTop>(label)
|
||||
}
|
||||
if (existingLabel == null) {
|
||||
logger.warn("Couldn't get existing label for $label")
|
||||
return null
|
||||
@@ -41,7 +47,7 @@ open class CommentExtractor(protected val fileExtractor: KotlinFileExtractor, pr
|
||||
return existingLabel
|
||||
}
|
||||
|
||||
private fun getLabelForNamedElement(element: IrElement) : String? {
|
||||
private fun getLabelForNamedElement(element: IrElement): String? {
|
||||
when (element) {
|
||||
is IrClass -> return fileExtractor.getClassLabel(element, listOf()).classLabel
|
||||
is IrTypeParameter -> return fileExtractor.getTypeParameterLabel(element)
|
||||
@@ -57,14 +63,14 @@ open class CommentExtractor(protected val fileExtractor: KotlinFileExtractor, pr
|
||||
is IrField -> return fileExtractor.getFieldLabel(element)
|
||||
is IrEnumEntry -> return fileExtractor.getEnumEntryLabel(element)
|
||||
is IrTypeAlias -> return fileExtractor.getTypeAliasLabel(element)
|
||||
|
||||
is IrAnonymousInitializer -> {
|
||||
val parentClass = element.parentClassOrNull
|
||||
if (parentClass == null) {
|
||||
logger.warnElement("Parent of anonymous initializer is not a class", element)
|
||||
return null
|
||||
}
|
||||
// Assign the comment to the class. The content of the `init` blocks might be extracted in multiple constructors.
|
||||
// Assign the comment to the class. The content of the `init` blocks might be
|
||||
// extracted in multiple constructors.
|
||||
return getLabelForNamedElement(parentClass)
|
||||
}
|
||||
|
||||
@@ -74,7 +80,10 @@ open class CommentExtractor(protected val fileExtractor: KotlinFileExtractor, pr
|
||||
|
||||
// todo add others:
|
||||
else -> {
|
||||
logger.warnElement("Unhandled element type found during comment extraction: ${element::class}", element)
|
||||
logger.warnElement(
|
||||
"Unhandled element type found during comment extraction: ${element::class}",
|
||||
element
|
||||
)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,18 @@ import org.jetbrains.kotlin.psi.KtVisitor
|
||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
|
||||
class CommentExtractorPSI(fileExtractor: KotlinFileExtractor, file: IrFile, fileLabel: Label<out DbFile>): CommentExtractor(fileExtractor, file, fileLabel) {
|
||||
class CommentExtractorPSI(
|
||||
fileExtractor: KotlinFileExtractor,
|
||||
file: IrFile,
|
||||
fileLabel: Label<out DbFile>
|
||||
) : CommentExtractor(fileExtractor, file, fileLabel) {
|
||||
// Returns true if it extracted the comments; false otherwise.
|
||||
fun extract(): Boolean {
|
||||
val psi2Ir = getPsi2Ir()
|
||||
if (psi2Ir == null) {
|
||||
logger.warn("Comments will not be extracted as Kotlin version is too old (${KotlinCompilerVersion.getVersion()})")
|
||||
logger.warn(
|
||||
"Comments will not be extracted as Kotlin version is too old (${KotlinCompilerVersion.getVersion()})"
|
||||
)
|
||||
return false
|
||||
}
|
||||
val ktFile = psi2Ir.getKtFile(file)
|
||||
@@ -37,28 +43,30 @@ class CommentExtractorPSI(fileExtractor: KotlinFileExtractor, file: IrFile, file
|
||||
override fun visitElement(element: PsiElement) {
|
||||
element.acceptChildren(this)
|
||||
|
||||
// Slightly hacky, but `visitComment` doesn't seem to visit comments with `tokenType` `KtTokens.DOC_COMMENT`
|
||||
if (element is PsiComment){
|
||||
// Slightly hacky, but `visitComment` doesn't seem to visit comments with
|
||||
// `tokenType` `KtTokens.DOC_COMMENT`
|
||||
if (element is PsiComment) {
|
||||
visitCommentElement(element)
|
||||
}
|
||||
}
|
||||
|
||||
private fun visitCommentElement(comment: PsiComment) {
|
||||
val type: CommentType = when (comment.tokenType) {
|
||||
KtTokens.EOL_COMMENT -> {
|
||||
CommentType.SingleLine
|
||||
val type: CommentType =
|
||||
when (comment.tokenType) {
|
||||
KtTokens.EOL_COMMENT -> {
|
||||
CommentType.SingleLine
|
||||
}
|
||||
KtTokens.BLOCK_COMMENT -> {
|
||||
CommentType.Block
|
||||
}
|
||||
KtTokens.DOC_COMMENT -> {
|
||||
CommentType.Doc
|
||||
}
|
||||
else -> {
|
||||
logger.warn("Unhandled comment token type: ${comment.tokenType}")
|
||||
return
|
||||
}
|
||||
}
|
||||
KtTokens.BLOCK_COMMENT -> {
|
||||
CommentType.Block
|
||||
}
|
||||
KtTokens.DOC_COMMENT -> {
|
||||
CommentType.Doc
|
||||
}
|
||||
else -> {
|
||||
logger.warn("Unhandled comment token type: ${comment.tokenType}")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
val commentLabel = tw.getFreshIdLabel<DbKtcomment>()
|
||||
tw.writeKtComments(commentLabel, type.value, comment.text)
|
||||
@@ -101,10 +109,12 @@ class CommentExtractorPSI(fileExtractor: KotlinFileExtractor, file: IrFile, file
|
||||
}
|
||||
}
|
||||
|
||||
private fun getKDocOwner(comment: KDoc) : PsiElement? {
|
||||
private fun getKDocOwner(comment: KDoc): PsiElement? {
|
||||
val owner = comment.owner
|
||||
if (owner == null) {
|
||||
logger.warn("Couldn't get owner of KDoc. The comment is extracted without an owner.")
|
||||
logger.warn(
|
||||
"Couldn't get owner of KDoc. The comment is extracted without an owner."
|
||||
)
|
||||
}
|
||||
return owner
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.github.codeql.comments
|
||||
|
||||
enum class CommentType(val value: Int) {
|
||||
SingleLine(1), Block(2), Doc(3)
|
||||
}
|
||||
SingleLine(1),
|
||||
Block(2),
|
||||
Doc(3)
|
||||
}
|
||||
|
||||
@@ -1,43 +1,47 @@
|
||||
package com.github.codeql
|
||||
|
||||
// Functions copied from stdlib/jdk7/src/kotlin/AutoCloseable.kt, which is not available within kotlinc,
|
||||
// Functions copied from stdlib/jdk7/src/kotlin/AutoCloseable.kt, which is not available within
|
||||
// kotlinc,
|
||||
// but allows the `.use` pattern to be applied to JDK7 AutoCloseables:
|
||||
|
||||
/**
|
||||
* Executes the given [block] function on this resource and then closes it down correctly whether an exception
|
||||
* is thrown or not.
|
||||
* Executes the given [block] function on this resource and then closes it down correctly whether an
|
||||
* exception is thrown or not.
|
||||
*
|
||||
* In case if the resource is being closed due to an exception occurred in [block], and the closing also fails with an exception,
|
||||
* the latter is added to the [suppressed][java.lang.Throwable.addSuppressed] exceptions of the former.
|
||||
* In case if the resource is being closed due to an exception occurred in [block], and the closing
|
||||
* also fails with an exception, the latter is added to the
|
||||
* [suppressed][java.lang.Throwable.addSuppressed] exceptions of the former.
|
||||
*
|
||||
* @param block a function to process this [AutoCloseable] resource.
|
||||
* @return the result of [block] function invoked on this resource.
|
||||
*/
|
||||
public inline fun <T : AutoCloseable?, R> T.useAC(block: (T) -> R): R {
|
||||
var exception: Throwable? = null
|
||||
try {
|
||||
return block(this)
|
||||
} catch (e: Throwable) {
|
||||
exception = e
|
||||
throw e
|
||||
} finally {
|
||||
this.closeFinallyAC(exception)
|
||||
}
|
||||
var exception: Throwable? = null
|
||||
try {
|
||||
return block(this)
|
||||
} catch (e: Throwable) {
|
||||
exception = e
|
||||
throw e
|
||||
} finally {
|
||||
this.closeFinallyAC(exception)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this [AutoCloseable], suppressing possible exception or error thrown by [AutoCloseable.close] function when
|
||||
* it's being closed due to some other [cause] exception occurred.
|
||||
*
|
||||
* The suppressed exception is added to the list of suppressed exceptions of [cause] exception.
|
||||
*/
|
||||
fun AutoCloseable?.closeFinallyAC(cause: Throwable?) = when {
|
||||
this == null -> {}
|
||||
cause == null -> close()
|
||||
else ->
|
||||
try {
|
||||
close()
|
||||
} catch (closeException: Throwable) {
|
||||
cause.addSuppressed(closeException)
|
||||
}
|
||||
}
|
||||
* Closes this [AutoCloseable], suppressing possible exception or error thrown by
|
||||
* [AutoCloseable.close] function when it's being closed due to some other [cause] exception
|
||||
* occurred.
|
||||
*
|
||||
* The suppressed exception is added to the list of suppressed exceptions of [cause] exception.
|
||||
*/
|
||||
fun AutoCloseable?.closeFinallyAC(cause: Throwable?) =
|
||||
when {
|
||||
this == null -> {}
|
||||
cause == null -> close()
|
||||
else ->
|
||||
try {
|
||||
close()
|
||||
} catch (closeException: Throwable) {
|
||||
cause.addSuppressed(closeException)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,33 +3,33 @@ package com.github.codeql
|
||||
import com.github.codeql.utils.getJvmName
|
||||
import com.github.codeql.utils.versions.*
|
||||
import com.intellij.openapi.vfs.StandardFileSystems
|
||||
import org.jetbrains.kotlin.fir.java.JavaBinarySourceElement
|
||||
import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
|
||||
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass
|
||||
import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.fir.java.JavaBinarySourceElement
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.ir.util.parentClassOrNull
|
||||
import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
|
||||
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass
|
||||
import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement
|
||||
import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass
|
||||
|
||||
// Adapted from Kotlin's interpreter/Utils.kt function 'internalName'
|
||||
// Translates class names into their JLS section 13.1 binary name,
|
||||
// and declarations within them into the parent class' JLS 13.1 name as
|
||||
// specified above, followed by a `$` separator and then the short name
|
||||
// for `that`.
|
||||
private fun getName(d: IrDeclarationWithName) = (d as? IrAnnotationContainer)?.let { getJvmName(it) } ?: d.name.asString()
|
||||
private fun getName(d: IrDeclarationWithName) =
|
||||
(d as? IrAnnotationContainer)?.let { getJvmName(it) } ?: d.name.asString()
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class) // Annotation required by kotlin versions < 1.5
|
||||
fun getFileClassName(f: IrFile) =
|
||||
getJvmName(f) ?:
|
||||
((f.fileEntry.name.replaceFirst(Regex(""".*[/\\]"""), "")
|
||||
.replaceFirst(Regex("""\.kt$"""), "")
|
||||
.replaceFirstChar { it.uppercase() }) + "Kt")
|
||||
getJvmName(f)
|
||||
?: ((f.fileEntry.name
|
||||
.replaceFirst(Regex(""".*[/\\]"""), "")
|
||||
.replaceFirst(Regex("""\.kt$"""), "")
|
||||
.replaceFirstChar { it.uppercase() }) + "Kt")
|
||||
|
||||
fun getIrElementBinaryName(that: IrElement): String {
|
||||
if (that is IrFile) {
|
||||
@@ -39,19 +39,21 @@ fun getIrElementBinaryName(that: IrElement): String {
|
||||
}
|
||||
|
||||
if (that !is IrDeclaration) {
|
||||
return "(unknown-name)"
|
||||
return "(unknown-name)"
|
||||
}
|
||||
|
||||
val shortName = when(that) {
|
||||
is IrDeclarationWithName -> getName(that)
|
||||
else -> "(unknown-name)"
|
||||
}
|
||||
val shortName =
|
||||
when (that) {
|
||||
is IrDeclarationWithName -> getName(that)
|
||||
else -> "(unknown-name)"
|
||||
}
|
||||
|
||||
val internalName = StringBuilder(shortName)
|
||||
if (that !is IrClass) {
|
||||
val parent = that.parent
|
||||
if (parent is IrFile) {
|
||||
// Note we'll fall through and do the IrPackageFragment case as well, since IrFile <: IrPackageFragment
|
||||
// Note we'll fall through and do the IrPackageFragment case as well, since IrFile <:
|
||||
// IrPackageFragment
|
||||
internalName.insert(0, getFileClassName(parent) + "$")
|
||||
}
|
||||
}
|
||||
@@ -60,7 +62,11 @@ fun getIrElementBinaryName(that: IrElement): String {
|
||||
.forEach {
|
||||
when (it) {
|
||||
is IrClass -> internalName.insert(0, getName(it) + "$")
|
||||
is IrPackageFragment -> it.packageFqName.asString().takeIf { fqName -> fqName.isNotEmpty() }?.let { fqName -> internalName.insert(0, "$fqName.") }
|
||||
is IrPackageFragment ->
|
||||
it.packageFqName
|
||||
.asString()
|
||||
.takeIf { fqName -> fqName.isNotEmpty() }
|
||||
?.let { fqName -> internalName.insert(0, "$fqName.") }
|
||||
}
|
||||
}
|
||||
return internalName.toString()
|
||||
@@ -68,16 +74,18 @@ fun getIrElementBinaryName(that: IrElement): String {
|
||||
|
||||
fun getIrClassVirtualFile(irClass: IrClass): VirtualFile? {
|
||||
val cSource = irClass.source
|
||||
// Don't emit a location for multi-file classes until we're sure we can cope with different declarations
|
||||
// inside a class disagreeing about their source file. In particular this currently causes problems when
|
||||
// a source-location for a declarations tries to refer to a file-id which is assumed to be declared in
|
||||
// Don't emit a location for multi-file classes until we're sure we can cope with different
|
||||
// declarations
|
||||
// inside a class disagreeing about their source file. In particular this currently causes
|
||||
// problems when
|
||||
// a source-location for a declarations tries to refer to a file-id which is assumed to be
|
||||
// declared in
|
||||
// the class trap file.
|
||||
if (irClass.origin == IrDeclarationOrigin.JVM_MULTIFILE_CLASS)
|
||||
return null
|
||||
when(cSource) {
|
||||
if (irClass.origin == IrDeclarationOrigin.JVM_MULTIFILE_CLASS) return null
|
||||
when (cSource) {
|
||||
is JavaSourceElement -> {
|
||||
val element = cSource.javaElement
|
||||
when(element) {
|
||||
when (element) {
|
||||
is BinaryJavaClass -> return element.virtualFile
|
||||
}
|
||||
}
|
||||
@@ -86,7 +94,7 @@ fun getIrClassVirtualFile(irClass: IrClass): VirtualFile? {
|
||||
}
|
||||
is KotlinJvmBinarySourceElement -> {
|
||||
val binaryClass = cSource.binaryClass
|
||||
when(binaryClass) {
|
||||
when (binaryClass) {
|
||||
is VirtualFileKotlinClass -> return binaryClass.file
|
||||
}
|
||||
}
|
||||
@@ -103,17 +111,17 @@ fun getIrClassVirtualFile(irClass: IrClass): VirtualFile? {
|
||||
private fun getRawIrClassBinaryPath(irClass: IrClass) =
|
||||
getIrClassVirtualFile(irClass)?.let {
|
||||
val path = it.path
|
||||
if(it.fileSystem.protocol == StandardFileSystems.JRT_PROTOCOL)
|
||||
// For JRT files, which we assume to be the JDK, hide the containing JAR path to match the Java extractor's behaviour.
|
||||
"/${path.split("!/", limit = 2)[1]}"
|
||||
else
|
||||
path
|
||||
if (it.fileSystem.protocol == StandardFileSystems.JRT_PROTOCOL)
|
||||
// For JRT files, which we assume to be the JDK, hide the containing JAR path to match the
|
||||
// Java extractor's behaviour.
|
||||
"/${path.split("!/", limit = 2)[1]}"
|
||||
else path
|
||||
}
|
||||
|
||||
fun getIrClassBinaryPath(irClass: IrClass): String {
|
||||
return getRawIrClassBinaryPath(irClass)
|
||||
// Otherwise, make up a fake location:
|
||||
?: getUnknownBinaryLocation(getIrElementBinaryName(irClass))
|
||||
return getRawIrClassBinaryPath(irClass)
|
||||
// Otherwise, make up a fake location:
|
||||
?: getUnknownBinaryLocation(getIrElementBinaryName(irClass))
|
||||
}
|
||||
|
||||
fun getIrDeclarationBinaryPath(d: IrDeclaration): String? {
|
||||
|
||||
@@ -5,7 +5,6 @@ import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.declarations.IrExternalPackageFragment
|
||||
import org.jetbrains.kotlin.ir.util.isFileClass
|
||||
import org.jetbrains.kotlin.ir.util.parentClassOrNull
|
||||
|
||||
fun isExternalDeclaration(d: IrDeclaration): Boolean {
|
||||
/*
|
||||
@@ -15,9 +14,11 @@ fun isExternalDeclaration(d: IrDeclaration): Boolean {
|
||||
EXPRESSION_BODY
|
||||
CONST Int type=kotlin.Int value=-2147483648
|
||||
*/
|
||||
if (d.origin == IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB ||
|
||||
d.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB ||
|
||||
d.origin.toString() == "FUNCTION_INTERFACE_CLASS") { // Treat kotlin.coroutines.* like ordinary library classes
|
||||
if (
|
||||
d.origin == IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB ||
|
||||
d.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB ||
|
||||
d.origin.toString() == "FUNCTION_INTERFACE_CLASS"
|
||||
) { // Treat kotlin.coroutines.* like ordinary library classes
|
||||
return true
|
||||
}
|
||||
/*
|
||||
@@ -39,11 +40,11 @@ fun isExternalDeclaration(d: IrDeclaration): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if `d` is not itself a class, but is a member of an external file class.
|
||||
*/
|
||||
/** Returns true if `d` is not itself a class, but is a member of an external file class. */
|
||||
fun isExternalFileClassMember(d: IrDeclaration): Boolean {
|
||||
if (d is IrClass) { return false }
|
||||
if (d is IrClass) {
|
||||
return false
|
||||
}
|
||||
val p = d.parent
|
||||
when (p) {
|
||||
is IrClass -> return p.isFileClass
|
||||
@@ -53,4 +54,3 @@ fun isExternalFileClassMember(d: IrDeclaration): Boolean {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,18 @@ fun getClassByFqName(pluginContext: IrPluginContext, fqName: String): IrClassSym
|
||||
return getClassByFqName(pluginContext, FqName(fqName))
|
||||
}
|
||||
|
||||
fun getFunctionsByFqName(pluginContext: IrPluginContext, pkgName: String, name: String): Collection<IrSimpleFunctionSymbol> {
|
||||
fun getFunctionsByFqName(
|
||||
pluginContext: IrPluginContext,
|
||||
pkgName: String,
|
||||
name: String
|
||||
): Collection<IrSimpleFunctionSymbol> {
|
||||
return getFunctionsByFqName(pluginContext, FqName(pkgName), Name.identifier(name))
|
||||
}
|
||||
|
||||
fun getPropertiesByFqName(pluginContext: IrPluginContext, pkgName: String, name: String): Collection<IrPropertySymbol> {
|
||||
fun getPropertiesByFqName(
|
||||
pluginContext: IrPluginContext,
|
||||
pkgName: String,
|
||||
name: String
|
||||
): Collection<IrPropertySymbol> {
|
||||
return getPropertiesByFqName(pluginContext, FqName(pkgName), Name.identifier(name))
|
||||
}
|
||||
|
||||
@@ -9,4 +9,5 @@ fun IrFunction.isLocalFunction(): Boolean {
|
||||
return this.visibility == DescriptorVisibilities.LOCAL
|
||||
}
|
||||
|
||||
val IrClass.isInterfaceLike get() = kind == ClassKind.INTERFACE || kind == ClassKind.ANNOTATION_CLASS
|
||||
val IrClass.isInterfaceLike
|
||||
get() = kind == ClassKind.INTERFACE || kind == ClassKind.ANNOTATION_CLASS
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.github.codeql.utils
|
||||
|
||||
import com.github.codeql.utils.Psi2IrFacade
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
@@ -8,8 +7,11 @@ import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.util.isFakeOverride
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||
|
||||
class IrVisitorLookup(private val psi2Ir: Psi2IrFacade, private val psi: PsiElement, private val file: IrFile) :
|
||||
IrElementVisitor<Unit, MutableCollection<IrElement>> {
|
||||
class IrVisitorLookup(
|
||||
private val psi2Ir: Psi2IrFacade,
|
||||
private val psi: PsiElement,
|
||||
private val file: IrFile
|
||||
) : IrElementVisitor<Unit, MutableCollection<IrElement>> {
|
||||
private val location = psi.getLocation()
|
||||
|
||||
override fun visitElement(element: IrElement, data: MutableCollection<IrElement>): Unit {
|
||||
|
||||
@@ -3,11 +3,10 @@ package com.github.codeql
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
|
||||
/**
|
||||
* This behaves the same as Iterable<IrDeclaration>.find, but requires
|
||||
* that the value found is of the subtype S, and it casts
|
||||
* the result for you appropriately.
|
||||
* This behaves the same as Iterable<IrDeclaration>.find, but requires that the value found is of
|
||||
* the subtype S, and it casts the result for you appropriately.
|
||||
*/
|
||||
inline fun <reified S: IrDeclaration> Iterable<IrDeclaration>.findSubType(
|
||||
inline fun <reified S : IrDeclaration> Iterable<IrDeclaration>.findSubType(
|
||||
predicate: (S) -> Boolean
|
||||
): S? {
|
||||
return this.find { it is S && predicate(it) } as S?
|
||||
|
||||
@@ -17,43 +17,46 @@ import org.jetbrains.kotlin.name.Name
|
||||
|
||||
private data class MethodKey(val className: FqName, val functionName: Name)
|
||||
|
||||
private fun makeDescription(className: FqName, functionName: String) = MethodKey(className, Name.guessByFirstCharacter(functionName))
|
||||
private fun makeDescription(className: FqName, functionName: String) =
|
||||
MethodKey(className, Name.guessByFirstCharacter(functionName))
|
||||
|
||||
// This essentially mirrors SpecialBridgeMethods.kt, a backend pass which isn't easily available to our extractor.
|
||||
private val specialFunctions = mapOf(
|
||||
makeDescription(StandardNames.FqNames.collection, "<get-size>") to "size",
|
||||
makeDescription(FqName("java.util.Collection"), "<get-size>") to "size",
|
||||
makeDescription(StandardNames.FqNames.map, "<get-size>") to "size",
|
||||
makeDescription(FqName("java.util.Map"), "<get-size>") to "size",
|
||||
makeDescription(StandardNames.FqNames.charSequence.toSafe(), "<get-length>") to "length",
|
||||
makeDescription(FqName("java.lang.CharSequence"), "<get-length>") to "length",
|
||||
makeDescription(StandardNames.FqNames.map, "<get-keys>") to "keySet",
|
||||
makeDescription(FqName("java.util.Map"), "<get-keys>") to "keySet",
|
||||
makeDescription(StandardNames.FqNames.map, "<get-values>") to "values",
|
||||
makeDescription(FqName("java.util.Map"), "<get-values>") to "values",
|
||||
makeDescription(StandardNames.FqNames.map, "<get-entries>") to "entrySet",
|
||||
makeDescription(FqName("java.util.Map"), "<get-entries>") to "entrySet",
|
||||
makeDescription(StandardNames.FqNames.mutableList, "removeAt") to "remove",
|
||||
makeDescription(FqName("java.util.List"), "removeAt") to "remove",
|
||||
makeDescription(StandardNames.FqNames._enum.toSafe(), "<get-ordinal>") to "ordinal",
|
||||
makeDescription(FqName("java.lang.Enum"), "<get-ordinal>") to "ordinal",
|
||||
makeDescription(StandardNames.FqNames._enum.toSafe(), "<get-name>") to "name",
|
||||
makeDescription(FqName("java.lang.Enum"), "<get-name>") to "name",
|
||||
makeDescription(StandardNames.FqNames.number.toSafe(), "toByte") to "byteValue",
|
||||
makeDescription(FqName("java.lang.Number"), "toByte") to "byteValue",
|
||||
makeDescription(StandardNames.FqNames.number.toSafe(), "toShort") to "shortValue",
|
||||
makeDescription(FqName("java.lang.Number"), "toShort") to "shortValue",
|
||||
makeDescription(StandardNames.FqNames.number.toSafe(), "toInt") to "intValue",
|
||||
makeDescription(FqName("java.lang.Number"), "toInt") to "intValue",
|
||||
makeDescription(StandardNames.FqNames.number.toSafe(), "toLong") to "longValue",
|
||||
makeDescription(FqName("java.lang.Number"), "toLong") to "longValue",
|
||||
makeDescription(StandardNames.FqNames.number.toSafe(), "toFloat") to "floatValue",
|
||||
makeDescription(FqName("java.lang.Number"), "toFloat") to "floatValue",
|
||||
makeDescription(StandardNames.FqNames.number.toSafe(), "toDouble") to "doubleValue",
|
||||
makeDescription(FqName("java.lang.Number"), "toDouble") to "doubleValue",
|
||||
makeDescription(StandardNames.FqNames.string.toSafe(), "get") to "charAt",
|
||||
makeDescription(FqName("java.lang.String"), "get") to "charAt",
|
||||
)
|
||||
// This essentially mirrors SpecialBridgeMethods.kt, a backend pass which isn't easily available to
|
||||
// our extractor.
|
||||
private val specialFunctions =
|
||||
mapOf(
|
||||
makeDescription(StandardNames.FqNames.collection, "<get-size>") to "size",
|
||||
makeDescription(FqName("java.util.Collection"), "<get-size>") to "size",
|
||||
makeDescription(StandardNames.FqNames.map, "<get-size>") to "size",
|
||||
makeDescription(FqName("java.util.Map"), "<get-size>") to "size",
|
||||
makeDescription(StandardNames.FqNames.charSequence.toSafe(), "<get-length>") to "length",
|
||||
makeDescription(FqName("java.lang.CharSequence"), "<get-length>") to "length",
|
||||
makeDescription(StandardNames.FqNames.map, "<get-keys>") to "keySet",
|
||||
makeDescription(FqName("java.util.Map"), "<get-keys>") to "keySet",
|
||||
makeDescription(StandardNames.FqNames.map, "<get-values>") to "values",
|
||||
makeDescription(FqName("java.util.Map"), "<get-values>") to "values",
|
||||
makeDescription(StandardNames.FqNames.map, "<get-entries>") to "entrySet",
|
||||
makeDescription(FqName("java.util.Map"), "<get-entries>") to "entrySet",
|
||||
makeDescription(StandardNames.FqNames.mutableList, "removeAt") to "remove",
|
||||
makeDescription(FqName("java.util.List"), "removeAt") to "remove",
|
||||
makeDescription(StandardNames.FqNames._enum.toSafe(), "<get-ordinal>") to "ordinal",
|
||||
makeDescription(FqName("java.lang.Enum"), "<get-ordinal>") to "ordinal",
|
||||
makeDescription(StandardNames.FqNames._enum.toSafe(), "<get-name>") to "name",
|
||||
makeDescription(FqName("java.lang.Enum"), "<get-name>") to "name",
|
||||
makeDescription(StandardNames.FqNames.number.toSafe(), "toByte") to "byteValue",
|
||||
makeDescription(FqName("java.lang.Number"), "toByte") to "byteValue",
|
||||
makeDescription(StandardNames.FqNames.number.toSafe(), "toShort") to "shortValue",
|
||||
makeDescription(FqName("java.lang.Number"), "toShort") to "shortValue",
|
||||
makeDescription(StandardNames.FqNames.number.toSafe(), "toInt") to "intValue",
|
||||
makeDescription(FqName("java.lang.Number"), "toInt") to "intValue",
|
||||
makeDescription(StandardNames.FqNames.number.toSafe(), "toLong") to "longValue",
|
||||
makeDescription(FqName("java.lang.Number"), "toLong") to "longValue",
|
||||
makeDescription(StandardNames.FqNames.number.toSafe(), "toFloat") to "floatValue",
|
||||
makeDescription(FqName("java.lang.Number"), "toFloat") to "floatValue",
|
||||
makeDescription(StandardNames.FqNames.number.toSafe(), "toDouble") to "doubleValue",
|
||||
makeDescription(FqName("java.lang.Number"), "toDouble") to "doubleValue",
|
||||
makeDescription(StandardNames.FqNames.string.toSafe(), "get") to "charAt",
|
||||
makeDescription(FqName("java.lang.String"), "get") to "charAt",
|
||||
)
|
||||
|
||||
private val specialFunctionShortNames = specialFunctions.keys.map { it.functionName }.toSet()
|
||||
|
||||
@@ -71,7 +74,7 @@ private fun getSpecialJvmName(f: IrFunction): String? {
|
||||
}
|
||||
|
||||
fun getJvmName(container: IrAnnotationContainer): String? {
|
||||
for(a: IrConstructorCall in container.annotations) {
|
||||
for (a: IrConstructorCall in container.annotations) {
|
||||
val t = a.type
|
||||
if (t is IrSimpleType && a.valueArgumentsCount == 1) {
|
||||
val owner = t.classifier.owner
|
||||
@@ -79,7 +82,7 @@ fun getJvmName(container: IrAnnotationContainer): String? {
|
||||
if (owner is IrClass) {
|
||||
val aPkg = owner.packageFqName?.asString()
|
||||
val name = owner.name.asString()
|
||||
if(aPkg == "kotlin.jvm" && name == "JvmName" && v is IrConst<*>) {
|
||||
if (aPkg == "kotlin.jvm" && name == "JvmName" && v is IrConst<*>) {
|
||||
val value = v.value
|
||||
if (value is String) {
|
||||
return value
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package com.github.codeql
|
||||
|
||||
/**
|
||||
* Turns this list of nullable elements into a list of non-nullable
|
||||
* elements if they are all non-null, or returns null otherwise.
|
||||
* Turns this list of nullable elements into a list of non-nullable elements if they are all
|
||||
* non-null, or returns null otherwise.
|
||||
*/
|
||||
public fun <T : Any> List<T?>.requireNoNullsOrNull(): List<T>? {
|
||||
try {
|
||||
return this.requireNoNulls()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return this.requireNoNulls()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
|
||||
data class Location(val startOffset: Int, val endOffset: Int){
|
||||
fun contains(location: Location) : Boolean {
|
||||
data class Location(val startOffset: Int, val endOffset: Int) {
|
||||
fun contains(location: Location): Boolean {
|
||||
return this.startOffset <= location.startOffset && this.endOffset >= location.endOffset
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@ data class Location(val startOffset: Int, val endOffset: Int){
|
||||
}
|
||||
}
|
||||
|
||||
fun IrElement.getLocation() : Location {
|
||||
fun IrElement.getLocation(): Location {
|
||||
return Location(this.startOffset, this.endOffset)
|
||||
}
|
||||
|
||||
fun PsiElement.getLocation() : Location {
|
||||
fun PsiElement.getLocation(): Location {
|
||||
return Location(this.startOffset, this.endOffset)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,10 @@ import org.jetbrains.kotlin.ir.IrElement
|
||||
class LogCounter() {
|
||||
public val diagnosticInfo = mutableMapOf<String, Pair<Severity, Int>>()
|
||||
public val diagnosticLimit: Int
|
||||
|
||||
init {
|
||||
diagnosticLimit = System.getenv("CODEQL_EXTRACTOR_KOTLIN_DIAGNOSTIC_LIMIT")?.toIntOrNull() ?: 100
|
||||
diagnosticLimit =
|
||||
System.getenv("CODEQL_EXTRACTOR_KOTLIN_DIAGNOSTIC_LIMIT")?.toIntOrNull() ?: 100
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +37,7 @@ enum class Severity(val sev: Int) {
|
||||
|
||||
class LogMessage(private val kind: String, private val message: String) {
|
||||
val timestamp: String
|
||||
|
||||
init {
|
||||
timestamp = "${SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Date())}"
|
||||
}
|
||||
@@ -45,45 +48,57 @@ class LogMessage(private val kind: String, private val message: String) {
|
||||
|
||||
private fun escape(str: String): String {
|
||||
return str.replace("\\", "\\\\")
|
||||
.replace("\"", "\\\"")
|
||||
.replace("\u0000", "\\u0000")
|
||||
.replace("\u0001", "\\u0001")
|
||||
.replace("\u0002", "\\u0002")
|
||||
.replace("\u0003", "\\u0003")
|
||||
.replace("\u0004", "\\u0004")
|
||||
.replace("\u0005", "\\u0005")
|
||||
.replace("\u0006", "\\u0006")
|
||||
.replace("\u0007", "\\u0007")
|
||||
.replace("\u0008", "\\b")
|
||||
.replace("\u0009", "\\t")
|
||||
.replace("\u000A", "\\n")
|
||||
.replace("\u000B", "\\u000B")
|
||||
.replace("\u000C", "\\f")
|
||||
.replace("\u000D", "\\r")
|
||||
.replace("\u000E", "\\u000E")
|
||||
.replace("\u000F", "\\u000F")
|
||||
.replace("\"", "\\\"")
|
||||
.replace("\u0000", "\\u0000")
|
||||
.replace("\u0001", "\\u0001")
|
||||
.replace("\u0002", "\\u0002")
|
||||
.replace("\u0003", "\\u0003")
|
||||
.replace("\u0004", "\\u0004")
|
||||
.replace("\u0005", "\\u0005")
|
||||
.replace("\u0006", "\\u0006")
|
||||
.replace("\u0007", "\\u0007")
|
||||
.replace("\u0008", "\\b")
|
||||
.replace("\u0009", "\\t")
|
||||
.replace("\u000A", "\\n")
|
||||
.replace("\u000B", "\\u000B")
|
||||
.replace("\u000C", "\\f")
|
||||
.replace("\u000D", "\\r")
|
||||
.replace("\u000E", "\\u000E")
|
||||
.replace("\u000F", "\\u000F")
|
||||
}
|
||||
|
||||
fun toJsonLine(): String {
|
||||
val kvs = listOf(Pair("origin", "CodeQL Kotlin extractor"),
|
||||
Pair("timestamp", timestamp),
|
||||
Pair("kind", kind),
|
||||
Pair("message", message))
|
||||
return "{ " + kvs.map { p -> "\"${p.first}\": \"${escape(p.second)}\""}.joinToString(", ") + " }\n"
|
||||
val kvs =
|
||||
listOf(
|
||||
Pair("origin", "CodeQL Kotlin extractor"),
|
||||
Pair("timestamp", timestamp),
|
||||
Pair("kind", kind),
|
||||
Pair("message", message)
|
||||
)
|
||||
return "{ " +
|
||||
kvs.map { p -> "\"${p.first}\": \"${escape(p.second)}\"" }.joinToString(", ") +
|
||||
" }\n"
|
||||
}
|
||||
}
|
||||
|
||||
data class ExtractorContext(val kind: String, val element: IrElement, val name: String, val loc: String)
|
||||
data class ExtractorContext(
|
||||
val kind: String,
|
||||
val element: IrElement,
|
||||
val name: String,
|
||||
val loc: String
|
||||
)
|
||||
|
||||
open class LoggerBase(val logCounter: LogCounter) {
|
||||
val extractorContextStack = Stack<ExtractorContext>()
|
||||
|
||||
private val verbosity: Int
|
||||
|
||||
init {
|
||||
verbosity = System.getenv("CODEQL_EXTRACTOR_KOTLIN_VERBOSITY")?.toIntOrNull() ?: 3
|
||||
}
|
||||
|
||||
private val logStream: Writer
|
||||
|
||||
init {
|
||||
val extractorLogDir = System.getenv("CODEQL_EXTRACTOR_JAVA_LOG_DIR")
|
||||
if (extractorLogDir == null || extractorLogDir == "") {
|
||||
@@ -96,8 +111,8 @@ open class LoggerBase(val logCounter: LogCounter) {
|
||||
|
||||
private fun getDiagnosticLocation(): String? {
|
||||
val st = Exception().stackTrace
|
||||
for(x in st) {
|
||||
when(x.className) {
|
||||
for (x in st) {
|
||||
when (x.className) {
|
||||
"com.github.codeql.LoggerBase",
|
||||
"com.github.codeql.Logger",
|
||||
"com.github.codeql.FileLogger" -> {}
|
||||
@@ -117,20 +132,29 @@ open class LoggerBase(val logCounter: LogCounter) {
|
||||
file_number_diagnostic_number = 0
|
||||
}
|
||||
|
||||
fun diagnostic(dtw: DiagnosticTrapWriter, severity: Severity, msg: String, extraInfo: String?, locationString: String? = null, mkLocationId: () -> Label<DbLocation> = { dtw.unknownLocation }) {
|
||||
fun diagnostic(
|
||||
dtw: DiagnosticTrapWriter,
|
||||
severity: Severity,
|
||||
msg: String,
|
||||
extraInfo: String?,
|
||||
locationString: String? = null,
|
||||
mkLocationId: () -> Label<DbLocation> = { dtw.unknownLocation }
|
||||
) {
|
||||
val diagnosticLoc = getDiagnosticLocation()
|
||||
val diagnosticLocStr = if(diagnosticLoc == null) "<unknown location>" else diagnosticLoc
|
||||
val diagnosticLocStr = if (diagnosticLoc == null) "<unknown location>" else diagnosticLoc
|
||||
val suffix =
|
||||
if(diagnosticLoc == null) {
|
||||
if (diagnosticLoc == null) {
|
||||
" Missing caller information.\n"
|
||||
} else {
|
||||
val oldInfo = logCounter.diagnosticInfo.getOrDefault(diagnosticLoc, Pair(severity, 0))
|
||||
if(severity != oldInfo.first) {
|
||||
val oldInfo =
|
||||
logCounter.diagnosticInfo.getOrDefault(diagnosticLoc, Pair(severity, 0))
|
||||
if (severity != oldInfo.first) {
|
||||
// We don't want to get in a loop, so just emit this
|
||||
// directly without going through the diagnostic
|
||||
// counting machinery
|
||||
if (verbosity >= 1) {
|
||||
val message = "Severity mismatch ($severity vs ${oldInfo.first}) at $diagnosticLoc"
|
||||
val message =
|
||||
"Severity mismatch ($severity vs ${oldInfo.first}) at $diagnosticLoc"
|
||||
emitDiagnostic(dtw, Severity.Error, "Inconsistency", message, message)
|
||||
}
|
||||
}
|
||||
@@ -139,7 +163,8 @@ open class LoggerBase(val logCounter: LogCounter) {
|
||||
logCounter.diagnosticInfo[diagnosticLoc] = newInfo
|
||||
when {
|
||||
logCounter.diagnosticLimit <= 0 -> ""
|
||||
newCount == logCounter.diagnosticLimit -> " Limit reached for diagnostics from $diagnosticLoc.\n"
|
||||
newCount == logCounter.diagnosticLimit ->
|
||||
" Limit reached for diagnostics from $diagnosticLoc.\n"
|
||||
newCount > logCounter.diagnosticLimit -> return
|
||||
else -> ""
|
||||
}
|
||||
@@ -162,15 +187,36 @@ open class LoggerBase(val logCounter: LogCounter) {
|
||||
emitDiagnostic(dtw, severity, diagnosticLocStr, msg, fullMsg, locationString, mkLocationId)
|
||||
}
|
||||
|
||||
private fun emitDiagnostic(dtw: DiagnosticTrapWriter, severity: Severity, diagnosticLocStr: String, msg: String, fullMsg: String, locationString: String? = null, mkLocationId: () -> Label<DbLocation> = { dtw.unknownLocation }) {
|
||||
private fun emitDiagnostic(
|
||||
dtw: DiagnosticTrapWriter,
|
||||
severity: Severity,
|
||||
diagnosticLocStr: String,
|
||||
msg: String,
|
||||
fullMsg: String,
|
||||
locationString: String? = null,
|
||||
mkLocationId: () -> Label<DbLocation> = { dtw.unknownLocation }
|
||||
) {
|
||||
val locStr = if (locationString == null) "" else "At " + locationString + ": "
|
||||
val kind = if (severity <= Severity.WarnHigh) "WARN" else "ERROR"
|
||||
val logMessage = LogMessage(kind, "Diagnostic($diagnosticLocStr): $locStr$fullMsg")
|
||||
// We don't actually make the location until after the `return` above
|
||||
val locationId = mkLocationId()
|
||||
val diagLabel = dtw.getFreshIdLabel<DbDiagnostic>()
|
||||
dtw.writeDiagnostics(diagLabel, "CodeQL Kotlin extractor", severity.sev, "", msg, "${logMessage.timestamp} $fullMsg", locationId)
|
||||
dtw.writeDiagnostic_for(diagLabel, StringLabel("compilation"), file_number, file_number_diagnostic_number++)
|
||||
dtw.writeDiagnostics(
|
||||
diagLabel,
|
||||
"CodeQL Kotlin extractor",
|
||||
severity.sev,
|
||||
"",
|
||||
msg,
|
||||
"${logMessage.timestamp} $fullMsg",
|
||||
locationId
|
||||
)
|
||||
dtw.writeDiagnostic_for(
|
||||
diagLabel,
|
||||
StringLabel("compilation"),
|
||||
file_number,
|
||||
file_number_diagnostic_number++
|
||||
)
|
||||
logStream.write(logMessage.toJsonLine())
|
||||
}
|
||||
|
||||
@@ -203,6 +249,7 @@ open class LoggerBase(val logCounter: LogCounter) {
|
||||
diagnostic(dtw, Severity.Warn, msg, extraInfo)
|
||||
}
|
||||
}
|
||||
|
||||
fun error(dtw: DiagnosticTrapWriter, msg: String, extraInfo: String?) {
|
||||
if (verbosity >= 1) {
|
||||
diagnostic(dtw, Severity.Error, msg, extraInfo)
|
||||
@@ -210,11 +257,12 @@ open class LoggerBase(val logCounter: LogCounter) {
|
||||
}
|
||||
|
||||
fun printLimitedDiagnosticCounts(dtw: DiagnosticTrapWriter) {
|
||||
for((caller, info) in logCounter.diagnosticInfo) {
|
||||
for ((caller, info) in logCounter.diagnosticInfo) {
|
||||
val severity = info.first
|
||||
val count = info.second
|
||||
if(count >= logCounter.diagnosticLimit) {
|
||||
val message = "Total of $count diagnostics (reached limit of ${logCounter.diagnosticLimit}) from $caller."
|
||||
if (count >= logCounter.diagnosticLimit) {
|
||||
val message =
|
||||
"Total of $count diagnostics (reached limit of ${logCounter.diagnosticLimit}) from $caller."
|
||||
if (verbosity >= 1) {
|
||||
emitDiagnostic(dtw, severity, "Limit", message, message)
|
||||
}
|
||||
@@ -240,9 +288,11 @@ open class Logger(val loggerBase: LoggerBase, open val dtw: DiagnosticTrapWriter
|
||||
fun trace(msg: String) {
|
||||
loggerBase.trace(dtw, msg)
|
||||
}
|
||||
|
||||
fun trace(msg: String, exn: Throwable) {
|
||||
trace(msg + "\n" + exn.stackTraceToString())
|
||||
}
|
||||
|
||||
fun debug(msg: String) {
|
||||
loggerBase.debug(dtw, msg)
|
||||
}
|
||||
@@ -254,9 +304,11 @@ open class Logger(val loggerBase: LoggerBase, open val dtw: DiagnosticTrapWriter
|
||||
private fun warn(msg: String, extraInfo: String?) {
|
||||
loggerBase.warn(dtw, msg, extraInfo)
|
||||
}
|
||||
|
||||
fun warn(msg: String, exn: Throwable) {
|
||||
warn(msg, exn.stackTraceToString())
|
||||
}
|
||||
|
||||
fun warn(msg: String) {
|
||||
warn(msg, null)
|
||||
}
|
||||
@@ -264,24 +316,41 @@ open class Logger(val loggerBase: LoggerBase, open val dtw: DiagnosticTrapWriter
|
||||
private fun error(msg: String, extraInfo: String?) {
|
||||
loggerBase.error(dtw, msg, extraInfo)
|
||||
}
|
||||
|
||||
fun error(msg: String) {
|
||||
error(msg, null)
|
||||
}
|
||||
|
||||
fun error(msg: String, exn: Throwable) {
|
||||
error(msg, exn.stackTraceToString())
|
||||
}
|
||||
}
|
||||
|
||||
class FileLogger(loggerBase: LoggerBase, val ftw: FileTrapWriter): Logger(loggerBase, ftw.getDiagnosticTrapWriter()) {
|
||||
class FileLogger(loggerBase: LoggerBase, val ftw: FileTrapWriter) :
|
||||
Logger(loggerBase, ftw.getDiagnosticTrapWriter()) {
|
||||
fun warnElement(msg: String, element: IrElement, exn: Throwable? = null) {
|
||||
val locationString = ftw.getLocationString(element)
|
||||
val mkLocationId = { ftw.getLocation(element) }
|
||||
loggerBase.diagnostic(ftw.getDiagnosticTrapWriter(), Severity.Warn, msg, exn?.stackTraceToString(), locationString, mkLocationId)
|
||||
loggerBase.diagnostic(
|
||||
ftw.getDiagnosticTrapWriter(),
|
||||
Severity.Warn,
|
||||
msg,
|
||||
exn?.stackTraceToString(),
|
||||
locationString,
|
||||
mkLocationId
|
||||
)
|
||||
}
|
||||
|
||||
fun errorElement(msg: String, element: IrElement, exn: Throwable? = null) {
|
||||
val locationString = ftw.getLocationString(element)
|
||||
val mkLocationId = { ftw.getLocation(element) }
|
||||
loggerBase.diagnostic(ftw.getDiagnosticTrapWriter(), Severity.Error, msg, exn?.stackTraceToString(), locationString, mkLocationId)
|
||||
loggerBase.diagnostic(
|
||||
ftw.getDiagnosticTrapWriter(),
|
||||
Severity.Error,
|
||||
msg,
|
||||
exn?.stackTraceToString(),
|
||||
locationString,
|
||||
mkLocationId
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,5 +7,6 @@ import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
interface Psi2IrFacade {
|
||||
fun getKtFile(irFile: IrFile): KtFile?
|
||||
|
||||
fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement?
|
||||
}
|
||||
|
||||
@@ -1,30 +1,40 @@
|
||||
package com.github.codeql
|
||||
|
||||
/**
|
||||
* A triple of a type's database label, its signature for use in callable signatures, and its short name for use
|
||||
* in all tables that provide a user-facing type name.
|
||||
* A triple of a type's database label, its signature for use in callable signatures, and its short
|
||||
* name for use in all tables that provide a user-facing type name.
|
||||
*
|
||||
* `signature` is a Java primitive name (e.g. "int"), a fully-qualified class name ("package.OuterClass.InnerClass"),
|
||||
* or an array ("componentSignature[]")
|
||||
* Type variables have the signature of their upper bound.
|
||||
* Type arguments and anonymous types do not have a signature.
|
||||
* `signature` is a Java primitive name (e.g. "int"), a fully-qualified class name
|
||||
* ("package.OuterClass.InnerClass"), or an array ("componentSignature[]") Type variables have the
|
||||
* signature of their upper bound. Type arguments and anonymous types do not have a signature.
|
||||
*
|
||||
* `shortName` is a Java primitive name (e.g. "int"), a class short name with Java-style type arguments ("InnerClass<E>" or
|
||||
* "OuterClass<ConcreteArgument>" or "OtherClass<? extends Bound>") or an array ("componentShortName[]").
|
||||
* `shortName` is a Java primitive name (e.g. "int"), a class short name with Java-style type
|
||||
* arguments ("InnerClass<E>" or "OuterClass<ConcreteArgument>" or "OtherClass<? extends Bound>") or
|
||||
* an array ("componentShortName[]").
|
||||
*/
|
||||
data class TypeResultGeneric<SignatureType,out LabelType: AnyDbType>(val id: Label<out LabelType>, val signature: SignatureType?, val shortName: String) {
|
||||
fun <U: AnyDbType> cast(): TypeResultGeneric<SignatureType,U> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return this as TypeResultGeneric<SignatureType,U>
|
||||
data class TypeResultGeneric<SignatureType, out LabelType : AnyDbType>(
|
||||
val id: Label<out LabelType>,
|
||||
val signature: SignatureType?,
|
||||
val shortName: String
|
||||
) {
|
||||
fun <U : AnyDbType> cast(): TypeResultGeneric<SignatureType, U> {
|
||||
@Suppress("UNCHECKED_CAST") return this as TypeResultGeneric<SignatureType, U>
|
||||
}
|
||||
}
|
||||
data class TypeResultsGeneric<SignatureType>(val javaResult: TypeResultGeneric<SignatureType,DbType>, val kotlinResult: TypeResultGeneric<SignatureType,DbKt_type>)
|
||||
|
||||
typealias TypeResult<T> = TypeResultGeneric<String,T>
|
||||
typealias TypeResultWithoutSignature<T> = TypeResultGeneric<Unit,T>
|
||||
data class TypeResultsGeneric<SignatureType>(
|
||||
val javaResult: TypeResultGeneric<SignatureType, DbType>,
|
||||
val kotlinResult: TypeResultGeneric<SignatureType, DbKt_type>
|
||||
)
|
||||
|
||||
typealias TypeResult<T> = TypeResultGeneric<String, T>
|
||||
|
||||
typealias TypeResultWithoutSignature<T> = TypeResultGeneric<Unit, T>
|
||||
|
||||
typealias TypeResults = TypeResultsGeneric<String>
|
||||
|
||||
typealias TypeResultsWithoutSignatures = TypeResultsGeneric<Unit>
|
||||
|
||||
fun <T: AnyDbType> TypeResult<T>.forgetSignature(): TypeResultWithoutSignature<T> {
|
||||
fun <T : AnyDbType> TypeResult<T>.forgetSignature(): TypeResultWithoutSignature<T> {
|
||||
return TypeResultWithoutSignature(this.id, Unit, this.shortName)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import com.github.codeql.getJavaEquivalentClassId
|
||||
import com.github.codeql.utils.versions.codeQlWithHasQuestionMark
|
||||
import com.github.codeql.utils.versions.createImplicitParameterDeclarationWithWrappedDescriptor
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.backend.common.lower.parents
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addConstructor
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildClass
|
||||
@@ -27,57 +26,56 @@ import org.jetbrains.kotlin.ir.util.classId
|
||||
import org.jetbrains.kotlin.ir.util.constructedClassType
|
||||
import org.jetbrains.kotlin.ir.util.constructors
|
||||
import org.jetbrains.kotlin.ir.util.kotlinFqName
|
||||
import org.jetbrains.kotlin.ir.util.parents
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
|
||||
fun IrType.substituteTypeArguments(params: List<IrTypeParameter>, arguments: List<IrTypeArgument>) =
|
||||
when(this) {
|
||||
when (this) {
|
||||
is IrSimpleType -> substituteTypeArguments(params.map { it.symbol }.zip(arguments).toMap())
|
||||
else -> this
|
||||
}
|
||||
|
||||
private fun IrSimpleType.substituteTypeArguments(substitutionMap: Map<IrTypeParameterSymbol, IrTypeArgument>): IrSimpleType {
|
||||
private fun IrSimpleType.substituteTypeArguments(
|
||||
substitutionMap: Map<IrTypeParameterSymbol, IrTypeArgument>
|
||||
): IrSimpleType {
|
||||
if (substitutionMap.isEmpty()) return this
|
||||
|
||||
val newArguments = arguments.map {
|
||||
if (it is IrTypeProjection) {
|
||||
val itType = it.type
|
||||
if (itType is IrSimpleType) {
|
||||
subProjectedType(substitutionMap, itType, it.variance)
|
||||
val newArguments =
|
||||
arguments.map {
|
||||
if (it is IrTypeProjection) {
|
||||
val itType = it.type
|
||||
if (itType is IrSimpleType) {
|
||||
subProjectedType(substitutionMap, itType, it.variance)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
} else {
|
||||
it
|
||||
}
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
return IrSimpleTypeImpl(
|
||||
classifier,
|
||||
isNullable(),
|
||||
newArguments,
|
||||
annotations
|
||||
)
|
||||
return IrSimpleTypeImpl(classifier, isNullable(), newArguments, annotations)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if substituting `innerVariance T` into the context `outerVariance []` discards all knowledge about
|
||||
* what T could be.
|
||||
* Returns true if substituting `innerVariance T` into the context `outerVariance []` discards all
|
||||
* knowledge about what T could be.
|
||||
*
|
||||
* Note this throws away slightly more information than it could: for example, the projection "in (out List)" can refer to
|
||||
* any superclass of anything that implements List, which specifically excludes e.g. String, but can't be represented as
|
||||
* a type projection. The projection "out (in List)" on the other hand really is equivalent to "out Any?", which is to
|
||||
* say no bound at all.
|
||||
* Note this throws away slightly more information than it could: for example, the projection "in
|
||||
* (out List)" can refer to any superclass of anything that implements List, which specifically
|
||||
* excludes e.g. String, but can't be represented as a type projection. The projection "out (in
|
||||
* List)" on the other hand really is equivalent to "out Any?", which is to say no bound at all.
|
||||
*/
|
||||
private fun conflictingVariance(outerVariance: Variance, innerVariance: Variance) =
|
||||
(outerVariance == Variance.IN_VARIANCE && innerVariance == Variance.OUT_VARIANCE) ||
|
||||
(outerVariance == Variance.OUT_VARIANCE && innerVariance == Variance.IN_VARIANCE)
|
||||
(outerVariance == Variance.OUT_VARIANCE && innerVariance == Variance.IN_VARIANCE)
|
||||
|
||||
/**
|
||||
* When substituting `innerVariance T` into the context `outerVariance []`, returns the variance part of the result
|
||||
* `resultVariance T`. We already know they don't conflict.
|
||||
* When substituting `innerVariance T` into the context `outerVariance []`, returns the variance
|
||||
* part of the result `resultVariance T`. We already know they don't conflict.
|
||||
*/
|
||||
private fun combineVariance(outerVariance: Variance, innerVariance: Variance) =
|
||||
when {
|
||||
@@ -86,13 +84,20 @@ private fun combineVariance(outerVariance: Variance, innerVariance: Variance) =
|
||||
else -> Variance.INVARIANT
|
||||
}
|
||||
|
||||
private fun subProjectedType(substitutionMap: Map<IrTypeParameterSymbol, IrTypeArgument>, t: IrSimpleType, outerVariance: Variance): IrTypeArgument =
|
||||
private fun subProjectedType(
|
||||
substitutionMap: Map<IrTypeParameterSymbol, IrTypeArgument>,
|
||||
t: IrSimpleType,
|
||||
outerVariance: Variance
|
||||
): IrTypeArgument =
|
||||
substitutionMap[t.classifier]?.let { substitutedTypeArg ->
|
||||
if (substitutedTypeArg is IrTypeProjection) {
|
||||
if (conflictingVariance(outerVariance, substitutedTypeArg.variance))
|
||||
IrStarProjectionImpl
|
||||
else {
|
||||
val newProjectedType = substitutedTypeArg.type.let { if (t.isNullable()) it.codeQlWithHasQuestionMark(true) else it }
|
||||
val newProjectedType =
|
||||
substitutedTypeArg.type.let {
|
||||
if (t.isNullable()) it.codeQlWithHasQuestionMark(true) else it
|
||||
}
|
||||
val newVariance = combineVariance(outerVariance, substitutedTypeArg.variance)
|
||||
makeTypeProjection(newProjectedType, newVariance)
|
||||
}
|
||||
@@ -102,33 +107,43 @@ private fun subProjectedType(substitutionMap: Map<IrTypeParameterSymbol, IrTypeA
|
||||
} ?: makeTypeProjection(t.substituteTypeArguments(substitutionMap), outerVariance)
|
||||
|
||||
private fun IrTypeArgument.upperBound(context: IrPluginContext) =
|
||||
when(this) {
|
||||
when (this) {
|
||||
is IrStarProjection -> context.irBuiltIns.anyNType
|
||||
is IrTypeProjection -> when(this.variance) {
|
||||
Variance.INVARIANT -> this.type
|
||||
Variance.IN_VARIANCE -> if (this.type.isNullable()) context.irBuiltIns.anyNType else context.irBuiltIns.anyType
|
||||
Variance.OUT_VARIANCE -> this.type
|
||||
}
|
||||
is IrTypeProjection ->
|
||||
when (this.variance) {
|
||||
Variance.INVARIANT -> this.type
|
||||
Variance.IN_VARIANCE ->
|
||||
if (this.type.isNullable()) context.irBuiltIns.anyNType
|
||||
else context.irBuiltIns.anyType
|
||||
Variance.OUT_VARIANCE -> this.type
|
||||
}
|
||||
else -> context.irBuiltIns.anyNType
|
||||
}
|
||||
|
||||
private fun IrTypeArgument.lowerBound(context: IrPluginContext) =
|
||||
when(this) {
|
||||
when (this) {
|
||||
is IrStarProjection -> context.irBuiltIns.nothingType
|
||||
is IrTypeProjection -> when(this.variance) {
|
||||
Variance.INVARIANT -> this.type
|
||||
Variance.IN_VARIANCE -> this.type
|
||||
Variance.OUT_VARIANCE -> if (this.type.isNullable()) context.irBuiltIns.nothingNType else context.irBuiltIns.nothingType
|
||||
}
|
||||
is IrTypeProjection ->
|
||||
when (this.variance) {
|
||||
Variance.INVARIANT -> this.type
|
||||
Variance.IN_VARIANCE -> this.type
|
||||
Variance.OUT_VARIANCE ->
|
||||
if (this.type.isNullable()) context.irBuiltIns.nothingNType
|
||||
else context.irBuiltIns.nothingType
|
||||
}
|
||||
else -> context.irBuiltIns.nothingType
|
||||
}
|
||||
|
||||
fun IrType.substituteTypeAndArguments(substitutionMap: Map<IrTypeParameterSymbol, IrTypeArgument>?, useContext: KotlinUsesExtractor.TypeContext, pluginContext: IrPluginContext): IrType =
|
||||
fun IrType.substituteTypeAndArguments(
|
||||
substitutionMap: Map<IrTypeParameterSymbol, IrTypeArgument>?,
|
||||
useContext: KotlinUsesExtractor.TypeContext,
|
||||
pluginContext: IrPluginContext
|
||||
): IrType =
|
||||
substitutionMap?.let { substMap ->
|
||||
if (this is IrSimpleType) {
|
||||
val typeClassifier = this.classifier
|
||||
substMap[typeClassifier]?.let {
|
||||
when(useContext) {
|
||||
when (useContext) {
|
||||
KotlinUsesExtractor.TypeContext.RETURN -> it.upperBound(pluginContext)
|
||||
else -> it.lowerBound(pluginContext)
|
||||
}
|
||||
@@ -139,42 +154,41 @@ fun IrType.substituteTypeAndArguments(substitutionMap: Map<IrTypeParameterSymbol
|
||||
} ?: this
|
||||
|
||||
object RawTypeAnnotation {
|
||||
// Much of this is taken from JvmGeneratorExtensionsImpl.kt, which is not easily accessible in plugin context.
|
||||
// The constants "kotlin.internal.ir" and "RawType" could be referred to symbolically, but they move package
|
||||
// Much of this is taken from JvmGeneratorExtensionsImpl.kt, which is not easily accessible in
|
||||
// plugin context.
|
||||
// The constants "kotlin.internal.ir" and "RawType" could be referred to symbolically, but they
|
||||
// move package
|
||||
// between different versions of the Kotlin compiler.
|
||||
val annotationConstructor: IrConstructorCall by lazy {
|
||||
val irInternalPackage = FqName("kotlin.internal.ir")
|
||||
val parent = IrExternalPackageFragmentImpl(
|
||||
DescriptorlessExternalPackageFragmentSymbol(),
|
||||
irInternalPackage
|
||||
)
|
||||
val annoClass = IrFactoryImpl.buildClass {
|
||||
kind = ClassKind.ANNOTATION_CLASS
|
||||
name = irInternalPackage.child(Name.identifier("RawType")).shortName()
|
||||
}.apply {
|
||||
createImplicitParameterDeclarationWithWrappedDescriptor()
|
||||
this.parent = parent
|
||||
addConstructor {
|
||||
isPrimary = true
|
||||
}
|
||||
}
|
||||
val parent =
|
||||
IrExternalPackageFragmentImpl(
|
||||
DescriptorlessExternalPackageFragmentSymbol(),
|
||||
irInternalPackage
|
||||
)
|
||||
val annoClass =
|
||||
IrFactoryImpl.buildClass {
|
||||
kind = ClassKind.ANNOTATION_CLASS
|
||||
name = irInternalPackage.child(Name.identifier("RawType")).shortName()
|
||||
}
|
||||
.apply {
|
||||
createImplicitParameterDeclarationWithWrappedDescriptor()
|
||||
this.parent = parent
|
||||
addConstructor { isPrimary = true }
|
||||
}
|
||||
val constructor = annoClass.constructors.single()
|
||||
IrConstructorCallImpl.fromSymbolOwner(
|
||||
constructor.constructedClassType,
|
||||
constructor.symbol
|
||||
)
|
||||
IrConstructorCallImpl.fromSymbolOwner(constructor.constructedClassType, constructor.symbol)
|
||||
}
|
||||
}
|
||||
|
||||
fun IrType.toRawType(): IrType =
|
||||
when(this) {
|
||||
when (this) {
|
||||
is IrSimpleType -> {
|
||||
when(val owner = this.classifier.owner) {
|
||||
when (val owner = this.classifier.owner) {
|
||||
is IrClass -> {
|
||||
if (this.arguments.isNotEmpty())
|
||||
this.addAnnotations(listOf(RawTypeAnnotation.annotationConstructor))
|
||||
else
|
||||
this
|
||||
else this
|
||||
}
|
||||
is IrTypeParameter -> owner.superTypes[0].toRawType()
|
||||
else -> this
|
||||
@@ -187,29 +201,31 @@ fun IrClass.toRawType(): IrType {
|
||||
val result = this.typeWith(listOf())
|
||||
return if (this.typeParameters.isNotEmpty())
|
||||
result.addAnnotations(listOf(RawTypeAnnotation.annotationConstructor))
|
||||
else
|
||||
result
|
||||
else result
|
||||
}
|
||||
|
||||
fun IrTypeArgument.withQuestionMark(b: Boolean): IrTypeArgument =
|
||||
when(this) {
|
||||
when (this) {
|
||||
is IrStarProjection -> this
|
||||
is IrTypeProjection ->
|
||||
this.type.let { when(it) {
|
||||
is IrSimpleType -> if (it.isNullable() == b) this else makeTypeProjection(it.codeQlWithHasQuestionMark(b), this.variance)
|
||||
else -> this
|
||||
}}
|
||||
this.type.let {
|
||||
when (it) {
|
||||
is IrSimpleType ->
|
||||
if (it.isNullable() == b) this
|
||||
else makeTypeProjection(it.codeQlWithHasQuestionMark(b), this.variance)
|
||||
else -> this
|
||||
}
|
||||
}
|
||||
else -> this
|
||||
}
|
||||
|
||||
typealias TypeSubstitution = (IrType, KotlinUsesExtractor.TypeContext, IrPluginContext) -> IrType
|
||||
|
||||
private fun matchingTypeParameters(l: IrTypeParameter?, r: IrTypeParameter): Boolean {
|
||||
if (l === r)
|
||||
return true
|
||||
if (l == null)
|
||||
return false
|
||||
// Special case: match List's E and MutableList's E, for example, because in the JVM lowering they will map to the same thing.
|
||||
if (l === r) return true
|
||||
if (l == null) return false
|
||||
// Special case: match List's E and MutableList's E, for example, because in the JVM lowering
|
||||
// they will map to the same thing.
|
||||
val lParent = l.parent as? IrClass ?: return false
|
||||
val rParent = r.parent as? IrClass ?: return false
|
||||
val lJavaId = getJavaEquivalentClassId(lParent) ?: lParent.classId
|
||||
@@ -217,34 +233,51 @@ private fun matchingTypeParameters(l: IrTypeParameter?, r: IrTypeParameter): Boo
|
||||
}
|
||||
|
||||
// Returns true if type is C<T1, T2, ...> where C is declared `class C<T1, T2, ...> { ... }`
|
||||
fun isUnspecialised(paramsContainer: IrTypeParametersContainer, args: List<IrTypeArgument>, logger: Logger): Boolean {
|
||||
fun isUnspecialised(
|
||||
paramsContainer: IrTypeParametersContainer,
|
||||
args: List<IrTypeArgument>,
|
||||
logger: Logger
|
||||
): Boolean {
|
||||
return isUnspecialised(paramsContainer, args, logger, paramsContainer)
|
||||
}
|
||||
|
||||
private fun isUnspecialised(paramsContainer: IrTypeParametersContainer, args: List<IrTypeArgument>, logger: Logger, origParamsContainer: IrTypeParametersContainer): Boolean {
|
||||
val unspecialisedHere = paramsContainer.typeParameters.zip(args).all { paramAndArg ->
|
||||
(paramAndArg.second as? IrTypeProjection)?.let {
|
||||
// Type arg refers to the class' own type parameter?
|
||||
it.variance == Variance.INVARIANT &&
|
||||
matchingTypeParameters(it.type.classifierOrNull?.owner as? IrTypeParameter, paramAndArg.first)
|
||||
} ?: false
|
||||
}
|
||||
private fun isUnspecialised(
|
||||
paramsContainer: IrTypeParametersContainer,
|
||||
args: List<IrTypeArgument>,
|
||||
logger: Logger,
|
||||
origParamsContainer: IrTypeParametersContainer
|
||||
): Boolean {
|
||||
val unspecialisedHere =
|
||||
paramsContainer.typeParameters.zip(args).all { paramAndArg ->
|
||||
(paramAndArg.second as? IrTypeProjection)?.let {
|
||||
// Type arg refers to the class' own type parameter?
|
||||
it.variance == Variance.INVARIANT &&
|
||||
matchingTypeParameters(
|
||||
it.type.classifierOrNull?.owner as? IrTypeParameter,
|
||||
paramAndArg.first
|
||||
)
|
||||
} ?: false
|
||||
}
|
||||
val remainingArgs = args.drop(paramsContainer.typeParameters.size)
|
||||
|
||||
val parentTypeContainer = paramsContainer.parents.firstIsInstanceOrNull<IrTypeParametersContainer>()
|
||||
val parentTypeContainer =
|
||||
paramsContainer.parents.firstIsInstanceOrNull<IrTypeParametersContainer>()
|
||||
|
||||
val parentUnspecialised = when {
|
||||
remainingArgs.isEmpty() -> true
|
||||
parentTypeContainer == null -> {
|
||||
logger.error("Found more type arguments than parameters: ${origParamsContainer.kotlinFqName.asString()}")
|
||||
false
|
||||
val parentUnspecialised =
|
||||
when {
|
||||
remainingArgs.isEmpty() -> true
|
||||
parentTypeContainer == null -> {
|
||||
logger.error(
|
||||
"Found more type arguments than parameters: ${origParamsContainer.kotlinFqName.asString()}"
|
||||
)
|
||||
false
|
||||
}
|
||||
else -> isUnspecialised(parentTypeContainer, remainingArgs, logger, origParamsContainer)
|
||||
}
|
||||
else -> isUnspecialised(parentTypeContainer, remainingArgs, logger, origParamsContainer)
|
||||
}
|
||||
return unspecialisedHere && parentUnspecialised
|
||||
}
|
||||
|
||||
// Returns true if type is C<T1, T2, ...> where C is declared `class C<T1, T2, ...> { ... }`
|
||||
fun isUnspecialised(type: IrSimpleType, logger: Logger) = (type.classifier.owner as? IrClass)?.let {
|
||||
isUnspecialised(it, type.arguments, logger)
|
||||
} ?: false
|
||||
fun isUnspecialised(type: IrSimpleType, logger: Logger) =
|
||||
(type.classifier.owner as? IrClass)?.let { isUnspecialised(it, type.arguments, logger) }
|
||||
?: false
|
||||
|
||||
@@ -3,7 +3,11 @@ package com.github.codeql.comments
|
||||
import com.github.codeql.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
|
||||
class CommentExtractorLighterAST(fileExtractor: KotlinFileExtractor, file: IrFile, fileLabel: Label<out DbFile>): CommentExtractor(fileExtractor, file, fileLabel) {
|
||||
class CommentExtractorLighterAST(
|
||||
fileExtractor: KotlinFileExtractor,
|
||||
file: IrFile,
|
||||
fileLabel: Label<out DbFile>
|
||||
) : CommentExtractor(fileExtractor, file, fileLabel) {
|
||||
// We don't support LighterAST with old Kotlin versions
|
||||
fun extract(): Boolean {
|
||||
return false
|
||||
|
||||
@@ -2,4 +2,4 @@ package com.github.codeql.utils.versions
|
||||
|
||||
import org.jetbrains.kotlin.ir.SourceManager
|
||||
|
||||
typealias FileEntry = SourceManager.FileEntry
|
||||
typealias FileEntry = SourceManager.FileEntry
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
package org.jetbrains.kotlin.ir.symbols
|
||||
|
||||
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
|
||||
annotation class IrSymbolInternals
|
||||
@RequiresOptIn(level = RequiresOptIn.Level.WARNING) annotation class IrSymbolInternals
|
||||
|
||||
@@ -11,9 +11,11 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
fun isUnderscoreParameter(vp: IrValueParameter) =
|
||||
try {
|
||||
DescriptorToSourceUtils.getSourceFromDescriptor(vp.descriptor)
|
||||
?.safeAs<KtParameter>()?.isSingleUnderscore == true
|
||||
} catch(e: NotImplementedError) {
|
||||
// Some kinds of descriptor throw in `getSourceFromDescriptor` as that method is not normally expected to
|
||||
?.safeAs<KtParameter>()
|
||||
?.isSingleUnderscore == true
|
||||
} catch (e: NotImplementedError) {
|
||||
// Some kinds of descriptor throw in `getSourceFromDescriptor` as that method is not
|
||||
// normally expected to
|
||||
// be applied to synthetic functions.
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,5 +7,5 @@ import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass
|
||||
We need this class to exist, but the compiler will never give us an
|
||||
instance of it.
|
||||
*/
|
||||
abstract class JavaBinarySourceElement private constructor(val javaClass: BinaryJavaClass): SourceElement {
|
||||
}
|
||||
abstract class JavaBinarySourceElement private constructor(val javaClass: BinaryJavaClass) :
|
||||
SourceElement {}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
package com.github.codeql
|
||||
|
||||
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
|
||||
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
|
||||
|
||||
@OptIn(ExperimentalCompilerApi::class)
|
||||
abstract class Kotlin2ComponentRegistrar : ComponentRegistrar {
|
||||
|
||||
@@ -2,18 +2,17 @@ package com.github.codeql
|
||||
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
|
||||
class LinesOfCodeLighterAST(
|
||||
val logger: FileLogger,
|
||||
val tw: FileTrapWriter,
|
||||
val file: IrFile
|
||||
) {
|
||||
class LinesOfCodeLighterAST(val logger: FileLogger, val tw: FileTrapWriter, val file: IrFile) {
|
||||
// We don't support LighterAST with old Kotlin versions
|
||||
fun linesOfCodeInFile(@Suppress("UNUSED_PARAMETER") id: Label<DbFile>): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
// We don't support LighterAST with old Kotlin versions
|
||||
fun linesOfCodeInDeclaration(@Suppress("UNUSED_PARAMETER") d: IrDeclaration, @Suppress("UNUSED_PARAMETER") id: Label<out DbSourceline>): Boolean {
|
||||
fun linesOfCodeInDeclaration(
|
||||
@Suppress("UNUSED_PARAMETER") d: IrDeclaration,
|
||||
@Suppress("UNUSED_PARAMETER") id: Label<out DbSourceline>
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,12 +14,20 @@ fun getClassByClassId(pluginContext: IrPluginContext, id: ClassId): IrClassSymbo
|
||||
return getClassByFqName(pluginContext, id.asSingleFqName())
|
||||
}
|
||||
|
||||
fun getFunctionsByFqName(pluginContext: IrPluginContext, pkgName: FqName, name: Name): Collection<IrSimpleFunctionSymbol> {
|
||||
fun getFunctionsByFqName(
|
||||
pluginContext: IrPluginContext,
|
||||
pkgName: FqName,
|
||||
name: Name
|
||||
): Collection<IrSimpleFunctionSymbol> {
|
||||
val fqName = pkgName.child(name)
|
||||
return pluginContext.referenceFunctions(fqName)
|
||||
}
|
||||
|
||||
fun getPropertiesByFqName(pluginContext: IrPluginContext, pkgName: FqName, name: Name): Collection<IrPropertySymbol> {
|
||||
fun getPropertiesByFqName(
|
||||
pluginContext: IrPluginContext,
|
||||
pkgName: FqName,
|
||||
name: Name
|
||||
): Collection<IrPropertySymbol> {
|
||||
val fqName = pkgName.child(name)
|
||||
return pluginContext.referenceProperties(fqName)
|
||||
}
|
||||
|
||||
@@ -3,4 +3,3 @@ package com.github.codeql.utils.versions
|
||||
import org.jetbrains.kotlin.ir.expressions.IrSyntheticBodyKind
|
||||
|
||||
val kind_ENUM_ENTRIES: IrSyntheticBodyKind? = null
|
||||
|
||||
|
||||
@@ -3,5 +3,4 @@ package com.github.codeql.utils.versions
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.isRawType
|
||||
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||
|
||||
|
||||
fun IrSimpleType.isRawType() = this.isRawType()
|
||||
fun IrSimpleType.isRawType() = this.isRawType()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.github.codeql.utils.versions
|
||||
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.backend.common.ir.allOverridden
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
|
||||
fun IrSimpleFunction.allOverriddenIncludingSelf() = this.allOverridden(includeSelf = true)
|
||||
fun IrSimpleFunction.allOverriddenIncludingSelf() = this.allOverridden(includeSelf = true)
|
||||
|
||||
@@ -5,4 +5,4 @@ import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
||||
|
||||
@OptIn(ObsoleteDescriptorBasedAPI::class)
|
||||
fun getAnnotationType(context: IrPluginContext) =
|
||||
context.typeTranslator.translateType(context.builtIns.annotationType)
|
||||
context.typeTranslator.translateType(context.builtIns.annotationType)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.github.codeql.utils.versions
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
||||
|
||||
fun copyParameterToFunction(p: IrValueParameter, f: IrFunction) = p.copyTo(f)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.github.codeql.utils.versions
|
||||
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
|
||||
fun IrClass.createImplicitParameterDeclarationWithWrappedDescriptor() = this.createImplicitParameterDeclarationWithWrappedDescriptor()
|
||||
fun IrClass.createImplicitParameterDeclarationWithWrappedDescriptor() =
|
||||
this.createImplicitParameterDeclarationWithWrappedDescriptor()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.github.codeql.utils.versions
|
||||
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
fun getFileClassFqName(@Suppress("UNUSED_PARAMETER") d: IrDeclaration): FqName? {
|
||||
return null
|
||||
|
||||
@@ -3,4 +3,4 @@ package com.github.codeql.utils.versions
|
||||
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||
import org.jetbrains.kotlin.ir.types.impl.IrTypeBase
|
||||
|
||||
fun getKotlinType(s: IrSimpleType) = (s as? IrTypeBase)?.kotlinType
|
||||
fun getKotlinType(s: IrSimpleType) = (s as? IrTypeBase)?.kotlinType
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.jetbrains.kotlin.ir.util
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.lower.parents as kParents
|
||||
import org.jetbrains.kotlin.backend.common.lower.parentsWithSelf as kParentsWithSelf
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
|
||||
val IrDeclaration.parents: Sequence<IrDeclarationParent>
|
||||
get() = this.kParents
|
||||
|
||||
val IrDeclaration.parentsWithSelf: Sequence<IrDeclarationParent>
|
||||
get() = this.kParentsWithSelf
|
||||
@@ -3,6 +3,6 @@ package com.github.codeql.utils.versions
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.withHasQuestionMark
|
||||
|
||||
fun IrType.codeQlWithHasQuestionMark(b : Boolean): IrType {
|
||||
fun IrType.codeQlWithHasQuestionMark(b: Boolean): IrType {
|
||||
return this.withHasQuestionMark(b)
|
||||
}
|
||||
|
||||
@@ -2,4 +2,4 @@ package com.github.codeql.utils.versions
|
||||
|
||||
import org.jetbrains.kotlin.ir.IrFileEntry
|
||||
|
||||
typealias FileEntry = IrFileEntry
|
||||
typealias FileEntry = IrFileEntry
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
package com.github.codeql.utils.versions
|
||||
|
||||
import com.github.codeql.utils.Psi2IrFacade
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.backend.common.psi.PsiSourceManager
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.getKtFile
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import com.github.codeql.utils.Psi2IrFacade
|
||||
|
||||
fun getPsi2Ir(): Psi2IrFacade? = Psi2Ir()
|
||||
|
||||
private class Psi2Ir(): Psi2IrFacade {
|
||||
private class Psi2Ir() : Psi2IrFacade {
|
||||
override fun getKtFile(irFile: IrFile): KtFile? {
|
||||
return irFile.getKtFile()
|
||||
}
|
||||
|
||||
@@ -2,4 +2,4 @@ package com.github.codeql.utils.versions
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
|
||||
fun functionN(pluginContext: IrPluginContext) = pluginContext.irBuiltIns::functionN
|
||||
fun functionN(pluginContext: IrPluginContext) = pluginContext.irBuiltIns::functionN
|
||||
|
||||
@@ -2,5 +2,4 @@ package com.github.codeql.utils.versions
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
|
||||
fun getAnnotationType(context: IrPluginContext) =
|
||||
context.irBuiltIns.annotationType
|
||||
fun getAnnotationType(context: IrPluginContext) = context.irBuiltIns.annotationType
|
||||
|
||||
@@ -3,4 +3,5 @@ package com.github.codeql.utils.versions
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||
|
||||
fun isUnderscoreParameter(vp: IrValueParameter) = vp.origin == IrDeclarationOrigin.UNDERSCORE_PARAMETER
|
||||
fun isUnderscoreParameter(vp: IrValueParameter) =
|
||||
vp.origin == IrDeclarationOrigin.UNDERSCORE_PARAMETER
|
||||
|
||||
@@ -3,5 +3,4 @@ package com.github.codeql.utils.versions
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isRawType
|
||||
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||
|
||||
|
||||
fun IrSimpleType.isRawType() = this.isRawType()
|
||||
fun IrSimpleType.isRawType() = this.isRawType()
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.github.codeql.utils.versions
|
||||
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrField
|
||||
import org.jetbrains.kotlin.ir.declarations.IrMemberWithContainerSource
|
||||
import org.jetbrains.kotlin.load.kotlin.FacadeClassSource
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
fun getFileClassFqName(d: IrDeclaration): FqName? {
|
||||
// d is in a file class.
|
||||
|
||||
@@ -2,4 +2,4 @@ package com.github.codeql.utils.versions
|
||||
|
||||
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||
|
||||
fun getKotlinType(s: IrSimpleType) = s.kotlinType
|
||||
fun getKotlinType(s: IrSimpleType) = s.kotlinType
|
||||
|
||||
@@ -4,7 +4,7 @@ import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.makeNotNull
|
||||
import org.jetbrains.kotlin.ir.types.makeNullable
|
||||
|
||||
fun IrType.codeQlWithHasQuestionMark(b : Boolean): IrType {
|
||||
fun IrType.codeQlWithHasQuestionMark(b: Boolean): IrType {
|
||||
if (b) {
|
||||
return this.makeNullable()
|
||||
} else {
|
||||
|
||||
@@ -3,4 +3,4 @@ package com.github.codeql.utils.versions
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.util.allOverridden
|
||||
|
||||
fun IrSimpleFunction.allOverriddenIncludingSelf() = this.allOverridden(includeSelf = true)
|
||||
fun IrSimpleFunction.allOverriddenIncludingSelf() = this.allOverridden(includeSelf = true)
|
||||
|
||||
@@ -4,4 +4,4 @@ import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||
import org.jetbrains.kotlin.ir.util.copyTo
|
||||
|
||||
fun copyParameterToFunction(p: IrValueParameter, f: IrFunction) = p.copyTo(f)
|
||||
fun copyParameterToFunction(p: IrValueParameter, f: IrFunction) = p.copyTo(f)
|
||||
|
||||
@@ -3,4 +3,5 @@ package com.github.codeql.utils.versions
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.util.createImplicitParameterDeclarationWithWrappedDescriptor
|
||||
|
||||
fun IrClass.createImplicitParameterDeclarationWithWrappedDescriptor() = this.createImplicitParameterDeclarationWithWrappedDescriptor()
|
||||
fun IrClass.createImplicitParameterDeclarationWithWrappedDescriptor() =
|
||||
this.createImplicitParameterDeclarationWithWrappedDescriptor()
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package com.github.codeql.utils
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.extensions.FirIncompatiblePluginAPI
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
@@ -17,12 +16,20 @@ fun getClassByClassId(pluginContext: IrPluginContext, id: ClassId): IrClassSymbo
|
||||
return pluginContext.referenceClass(id)
|
||||
}
|
||||
|
||||
fun getFunctionsByFqName(pluginContext: IrPluginContext, pkgName: FqName, name: Name): Collection<IrSimpleFunctionSymbol> {
|
||||
fun getFunctionsByFqName(
|
||||
pluginContext: IrPluginContext,
|
||||
pkgName: FqName,
|
||||
name: Name
|
||||
): Collection<IrSimpleFunctionSymbol> {
|
||||
val id = CallableId(pkgName, name)
|
||||
return pluginContext.referenceFunctions(id)
|
||||
}
|
||||
|
||||
fun getPropertiesByFqName(pluginContext: IrPluginContext, pkgName: FqName, name: Name): Collection<IrPropertySymbol> {
|
||||
fun getPropertiesByFqName(
|
||||
pluginContext: IrPluginContext,
|
||||
pkgName: FqName,
|
||||
name: Name
|
||||
): Collection<IrPropertySymbol> {
|
||||
val id = CallableId(pkgName, name)
|
||||
return pluginContext.referenceProperties(id)
|
||||
}
|
||||
|
||||
@@ -3,4 +3,3 @@ package com.github.codeql.utils.versions
|
||||
import org.jetbrains.kotlin.ir.expressions.IrSyntheticBodyKind
|
||||
|
||||
val kind_ENUM_ENTRIES: IrSyntheticBodyKind? = IrSyntheticBodyKind.ENUM_ENTRIES
|
||||
|
||||
|
||||
@@ -4,21 +4,26 @@ import com.github.codeql.*
|
||||
import com.intellij.lang.LighterASTNode
|
||||
import com.intellij.util.diff.FlyweightCapableTreeStructure
|
||||
import org.jetbrains.kotlin.fir.backend.FirMetadataSource
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.util.SYNTHETIC_OFFSET
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.kdoc.lexer.KDocTokens
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.util.getChildren
|
||||
|
||||
class CommentExtractorLighterAST(fileExtractor: KotlinFileExtractor, file: IrFile, fileLabel: Label<out DbFile>): CommentExtractor(fileExtractor, file, fileLabel) {
|
||||
class CommentExtractorLighterAST(
|
||||
fileExtractor: KotlinFileExtractor,
|
||||
file: IrFile,
|
||||
fileLabel: Label<out DbFile>
|
||||
) : CommentExtractor(fileExtractor, file, fileLabel) {
|
||||
// Returns true if it extracted the comments; false otherwise.
|
||||
fun extract(): Boolean {
|
||||
val sourceElement = (file.metadata as? FirMetadataSource.File)?.files?.elementAtOrNull(0)?.source
|
||||
val sourceElement =
|
||||
(file.metadata as? FirMetadataSource.File)?.files?.elementAtOrNull(0)?.source
|
||||
val treeStructure = sourceElement?.treeStructure
|
||||
if (treeStructure == null) {
|
||||
return false
|
||||
@@ -33,34 +38,46 @@ class CommentExtractorLighterAST(fileExtractor: KotlinFileExtractor, file: IrFil
|
||||
fun LighterASTNode.isKDocComment() = this.tokenType == KDocTokens.KDOC
|
||||
|
||||
val kDocOwners = mutableMapOf<Int, MutableList<IrElement>>()
|
||||
val visitor = object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
val metadata = (element as? IrMetadataSourceOwner)?.metadata
|
||||
val sourceElement = (metadata as? FirMetadataSource)?.fir?.source
|
||||
val treeStructure = sourceElement?.treeStructure
|
||||
val visitor =
|
||||
object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
val metadata = (element as? IrMetadataSourceOwner)?.metadata
|
||||
val sourceElement = (metadata as? FirMetadataSource)?.fir?.source
|
||||
val treeStructure = sourceElement?.treeStructure
|
||||
|
||||
if (treeStructure != null) {
|
||||
sourceElement.lighterASTNode.getChildren(treeStructure).firstOrNull { it.isKDocComment() }
|
||||
?.let { kDoc ->
|
||||
// LighterASTNodes are not stable, so we can't
|
||||
// use the node itself as the key. But the
|
||||
// startOffset should uniquely identify them
|
||||
// anyway.
|
||||
val startOffset = kDoc.startOffset
|
||||
if (startOffset != UNDEFINED_OFFSET && startOffset != SYNTHETIC_OFFSET) {
|
||||
kDocOwners.getOrPut(startOffset, {mutableListOf<IrElement>()}).add(element)
|
||||
if (treeStructure != null) {
|
||||
sourceElement.lighterASTNode
|
||||
.getChildren(treeStructure)
|
||||
.firstOrNull { it.isKDocComment() }
|
||||
?.let { kDoc ->
|
||||
// LighterASTNodes are not stable, so we can't
|
||||
// use the node itself as the key. But the
|
||||
// startOffset should uniquely identify them
|
||||
// anyway.
|
||||
val startOffset = kDoc.startOffset
|
||||
if (
|
||||
startOffset != UNDEFINED_OFFSET &&
|
||||
startOffset != SYNTHETIC_OFFSET
|
||||
) {
|
||||
kDocOwners
|
||||
.getOrPut(startOffset, { mutableListOf<IrElement>() })
|
||||
.add(element)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
element.acceptChildrenVoid(this)
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
file.acceptVoid(visitor)
|
||||
return kDocOwners
|
||||
}
|
||||
|
||||
private fun extractComments(node: LighterASTNode, treeStructure: FlyweightCapableTreeStructure<LighterASTNode>, owners: Map<Int, List<IrElement>>) {
|
||||
private fun extractComments(
|
||||
node: LighterASTNode,
|
||||
treeStructure: FlyweightCapableTreeStructure<LighterASTNode>,
|
||||
owners: Map<Int, List<IrElement>>
|
||||
) {
|
||||
node.getChildren(treeStructure).forEach {
|
||||
if (KtTokens.COMMENTS.contains(it.tokenType)) {
|
||||
extractComment(it, owners)
|
||||
@@ -71,21 +88,22 @@ class CommentExtractorLighterAST(fileExtractor: KotlinFileExtractor, file: IrFil
|
||||
}
|
||||
|
||||
private fun extractComment(comment: LighterASTNode, owners: Map<Int, List<IrElement>>) {
|
||||
val type: CommentType = when (comment.tokenType) {
|
||||
KtTokens.EOL_COMMENT -> {
|
||||
CommentType.SingleLine
|
||||
val type: CommentType =
|
||||
when (comment.tokenType) {
|
||||
KtTokens.EOL_COMMENT -> {
|
||||
CommentType.SingleLine
|
||||
}
|
||||
KtTokens.BLOCK_COMMENT -> {
|
||||
CommentType.Block
|
||||
}
|
||||
KtTokens.DOC_COMMENT -> {
|
||||
CommentType.Doc
|
||||
}
|
||||
else -> {
|
||||
logger.warn("Unhandled comment token type: ${comment.tokenType}")
|
||||
return
|
||||
}
|
||||
}
|
||||
KtTokens.BLOCK_COMMENT -> {
|
||||
CommentType.Block
|
||||
}
|
||||
KtTokens.DOC_COMMENT -> {
|
||||
CommentType.Doc
|
||||
}
|
||||
else -> {
|
||||
logger.warn("Unhandled comment token type: ${comment.tokenType}")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
val commentLabel = tw.getFreshIdLabel<DbKtcomment>()
|
||||
tw.writeKtComments(commentLabel, type.value, comment.toString())
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
package com.github.codeql
|
||||
|
||||
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
|
||||
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
|
||||
|
||||
@OptIn(ExperimentalCompilerApi::class)
|
||||
abstract class Kotlin2ComponentRegistrar : ComponentRegistrar {
|
||||
|
||||
@@ -2,23 +2,19 @@ package com.github.codeql
|
||||
|
||||
import com.intellij.lang.LighterASTNode
|
||||
import com.intellij.util.diff.FlyweightCapableTreeStructure
|
||||
import org.jetbrains.kotlin.config.KotlinCompilerVersion
|
||||
import org.jetbrains.kotlin.fir.backend.FirMetadataSource
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.KtSourceElement
|
||||
import org.jetbrains.kotlin.fir.backend.FirMetadataSource
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.util.getChildren
|
||||
|
||||
class LinesOfCodeLighterAST(
|
||||
val logger: FileLogger,
|
||||
val tw: FileTrapWriter,
|
||||
val file: IrFile
|
||||
) {
|
||||
class LinesOfCodeLighterAST(val logger: FileLogger, val tw: FileTrapWriter, val file: IrFile) {
|
||||
val fileEntry = file.fileEntry
|
||||
|
||||
fun linesOfCodeInFile(id: Label<DbFile>): Boolean {
|
||||
val sourceElement = (file.metadata as? FirMetadataSource.File)?.files?.elementAtOrNull(0)?.source
|
||||
val sourceElement =
|
||||
(file.metadata as? FirMetadataSource.File)?.files?.elementAtOrNull(0)?.source
|
||||
if (sourceElement == null) {
|
||||
return false
|
||||
}
|
||||
@@ -42,7 +38,11 @@ class LinesOfCodeLighterAST(
|
||||
return true
|
||||
}
|
||||
|
||||
private fun linesOfCodeInLighterAST(id: Label<out DbSourceline>, e: IrElement, s: KtSourceElement) {
|
||||
private fun linesOfCodeInLighterAST(
|
||||
id: Label<out DbSourceline>,
|
||||
e: IrElement,
|
||||
s: KtSourceElement
|
||||
) {
|
||||
val rootStartOffset = s.startOffset
|
||||
val rootEndOffset = s.endOffset
|
||||
if (rootStartOffset < 0 || rootEndOffset < 0) {
|
||||
@@ -63,14 +63,28 @@ class LinesOfCodeLighterAST(
|
||||
|
||||
val treeStructure = s.treeStructure
|
||||
|
||||
processSubtree(e, treeStructure, rootFirstLine, rootLastLine, lineContents, s.lighterASTNode)
|
||||
processSubtree(
|
||||
e,
|
||||
treeStructure,
|
||||
rootFirstLine,
|
||||
rootLastLine,
|
||||
lineContents,
|
||||
s.lighterASTNode
|
||||
)
|
||||
|
||||
val code = lineContents.count { it.containsCode }
|
||||
val comment = lineContents.count { it.containsComment }
|
||||
tw.writeNumlines(id, numLines, code, comment)
|
||||
}
|
||||
|
||||
private fun processSubtree(e: IrElement, treeStructure: FlyweightCapableTreeStructure<LighterASTNode>, rootFirstLine: Int, rootLastLine: Int, lineContents: Array<LineContent>, node: LighterASTNode) {
|
||||
private fun processSubtree(
|
||||
e: IrElement,
|
||||
treeStructure: FlyweightCapableTreeStructure<LighterASTNode>,
|
||||
rootFirstLine: Int,
|
||||
rootLastLine: Int,
|
||||
lineContents: Array<LineContent>,
|
||||
node: LighterASTNode
|
||||
) {
|
||||
if (KtTokens.WHITESPACES.contains(node.tokenType)) {
|
||||
return
|
||||
}
|
||||
@@ -120,7 +134,7 @@ class LinesOfCodeLighterAST(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(child in children) {
|
||||
for (child in children) {
|
||||
processSubtree(e, treeStructure, rootFirstLine, rootLastLine, lineContents, child)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
// Nothing to do
|
||||
@@ -1,31 +1,197 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Before running this, make sure there is an SSO-enabled token with package:write
|
||||
# permissions to codeql supplied via the GITHUB_TOKEN environment variable
|
||||
help="Usage: ./publish [--override-release] [--dry-run]
|
||||
Publish the automodel query pack.
|
||||
|
||||
AUTOMODEL_ROOT="$(readlink -f "$(dirname $0)")"
|
||||
WORKSPACE_ROOT="$AUTOMODEL_ROOT/../../.."
|
||||
GRPS="automodel,-test"
|
||||
If no arguments are provided, publish the version of the codeql repo specified by the latest official release of the codeml-automodel repo.
|
||||
If the --override-release argument is provided, your current local HEAD is used (for unofficial releases or patching).
|
||||
If the --dry-run argument is provided, the release is not published (for testing purposes)."
|
||||
|
||||
if [ -z "$CODEQL_DIST" ]; then
|
||||
echo "CODEQL_DIST not set"
|
||||
exit -1
|
||||
# Echo the help message
|
||||
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
|
||||
echo "$help"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cd "$AUTOMODEL_ROOT"
|
||||
# Check the number of arguments are valid
|
||||
if [ $# -gt 2 ]; then
|
||||
echo "Error: Invalid arguments provided"
|
||||
echo "$help"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OVERRIDE_RELEASE=0
|
||||
DRY_RUN=0
|
||||
for arg in "$@"
|
||||
do
|
||||
case $arg in
|
||||
--override-release)
|
||||
OVERRIDE_RELEASE=1
|
||||
shift # Remove --override-release from processing
|
||||
;;
|
||||
--dry-run)
|
||||
DRY_RUN=1
|
||||
shift # Remove --dry-run from processing
|
||||
;;
|
||||
*)
|
||||
echo "Error: Invalid argument provided: $arg"
|
||||
echo "$help"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Describe what we're about to do based on the command-line arguments
|
||||
if [ $OVERRIDE_RELEASE = 1 ]; then
|
||||
echo "Publishing the current HEAD of the automodel repo"
|
||||
else
|
||||
echo "Publishing the version of the automodel repo specified by the latest official release of the codeml-automodel repo"
|
||||
fi
|
||||
if [ $DRY_RUN = 1 ]; then
|
||||
echo "Dry run: we will step through the process but we won't publish the query pack"
|
||||
else
|
||||
echo "Not a dry run! Publishing the query pack"
|
||||
fi
|
||||
|
||||
# If we're publishing the codeml-automodel release then we will checkout the sha specified in the release.
|
||||
# So we need to check that there are no uncommitted changes in the local branch.
|
||||
# And, if we're publishing the current HEAD, it's cleaner to ensure that there are no uncommitted changes.
|
||||
if ! git diff --quiet; then
|
||||
echo "Error: Uncommitted changes exist. Please commit or stash your changes before publishing."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check the above environment variables are set
|
||||
if [ -z "${GITHUB_TOKEN}" ]; then
|
||||
echo "Error: GITHUB_TOKEN environment variable not set. Please set this to a token with package:write permissions to codeql."
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "${GH_TOKEN}" ]; then
|
||||
echo "Error: GH_TOKEN environment variable not set. Please set this to a token with repo permissions to github/codeml-automodel."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get the sha of the previous release, i.e. the last commit to the main branch that updated the query pack version
|
||||
PREVIOUS_RELEASE_SHA=$(git rev-list -n 1 main -- ./src/qlpack.yml)
|
||||
if [ -z "$PREVIOUS_RELEASE_SHA" ]; then
|
||||
echo "Error: Could not get the sha of the previous release of codeml-automodel query pack"
|
||||
exit 1
|
||||
else
|
||||
echo "Previous query-pack release sha: $PREVIOUS_RELEASE_SHA"
|
||||
fi
|
||||
|
||||
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
CURRENT_SHA=$(git rev-parse HEAD)
|
||||
|
||||
if [ $OVERRIDE_RELEASE = 1 ]; then
|
||||
# Check that the current HEAD is downstream from PREVIOUS_RELEASE_SHA
|
||||
if ! git merge-base --is-ancestor "$PREVIOUS_RELEASE_SHA" "$CURRENT_SHA"; then
|
||||
echo "Error: The current HEAD is not downstream from the previous release"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# Get the latest release of codeml-automodel
|
||||
TAG_NAME=$(gh api -H 'Accept: application/vnd.github+json' -H 'X-GitHub-Api-Version: 2022-11-28' /repos/github/codeml-automodel/releases/latest | jq -r .tag_name)
|
||||
# Check TAG_NAME is not empty
|
||||
if [ -z "$TAG_NAME" ]; then
|
||||
echo "Error: Could not get latest release of codeml-automodel"
|
||||
exit 1
|
||||
fi
|
||||
echo "Updating to latest automodel release: $TAG_NAME"
|
||||
# Before downloading, delete any existing release.zip, and ignore failure if not present
|
||||
rm release.zip || true
|
||||
gh release download $TAG_NAME -A zip -O release.zip --repo 'https://github.com/github/codeml-automodel'
|
||||
# Before unzipping, delete any existing release directory, and ignore failure if not present
|
||||
rm -rf release || true
|
||||
unzip -o release.zip -d release
|
||||
REVISION=$(jq -r '.["codeql-sha"]' release/codeml-automodel*/codeml-automodel-release.json)
|
||||
echo "The latest codeml-automodel release specifies the codeql sha $REVISION"
|
||||
# Check that REVISION is downstream from PREVIOUS_RELEASE_SHA
|
||||
if ! git merge-base --is-ancestor "$PREVIOUS_RELEASE_SHA" "$REVISION"; then
|
||||
echo "Error: The codeql version $REVISION is not downstream of the query-pack version $PREVIOUS_RELEASE_SHA"
|
||||
exit 1
|
||||
fi
|
||||
# Get the version of the codeql code specified by the codeml-automodel release
|
||||
git checkout "$REVISION"
|
||||
fi
|
||||
|
||||
# Get the absolute path of the automodel repo
|
||||
AUTOMODEL_ROOT="$(readlink -f "$(dirname $0)")"
|
||||
# Get the absolute path of the workspace root
|
||||
WORKSPACE_ROOT="$AUTOMODEL_ROOT/../../.."
|
||||
# Specify the groups of queries to test and publish
|
||||
GRPS="automodel,-test"
|
||||
|
||||
# Install the codeql gh extension
|
||||
gh extensions install github/gh-codeql
|
||||
|
||||
pushd "$AUTOMODEL_ROOT"
|
||||
echo Testing automodel queries
|
||||
"${CODEQL_DIST}/codeql" test run test
|
||||
gh codeql test run test
|
||||
popd
|
||||
|
||||
cd "$WORKSPACE_ROOT"
|
||||
pushd "$WORKSPACE_ROOT"
|
||||
echo "Preparing the release"
|
||||
gh codeql pack release --groups $GRPS -v
|
||||
|
||||
echo Preparing release
|
||||
"${CODEQL_DIST}/codeql" pack release --groups $GRPS
|
||||
if [ $DRY_RUN = 1 ]; then
|
||||
echo "Dry run: not publishing the query pack"
|
||||
gh codeql pack publish --groups $GRPS --dry-run -v
|
||||
else
|
||||
echo "Not a dry run! Publishing the query pack"
|
||||
gh codeql pack publish --groups $GRPS -v
|
||||
fi
|
||||
|
||||
echo Publishing automodel
|
||||
"${CODEQL_DIST}/codeql" pack publish --groups $GRPS
|
||||
echo "Bumping versions"
|
||||
gh codeql pack post-release --groups $GRPS -v
|
||||
popd
|
||||
|
||||
echo Bumping versions
|
||||
"${CODEQL_DIST}/codeql" pack post-release --groups $GRPS
|
||||
# The above commands update
|
||||
# ./src/CHANGELOG.md
|
||||
# ./src/codeql-pack.release.yml
|
||||
# ./src/qlpack.yml
|
||||
# and add a new file
|
||||
# ./src/change-notes/released/<version>.md
|
||||
|
||||
echo Automodel packs successfully published. Please commit and push the version changes.
|
||||
# Get the filename of the most recently created file in ./src/change-notes/released/*.md
|
||||
# This will be the file for the new release
|
||||
NEW_CHANGE_NOTES_FILE=$(ls -t ./src/change-notes/released/*.md | head -n 1)
|
||||
|
||||
# Make a copy of the modified files
|
||||
mv ./src/CHANGELOG.md ./src/CHANGELOG.md.dry-run
|
||||
mv ./src/codeql-pack.release.yml ./src/codeql-pack.release.yml.dry-run
|
||||
mv ./src/qlpack.yml ./src/qlpack.yml.dry-run
|
||||
mv "$NEW_CHANGE_NOTES_FILE" ./src/change-notes/released.md.dry-run
|
||||
|
||||
if [ $OVERRIDE_RELEASE = 1 ]; then
|
||||
# Restore the original files
|
||||
git checkout ./src/CHANGELOG.md
|
||||
git checkout ./src/codeql-pack.release.yml
|
||||
git checkout ./src/qlpack.yml
|
||||
else
|
||||
# Restore the original files
|
||||
git checkout "$CURRENT_BRANCH" --force
|
||||
fi
|
||||
|
||||
if [ $DRY_RUN = 1 ]; then
|
||||
echo "Inspect the updated dry-run version files:"
|
||||
ls -l ./src/*.dry-run
|
||||
ls -l ./src/change-notes/*.dry-run
|
||||
else
|
||||
# Add the updated files to the current branch
|
||||
echo "Adding the version changes"
|
||||
mv -f ./src/CHANGELOG.md.dry-run ./src/CHANGELOG.md
|
||||
mv -f ./src/codeql-pack.release.yml.dry-run ./src/codeql-pack.release.yml
|
||||
mv -f ./src/qlpack.yml.dry-run ./src/qlpack.yml
|
||||
mv -f ./src/change-notes/released.md.dry-run "$NEW_CHANGE_NOTES_FILE"
|
||||
git add ./src/CHANGELOG.md
|
||||
git add ./src/codeql-pack.release.yml
|
||||
git add ./src/qlpack.yml
|
||||
git add "$NEW_CHANGE_NOTES_FILE"
|
||||
echo "Added the following updated version files to the current branch:"
|
||||
git status -s
|
||||
echo "To complete the release, please commit these files and merge to the main branch"
|
||||
fi
|
||||
|
||||
echo "Done"
|
||||
@@ -15,7 +15,6 @@ private import semmle.code.java.security.QueryInjection
|
||||
private import semmle.code.java.dataflow.internal.ModelExclusions as ModelExclusions
|
||||
private import AutomodelJavaUtil as AutomodelJavaUtil
|
||||
private import semmle.code.java.security.PathSanitizer as PathSanitizer
|
||||
private import AutomodelSharedGetCallable as AutomodelSharedGetCallable
|
||||
import AutomodelSharedCharacteristics as SharedCharacteristics
|
||||
import AutomodelEndpointTypes as AutomodelEndpointTypes
|
||||
|
||||
@@ -26,14 +25,18 @@ newtype JavaRelatedLocationType =
|
||||
|
||||
newtype TApplicationModeEndpoint =
|
||||
TExplicitArgument(Call call, DataFlow::Node arg) {
|
||||
AutomodelJavaUtil::isFromSource(call) and
|
||||
exists(Argument argExpr |
|
||||
arg.asExpr() = argExpr and call = argExpr.getCall() and not argExpr.isVararg()
|
||||
)
|
||||
} or
|
||||
TInstanceArgument(Call call, DataFlow::Node arg) {
|
||||
arg = DataFlow::getInstanceArgument(call) and not call instanceof ConstructorCall
|
||||
AutomodelJavaUtil::isFromSource(call) and
|
||||
arg = DataFlow::getInstanceArgument(call) and
|
||||
not call instanceof ConstructorCall
|
||||
} or
|
||||
TImplicitVarargsArray(Call call, DataFlow::Node arg, int idx) {
|
||||
AutomodelJavaUtil::isFromSource(call) and
|
||||
exists(Argument argExpr |
|
||||
arg.asExpr() = argExpr and
|
||||
call.getArgument(idx) = argExpr and
|
||||
@@ -41,8 +44,12 @@ newtype TApplicationModeEndpoint =
|
||||
not exists(int i | i < idx and call.getArgument(i).(Argument).isVararg())
|
||||
)
|
||||
} or
|
||||
TMethodReturnValue(Call call) { not call instanceof ConstructorCall } or
|
||||
TMethodReturnValue(Call call) {
|
||||
AutomodelJavaUtil::isFromSource(call) and
|
||||
not call instanceof ConstructorCall
|
||||
} or
|
||||
TOverriddenParameter(Parameter p, Method overriddenMethod) {
|
||||
AutomodelJavaUtil::isFromSource(p) and
|
||||
not p.getCallable().callsConstructor(_) and
|
||||
p.getCallable().(Method).overrides(overriddenMethod)
|
||||
}
|
||||
@@ -56,8 +63,6 @@ abstract private class ApplicationModeEndpoint extends TApplicationModeEndpoint
|
||||
*/
|
||||
abstract Callable getCallable();
|
||||
|
||||
abstract Call getCall();
|
||||
|
||||
/**
|
||||
* Gets the input (if any) for this endpoint, eg.: `Argument[0]`.
|
||||
*
|
||||
@@ -91,50 +96,50 @@ abstract private class ApplicationModeEndpoint extends TApplicationModeEndpoint
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
class TCallArgument = TExplicitArgument or TInstanceArgument or TImplicitVarargsArray;
|
||||
|
||||
/**
|
||||
* A class representing nodes that are arguments to calls.
|
||||
* An endpoint that represents an "argument" to a call in a broad sense, including
|
||||
* both explicit arguments and the instance argument.
|
||||
*/
|
||||
class ExplicitArgument extends ApplicationModeEndpoint, TExplicitArgument {
|
||||
abstract class CallArgument extends ApplicationModeEndpoint, TCallArgument {
|
||||
Call call;
|
||||
DataFlow::Node arg;
|
||||
|
||||
ExplicitArgument() { this = TExplicitArgument(call, arg) }
|
||||
|
||||
override Callable getCallable() { result = call.getCallee().getSourceDeclaration() }
|
||||
|
||||
override Call getCall() { result = call }
|
||||
override string getMaDOutput() { none() }
|
||||
|
||||
override DataFlow::Node asNode() { result = arg }
|
||||
|
||||
Call getCall() { result = call }
|
||||
|
||||
override string toString() { result = arg.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An endpoint that represents an explicit argument to a call.
|
||||
*/
|
||||
class ExplicitArgument extends CallArgument, TExplicitArgument {
|
||||
ExplicitArgument() { this = TExplicitArgument(call, arg) }
|
||||
|
||||
private int getArgIndex() { this.asTop() = call.getArgument(result) }
|
||||
|
||||
override string getMaDInput() { result = "Argument[" + this.getArgIndex() + "]" }
|
||||
|
||||
override string getMaDOutput() { none() }
|
||||
|
||||
override Top asTop() { result = arg.asExpr() }
|
||||
|
||||
override DataFlow::Node asNode() { result = arg }
|
||||
|
||||
override string toString() { result = arg.toString() }
|
||||
}
|
||||
|
||||
class InstanceArgument extends ApplicationModeEndpoint, TInstanceArgument {
|
||||
Call call;
|
||||
DataFlow::Node arg;
|
||||
|
||||
/**
|
||||
* An endpoint that represents the instance argument to a call.
|
||||
*/
|
||||
class InstanceArgument extends CallArgument, TInstanceArgument {
|
||||
InstanceArgument() { this = TInstanceArgument(call, arg) }
|
||||
|
||||
override Callable getCallable() { result = call.getCallee().getSourceDeclaration() }
|
||||
|
||||
override Call getCall() { result = call }
|
||||
|
||||
override string getMaDInput() { result = "Argument[this]" }
|
||||
|
||||
override string getMaDOutput() { none() }
|
||||
|
||||
override Top asTop() { if exists(arg.asExpr()) then result = arg.asExpr() else result = call }
|
||||
|
||||
override DataFlow::Node asNode() { result = arg }
|
||||
|
||||
override string toString() { result = arg.toString() }
|
||||
}
|
||||
|
||||
@@ -147,26 +152,14 @@ class InstanceArgument extends ApplicationModeEndpoint, TInstanceArgument {
|
||||
* In order to be able to distinguish between varargs endpoints and regular endpoints, we export the `isVarargsArray`
|
||||
* meta data field in the extraction queries.
|
||||
*/
|
||||
class ImplicitVarargsArray extends ApplicationModeEndpoint, TImplicitVarargsArray {
|
||||
Call call;
|
||||
DataFlow::Node vararg;
|
||||
class ImplicitVarargsArray extends CallArgument, TImplicitVarargsArray {
|
||||
int idx;
|
||||
|
||||
ImplicitVarargsArray() { this = TImplicitVarargsArray(call, vararg, idx) }
|
||||
|
||||
override Callable getCallable() { result = call.getCallee().getSourceDeclaration() }
|
||||
|
||||
override Call getCall() { result = call }
|
||||
ImplicitVarargsArray() { this = TImplicitVarargsArray(call, arg, idx) }
|
||||
|
||||
override string getMaDInput() { result = "Argument[" + idx + "]" }
|
||||
|
||||
override string getMaDOutput() { none() }
|
||||
|
||||
override Top asTop() { result = call }
|
||||
|
||||
override DataFlow::Node asNode() { result = vararg }
|
||||
|
||||
override string toString() { result = vararg.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -180,8 +173,6 @@ class MethodReturnValue extends ApplicationModeEndpoint, TMethodReturnValue {
|
||||
|
||||
override Callable getCallable() { result = call.getCallee().getSourceDeclaration() }
|
||||
|
||||
override Call getCall() { result = call }
|
||||
|
||||
override string getMaDInput() { none() }
|
||||
|
||||
override string getMaDOutput() { result = "ReturnValue" }
|
||||
@@ -211,8 +202,6 @@ class OverriddenParameter extends ApplicationModeEndpoint, TOverriddenParameter
|
||||
result = overriddenMethod.getSourceDeclaration()
|
||||
}
|
||||
|
||||
override Call getCall() { none() }
|
||||
|
||||
private int getArgIndex() { p.getCallable().getParameter(result) = p }
|
||||
|
||||
override string getMaDInput() { none() }
|
||||
@@ -239,7 +228,9 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
|
||||
|
||||
class EndpointType = AutomodelEndpointTypes::EndpointType;
|
||||
|
||||
class NegativeEndpointType = AutomodelEndpointTypes::NegativeSinkType;
|
||||
class SinkType = AutomodelEndpointTypes::SinkType;
|
||||
|
||||
class SourceType = AutomodelEndpointTypes::SourceType;
|
||||
|
||||
class RelatedLocation = Location::Top;
|
||||
|
||||
@@ -248,13 +239,12 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
|
||||
// Sanitizers are currently not modeled in MaD. TODO: check if this has large negative impact.
|
||||
predicate isSanitizer(Endpoint e, EndpointType t) {
|
||||
exists(t) and
|
||||
(
|
||||
e.asNode().getType() instanceof BoxedType
|
||||
or
|
||||
e.asNode().getType() instanceof PrimitiveType
|
||||
or
|
||||
e.asNode().getType() instanceof NumberType
|
||||
)
|
||||
AutomodelJavaUtil::isUnexploitableType([
|
||||
// for most endpoints, we can get the type from the node
|
||||
e.asNode().getType(),
|
||||
// but not for calls to void methods, where we need to go via the AST
|
||||
e.asTop().(Expr).getType()
|
||||
])
|
||||
or
|
||||
t instanceof AutomodelEndpointTypes::PathInjectionSinkType and
|
||||
e.asNode() instanceof PathSanitizer::PathInjectionSanitizer
|
||||
@@ -316,7 +306,7 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
|
||||
*/
|
||||
RelatedLocation getRelatedLocation(Endpoint e, RelatedLocationType type) {
|
||||
type = CallContext() and
|
||||
result = e.getCall()
|
||||
result = e.(CallArgument).getCall()
|
||||
or
|
||||
type = MethodDoc() and
|
||||
result = e.getCallable().(Documentable).getJavadoc()
|
||||
@@ -326,22 +316,6 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
|
||||
}
|
||||
}
|
||||
|
||||
private class JavaCallable = Callable;
|
||||
|
||||
private module ApplicationModeGetCallable implements AutomodelSharedGetCallable::GetCallableSig {
|
||||
class Callable = JavaCallable;
|
||||
|
||||
class Endpoint = ApplicationCandidatesImpl::Endpoint;
|
||||
|
||||
/**
|
||||
* Returns the API callable being modeled.
|
||||
*
|
||||
* We usually want to use `.getSourceDeclaration()` instead of just 'the' callable,
|
||||
* because the source declaration callable has erased generic type parameters.
|
||||
*/
|
||||
Callable getCallable(Endpoint e) { result = e.getCall().getCallee() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains endpoints that are defined in QL code rather than as a MaD model. Ideally this predicate
|
||||
* should be empty.
|
||||
@@ -369,10 +343,10 @@ class ApplicationModeMetadataExtractor extends string {
|
||||
|
||||
predicate hasMetadata(
|
||||
Endpoint e, string package, string type, string subtypes, string name, string signature,
|
||||
string input, string output, string isVarargsArray
|
||||
string input, string output, string isVarargsArray, string alreadyAiModeled,
|
||||
string extensibleType
|
||||
) {
|
||||
exists(Callable callable |
|
||||
e.getCallable() = callable and
|
||||
exists(Callable callable | e.getCallable() = callable |
|
||||
(if exists(e.getMaDInput()) then input = e.getMaDInput() else input = "") and
|
||||
(if exists(e.getMaDOutput()) then output = e.getMaDOutput() else output = "") and
|
||||
package = callable.getDeclaringType().getPackage().getName() and
|
||||
@@ -382,19 +356,107 @@ class ApplicationModeMetadataExtractor extends string {
|
||||
subtypes = AutomodelJavaUtil::considerSubtypes(callable).toString() and
|
||||
name = callable.getName() and
|
||||
signature = ExternalFlow::paramsString(callable) and
|
||||
if e instanceof ImplicitVarargsArray
|
||||
then isVarargsArray = "true"
|
||||
else isVarargsArray = "false"
|
||||
(
|
||||
if e instanceof ImplicitVarargsArray
|
||||
then isVarargsArray = "true"
|
||||
else isVarargsArray = "false"
|
||||
) and
|
||||
extensibleType = e.getExtensibleType()
|
||||
) and
|
||||
(
|
||||
not CharacteristicsImpl::isModeled(e, _, extensibleType, _) and alreadyAiModeled = ""
|
||||
or
|
||||
CharacteristicsImpl::isModeled(e, _, extensibleType, alreadyAiModeled)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given `endpoint` should be considered a candidate for the `extensibleType`.
|
||||
*
|
||||
* The other parameters record various other properties of interest.
|
||||
*/
|
||||
predicate isCandidate(
|
||||
Endpoint endpoint, string package, string type, string subtypes, string name, string signature,
|
||||
string input, string output, string isVarargs, string extensibleType, string alreadyAiModeled
|
||||
) {
|
||||
CharacteristicsImpl::isCandidate(endpoint, _) and
|
||||
not exists(CharacteristicsImpl::UninterestingToModelCharacteristic u |
|
||||
u.appliesToEndpoint(endpoint)
|
||||
) and
|
||||
any(ApplicationModeMetadataExtractor meta)
|
||||
.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, isVarargs,
|
||||
alreadyAiModeled, extensibleType) and
|
||||
// If a node is already modeled in MaD, we don't include it as a candidate. Otherwise, we might include it as a
|
||||
// candidate for query A, but the model will label it as a sink for one of the sink types of query B, for which it's
|
||||
// already a known sink. This would result in overlap between our detected sinks and the pre-existing modeling. We
|
||||
// assume that, if a sink has already been modeled in a MaD model, then it doesn't belong to any additional sink
|
||||
// types, and we don't need to reexamine it.
|
||||
alreadyAiModeled.matches(["", "%ai-%"]) and
|
||||
AutomodelJavaUtil::includeAutomodelCandidate(package, type, name, signature)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given `endpoint` is a negative example for the `extensibleType`
|
||||
* because of the `characteristic`.
|
||||
*
|
||||
* The other parameters record various other properties of interest.
|
||||
*/
|
||||
predicate isNegativeExample(
|
||||
Endpoint endpoint, EndpointCharacteristic characteristic, float confidence, string package,
|
||||
string type, string subtypes, string name, string signature, string input, string output,
|
||||
string isVarargsArray, string extensibleType
|
||||
) {
|
||||
characteristic.appliesToEndpoint(endpoint) and
|
||||
// the node is known not to be an endpoint of any appropriate type
|
||||
forall(AutomodelEndpointTypes::EndpointType tp |
|
||||
tp = CharacteristicsImpl::getAPotentialType(endpoint)
|
||||
|
|
||||
characteristic.hasImplications(tp, false, _)
|
||||
) and
|
||||
// the lowest confidence across all endpoint types should be at least highConfidence
|
||||
confidence =
|
||||
min(float c |
|
||||
characteristic.hasImplications(CharacteristicsImpl::getAPotentialType(endpoint), false, c)
|
||||
) and
|
||||
confidence >= SharedCharacteristics::highConfidence() and
|
||||
any(ApplicationModeMetadataExtractor meta)
|
||||
.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output,
|
||||
isVarargsArray, _, extensibleType) and
|
||||
// It's valid for a node to be both a potential source/sanitizer and a sink. We don't want to include such nodes
|
||||
// as negative examples in the prompt, because they're ambiguous and might confuse the model, so we explicitly exclude them here.
|
||||
not exists(EndpointCharacteristic characteristic2, float confidence2 |
|
||||
characteristic2 != characteristic
|
||||
|
|
||||
characteristic2.appliesToEndpoint(endpoint) and
|
||||
confidence2 >= SharedCharacteristics::maximalConfidence() and
|
||||
characteristic2
|
||||
.hasImplications(CharacteristicsImpl::getAPotentialType(endpoint), true, confidence2)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given `endpoint` is a positive example for the `endpointType`.
|
||||
*
|
||||
* The other parameters record various other properties of interest.
|
||||
*/
|
||||
predicate isPositiveExample(
|
||||
Endpoint endpoint, string endpointType, string package, string type, string subtypes, string name,
|
||||
string signature, string input, string output, string isVarargsArray, string extensibleType
|
||||
) {
|
||||
any(ApplicationModeMetadataExtractor meta)
|
||||
.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output,
|
||||
isVarargsArray, _, extensibleType) and
|
||||
CharacteristicsImpl::isKnownAs(endpoint, endpointType, _) and
|
||||
exists(CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()))
|
||||
}
|
||||
|
||||
/*
|
||||
* EndpointCharacteristic classes that are specific to Automodel for Java.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A negative characteristic that indicates that an is-style boolean method is unexploitable even if it is a sink.
|
||||
* A negative characteristic that indicates that parameters of an is-style boolean method should not be considered sinks.
|
||||
*
|
||||
* A sink is highly unlikely to be exploitable if its callable's name starts with `is` and the callable has a boolean return
|
||||
* type (e.g. `isDirectory`). These kinds of calls normally do only checks, and appear before the proper call that does
|
||||
@@ -406,15 +468,15 @@ private class UnexploitableIsCharacteristic extends CharacteristicsImpl::NotASin
|
||||
UnexploitableIsCharacteristic() { this = "unexploitable (is-style boolean method)" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
not ApplicationCandidatesImpl::isSink(e, _, _) and
|
||||
e.getCallable().getName().matches("is%") and
|
||||
e.getCallable().getReturnType() instanceof BooleanType
|
||||
e.getCallable().getReturnType() instanceof BooleanType and
|
||||
not ApplicationCandidatesImpl::isSink(e, _, _)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A negative characteristic that indicates that an existence-checking boolean method is unexploitable even if it is a
|
||||
* sink.
|
||||
* A negative characteristic that indicates that parameters of an existence-checking boolean method should not be
|
||||
* considered sinks.
|
||||
*
|
||||
* A sink is highly unlikely to be exploitable if its callable's name is `exists` or `notExists` and the callable has a
|
||||
* boolean return type. These kinds of calls normally do only checks, and appear before the proper call that does the
|
||||
@@ -424,9 +486,7 @@ private class UnexploitableExistsCharacteristic extends CharacteristicsImpl::Not
|
||||
UnexploitableExistsCharacteristic() { this = "unexploitable (existence-checking boolean method)" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
not ApplicationCandidatesImpl::isSink(e, _, _) and
|
||||
exists(Callable callable |
|
||||
callable = ApplicationModeGetCallable::getCallable(e) and
|
||||
exists(Callable callable | callable = e.getCallable() |
|
||||
callable.getName().toLowerCase() = ["exists", "notexists"] and
|
||||
callable.getReturnType() instanceof BooleanType
|
||||
)
|
||||
@@ -434,14 +494,23 @@ private class UnexploitableExistsCharacteristic extends CharacteristicsImpl::Not
|
||||
}
|
||||
|
||||
/**
|
||||
* A negative characteristic that indicates that an endpoint is an argument to an exception, which is not a sink.
|
||||
* A negative characteristic that indicates that parameters of an exception method or constructor should not be considered sinks,
|
||||
* and its return value should not be considered a source.
|
||||
*/
|
||||
private class ExceptionCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic {
|
||||
private class ExceptionCharacteristic extends CharacteristicsImpl::NeitherSourceNorSinkCharacteristic
|
||||
{
|
||||
ExceptionCharacteristic() { this = "exception" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
ApplicationModeGetCallable::getCallable(e).getDeclaringType().getASupertype*() instanceof
|
||||
TypeThrowable
|
||||
e.getCallable().getDeclaringType().getASupertype*() instanceof TypeThrowable and
|
||||
(
|
||||
e.getExtensibleType() = "sinkModel" and
|
||||
not ApplicationCandidatesImpl::isSink(e, _, _)
|
||||
or
|
||||
e.getExtensibleType() = "sourceModel" and
|
||||
not ApplicationCandidatesImpl::isSource(e, _, _) and
|
||||
e.getMaDOutput() = "ReturnValue"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,7 +523,6 @@ private class IsMaDTaintStepCharacteristic extends CharacteristicsImpl::NotASink
|
||||
IsMaDTaintStepCharacteristic() { this = "taint step" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
e.getExtensibleType() = "sinkModel" and
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(e.asNode(), _, _)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(e.asNode(), _, _)
|
||||
@@ -475,18 +543,20 @@ private class LocalCall extends CharacteristicsImpl::UninterestingToModelCharact
|
||||
LocalCall() { this = "local call" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
ApplicationModeGetCallable::getCallable(e).fromSource()
|
||||
e.(CallArgument).getCallable().fromSource()
|
||||
or
|
||||
e.(MethodReturnValue).getCallable().fromSource()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Characteristic that marks endpoints as uninteresting to model, according to the Java ModelExclusions module.
|
||||
* A characteristic that marks endpoints as uninteresting to model, according to the Java ModelExclusions module.
|
||||
*/
|
||||
private class ExcludedFromModeling extends CharacteristicsImpl::UninterestingToModelCharacteristic {
|
||||
ExcludedFromModeling() { this = "excluded from modeling" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
ModelExclusions::isUninterestingForModels(ApplicationModeGetCallable::getCallable(e))
|
||||
ModelExclusions::isUninterestingForModels(e.getCallable())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,8 +569,7 @@ private class NonPublicMethodCharacteristic extends CharacteristicsImpl::Uninter
|
||||
NonPublicMethodCharacteristic() { this = "non-public method" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
e.getExtensibleType() = "sinkModel" and
|
||||
not ApplicationModeGetCallable::getCallable(e).isPublic()
|
||||
exists(Callable c | c = e.getCallable() | not c.isPublic())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -522,11 +591,10 @@ private class OtherArgumentToModeledMethodCharacteristic extends Characteristics
|
||||
}
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
e.getExtensibleType() = "sinkModel" and
|
||||
not ApplicationCandidatesImpl::isSink(e, _, _) and
|
||||
exists(Endpoint otherSink |
|
||||
exists(CallArgument otherSink |
|
||||
ApplicationCandidatesImpl::isSink(otherSink, _, "manual") and
|
||||
e.getCall() = otherSink.getCall() and
|
||||
e.(CallArgument).getCall() = otherSink.getCall() and
|
||||
e != otherSink
|
||||
)
|
||||
}
|
||||
@@ -540,10 +608,7 @@ private class OtherArgumentToModeledMethodCharacteristic extends Characteristics
|
||||
private class FunctionValueCharacteristic extends CharacteristicsImpl::LikelyNotASinkCharacteristic {
|
||||
FunctionValueCharacteristic() { this = "function value" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
e.getExtensibleType() = "sinkModel" and
|
||||
e.asNode().asExpr() instanceof FunctionalExpr
|
||||
}
|
||||
override predicate appliesToEndpoint(Endpoint e) { e.asNode().asExpr() instanceof FunctionalExpr }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -557,10 +622,7 @@ private class CannotBeTaintedCharacteristic extends CharacteristicsImpl::LikelyN
|
||||
{
|
||||
CannotBeTaintedCharacteristic() { this = "cannot be tainted" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
e.getExtensibleType() = "sinkModel" and
|
||||
not this.isKnownOutNodeForStep(e)
|
||||
}
|
||||
override predicate appliesToEndpoint(Endpoint e) { not this.isKnownOutNodeForStep(e) }
|
||||
|
||||
/**
|
||||
* Holds if the node `n` is known as the predecessor in a modeled flow step.
|
||||
|
||||
@@ -25,20 +25,20 @@ private import AutomodelJavaUtil
|
||||
bindingset[limit]
|
||||
private Endpoint getSampleForSignature(
|
||||
int limit, string package, string type, string subtypes, string name, string signature,
|
||||
string input, string output, string isVarargs, string extensibleType
|
||||
string input, string output, string isVarargs, string extensibleType, string alreadyAiModeled
|
||||
) {
|
||||
exists(int n, int num_endpoints, ApplicationModeMetadataExtractor meta |
|
||||
num_endpoints =
|
||||
count(Endpoint e |
|
||||
e.getExtensibleType() = extensibleType and
|
||||
meta.hasMetadata(e, package, type, subtypes, name, signature, input, output, isVarargs)
|
||||
meta.hasMetadata(e, package, type, subtypes, name, signature, input, output, isVarargs,
|
||||
alreadyAiModeled, extensibleType)
|
||||
)
|
||||
|
|
||||
result =
|
||||
rank[n](Endpoint e, Location loc |
|
||||
loc = e.asTop().getLocation() and
|
||||
e.getExtensibleType() = extensibleType and
|
||||
meta.hasMetadata(e, package, type, subtypes, name, signature, input, output, isVarargs)
|
||||
meta.hasMetadata(e, package, type, subtypes, name, signature, input, output, isVarargs,
|
||||
alreadyAiModeled, extensibleType)
|
||||
|
|
||||
e
|
||||
order by
|
||||
@@ -55,31 +55,15 @@ private Endpoint getSampleForSignature(
|
||||
}
|
||||
|
||||
from
|
||||
Endpoint endpoint, ApplicationModeMetadataExtractor meta, DollarAtString package,
|
||||
DollarAtString type, DollarAtString subtypes, DollarAtString name, DollarAtString signature,
|
||||
DollarAtString input, DollarAtString output, DollarAtString isVarargsArray,
|
||||
DollarAtString alreadyAiModeled, DollarAtString extensibleType
|
||||
Endpoint endpoint, DollarAtString package, DollarAtString type, DollarAtString subtypes,
|
||||
DollarAtString name, DollarAtString signature, DollarAtString input, DollarAtString output,
|
||||
DollarAtString isVarargsArray, DollarAtString alreadyAiModeled, DollarAtString extensibleType
|
||||
where
|
||||
not exists(CharacteristicsImpl::UninterestingToModelCharacteristic u |
|
||||
u.appliesToEndpoint(endpoint)
|
||||
) and
|
||||
CharacteristicsImpl::isSinkCandidate(endpoint, _) and
|
||||
isCandidate(endpoint, package, type, subtypes, name, signature, input, output, isVarargsArray,
|
||||
extensibleType, alreadyAiModeled) and
|
||||
endpoint =
|
||||
getSampleForSignature(9, package, type, subtypes, name, signature, input, output,
|
||||
isVarargsArray, extensibleType) and
|
||||
// If a node is already modeled in MaD, we don't include it as a candidate. Otherwise, we might include it as a
|
||||
// candidate for query A, but the model will label it as a sink for one of the sink types of query B, for which it's
|
||||
// already a known sink. This would result in overlap between our detected sinks and the pre-existing modeling. We
|
||||
// assume that, if a sink has already been modeled in a MaD model, then it doesn't belong to any additional sink
|
||||
// types, and we don't need to reexamine it.
|
||||
(
|
||||
not CharacteristicsImpl::isModeled(endpoint, _, _, _) and alreadyAiModeled = ""
|
||||
or
|
||||
alreadyAiModeled.matches("%ai-%") and
|
||||
CharacteristicsImpl::isModeled(endpoint, _, _, alreadyAiModeled)
|
||||
) and
|
||||
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, isVarargsArray) and
|
||||
includeAutomodelCandidate(package, type, name, signature)
|
||||
isVarargsArray, extensibleType, alreadyAiModeled)
|
||||
select endpoint.asNode(),
|
||||
"Related locations: $@, $@, $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@.", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
|
||||
|
||||
@@ -42,25 +42,13 @@ Endpoint getSampleForCharacteristic(EndpointCharacteristic c, int limit) {
|
||||
|
||||
from
|
||||
Endpoint endpoint, EndpointCharacteristic characteristic, float confidence, string message,
|
||||
ApplicationModeMetadataExtractor meta, DollarAtString package, DollarAtString type,
|
||||
DollarAtString subtypes, DollarAtString name, DollarAtString signature, DollarAtString input,
|
||||
DollarAtString output, DollarAtString isVarargsArray, DollarAtString extensibleType
|
||||
DollarAtString package, DollarAtString type, DollarAtString subtypes, DollarAtString name,
|
||||
DollarAtString signature, DollarAtString input, DollarAtString output,
|
||||
DollarAtString isVarargsArray, DollarAtString extensibleType
|
||||
where
|
||||
endpoint = getSampleForCharacteristic(characteristic, 100) and
|
||||
extensibleType = endpoint.getExtensibleType() and
|
||||
confidence >= SharedCharacteristics::highConfidence() and
|
||||
characteristic.hasImplications(any(NegativeSinkType negative), true, confidence) and
|
||||
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, isVarargsArray) and
|
||||
// It's valid for a node to satisfy the logic for both `isSink` and `isSanitizer`, but in that case it will be
|
||||
// treated by the actual query as a sanitizer, since the final logic is something like
|
||||
// `isSink(n) and not isSanitizer(n)`. We don't want to include such nodes as negative examples in the prompt, because
|
||||
// they're ambiguous and might confuse the model, so we explicitly exclude all known sinks from the negative examples.
|
||||
not exists(EndpointCharacteristic characteristic2, float confidence2, SinkType positiveType |
|
||||
not positiveType instanceof NegativeSinkType and
|
||||
characteristic2.appliesToEndpoint(endpoint) and
|
||||
confidence2 >= SharedCharacteristics::maximalConfidence() and
|
||||
characteristic2.hasImplications(positiveType, true, confidence2)
|
||||
) and
|
||||
isNegativeExample(endpoint, characteristic, confidence, package, type, subtypes, name, signature,
|
||||
input, output, isVarargsArray, extensibleType) and
|
||||
message = characteristic
|
||||
select endpoint.asNode(),
|
||||
message + "\nrelated locations: $@, $@, $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@.", //
|
||||
|
||||
@@ -18,11 +18,8 @@ from
|
||||
DollarAtString signature, DollarAtString input, DollarAtString output,
|
||||
DollarAtString isVarargsArray, DollarAtString extensibleType
|
||||
where
|
||||
extensibleType = endpoint.getExtensibleType() and
|
||||
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, isVarargsArray) and
|
||||
// Extract positive examples of sinks belonging to the existing ATM query configurations.
|
||||
CharacteristicsImpl::isKnownAs(endpoint, endpointType, _) and
|
||||
exists(CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()))
|
||||
isPositiveExample(endpoint, endpointType, package, type, subtypes, name, signature, input, output,
|
||||
isVarargsArray, extensibleType)
|
||||
select endpoint.asNode(),
|
||||
endpointType + "\nrelated locations: $@, $@, $@." +
|
||||
"\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@.", //
|
||||
|
||||
@@ -30,11 +30,6 @@ abstract class SinkType extends EndpointType {
|
||||
SinkType() { any() }
|
||||
}
|
||||
|
||||
/** The `Negative` class for non-sinks. */
|
||||
class NegativeSinkType extends SinkType {
|
||||
NegativeSinkType() { this = "non-sink" }
|
||||
}
|
||||
|
||||
/** A sink relevant to the SQL injection query */
|
||||
class SqlInjectionSinkType extends SinkType {
|
||||
SqlInjectionSinkType() { this = "sql-injection" }
|
||||
|
||||
@@ -15,7 +15,6 @@ private import semmle.code.java.security.QueryInjection
|
||||
private import semmle.code.java.security.RequestForgery
|
||||
private import semmle.code.java.dataflow.internal.ModelExclusions as ModelExclusions
|
||||
private import AutomodelJavaUtil as AutomodelJavaUtil
|
||||
private import AutomodelSharedGetCallable as AutomodelSharedGetCallable
|
||||
import AutomodelSharedCharacteristics as SharedCharacteristics
|
||||
import AutomodelEndpointTypes as AutomodelEndpointTypes
|
||||
|
||||
@@ -25,32 +24,29 @@ newtype JavaRelatedLocationType =
|
||||
|
||||
newtype TFrameworkModeEndpoint =
|
||||
TExplicitParameter(Parameter p) {
|
||||
not p.getType() instanceof PrimitiveType and
|
||||
not p.getType() instanceof BoxedType and
|
||||
not p.getType() instanceof NumberType
|
||||
AutomodelJavaUtil::isFromSource(p) and
|
||||
not AutomodelJavaUtil::isUnexploitableType(p.getType())
|
||||
} or
|
||||
TQualifier(Callable c) { not c instanceof Constructor } or
|
||||
TQualifier(Callable c) { AutomodelJavaUtil::isFromSource(c) and not c instanceof Constructor } or
|
||||
TReturnValue(Callable c) {
|
||||
AutomodelJavaUtil::isFromSource(c) and
|
||||
c instanceof Constructor
|
||||
or
|
||||
AutomodelJavaUtil::isFromSource(c) and
|
||||
c instanceof Method and
|
||||
(
|
||||
not c.getReturnType() instanceof VoidType and
|
||||
not c.getReturnType() instanceof PrimitiveType
|
||||
)
|
||||
not AutomodelJavaUtil::isUnexploitableType(c.getReturnType())
|
||||
} or
|
||||
TOverridableParameter(Method m, Parameter p) {
|
||||
AutomodelJavaUtil::isFromSource(p) and
|
||||
not AutomodelJavaUtil::isUnexploitableType(p.getType()) and
|
||||
p.getCallable() = m and
|
||||
m instanceof ModelExclusions::ModelApi and
|
||||
not m.getDeclaringType().isFinal() and
|
||||
not m.isFinal() and
|
||||
not m.isStatic()
|
||||
AutomodelJavaUtil::isOverridable(m)
|
||||
} or
|
||||
TOverridableQualifier(Method m) {
|
||||
AutomodelJavaUtil::isFromSource(m) and
|
||||
m instanceof ModelExclusions::ModelApi and
|
||||
not m.getDeclaringType().isFinal() and
|
||||
not m.isFinal() and
|
||||
not m.isStatic()
|
||||
AutomodelJavaUtil::isOverridable(m)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,7 +75,7 @@ abstract class FrameworkModeEndpoint extends TFrameworkModeEndpoint {
|
||||
/**
|
||||
* Returns the callable that contains the endpoint.
|
||||
*/
|
||||
abstract Callable getEnclosingCallable();
|
||||
abstract Callable getCallable();
|
||||
|
||||
abstract Top asTop();
|
||||
|
||||
@@ -101,7 +97,7 @@ class ExplicitParameterEndpoint extends FrameworkModeEndpoint, TExplicitParamete
|
||||
|
||||
override string getParamName() { result = param.getName() }
|
||||
|
||||
override Callable getEnclosingCallable() { result = param.getCallable() }
|
||||
override Callable getCallable() { result = param.getCallable() }
|
||||
|
||||
override Top asTop() { result = param }
|
||||
|
||||
@@ -121,7 +117,7 @@ class QualifierEndpoint extends FrameworkModeEndpoint, TQualifier {
|
||||
|
||||
override string getParamName() { result = "this" }
|
||||
|
||||
override Callable getEnclosingCallable() { result = callable }
|
||||
override Callable getCallable() { result = callable }
|
||||
|
||||
override Top asTop() { result = callable }
|
||||
|
||||
@@ -139,7 +135,7 @@ class ReturnValue extends FrameworkModeEndpoint, TReturnValue {
|
||||
|
||||
override string getParamName() { none() }
|
||||
|
||||
override Callable getEnclosingCallable() { result = callable }
|
||||
override Callable getCallable() { result = callable }
|
||||
|
||||
override Top asTop() { result = callable }
|
||||
|
||||
@@ -158,7 +154,7 @@ class OverridableParameter extends FrameworkModeEndpoint, TOverridableParameter
|
||||
|
||||
override string getParamName() { result = param.getName() }
|
||||
|
||||
override Callable getEnclosingCallable() { result = method }
|
||||
override Callable getCallable() { result = method }
|
||||
|
||||
override Top asTop() { result = param }
|
||||
|
||||
@@ -176,7 +172,7 @@ class OverridableQualifier extends FrameworkModeEndpoint, TOverridableQualifier
|
||||
|
||||
override string getParamName() { result = "this" }
|
||||
|
||||
override Callable getEnclosingCallable() { result = m }
|
||||
override Callable getCallable() { result = m }
|
||||
|
||||
override Top asTop() { result = m }
|
||||
|
||||
@@ -197,7 +193,9 @@ module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig {
|
||||
|
||||
class EndpointType = AutomodelEndpointTypes::EndpointType;
|
||||
|
||||
class NegativeEndpointType = AutomodelEndpointTypes::NegativeSinkType;
|
||||
class SinkType = AutomodelEndpointTypes::SinkType;
|
||||
|
||||
class SourceType = AutomodelEndpointTypes::SourceType;
|
||||
|
||||
class RelatedLocation = Location::Top;
|
||||
|
||||
@@ -239,8 +237,8 @@ module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig {
|
||||
additional predicate sinkSpec(
|
||||
Endpoint e, string package, string type, string name, string signature, string ext, string input
|
||||
) {
|
||||
e.getEnclosingCallable().hasQualifiedName(package, type, name) and
|
||||
signature = ExternalFlow::paramsString(e.getEnclosingCallable()) and
|
||||
e.getCallable().hasQualifiedName(package, type, name) and
|
||||
signature = ExternalFlow::paramsString(e.getCallable()) and
|
||||
ext = "" and
|
||||
input = e.getMaDInput()
|
||||
}
|
||||
@@ -249,8 +247,8 @@ module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig {
|
||||
Endpoint e, string package, string type, string name, string signature, string ext,
|
||||
string output
|
||||
) {
|
||||
e.getEnclosingCallable().hasQualifiedName(package, type, name) and
|
||||
signature = ExternalFlow::paramsString(e.getEnclosingCallable()) and
|
||||
e.getCallable().hasQualifiedName(package, type, name) and
|
||||
signature = ExternalFlow::paramsString(e.getCallable()) and
|
||||
ext = "" and
|
||||
output = e.getMaDOutput()
|
||||
}
|
||||
@@ -262,10 +260,10 @@ module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig {
|
||||
*/
|
||||
RelatedLocation getRelatedLocation(Endpoint e, RelatedLocationType type) {
|
||||
type = MethodDoc() and
|
||||
result = e.getEnclosingCallable().(Documentable).getJavadoc()
|
||||
result = e.getCallable().(Documentable).getJavadoc()
|
||||
or
|
||||
type = ClassDoc() and
|
||||
result = e.getEnclosingCallable().getDeclaringType().(Documentable).getJavadoc()
|
||||
result = e.getCallable().getDeclaringType().(Documentable).getJavadoc()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,25 +285,116 @@ class FrameworkModeMetadataExtractor extends string {
|
||||
|
||||
predicate hasMetadata(
|
||||
Endpoint e, string package, string type, string subtypes, string name, string signature,
|
||||
string input, string output, string parameterName
|
||||
string input, string output, string parameterName, string alreadyAiModeled,
|
||||
string extensibleType
|
||||
) {
|
||||
(if exists(e.getParamName()) then parameterName = e.getParamName() else parameterName = "") and
|
||||
name = e.getEnclosingCallable().getName() and
|
||||
(if exists(e.getMaDInput()) then input = e.getMaDInput() else input = "") and
|
||||
(if exists(e.getMaDOutput()) then output = e.getMaDOutput() else output = "") and
|
||||
package = e.getEnclosingCallable().getDeclaringType().getPackage().getName() and
|
||||
type = e.getEnclosingCallable().getDeclaringType().getErasure().(RefType).nestedName() and
|
||||
subtypes = AutomodelJavaUtil::considerSubtypes(e.getEnclosingCallable()).toString() and
|
||||
signature = ExternalFlow::paramsString(e.getEnclosingCallable())
|
||||
exists(Callable callable | e.getCallable() = callable |
|
||||
(if exists(e.getMaDInput()) then input = e.getMaDInput() else input = "") and
|
||||
(if exists(e.getMaDOutput()) then output = e.getMaDOutput() else output = "") and
|
||||
package = callable.getDeclaringType().getPackage().getName() and
|
||||
// we're using the erased types because the MaD convention is to not specify type parameters.
|
||||
// Whether something is or isn't a sink doesn't usually depend on the type parameters.
|
||||
type = callable.getDeclaringType().getErasure().(RefType).nestedName() and
|
||||
subtypes = AutomodelJavaUtil::considerSubtypes(callable).toString() and
|
||||
name = callable.getName() and
|
||||
signature = ExternalFlow::paramsString(callable) and
|
||||
(if exists(e.getParamName()) then parameterName = e.getParamName() else parameterName = "") and
|
||||
e.getExtensibleType() = extensibleType
|
||||
) and
|
||||
(
|
||||
not CharacteristicsImpl::isModeled(e, _, extensibleType, _) and alreadyAiModeled = ""
|
||||
or
|
||||
CharacteristicsImpl::isModeled(e, _, extensibleType, alreadyAiModeled)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given `endpoint` should be considered a candidate for the `extensibleType`.
|
||||
*
|
||||
* The other parameters record various other properties of interest.
|
||||
*/
|
||||
predicate isCandidate(
|
||||
Endpoint endpoint, string package, string type, string subtypes, string name, string signature,
|
||||
string input, string output, string parameterName, string extensibleType, string alreadyAiModeled
|
||||
) {
|
||||
CharacteristicsImpl::isCandidate(endpoint, _) and
|
||||
not exists(CharacteristicsImpl::UninterestingToModelCharacteristic u |
|
||||
u.appliesToEndpoint(endpoint)
|
||||
) and
|
||||
any(FrameworkModeMetadataExtractor meta)
|
||||
.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, parameterName,
|
||||
alreadyAiModeled, extensibleType) and
|
||||
// If a node is already modeled in MaD, we don't include it as a candidate. Otherwise, we might include it as a
|
||||
// candidate for query A, but the model will label it as a sink for one of the sink types of query B, for which it's
|
||||
// already a known sink. This would result in overlap between our detected sinks and the pre-existing modeling. We
|
||||
// assume that, if a sink has already been modeled in a MaD model, then it doesn't belong to any additional sink
|
||||
// types, and we don't need to reexamine it.
|
||||
alreadyAiModeled.matches(["", "%ai-%"]) and
|
||||
AutomodelJavaUtil::includeAutomodelCandidate(package, type, name, signature)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given `endpoint` is a negative example for the `extensibleType`
|
||||
* because of the `characteristic`.
|
||||
*
|
||||
* The other parameters record various other properties of interest.
|
||||
*/
|
||||
predicate isNegativeExample(
|
||||
Endpoint endpoint, EndpointCharacteristic characteristic, float confidence, string package,
|
||||
string type, string subtypes, string name, string signature, string input, string output,
|
||||
string parameterName, string extensibleType
|
||||
) {
|
||||
characteristic.appliesToEndpoint(endpoint) and
|
||||
// the node is known not to be an endpoint of any appropriate type
|
||||
forall(AutomodelEndpointTypes::EndpointType tp |
|
||||
tp = CharacteristicsImpl::getAPotentialType(endpoint)
|
||||
|
|
||||
characteristic.hasImplications(tp, false, _)
|
||||
) and
|
||||
// the lowest confidence across all endpoint types should be at least highConfidence
|
||||
confidence =
|
||||
min(float c |
|
||||
characteristic.hasImplications(CharacteristicsImpl::getAPotentialType(endpoint), false, c)
|
||||
) and
|
||||
confidence >= SharedCharacteristics::highConfidence() and
|
||||
any(FrameworkModeMetadataExtractor meta)
|
||||
.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, parameterName,
|
||||
_, extensibleType) and
|
||||
// It's valid for a node to be both a potential source/sanitizer and a sink. We don't want to include such nodes
|
||||
// as negative examples in the prompt, because they're ambiguous and might confuse the model, so we explicitly exclude them here.
|
||||
not exists(EndpointCharacteristic characteristic2, float confidence2 |
|
||||
characteristic2 != characteristic
|
||||
|
|
||||
characteristic2.appliesToEndpoint(endpoint) and
|
||||
confidence2 >= SharedCharacteristics::maximalConfidence() and
|
||||
characteristic2
|
||||
.hasImplications(CharacteristicsImpl::getAPotentialType(endpoint), true, confidence2)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given `endpoint` is a positive example for the `endpointType`.
|
||||
*
|
||||
* The other parameters record various other properties of interest.
|
||||
*/
|
||||
predicate isPositiveExample(
|
||||
Endpoint endpoint, string endpointType, string package, string type, string subtypes, string name,
|
||||
string signature, string input, string output, string parameterName, string extensibleType
|
||||
) {
|
||||
any(FrameworkModeMetadataExtractor meta)
|
||||
.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, parameterName,
|
||||
_, extensibleType) and
|
||||
CharacteristicsImpl::isKnownAs(endpoint, endpointType, _)
|
||||
}
|
||||
|
||||
/*
|
||||
* EndpointCharacteristic classes that are specific to Automodel for Java.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A negative characteristic that indicates that an is-style boolean method is unexploitable even if it is a sink.
|
||||
* A negative characteristic that indicates that parameters of an is-style boolean method should not be considered sinks,
|
||||
* and its return value should not be considered a source.
|
||||
*
|
||||
* A sink is highly unlikely to be exploitable if its callable's name starts with `is` and the callable has a boolean return
|
||||
* type (e.g. `isDirectory`). These kinds of calls normally do only checks, and appear before the proper call that does
|
||||
@@ -313,45 +402,70 @@ class FrameworkModeMetadataExtractor extends string {
|
||||
*
|
||||
* TODO: this might filter too much, it's possible that methods with more than one parameter contain interesting sinks
|
||||
*/
|
||||
private class UnexploitableIsCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic {
|
||||
private class UnexploitableIsCharacteristic extends CharacteristicsImpl::NeitherSourceNorSinkCharacteristic
|
||||
{
|
||||
UnexploitableIsCharacteristic() { this = "unexploitable (is-style boolean method)" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
not FrameworkCandidatesImpl::isSink(e, _, _) and
|
||||
e.getEnclosingCallable().getName().matches("is%") and
|
||||
e.getEnclosingCallable().getReturnType() instanceof BooleanType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A negative characteristic that indicates that an existence-checking boolean method is unexploitable even if it is a
|
||||
* sink.
|
||||
*
|
||||
* A sink is highly unlikely to be exploitable if its callable's name is `exists` or `notExists` and the callable has a
|
||||
* boolean return type. These kinds of calls normally do only checks, and appear before the proper call that does the
|
||||
* dangerous/interesting thing, so we want the latter to be modeled as the sink.
|
||||
*/
|
||||
private class UnexploitableExistsCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic {
|
||||
UnexploitableExistsCharacteristic() { this = "unexploitable (existence-checking boolean method)" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
not FrameworkCandidatesImpl::isSink(e, _, _) and
|
||||
exists(Callable callable |
|
||||
callable = e.getEnclosingCallable() and
|
||||
callable.getName().toLowerCase() = ["exists", "notexists"] and
|
||||
callable.getReturnType() instanceof BooleanType
|
||||
e.getCallable().getName().matches("is%") and
|
||||
e.getCallable().getReturnType() instanceof BooleanType and
|
||||
(
|
||||
e.getExtensibleType() = "sinkModel" and
|
||||
not FrameworkCandidatesImpl::isSink(e, _, _)
|
||||
or
|
||||
e.getExtensibleType() = "sourceModel" and
|
||||
not FrameworkCandidatesImpl::isSource(e, _, _) and
|
||||
e.getMaDOutput() = "ReturnValue"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A negative characteristic that indicates that an endpoint is an argument to an exception, which is not a sink.
|
||||
* A negative characteristic that indicates that parameters of an existence-checking boolean method should not be
|
||||
* considered sinks, and its return value should not be considered a source.
|
||||
*
|
||||
* A sink is highly unlikely to be exploitable if its callable's name is `exists` or `notExists` and the callable has a
|
||||
* boolean return type. These kinds of calls normally do only checks, and appear before the proper call that does the
|
||||
* dangerous/interesting thing, so we want the latter to be modeled as the sink.
|
||||
*/
|
||||
private class ExceptionCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic {
|
||||
private class UnexploitableExistsCharacteristic extends CharacteristicsImpl::NeitherSourceNorSinkCharacteristic
|
||||
{
|
||||
UnexploitableExistsCharacteristic() { this = "unexploitable (existence-checking boolean method)" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
exists(Callable callable |
|
||||
callable = e.getCallable() and
|
||||
callable.getName().toLowerCase() = ["exists", "notexists"] and
|
||||
callable.getReturnType() instanceof BooleanType
|
||||
|
|
||||
e.getExtensibleType() = "sinkModel" and
|
||||
not FrameworkCandidatesImpl::isSink(e, _, _)
|
||||
or
|
||||
e.getExtensibleType() = "sourceModel" and
|
||||
not FrameworkCandidatesImpl::isSource(e, _, _) and
|
||||
e.getMaDOutput() = "ReturnValue"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A negative characteristic that indicates that parameters of an exception method or constructor should not be considered sinks,
|
||||
* and its return value should not be considered a source.
|
||||
*/
|
||||
private class ExceptionCharacteristic extends CharacteristicsImpl::NeitherSourceNorSinkCharacteristic
|
||||
{
|
||||
ExceptionCharacteristic() { this = "exception" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
e.getEnclosingCallable().getDeclaringType().getASupertype*() instanceof TypeThrowable
|
||||
e.getCallable().getDeclaringType().getASupertype*() instanceof TypeThrowable and
|
||||
(
|
||||
e.getExtensibleType() = "sinkModel" and
|
||||
not FrameworkCandidatesImpl::isSink(e, _, _)
|
||||
or
|
||||
e.getExtensibleType() = "sourceModel" and
|
||||
not FrameworkCandidatesImpl::isSource(e, _, _) and
|
||||
e.getMaDOutput() = "ReturnValue"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,6 +477,6 @@ private class NotAModelApi extends CharacteristicsImpl::UninterestingToModelChar
|
||||
NotAModelApi() { this = "not a model API" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
not e.getEnclosingCallable() instanceof ModelExclusions::ModelApi
|
||||
not e.getCallable() instanceof ModelExclusions::ModelApi
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,29 +16,12 @@ private import AutomodelFrameworkModeCharacteristics
|
||||
private import AutomodelJavaUtil
|
||||
|
||||
from
|
||||
Endpoint endpoint, FrameworkModeMetadataExtractor meta, DollarAtString package,
|
||||
DollarAtString type, DollarAtString subtypes, DollarAtString name, DollarAtString signature,
|
||||
DollarAtString input, DollarAtString output, DollarAtString parameterName,
|
||||
DollarAtString alreadyAiModeled, DollarAtString extensibleType
|
||||
Endpoint endpoint, DollarAtString package, DollarAtString type, DollarAtString subtypes,
|
||||
DollarAtString name, DollarAtString signature, DollarAtString input, DollarAtString output,
|
||||
DollarAtString parameterName, DollarAtString alreadyAiModeled, DollarAtString extensibleType
|
||||
where
|
||||
endpoint.getExtensibleType() = extensibleType and
|
||||
not exists(CharacteristicsImpl::UninterestingToModelCharacteristic u |
|
||||
u.appliesToEndpoint(endpoint)
|
||||
) and
|
||||
CharacteristicsImpl::isSinkCandidate(endpoint, _) and
|
||||
// If a node is already a known sink for any of our existing ATM queries and is already modeled as a MaD sink, we
|
||||
// don't include it as a candidate. Otherwise, we might include it as a candidate for query A, but the model will
|
||||
// label it as a sink for one of the sink types of query B, for which it's already a known sink. This would result in
|
||||
// overlap between our detected sinks and the pre-existing modeling. We assume that, if a sink has already been
|
||||
// modeled in a MaD model, then it doesn't belong to any additional sink types, and we don't need to reexamine it.
|
||||
(
|
||||
not CharacteristicsImpl::isSink(endpoint, _, _) and alreadyAiModeled = ""
|
||||
or
|
||||
alreadyAiModeled.matches("%ai-%") and
|
||||
CharacteristicsImpl::isSink(endpoint, _, alreadyAiModeled)
|
||||
) and
|
||||
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, parameterName) and
|
||||
includeAutomodelCandidate(package, type, name, signature)
|
||||
isCandidate(endpoint, package, type, subtypes, name, signature, input, output, parameterName,
|
||||
extensibleType, alreadyAiModeled)
|
||||
select endpoint,
|
||||
"Related locations: $@, $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@.", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, MethodDoc()), "MethodDoc", //
|
||||
|
||||
@@ -14,29 +14,15 @@ private import AutomodelJavaUtil
|
||||
|
||||
from
|
||||
Endpoint endpoint, EndpointCharacteristic characteristic, float confidence,
|
||||
DollarAtString message, FrameworkModeMetadataExtractor meta, DollarAtString package,
|
||||
DollarAtString type, DollarAtString subtypes, DollarAtString name, DollarAtString signature,
|
||||
DollarAtString input, DollarAtString output, DollarAtString parameterName,
|
||||
DollarAtString extensibleType
|
||||
DollarAtString package, DollarAtString type, DollarAtString subtypes, DollarAtString name,
|
||||
DollarAtString signature, DollarAtString input, DollarAtString output,
|
||||
DollarAtString parameterName, DollarAtString extensibleType
|
||||
where
|
||||
endpoint.getExtensibleType() = extensibleType and
|
||||
characteristic.appliesToEndpoint(endpoint) and
|
||||
confidence >= SharedCharacteristics::highConfidence() and
|
||||
characteristic.hasImplications(any(NegativeSinkType negative), true, confidence) and
|
||||
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, parameterName) and
|
||||
// It's valid for a node to satisfy the logic for both `isSink` and `isSanitizer`, but in that case it will be
|
||||
// treated by the actual query as a sanitizer, since the final logic is something like
|
||||
// `isSink(n) and not isSanitizer(n)`. We don't want to include such nodes as negative examples in the prompt, because
|
||||
// they're ambiguous and might confuse the model, so we explicitly exclude all known sinks from the negative examples.
|
||||
not exists(EndpointCharacteristic characteristic2, float confidence2, SinkType positiveType |
|
||||
not positiveType instanceof NegativeSinkType and
|
||||
characteristic2.appliesToEndpoint(endpoint) and
|
||||
confidence2 >= SharedCharacteristics::maximalConfidence() and
|
||||
characteristic2.hasImplications(positiveType, true, confidence2)
|
||||
) and
|
||||
message = characteristic
|
||||
isNegativeExample(endpoint, characteristic, confidence, package, type, subtypes, name, signature,
|
||||
input, output, parameterName, extensibleType)
|
||||
select endpoint,
|
||||
message + "\nrelated locations: $@, $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@.", //
|
||||
characteristic + "\nrelated locations: $@, $@." +
|
||||
"\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@.", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, MethodDoc()), "MethodDoc", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, ClassDoc()), "ClassDoc", //
|
||||
package, "package", //
|
||||
|
||||
@@ -13,15 +13,12 @@ private import AutomodelEndpointTypes
|
||||
private import AutomodelJavaUtil
|
||||
|
||||
from
|
||||
Endpoint endpoint, EndpointType endpointType, FrameworkModeMetadataExtractor meta,
|
||||
DollarAtString package, DollarAtString type, DollarAtString subtypes, DollarAtString name,
|
||||
DollarAtString signature, DollarAtString input, DollarAtString output,
|
||||
DollarAtString parameterName, DollarAtString extensibleType
|
||||
Endpoint endpoint, EndpointType endpointType, DollarAtString package, DollarAtString type,
|
||||
DollarAtString subtypes, DollarAtString name, DollarAtString signature, DollarAtString input,
|
||||
DollarAtString output, DollarAtString parameterName, DollarAtString extensibleType
|
||||
where
|
||||
endpoint.getExtensibleType() = extensibleType and
|
||||
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, parameterName) and
|
||||
// Extract positive examples of sinks belonging to the existing ATM query configurations.
|
||||
CharacteristicsImpl::isKnownAs(endpoint, endpointType, _)
|
||||
isPositiveExample(endpoint, endpointType, package, type, subtypes, name, signature, input, output,
|
||||
parameterName, extensibleType)
|
||||
select endpoint,
|
||||
endpointType + "\nrelated locations: $@, $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@.", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, MethodDoc()), "MethodDoc", //
|
||||
|
||||
@@ -82,3 +82,43 @@ predicate includeAutomodelCandidate(string package, string type, string name, st
|
||||
not automodelCandidateFilter(_, _, _, _) or
|
||||
automodelCandidateFilter(package, type, name, signature)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given program element corresponds to a piece of source code,
|
||||
* that is, it is not compiler-generated.
|
||||
*
|
||||
* Note: This is a stricter check than `Element::fromSource`, which simply
|
||||
* checks whether the element is in a source file as opposed to a JAR file.
|
||||
* There can be compiler-generated elements in source files (especially for
|
||||
* Kotlin), which we also want to exclude.
|
||||
*/
|
||||
predicate isFromSource(Element e) {
|
||||
// from a source file (not a JAR)
|
||||
e.fromSource() and
|
||||
// not explicitly marked as compiler-generated
|
||||
not e.isCompilerGenerated() and
|
||||
// does not have a dummy location
|
||||
not e.hasLocationInfo(_, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint cannot flow through the given type (because it is a numeric
|
||||
* type or some other type with a fixed set of values).
|
||||
*/
|
||||
predicate isUnexploitableType(Type tp) {
|
||||
tp instanceof PrimitiveType or
|
||||
tp instanceof BoxedType or
|
||||
tp instanceof NumberType or
|
||||
tp instanceof VoidType
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given method can be overridden, that is, it is not final,
|
||||
* static, or private.
|
||||
*/
|
||||
predicate isOverridable(Method m) {
|
||||
not m.getDeclaringType().isFinal() and
|
||||
not m.isFinal() and
|
||||
not m.isStatic() and
|
||||
not m.isPrivate()
|
||||
}
|
||||
|
||||
@@ -16,7 +16,17 @@ signature module CandidateSig {
|
||||
* An endpoint is a potential candidate for modeling. This will typically be bound to the language's
|
||||
* DataFlow node class, or a subtype thereof.
|
||||
*/
|
||||
class Endpoint;
|
||||
class Endpoint {
|
||||
/**
|
||||
* Gets the kind of this endpoint, either "sourceModel" or "sinkModel".
|
||||
*/
|
||||
string getExtensibleType();
|
||||
|
||||
/**
|
||||
* Gets a string representation of this endpoint.
|
||||
*/
|
||||
string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A related location for an endpoint. This will typically be bound to the supertype of all AST nodes (eg., `Top`).
|
||||
@@ -31,14 +41,19 @@ signature module CandidateSig {
|
||||
class RelatedLocationType;
|
||||
|
||||
/**
|
||||
* A class kind for an endpoint.
|
||||
* An endpoint type considered by this specification.
|
||||
*/
|
||||
class EndpointType extends string;
|
||||
|
||||
/**
|
||||
* An EndpointType that denotes the absence of any sink.
|
||||
* A sink endpoint type considered by this specification.
|
||||
*/
|
||||
class NegativeEndpointType extends EndpointType;
|
||||
class SinkType extends EndpointType;
|
||||
|
||||
/**
|
||||
* A source endpoint type considered by this specification.
|
||||
*/
|
||||
class SourceType extends EndpointType;
|
||||
|
||||
/**
|
||||
* Gets the endpoint as a location.
|
||||
@@ -103,7 +118,7 @@ module SharedCharacteristics<CandidateSig Candidate> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `endpoint` is modeled as `endpointType` (endpoint type must not be negative).
|
||||
* Holds if `endpoint` is modeled as `endpointType`.
|
||||
*/
|
||||
predicate isKnownAs(
|
||||
Candidate::Endpoint endpoint, Candidate::EndpointType endpointType,
|
||||
@@ -111,19 +126,31 @@ module SharedCharacteristics<CandidateSig Candidate> {
|
||||
) {
|
||||
// If the list of characteristics includes positive indicators with maximal confidence for this class, then it's a
|
||||
// known sink for the class.
|
||||
not endpointType instanceof Candidate::NegativeEndpointType and
|
||||
characteristic.appliesToEndpoint(endpoint) and
|
||||
characteristic.hasImplications(endpointType, true, maximalConfidence())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the candidate sink `candidateSink` should be considered as a possible sink of type `sinkType`, and
|
||||
* classified by the ML model. A candidate sink is a node that cannot be excluded from `sinkType` based on its
|
||||
* characteristics.
|
||||
* Gets a potential type of this endpoint to make sure that sources are
|
||||
* associated with source types and sinks with sink types.
|
||||
*/
|
||||
predicate isSinkCandidate(Candidate::Endpoint candidateSink, Candidate::EndpointType sinkType) {
|
||||
not sinkType instanceof Candidate::NegativeEndpointType and
|
||||
not exists(getAReasonSinkExcluded(candidateSink, sinkType))
|
||||
Candidate::EndpointType getAPotentialType(Candidate::Endpoint endpoint) {
|
||||
endpoint.getExtensibleType() = "sourceModel" and
|
||||
result instanceof Candidate::SourceType
|
||||
or
|
||||
endpoint.getExtensibleType() = "sinkModel" and
|
||||
result instanceof Candidate::SinkType
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given `endpoint` should be considered as a candidate for type `endpointType`,
|
||||
* and classified by the ML model.
|
||||
*
|
||||
* A candidate is an endpoint that cannot be excluded from `endpointType` based on its characteristics.
|
||||
*/
|
||||
predicate isCandidate(Candidate::Endpoint endpoint, Candidate::EndpointType endpointType) {
|
||||
endpointType = getAPotentialType(endpoint) and
|
||||
not exists(getAnExcludingCharacteristic(endpoint, endpointType))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,27 +166,16 @@ module SharedCharacteristics<CandidateSig Candidate> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of characteristics that cause `candidateSink` to be excluded as an effective sink for a given sink
|
||||
* type.
|
||||
* Gets a characteristics that disbar `endpoint` from being a candidate for `endpointType`
|
||||
* with at least medium confidence.
|
||||
*/
|
||||
EndpointCharacteristic getAReasonSinkExcluded(
|
||||
Candidate::Endpoint candidateSink, Candidate::EndpointType sinkType
|
||||
EndpointCharacteristic getAnExcludingCharacteristic(
|
||||
Candidate::Endpoint endpoint, Candidate::EndpointType endpointType
|
||||
) {
|
||||
// An endpoint is a sink candidate if none of its characteristics give much indication whether or not it is a sink.
|
||||
not sinkType instanceof Candidate::NegativeEndpointType and
|
||||
result.appliesToEndpoint(candidateSink) and
|
||||
(
|
||||
// Exclude endpoints that have a characteristic that implies they're not sinks for _any_ sink type.
|
||||
exists(float confidence |
|
||||
confidence >= mediumConfidence() and
|
||||
result.hasImplications(any(Candidate::NegativeEndpointType t), true, confidence)
|
||||
)
|
||||
or
|
||||
// Exclude endpoints that have a characteristic that implies they're not sinks for _this particular_ sink type.
|
||||
exists(float confidence |
|
||||
confidence >= mediumConfidence() and
|
||||
result.hasImplications(sinkType, false, confidence)
|
||||
)
|
||||
result.appliesToEndpoint(endpoint) and
|
||||
exists(float confidence |
|
||||
confidence >= mediumConfidence() and
|
||||
result.hasImplications(endpointType, false, confidence)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -250,12 +266,46 @@ module SharedCharacteristics<CandidateSig Candidate> {
|
||||
override predicate hasImplications(
|
||||
Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
endpointType instanceof Candidate::NegativeEndpointType and
|
||||
isPositiveIndicator = true and
|
||||
endpointType instanceof Candidate::SinkType and
|
||||
isPositiveIndicator = false and
|
||||
confidence = highConfidence()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A high-confidence characteristic that indicates that an endpoint is not a source of any type. These endpoints can be
|
||||
* used as negative samples for training or for a few-shot prompt.
|
||||
*/
|
||||
abstract class NotASourceCharacteristic extends EndpointCharacteristic {
|
||||
bindingset[this]
|
||||
NotASourceCharacteristic() { any() }
|
||||
|
||||
override predicate hasImplications(
|
||||
Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
endpointType instanceof Candidate::SourceType and
|
||||
isPositiveIndicator = false and
|
||||
confidence = highConfidence()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A high-confidence characteristic that indicates that an endpoint is neither a source nor a sink of any type.
|
||||
*/
|
||||
abstract class NeitherSourceNorSinkCharacteristic extends NotASinkCharacteristic,
|
||||
NotASourceCharacteristic
|
||||
{
|
||||
bindingset[this]
|
||||
NeitherSourceNorSinkCharacteristic() { any() }
|
||||
|
||||
final override predicate hasImplications(
|
||||
Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
NotASinkCharacteristic.super.hasImplications(endpointType, isPositiveIndicator, confidence) or
|
||||
NotASourceCharacteristic.super.hasImplications(endpointType, isPositiveIndicator, confidence)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A medium-confidence characteristic that indicates that an endpoint is unlikely to be a sink of any type. These
|
||||
* endpoints can be excluded from scoring at inference time, both to save time and to avoid false positives. They should
|
||||
@@ -269,8 +319,8 @@ module SharedCharacteristics<CandidateSig Candidate> {
|
||||
override predicate hasImplications(
|
||||
Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
endpointType instanceof Candidate::NegativeEndpointType and
|
||||
isPositiveIndicator = true and
|
||||
endpointType instanceof Candidate::SinkType and
|
||||
isPositiveIndicator = false and
|
||||
confidence = mediumConfidence()
|
||||
}
|
||||
}
|
||||
@@ -290,8 +340,8 @@ module SharedCharacteristics<CandidateSig Candidate> {
|
||||
override predicate hasImplications(
|
||||
Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
endpointType instanceof Candidate::NegativeEndpointType and
|
||||
isPositiveIndicator = true and
|
||||
endpointType instanceof Candidate::SinkType and
|
||||
isPositiveIndicator = false and
|
||||
confidence = mediumConfidence()
|
||||
}
|
||||
}
|
||||
@@ -344,17 +394,16 @@ module SharedCharacteristics<CandidateSig Candidate> {
|
||||
/**
|
||||
* A negative characteristic that indicates that an endpoint was manually modeled as a neutral model.
|
||||
*/
|
||||
private class NeutralModelCharacteristic extends NotASinkCharacteristic {
|
||||
private class NeutralModelCharacteristic extends NeitherSourceNorSinkCharacteristic {
|
||||
NeutralModelCharacteristic() { this = "known non-sink" }
|
||||
|
||||
override predicate appliesToEndpoint(Candidate::Endpoint e) { Candidate::isNeutral(e) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A negative characteristic that indicates that an endpoint is not part of the source code for the project being
|
||||
* analyzed.
|
||||
* A negative characteristic that indicates that an endpoint is a sanitizer, and thus not a source.
|
||||
*/
|
||||
private class IsSanitizerCharacteristic extends NotASinkCharacteristic {
|
||||
private class IsSanitizerCharacteristic extends NotASourceCharacteristic {
|
||||
IsSanitizerCharacteristic() { this = "known sanitizer" }
|
||||
|
||||
override predicate appliesToEndpoint(Candidate::Endpoint e) { Candidate::isSanitizer(e, _) }
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* An automodel extraction mode instantiates this interface to define how to access
|
||||
* the callable that's associated with an endpoint.
|
||||
*/
|
||||
signature module GetCallableSig {
|
||||
/**
|
||||
* A callable is the definition of a method, function, etc. - something that can be called.
|
||||
*/
|
||||
class Callable;
|
||||
|
||||
/**
|
||||
* An endpoint is a potential candidate for modeling. This will typically be bound to the language's
|
||||
* DataFlow node class, or a subtype thereof.
|
||||
*/
|
||||
class Endpoint;
|
||||
|
||||
/**
|
||||
* Gets the callable that's associated with the given endpoint.
|
||||
*/
|
||||
Callable getCallable(Endpoint endpoint);
|
||||
}
|
||||
@@ -1,3 +1,15 @@
|
||||
## 0.0.13
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.0.12
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.0.11
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.0.10
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -65,7 +65,7 @@ A significant part of the behavior of extraction queries is implemented in share
|
||||
|
||||
## Packaging
|
||||
|
||||
Automodel extraction queries come as a dedicated package. See [qlpack.yml](https://github.com/github/codeql/blob/main/java/ql/automodel/src/qlpack.yml). The [publish.sh](https://github.com/github/codeql/blob/main/java/ql/automodel/publish.sh) script is responsible for publishing a new version to the [package registry](https://github.com/orgs/codeql/packages/container/package/java-automodel-queries).
|
||||
Automodel extraction queries come as a dedicated package. See [qlpack.yml](https://github.com/github/codeql/blob/main/java/ql/automodel/src/qlpack.yml). The [publish.sh](https://github.com/github/codeql/blob/main/java/ql/automodel/publish.sh) script is responsible for publishing a new version to the [package registry](https://github.com/orgs/codeql/packages/container/package/java-automodel-queries). **The extraction queries are functionally coupled with other automodel components. Only publish the query pack as part of the automodel release process.**
|
||||
|
||||
### Backwards Compatibility
|
||||
|
||||
|
||||
3
java/ql/automodel/src/change-notes/released/0.0.11.md
Normal file
3
java/ql/automodel/src/change-notes/released/0.0.11.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.0.11
|
||||
|
||||
No user-facing changes.
|
||||
3
java/ql/automodel/src/change-notes/released/0.0.12.md
Normal file
3
java/ql/automodel/src/change-notes/released/0.0.12.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.0.12
|
||||
|
||||
No user-facing changes.
|
||||
3
java/ql/automodel/src/change-notes/released/0.0.13.md
Normal file
3
java/ql/automodel/src/change-notes/released/0.0.13.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.0.13
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.0.10
|
||||
lastReleaseVersion: 0.0.13
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/java-automodel-queries
|
||||
version: 0.0.10
|
||||
version: 0.0.13
|
||||
groups:
|
||||
- java
|
||||
- automodel
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
| PluginImpl.java:5:27:5:37 | name | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | PluginImpl.java:5:27:5:37 | name | CallContext | hudson/Plugin.java:5:5:5:31 | /** Configure method doc */ | MethodDoc | hudson/Plugin.java:3:1:3:17 | /** Plugin doc */ | ClassDoc | file://hudson:1:1:1:1 | hudson | package | file://Plugin:1:1:1:1 | Plugin | type | file://true:1:1:1:1 | true | subtypes | file://configure:1:1:1:1 | configure | name | file://(String,String):1:1:1:1 | (String,String) | signature | file://:1:1:1:1 | | input | file://Parameter[0]:1:1:1:1 | Parameter[0] | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
|
||||
| PluginImpl.java:5:40:5:51 | value | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | PluginImpl.java:5:40:5:51 | value | CallContext | hudson/Plugin.java:5:5:5:31 | /** Configure method doc */ | MethodDoc | hudson/Plugin.java:3:1:3:17 | /** Plugin doc */ | ClassDoc | file://hudson:1:1:1:1 | hudson | package | file://Plugin:1:1:1:1 | Plugin | type | file://true:1:1:1:1 | true | subtypes | file://configure:1:1:1:1 | configure | name | file://(String,String):1:1:1:1 | (String,String) | signature | file://:1:1:1:1 | | input | file://Parameter[1]:1:1:1:1 | Parameter[1] | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
|
||||
| Test.java:19:3:19:11 | reference | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:19:3:19:24 | set(...) | CallContext | Test.java:19:3:19:11 | reference | MethodDoc | Test.java:19:3:19:11 | reference | ClassDoc | file://java.util.concurrent.atomic:1:1:1:1 | java.util.concurrent.atomic | package | file://AtomicReference:1:1:1:1 | AtomicReference | type | file://false:1:1:1:1 | false | subtypes | file://set:1:1:1:1 | set | name | file://(Object):1:1:1:1 | (Object) | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sinkModel:1:1:1:1 | sinkModel | extensibleType |
|
||||
| Test.java:24:3:24:10 | supplier | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:24:3:24:16 | get(...) | CallContext | Test.java:24:3:24:10 | supplier | MethodDoc | Test.java:24:3:24:10 | supplier | ClassDoc | file://java.util.function:1:1:1:1 | java.util.function | package | file://Supplier:1:1:1:1 | Supplier | type | file://true:1:1:1:1 | true | subtypes | file://get:1:1:1:1 | get | name | file://():1:1:1:1 | () | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sinkModel:1:1:1:1 | sinkModel | extensibleType |
|
||||
| Test.java:24:3:24:16 | get(...) | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:24:3:24:16 | get(...) | CallContext | Test.java:24:3:24:16 | get(...) | MethodDoc | Test.java:24:3:24:16 | get(...) | ClassDoc | file://java.util.function:1:1:1:1 | java.util.function | package | file://Supplier:1:1:1:1 | Supplier | type | file://true:1:1:1:1 | true | subtypes | file://get:1:1:1:1 | get | name | file://():1:1:1:1 | () | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
|
||||
| Test.java:28:3:32:3 | copy(...) | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:28:3:32:3 | copy(...) | CallContext | Test.java:28:3:32:3 | copy(...) | MethodDoc | Test.java:28:3:32:3 | copy(...) | ClassDoc | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
|
||||
| Test.java:36:10:38:3 | newInputStream(...) | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:36:10:38:3 | newInputStream(...) | CallContext | Test.java:36:10:38:3 | newInputStream(...) | MethodDoc | Test.java:36:10:38:3 | newInputStream(...) | ClassDoc | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://newInputStream:1:1:1:1 | newInputStream | name | file://(Path,OpenOption[]):1:1:1:1 | (Path,OpenOption[]) | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
|
||||
| Test.java:37:4:37:11 | openPath | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:36:10:38:3 | newInputStream(...) | CallContext | Test.java:37:4:37:11 | openPath | MethodDoc | Test.java:37:4:37:11 | openPath | ClassDoc | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://newInputStream:1:1:1:1 | newInputStream | name | file://(Path,OpenOption[]):1:1:1:1 | (Path,OpenOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray | file://ai-manual:1:1:1:1 | ai-manual | alreadyAiModeled | file://sinkModel:1:1:1:1 | sinkModel | extensibleType |
|
||||
| Test.java:43:4:43:22 | get(...) | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:43:4:43:22 | get(...) | CallContext | Test.java:43:4:43:22 | get(...) | MethodDoc | Test.java:43:4:43:22 | get(...) | ClassDoc | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Paths:1:1:1:1 | Paths | type | file://false:1:1:1:1 | false | subtypes | file://get:1:1:1:1 | get | name | file://(String,String[]):1:1:1:1 | (String,String[]) | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
|
||||
| Test.java:54:3:59:3 | walk(...) | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:54:3:59:3 | walk(...) | CallContext | Test.java:54:3:59:3 | walk(...) | MethodDoc | Test.java:54:3:59:3 | walk(...) | ClassDoc | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://walk:1:1:1:1 | walk | name | file://(Path,FileVisitOption[]):1:1:1:1 | (Path,FileVisitOption[]) | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
|
||||
| Test.java:56:4:56:4 | o | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:54:3:59:3 | walk(...) | CallContext | Test.java:54:3:59:3 | walk(...) | MethodDoc | Test.java:54:3:59:3 | walk(...) | ClassDoc | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://walk:1:1:1:1 | walk | name | file://(Path,FileVisitOption[]):1:1:1:1 | (Path,FileVisitOption[]) | signature | file://Argument[1]:1:1:1:1 | Argument[1] | input | file://:1:1:1:1 | | output | file://true:1:1:1:1 | true | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sinkModel:1:1:1:1 | sinkModel | extensibleType |
|
||||
| Test.java:63:3:63:3 | c | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:63:3:63:20 | getInputStream(...) | CallContext | Test.java:63:3:63:3 | c | MethodDoc | Test.java:63:3:63:3 | c | ClassDoc | file://java.net:1:1:1:1 | java.net | package | file://URLConnection:1:1:1:1 | URLConnection | type | file://true:1:1:1:1 | true | subtypes | file://getInputStream:1:1:1:1 | getInputStream | name | file://():1:1:1:1 | () | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sinkModel:1:1:1:1 | sinkModel | extensibleType |
|
||||
| Test.java:68:30:68:47 | writer | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:68:30:68:47 | writer | CallContext | Test.java:68:30:68:47 | writer | MethodDoc | Test.java:68:30:68:47 | writer | ClassDoc | file://java.lang:1:1:1:1 | java.lang | package | file://Throwable:1:1:1:1 | Throwable | type | file://true:1:1:1:1 | true | subtypes | file://printStackTrace:1:1:1:1 | printStackTrace | name | file://(PrintWriter):1:1:1:1 | (PrintWriter) | signature | file://:1:1:1:1 | | input | file://Parameter[0]:1:1:1:1 | Parameter[0] | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
|
||||
@@ -1 +0,0 @@
|
||||
AutomodelApplicationModeExtractCandidates.ql
|
||||
@@ -1,3 +0,0 @@
|
||||
| Test.java:48:10:50:3 | compareTo(...) | known sanitizer\nrelated locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:48:10:50:3 | compareTo(...) | CallContext | Test.java:48:10:50:3 | compareTo(...) | MethodDoc | Test.java:48:10:50:3 | compareTo(...) | ClassDoc | file://java.io:1:1:1:1 | java.io | package | file://File:1:1:1:1 | File | type | file://true:1:1:1:1 | true | subtypes | file://compareTo:1:1:1:1 | compareTo | name | file://(File):1:1:1:1 | (File) | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
|
||||
| Test.java:49:4:49:5 | f2 | known non-sink\nrelated locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:48:10:50:3 | compareTo(...) | CallContext | Test.java:49:4:49:5 | f2 | MethodDoc | Test.java:49:4:49:5 | f2 | ClassDoc | file://java.io:1:1:1:1 | java.io | package | file://File:1:1:1:1 | File | type | file://true:1:1:1:1 | true | subtypes | file://compareTo:1:1:1:1 | compareTo | name | file://(File):1:1:1:1 | (File) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray | file://sinkModel:1:1:1:1 | sinkModel | extensibleType |
|
||||
| Test.java:55:4:55:4 | p | taint step\nrelated locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:54:3:59:3 | walk(...) | CallContext | Test.java:55:4:55:4 | p | MethodDoc | Test.java:55:4:55:4 | p | ClassDoc | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://walk:1:1:1:1 | walk | name | file://(Path,FileVisitOption[]):1:1:1:1 | (Path,FileVisitOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray | file://sinkModel:1:1:1:1 | sinkModel | extensibleType |
|
||||
@@ -1 +0,0 @@
|
||||
AutomodelApplicationModeExtractNegativeExamples.ql
|
||||
@@ -1,4 +0,0 @@
|
||||
| Test.java:29:4:29:9 | source | path-injection\nrelated locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:28:3:32:3 | copy(...) | CallContext | Test.java:29:4:29:9 | source | MethodDoc | Test.java:29:4:29:9 | source | ClassDoc | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray | file://sinkModel:1:1:1:1 | sinkModel | extensibleType |
|
||||
| Test.java:30:4:30:9 | target | path-injection\nrelated locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:28:3:32:3 | copy(...) | CallContext | Test.java:30:4:30:9 | target | MethodDoc | Test.java:30:4:30:9 | target | ClassDoc | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://Argument[1]:1:1:1:1 | Argument[1] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray | file://sinkModel:1:1:1:1 | sinkModel | extensibleType |
|
||||
| Test.java:37:4:37:11 | openPath | path-injection\nrelated locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:36:10:38:3 | newInputStream(...) | CallContext | Test.java:37:4:37:11 | openPath | MethodDoc | Test.java:37:4:37:11 | openPath | ClassDoc | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://newInputStream:1:1:1:1 | newInputStream | name | file://(Path,OpenOption[]):1:1:1:1 | (Path,OpenOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray | file://sinkModel:1:1:1:1 | sinkModel | extensibleType |
|
||||
| Test.java:63:3:63:20 | getInputStream(...) | remote\nrelated locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:63:3:63:20 | getInputStream(...) | CallContext | Test.java:63:3:63:20 | getInputStream(...) | MethodDoc | Test.java:63:3:63:20 | getInputStream(...) | ClassDoc | file://java.net:1:1:1:1 | java.net | package | file://URLConnection:1:1:1:1 | URLConnection | type | file://true:1:1:1:1 | true | subtypes | file://getInputStream:1:1:1:1 | getInputStream | name | file://():1:1:1:1 | () | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
|
||||
@@ -1 +0,0 @@
|
||||
AutomodelApplicationModeExtractPositiveExamples.ql
|
||||
@@ -0,0 +1,2 @@
|
||||
testFailures
|
||||
failures
|
||||
@@ -0,0 +1,35 @@
|
||||
import java
|
||||
import AutomodelApplicationModeCharacteristics as Characteristics
|
||||
import AutomodelExtractionTests
|
||||
|
||||
module TestHelper implements TestHelperSig<Characteristics::ApplicationCandidatesImpl> {
|
||||
Location getEndpointLocation(Characteristics::Endpoint endpoint) {
|
||||
result = endpoint.asTop().getLocation()
|
||||
}
|
||||
|
||||
predicate isCandidate(
|
||||
Characteristics::Endpoint endpoint, string name, string signature, string input, string output,
|
||||
string extensibleType
|
||||
) {
|
||||
Characteristics::isCandidate(endpoint, _, _, _, name, signature, input, output, _,
|
||||
extensibleType, _)
|
||||
}
|
||||
|
||||
predicate isPositiveExample(
|
||||
Characteristics::Endpoint endpoint, string endpointType, string name, string signature,
|
||||
string input, string output, string extensibleType
|
||||
) {
|
||||
Characteristics::isPositiveExample(endpoint, endpointType, _, _, _, name, signature, input,
|
||||
output, _, extensibleType)
|
||||
}
|
||||
|
||||
predicate isNegativeExample(
|
||||
Characteristics::Endpoint endpoint, string name, string signature, string input, string output,
|
||||
string extensibleType
|
||||
) {
|
||||
Characteristics::isNegativeExample(endpoint, _, _, _, _, _, name, signature, input, output, _,
|
||||
extensibleType)
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<Extraction<Characteristics::ApplicationCandidatesImpl, TestHelper>>
|
||||
@@ -2,7 +2,7 @@ import hudson.Plugin;
|
||||
|
||||
public class PluginImpl extends Plugin {
|
||||
@Override
|
||||
public void configure(String name, String value) {
|
||||
public void configure(String name, String value) { // $ sourceModelCandidate=configure(String,String):Parameter[0] sourceModelCandidate=configure(String,String):Parameter[1]
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,56 +16,59 @@ import java.util.concurrent.FutureTask;
|
||||
class Test {
|
||||
public static void main(String[] args) throws Exception {
|
||||
AtomicReference<String> reference = new AtomicReference<>(); // uninteresting (parameterless constructor)
|
||||
reference.set(args[0]); // arg[0] is not a candidate (modeled as value flow step)
|
||||
// ^^^^^^ Argument[this] is a candidate
|
||||
reference.set( // $ sinkModelCandidate=set(Object):Argument[this]
|
||||
args[0] // $ negativeSinkExample=set(Object):Argument[0] // modeled as a flow step
|
||||
); // $ negativeSourceExample=set(Object):ReturnValue // return type is void
|
||||
}
|
||||
|
||||
public static void callSupplier(Supplier<String> supplier) {
|
||||
supplier.get(); // Argument[this] is a sink candidate; the call is a source candidate
|
||||
supplier.get(); // $ sourceModelCandidate=get():ReturnValue sinkModelCandidate=get():Argument[this]
|
||||
}
|
||||
|
||||
public static void copyFiles(Path source, Path target, CopyOption option) throws Exception {
|
||||
Files.copy( // the call is a source candidate
|
||||
source, // positive example (known sink)
|
||||
target, // positive example (known sink)
|
||||
Files.copy(
|
||||
source, // $ positiveSinkExample=copy(Path,Path,CopyOption[]):Argument[0](path-injection)
|
||||
target, // $ positiveSinkExample=copy(Path,Path,CopyOption[]):Argument[1](path-injection)
|
||||
option // no candidate (not modeled, but source and target are modeled)
|
||||
);
|
||||
); // $ sourceModelCandidate=copy(Path,Path,CopyOption[]):ReturnValue
|
||||
}
|
||||
|
||||
public static InputStream getInputStream(Path openPath) throws Exception {
|
||||
return Files.newInputStream( // the call is a source candidate
|
||||
openPath // positive example (known sink), candidate ("only" ai-modeled, and useful as a candidate in regression testing)
|
||||
);
|
||||
return Files.newInputStream(
|
||||
openPath // $ sinkModelCandidate=newInputStream(Path,OpenOption[]):Argument[0] positiveSinkExample=newInputStream(Path,OpenOption[]):Argument[0](path-injection) // sink candidate because "only" ai-modeled, and useful as a candidate in regression testing
|
||||
); // $ sourceModelCandidate=newInputStream(Path,OpenOption[]):ReturnValue
|
||||
}
|
||||
|
||||
public static InputStream getInputStream(String openPath) throws Exception {
|
||||
return Test.getInputStream( // the call is not a source candidate (argument to local call)
|
||||
Paths.get(openPath) // no sink candidate (argument to local call); the call is a source candidate
|
||||
Paths.get(
|
||||
openPath // $ negativeSinkExample=get(String,String[]):Argument[0] // modeled as a flow step
|
||||
) // $ sourceModelCandidate=get(String,String[]):ReturnValue
|
||||
);
|
||||
}
|
||||
|
||||
public static int compareFiles(File f1, File f2) {
|
||||
return f1.compareTo( // compareTo call is a known sanitizer
|
||||
f2 // negative sink example (modeled as not a sink)
|
||||
); // the call is a negative source candidate (sanitizer)
|
||||
return f1.compareTo( // $ negativeSinkExample=compareTo(File):Argument[this]
|
||||
f2 // $ negativeSinkExample=compareTo(File):Argument[0] // modeled as not a sink
|
||||
); // $ negativeSourceExample=compareTo(File):ReturnValue // return type is int
|
||||
}
|
||||
|
||||
public static void FilesWalkExample(Path p, FileVisitOption o) throws Exception {
|
||||
Files.walk( // the call is a source candidate
|
||||
p, // negative example (modeled as a taint step)
|
||||
o, // the implicit varargs array is a candidate
|
||||
Files.walk(
|
||||
p, // $ negativeSinkExample=walk(Path,FileVisitOption[]):Argument[0] // modeled as a flow step
|
||||
o, // the implicit varargs array is a candidate, annotated on the last line of the call
|
||||
o // not a candidate (only the first arg corresponding to a varargs array
|
||||
// is extracted)
|
||||
);
|
||||
); // $ sourceModelCandidate=walk(Path,FileVisitOption[]):ReturnValue sinkModelCandidate=walk(Path,FileVisitOption[]):Argument[1]
|
||||
}
|
||||
|
||||
public static void WebSocketExample(URLConnection c) throws Exception {
|
||||
c.getInputStream(); // the call is a source example, c is a sink candidate
|
||||
c.getInputStream(); // $ sinkModelCandidate=getInputStream():Argument[this] positiveSourceExample=getInputStream():ReturnValue(remote) // not a source candidate (manual modeling)
|
||||
}
|
||||
}
|
||||
|
||||
class OverrideTest extends Exception {
|
||||
public void printStackTrace(PrintWriter writer) { // writer is a source candidate because it overrides an existing method
|
||||
public void printStackTrace(PrintWriter writer) { // $ sourceModelCandidate=printStackTrace(PrintWriter):Parameter[0]
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -80,3 +83,21 @@ class TaskUtils {
|
||||
return ft;
|
||||
}
|
||||
}
|
||||
|
||||
class MoreTests {
|
||||
public static void FilesListExample(Path p) throws Exception {
|
||||
Files.list(
|
||||
Files.createDirectories(
|
||||
p // $ positiveSinkExample=createDirectories(Path,FileAttribute[]):Argument[0](path-injection)
|
||||
) // $ sourceModelCandidate=createDirectories(Path,FileAttribute[]):ReturnValue negativeSinkExample=list(Path):Argument[0] // modeled as a flow step
|
||||
); // $ sourceModelCandidate=list(Path):ReturnValue
|
||||
|
||||
Files.delete(
|
||||
p // $ sinkModelCandidate=delete(Path):Argument[0] positiveSinkExample=delete(Path):Argument[0](path-injection)
|
||||
); // $ negativeSourceExample=delete(Path):ReturnValue // return type is void
|
||||
|
||||
Files.deleteIfExists(
|
||||
p // $ sinkModelCandidate=deleteIfExists(Path):Argument[0] positiveSinkExample=deleteIfExists(Path):Argument[0](path-injection)
|
||||
); // $ negativeSourceExample=deleteIfExists(Path):ReturnValue // return type is boolean
|
||||
}
|
||||
}
|
||||
77
java/ql/automodel/test/AutomodelExtractionTests.qll
Normal file
77
java/ql/automodel/test/AutomodelExtractionTests.qll
Normal file
@@ -0,0 +1,77 @@
|
||||
import java
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import AutomodelSharedCharacteristics
|
||||
|
||||
signature module TestHelperSig<CandidateSig Candidate> {
|
||||
Location getEndpointLocation(Candidate::Endpoint e);
|
||||
|
||||
predicate isCandidate(
|
||||
Candidate::Endpoint e, string name, string signature, string input, string output,
|
||||
string extensibleType
|
||||
);
|
||||
|
||||
predicate isPositiveExample(
|
||||
Candidate::Endpoint e, string endpointType, string name, string signature, string input,
|
||||
string output, string extensibleType
|
||||
);
|
||||
|
||||
predicate isNegativeExample(
|
||||
Candidate::Endpoint e, string name, string signature, string input, string output,
|
||||
string extensibleType
|
||||
);
|
||||
}
|
||||
|
||||
module Extraction<CandidateSig Candidate, TestHelperSig<Candidate> TestHelper> implements TestSig {
|
||||
string getARelevantTag() {
|
||||
result in [
|
||||
"sourceModelCandidate", "sinkModelCandidate", // a candidate source/sink
|
||||
"positiveSourceExample", "positiveSinkExample", // a known source/sink
|
||||
"negativeSourceExample", "negativeSinkExample" // a known non-source/non-sink
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* If `extensibleType` is `sourceModel` then the result is `ifSource`, if it
|
||||
* is `sinkModel` then the result is `ifSink`.
|
||||
*/
|
||||
bindingset[extensibleType, ifSource, ifSink]
|
||||
private string ifSource(string extensibleType, string ifSource, string ifSink) {
|
||||
extensibleType = "sourceModel" and result = ifSource
|
||||
or
|
||||
extensibleType = "sinkModel" and result = ifSink
|
||||
}
|
||||
|
||||
additional predicate selectEndpoint(
|
||||
Candidate::Endpoint endpoint, string name, string signature, string input, string output,
|
||||
string extensibleType, string tag, string suffix
|
||||
) {
|
||||
TestHelper::isCandidate(endpoint, name, signature, input, output, extensibleType) and
|
||||
tag = ifSource(extensibleType, "sourceModelCandidate", "sinkModelCandidate") and
|
||||
suffix = ""
|
||||
or
|
||||
TestHelper::isNegativeExample(endpoint, name, signature, input, output, extensibleType) and
|
||||
tag = "negative" + ifSource(extensibleType, "Source", "Sink") + "Example" and
|
||||
suffix = ""
|
||||
or
|
||||
exists(string endpointType |
|
||||
TestHelper::isPositiveExample(endpoint, endpointType, name, signature, input, output,
|
||||
extensibleType) and
|
||||
tag = "positive" + ifSource(extensibleType, "Source", "Sink") + "Example" and
|
||||
suffix = "(" + endpointType + ")"
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(
|
||||
Candidate::Endpoint endpoint, string name, string signature, string input, string output,
|
||||
string extensibleType, string suffix
|
||||
|
|
||||
selectEndpoint(endpoint, name, signature, input, output, extensibleType, tag, suffix)
|
||||
|
|
||||
TestHelper::getEndpointLocation(endpoint) = location and
|
||||
endpoint.toString() = element and
|
||||
// for source models only the output is relevant, and vice versa for sink models
|
||||
value = name + signature + ":" + ifSource(extensibleType, output, input) + suffix
|
||||
)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user