没有合适的资源?快使用搜索试试~ 我知道了~
FFFFF网址:http://www.elsevier.nl/locate/entcs/volume62.html14页一个类型保持的翻译ickle到Java(延伸摘要)D. 安科纳1,3,7 C。 安德森4楼 Damiani达米亚尼1,5,8S. Drossopoulou 2,4,9P. Giannini 1,6,10 E. Zucca祖卡1,3,11摘要我们提出了一个翻译从ickle(一个类似Java的语言,允许对象,可以改变他们的类在运行时)到普通的Java。将任何ickle类映射到Java类的转换是由将ickle对象与其Java对应物关联的不变量驱动翻译,这是证明,以保持双方的静态和动态语义的语言,是一个增强版本的前同一作者的建议。1介绍动态对象重新分类是一种允许对象在保留其身份的同时更改其类的功能。因此,对象的行为可以以基本的方式改变(例如,、非空列表变为空、图标化的获胜者被扩展等。)通过重新分类,而不是用新类的对象替换旧类的对象。缺乏重新分类原语一直被认为是面向对象编程的一个实际限制。ickle[3]是一种支持动态对象重新分类的类Java语言,旨在说明对象重新分类的功能,这些功能可以扩展1由MURST公司1999年项目“Teoria della Concorrenza,Linguaggi di Ordine Corpore eStructure di Tipi(TOSCA)”部分支助2由EPSRC部分支持(拨款编号:GR/L 76709)3地址:DISI,Uni versi t`adiGen ova,Gen ova,Ital y.4 地址:英国伦敦帝国学院。5地址:Dipartime n todiInformatica,Uni versita`diTorino,Torino,Ital y.6Address:DIS TA,Uni versi t`adelPiemo n te Orie ntale,Alessandria,Ital y.7电子邮件地址:davide@disi.unige.it8电子邮件地址:damiani@di.unito.it9电子邮件地址:scd@doc.ic.ac.uk10电子邮件:giannini@di.unito.it11电子邮件: zucca@disi.unige.it2001年由ElsevierScienceB出版。 诉 操作访问根据C CB Y-NC-N D许可证进行。2FFFFFFFFFF一种命令式、类型化、基于类、面向对象的语言。相对于其他动态对象重新分类的建议,ickle的一个突出特征(参见,例如,,[2,4,5]),是类型安全的,在这个意义上,任何类型正确的程序保证永远不会访问不存在的字段或方法。ickle本质上是Java的一个小子集(只有非抽象类、实例字段和方法、整数和布尔类型以及最小集合语句和表达式),并具有动态对象重新分类的功能。 特别是,一个不使用这些功能的ickle类是一个Java类。在本文中,我们考虑的问题,实现,从ickle设计,一个工作扩展与动态对象重新分类的一个真正的面向对象语言。特别是,我们表明,Java环境可以很容易地和自然地扩展,以这种方式来处理标准Java和Fickle课程为了证明这一点,我们定义了一个从ickle到普通Java的翻译。翻译被证明是保持静态和动态语义(也就是说,格式良好的ickle程序被翻译成格式良好的Java程序的行为“以同样的方式”)。此外,翻译是有效的,在某种意义上说,它为Java编译器的有效扩展提供了基础。这是通过以下事实来确保的:ickle类的翻译不依赖于所使用的类的实现,因此可以以单独的方式完成,即不需要它们的源代码,就像Java编译一样。这是因为转换所需的类型信息可以从存储在二进制文件中的类型信息中检索到。与同作者的前一版本[1]相比,本文所提出的翻译更简单,而且保留了类型。本文的组织如下:在第2节中,我们介绍了ickle。在第三节中,我们描述了翻译。 在第4节中,我们陈述了的翻译(静态和动态语义的保存)12,并说明了 与Java单独编译的翻译的兼容性。在结论中,我们总结了这项工作的相关性,并说明了相对于[1]中描述的翻译的优势。2语言Fickle本节并不打算详尽介绍该语言。我们在[3]中找到了一个完整的定义。该语言的语法在图1中指定。我们参考[3]定义了ickle的静态语义(ickle的类型系统可以很容易地适应作为翻译目标的Java子集)和在续集中使用的一些辅助函数在Fickle中,类定义可以在关键字state或root之前12将在即将发表的扩展文件中提供证明3Rp::=classclass::=[root|state]classcextendsc′{field_name_name}field::=t fmeth::=tm(t′x)[φ]{slreturne;}t::=boolean|int|Cφ::={c+}sl::=ss::={sl} |if(e)s1else s2|se;|这个!c、se::= var = e |e1.m(e2)|new c()e::= se|sval|var|这var::= x|e.Fsval::=真|虚假|null |nFig. 1. 《Fickle》具有以下含义:• 状态类是用来描述一个对象满足某些条件时的属性;当它不再满足这些条件时,它必须被显式地重新分类到另一个状态类。我们需要状态类来扩展根类或状态类。• 根类抽象于状态类。只有当C2是C1的唯一定义的根超类的子类时,状态类C1的对象才可以被重新分类为类C2。我们要求根类只扩展普通类,即。,既不是根也不是状态,类对于程序p的类c,如果c是状态类,则(p,c)表示c的根超类,否则表示c的根普通类C的对象的行为就像普通的Java对象一样,也就是说,永远不会被重新分类。然而,由于状态类是普通类的子类,绑定到C类型变量x的对象可以被重新分类。也就是说,如果C有两个状态子类C1和C2,并且x引用类C1的对象o,则可以重新分类为C2。状态或根类C的对象都是通过表达式new C()以通常的方式创建的。重新分类声明,这!!C,将其类设置为C,其中C必须是一个状态类,其静态类型的根类与此。重新分类操作保留根类中定义的字段的类型和值,删除其他字段,并添加根类中未定义的C字段,以通常的方式初始化它们4FF--F例2.1下面的ickle程序定义了类13P、R、S1和S2。public int f1;int m1(){R}{this.f1=1; return this.m2();} int m2(){R}{return this.f1;}}根类R扩展P{ }状态类S1扩展R{int m2(){R}{this!!S2; this.f2=this.f1; return this.f2;}static void main(String[] args){System.out.println(new S1().m1());}}状态类S2extendsR{ int f2;int m1(){return 3;}int m2(){R}{this.f2=1;return this.f1+this.f2;}}考虑例2.1中的程序重新分类直接由重新分类语句引起,就像这样!!类S1的方法m2的主体中的S2,或者间接地通过方法调用,如main的主体中的new S1().m1(),这反过来又导致调用类S1的方法m2。在类S1的方法m2开始时,接收者是类S1的对象,因此它只有场f1,而没有场f2。 在执行此之后!如果接收器属于S2类,则场f1保持其值,而F2现在已经可以使用了。变量(也就是字段和参数,因为为了简单起见,ickle没有局部变量)和方法的返回值必须用不是状态类的类型声明;我们称这些类型为非状态类型。因此,字段和参数可以表示确实改变类的对象,但这些改变并不改变它们的类型。类型可以是状态类的表达式只有创建表达式(new C())和this;此外,这可能会改变。在方法体之前的注释(如R)称为对象。与throws子句中的异常类似,异常列出了所有对象的根类,这些对象可以通过执行该方法来重新分类。不带任何对象的方法,比如类S2中的m1,不会导致任何重新分类。由非空对象注释的方法,如类S1中的m2,可以在它们的对象(在示例中为R)中重新分类类(的子类)一个带参数注释的方法只能被方法覆盖,13类S1包含main方法。为了简单起见,我们在ickle语法(图1)和翻译的形式定义(3.25FFFF用相同或更少的分量表示14。通过依赖于对象注释,ickle的类型和对象系统确保了重新分类不会导致对未为对象定义的字段或方法的访问。键入表达式(或语句)d程序p中类声明的上下文和环境γp,γ d:t ||C ||φ涉及三个组成部分:t是由d的求值(如果d是表达式)或void(如果d是语句)返回的值的类型,c是在d的求值之后的this的类型,φ保守地估计了d的求值对对象的重新分类效应。(See[3]关于打字规则。请注意,对象是由程序员显式声明的,而不是由编译器推断的。即使可以在实践中实现事件推断,通过允许程序员用比推断出的事件更多的事件来注释方法,可以实现方法重写的更大灵活性(类似于异常发生的情况3Fickle的Java翻译在本节中,我们将介绍翻译。我们首先给出对象编码的非正式概述(3.1节),然后给出形式定义(3.23.1对象编码这种转换是基于这样一种思想,即状态类c的每个对象o都可以在Java中由一对对象编码;我们称id为imp的身份对象,imp为id的实现对象。粗略地说,id提供了o的身份,imp提供了o的行为,所以对o的任何重新分类都会改变imp而不是id,并且方法调用由imp解决。因此,两个具有相同身份的实现者在不同的执行阶段表示相同的对象。一个不是状态类的实例的对象o不需要原则上要编码;然而,为了一致性,在这种情况下也采用上述相同种类的编码,使得在执行翻译后的程序对于任何ickle对象都有一个标识对象。注意,虽然状态类的ickle对象可以有多个实现者,比如和,但反过来不可能成立:如果和是ickle对象的翻译,那么id=idJ。对象的重新分类可以通过图2中的图表来举例说明。类根据以下两条规则进行翻译:14这意味着在类c的方法中添加新的对象不需要任何更改C的子类,但可能需要对其超类进行一些更改6O2SImpO1O3IDFIDF000FF身份实现者i1实现者i2图二. 物体的重新分类实现者i3• 每个ickle类(包括Object)都被转换为一个Java类(其实例是实现者);• 转换保留了继承层次。我们用例2.1中的类来说明上述情况。让我们有静态类型R。在指令return new S1();s所指的ickle对象在翻译中被编码,如图11所示。3,通过两个Java对象o1和o2,其中o2的字段imp指向o1(虚线)。变量s引用类S1的Java对象o1,与身份到S1到S2图三. 由s引用的Ficklefields:f1继承自P,id类型为Identity,继承自FickleObject类(见图4)。字段id和imp在转换中分别用于恢复对象的身份和实现者。 在这种情况下,field id指向一个Identity类的对象o 2,它只包含一个指向对象当前实现者的fieldimp(在这种特殊情况下,对象o 1由s本身引用)。重新分类后!!!S2类的Java对象o3被创建,IDIDIDImp重新分类Imp重新分类Imp7FF程序%1类% n类%1nclass Identity extendsObject{ FickleObject imp;Identity(FickleObject theImp){this.imp=theImp;}}class FickleObject扩展Object{ Identity id;FickleObject(){//创建实例id=newIdentity(this);}FickleObject(FickleObject oldImp){//重新分类对象id=oldImp.id;id.imp=this;}}见图4。 类Identity和FickleObject标识O2指向新对象O3。请注意,s引用的ickle对象的新实现者可以从先前的实现者o1中恢复,方法是访问其表示身份对象o2的id字段,然后选择o2的字段imp3.2程序翻译一个ickle程序p的翻译由p中声明的所有类的翻译组成。由于语句和表达式的翻译依赖于它们的类型,程序p作为参数传递给类的翻译函数。[[p]]=[[class]](p). . . [[class]] (p),其中 p=class. . . 类3.3类的翻译如前所述,每个转换后的类都扩展了类FickleObject。这样一个类的定义以及类恒等式的定义在图中给出。四、构造函数FickleObject()被调用,每当一个新的创建Fickle类,并将字段ID转换为新标识。另一方面,构造函数FickleObject(FickleObject oldImp)在对象被重新分类时被调用一个对象o需要被重新分类为状态类c(回想一下,在翻译中,除了Identity之外的每个类都是FickleObject的子类),并且由编码,它被转换为,其中impJ表示类c的新实现者(由c的适当构造函数提供;参见下面的定义)。构造函数的参数表示旧的实现者imp,从中可以恢复身份id,而impJ则由this表示。8F1RF1MM1n类字段被初始化,以便标识和新的实现者相互指向。每个ickle类c都被翻译成一个Java类,包含c的所有字段和方法声明的翻译,以及一对构造函数,分别用于创建实例和重新分类对象。字段和方法的转换与类的类型无关。然而,在状态类中重新分类对象的构造函数是不同的- 在其他的种类中,它们是不同的∆[[roo t]classcextendsc′{tf;. tf;meth. . . []](p)=classcextendsnam e(c′){[[t1f1;]]field(c). . . [[tmfm;]]field(c)[[met h1]]meth(p,c). . . [[methn]]meth(p,c)public void run(){public intfindDuplicate();f1=oldImp.f1;. fm=oldImp. fm;}}其中,如果c′=Object,则name(c′)=FickleObject,否则name(c′)=c′。[[stateclasscextendsc′{field1... 字段m甲基1 ... 甲基n}]]类(p)=classcextendsc′{[[field1]]field(c). . . [[fieldm]]field(c)[[met h1]]meth(p,c). . . [[methn]]meth(p,c)public void run(){c(R(p,c)oldImp){super(oldImp);}}更准确地说,在普通类和根类中定义的用于重新分类的构造函数c(coldImp),在调用超类中相应的构造函数之后,将c中声明的旧实现者(由oldImp表示)的所有字段复制到新实现者(由this表示)的相应字段中。 此步骤不在相应的构造函数中执行c((p,c)oldImp)在状态类中,因为根据ickle语义,只有根超类的字段才能通过重新分类来保留3.4翻译领域每个字段f的翻译都配备了一个静态方法f,用于将值v的赋值转换为对象o的字段f(见下面的3.7.3节),因为只有在对v求值之后才能正确地选择对象o的实现者。9领域stmts stmtstmts1 2stmt[[tf;]](c)=t f;static ttof(FickleObject anImp,tx){return((c)anImp. ID. x=x;}3.5方法翻译翻译方法包括翻译其主体。省略了省略项,而签名保持不变。由于语句和表达式的转换取决于它们的类型,程序p和环境γ定义了参数和this的类型,因此必须作为参数传递。 对应的翻译功能。[[tm(t′x)φ{slreturne;}]]甲基 (p,c)=tm(t′x){[[sl]]stmts(p,γ)return[[e]]expr(p,γ′);}statict callm(FickleObject anImp,t′x){return((c)anImp. ID. intn(x);}其中,γ=t′x,c this,γ′=t′x,c′this,p,γ≠sl:空||c′||.注意,用于翻译返回的表达式e的环境γ j可能与γ不同,因为sl的执行可能会对其重新分类。此外,每个方法m的翻译都配备了一个静态方法调用m,用于翻译接收者o上的m调用和参数x(参见下面的3.7.3节);实际上,只有在评估参数x之后才能正确选择o的实现者。3.6发言的翻译除了对象重新分类,所有语句都是通过替换其组成语句或子表达式来翻译的。符号γ[cthis]表示通过更新γ以将this映射到c而获得的环境。[[ssl]](p,γ)=[[s]](p,γ)[[sl]](p,γ′)其中p,γ≤s:空||C||且γ′=γ[cthis][[{sl}]]stmt(p,γ)={[[sl]]撑条 (p,γ)}[[if(e)selses]](p,γ)=if([[e]]expr(p,γ))[s1]]stmt(p,γ′)else[[s2]]stmt(p,γ′)其中p,γ∈:布尔值||C||,γ′=γ[c this][[se;]]stmt(p,γ)=[[se]]expr(p,γ);这!c;]]stmt(p,γ)=nnewc(this);将重新分类转换为类c包括调用类c的适当构造函数;这作为参数传递给构造函数 以便正确地初始化新实现器的字段10exprexprexprexpr3.7表达的翻译在翻译中保留了表达式的类型。这是正式的节。四、3.7.1值、变量赋值和对象创建翻译是直截了当的。[[sval]](p,γ)=Δsval[[x=e]](p,γ)=x=[[e]](p,γ)[[newc()]] (p,γ)=nnewc()3.7.2参数、this和字段选择在我们的编码中,为了访问对象的当前实现者,我们必须选择当前由object.例如,参数x不能简单地转换为自身,因为x可能引用一个过时的实现者。请注意,对于int和boolean类型的参数和字段,不会出现此问题。(t)x. ID. imp),如果t是类[[x]]expr(p,γ)=[[this]][[expr否则,(p,γ)=n((c)id. imp)n(t)[[e]] expr(p,γ). f. ID. imp),如果t是类e.f]]expr(p,γ)=([[e]]expr(p,γ).f,否则其中p,γ<$x:t|||| ,p,γ,这:c|||| ,和p,γεe.f:t||||.选择后需要向下转换,因为fieldimp的类型为FickleObject。3.7.3字段赋值和方法调用由e1的平移表示的对象的字段f(或方法m)通过其标识的实现者来访问然而,e2可以重新分类对象,因此选择id。imp只有在评估e2的翻译之后才是正确的。这是通过调用与字段和方法相关联的辅助静态方法来实现的。[[e1.f= e2]]expr(p,γ)=ε((c′ ′)c. tof([[e1]]expr(p,γ),[[e2]]expr(p,γ′)[[e1.m(e2)]]expr(p,γ)=εc. callm([[e1]]expr(p,γ),[[e2]]expr(p,γ′))其中p,γ ∈1:c′||c′′||,γ′= γ[c′′this],p,γ′∈ e2:c′′||||φ和c = φ@pc′必须调用静态方法的类c是通过应用函数φ ={c1,.,cn}转换为表达式e1的静态类型cJ:11′ < $ci如果R(p,c′)=ci,其中i∈ 1,.,n{c1,., cn}@pc=拉克否则事实上,如果执行e2会将e1表示的对象重新分类,那么域f和方法m必须在R(p,CJ)而不是CJ中搜索。另一种可能性是引入局部变量来存储中间结果,而不是使用静态方法,但在这种情况下,我们将从表达式的翻译中获得语句例3.1例2.1中的程序翻译如下。int f1; intstatic int tof1(FickleObject anImp,int x){return((P)anImp.id.imp).f1=x;} int m1(){P.tof1(P)id.imp),1);returnP.callm2(P)id.imp));}static int callm1(FickleObject anImp){return((P)anImp.id.imp).m1();}int m2(){return((P)id.imp).f1;}static int callm2(FickleObject anImp){return((P)anImp.id.imp). m2();} P(){}P(P oldImp){super(oldImp); f1=oldImp.f1;}}classR extends P{ R(){}R(Rint findDuplicate();}}classS1 extendsR{int m2(){new S2(this); S2.tof2(S2)id.imp),((S2)id.imp).f1);return((S2)id.imp).f2;} static int callm2(FickleObject anImp){return((S1)anImp.id.imp).m2();}static void main(String[] args){System.out.println(R.callm1(new S1();} S1(){}{super(nums,nums);}}{int f2;static int tof2(FickleObject anImp,int x){return((S2)anImp.id.imp).f2=x;} int m1(){return 3;}static int callm1(FickleObject anImp){return((S2)anImp.id.imp). m1();}int m2(){S2.tof2(S2)id.imp),1); return((S2)id.imp).f1+((S2)id. imp).f2;} static int callm2(FickleObjectanImp){return(S2)anImp.id.imp).m2();} S2(){}{super(nums,nums);}}12FF►≈►≈4翻译的性质在这一节中,我们形式化前面提到的翻译的属性。4.1保持静态正确性定理4.1对于任意的Fickle程序p,如果p是良型的(在Fickle中),则[[p]] prog是类型良好的(Java中)。为了被证明,该定理的主张必须扩展到p的所有子项,从而扩展到所有类型判断。加强的索赔可以通过归纳的类型规则证明该转换在以下意义上保留类型:如果ickle表达式e具有类型tw.r.t.一个程序p和一个环境γ,那么e被翻译成一个表达式eJ,它的类型为tw.r.t.[p]和γ。4.2动态语义我们考虑的语言ickle的语义是在[3]中介绍的。这种语义将成对的表达式和存储重写为成对的值(或异常nullPntrExc,指示对空对象的引用),并存储。用v表示的值是布尔值、整数或地址,用i表示。存储映射参数和接收器this到值和映射地址到对象。 对象是字段和由它们所属的类标记的值之间的映射:[[f 1:v1,.,[v r:vr]]c.判断,v,σJ表示e在存储器σ中的估值w.r.t. p产生值v并将存储修改为σJ。为了说明语义正确性结果,我们引入值p,σ,σJv vJ之间的关系,即σJ中的vJ是σw.r. t中vp.对于基元值来说,这种关系是同一性,对于地址来说,它意味着地址所引用的对象是一个对象对另一个对象的转换。这个关系导出了存储器pσ σJ之间的关系,它表示存储器σJ是存储器σ的σ中的c与σJ中的对象oJ相关。定理4.2对于一个好类型的程序p,一个好类型的表达式e,存储σ0和σ1使得p<$σ0<$σ1,e,σ0<$pv,σ0J蕴涵[[e]],σ1<$[[p]]vJ,σ1J其中p<$σJ0<$σJ1和p,σJ0,σJ1<$v<$vJ。通过对e,σ0∈pv,σ0J的推导,归纳出了σ 0 ∈ pv,σ0 j的表达式.4.3支持单独编译对于任意的Fickle程序p,设classes(p)表示定义在p,并且,对于类(p)中的每个类c,depp(c)c的所有依赖关系的集合,13FF也就是说,c的所有超类和c使用的所有类(直接或间接)(我们省略了琐碎的形式定义)。此外,让stripc是Fickle程序上的函数,定义如下:stripc(cld1. cldn)= strip(cld1). strip(cldm),其中类(cld 1. cld m)= depp(c)且m ≤ nstri p([root|state]classcextendsc′{fieldstrings h})=[根|state]classcextendsc′{fieldstrip(meth)}strip(meth1. methn)= strip(meth1). strip(methn)strip(t m(t′x)φ{slreturne;})=t m(t′x)φ{returnv(t);}如果t=boolean,则返回false其中v(t)=0 ift=int空值否则下面的定理表明,一个可访问类c的翻译只依赖于c的主体和所有依赖项的类型信息(即类类型、父类、方法头和字段声明)。这些信息存储在一个常规的Java类文件中,因此,当只有其他(Fickle)类的二进制文件可用时,也可以成功地执行c的翻译。注意,这意味着Fickle语言支持单独的编译,但并不意味着Fickle和Java代码之间的兼容性。定理4.3对任意Fickle程序p和Fickle类的cld1声明c,如果[[cld1]]cld(p)=cld2,则[[cld1]]cld(stri pc(p))=cld2。5结论我们定义了一种从ickle(一种支持动态对象重新分类的类Java语言)到普通Java的翻译,并证明了这种翻译在保持静态和动态语义的意义上表现良好。这是一个很好的理论结果,为了确保这些属性,我们能够确定一些不变量,这些不变量被证明是一个非常有用的翻译指南翻译改进了以前的一个由相同的作者在[1]中介绍。在[1]的翻译中,对象的编码是一对对象,其中w是i的包装对象,i是w的实现对象。为了保持原始程序的层次结构,程序变量所引用的包装器是根类的,产生以下内容15除了root和state这两种类型之外,类文件格式可以很容易地扩展以存储这条新信息。16此属性不依赖于Java对反射的支持14FF问题(i) 类型被保留到状态类。 即如果如果表达式e的类型为t,并且t不是状态类,则它的转换类型相同,否则它的类型为tJ,其中tJ是t的根超类。(ii) 一个状态类的根超类和普通超类的域是重复的。当前的翻译解决了这两个问题,使类的翻译更加统一(正如我们从对象的创建中所看到的那样)。然而,在前面的翻译中,不属于状态类的对象被编码为,所以没有创建额外的对象,在这个新的转换中,这样的对象由编码,其中id是身份对象。另一种实现ickle(或者更一般地,支持对象动态重新分类的面向对象语言)的方向可以是直接的方式,通过操纵对象布局或对象查找表。引用[1] 安科纳,D., C. Anderson,F. Damiani,S. Drossopoulou,P. Giannini和E. Zucca,An effective translation of Fickle into Java(extended abstract),in:ICTCS210-230[2] 钱伯斯,C.,Predicate Classes,in:ECOOP268-296。[3] Drossopoulou , S. , F. Damiani , M. Dezani-Ciancaglini 和 P. Giannini ,Fickle:Dynamic object re-classification,in:ECOOP130- 149[4] Ernst,M. D、C. Kaplan和C. Chambers,Predicate Dispatching:A UniFied Theory of Dispatch,in:ECOOP186-211[5] 塞拉诺,M.,Wide Classes,in:ECOOP391-415
下载后可阅读完整内容,剩余1页未读,立即下载
cpongm
- 粉丝: 5
- 资源: 2万+
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
- 基于Python和Opencv的车牌识别系统实现
- 我的代码小部件库:统计、MySQL操作与树结构功能
- React初学者入门指南:快速构建并部署你的第一个应用
- Oddish:夜潜CSGO皮肤,智能爬虫技术解析
- 利用REST HaProxy实现haproxy.cfg配置的HTTP接口化
- LeetCode用例构造实践:CMake和GoogleTest的应用
- 快速搭建vulhub靶场:简化docker-compose与vulhub-master下载
- 天秤座术语表:glossariolibras项目安装与使用指南
- 从Vercel到Firebase的全栈Amazon克隆项目指南
- ANU PK大楼Studio 1的3D声效和Ambisonic技术体验
- C#实现的鼠标事件功能演示
- 掌握DP-10:LeetCode超级掉蛋与爆破气球
- C与SDL开发的游戏如何编译至WebAssembly平台
- CastorDOC开源应用程序:文档管理功能与Alfresco集成
- LeetCode用例构造与计算机科学基础:数据结构与设计模式
- 通过travis-nightly-builder实现自动化API与Rake任务构建
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功