カットの下で:asmプロキシメソッドを「破棄」し、panic()メソッドを実装し、ランタイムエラーをサポートします。
asmのすべてのスタブメソッドとプロキシメソッドを覚えていますか? 捨てて忘れてください。 残りのファイルは、multiboot.sとruntime.sの2つだけです。multiboot.sの内容は変更されず、runtime.sはこれに縮小されます。
global dummy dummy: ; ret global __unsafe_get_addr; convert uint32 to pointer __unsafe_get_addr: push ebp mov ebp, esp mov eax, [ebp+8] mov esp, ebp pop ebp ret
残りはすべて容赦なくナイフの下に置いた。
DATAタスクの後に、テキストセクションでlink.ldを開きます。
__go_new = go.runtime.New; __go_new_nopointers = go.runtime.New; __go_print_string = go.screen.PrintStr; __go_print_empty_interface = go.screen.PrintInterface; __go_print_nl = go.screen.PrintNl; __go_print_pointer = go.screen.PrintHex; __go_print_uint64 = go.screen.PrintUint64; __go_runtime_error = go.runtime.RuntimeError; __go_panic = go.runtime.Panic; runtime.efacetype = go.runtime.InterfaceType; runtime.ifacetypeeq = go.runtime.InterfaceTypeEq; runtime.ifaceE2T2 = go.runtime.InterfaceE2T2; __go_type_hash_identity = go.runtime.TypeHashIdentity; __go_type_equal_identity = go.runtime.TypeEqualIdentity; __go_strcmp = go.runtime.StrCmp; __go_type_hash_error = dummy; __go_type_equal_error = dummy; __go_register_gc_roots = dummy; __go_type_hash_identity_descriptor = dummy; __go_type_equal_error_descriptor = dummy; __go_type_equal_identity_descriptor = dummy; __go_type_hash_error_descriptor = dummy; __go_type_hash_empty_interface = dummy; __go_empty_interface_compare = dummy; __go_type_hash_string = dummy; __go_type_equal_string = dummy; __go_type_equal_empty_interface = dummy; __go_type_hash_string_descriptor = dummy; __go_type_equal_string_descriptor = dummy; __go_type_hash_empty_interface_descriptor = dummy; __go_type_equal_empty_interface_descriptor = dummy;
はい、急いで警告します。実装した方法の一部はまだ説明されていないため、ダミーに置き換えるか、自分で実装する必要があります。
ここで何が起こっていますか? シンボルエイリアスを作成します。これは、asmでプロキシメソッドを記述するよりもはるかに優れたソリューションです。
さて、私が捨てたasm-proxyを約束したので、パニックメソッドの実装に取り掛かりましょう。
この記事から 、TypeDescriptor、EmptyInterface、およびUncommonの構造をruntime.goファイルにコピーします。
追加する:
//, type PanicStack struct { Next *PanicStack // Arg interface{} // WasRecovered bool // IsForeign bool // } // panic() func Panic(arg interface{}) { //stackTrace(3) //, , , =) p := PanicStack{} // . : , p.Arg = arg // PrintPanic(&p) // for { // } } // func PrintPanic(p *PanicStack) { if p.Next != nil { // , () PrintPanic(p.Next) print("\t") } print("panic: ") // print(p.Arg) // if p.WasRecovered { // print("[recovered]") // } print("\n") }
正直なところ、パニック引数が文字列である場合、ここで終了できますが、悲しいかな-空のインターフェイスなので、空のインターフェイスの印刷を実装し、それに応じてインターフェイスを型にキャストする必要があります、これを行います。 ワーニング:このセクションで書かれたすべてのコードはMVPの初期段階の鮮明な例であり、もちろん動作しますが、あまりにも原始的であり、欠陥があります。
runtime.goでまだ作業中であることを思い出させてください。
// func StrCmp(s1, s2 string) int { if len(s1) < len(s2) { return -1 } if len(s2) < len(s1) { return 1 } for i := 0; i < len(s1); i++ { if s1[i] < s2[i] { return -1 } if s2[i] < s1[i] { return 1 } } return 0 } // TypeDescriptor func InterfaceType(arg *EmptyInterface) TypeDescriptor { return *(arg.__type_descriptor) } // , , func InterfaceTypeEq(arg1, arg2 *EmptyInterface) bool { return *(arg1.__type_descriptor.string) == *(arg2.__type_descriptor.string) } // //iface - TypeDescriptor //e - //ret - , // ok - func InterfaceE2T2(iface *TypeDescriptor, e EmptyInterface, ret uint32) (ok bool) { if *(iface.string) == *(e.__type_descriptor.string) { // memcpy(ret, e.__object, uint32(iface.size)) // return true } else { return false } }
screen.goファイルに切り替えます。
// , func PrintInterface(arg interface{}) { v, ok := arg.(string) if ok { print(v) } }
さて、コード(kernel.go、Loadメソッド)を記述できます。
panic("Habrahabr")
そして、qemuの出力に感心します。
panic: Habrahabr
すでに悪くないですよね? しかし、ランタイムエラー処理も約束しました。
runtime.go:
// const ( SLICE_INDEX_OUT_OF_BOUNDS = uint32(iota) ARRAY_INDEX_OUT_OF_BOUNDS STRING_INDEX_OUT_OF_BOUNDS SLICE_SLICE_OUT_OF_BOUNDS ARRAY_SLICE_OUT_OF_BOUNDS STRING_SLICE_OUT_OF_BOUNDS NIL_DEREFERENCE MAKE_SLICE_OUT_OF_BOUNDS MAKE_MAP_OUT_OF_BOUNDS MAKE_CHAN_OUT_OF_BOUNDS DIVISION_BY_ZERO MSG_INDEX_OUT_OF_RANGE = "index out of range" MSG_SLICE_BOUNDS_OUT_OF_RANGE = "slice vounds out of range" MSG_NIL_DEREFERENCE = "nil pointer dereference" MSG_MAKE_SLICE_OUT_OF_BOUNDS = "make slice len or cap out of range" MSG_MAKE_MAP_OUT_OF_BOUNDS = "make map len out of range" MSG_MAKE_CHAN_OUT_OF_BOUNDS = "make chan len out of range" MSG_DIVISION_BY_ZERO = "integer divide by zero" MSG_UNKNOWN = "unknown" ) // , , , , func RuntimeError(i uint32) { switch i { case SLICE_INDEX_OUT_OF_BOUNDS, ARRAY_INDEX_OUT_OF_BOUNDS, STRING_INDEX_OUT_OF_BOUNDS: panic(MSG_INDEX_OUT_OF_RANGE) case SLICE_SLICE_OUT_OF_BOUNDS, ARRAY_SLICE_OUT_OF_BOUNDS, STRING_SLICE_OUT_OF_BOUNDS: panic(MSG_SLICE_BOUNDS_OUT_OF_RANGE) case NIL_DEREFERENCE: panic(MSG_NIL_DEREFERENCE) case MAKE_SLICE_OUT_OF_BOUNDS: panic(MSG_MAKE_SLICE_OUT_OF_BOUNDS) case MAKE_MAP_OUT_OF_BOUNDS: panic(MSG_MAKE_MAP_OUT_OF_BOUNDS) case MAKE_CHAN_OUT_OF_BOUNDS: panic(MSG_MAKE_CHAN_OUT_OF_BOUNDS) case DIVISION_BY_ZERO: panic(MSG_DIVISION_BY_ZERO) default: panic(MSG_UNKNOWN) } }
したがって、今、どこかでミスをすると、それについてのメッセージが表示されます。
ワーニング:前の部分のコードは多くのランタイムエラーを生成しますが、今のところそれらを修正する方法は示しません。宿題にしましょう。
記事の準備にご協力いただきありがとうございます:
Victorya1-校正、 ラフニング
kirill_danshin-それらの議論。 パーツ、興味深い議論
UPD:文字列型へのインターフェイスのキャストの問題に関する興味深い議論
キリル:
私の考えは何ですか
[12:09:22 PM]イヴァン:
注意深く読んだ)
[12:09:35 PM]キリル:
gosh文字列は不変です
[12:09:45 PM]イヴァン:
うん
変更は新しいものの作成につながります
[12:10:25 PM]キリル:
InterfaceE2T2は、理論上、データをコピーしない場合があります
既存のものを参照
[12:12:47 PM]イヴァン:
それはできません、a)文字列だけでなく、b)戻りアドレスを取得し、さらに、ポインタではなく、期待されるタイプのデータをそこに書き込む必要があります
[12:13:31 PM]キリル:
私は何かを見落としていました
申し訳ありませんが、それはできません
私はいつも食べ物のこの制限に休みます
結局のところ、ここでは修正することはできません
[12:16:05 PM]イヴァン:
なぜ、ランタイムの拡張によって可能になるのか、つまり InterfaceToString(iface interface {})メソッドを実装します(str * string、ok bool)
しかし、これはランタイムメソッドではなく、ユーザーになります
うーん...この小さな議論を記事に追加しますか?
[12:17:01 PM]キリル:
はい、来ます
これは興味深いタスクです。遅かれ早かれ解決する必要があります
まったく同じインターフェイス{}-> []バイト
UPD2: Cyrilとの議論から関数を実装しようとしました。