This commit is contained in:
parent
ee60237550
commit
d63b6a6f53
6 changed files with 424 additions and 228 deletions
144
slides.typ
144
slides.typ
|
|
@ -1,5 +1,6 @@
|
||||||
#import "@preview/polylux:0.4.0": *
|
#import "@preview/polylux:0.4.0": *
|
||||||
#import "slides/lib.typ": *
|
#import "slides/lib.typ": *
|
||||||
|
#import "slides/icons.typ" as ico
|
||||||
|
|
||||||
#import "@preview/codly:1.3.0": *
|
#import "@preview/codly:1.3.0": *
|
||||||
#import "@preview/codly-languages:0.1.1": *
|
#import "@preview/codly-languages:0.1.1": *
|
||||||
|
|
@ -83,7 +84,13 @@
|
||||||
#grid(
|
#grid(
|
||||||
columns: (1fr, 1fr),
|
columns: (1fr, 1fr),
|
||||||
image("slides/imgs/google.png", width: 200pt),
|
image("slides/imgs/google.png", width: 200pt),
|
||||||
image("slides/imgs/phone.png", height: 350pt)
|
//image("slides/imgs/phone.png", height: 350pt)
|
||||||
|
ico.phone(
|
||||||
|
height: 350pt,
|
||||||
|
body: {
|
||||||
|
ico.android(height: 150pt, stroke: none)
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
#v(2em)
|
#v(2em)
|
||||||
]
|
]
|
||||||
|
|
@ -111,7 +118,15 @@
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
move(dx: 20pt, image("slides/imgs/phone.png", height: 350pt))
|
move(
|
||||||
|
dx: 20pt,
|
||||||
|
ico.phone(
|
||||||
|
height: 350pt,
|
||||||
|
body: {
|
||||||
|
ico.android(height: 150pt, stroke: none)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
@ -201,8 +216,8 @@
|
||||||
foreground: {
|
foreground: {
|
||||||
place-fg(x: 44%, y: 55%, $ lr(}, size: #130pt) $ )
|
place-fg(x: 44%, y: 55%, $ lr(}, size: #130pt) $ )
|
||||||
place-fg(x: 44%, y: 26%, $ lr(}, size: #110pt) $ )
|
place-fg(x: 44%, y: 26%, $ lr(}, size: #110pt) $ )
|
||||||
arrow((385pt, 260pt), (450pt, 230pt))
|
arrow((385pt, -260pt), (450pt, -230pt))
|
||||||
arrow((385pt, 125pt), (450pt, 110pt))
|
arrow((385pt, -125pt), (450pt, -110pt))
|
||||||
},
|
},
|
||||||
)[
|
)[
|
||||||
#show: yes-codly
|
#show: yes-codly
|
||||||
|
|
@ -386,13 +401,46 @@
|
||||||
#highlight-block(pb3-text)
|
#highlight-block(pb3-text)
|
||||||
]
|
]
|
||||||
|
|
||||||
#slide(
|
#for i in range(3) {
|
||||||
foreground: rotate(30deg, smallcaps(text(fill: pirat-color.red, size: 50pt)[MOCHE + ANIM]))
|
let stage = (
|
||||||
)[
|
"static-only",
|
||||||
// TODO Outline / problematics / drawingp
|
"static-vs-dyn",
|
||||||
|
"theseus",
|
||||||
|
).at(i)
|
||||||
|
slide(
|
||||||
|
//foreground: rotate(30deg, smallcaps(text(fill: pirat-color.red, size: 50pt)[#stage]))
|
||||||
|
)[
|
||||||
|
#if i == 0 {
|
||||||
|
place(
|
||||||
|
top+left,
|
||||||
|
dx: 70%,
|
||||||
|
dy: 25%,
|
||||||
|
align(center, text(fill: pirat-color.red.darken(15%))[
|
||||||
|
*PB 1*: Static Analysis \ are the tools working?
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
#if i == 1 {
|
||||||
|
place(
|
||||||
|
top+left,
|
||||||
|
dx: 35%,
|
||||||
|
dy: 25%,
|
||||||
|
align(center, text(fill: pirat-color.red.darken(15%))[
|
||||||
|
*PB 2*: Do Static Tools \ match Android Runtime?
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
#if i == 2 {
|
||||||
|
place(
|
||||||
|
top+left,
|
||||||
|
dx: 5%,
|
||||||
|
dy: 5%,
|
||||||
|
align(center, text(fill: pirat-color.red.darken(15%))[
|
||||||
|
*PB 3*: Improve any Static Tools \ with dynamic analysis?
|
||||||
|
]))
|
||||||
|
}
|
||||||
#set align(horizon+center)
|
#set align(horizon+center)
|
||||||
#th-outline()
|
#theseus-outline(stage: stage)
|
||||||
]
|
]
|
||||||
|
}
|
||||||
|
|
||||||
#new-section-slide([Tool Reusability])
|
#new-section-slide([Tool Reusability])
|
||||||
|
|
||||||
|
|
@ -723,36 +771,79 @@
|
||||||
#slide(
|
#slide(
|
||||||
title: [Android ClassLoaders],
|
title: [Android ClassLoaders],
|
||||||
foreground: {
|
foreground: {
|
||||||
//rotate(30deg, text(fill: pirat-color.red, size: 50pt)[DESSIN RUNTIME CL \ \ WITH DELEGATION])
|
let stroke = black + 5pt
|
||||||
let stroke = black + 3pt
|
let y0 = -177pt
|
||||||
let y0 = 170pt
|
let y1 = -270pt
|
||||||
let y1 = 270pt
|
|
||||||
let x0 = 250pt
|
let x0 = 250pt
|
||||||
let x1 = 292pt
|
let x1 = 272pt
|
||||||
let x2 = 550pt
|
let x2 = 570pt
|
||||||
let x3 = 600pt
|
let x3 = 600pt
|
||||||
place(bottom+left, line(start: (x0, -y0), end: (x0, -y1), stroke: stroke))
|
arrow(
|
||||||
place(bottom+left, line(start: (x3, -y0), end: (x3, -y1), stroke: stroke))
|
(x0, y0),
|
||||||
arrow((x0, y1), (x1, y1), strk: stroke)
|
(x0, y1),
|
||||||
arrow((x3, y1), (x2, y1), strk: stroke)
|
(x1, y1),
|
||||||
|
stroke: stroke
|
||||||
|
)
|
||||||
|
arrow(
|
||||||
|
(x3, y0),
|
||||||
|
(x3, y1),
|
||||||
|
(x2, y1),
|
||||||
|
stroke: stroke
|
||||||
|
)
|
||||||
place-fg(x: x0 - 2.5em, y: (y0+y1)/2)[Delegate]
|
place-fg(x: x0 - 2.5em, y: (y0+y1)/2)[Delegate]
|
||||||
place-fg(x: x3 + 2.5em, y: (y0+y1)/2)[Delegate]
|
place-fg(x: x3 + 2.5em, y: (y0+y1)/2)[Delegate]
|
||||||
}, {
|
}, {
|
||||||
|
let stroke = black + 3pt
|
||||||
|
let width = 13em
|
||||||
|
let height = 4.5em
|
||||||
set align(center+horizon)
|
set align(center+horizon)
|
||||||
set rect(width: 250pt, height: 75pt, radius: 20pt, inset: 20pt)
|
set rect(width: 250pt, height: 75pt, radius: 20pt, inset: 20pt)
|
||||||
|
|
||||||
v(1fr)
|
v(1fr)
|
||||||
|
|
||||||
rect()[Boot Class Loader]
|
rect(
|
||||||
|
stroke: stroke,
|
||||||
|
height: height,
|
||||||
|
width: width,
|
||||||
|
{
|
||||||
|
[*Boot Class Loader*]
|
||||||
|
v(-0.5em)
|
||||||
|
line(length: 80%)
|
||||||
|
v(-0.5em)
|
||||||
|
[_Platform Classes_]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
v(1fr)
|
v(1fr)
|
||||||
|
|
||||||
stack(
|
stack(
|
||||||
dir: ltr,
|
dir: ltr,
|
||||||
1fr,
|
1fr,
|
||||||
rect()[System Class Loader],
|
rect(
|
||||||
|
stroke: stroke,
|
||||||
|
height: height,
|
||||||
|
width: width,
|
||||||
|
{
|
||||||
|
[*System Class Loader*]
|
||||||
|
v(-0.5em)
|
||||||
|
line(length: 80%)
|
||||||
|
v(-0.5em)
|
||||||
|
sym.emptyset
|
||||||
|
}
|
||||||
|
),
|
||||||
1fr,
|
1fr,
|
||||||
rect()[APK Class Loader],
|
rect(
|
||||||
|
stroke: stroke,
|
||||||
|
height: height,
|
||||||
|
width: width,
|
||||||
|
{
|
||||||
|
[*APK Class Loader*]
|
||||||
|
v(-0.5em)
|
||||||
|
line(length: 80%)
|
||||||
|
v(-0.5em)
|
||||||
|
[_APK Classes_]
|
||||||
|
}
|
||||||
|
),
|
||||||
1fr,
|
1fr,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -1130,7 +1221,7 @@
|
||||||
)[
|
)[
|
||||||
// TODO: bien tout rappeler l'objectif
|
// TODO: bien tout rappeler l'objectif
|
||||||
#set align(center+horizon)
|
#set align(center+horizon)
|
||||||
#th-outline(hide-static: true)
|
#theseus-outline()
|
||||||
]
|
]
|
||||||
|
|
||||||
#slide(
|
#slide(
|
||||||
|
|
@ -1288,7 +1379,7 @@
|
||||||
foreground: rotate(30deg, text(fill: pirat-color.red, size: 50pt)[MOCHE]),
|
foreground: rotate(30deg, text(fill: pirat-color.red, size: 50pt)[MOCHE]),
|
||||||
)[
|
)[
|
||||||
#set align(center+horizon)
|
#set align(center+horizon)
|
||||||
#th-outline(hide-static: true)
|
#theseus-outline()
|
||||||
]
|
]
|
||||||
|
|
||||||
#counter("logical-slide").update( n => n - 1 )
|
#counter("logical-slide").update( n => n - 1 )
|
||||||
|
|
@ -1340,7 +1431,7 @@
|
||||||
foreground: rotate(30deg, text(fill: pirat-color.red, size: 50pt)[MOCHE]),
|
foreground: rotate(30deg, text(fill: pirat-color.red, size: 50pt)[MOCHE]),
|
||||||
)[
|
)[
|
||||||
#set align(center+horizon)
|
#set align(center+horizon)
|
||||||
#th-outline(hide-static: false)
|
#theseus-outline()
|
||||||
]
|
]
|
||||||
|
|
||||||
#for i in range(3) {
|
#for i in range(3) {
|
||||||
|
|
@ -1391,6 +1482,7 @@
|
||||||
title: [Impact on Finishing Rate],
|
title: [Impact on Finishing Rate],
|
||||||
foreground: {
|
foreground: {
|
||||||
let strk = 3pt + pirat-color.blue
|
let strk = 3pt + pirat-color.blue
|
||||||
|
import "slides/icons.typ": arrow
|
||||||
arrow((360pt, 330pt), (380pt, 310pt), strk: strk)
|
arrow((360pt, 330pt), (380pt, 310pt), strk: strk)
|
||||||
arrow((420pt, 330pt), (400pt, 310pt), strk: strk)
|
arrow((420pt, 330pt), (400pt, 310pt), strk: strk)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,15 @@
|
||||||
start = end
|
start = end
|
||||||
end = pt
|
end = pt
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
let r = 10pt
|
||||||
|
place(
|
||||||
|
bottom + left,
|
||||||
|
dx: start.at(0) - r,
|
||||||
|
dy: start.at(1) + r,
|
||||||
|
circle(radius: r, fill: blue)
|
||||||
|
)
|
||||||
|
*/
|
||||||
place(
|
place(
|
||||||
bottom + left,
|
bottom + left,
|
||||||
line(
|
line(
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 191 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 82 KiB |
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#import "../lib.typ": pb1-text, pb2-text, pb3-text
|
#import "../lib.typ": pb1-text, pb2-text, pb3-text
|
||||||
#import "icons.typ": arrow
|
#import "icons.typ": arrow
|
||||||
|
#import "outlines.typ": theseus-outline
|
||||||
|
|
||||||
#let pirat-color = (
|
#let pirat-color = (
|
||||||
black: rgb("#000000"),
|
black: rgb("#000000"),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#import "./icons.typ": *
|
#import "./icons.typ": *
|
||||||
|
|
||||||
|
|
||||||
#let static_outline(
|
#let static-outline(
|
||||||
small_icon_size: 100pt,
|
small_icon_size: 100pt,
|
||||||
big_icon_size: 200pt,
|
big_icon_size: 200pt,
|
||||||
) = context {
|
) = context {
|
||||||
|
|
@ -83,7 +83,7 @@
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#let dexhunter_outline(
|
#let dexhunter-outline(
|
||||||
small_icon_size: 100pt,
|
small_icon_size: 100pt,
|
||||||
big_icon_size: 200pt,
|
big_icon_size: 200pt,
|
||||||
) = context {
|
) = context {
|
||||||
|
|
@ -221,10 +221,22 @@
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#let theseus_outline(
|
#let theseus-outline(
|
||||||
small_icon_size: 60pt,
|
small_icon_size: 60pt,
|
||||||
big_icon_size: 90pt,
|
big_icon_size: 90pt,
|
||||||
|
stage: "theseus",
|
||||||
) = context {
|
) = context {
|
||||||
|
let stages = (
|
||||||
|
"static-only",
|
||||||
|
"static-vs-dyn",
|
||||||
|
"theseus",
|
||||||
|
"theseus-no-static",
|
||||||
|
"theseus-vs-static",
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
stage in stages,
|
||||||
|
message: "theseus-outline stage arg must be in: " + repr(stages)
|
||||||
|
)
|
||||||
let width = (
|
let width = (
|
||||||
small_icon_size * 4 +
|
small_icon_size * 4 +
|
||||||
big_icon_size * 3 +
|
big_icon_size * 3 +
|
||||||
|
|
@ -352,12 +364,55 @@
|
||||||
height: height,
|
height: height,
|
||||||
//stroke: black,
|
//stroke: black,
|
||||||
{
|
{
|
||||||
|
if stage == "static-only" {
|
||||||
|
place(
|
||||||
|
left+bottom,
|
||||||
|
dx: app2_pos.at(0),
|
||||||
|
dy: app2_pos.at(1),
|
||||||
|
app
|
||||||
|
)
|
||||||
|
/*
|
||||||
|
arrow(
|
||||||
|
stroke: arrow_width + black,
|
||||||
|
(
|
||||||
|
app_static_pos.at(0) + app_size.width + arrow_gap,
|
||||||
|
app_static_pos.at(1) - app_size.height / 2
|
||||||
|
),
|
||||||
|
(
|
||||||
|
analyser_pos2.at(0) - arrow_gap,
|
||||||
|
analyser_pos2.at(1) - analyser_size.height / 2
|
||||||
|
)
|
||||||
|
)*/
|
||||||
|
} else {
|
||||||
place(
|
place(
|
||||||
left+bottom,
|
left+bottom,
|
||||||
dx: app_pos.at(0),
|
dx: app_pos.at(0),
|
||||||
dy: app_pos.at(1),
|
dy: app_pos.at(1),
|
||||||
app
|
app
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if stage not in ("static-only") {
|
||||||
|
place(
|
||||||
|
left+bottom,
|
||||||
|
dx: phone_pos.at(0),
|
||||||
|
dy: phone_pos.at(1),
|
||||||
|
phone
|
||||||
|
)
|
||||||
|
arrow(
|
||||||
|
stroke: arrow_width + black,
|
||||||
|
(
|
||||||
|
app_pos.at(0) + app_size.width + arrow_gap,
|
||||||
|
app_pos.at(1) - app_size.height / 2
|
||||||
|
),
|
||||||
|
(
|
||||||
|
phone_pos.at(0) - arrow_gap,
|
||||||
|
phone_pos.at(1) - phone_size.height / 2,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if stage not in ("static-only", "static-vs-dyn") {
|
||||||
place(
|
place(
|
||||||
left+bottom,
|
left+bottom,
|
||||||
dx: dex_pos0.at(0),
|
dx: dex_pos0.at(0),
|
||||||
|
|
@ -376,24 +431,7 @@
|
||||||
dy: rprt_pos2.at(1),
|
dy: rprt_pos2.at(1),
|
||||||
rprt
|
rprt
|
||||||
)
|
)
|
||||||
place(
|
|
||||||
left+bottom,
|
|
||||||
dx: phone_pos.at(0),
|
|
||||||
dy: phone_pos.at(1),
|
|
||||||
phone
|
|
||||||
)
|
|
||||||
|
|
||||||
arrow(
|
|
||||||
stroke: arrow_width + black,
|
|
||||||
(
|
|
||||||
app_pos.at(0) + app_size.width + arrow_gap,
|
|
||||||
app_pos.at(1) - app_size.height / 2
|
|
||||||
),
|
|
||||||
(
|
|
||||||
phone_pos.at(0) - arrow_gap,
|
|
||||||
phone_pos.at(1) - phone_size.height / 2,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
arrow(
|
arrow(
|
||||||
stroke: arrow_width + black,
|
stroke: arrow_width + black,
|
||||||
(arrow_1_x0, arrow_1_y0_0),
|
(arrow_1_x0, arrow_1_y0_0),
|
||||||
|
|
@ -460,6 +498,37 @@
|
||||||
dy: app2_pos.at(1),
|
dy: app2_pos.at(1),
|
||||||
app2
|
app2
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
if stage == "static-vs-dyn" {
|
||||||
|
arrow(
|
||||||
|
stroke: arrow_width + black,
|
||||||
|
(
|
||||||
|
app_pos.at(0) + app2_size.width / 2,
|
||||||
|
app_pos.at(1) + arrow_gap
|
||||||
|
),
|
||||||
|
(
|
||||||
|
app_pos.at(0) + app2_size.width / 2,
|
||||||
|
phone_pos.at(1) + arrow_gap * 2
|
||||||
|
),
|
||||||
|
(
|
||||||
|
app2_pos.at(0) + app2_size.width + arrow_gap,
|
||||||
|
phone_pos.at(1) + arrow_gap * 2
|
||||||
|
),
|
||||||
|
(
|
||||||
|
app2_pos.at(0) + app2_size.width + arrow_gap,
|
||||||
|
app2_pos.at(1) - app2_size.height / 2
|
||||||
|
),
|
||||||
|
(
|
||||||
|
analyser_pos.at(0) - arrow_gap,
|
||||||
|
analyser_pos.at(1) - analyser_size.height / 2,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if stage in (
|
||||||
|
"theseus",
|
||||||
|
"theseus-vs-static",
|
||||||
|
"static-only"
|
||||||
|
) {
|
||||||
arrow(
|
arrow(
|
||||||
stroke: arrow_width + black,
|
stroke: arrow_width + black,
|
||||||
(
|
(
|
||||||
|
|
@ -471,6 +540,13 @@
|
||||||
analyser_pos.at(1) - analyser_size.height / 2,
|
analyser_pos.at(1) - analyser_size.height / 2,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
if stage in (
|
||||||
|
"theseus",
|
||||||
|
"static-vs-dyn",
|
||||||
|
"theseus-vs-static",
|
||||||
|
"static-only"
|
||||||
|
) {
|
||||||
place(
|
place(
|
||||||
bottom+left,
|
bottom+left,
|
||||||
dx: analyser_pos.at(0),
|
dx: analyser_pos.at(0),
|
||||||
|
|
@ -494,6 +570,11 @@
|
||||||
dy: rprt2_pos.at(1),
|
dy: rprt2_pos.at(1),
|
||||||
rprt
|
rprt
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
if stage in (
|
||||||
|
"theseus",
|
||||||
|
"theseus-vs-static",
|
||||||
|
) {
|
||||||
arrow(
|
arrow(
|
||||||
stroke: arrow_width + black,
|
stroke: arrow_width + black,
|
||||||
(
|
(
|
||||||
|
|
@ -513,7 +594,34 @@
|
||||||
patcher_pos.at(1) + arrow_gap
|
patcher_pos.at(1) + arrow_gap
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if stage not in (
|
||||||
|
"static-only",
|
||||||
|
"static-vs-dyn",
|
||||||
|
"theseus",
|
||||||
|
"theseus-no-static",
|
||||||
|
) {
|
||||||
|
arrow(
|
||||||
|
stroke: arrow_width + black,
|
||||||
|
(
|
||||||
|
app_pos.at(0) + app_size.width/2,
|
||||||
|
app_pos.at(1) - app_size.height - arrow_gap,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
app_pos.at(0) + app_size.width/2,
|
||||||
|
analyser_pos2.at(1) - analyser_size.height / 2,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
analyser_pos2.at(0) - arrow_gap,
|
||||||
|
analyser_pos2.at(1) - analyser_size.height / 2,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if stage in (
|
||||||
|
"theseus-vs-static",
|
||||||
|
) {
|
||||||
place(
|
place(
|
||||||
bottom+left,
|
bottom+left,
|
||||||
dx: analyser_pos2.at(0),
|
dx: analyser_pos2.at(0),
|
||||||
|
|
@ -538,27 +646,13 @@
|
||||||
rprt3_pos.at(1) - rprt_size.height / 2,
|
rprt3_pos.at(1) - rprt_size.height / 2,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
arrow(
|
}
|
||||||
stroke: arrow_width + black,
|
|
||||||
(
|
|
||||||
app_pos.at(0) + app_size.width/2,
|
|
||||||
app_pos.at(1) - app_size.height - arrow_gap,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
app_pos.at(0) + app_size.width/2,
|
|
||||||
analyser_pos2.at(1) - analyser_size.height / 2,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
analyser_pos2.at(0) - arrow_gap,
|
|
||||||
analyser_pos2.at(1) - analyser_size.height / 2,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#set page(flipped: true)
|
#set page(flipped: true)
|
||||||
#set align(center+horizon)
|
#set align(center+horizon)
|
||||||
#dexhunter_outline()
|
#dexhunter-outline()
|
||||||
#static_outline()
|
#static-outline()
|
||||||
#theseus_outline()
|
#theseus-outline()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue