- 浏览: 3019207 次
- 性别:
- 来自: 海外
文章分类
- 全部博客 (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 68859JavaEye的blog系统只允许把帖放到单一类别下,而不能用 ... -
吉里吉里2 2.30版正式发布了
2008-09-16 03:10 16595这应该是不到两个小时之前才发生的事吧……嗯应该还算是新闻。 ... -
吉里吉里2相关的一些引用资料
2008-02-20 17:15 4100[後知後覺]吉里吉里與KAG引擎與Fate/StayNight ... -
吉里吉里2 2.28 rev3发布
2008-01-23 17:41 34982008/01/22 引用レイヤの重ね合わせ方によってはまれに ... -
[笔记] 关于KAG3中宏参数的类型
2008-01-07 20:03 2092[macro name=addFiveZero] [eval ... -
吉里吉里3观察记录(2008-01-05 3523)
2008-01-05 19:27 2623其实应该是到2008-01-03的3511,不过我机上是更新到 ... -
把吉里吉里3 revision 3419中Risse的部分build了出来测试
2007-11-16 16:36 3609把吉里吉里3 revision 3419中Risse的部分bu ... -
KAGEX revision 3614更新
2007-11-14 12:45 1941今天刚出现的更新: 最 ... -
TJS2中对象的表示方法,其代表的运行时环境,与闭包的关系
2007-11-03 09:30 3037对一个对象实例调用(string)转换时,可能会看到这样的结果 ... -
吉里吉里1/吉里吉里2中KAG脚本的解释执行(1)
2007-10-29 18:35 9476从我开始关注吉里吉里2这个引擎开始,就一直看到关于“KAG的执 ... -
吉里吉里2中TJS2 VM的dispatch loop
2007-10-29 15:03 4738稍微在吉里吉里2.28的源代码里找了下TJS2 VM的执行机制 ...
相关推荐
查看数据库数据字典,包括表、视图、索引、存储过程等内容; 支持SQL Server和Oracle两大商业数据库; 能将数据字典导出为Html、XML、Word等文件格式; 免费注册、免费使用; 没有注册,会定时弹出About窗口。
c# Dictionary 使用和 字典的键值对的相关使用
Dictionary对象使用方法 1.Dictionary对象的成员概要 2. 对Dictionary中增加和删除条目 3. 修改键或条目的值
项目使用日语Dictionary
了解Dictionary的开发人员都了解,和List相比,字典添加会慢,但是查找会比较快,那么Dictionary是如何实现的呢? Dictionary的构造 下面的代码我看看Dictionary在构造时都做了什么: private void ...
实现Dictionary正反向排序,解决Dictionary自动排序的问题,个性化排序。
使用linq对dictionary类型数据进行相应处理, 对dictionary类型数据进行查询,修改,删除,dictionary的嵌套应用。
自定义Dictionary类如何在类内实现线程同步自定义Dictionary类如何在类内实现线程同步自定义Dictionary类如何在类内实现线程同步自定义Dictionary类如何在类内实现线程同步自定义Dictionary类如何在类内实现线程同步...
简单的c#使用list/dictionary实现平均成绩代码
在内部, OrderedDictionary使用后备存储,该后备存储由用于存储键值对的Dictionary和用于管理有序键的Array 。 当然,这不是可以实现的性能最高的实现,但是在重用Swift标准库中的大多数功能的
- Unity的inspector不会在inspector中公开Dictionary类, Tvalue> - Unity不序列化字典信息,因此按下播放或重新加载场景时,收集的所有字典数据都会丢失。 解决方案: - 为MonoBehaviour创建自定义编辑器,利用字典 ...
在C#中,我们可以使用Dictionary来实现去重的功能。 通过实例详细讲解c# dictionary Dictionary是按照键来存储和访问数据的,因此如果我们想要去重,就需要使用一个唯一的键来作为Dictionary的键。下面是一个示例...
C#中Json 解析类库,C# 解析json 时,通常使用的是强类型对象反序列化,但是有时候不知道具体的对象时,使用该类库,使用dictionary 解析弱类型数据
Dictionary
Dictionary Learning算法的matlab实现代码
集合Dictionary中按键排序较容易实现,本例实现了C#集合Dictionary中按值的降序排列。
Data dictionary for oracle(数据字典)是一款可查看ORACLE数据字典的工具。
dictionary泛型数据案例,包括list泛型实例
数据字典 The Relational Database Dictionary
本文是关于C#dictionary的用法