使用交叉联接

mikel阅读(788)

使用交叉联接
没有 Where 子句的交叉联接将产生联接所涉及的表的笛卡尔积。第一个表的行数乘以第二个表的行数等于笛卡尔积结果集的大小。下面是 Transact-SQL 交叉联接示例:
USE pubs
Select au_fname, au_lname, pub_name
FROM authors CROSS JOIN publishers
orDER BY au_lname DESC
结果集包含 184 行(authors 有 23 行,publishers 有 8 行;23 乘以 8 等于 184)。
不过,如果添加一个 Where 子句,则交叉联接的作用将同内联接一样。例如,下面的 Transact-SQL 查询得到相同的结果集:
USE pubs
Select au_fname, au_lname, pub_name
FROM authors CROSS JOIN publishers
Where authors.city = publishers.city
orDER BY au_lname DESC
— 或
USE pubs
Select au_fname, au_lname, pub_name
FROM authors INNER JOIN publishers
ON authors.city = publishers.city
orDER BY au_lname DESC

C#的反射方法

mikel阅读(688)

using System;
namespace Webtest
…{
public interface interface1
…{
int add();
}
public class ReflectTest:interface1
…{
public String Write;
private String Writec;
public String Writea
…{
get
…{
return Write;
}
set
…{
Write = value;
}
}
private String Writeb
…{
get
…{
return Writec;
}
set
…{
Writec = value;
}
}
public ReflectTest()
…{
this.Write = “Write”;
this.Writec = “Writec”;
}
public ReflectTest(string str1,string str2)
…{
this.Write = str1;
this.Writec = str2;
}
public string WriteString(string s,int b)
…{
return “欢迎您,” + s + “—” + b; ;
}
public static string WriteName(string s)
…{
return “欢迎您光临,” + s;
}
public string WriteNoPara()
…{
return “您使用的是无参数方法”;
}
private string WritePrivate()
…{
return “私有类型的方法”;
}
public int add()
…{
return 5;
}
}
}
然后,建立再建立一个项目引入该HelloWorld.dll,
using System;
using System.Threading;
using System.Reflection;
class Test
…{
delegate string TestDelegate(string value,int value1);
static void Main()
…{
//Assembly t = Assembly.LoadFrom(“HelloWorld.dll”); 与下面相同的效果
Assembly t = Assembly.Load(“HelloWorld”);
//**********************************************************************
foreach (Type aaa in t.GetTypes())
…{
//Console.Write(aaa.Name); //显示该dll下所有的类
}
//**********************************************************************
Module[] modules = t.GetModules();
foreach (Module module in modules)
…{
//Console.WriteLine(“module name:” + module.Name);//显示模块的名字本例为”HelloWorld.dll”
}
//**********************************************************************
Type a = typeof(Webtest.ReflectTest);//得到具体的类的类型,和下面一个效果
//Type a = t.GetType(“Webtest.ReflectTest”);//
//Console.Write(a.Name);
//**********************************************************************
string[] bb =…{ “aaaa”, “bbbbb” };
object obj = Activator.CreateInstance(a,bb); //创建该类的实例,后面的bb为有参构造函数的参数
//object obj = t.CreateInstance(“Webtest.ReflectTest”);//与上面方法相同
//**********************************************************************
MethodInfo[] miArr = a.GetMethods();
foreach (MethodInfo mi0 in miArr)
…{
//Console.Write(mi0.Name); //显示所有的共有方法
}
//**********************************************************************
MethodInfo mi = a.GetMethod(“WriteString”);//显示具体的方法
object[] aa=…{“使用的是带有参数的非静态方法”,2};
string s = (string)mi.Invoke(obj,aa); //带参数方法的调用
MethodInfo mi1 = a.GetMethod(“WriteName”);
String[] aa1 =…{“使用的是静态方法”};
string s1 = (string)mi1.Invoke(null, aa1); //静态方法的调用
MethodInfo mi2 = a.GetMethod(“WriteNoPara”);
string s2 = (string)mi2.Invoke(obj, null); //不带参数的方法调用
MethodInfo mi3 = a.GetMethod(“WritePrivate”,BindingFlags.Instance | BindingFlags.NonPublic);
string s3 = (string)mi3.Invoke(obj, null); //私有类型方法调用
//Console.Write(s3);
//**********************************************************************
PropertyInfo[] piArr = a.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
foreach (PropertyInfo pi in piArr)
…{
//Console.Write(pi.Name); //显示所有的属性
}
//**********************************************************************
PropertyInfo pi1=a.GetProperty(“Writea”);
//pi1.SetValue(obj, “Writea”, null);
//Console.Write(pi1.GetValue(obj,null));
PropertyInfo pi2 = a.GetProperty(“Writeb”, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
pi2.SetValue(obj, “Writeb”, null);
//Console.Write(pi2.GetValue(obj, null));
FieldInfo fi1 = a.GetField(“Write”);
//Console.Write(fi1.GetValue(obj));
//**********************************************************************
ConstructorInfo[] ci1 = a.GetConstructors();
foreach (ConstructorInfo ci in ci1)
…{
//Console.Write(ci.ToString()); //获得构造函数的形式
}
ConstructorInfo asCI = a.GetConstructor(new Type[] …{ typeof(string), typeof(string) });
//Console.Write(asCI.ToString());
//**********************************************************************
Webtest.interface1 obj1 = (Webtest.interface1)t.CreateInstance(“Webtest.ReflectTest”);
Webtest.ReflectTest obj2 = (Webtest.ReflectTest)t.CreateInstance(“Webtest.ReflectTest”);
//Console.Write(obj1.add());典型的工厂模式
//**********************************************************************
foreach (Type tt in t.GetTypes())
…{
if (tt.GetInterface(“interface1”)!=null)
…{
Webtest.interface1 obj3 = (Webtest.interface1)Activator.CreateInstance(a);
//Console.Write(obj3.add());
}
}
//**********************************************************************
TestDelegate method = (TestDelegate)Delegate.CreateDelegate(typeof(TestDelegate), obj, “WriteString”);
//动态创建委托的简单例子
//Console.Write(method(“str1”, 2));
//**********************************************************************
ConstructorInfo asCI1 = a.GetConstructor(new Type[0]);
Webtest.ReflectTest obj5 = (Webtest.ReflectTest)asCI1.Invoke(null);
//通过无参构造函数实例化的方法
//Console.Write(obj5.Writea);
ConstructorInfo asCI2 = a.GetConstructor(new Type[] …{ typeof(string), typeof(string) });
//通过有参构造函数实例化的方法
Webtest.ReflectTest obj6 = (Webtest.ReflectTest)asCI2.Invoke(bb);
Console.Write(obj6.Writea);
//**********************************************************************
Console.Read();
}
}

Flex介绍很全很详细

mikel阅读(739)

一,概述
二,Flex开发环境
三,RIA的发展和Flex的版本历史
四,使用FLEX BUILDER 2进行应用开发步骤
五,Flex编程语言
六,MXML标记语言
七,ActionScript编程语言
八,Flex面向对象编程
九,Flex的安全机制
十,Flex程序性能的优化
十一,Flex与Flash
十二,Flex社区的开源的组件库项目
参考
地址:
http://www.deepcast.net/wiki/ow.asp?p=Flex&revision=61#h3

Linq To Sql

mikel阅读(904)

 Linq To SQL是Microsoft开发的针对解决data!=object问题的新技术。在笔者的一系列的文章中,对它已经做了大量的介绍。现在,笔者将从经验的角度,谈谈它的优劣。
  1、Linq To SQL的优点
  在Linq To SQL推出之前,我们只是把sql语句形成一个string,然后,通过ado.net传给sql server,返回结果集.这里的缺陷就是,如果你sql语句写的有问题,只有到运行时才知道.而且并不所有的人都懂数据库的。Linq To SQl 在一切围绕数据的项目内都可以使用。特别是在项目中缺少sql server方面的专家时,Linq To SQl的强大的功能可以帮我们快速的完成项目。Linq To SQl的推出,是让大家从烦琐的技术细节中解脱出来,更加关注项目的逻辑。Linq To Sql的出现,大大降低了数据库应用程序开发的门楷,它实质是事先为你构架了数据访问层,势必将加快数据库应用程序的开发进度。Linq To Sql解放了众多程序员,让他们的把更多的精力放到业务逻辑以及code上,而不是数据库。对于初学者来讲,Linq To Sql可以让他们迅速进入数据库应用程序开发领域,节约了培训成本。
  Linq To SQl 的实现,是在ado.net和C#2.0的基础上的。它通过自动翻译sql语句,并把结果集创建成对象并返回。这里我们可以看出,发送到Sql Server端的sql语句是Linq To Sql自动生成的。这对不懂sql的人来说,无疑是个福音。第二,Linq To Sql语句是在编译期间就做检查的。而不是运行时检查。这样,那里出了问题,可以及时更改,而不是到了运行时才发现问题。第三,Linq To Sql是针对对象操作的,更符合今天的oo呼声。
  在Linq To SQl 之前,在java领域有Hibernate,在net领域有NHibernate技术,来实现object/relational 持久和查询服务。那和NHibernate比起来,它又有那些优势呢.第一,影射代码自动生成。VS2008提供了SqlMetal和OR Designer两个工具来完成此步骤。而在NHibernate中,你不得不自己手工写。第二,影射代码有更多的选择.NHibernate只能把数据库的信息配置在一个xml中,而Linq To Sql有两种方式,一个是放到xml中,我们称为Externl Mapping, 再一种就是以Attribute的形式,存在于各个property中。当然,笔者本人并没有使用过NHibernate,只是从资料上得到这些消息,所以无法给出更多的比较。
  2、Linq To Sql的缺点
  很久前,有个网友问到这么一个问题。他在界面上有个DataView,里面绑定了一些Column,然后他勾选那一列就按某列排序。其传回的参数是列的名字。然后问我该怎么用Dlinq 来实现。
  在以前拼接Sql语句的年代,这个很简单,一个” order by ” + string,想按什么排就按什么来排。而现在dlinq是用是一个对象的属性,已经不可能拼接了。我当时给他的答案是这样的。
以下是引用片段:
private void Methods(string orderId)
  {
   var q = db.Customers.Select(c=>c);
  
   switch(orderId)
   {
   case “ID”:
   q = q.OrderBy(c=>c.ID);
   break;
   case “Name”:
   q = q.OrderBy(c=>c.Name);
   break;
   default:
   break;
   }
  
   var result = q.ToList();
  }
  我那时也没有想出一个更好的方案来。而后告诉他去查下Compiled Query,说不定可以找到更方便的。后来我才在这个例子中,看到更方便的。
以下是引用片段:
var query =
   db.Customers.Where(“City == @0 and orders.Count >= @1”, “London”, 10).
   orderBy(“CompanyName”).
   Select(“New(CompanyName as Name, Phone)”);
  在这里OrderBy直接接收的就是列的名字。再仔细一看,好像Where里是Linq的语句哎,那OrderBy也该是linq语句。后来,我把CompanyName换成小写的,一跑过了。莫非真的是列的名字?出个难题吧。找了一个列名,是带空格的,重新来建这个工程.一跑,错了!把列名用中国扩号扩起来了,也是错了。咳,只是动态构造Expression Tree而已,永远都不能直接接收列的名字。这个例子看着是很简单,可不知道你有没有注意到它有一个80多k的Dynamic.cs文件。更有意思的事情是,它的名称空间是System.Linq.Dynamic.看样子,ms本来是打算把它加在.net3.5中吗.不晓得为什么放到了例子中了。这个名称空间下,其主要内容就是动态构造Expression Tree. 和Linq To Sql进阶系列(七)动态查询一文中的方法类似。只是,它还包含了解析字符串部分.
  从上面那个例子中,可以看出,Linq To Sql在这种动态构造语句时,比拼接sql麻烦很多。在Linq To Sql进阶系列(七)动态查询 一文中,笔者极力推荐使用object的查询。这符合Linq To Sql的设计原则。因为,它主要是为了解决data!=objects 的问题而产生的.它所有的操作均针对object,那就让我们使用object的查询吧.
  当然,依然会有人习惯拼接字符串.我并不认为这是个坏毛病。只是有点不符合oo思想而已。事实上,在Linq To Sql中,你依然可以使用拼接字符串的形式,而不使用它提供的Query Expression. 它提供了这么两个接口,一个是,db.ExecuteQuery(string sql); 另一个是,db.ExecuteCommand(string sql);这两个函数都是直接接收 sql语句的.习惯拼接的人,依然可以调用它们来实现你的程序。特别是第一个,其返回的就是一个对象的集合,多少还是有点oo思想的。
  看下面的例子:
以下是引用片段:
var products = db.ExecuteQuery(
   “Select [Product List].ProductID, [Product List].ProductName ” +
   “FROM Products AS [Product List] ” +
   “Where [Product List].Discontinued = 0 ” +
   “ORDER BY [Product List].ProductName;”
   ).ToList();
  它返回的就是product的集合。而不是什么dataset和datatable之类的。这里,你可以大胆的使用该函数继续拼接你的sql吧,再看下面这个:
以下是引用片段:
db.ExecuteCommand(“Update Products SET UnitPrice = UnitPrice + 1.00”);
  它在做批处理的时候,你想不用它,都不行.当然,你如果觉得性能不是问题的话,那就用submitchange方法来做更新好了。简单明了的说,Linq To Sql在批处理更新的时候,SubmitChange只会一个个的更新。浪费时间资源.而这个接口,恰好满足了批处理更新或删除的问题。从这两个例子,我们可以看出。没有任何方案是万能的。各有各的优点。
  3、Linq To Sql的性能
  Linq 的性能已经被好多人提及.Linq To Object 的性能大家讨论的比较多些.它确实比自己实现的查找要慢.但是当数据量特别大时,更多是时间是花在分配虚拟内存上了,那么他们的差别就不是那么明显了。 Linq To Sql是又如何提升性能的?第一,采用延迟加载(deferred loading)技术。语句是声明了,但是并不立即执行,而是在真正需要的时候才执行。第二,采用缓存技术。已经取到内存的数据,再依次提取时,会先从缓存中返回,而不是再次访问数据库。当然,笔者建议,不要对象的时候,没有必要使用Linq To Sql.比如,只是填充DataView或DataGrid时,返回dataset或datatable要比用Linq To Sql实现的快很多。
  结论:各种技术都有其自身的优点和缺点。使用什么样的技术,完全在于项目对性能和开发进度的要求,以及程序员自身的爱好有关。扬长避短,才是正道。

Python的学习资源

mikel阅读(894)

Python基本安装:
* http://www.python.org/ 官方标准Python开发包和支持环境,同时也是Python的官方网站
* http://www.activestate.com/ 集成多个有用插件的强大非官方版本,特别是针对Windows环境有不少改进;
Python文档:
* http://www.python.org/doc/current/lib/lib.html Python库参考手册。
* http://www.byteofpython.info/ 可以代替Tutorial使用,有中文译版的入门书籍。
* http://diveintopython.org/ 一本比较全面易懂的入门书,中文版翻译最近进步为很及时的5.4了。
* http://www.python.org/peps/pep-0008.html 建议采用的Python编码风格。
* http://doc.zoomquiet.org/ 包括Python内容的一个挺全面的文档集。
常用插件:
* http://www.pfdubois.com/numpy/ Python的数学运算库,有时候一些别的库也会调用里面的一些功能,比如数组什么的;
* http://www.pythonware.com/products/pil/ Python下著名的图像处理库Pil;
* http://simpy.sourceforge.net/ 利用Python进行仿真、模拟的解决方案;
* Matplotlib 据说是一个用来绘制二维图形的Python模块,它克隆了许多Matlab中的函数, 用以帮助Python用户轻松获得高质量(达到出版水平)的二维图形;
* http://www.amk.ca/python/code/crypto python的加解密扩展模块;
* http://cjkpython.i18n.org/ 提供与python有关的CJK语言支持功能:转码、显示之类。
* Psyco、Pyrex:两个用于提高Python代码运行效率的解决方案;
* Pyflakes、PyChecker、PyLint:都是用来做Python代码语法检查的工具。
* http://wxpython.sourceforge.net/ 基于wxWindows的易用且强大的图形界面开发包wxPython;
* http://www.pygame.org/ 用Python帮助开发游戏的库,也可以用这个来播放视频或者音频什么的,大概依靠的是SDL;
* http://starship.python.net/crew/theller/py2exe/ win下将Python程序编译为可执行程序的工具,是一个让程序脱离Python运行环境的办法,也可以生成Windows服务或者COM组件。其他能完成Python脚本到可执行文件这个工作的还有Gordon McMillan's Installer、Linux专用的freeze以及py2app、setuptools等。不过此类工具难免与一些模块有一些兼容性的问题,需要现用现测一下。
* 嵌入式数据库:BerkeleyDB的Python版,当然还有其他的好多。
* PEAK提供一些关于超轻量线程框架等基础性重要类库实现。
部分常用工具:
* http://www.scons.org/ Java有Ant这个巨火的构建工具,Python的特性允许我们构建更新类型的构建工具,就是scons了。
* Python Sidebar for Mozilla FireFox的一个插件,提供一个用来查看Python文档、函数库的侧边栏。
* IPython 很好用的Python Shell。wxPython发行版还自带了PyCrust、PyShell、PyAlaCarte和PyAlaMode等几个工具,分别是图形界面Shell和代码编辑器等,分别具有不同特点可以根据自己的需要选用。
* Easy Install 快速安装Python模块的易用性解决方案。
推荐资源:
* Parnassus山的拱顶 巨大的Python代码库,包罗万象。既可以从上面下载代码参考学习,同时也是与Python有关程序的大列表。
* Python号星际旅行船 著名Python社区,代码、文档、高人这里都有。
* faqts.com的Python程序设计知识数据库 Python程序设计知识库,都是与Python有关的程序设计问题及解决方法。
* 啄木鸟 Pythonic 开源社区 著名的(也可以说是最好的)国内Python开源社区。
代码示例:
* http://newedit.tigris.org/technical.htm Limodou的NewEdit编辑器的技术手册,讨论了一些关于插件接口实现、i18实现、wxPython使用有关的问题,值得参考。
其他东西:
* http://www.forum.nokia.com/main/0,,034-821,00.html Nokia居然发布了在Series 60系统上运行Python程序(图形界面用wxPython)的库,还有一个Wiki页是关于这个的:http: //www.postneo.com/postwiki/moin.cgi/PythonForSeries60 。Python4Symbian这个页面是记录的我的使用经验。
* pyre:使用Python完成高性能计算需求的包,真的可以做到么?还没研究。
* Parallel Python:纯Python的并行计算解决方案。相关中文参考页面
* Pexpect:用Python作为外壳控制其他命令行程序的工具(比如Linux下标准的ftp、telnet程序什么的),还没有测试可用程度如何。
* pyjamas:Google GWT的Python克隆,还处在早期版本阶段。
* Durus:Python的对象数据库。
有意思的东西:
* Howie:用Python实现的MSN对话机器人。
* Cankiri:用一个Python脚本实现的屏幕录像机。
参考资料
* ZDNET文章:学习Python语言必备的资源
* Pythonic Web 应用平台对比
* 在wxPython下进行图像处理的经验 (其实,仅使用wxPython也可以完成很多比较基础的图像处理工作,具体可以参照《wxPython in Action》一书的第12节)
* 通过win32扩展接口使用Python获得系统进程列表的方法
* 如何获得Python脚本所在的目录位置
* Python的缩进问题
* py2exe使用中遇到的问题
* idle的中文支持问题
* 序列化存储 Python 对象
Python IDE
我的IDE选择经验
* http://www.xored.com Trustudio 一个基于Eclipse的、同时支持Python和PHP的插件,曾经是我最喜欢的Python IDE环境,功能相当全了,不过有些细节不完善以致不大好用。
* http://pydev.sourceforge.net/ 另一个基于Eclipse的,非常棒的Python环境,改进速度非常快,现在是我最喜欢的IDE。
* http://www-900.ibm.com/developerWorks/cn/opensource/os-ecant/index.shtml 用 Eclipse 和 Ant 进行 Python 开发
* http://www.die-offenbachs.de/detlev/eric3.html ERIC3 基于QT实现的不错的PYTHON IDE,支持调试,支持自动补全,甚至也支持重构,曾经在Debian下用它,但图形界面开发主要辅助qt,我倾向wxpython,所以最后还是放弃了这个。
* http://www.scintilla.org/ 同时支持Win和Linux的源代码编辑器,似乎支持Python文件的编辑。
* http://boa-constructor.sourceforge.net/ 著名的基于WxPython的GUI快速生成用的Python IDE,但是开发进度实在太差了……
* http://pype.sourceforge.net/ 成熟的Python代码编辑器,号称功能介于EMACS和IDLE之间的编辑器。
* http://www.stani.be/python/spe SPE:号称是一个Full Featured编辑器,集成WxGlade支持GUI设计。

.NET企业级应用架构设计系列之应用服务器

mikel阅读(821)

其实很久以前就想写点关于架构设计方面的东西,一直以来都没有最终落到实处。正好这段时间在做一个WEB架构,决定把和架构设计有关的内容写成一个系列文章。算是回馈CSDN提供的各种免费服务,同时给初学架构设计的朋友一点小小的提示。在我工作的六年多时间里,除了第一年是纯粹编码以外,其余时间都在做和架构设计有关的工作,当然也还一直在写各种各样的代码。也就是说,我本人其实也只有不多的经验可以分享,所以文中肯定有一些观点不能让所有人都认同。如有异议,可以发邮件和我讨论。
1、架构设计是件容易的事情。
对于不熟悉的或者没有架构设计经验的程序员来说,架构设计可能看起来很神秘,不知道究竟该如何入手,也不清楚该注意些什么问题。其实架构设计也只是软件系统开发中的一个环节而已,整个软件系统的开发和维护以及变更还涉及到很多事情,包括技术、团队、沟通、市场、环境等等。在分析和设计架构的时候也是有很多套路和原则的,只要我们按照一些最基本最简单的思路和方向去分析问题,就能得到一个大体满足实际情况的架构。前面我曾写过两篇关于人脑思考复杂系统的文章:《破解复杂性》以及《和SOA一起对抗复杂性》。这是软件开发中的方法论问题,可以贯穿于软件开发的多个阶段,包括架构设计、编码实现、后期维护等。掌握一定的原则,对架构设计能带来事半功倍的效果。
2、架构设计不是件简单的事情。
虽然架构设计是件容易的事情,但也不是大多数没有架构设计经验的程序员想象中的画画框图那么简单。把几台服务器一摆,每一台服务器运行什么软件分配好,然后用网络连接起来,似乎每个企业级应用都是如此简间单单的几步。但现实生活中的软件系统实实在在可以用复杂大系统来形容,从规划、开发、维护和变更涉及到许许多多的人和事。架构设计就是要在规划阶段都把后面的事情尽量把握进来,要为稳定性努力,还要为可维护性、扩扩展性以及诸多的性能指标而思前想后。除了技术上的考虑,还要考虑人的因素,包括人员的组织、软件过程的组织、团队的协作和沟通等。也许你会说那些都是项目管理上的事,和架构设计没关系。但一个团队成员无法实现的架构没有任何实际意义。另外,现在是解决方案满天飞的时代,很多事情都可以用多种途径来解决,如何在五花八门的选项中间做出决定,可能让很多人都头疼过。自己的经验、团队的素质、领导的要求、环境的压力以及市场的变化,够折腾了。
3、架构设计需要方法论的指导
所以,总的说来,在正确的方法论指导下,复杂的架构设计其实是件容易的事情。这些方法论的思路包括,至上而下的分析,关注点分离,横向/纵向模块划分等。有时候觉得架构设计决策就像是浏览Google Earth,实际上反映的是一种自上而下的决策过程。对问题的分解是软件思维的基本素质,可以有横向分解、纵向分解以及两者的结合。能不能有效快速准确的分解问题,是软件开发人员需要首先训练的项目。另外,架构设计中图形化的工具非常有用,它能把系统的结构和运作机制以图形化的方式表达出来。也正因为这样才有了架构设计就是画框图的误会。再者,架构设计是一个工程性质的工作,对当事人的实际从业经验要求较高。只有对市场上的各种技术有较全面的了解之后才有可能设计出一个尽可能满足各种设计约束的架构。
这里说的技术选型实际上是指技术方向的选择,或者叫平台方案的选择,也或者叫技术路线等,总之是大方向的把握。假定项目背景是要做一个中型WEB系统,公司组建新的技术团队以及运营团队来运作。基于这个模糊的项目背景,看看我们能得到些什么。
首先我们想到的是目标系统的特征:
A) 稳定性及可服务性:这是对软件系统最基本的要求,为客户提供稳定的服务是业务开展的最基础的保证。这是和客户的耐心作战,是赢取客户和扩展业务纵深度的前提。很难想象有人会在一个不稳定的系统面前花费精力去做一件本该很容易的事情。
B) 整体性能及升级扩容余地:虽然很多时候对系统压力的担心是多余的,但系统架构必须有一定的应付突发事故的能力以及具备足够的升级扩容空间来满足潜在的业务扩张,不然总会有手忙脚乱的一天。系统性能是可服务性的一方面,而升级扩容空间是系统持续长期运作的保障。
C) 可维护性及可管理性:除开灵活的系统实现带来的可维护性,系统软件和硬件设备的选择同样对可维护性产生重要的影响。这需要结合团队的人员架构来共同考虑。在维护性和管理性方面的问题必然带来升级扩容和应对业务变化方面的巨大困难。
再者,我们会想想想在市面上的解决方案提供商都能给我们什么:
目前在WEB系统方面流行的主要是Java和.NET两个平台级的软件技术方向,它们之所以流行是因为在许许多多的场合被证明能保障较高的生产效率。两者提供的解决方案都表现不错,只不过可能达到相同的目标所带来的总体拥有成本不一样而已。也就是说,它们在解决方案特征方面差异不大。一些重点比对项参考如下:

结论来自某人才招聘网统计资料。
另外,我们会想想我们的人手,也就是说开发团队方面我们拥有哪些方面的人和技术。整个系统的开发需要团队的力量,架构设计和技术方向选型必须关注人的因素。只有合适的人做合适的事情才能产生预期的高位价值。人力资源和技术团队架构在选择方向上起重要作用,需要结合人力资源市场的人才分布来思考。团队的历史经验对架构设计有很大影响,因为历史经验可以带来架构重用以及减少学习新技术和新工具的曲线。另外,团队的素质决定了软件过程能否顺利实施。所以,团队在架构设计中占有相当的分量。(这里,绝不要把架构设计孤立起来看待,它不只是规划期的事情,因为架构设计是对未来的把握,任何影响因素都要尽可能的考虑进来,特别是一些重大影响因素)。
上面的三点,是一个相互联系的整体,合在一起就成了一个三角形,我把它叫做“架构设计三角形”:

图1:架构设计三角形
接下来,结合一些指导原则,它们是许许多多软件系统成功失败的经验总结。其实,看起来也是非常直接的,很容易理解和接受:
A) 寻找最容易找到的技术人员组建团队。这样可以将人员流动对项目的影响减到最小,同时也可以快速的组建团队进入开发期。目前,IT人力资源市场上,Java方向和.NET方向是两个主要的企业级开发方向,多数IT人才也集中在这两块。
B) 使用成熟技术。显然,Java和.NET都是成熟的技术。在软件解决方案方面都有各自的长处和优点,但对企业级应用而言都可以胜任。通过这几年的发展,. NET和Java都形成了丰富的社会技术资源。.NET经过1.0到3.5的发展,在企业级应用方面已经在实践中变得更加的成熟了。
C) 使用团队熟悉的技术。避免过于陡峭的学习曲线,可以把对开发人员的的要求降至更低。但这并不等于使用最简单的技术来完成复杂庞大的企业级应用,应该是在易学易用的前提下构造稳定可维护的目标系统。冒着危险选择不成熟的技术或者不熟悉的技术都只能得到一个漏洞百出的系统。
D) 侧重于开发期的规划与管理,开发稳定可维护的产品。对团队的素质侧重点放在沟通和编写稳定可维护代码的视角,避免目标系统走向紊乱而不可控的地步,从而导致最终失败的结局。
结合具体实际情况,结论很容易得出了。考虑的方向是:目标系统 + 技术团队 =〉 解决方案。详细的结论就不写了,我这次为项目选择的是.NET 2.0平台。
这里要说到的是关于三层架构中的应用服务器。对于电子商务网站来说,成熟的架构基本上都是采用分层式的。分层的结构一方面适合人脑的思维方式,另一方面在解决扩展性方面非常有效。目前市面上的各大解决方案提供商在电子商务和一般WEB应用领域都有相应的分层解决方案,软件架构设计在这一方面几乎不存在多少争议。

出于对可扩展性以及可维护性的考虑,对业务逻辑的解耦得到的便是应用服务器。这样的情况在软件系统中非常常见,在任何情况下都能通过额外一层间接来获得灵活性,但可能会引入潜在的性能问题。在Windows Server 2008问世之前的.NET平台没有应用服务器的专门产品,架设应用服务器可以有多种可选的方案。常用的.NET应用服务器方案有:
A) 使用IIS驻留ASP.NET Web Service
B) 使用.NET Remoting提供远程对象
C) 使用Enterprise Services (COM+)提供跨进程对象调用
这三种方式中,采用TCP/Binary通道的.NET Remoting是三个方案中性能最好的,而采用ASP.NET Web Service则能提供更多的跨平台特性。在性能对比上,ASP.NET Web Service大约是TCP/Binary .NET Remoting的60%左右(和传递的数据有关),这中间的差异来自于网络传输上,ASP.NET Web Service会花费较多时间在大对象的数据串行化(Serialization)上。二进制格式的Remoting实现也提供了对象级(包括单件—— singleton特性)的支持,在性能要求占主导地位的方案中较多采用。
面对这个情况,最好的解决方法是在实现的时候提供一个更加灵活的架构来随时调整策略而让调整的代价保持在最小。也就是说,把系统的核心逻辑实现为一个对象群,然后通过facet方式以最小的努力适配到不同的调用接口上。当然,最初是以SOA的方式发布WEB服务给客户端调用,以尽量实现可移植的环境。如果发现这种模式成为了系统中的性能瓶颈,则改为TCP/Binary .NET Remoting的方式发布对象。
总的来说,应用服务器的架构是一个演化模型,可以随着需求和实际负载的变化实行平滑过渡。当应用服务器的横向扩展也无法应付更大的负载的时候,实际的性能瓶颈已经转移到了系统的其它地方,比如磁盘访问或接入带宽等。

Easy Setup Guide for BlazeDS: Zero to hero in (le

mikel阅读(931)


Ever wanted to get started with BlazeDS? A bug bit me yesterday that forced me to sit at my computer for hours on end until I could achieve two things: setting up Amazon's EC2 Webservice and getting up and running with BlazeDS.
I had to do quite a bit of hunting due to my inexperience with a few things, which led me to the conclusion that someone else out there might want to benefit from my Googling and bookmarking.
Anyone who is on a Mac (I'm on Leopard) should be able to follow along fairly easily, and if you're on Windows, it shouldn't be too bad to translate.
Setting up EC2
Shared hosting is soon become something of yesteryear. With resources like Amazon's Web Services, you can set up and have complete control over your own virtual web server with a very reasonable (and sensible) pricing system. It's ultra-scalable. You only pay for the storage, transfer, and server instances that you use. Little use = little money, yet if you find yourself having to scale for whatever reason, Amazon doesn't require you to do anything. You just get the bill for the extra usage that month. Perfect for someone like me.
Step 1: Sign up for EC2
This one's pretty easy, and I won't go into too much detail unless you have great difficulties filling out forms and registering credit card information.
The important part of this is that you'll get two things that are vital for your EC2 setup: a X509 certificate and a private key file. You can access these through the “View Access Key Identifiers” link under “Your Web Services Account.” Your files should look something like this:
# cert-HKZYKTAIG2ECMXYIBH3HXV4ZBZQ55CL.pem
# pk-HKZYKTAIG2ECMXYIBH3HXV4ZBZQ55CLO.pem
On to the next step.
Step 2: Set up command line tools
For this step, you'll need to start Terminal and have downloaded the command line tools from Amazon. The command line tools are simply some java classes that need to be unzipped to the location of your choice. I unzipped them to this location (.ec2) that I created: /Users/Cahlan/.ec2/
Now we're going to set up some environment variables. This will allow your ec2 command line tools to function. There are only a few, and you input them like so:
export EC2_HOME=/path/to/unzipped/tools
Mine was /Users/Cahlan/.ec2 or ~/.ec2 if you're already in the “Cahlan” folder.
export PATH=$PATH:$EC2_HOME/bin
export EC2_PRIVATE_KEY=~/.ec2/pk-HKZYKTAIG2ECMXYIBH3HXV4ZBZQ55CLO.pem
This sets our private key file, and
export EC2_CERT=~/.ec2/cert-HKZYKTAIG2ECMXYIBH3HXV4ZBZQ55CLO.pem
this sets our certificate file.
And since I'm on a Mac, I also have to set the environment variable for JAVA_HOME: JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Home/
Some helpful links for this section:
# EC2 Development Guide
# Jesse Andrews: EC2 Setup
Step 3: Create keypair
One last step before we get dirty. We have to create a keypair so that we can authenticate with the web service. You'll do this with this command “ec2-add-keypair gsg-keypair” which will output the key to the terminal. I tried copying and pasting this into a text file with disastrous results. We'll just store the output to a file, it's a much easier way:
ec2-add-keypair gsg-keypair >> /Users/Cahlan/.ec2/keypair
Now the keypair is stored in a file called “keypair” in our .ec2 folder. We have to make sure the file is protected, or our SSH sessions won't work. Type this command to set the right permissions for your newly-made keypair file:
chmod 600 /Users/Cahlan/.ec2/keypair
Now we'll actually create our virtual server on EC2.
Step 4: Create Server Instance
If you've done everything right up until this point, you should be able to type the command
ec2-describe-images -o amazon
to see all of the available Amazon-supported images that you can use. If that didn't work, you'll need to review the previous steps to find out where you went wrong.
So, now we need to pick an image. I chose a Linux Fedora distro with Apache and MySQL already installed, looks like this: ec2-public-images/fedora-core4-apache-mySQL.manifest.xml
To actually install this image, you'll type a command similar to this:
ec2-run-instances ami-25b6534c -k gsg-keypair
We got that ami code from the previous “describe” command. This will start your instance up and running. You may need to wait a few moments for it to fully initialize, you can check the progress with
ec2-describe-instances
You'll know it's up and running when you see something very similar to this:
RESERVATION r-xxxxxxxx ############ default
INSTANCE i-xxxxxxxx ami-25b6534c ec2-##-##-##-##-##-##.z-2.compute-1.amazonaws.com running gsg-keypair
Sweet! Now we need to quickly enable some ports:
ec2-authorize default -p 22 for SSH,
ec2-authorize default -p 80 for HTTP, and
ec2-authorize default -p 8400 for Tomcat/BlazeDS.
Test your new setup by navigating to this URL in your web browser: “ec2-##-##-##-##-##-##.z-2.compute-1.amazonaws.com” replacing of course the “#” with the correct numbers from your ec2-describe-instances statement.
We're halfway there.
Some helpful links for this section:
# Rober Sosinksi: EC2 w/ Mac OSx
# Jesse Andrews: EC2 Setup
Set up BlazeDS
Step 5: Download Java JDK
Now we'll SSH into our server with the following command:
ssh -i /Users/Cahlan/.ec2/keypair root@ec2-##-##-##-##-##-##.z-2.compute-1.amazonaws.com
If you've done everything right, you'll see some ASCII art and a greeting from Amazon.
Now we need the Java JDK installed. After changing to the root directory and creating a directory called “java” (mkdir java), I executed a wget:
wget -O /java/jdk-6u5-linux-i586.bin http://192.18.108.228/ECom/EComTicketServlet/BEGIN2FCA82FFFBA7733BAEFFC22E660E1F22/
-2147483648/2647525119/1/877946/877802/2647525119/2ts+/westCoastFSEND/
jdk-6u5-oth-JPR/jdk-6u5-oth-JPR:4/jdk-6u5-linux-i586.bin
(line wrapped)
You'll need to verify with the Java website that you get the correct URL. Your URL will probably be similar, or at least as painfully long.
A couple last steps, we'll change the permissions on the bin file and then execute it:
chmod +x /java/jdk-6u5-linux-i586.bin
./java/jdk-6u5-linux-i586.bin
You'll need to agree to the terms displayed in the output and then java will be installed.
Before we move on, let's set the appropriate environment vars for our BlazeDS to work.
export JAVA_HOME=/java/jdk1.6.0_05/bin
Some helpful links for this section:
# JDK Downloads Page
# JDK Install Instructions
Step 6: Download BlazeDS
We're pretty close now. We need to download BlazeDS, doing something very similar to what we did for the JDK:
Go back to root, make a “blaze” directory: cd ../
mkdir blaze
cd blaze
wget http://download.macromedia.com/pub/opensource/blazeds/blazeds_turnkey_3-0-0-544.zip
We're using the turnkey download of BlazeDS because it comes packaged with an integrated Tomcat server.
Now we just need to unzip the file:
unzip blazeds_turnkey_3-0-0-544.zip
Step 7: Startup Tomcat
Finally, we'll start our newly unpacked Tomcat server using this command:
./blaze/tomcat/bin/startup.sh
We should also start the sampledb for the samples app to work correctly:
/blaze/sampledb/startdb.sh
Some helpful links for this section:
# BlazeDS Install Guide
# BlazeDS Release Builds
Step 8: Check it Out
You can now admire and test your newly deployed BlazeDS install by going to http://your.long.url.amazon.com:8400
Not bad for implementing an entire LAMP setup along with Tomcat and BlazeDS in one sitting! See how your boss/clients react when you use all of these buzzwords in a sentence: Amazon EC2, Linux Fedora, Compute Cloud, BlazeDS, Tomcat. Kudos for whoever can make the sweetest sounding sentence with all of these. or maybe some sweet rhyme?

Tamarine

mikel阅读(1191)

本系列主要是用来研究AVM2和Tamarine相关的技术应用。
一、相关概念
(灰色部分选自 http://www.ibm.com/developerworks/cn/web/wa-actionscript/index.html)
在 2006 年 11 月 7 日,Adobe Systems 宣布将 ActionScript Virtual Machine 的代码捐献给 Mozilla Foundation。因此,Mozilla 启动了一个新的开放源码项目 Tamarin。Adobe 和 Mozilla 希望以此促进这种创建 Web 应用程序的标准化语言的推广。
Tamarin 项目的目标是实现一个高性能的开放源码的 ECMAScript 第四版(ES4)语言规范实现。Mozilla 在 SpiderMonkey 中使用 Tamarin Virtual Machine,SpiderMonkey 是 Firefox 和其他基于 Mozilla 的产品中嵌入的核心 JavaScript 引擎。Adobe 将继续在 Adobe Flash Player 中的 ActionScript Virtual Machine 中使用这些代码。
Tamarin Virtual Machine 当前实现 ECMAScript 第三版语言标准(这是 JavaScript、Adobe ActionScript 和 Microsoft Jscript 采用的标准),还支持 ES4 中建议的一些新语言特性。社区正在开发 ES4 的开放源码实现,Adobe 和 Mozilla 希望以此促进这种创建 Web 应用程序的标准化语言的推广。Tamarin 项目的目标是帮助开发人员创建可以跨多个平台的效果丰富的高度交互性的用户体验。
Tamarin 将支持 ECMAScript 第四版(或者说 “JavaScript 2”)语言,并将集成进 Mozilla 2 项目中的 SpiderMonkey,预期在 2008 年发布。
Mozilla 的开放源码项目团队刚刚开始开发 Tamarin,它的一些目标技术包括:
* 1. 将 Tamarin VM 和垃圾收集器集成进 SpiderMonkey 中
* 2. 使用 SpiderMonkey 编译器生成 Tamarin 的代码
* 3. 将即时编译器移植到新的硬件平台
* 4. 完成 ECMAScript 4 编译器
从以上概述,我们可以对Tamarin有一个初步的认识。虽然Tamarin实现的是一个ES4引擎,但这个项目对我们认识AVM2虚拟机和实现Flex下的脚本解析器活脚本引擎绝对有重要的意义。
下面我们来看一张图:

图描述了AVM2框架的各个部分和各部分之间的关系。可以看出,是的Flash Player 9有JIT complier,同时,它也有Interpreter,并且一切都建立在.abc之上。
随着Flash Player以后的版本将基于tamarine平台,将会有更多的脚本语言能够参与进来,包括目前tamaine的ES4,AS3(Action Monkey)和Iron系列(见IronMonkey, 可能包括IronPython,IronRuby,IronPHP等),到此,像不像.NET framework的感觉了? 甚至还要代替IE的JavaScript、VBScript客户端脚本(见ScreamingMonkey)。
好了,前景先简单介绍到这里,下面,让我们进入tamarine及其AVM2相关的世界中吧。
二、获得tamarin
我们可以用多种途径很轻松的获得tamarin。由于tamarin项目没有release,目前还只是模型阶段。
1、我们可以用mercurial的命令:
hg clone http://hg.mozilla.org/tamarin-tracing/
(没有hg的,可以用APT来安装,sudo apt-get install mercurial)
2、可以直接用浏览器从http://hg.mozilla.org/tamarin-tracing/上点击gz或zip链接来下载。
下载完毕后,就可以编译了。
这里注意编译环境:
1、在WINDOWS下,通过项目文件夹\platform\win32下的avmplus_9.sln或avm_plus_8.sln打开整个项目。
注意解决方案中有zlib,而tamarin源码包并没有包括zlib, 去相关网站下载zlib源码,并配置好路径即可。
编译时,core文件夹下的ErrorCostants.cpp可能出错,这个文件是错误信息常量表,可能是因为字符集的问题,我的解决方案很简单,删除多余的语言,只留en,即数组只留en。其他全部去掉,这样编译就通过了,反正这个文件没什么大用。
2、在LINUX下编译,最好建立一个空的文件夹,然后编译:
mkdir build
cd build
../tamarin-tracing/configure –enable-shell –enable-Debugger
编译最好加 –enable-shell –enable-Debugger这两个选项,否则,嘿嘿,第一个不加是不会有可执行的shell的,至于第二个不加,调试,汇编都不会有。
configure后生成Makefile文件。然后就可以make了。make后会生成shell目录,里面就有我们最终想要的avmshell这个Interpreter了。
3、测试一下:首先作一个hello.as 内容为print (“Hello world”);
然后用Flex-sdk的asc.jar:
java -jar asc.jar hello.as
显示 hello.abc, 84 bytes written
ls一下,可以看到hello.abc
然后./avmshell hello.abc
输出结果Hello world
好了,今天简单介绍这些作为进入tamarin世界的热身运用,下一期,我们来深入了解avmshell这个shell工具
==============================================
由于资料较少,肯定会有遗漏,错误和要补充的地方,欢迎大家讨论并指正。
1、越过编译步骤
我们接着说这个avmshell这个tamarin编译后最终生成的可执行文件。其实呢,当我们了解如何编译后,我们完全可以掠过这个恼人而有时又不成功的过程。
我们从adobe的opensource站上弄下来flex-sdk, 并用ant编译
svn checkout http://opensource.adobe.com/svn/opensource/flex/sdk/trunk/ flex-sdk
cd flex-sdk
ant
这样,我们就可以获得这个avmshell了:
在flex-sdk/modules/asc/bin/linux下有avmplus_s
在flex-sdk/modules/asc/bin/windows下有avmplus_s.exe
这个avmplus_s与我们编译过的avmshell是相同的。
我们再看看flex-sdk/modules/asc/bin/这个目录,你会发现很多有趣的东西,比如asc.exe就是我们上一章用到的 asc.jar,而abcdump.exe就是反编译as3 swf的工具,这个反编译器的制作方法可以参考https: //www.flashsec.org/wiki/Simple_AS3_Decompiler_Using_Tamarin页面下放的How to build an AS3 decompiler。
而且flex-sdk/modules/asc/已经开放了源码,在src文件夹内,也就是说flex sdk\bin下的asc.exe的代码可以在这里参考。
顺便提一下,在flex-sdk/modules/真的有不少好东西,可不光是asc哦,还有一个compiler, 这东西又是做什么的呢,看一下Readme发现,这个东西是用来编译mxml的,是mxmlc某种意义上的替代品,成品名叫flex-compiler- oem.jar。位置在flex-sdk/lib/flex-compiler-oem.jar。
Readme里提供了用法:
import flex2.tools.oem.Application;
import java.io.*;
public class Example
{
public static void main(String[] args)
{
try
{
Application application = new Application(“example”, new File(“example.mxml”));
Configuration c = application.getDefaultConfiguration();
c.setLicense(“compiler”,”0000-0000-0000-0000-0000-0000″);
application.setConfiguration(c);
application.setOutput(new File(“example.swf”));
application.build();
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
}
example.mxml:




然后javac -classpath ${flex.dir}/lib/flex-compiler-oem.jar Example.java,这样就可以了。
当然,这并不是真正的mxmlc,但是仍然可以使用,并且这个complier是有源码提供的哦。
至于modules下其他东西,请大家自己研究吧,再研究这个就跑题了。
2、avmshell命令行操作
上面我们简单介绍了asc工具的来历,它是专门编译AS3到.abc的工具。回忆一下上一章的图,一切都是从这个.abc开始的,为了简明,我们在前几章都假设都是用AS来生成.abc文件,而不是用ES4,因为ES4偶也没学好,呵呵,并且反正生成的.abc是一样的。之后我们可能会讲到另一个ES4的编译器,它叫esc。
首先,回顾上一章那个最简单的程序hello.as:
print(“”Hello world”)
它用ASC编译后成为hello.abc。
我们来看看avmshell的常用参数:
最常用的,我看就是-lifespan了。
./avmshell -lifespan hello.abc
输出
Hello world
Run time was 195 msec = 0.20 sec
这跟time ./avmshell hello.abc用法类似。
另一个是./avmshell -Dverbose hello.abc ,则输出详细汇编信息。
想要更狠更详尽的输出信息? 试试这个 ./avmshell -Dverbose_init hello.abc ,他将会把builtin的汇编信息一同输出,接下来就请看满屏幕飞的代码吧。
其他常用参数
-log 参数, 主要用于输出到文件。
-Dtimeout,设置最多执行15秒
-Dnoloops,设置禁止循环
-Dnogc,不进行垃圾收集
-Dgcstats,生成gc统计信息
-Dstats ,显示优化信息统计
大家可以逐个去试试,也可以去看源码部分。
今天先到这里,接下来会继续深入tamarin的核心部分,如builtin内建包和shell包以及如何用native关键字融合本地C代码,进行JIT编译优化。
1、ASC编译器简介
avmshell执行了abc文件,并输出结果,后面跟的具体的参数主要是用于输出管理,而编译AS3文件的重任则落在了asc.jar上。
上一次我们提到如何简单地使用asc编译器编译.as文件生成.abc。然而Hello world永远都是那么的简单。
那么这一节,我们就来重点看看这个 asc编译器。
源码:flex-sdk/modules/asc/src
成品:asc.jar
主要用法和参数,输入java -jar asc.jar回车即可看到:
asc {-AS3|-ES|-d|-f|-h|-i|-import |-in |-m|-p}* filespec
……
……
2、制作asc.exe
当然,我们也可以把它制作成可执行的文件
我们首先作一个asc.as,内容如下:
package
{
import avmplus.System;
var cmdline = “java -jar asc.jar”;
if( System.argv.length > 0 )
{
cmdline += ” ” + System.argv.join( ” ” );
}
System.exec( cmdline );
}
然后,我们利用asc编译器:
java -jar asc.jar asc.as回车
输出 asc.abc, 205 bytes written
这样,我们可以./avmshell asc.abc来代替java -jar asc.jar,以后就用avmshell来编译和执行文件了。
当然,这样作还是不方便,那么,我们可以这样:
java -jar asc.jar -exe asc.exe asc.as
输出
asc.abc, 205 bytes written
java.io.FileNotFoundException: asc.exe (No such file or directory)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.(FileInputStream.java:106)
at macromedia.asc.embedding.Compiler.createProjector(Unknown Source)
at macromedia.asc.embedding.Compiler.doCompile(Unknown Source)
at macromedia.asc.embedding.Compiler.doCompile(Unknown Source)
at macromedia.asc.embedding.Main.handleFile(Unknown Source)
at macromedia.asc.embedding.Main.main(Unknown Source)
asc.exe, 0 bytes written
提示没有这个文件asc.exe生成失败。这可能是个BUG,呵呵,不过解决这个问题很简单,建立一个空的asc.exe就可以了:
cat /dev/null > asc.exe
然后java -jar asc.jar -exe asc.exe asc.as
输出
asc.abc, 205 bytes written
asc.exe, 213 bytes written
好了,asc.exe生成完毕。这样,我们就可以使用这个asc.exe来代替java -jar asc.jar 了
当然,在linux下没这么好的待遇,不过有一个asc.sh,就在flex-sdk/modules/asc/bin下
至于这个神奇的avmplus.System包,它居然能对命令行操作,它的原型是
public native static function exec(command:String):int
也就是说他执行的native的本地代码,它脱离了AVM2的平台去执行了已经设计好的本地代码。大家翻开core目录下的任意.cpp和.as代码
发现都有大量的native代码出现。
其实大家在开发的时候,也会主义到,flex基类里也有很多native代码,比如常用的数字转型:Number(expression)。
这类的本地代码主要安装在解释器内部,如Flash Player 以本机代码的形式实现。
当然也可以是你自己的avmshell。或者自己的MyFlash Player或本地定制的AIR运行时环境。听到这很令人激动不是么?
比如制作public native static function alert(text:String):void 在win下来执行
MessageBoxW(NULL, text->c_str(), L”我的AIR”, MB_ICONEXCLAMATION);
在linux下执行其他的比如QMessageBox等。
当然我们还能制作一些函数调用本地dll等,我们利用这些native代码,可以实现另一个Yahoo ! Widget桌面小件平台运行时环境。
不过大家别激动,关于这些内容,我们在以后的章节会继续详细讨论。
性子急的朋友,可以现看看tamarine-tracing/shell/SystemClass.cpp
3、更复杂的编译:
1) -strict treat undeclared variable and method access as errors
意思是把没定义的变量和方法一律视为错误
首先,我们还是以之前的demo为例子,只是这次我们加上参数-strict
java -jar asc.jar -strict hello.as
输出
[Compiler] Error #1180: Call to a possibly undefined method print.
hello.as, Ln 1, Col 1:
print (“Hello world”);
^
1 error found
在编译hello.as时,我们并没有定义print方法,所以使用strict报错
平时,在编译as文件的时候,一般asc会包含一些内建的方法,所以我们并不需要包含这些文件,但是如果我们使用-strict选项,则必须引用完全。
2)-import = make the packages in the specified file available for import
意思是在指定的文件的packages包内添加import引用
所以我们可以这样写上面的例子:
java -jar asc.jar -strict -import ../../tamarin-tracing/core/builtin_full.abc -import ../../tamarin-tracing/shell/shell_full.abc hello.as
每一个要import的对象前都要写-import,路径要写对,在本文里,编译和执行的目录相对于tamarin项目是../../。
输出
hello.abc, 69 bytes written
还记得第一章编译的输出吗?不用-strict的输出是hello.abc, 84 bytes written,。可见,使用-strict后,尺寸缩小了。
我们再来一个复杂点的例子, Test.as :
package {
public class Test {
public function Test() {
trace(“hello World”);
}
}
}
编译 java -jar asc.jar Test.as
却输出:
[Compiler] Error #1017: The definition of base class Object was not found.
Test.as, Ln 1, Col 1:
package {
^
1 error found
为什么呢,是因为类声明全写为public class Test extend Object因为继承Object所以省略了,但是Object类编译时没有包含进来。
我们可以 java -jar asc.jar -import ../../tamarin-tracing/core/builtin_full.abc Test.as
输出 Test.abc, 182 bytes written
这个builtin_full.abc是什么呢,它是怎么构建的,答案是tamarin-tracing/core/builtin.py脚本,关于这个脚本,我们以后再详细讨论。
3) -in = include the specified filename
(multiple -in arguments allowed)
编译时包含指定文件。允许多个in选项
这个-in跟-import差不多,但也不完全一样。-import 等于是在package {} 里写import ,而-in是在编译过程中加入主文件的附属文件,即编译多个文件的意思。
比如,再写一个TestImpl.as:
package com {
public class TestImpl {
public function TestImpl() : void {
var test : Test = new Test();
}
}
new TestImpl();
}
然后编译这个文件:
从2) 小节已知的知识我们可能要这样输入命令:
java -jar asc.jar -import ../../tamarin-tracing/core/builtin_full.as -import Test.as TestImpl.as
执行成功了,输出
TestImpl.abc, 201 bytes written
然而,运行它的时候,就出问题了:
./avmshell TestImpl.abc
输出
VerifyError: Error #1014: Class Test could not be found.
at TestImpl$iinit()
at global$init()
从输出信息来看,是Test类没有找到,但是,我们的确引入了Test.as了阿。
原因是因为 -in选项,编译的时候并没有让Test.as加入到此次编译过程中,而只是-import了Test.abc,相当于在TestImpl里加了import com.TestImpl;但Test.as并没有加入到项目中。
这时,我们应该使用-in选项:
java -jar asc.jar -import ../../tamarin-tracing/core/builtin_full.as -in Test.as TestImpl.as
输出
TestImpl.abc, 384 bytes written
字节明显比刚才的201 bytes多。
现在我们执行它:
./avmshell TestImpl.abc
输出
hello World
4) -swf classname,width,height[,fps] = emit a SWF file
编译成为swf文件,格式为-swf 类名, 宽, 高, [祯率]
大家记得,还有个mxmlc吧,这个也能编译swf,有什么区别呢?
我们制作一个文件来测试一下,MyTest.as:
package {
import flash.display.*;
import flash.text.*;
public class MyTest extends Sprite {
public function MyTest() {
var test:Test = new Test();
var text = new TextField();
text.width = 400;
text.x = 0;
text.y = 0;
text.text = 'This is only a Test';
addChild(text);
}
}
}
我们编译这个文件mxmlc MyTest.as
输出/home/test/dev/tamarin/build/shell/MySprite.swf (856 bytes)
这样就OK了,我们可以用flashplayer测试结果:flex-sdk/in/player/lnx/flashplayer MySprite.swf回车
屏幕显示This is only a test,控制台显示hello World
而我们用asc则是(为了方便,我已经不想用../../tamarin-tracing这类path了,直接把常用abc文件copy到当前目录下)
java -jar asc.jar -import -swf MyTest,400,300 -import builtin_full.abc -import playerglobal.abc -in Test.as MyTest.as
输出
MySprite.swf, 830 bytes written
测试结果,除了背景色不同外,结果相同。
关键是mxmlc读取flex-config.xml,而且更重要的是,它自动会寻找Test.as,而asc是不会的。
6) 其他操作
关于其他操作,还请大家去看flex-sdk的源码和tamarin的avmshell项目代码,过后,我们也有详细的讨论,请大家关注
好了,今天的asc命令行编译工具就介绍到这里,接下来的内容,就让我们更深入tamarin一些吧。
前的三章已经为我们介绍了tamarin和开源的flex-sdk的一些基本操作,本章我们将要更加深入地了解avmshell和asc编译器。
1、深入tamarin项目
到目前为止,我们仔细看看我们的tamarin-tracing的目录结构,可能我们从来都没深入了解也注意到他里面的文件:
|– build // make后的编译版本
| |– Makefile
| |– core
| |– extensions
| |– nanojit
| |– pcre
| |– platform
| |– shell // 前三章,我们一直在这里用./avmshell和java -jar asc.jar进行示例
| |– space
|– tamarin-tracing // tamarin源码工程
|– axscript
|– build
|– configure
|– configure.py
|– core // 核心文件夹
|– esc // esc的编译器,相对于asc
|– extensions
|– localization
|– manifest.mk
|– nanojit
|– pcre
|– platform // 平台文件夹,win的项目文件在这里可以找到
|– shell // shell文件夹
|– space // mmgc项目文件夹
|– test
|– utils // 工具文件夹
`– zlib
结构大体如上,其中
tamarin-tracing/core是程序的核心项目部分,主要构建builtin.abc部分,也有资料写作global.abc
tamarin-tracing/shell是shell项目部分,主要构建shell.abc,也有资料写作toplevel.abc
关于global.abc和toplevel.abc的构建请参考redtamarin项目(http://code.google.com/p/redtamarin/)
这是另一个不同于官方的tamarin实现。
2、定制我们自己的tamarin
大家是不是有一些头晕,tamarin就tamarin吧,怎么还有个redtamerin? 这是另一套完全不同的项目吗?
其实,关于tamarin项目的结果,只是最终生成了avmshell这个可以运行虚拟机的shell。而shell究竟内容如何,主要靠core和shell这两大块。
而core是核心,里面基本是雷打不动的代码,比较偏重虚拟机内的方法,如AS3的内建方法,而shell则是偏重于本地代码的实现,即不同平台上的shell的实现,类似AIR。
两部分合在一起,构成了tamarin的avmshell。
那么,我们完全可以自己制作一个属于自己的shell,或自己的Integrated Runtime环境,就像之前说的Yahoo!Widgets。
Goole Code上实现的叫redtamarin,我们大可以自己作一个bluetamarin。或者My Integrated Runtime 或叫 MIR,以区别AIR。这是完全可行的。
redtamarin在global.as中选用了
include “core\/builtin.as”
include “core\/Math.as”
include “core\/ErrorConstants.as”
include “core\/Error.as”
include “core\/RegExp.as”
include “core\/Date.as”
include “core\/XML.as”
编译成global.abc来代替官方的 tamarin-tracing/core下的builtin_full.abc
在toplevel.as中,用
include “shell\\toplevel.as”
include “shell\\Domain.as”
include “shell\\StringBuilder.as”
include “shell\\ByteArray.as”
include “shell\\IntArray.as”
include “shell\\UIntArray.as”
include “shell\\DoubleArray.as”
include “shell\\FloatArray.as”
include “shell\\ShortArray.as”
include “shell\\UShortArray.as”
include “shell\\Dictionary.as”
include “shell\\Endian.as”
include “shell\\Java.as”
//RedTamarin
include “shell\\standard.Errors.as”
include “shell\\standard.Operations.as”
include “shell\\standard.as”
编译成toplevel.abc代替了官方 tamarin-tracing/shell下的shell_full.abc,如此而已。
3、创建自己的build版本,定制avmshell
如果真要按照redtamarin这样定制下来,你会发现,需要编译的地方很多,出错的地方更多。可能大家会觉得没有足够的知识和耐心是不能作到的。
实际上呢,redtamarin已经是个趋向与成品的东西了,就是说它中间经历过的编译过程和产生的文件,事实上都被删掉了,只留下了最终的结果,所以你按照redtamarin编译是没法成功的。
其实我们没有必要从头来定制,core和shell的大部分代码我们都是很需要的,我们只需要在上面加上我们需要的就可以了,而tamarin-tracing也提供了编译和定制的工具。
我们看 tamarin-tracing/core下面有一个builtin.py,这个文件可以编译一些头文件和abc文件或声明内容。
1)它调用了forth语言编译器fc(在 tamarin-tracing/utils下),生成fpu和soft fp部分,关于forth语言,这里就不再赘述。
2)它使用asc.jar下的macromedia.asc.embedding.ScriptCompiler而不是macromedia.asc.embedding.Main(java -jar asc.jar默认的是执行Main)。
这个ScriptCompiler既可以编译abc又可以生成cpp和h文件,从而在make时进一步编译生成avmshell。
同理在tamarin-tracing/shell下也有shell.py,这个文件编译shell相关的abc和头文件或声明内容。
(注意,在运行这两个python脚本后生成的builtin_full.h和shell_full.h有BUG,稍后我们会详细说明这个问题)
我们通过修改源文件,并使用builtin.py和shell.py来快速地进行cpp和h文件的生成,然后新建空文件夹configure,再通过make就可以构建我们自己的avmshell了。
4、初步实践
首先,我们在build/shell的目录下(还是我们经常测试的那个目录)制作testShell.as:
import avmplus.*;
System.alert(“test alert”);
编译java-jar asc.jar testShell.as
testShell.abc, 100 bytes written
运行./avmshell testShell.abc
结果输出
TypeError: Error #1006: alert is not a function.
at global$init()
avmshell运行提示并没有定义这个alert方法。
事实上,shell里也没有这个alert方法。我们可以定义一个。
在tamarin-tracing/shell/shell.as中
package avmplus
{
public class System
{
public native static function exit(status:int):void
public native static function getAvmplusVersion():String
public native static function debugger():void
public native static function isDebugger():Boolean
public native static function getTimer():uint
public native static function readLine():String
// 加入这个方法
public static function alert(text:String):void
{
// 方法体,自己定义
write(text);
}
然后编译这个shell的头文件
tamarin-tracing/shell/shell.py
输出
Building Full AS3
shell: 80361
Files: 18 Time: 2614ms
BEFORE 80361
AFTER 46010
SAVED 34351 43%
Building Min AS3
shell: 6504
Files: 6 Time: 836ms
BEFORE 6504
AFTER 3281
SAVED 3223 50%
然后新建一个build2目录去configure & make,
mkdir build2
cd build2
../tamarin-tracing/configure –enable-shell –enable-debugger
make
你会发现,编译错误,这是个BUG。
解决方法是,修改shell_full.h,把所有以AVMPLUS_NATIVE_METHOD_DECL(Atom开头的宏命令都替换成AVMPLUS_NATIVE_METHOD_DECL(BoxReturnType
比如AVMPLUS_NATIVE_METHOD_DECL(Atom, avmplus_Domain_private__load)替换为AVMPLUS_NATIVE_METHOD_DECL (BoxReturnType, avmplus_Domain_private__load)
好了,再make,就可以编译了
然后,我们就在build2/shell下,得到了一个新的avmshell
我们有两个不同avmshell了,两个不同的shell环境,想不想搞几个不同的avmshell发行版呢,
或者自己作一个特色的AIR出来?
这时候,我们可以针对两个build环境分别测试:
在build/shell下用./avmshell testShell.abc
结果输出
TypeError: Error #1006: alert is not a function.
at global$init()
在build2/shell下用./avmshell testShell.abc
结果输出
test alert
有些环境下的tamarin-tracing可能输出不出来结果,不过没关系
用./avmshell -Dastrace 1 testShell.abc
结果输出
87 AVMINF: MTHD global$init ()
88 AVMINF: MTHD Object$$cinit ()
88 AVMINF: MTHD Class$$cinit ()
90 AVMINF: MTHD Function$$cinit ()
90 AVMINF: MTHD Object$/private::_hideproto ()
160 AVMINF: MTHD Object$/private::init ()
161 AVMINF: MTHD Object$/private::_hideproto ()
161 AVMINF: MTHD private::MethodClosure$$cinit ()
164 AVMINF: MTHD Namespace$$cinit ()
164 AVMINF: MTHD Object$/private::_hideproto ()
165 AVMINF: MTHD QName$$cinit ()
165 AVMINF: MTHD Object$/private::_hideproto ()
166 AVMINF: MTHD Boolean$$cinit ()
166 AVMINF: MTHD Object$/private::_hideproto ()
171 AVMINF: MTHD Number$$cinit ()
181 AVMINF: MTHD Object$/private::_hideproto ()
183 AVMINF: MTHD int$$cinit ()
184 AVMINF: MTHD Object$/private::_hideproto ()
186 AVMINF: MTHD uint$$cinit ()
206 AVMINF: MTHD Object$/private::_hideproto ()
218 AVMINF: MTHD String$$cinit ()
219 AVMINF: MTHD Object$/private::_hideproto ()
232 AVMINF: MTHD Array$$cinit ()
233 AVMINF: MTHD Object$/private::_hideproto ()
236 AVMINF: MTHD private::FieldName$$cinit ()
236 AVMINF: MTHD private::StackFrame$$cinit ()
237 AVMINF: MTHD private::ArraySort$$cinit ()
286 AVMINF: MTHD global$init ()
286 AVMINF: MTHD global$init ()
286 AVMINF: MTHD avmplus::Domain$$cinit ()
286 AVMINF: MTHD global/private::getfiles ()
286 AVMINF: MTHD global$init ()
287 AVMINF: MTHD avmplus::System$$cinit ()
287 AVMINF: MTHD avmplus::System$/private::getArgv ()
287 AVMINF: MTHD avmplus::File$$cinit ()
287 AVMINF: MTHD flash.system::Capabilities$$cinit ()
287 AVMINF: MTHD Array/get length ()
287 AVMINF: MTHD Array/http://adobe.com/AS3/2006/builtin::shift ()
287 AVMINF: MTHD Array$/private::_shift ()
287 AVMINF: MTHD Array/get length ()
287 AVMINF: MTHD Array/set length ()
287 AVMINF: MTHD Array/private::_set_length ()
287 AVMINF: MTHD Array/http://adobe.com/AS3/2006/builtin::push ()
287 AVMINF: MTHD Array$/private::_push ()
287 AVMINF: MTHD Array/get length ()
287 AVMINF: MTHD Array/get length ()
287 AVMINF: MTHD Array/set length ()
287 AVMINF: MTHD Array/private::_set_length ()
288 AVMINF: MTHD Array/get length ()
288 AVMINF: MTHD Array/get length ()
288 AVMINF: MTHD Array/get length ()
288 AVMINF: MTHD avmplus::Domain/load ()
288 AVMINF: MTHD avmplus::Domain/private::_readAndLoad ()
288 AVMINF: MTHD global$init ()
288 AVMINF: MTHD flash.utils::ByteArray$$cinit ()
288 AVMINF: MTHD flash.utils::ByteArray$/readFile ()
288 AVMINF: MTHD global/_checkNull ()
288 AVMINF: MTHD flash.utils::ByteArray$iinit ()
288 AVMINF: MTHD Object$iinit ()
289 AVMINF: MTHD global$init ()
289 AVMINF: MTHD avmplus::System$/alert ()
289 AVMINF: MTHD avmplus::System$/write ()
289 AVMINF: MTHD global/_checkNull ()
testShell289 AVMINF: MTHD Array/get length ()
看这一行289 AVMINF: MTHD avmplus::System$/alert (),说明alert已经执行,并调用了289 AVMINF: MTHD avmplus::System$/write ()
好了,今天的内容到此结束。
今天主要稍微简单地深入了一下tamarin的各部分构成,以及如何定制自己的avmshell。
接下来,我们要深入了解native这个本地代码关键字以及如何让AVM2虚拟机来执行外部的本地代码。
上回书说道,如何在tamarin项目的shell中加入定制AS3代码,下面我们接着上回的说。这次,我们将要设计并使用本地代码。
1、修改shell子项目
这次让我们关注tamarin-tracing/shell这个目录。
上次简单地说道tamarin-tracing/shell/shell.py可以构建shell编译的相关C++和abc文件,这次我们来深入看看shell这个项目。
打开tamarin-tracing/shell/shell.py,我们看fullas3这个方法
def fullas3():
print
print “Building Full AS3″
# compile builtins
os.system(asc+fullconfig+” -d -abcfuture -import ../core/builtin_full.abc -builtin -out shell main.as shell.as fib.as ../extensions/Dictionary.as Endian.as “+zlibfiles+” Domain.as ByteArray.as”)
# run optimizer
os.system(abcopt+” ../core/builtin_full.abc shell.abc >shell_full.out”)
os.system(“tail -3 shell_full.out”)
mv(“shell.abc”, “shell_full_orig.abc”)
mv(“shell.abc2”, “shell_full.abc”)
mv(“shell.cpp2”, “shell_full.cpp”)
mv(“shell.h2”, “shell_full.h”)
rm(“shell.cpp”)
rm(“shell.h”)
一般我们编译都是用full这个配置来编译,比如builtin_full和shell_full,因为full包含了更全的内容。关于full的配置,从变量fullconfig=”-config CONFIG::Full=true”来区分编译内容,具体内容全部放在as文件中, 写法如下:
CONFIG::Full
private static function _join(o, sep):String
…{
var s:String = (sep === undefined) ? “,” : String(sep);
var out:String = “”;
for (var i:uint = 0, n:uint=o.length; i < n; i++) ...{ var x = o[i]; if (x != null) out += x; if (i+1 < n) out += s; } return out; } 这样编译通过-config CONFIG::Full=true或false来决定这个_join是否要参与AS3编译成c++或abc代码,这些代码的头定义最终织入到 shell_full.h和builtin_full.h或shell_min.h和builtin_min.h中。 从shell文件夹和core文件夹中的文件可以看出,文件命名规则是这样的: 以Number类为例,则有Number.as、NumberClass.cpp、NumberClass.h。 然后,让我们看看make编译shell的关键操作流程: 1、shell.py脚本会利用asc.jar中的macromedia.asc.embedding.ScriptCompiler先来编译这些as,然后生成的头全部放入shell_full.h或shell_min.h中(编译core项目生成builtin_full.h道理与之相同) 2、然后根据生成的h和cpp文件在shell文件夹的manifest.mk中编译(.net是在avmplus_9.vcproj项目文件中) 这跟之前提到的一样,先shell.py再make项目的做法相吻合。 2、制作native method 为了便于诠释,我们跟《Implementing Native Methods in Tamarin 》文章一样,采用以斐波那契数列为例。关于这篇文章,感兴趣的朋友请看http://www.bluishcoder.co.nz/2008/02/implementing-native-methods-in-tamarin.html 我们首先建立一个fib.as: package testing ...{ public function fib(n) ...{ if(n <= 1) return 1; else return fib(n-1) + fib(n-2); } public native function fib2(n:int):int; } 我们一口气建立了两个fib函数,一个是as3实现,另一个使用了native method,转入本地代码实现。这样便于我们去对比参考。 然后我们去修改shell.py,在fullas3方法中加入我们要实现的这个fib.as让其参与编译: os.system(asc+fullconfig+" -d -abcfuture -import ../core/builtin_full.abc -builtin -out shell main.as shell.as fib.as ../extensions/Dictionary.as Endian.as "+zlibfiles+" Domain.as ByteArray.as") 然后系统生成了shell_full.h,我们可以看到已经有fib2方法的头产生: struct null_fib2_args ...{ public: ScriptObjectp /**//*global0*/ self; private: int32_t self_pad; public: int32_t n; private: int32_t n_pad; public: StatusOut* status_out; }; AVMPLUS_NATIVE_METHOD_DECL(int32_t, null_fib2) 分别为一个结构体和一个宏。 然后我们实现这个fib2,fibimpl.cpp: #include "avmshell.h" #include
namespace avmshell
…{
int32_t native_fib(int32_t n) …{
if(n <= 1) return 1; else return native_fib(n-1)+native_fib(n-2); } AVMPLUS_NATIVE_METHOD(int32_t, null_fib2) ...{ return native_fib(args->n);
}
}
我们的实现中的宏定义为:
#define AVMPLUS_NATIVE_METHOD(ret, name)
ret FASTCALL name (const name##_args* args) /**//* no semi */
在fibimpl.cpp中也就相当于:
int32_t FASTCALL null_fib2 (const null_fib2_args* args) /**//**//**//* no semi */
{
return native_fib(args->n);
}
这样shell调用fib2就相当于调用null_fib2,null_fib2又相当于调用native_fib(args->n),args就是结构体指针null_fib2_args。
这种调用在tamarin里很常见,他们也具有一定的命名规则,如上一章在shell.as内有一个exit方法:
package avmplus
…{
public class System
…{
public native static function exit(status:int):void
………………
对应的SystemClass.cpp中就有:
AVMPLUS_NATIVE_METHOD(void, avmplus_System_exit)
…{
::exit(args->status);
}
命名规则是:包_类_方法。因为是public的所以没有表示范围
在这个System类内还有_exec方法,那么它的宏是:
AVMPLUS_NATIVE_METHOD(int32_t, avmplus_System_private__exec)
命名规则是:包_类_作用域_方法名。
注意,我们并没有准寻[foo].as、[foo]Class.cpp、[foo]Class.h这一命名规则,这说明这规则是编码规范而已。
然后,我们修改shell文件夹下的manifest.mk,在编译路径中加入这个fibimpl.cpp:
shell_CXXSRCS := $(shell_CXXSRCS)
$(curdir)/avmshell.cpp
$(curdir)/ByteArrayGlue.cpp
$(curdir)/ConsoleOutputStream.cpp
$(curdir)/DataIO.cpp
$(curdir)/DebugCLI.cpp
$(curdir)/DomainClass.cpp
$(curdir)/FileClass.cpp
$(curdir)/FileInputStream.cpp
$(curdir)/SystemClass.cpp
$(curdir)/fibimpl.cpp
$(curdir)/../extensions/DictionaryGlue.cpp
$(NULL)
然后我们make编译项目,注意上一篇提到的Atom BUG问题,这里不再赘述。
假设新的build的shell叫build2,区别与之前的build。在build2/shell目录下,制作fibtest.as和fibtest2.as:
$ cat fibtest.as
import testing.*;
print(“fib 30 = ” + fib(30));
$ cat fibtest2.as
import testing.*;
print(“fib 30 = ” + fib2(30));
分别测试基于as3的fib和基于native method的fib2:
$ ./shell/avmshell -lifespan fibtest.abc
fib 30 = 1346269
Run time was 2390 msec = 2.39 sec
$ ./shell/avmshell -lifespan fibtest2.abc
fib 30 = 1346269
Run time was 443 msec = 0.44 sec
$ ./shell/avmshell -lifespan -interp fibtest.abc
fib 30 = 1346269
Run time was 129007 msec = 129.01 sec
$ ./shell/avmshell -lifespan -interp fibtest2.abc
fib 30 = 1346269
Run time was 329 msec = 0.33 sec
发现native method明显快于as3 mathod(这是当然的,呵呵)
好了,我们诠释了《Implementing Native Methods in Tamarin 》一文,实践了fib2这个方法的从无到有的过程。
2、关于Atom的BUG
这两章一直有提到这个AVMPLUS_NATIVE_METHOD_DECL(Atom, ……)出错的问题。我们就在这节由这个问题引出更深层次的东西。
首先我们看AVMPLUS_NATIVE_METHOD_DECL的定义:
#define AVMPLUS_NATIVE_METHOD_DECL(ret, name)
enum …{ k_NativeRetType_##name = NativeRetType_##ret } ;
extern ret FASTCALL name (const name##_args* args); /**//* semi required here */
可以看到这个宏做两件事情,一件是用来做enum返回类型的定义,另一个是做extern方法定义,如:
AVMPLUS_NATIVE_METHOD_DECL(bool, avmplus_File_private__write),解析成:
enum { k_NativeRetType_avmplus_File_private__write = NativeRetType_bool }
extern bool FASTCALL avmplus_File_private__write(const avmplus_File_private__write_args* args)
而native method的返回类型如下:
typedef uint64_t BoxReturnType;
// a little syntactic sugar for mapping C++ ret types into a useful enum
enum NativeRetType
…{
NativeRetType_ScriptObjectp,
NativeRetType_bool,
NativeRetType_int32_t,
NativeRetType_uint32_t,
NativeRetType_Namespacep,
NativeRetType_double,
NativeRetType_BoxReturnType,
NativeRetType_Stringp,
NativeRetType_void
};
而且,对应的shell_full.h里就有对应的结构体:
struct avmplus_File_private__write_args
…{
public: ScriptObjectp /**//*File$*/ classself; private: int32_t classself_pad;
public: Stringp filename; private: int32_t filename_pad;
public: Stringp data; private: int32_t data_pad;
public: StatusOut* status_out;
};
这个结构体是是对应的as文件用shell.py生成的,就像刚才我们生成null_fib2_args一样。
这样,这一个PY脚本工具生成的宏就把整个的native method定义流程和结构串在了一起。
Atom的产生原因是定义as文件时返回了不明确的类型,比如Object类型或*类型。Atom的定义如下:
// NOTE, for the new native-method glue to work properly,
// Atom must be a unique type, NOT an alias onto int, intptr_t, etc.
// Please use the atomPtr, atomKind, etc functions in AtomConstants.h to
// operate on them.
typedef struct Atom_* Atom;
说明单是Atom是个抽象的不能使用的类型,而且,建议使用的是atomPtr和atomKind,而atomPtr又是#define atomPtr(a) ((void*)(uintptr_t(a) & ~7)),7为atomKind的enum数量,uintptr_t返回的是uint64_t。
为什么要用NativeRetType_BoxReturnType来替换呢,因为在解释器Interpreter.cpp里,处理NativeRetType_BoxReturnType为:
case NativeRetType_BoxReturnType:
// remember: plain object and any-typed values are handled as Box, not SO*
result.q = (*NativeMethodProc_BoxReturnType(f))(argv);
break;
可以看出,BoxReturnType为plain object(即Object基类型)和任何被类型化的值。所以换为BoxReturnType就不会出问题了。
今天详细地介绍了如何制作本地方法,并深入了解本地方法的来龙去脉和核心宏方法。
下面,我们将深入到源代码内部去看看tamarin的运作机制,并且将介绍其他子项目的功能和内容。精彩不容错过呦
============================================================================
最近特别忙,就没空出时间继续研究。不过以后会补上进度的。