- 浏览: 3053162 次
- 性别:
- 来自: 海外
文章分类
- 全部博客 (430)
- Programming Languages (23)
- Compiler (20)
- Virtual Machine (57)
- Garbage Collection (4)
- HotSpot VM (26)
- Mono (2)
- SSCLI Rotor (1)
- Harmony (0)
- DLR (19)
- Ruby (28)
- C# (38)
- F# (3)
- Haskell (0)
- Scheme (1)
- Regular Expression (5)
- Python (4)
- ECMAScript (2)
- JavaScript (18)
- ActionScript (7)
- Squirrel (2)
- C (6)
- C++ (10)
- D (2)
- .NET (13)
- Java (86)
- Scala (1)
- Groovy (3)
- Optimization (6)
- Data Structure and Algorithm (3)
- Books (4)
- WPF (1)
- Game Engines (7)
- 吉里吉里 (12)
- UML (1)
- Reverse Engineering (11)
- NSIS (4)
- Utilities (3)
- Design Patterns (1)
- Visual Studio (9)
- Windows 7 (3)
- x86 Assembler (1)
- Android (2)
- School Assignment / Test (6)
- Anti-virus (1)
- REST (1)
- Profiling (1)
- misc (39)
- NetOA (12)
- rant (6)
- anime (5)
- Links (12)
- CLR (7)
- GC (1)
- OpenJDK (2)
- JVM (4)
- KVM (0)
- Rhino (1)
- LINQ (2)
- JScript (0)
- Nashorn (0)
- Dalvik (1)
- DTrace (0)
- LLVM (0)
- MSIL (0)
最新评论
-
mldxs:
虽然很多还是看不懂,写的很好!
虚拟机随谈(一):解释器,树遍历解释器,基于栈与基于寄存器,大杂烩 -
HanyuKing:
Java的多维数组 -
funnyone:
Java 8的default method与method resolution -
ljs_nogard:
Xamarin workbook - .Net Core 中不 ...
LINQ的恶搞…… -
txm119161336:
allocatestlye1 顺序为 // Fields o ...
最近做的两次Java/JVM分享的概要
有时候我们希望能够在存档文件里保存些结构化的数据,而不只是简单的字符串或者数字。但是你会发现,f、sf里只能保存数字、字符串、数组或者字典,却不能保存一般的Object。为什么会这样的呢?
从Initialize.tjs可以看到,f、sf分别是kag.flags、kag.sflags的别名。它们本身都是字典(或者叫关联数组)。于是我们可以对关联数组做个测试,看看问题是不是出在它上面:
执行这段代码后,可以看到test.txt里的内容是:
也就是说虽然要保存的对象本身并不是null(如果是null的话,保存时不会写出object地址的注释),但是却被保存为了null。所以很明显,向f或者sf写入对象的话肯定也是一样的结果。
但是到底系统是怎么实现这个功能导致了这个结果的呢?
============================================================================
先看看KAG是如何读取和保存系统变量的:
看了这两个函数应该可以理解,系统在保存系统变量(sf)时其实就是把一个字典的内容以字面量的形式写道外部文件(存档)中。在读取存档时,只要对存档里的字典字面量eval一下就能还原其内容。很直观。
对游戏变量(f)也是同理,这里就略过了。
============================================================================
然后再看看TJS2引擎是如何实现Dictionary.saveStruct()的。
tjsDictionary把读取数据的工作交给了tjsCustomObject(Owner)。Owner接下来会遍历自己所储存的属性,参见下面引用自tjsObject.cpp的几段代码;遍历的时候会调用一个callback,也就是上面的FuncCall()。
注意到这个FuncCall里是如何处理Object类型的数据的:它调用了tTJSArrayNI::SaveStructureDataForObject(),实现如下:
问题就出在这里了。这个函数的逻辑只会正确保存字典、数组或者null;其它Object类型的对象都被保存为了null。
于是这是一个by design的问题了吧……sigh。真是糟糕的决定。
-------------------------------------------------------------------------------
从Initialize.tjs可以看到,f、sf分别是kag.flags、kag.sflags的别名。它们本身都是字典(或者叫关联数组)。于是我们可以对关联数组做个测试,看看问题是不是出在它上面:
class TestSave { var value = 0; function TestSave(initVal) { value = initVal; } property Value { getter() { return value; } setter(newValue) { value = newValue; } } } var vfx = %[ "cool" => new TestSave(1) ]; (Dictionary.saveStruct incontextof vfx)("./test.txt", "");
执行这段代码后,可以看到test.txt里的内容是:
%[ "cool" => null /* (object) "(object 0x01702668:0x01702668)" */ ]
也就是说虽然要保存的对象本身并不是null(如果是null的话,保存时不会写出object地址的注释),但是却被保存为了null。所以很明显,向f或者sf写入对象的话肯定也是一样的结果。
但是到底系统是怎么实现这个功能导致了这个结果的呢?
============================================================================
先看看KAG是如何读取和保存系统变量的:
MainWindow.tjs/1007 写道
function loadSystemVariables() { // システム変数の読み込み try { var fn = saveDataLocation + "/" + dataName + "sc.ksd"; if(Storages.isExistentStorage(fn)) { scflags = Scripts.evalStorage(fn); scflags = %[] if scflags === void; } else { scflags = %[]; } var fn = saveDataLocation + "/" + dataName + "su.ksd"; if(Storages.isExistentStorage(fn)) { sflags = Scripts.evalStorage(fn); sflags = %[] if sflags === void; } else { sflags = %[]; } } catch(e) { throw new Exception("システム変数データを読み込めないか、" "あるいはシステム変数データが壊れています(" + e.message + ")"); } }
MainWindow.tjs/1140 写道
function saveSystemVariables() { // システム変数の保存 if(!isMain) return; // プラグインを呼ぶ forEachEventHook('onSaveSystemVariables', function(handler, f) { handler(); } incontextof this); // フルスクリーン scflags.fullScreen = fullScreened; // 文字表示速度 scflags.autoModePageWait = autoModePageWait; scflags.autoModeLineWait = autoModeLineWait; scflags.userChSpeed = userChSpeed; scflags.userCh2ndSpeed = userCh2ndSpeed; scflags.chDefaultAntialiased = chDefaultAntialiased; scflags.chDefaultFace = chDefaultFace; scflags.chNonStopToPageBreak = chNonStopToPageBreak; scflags.ch2ndNonStopToPageBreak = ch2ndNonStopToPageBreak; // ブックマーク名 scflags.bookMarkNames = bookMarkNames; // (コピーではなくて)参照で十分 scflags.bookMarkDates = bookMarkDates; scflags.bookMarkProtectedStates = bookMarkProtectedStates; scflags.lastSaveDataNameGlobal = lastSaveDataNameGlobal; // ファイルに書き込む if(!readOnlyMode) { var fn = saveDataLocation + "/" + dataName + "sc.ksd"; (Dictionary.saveStruct incontextof scflags)(fn, saveDataMode); var fn = saveDataLocation + "/" + dataName + "su.ksd"; (Dictionary.saveStruct incontextof sflags)(fn, saveDataMode); } }
看了这两个函数应该可以理解,系统在保存系统变量(sf)时其实就是把一个字典的内容以字面量的形式写道外部文件(存档)中。在读取存档时,只要对存档里的字典字面量eval一下就能还原其内容。很直观。
对游戏变量(f)也是同理,这里就略过了。
============================================================================
然后再看看TJS2引擎是如何实现Dictionary.saveStruct()的。
tjsDictionary.cpp/59 写道
TJS_BEGIN_NATIVE_METHOD_DECL(/*func.name*/saveStruct) { // Structured output for flie; // the content can be interpret as an expression to re-construct the object. TJS_GET_NATIVE_INSTANCE(/* var. name */ni, /* var. type */tTJSDictionaryNI); if(!ni->IsValid()) return TJS_E_INVALIDOBJECT; if(numparams < 1) return TJS_E_BADPARAMCOUNT; ttstr name(*param[0]); ttstr mode; if(numparams >= 2 && param[1]->Type() != tvtVoid) mode = *param[1]; iTJSTextWriteStream * stream = TJSCreateTextStreamForWrite(name, mode); try { std::vector<iTJSDispatch2 *> stack; stack.push_back(objthis); tTJSStringAppender string; ni->SaveStructuredData(stack, string, TJS_W("")); stream->Write(ttstr(string.GetData(), string.GetLen())); } catch(...) { stream->Destruct(); throw; } stream->Destruct(); if(result) *result = tTJSVariant(objthis, objthis); return TJS_S_OK; } TJS_END_NATIVE_STATIC_METHOD_DECL(/*func.name*/saveStruct)
tjsDictionary.cpp/263 写道
void tTJSDictionaryNI::SaveStructuredData(std::vector<iTJSDispatch2 *> &stack, tTJSStringAppender & string, const ttstr &indentstr) { #ifdef TJS_TEXT_OUT_CRLF string += TJS_W("%[\r\n"); #else string += TJS_W("%[\n"); #endif ttstr indentstr2 = indentstr + TJS_W(" "); tSaveStructCallback callback; callback.Stack = &stack; callback.String = &string; callback.IndentStr = &indentstr2; callback.First = true; // in header file: tTJSCustomObject * Owner; Owner->EnumMembers(TJS_IGNOREPROP, &tTJSVariantClosure(&callback, NULL), Owner); #ifdef TJS_TEXT_OUT_CRLF if(!callback.First) string += TJS_W("\r\n"); #else if(!callback.First) string += TJS_W("\n"); #endif string += indentstr; string += TJS_W("]"); }
tjsDictionary.cpp/290 写道
tjs_error TJS_INTF_METHOD tTJSDictionaryNI::tSaveStructCallback::FuncCall( tjs_uint32 flag, const tjs_char * membername, tjs_uint32 *hint, tTJSVariant *result, tjs_int numparams, tTJSVariant **param, iTJSDispatch2 *objthis) { // called indirectly from tTJSDictionaryNI::SaveStructuredData if(numparams < 3) return TJS_E_BADPARAMCOUNT; // hidden members are not processed tjs_uint32 flags = (tjs_int)*param[1]; if(flags & TJS_HIDDENMEMBER) { if(result) *result = (tjs_int)1; return TJS_S_OK; } #ifdef TJS_TEXT_OUT_CRLF if(!First) *String += TJS_W(",\r\n"); #else if(!First) *String += TJS_W(",\n"); #endif First = false; *String += *IndentStr; *String += TJS_W("\""); *String += ttstr(*param[0]).EscapeC(); *String += TJS_W("\" => "); tTJSVariantType type = param[2]->Type(); if(type == tvtObject) { // object tTJSVariantClosure clo = param[2]->AsObjectClosureNoAddRef(); tTJSArrayNI::SaveStructuredDataForObject(clo.SelectObjectNoAddRef(), *Stack, *String, *IndentStr); } else { *String += TJSVariantToExpressionString(*param[2]); } if(result) *result = (tjs_int)1; return TJS_S_OK; }
tjsDictionary把读取数据的工作交给了tjsCustomObject(Owner)。Owner接下来会遍历自己所储存的属性,参见下面引用自tjsObject.cpp的几段代码;遍历的时候会调用一个callback,也就是上面的FuncCall()。
注意到这个FuncCall里是如何处理Object类型的数据的:它调用了tTJSArrayNI::SaveStructureDataForObject(),实现如下:
tjsArray.cpp/1003 写道
void tTJSArrayNI::SaveStructuredDataForObject(iTJSDispatch2 *dsp, std::vector<iTJSDispatch2 *> &stack, tTJSStringAppender &string, const ttstr &indentstr) { // check object recursion std::vector<iTJSDispatch2 *>::iterator i; for(i = stack.begin(); i!=stack.end(); i++) { if(*i == dsp) { // object recursion detected string += TJS_W("null /* object recursion detected */"); return; } } // determin dsp's object type tTJSDictionaryNI *dicni = NULL; tTJSArrayNI *arrayni = NULL; if(dsp && TJS_SUCCEEDED(dsp->NativeInstanceSupport(TJS_NIS_GETINSTANCE, TJSGetDictionaryClassID(), (iTJSNativeInstance**)&dicni)) ) { // dictionary stack.push_back(dsp); dicni->SaveStructuredData(stack, string, indentstr); stack.pop_back(); } else if(dsp && TJS_SUCCEEDED(dsp->NativeInstanceSupport(TJS_NIS_GETINSTANCE, ClassID_Array, (iTJSNativeInstance**)&arrayni)) ) { // array stack.push_back(dsp); arrayni->SaveStructuredData(stack, string, indentstr); stack.pop_back(); } else if(dsp != NULL) { // other objects string += TJS_W("null /* (object) \""); // stored as a null tTJSVariant val(dsp,dsp); string += ttstr(val).EscapeC(); string += TJS_W("\" */"); } else { // null string += TJS_W("null"); } }
问题就出在这里了。这个函数的逻辑只会正确保存字典、数组或者null;其它Object类型的对象都被保存为了null。
于是这是一个by design的问题了吧……sigh。真是糟糕的决定。
-------------------------------------------------------------------------------
tjsObject.cpp/1202 写道
void tTJSCustomObject::InternalEnumMembers(tjs_uint32 flags, tTJSVariantClosure *callback, iTJSDispatch2 *objthis) { // enumlate members by calling callback. // note that member changes(delete or insert) through this function is not guaranteed. if(!callback) return; tTJSVariant name; tTJSVariant newflags; tTJSVariant value; tTJSVariant * params[3] = { &name, &newflags, &value }; const tTJSSymbolData * lv1 = Symbols; const tTJSSymbolData * lv1lim = lv1 + HashSize; for(; lv1 < lv1lim; lv1++) { const tTJSSymbolData * d = lv1->Next; while(d) { const tTJSSymbolData * nextd = d->Next; if(d->SymFlags & TJS_SYMBOL_USING) { if(!CallEnumCallbackForData(flags, params, *callback, objthis, d)) return ; } d = nextd; } if(lv1->SymFlags & TJS_SYMBOL_USING) { if(!CallEnumCallbackForData(flags, params, *callback, objthis, lv1)) return ; } } }
tjsObject.cpp/1177 写道
bool tTJSCustomObject::CallEnumCallbackForData( tjs_uint32 flags, tTJSVariant ** params, tTJSVariantClosure & callback, iTJSDispatch2 * objthis, const tTJSCustomObject::tTJSSymbolData * data) { tjs_uint32 newflags = 0; if(data->SymFlags & TJS_SYMBOL_HIDDEN) newflags |= TJS_HIDDENMEMBER; if(data->SymFlags & TJS_SYMBOL_STATIC) newflags |= TJS_STATICMEMBER; *params[0] = data->Name; *params[1] = (tjs_int)newflags; if(!(flags & TJS_ENUM_NO_VALUE)) { // get value if(TJS_FAILED(TJSDefaultPropGet(flags, *(tTJSVariant*)(&(data->Value)), params[2], objthis))) return false; } tTJSVariant res; if(TJS_FAILED(callback.FuncCall(NULL, NULL, NULL, &res, (flags & TJS_ENUM_NO_VALUE) ? 2 : 3, params, NULL))) return false; return (bool)(tjs_int)(res); }
tjsObject.cpp/1342 写道
tjs_error TJSDefaultPropGet(tjs_uint32 flag, tTJSVariant &targ, tTJSVariant *result, iTJSDispatch2 *objthis) { if(!(flag & TJS_IGNOREPROP)) { // if TJS_IGNOREPROP is not specified // if member's type is tvtObject, call the object's PropGet with "member=NULL" // ( default member invocation ). if it is succeeded, return its return value. // if the PropGet's return value is TJS_E_ACCESSDENYED, // return as an error, otherwise return the member itself. if(targ.Type() == tvtObject) { tTJSVariantClosure tvclosure = targ.AsObjectClosure(); tjs_error hr = TJS_E_NOTIMPL; try { if(tvclosure.Object) { hr = tvclosure.Object->PropGet(0, NULL, NULL, result, TJS_SELECT_OBJTHIS(tvclosure, objthis)); } } catch(...) { tvclosure.Release(); throw; } tvclosure.Release(); if(TJS_SUCCEEDED(hr)) return hr; if(hr != TJS_E_NOTIMPL && hr != TJS_E_INVALIDTYPE && hr != TJS_E_INVALIDOBJECT) return hr; } } // return the member itself if(!result) return TJS_E_INVALIDPARAM; result->CopyRef(targ); return TJS_S_OK; }
发表评论
-
habakiri / kirikirij notes
2013-01-19 01:36 0编译startup.tjs时的stack trace: Thr ... -
自己关于VM的帖的目录
2009-04-07 14:02 69656JavaEye的blog系统只允许把帖放到单一类别下,而不能用 ... -
吉里吉里2 2.30版正式发布了
2008-09-16 03:10 16703这应该是不到两个小时之前才发生的事吧……嗯应该还算是新闻。 ... -
吉里吉里2相关的一些引用资料
2008-02-20 17:15 4164[後知後覺]吉里吉里與KAG引擎與Fate/StayNight ... -
吉里吉里2 2.28 rev3发布
2008-01-23 17:41 36322008/01/22 引用レイヤの重ね合わせ方によってはまれに ... -
[笔记] 关于KAG3中宏参数的类型
2008-01-07 20:03 2134[macro name=addFiveZero] [eval ... -
吉里吉里3观察记录(2008-01-05 3523)
2008-01-05 19:27 2710其实应该是到2008-01-03的3511,不过我机上是更新到 ... -
把吉里吉里3 revision 3419中Risse的部分build了出来测试
2007-11-16 16:36 3684把吉里吉里3 revision 3419中Risse的部分bu ... -
KAGEX revision 3614更新
2007-11-14 12:45 2000今天刚出现的更新: 最 ... -
TJS2中对象的表示方法,其代表的运行时环境,与闭包的关系
2007-11-03 09:30 3133对一个对象实例调用(string)转换时,可能会看到这样的结果 ... -
吉里吉里1/吉里吉里2中KAG脚本的解释执行(1)
2007-10-29 18:35 9553从我开始关注吉里吉里2这个引擎开始,就一直看到关于“KAG的执 ... -
吉里吉里2中TJS2 VM的dispatch loop
2007-10-29 15:03 4818稍微在吉里吉里2.28的源代码里找了下TJS2 VM的执行机制 ...
相关推荐
在Unity游戏引擎开发中,数据序列化是一个至关重要的环节,特别是在保存和加载游戏状态、传输数据或者进行网络通信时。`SerializableDictionary`是Unity社区为解决标准字典类型(`System.Collections.Generic....
《构建个人搜索引擎:深入理解Lucene 2.0与Heritrix》 在这个数字化时代,搜索引擎已经成为我们获取信息的关键...无论你是想打造个人搜索引擎,还是希望在企业级应用中集成搜索功能,这份资料都将是你宝贵的参考资料。
InnoDB是一个提供了ACID事务支持的存储引擎,它在MySQL中被广泛使用,特别是在事务性数据库系统中。InnoDB为表提供了行级锁定和外键约束的特性,并且通过MVCC(多版本并发控制)机制支持高并发读写操作。 2. InnoDB...
- 数据字典缓存(data dictionary cache):存储最近使用的对象定义 - PL/SQL 缓冲区(PL/SQL buffer):用于存储过程、函数、打包的过程、打包的函数、对象类型定义和触发器。 - **大型池(Large Buffer)**:用于共享...
通过上述资源的学习,无论是Redis的新手还是有经验的开发者,都能找到适合自己的学习路径,从理论到实践,逐步掌握Redis的使用和优化技巧,充分发挥其在数据处理和存储方面的强大能力。Redis作为一个高度可扩展和多...
**`Dictionary`的数据结构实现**: 1. **基本结构**:`Dictionary, TValue>`使用哈希表作为基础数据结构。 2. **数组与桶**:创建两个数组,一个是`entries`数组用于存放键值对,另一个是`buckets`数组用于记录键的...
倒排索引由多个组件构成,如Segment Info文件记录了段的信息,Term Dictionary用于存储所有唯一词项及其对应的文档频率和位置信息,Posting List则保存了每个词项在哪些文档出现以及它们的位置。Nluke允许用户查看...
在实现中,assertion就是在程序中的一条语句,它对一个boolean表达式进行检查,一个正确程序必须保证这个boolean表达式的值为true;如果该值为false,说明程序已经处于不正确的状态下,系统将给出警告或退出。...
包含在Windows 2000中的Active Server Pages 3.0 (ASP 3.0)是Microsoft公司推出的又一个支持Internet的功能强大的网页制作软件包,除了继续保持其适应于各种浏览器的基本特征外,与ASP 2.0相比,功能更加强大,目前...
包含在Windows 2000中的Active Server Pages 3.0 (ASP 3.0)是Microsoft公司推出的又一个支持Internet的功能强大的网页制作软件包,除了继续保持其适应于各种浏览器的基本特征外,与ASP 2.0相比,功能更加强大,目前...