当前位置: 首页 > news >正文

2312llvm,用匹配器构建clang工具

原文

LibToolingLibASTMatchers构建工具

这里展示如何基于ClangLibTooling构建有用的源到源翻译工具.基础

步骤0:取Clang

因为ClangLLVM项目的一部分,因此你需要先下载LLVM的源码.ClangLLVM都在同一个git仓库中,在不同的目录下.更多见入门指南.

cd ~/clang-llvm
git clone https://github.com/llvm/llvm-project.git

接着,要取CMake构建系统和Ninja构建工具.

cd ~/clang-llvm
git clone https://github.com/martine/ninja.git
cd ninja
git checkout release
./bootstrap.py
sudo cp ninja /usr/bin/
cd ~/clang-llvm
git clone git://cmake.org/stage/cmake.git
cd cmake
git checkout next
./bootstrap
make
sudo make install

好.现在构建Clang!

cd ~/clang-llvm
mkdir build && cd build
cmake -G Ninja ../llvm -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" -DLLVM_BUILD_TESTS=ON  
# 允许测试,默认关闭.
ninja
ninja check       # Test LLVM only.
ninja clang-test  # Test Clang only.
ninja install

好,可以了.所有测试都应通过.
最后,想设置Clang为它自己的编译器.

cd ~/clang-llvm/build
cmake ../llvm

第二个命令打开配置ClangGUI.你需要设置CMAKE_CXX_COMPILER项.按"t"打开高级模式.向下滚动到CMAKE_CXX_COMPILER,并设置它为/usr/bin/clang++,或安装位置.
"c"配置,然后按"g"生成CMake的文件.
最后,最后一次运行ninja,你就完成了.

步骤1:创建ClangTool

创建最简单的ClangTool:语法检查器.虽然已有clang-check了.

首先,为工具创建新目录,并告诉CMake它存在.因为这不会是核心clang工具,它将在clang-tools-extra仓库中.

cd ~/clang-llvm
mkdir clang-tools-extra/loop-convert
echo 'add_subdirectory(loop-convert)' >> clang-tools-extra/CMakeLists.txt
vim clang-tools-extra/loop-convert/CMakeLists.txt

CMakeLists.txt应包含以下内容:

set(LLVM_LINK_COMPONENTS support)
add_clang_executable(loop-convertLoopConvert.cpp)
target_link_libraries(loop-convertPRIVATEclangASTclangASTMatchersclangBasicclangFrontendclangSerializationclangTooling)

完成后,Ninja可编译此工具.编译!在clang-tools-extra/loop-convert/LoopConvert.cpp中放置以下内容.
不同部件见LibTooling文档.

//声明`clang::SyntaxOnlyAction`.
#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
//声明`llvm::cl::extrahelp`.
#include "llvm/Support/CommandLine.h"
using namespace clang::tooling;
using namespace llvm;
//对所有命令行选项,自定义分类,这样只显示他们.
static llvm::cl::OptionCategory MyToolCategory("my-tool options");//`CommonOptionsParser`用与编译数据库和输入文件相关的常见命令行选项的`说明`声明`HelpMessage`.
//在所有工具中都有此帮助消息.static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
//之后可添加此`特定工具`的帮助消息.
static cl::extrahelp MoreHelp("\nMore help text...\n");
int main(int argc, const char **argv) {auto ExpectedParser = CommonOptionsParser::create(argc, argv, MyToolCategory);if (!ExpectedParser) {//对不支持的选项,优雅失败.llvm::errs() << ExpectedParser.takeError();return 1;}CommonOptionsParser& OptionsParser = ExpectedParser.get();ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList());return Tool.run(newFrontendActionFactory<clang::SyntaxOnlyAction>().get());
}

就这样!可通过从build目录运行ninja来编译新工具.

cd ~/clang-llvm/build
ninja

现在应可在源文件上运行在~/clang-llvm/build/bin中的语法检查器.试试!

echo "int main() { return 0; }" > test.cpp
bin/loop-convert test.cpp --

注意指定源文件后的两个破折号.在破折号之后传递编译器附加选项,而不是从编译数据库中加载它们,现在不需要选项.

Intermezzo:学习AST匹配器基础

Clang最近推出了,提供简单,强大且简洁方式来描述AST中的指定模式ASTMatcher库.

宏和模板提供支持的DSL实现匹配器(见ASTMatchers.h,这里),它提供了函数式语言常见的代数数据类型的感觉.

如,假设只想检查二元符号.有个叫binaryOperator的匹配器可完成:

binaryOperator(hasOperatorName("+"), hasLHS(integerLiteral(equals(0))))

它会与左侧正好是0字面加式匹配.不会与其他形式0(如"\0"NULL)匹配,但它与到0的扩展宏匹配.

匹配器也不会匹配调用"+"重载符号,因为有个单独的operatorCallExpr匹配器来处理重载符号.

有个AST匹配器来匹配AST的所有不同节点,缩小匹配器以仅匹配指定条件AST节点,及从一个AST节点取到另一个AST节点的遍历匹配器.

AST匹配器的完整列表

所有名词匹配器都描述了AST中的可绑定实体,以便找到匹配项时可引用它们.为此,只需在这些匹配器上调用bind方法,如:

variable(hasType(isInteger())).bind("intvar")

第2步:使用AST匹配器

好的,使用匹配器.先定义一个抓按零定义初化的新变量的所有语句的匹配器.从匹配所有for循环开始:

forStmt()

接着,要在循环的第一部分,指定声明单个变量,以便可扩展匹配器

forStmt(hasLoopInit(declStmt(hasSingleDecl(varDecl()))))

最后,可添加把变量初化为零的条件.

forStmt(hasLoopInit(declStmt(hasSingleDecl(varDecl(hasInitializer(integerLiteral(equals(0))))))))

很容易阅读和理解匹配器定义(“匹配,init部分声明了一个按0字面初化的变量的循环”),但很难确定每个部分都是必要的.

注意,此匹配器不会匹配,初化为"\0",0.0,NULL或除0整数之外的零的变量的循环.
最后一步是给匹配器取个名字,并绑定ForStmt,因为想用它干活:

StatementMatcher LoopMatcher =forStmt(hasLoopInit(declStmt(hasSingleDecl(varDecl(hasInitializer(integerLiteral(equals(0)))))))).bind("forLoop");

定义了匹配器后,要添加更多助手来运行它们.匹配器与MatchCallback配对,并用MatchFinder对象注册,然后从ClangTool运行.
添加以下内容LoopConvert.cpp:

#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang;
using namespace clang::ast_matchers;
StatementMatcher LoopMatcher =forStmt(hasLoopInit(declStmt(hasSingleDecl(varDecl(hasInitializer(integerLiteral(equals(0)))))))).bind("forLoop");
class LoopPrinter : public MatchFinder::MatchCallback {
public :virtual void run(const MatchFinder::MatchResult &Result) {if (const ForStmt *FS = Result.Nodes.getNodeAs<clang::ForStmt>("forLoop"))FS->dump();}
};

并将main()更改为:

int main(int argc, const char **argv) {auto ExpectedParser = CommonOptionsParser::create(argc, argv, MyToolCategory);if (!ExpectedParser) {//对不支持的选项,优雅失败.llvm::errs() << ExpectedParser.takeError();return 1;}CommonOptionsParser& OptionsParser = ExpectedParser.get();ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList());LoopPrinter Printer;MatchFinder Finder;Finder.addMatcher(LoopMatcher, &Printer);return Tool.run(newFrontendActionFactory(&Finder).get());
}

现在,应该可重新编译,并运行代码以发现for循环.创建包含几个示例的新文件,并测试新手工作品:

cd ~/clang-llvm/llvm/llvm_build/
ninja loop-convert
vim ~/test-files/simple-loops.cc
bin/loop-convert ~/test-files/simple-loops.cc

步骤3.5:更复杂的匹配器

简单匹配器可发现for循环,但仍需要过滤掉更多循环.可用一些巧妙选择的匹配器来完成很大一部分剩余工作,但先要决定想要允许的属性.

如何表征可转换为基于区间语法的数组上的循环?大小为N的数组,基于区间的循环:
1,从0索引开始
2,连续迭代
3,在N-1索引处结束

已检查了
(1),因此还要添加的只是检查循环条件,以确保循环的索引变量N比较,并再次检查确保增量步骤只是递增相同变量.
(2)的匹配器很简单:要求在init部分,声明相同变量前增量或后增量.

可惜,不能写此匹配器.匹配器不包含比较两个任意AST节点,并确定是否相等的逻辑,因此最好就是匹配比允许更多,并额外与回调比较.
就可开始构建该子匹配器.可要求增量步骤一元增量,如下:

hasIncrement(unaryOperator(hasOperatorName("++")))

指定递增内容,引入了ClangAST的另一个怪癖:因为它们是引用变量声明的表达式,按DeclRefExpr(“声明引用式”)表示变量用法.

要找到引用指定声明的unaryOperator,可简单地给它添加第二个条件:

hasIncrement(unaryOperator(hasOperatorName("++"),hasUnaryOperand(declRefExpr())))

此外,可按仅在递增变量为整数时才限制匹配匹配器:

hasIncrement(unaryOperator(hasOperatorName("++"),hasUnaryOperand(declRefExpr(to(varDecl(hasType(isInteger())))))))

最后一步是,把标识附加到此变量,以便可在回调中提取它:

hasIncrement(unaryOperator(hasOperatorName("++"),hasUnaryOperand(declRefExpr(to(varDecl(hasType(isInteger())).bind("incrementVariable"))))))

添加这段代码到LoopMatcher定义中,并确保配备了新匹配器的程序,只打印出按零初化声明单个变量的循环,并有由某个变量一元增量组成的增量步骤.

现在,只需要添加一个匹配器,来检查for循环的条件变量部分是否与数组大小比较.只有一个问题:如果不查看循环主体,则不知道正在迭代的数组!

再次限制为,在匹配器中得到近似想要结果,在回调中填写细节.因此,从如下开始:

hasCondition(binaryOperator(hasOperatorName("<")))

确保左侧引用变量,且右侧有整数类型.

hasCondition(binaryOperator(hasOperatorName("<"),hasLHS(declRefExpr(to(varDecl(hasType(isInteger()))))),hasRHS(expr(hasType(isInteger())))))

为什么?因为它不管用.在test-files/simple.cpp中提供的三个循环中,没有一个有匹配条件.快速查看第一个由上一个循环转换迭代生成的for循环的AST转储,展示了答案:

(ForStmt 0x173b240(DeclStmt 0x173afc80x173af50 "int i =(IntegerLiteral 0x173afa8 'int' 0)")<<>>(BinaryOperator 0x173b060 '_Bool' '<'(ImplicitCastExpr 0x173b030 'int'(DeclRefExpr 0x173afe0 'int' lvalue Var 0x173af50 'i' 'int'))(ImplicitCastExpr 0x173b048 'int'(DeclRefExpr 0x173b008 'const int' lvalue Var 0x170fa80 'N' 'const int')))(UnaryOperator 0x173b0b0 'int' lvalue prefix '++'(DeclRefExpr 0x173b088 'int' lvalue Var 0x173af50 'i' 'int'))(CompoundStatement ...

已知道声明增量都匹配,否则就不会转储该循环.原因在小于符号的第一个操作数(即LHS)的隐式转换,即引用i中,有个L值到R值的转换.

好的是,匹配器库,以ignoringParenImpCast提供了此问题方法,告诉匹配器,在继续匹配前,忽略隐式转换和括号.

调整条件符号,恢复期望匹配.

hasCondition(binaryOperator(hasOperatorName("<"),hasLHS(ignoringParenImpCasts(declRefExpr(to(varDecl(hasType(isInteger())))))),hasRHS(expr(hasType(isInteger())))))

在把绑定添加到想抓的式中,并把标识串提取到变量中后,完成了数组第2步.

步骤4:提取匹配的节点

目前,匹配器回调,还不是很有趣:它只是转储循环的AST.有时,需要更改输入源码.接着,使用上一步中绑定的节点.

MatchFinder::run()回调带MatchFinder::MatchResult&参数.感兴趣的是它的ContextNodes成员.

即,Clang使用ASTContext类,来表示AST的环境信息,但最重要的是多个操作需要ASTContext*参数.
直接有用的是匹配节点的集合,及如何提取它们.
因为绑定了三个(由ConditionVarName,InitVarNameIncrementVarName标识)变量,因此可用getNodeAs()成员函数取匹配节点.
LoopConvert.cpp中添加

#include "clang/AST/ASTContext.h"

更改LoopMatcher为:

StatementMatcher LoopMatcher =forStmt(hasLoopInit(declStmt(hasSingleDecl(varDecl(hasInitializer(integerLiteral(equals(0)))).bind("initVarName")))),hasIncrement(unaryOperator(hasOperatorName("++"),hasUnaryOperand(declRefExpr(to(varDecl(hasType(isInteger())).bind("incVarName")))))),hasCondition(binaryOperator(hasOperatorName("<"),hasLHS(ignoringParenImpCasts(declRefExpr(to(varDecl(hasType(isInteger())).bind("condVarName"))))),hasRHS(expr(hasType(isInteger())))))).bind("forLoop");

并将LoopPrinter::run更改为

void LoopPrinter::run(const MatchFinder::MatchResult &Result) {ASTContext *Context = Result.Context;const ForStmt *FS = Result.Nodes.getNodeAs<ForStmt>("forLoop");//不想转换头文件!if (!FS || !Context->getSourceManager().isWrittenInMainFile(FS->getForLoc()))return;const VarDecl *IncVar = Result.Nodes.getNodeAs<VarDecl>("incVarName");const VarDecl *CondVar = Result.Nodes.getNodeAs<VarDecl>("condVarName");const VarDecl *InitVar = Result.Nodes.getNodeAs<VarDecl>("initVarName");if (!areSameVariable(IncVar, CondVar) || !areSameVariable(IncVar, InitVar))return;llvm::outs() << "发现可能基于数组的循环.\n";
}

Clang用每个表示变量声明的变量关联VarDecl.因为每个声明的"规范"形式按地址都是唯一的,因此只需要确保(VarDecl的基类)ValueDecl不是NULL并比较规范声明.

static bool areSameVariable(const ValueDecl *First, const ValueDecl *Second) {return First && Second &&First->getCanonicalDecl() == Second->getCanonicalDecl();
}

如果执行到达LoopPrinter::run()的末尾,知道循环如下

for (int i= 0; i < expr(); ++i) { ... }

现在,只打印一条说明发现了个循环的消息.

顺便,尽管Clang已通过提供规范式方法,完成了艰苦工作,测试两个式是否相同,并不是那么简单:

static bool areSameExpr(ASTContext *Context, const Expr *First, const Expr *Second) {if (!First || !Second)return false;llvm::FoldingSetNodeID FirstID, SecondID;First->Profile(FirstID, *Context, true);Second->Profile(SecondID, *Context, true);return FirstID == SecondID;
}

此代码依赖两个llvm::FoldingSetNodeID间的比较.如Stmt::Profile()文档所示,Profile()成员函数,根据AST中的节点属性及其子节点属性构建节点描述.
然后,FoldingSetNodeID比较式的哈希来用.稍后需要areSameExpr.在添加其他循环test-files/simple.cpp上,运行新代码前,请试找出哪些是可转换的循环.

相关文章:

2312llvm,用匹配器构建clang工具

原文 用LibTooling和LibASTMatchers构建工具 这里展示如何基于Clang的LibTooling构建有用的源到源翻译工具.基础 步骤0:取Clang 因为Clang是LLVM项目的一部分,因此你需要先下载LLVM的源码.Clang和LLVM都在同一个git仓库中,在不同的目录下.更多见入门指南. cd ~/clang-llvm…...

12.26ARM作业

三个按键中断&#xff0c;控制对应灯亮灭 main.c #include "key_it.h"void delay(int ms){int i,j;for(i0;i<ms;i){for(j0;j<2000;j);}}int main(){all_led_init();key1_it_config();key2_it_config();key3_it_config();while(1){printf("do main...\n&…...

Objectiv-C设计模式笔记

文章目录 通用知识点对象创建原型模式定义适用场景示例 工厂方法定义适用场景示例 抽象工厂定义适用场景示例 生成器模式定义适用场景示例 单例模式定义适用场景示例 接口适配适配器定义适用场景示例 桥接定义适用场景示例 外观模式定义适用场景示例 对象去耦中介者定义适用场景…...

AI安全综述

1、引言 AI安全这个话题&#xff0c;通常会引伸出来图像识别领域的对抗样本攻击。下面这张把“熊猫”变“猴子”的攻击样例应该都不陌生&#xff0c;包括很多照片/视频过人脸的演示也很多。 对抗样本的研究领域已经具备了一定的成熟性&#xff0c;有一系列的理论来论述对抗样本…...

计算机网络概述(下)——“计算机网络”

各位CSDN的uu们你们好呀&#xff0c;今天继续计算机网络概述的学习&#xff0c;下面&#xff0c;让我们一起进入计算机网络概述的世界吧&#xff01;&#xff01;&#xff01; 计算机网络体系结构 数据传输流程 计算机网络性能指标 计算机网络体系结构 两个计算机系统必须高度…...

anaconda创建环境时安装默认的第三方库

感谢阅读 写作原因首先要有python解释器加入每次创建环境都需要的python库查看所有的默认安装库还原方法 写作原因 近期由于多个项目在多头并举&#xff0c;出现了每次安装环境都要重新打一遍指令的麻烦问题&#xff0c;出于节约时间从而提高工作效率的目的。我尝试了很多方法…...

STM32 cubeMX 光敏电阻AD转化实验

文章代码使用 HAL 库。 文章目录 前言一、光敏电阻介绍二、光敏电阻原理图解析三、ADC采样介绍1. 工作原理&#xff1a;2. ADC精度&#xff1a; 四、STM32 cubeMX配置ADC采样五、代码编写总结 前言 实验开发板&#xff1a;STM32F051K8。所需软件&#xff1a;keil5 &#xff0c;…...

AutoSAR(基础入门篇)3.2-Autosar中RTE的Ports【S/R】与【C/S】

目录 一、RTE的Ports【S/R】 1、特征 1.1、扮演SWCs和BSW的交流途径 1.2、其他特征...

安装kafka

静态文件安装&#xff08;单机&#xff09; 解压到指定目录&#xff08;解压到 /usr&#xff09; tar -zxf kafka_2.11-2.2.0.tgz -C /usr/ 到指定的解压目录下 cd /usr/kafka_2.11-2.2.0/ 配置主机名 查看是否配置了HOSTNAME vim /etc/sysconfig/network 没有就新增 HOSTNA…...

[MySQL] MySQL 高级(进阶) SQL 语句

一、高效查询方式 1.1 指定指字段进行查看 事先准备好两张表 select 字段1&#xff0c;字段2 from 表名; 1.2 对字段进行去重查看 SELECT DISTINCT "字段" FROM "表名"; 1.3 where条件查询 SELECT "字段" FROM 表名" WHERE "条件…...

创建springboot项目

SpringBoot 就相当于不需要配置文件的SpringSpringMVC。 常用的框架和第三方库都已经配置好了。 maven安装配置 管理项目依赖库的 maven的安装教程网上有很多&#xff0c;这里简单记录一下。 官网下载maven后并解压。 在其目录下添加一个目录repository 然后在conf目录下…...

“双十一、二” 业务高峰如何扛住?韵达快递选择 TDengine

小 T 导读&#xff1a; 为了有效处理每日亿级的数据量&#xff0c;早在 2021 年&#xff0c;韵达就选择用 TDengine 替代了 MySQL&#xff0c;并在三台服务器上成功部署和上线了 TDengine 2.0 集群。如今&#xff0c;随着 TDengine 3.0 版本的逐渐成熟&#xff0c;韵达决定将现…...

STM32L432+LIS3DH【加速度传感器】:端侧AI

一、搜集芯片资料 1.LIS3DHTR:加速度传感器 查找链接&#xff1a; https://www.st.com/zh/mems-and-sensors/lis3dh.html 2. NUCLEO-L432KC&#xff1a;芯片 查找连接&#xff1a; https://www.st.com/zh/evaluation-tools/nucleo-l432kc.html#cad-resources 1.原理图 引…...

VCG Mesh刚性旋转(变换矩阵)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 旋转矩阵如果从线性空间的角度来看,它类似于一个投影过程。假设坐标 P ( x 1 , y 1 , z 1 ) P(x_1,y_1,z_1)...

R语言【base】——system.file() 在软件包等中查找文件的完整文件名。

Package base version 4.3.2 Parameters system.file(..., package "base", lib.loc NULL,mustWork FALSE) 参数【...】&#xff1a;字符向量&#xff0c;指定某个软件包中的子目录和文件。默认情况下&#xff0c;没有值则返回软件包的根目录。不支持通配符。 …...

HTML制作暴雨特效

🎀效果展示 🎀代码展示 <body> <!-- partial:index.partial.html --> <canvas id="canvas-club">...

cesium实现区域贴图及加载多个gif动图

1、cesium加载多个gif动图 Cesium的Billboard支持单帧纹理贴图&#xff0c;如果能够将gif动图进行解析&#xff0c;获得时间序列对应的每帧图片&#xff0c;然后按照时间序列动态更新Billboard的纹理&#xff0c;即可实现动图纹理效果。为此也找到了相对于好一点的第三方库libg…...

blackbox黑盒监控部署(k8s内)tensuns专用

一、前言 部署在k8s中需要用到deployment、configmap、service服务 二、部署 创建存放yaml的目录 mkdir /opt/blackbox-exporter && cd /opt/blackbox-exporter 编辑blackbox配置文件&#xff0c;使用configmap挂在这 vi configmap.yaml apiVersion: v1 kind: Confi…...

“C语言“——scanf()、getchar() 、putchar()、之间的关系

scanf函数说明 scanf函数是对来自于标准输入流的输入数据作格式转换&#xff0c;并将转换结果保存至format后面的实参所指向的对象。 而const char*format 指向的字符串为格式控制字符串&#xff0c;它指定了可输入的字符串以及赋值时转换方法。 简单来说给一个打印格式(输入…...

Spring Boot3 Web开发技术

前期回顾 springboot项目常见的配置文件类型有哪些&#xff1f;哪种类型的优先级最高 yml properties yaml 读取配置文件里的数据用什么注解&#xff1f; value restful风格 RESTful 风格与传统的 HTTP 请求方式相比&#xff0c;更加简洁&#xff0c;安全&#xff0c;能隐…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

Caliper 配置文件解析:config.yaml

Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)

船舶制造装配管理现状&#xff1a;装配工作依赖人工经验&#xff0c;装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书&#xff0c;但在实际执行中&#xff0c;工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...

Mysql中select查询语句的执行过程

目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析&#xff08;Parser&#xff09; 2.4、执行sql 1. 预处理&#xff08;Preprocessor&#xff09; 2. 查询优化器&#xff08;Optimizer&#xff09; 3. 执行器…...

C++.OpenGL (20/64)混合(Blending)

混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...

为什么要创建 Vue 实例

核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...

Linux安全加固:从攻防视角构建系统免疫

Linux安全加固:从攻防视角构建系统免疫 构建坚不可摧的数字堡垒 引言:攻防对抗的新纪元 在日益复杂的网络威胁环境中,Linux系统安全已从被动防御转向主动免疫。2023年全球网络安全报告显示,高级持续性威胁(APT)攻击同比增长65%,平均入侵停留时间缩短至48小时。本章将从…...

高保真组件库:开关

一:制作关状态 拖入一个矩形作为关闭的底色:44 x 22,填充灰色CCCCCC,圆角23,边框宽度0,文本为”关“,右对齐,边距2,2,6,2,文本颜色白色FFFFFF。 拖拽一个椭圆,尺寸18 x 18,边框为0。3. 全选转为动态面板状态1命名为”关“。 二:制作开状态 复制关状态并命名为”开…...

Linux入门(十五)安装java安装tomcat安装dotnet安装mysql

安装java yum install java-17-openjdk-devel查找安装地址 update-alternatives --config java设置环境变量 vi /etc/profile #在文档后面追加 JAVA_HOME"通过查找安装地址命令显示的路径" #注意一定要加$PATH不然路径就只剩下新加的路径了&#xff0c;系统很多命…...