java開發(fā)編譯器:把結(jié)構(gòu)體數(shù)組編譯成java字節(jié)碼

由于本節(jié)代碼邏輯有點(diǎn)復(fù)雜醋虏,請參看視頻用java開發(fā)C語言編譯器 以便加深理解和掌握

上一節(jié)寻咒,我們在C程序中引入結(jié)構(gòu)體,在編譯成java字節(jié)碼時颈嚼,我們把結(jié)構(gòu)體轉(zhuǎn)換成一個只包含公有數(shù)據(jù)成員的類毛秘,于是我們把含有結(jié)構(gòu)體的C代碼成功編譯成了java字節(jié)碼,這節(jié)我們要在上一節(jié)的基礎(chǔ)上加大難度阻课,把結(jié)構(gòu)體變成結(jié)構(gòu)體數(shù)組叫挟,看看我們的編譯器是如何把含有結(jié)構(gòu)體數(shù)組的C代碼編譯成java字節(jié)碼的。完成本節(jié)代碼后限煞,我們的編譯器能把下面C代碼編譯成java字節(jié)碼并能在虛擬機(jī)上正確運(yùn)行:

struct CTag {
    int x;
    char c;
};

void main() {
   struct CTag myTag[5];
   myTag[2].x = 1;

   printf("the x value of second struct object is :%d", myTag[2].x);
}

end

我們把C語言中的結(jié)構(gòu)體等價于java虛擬機(jī)上的一個類抹恳,那么結(jié)構(gòu)體數(shù)組自然就可以對應(yīng)于java上的類數(shù)組,由此我們先看看jvm提供了那些指令讓我們操作類數(shù)組署驻,以及這些指令的用法奋献。

在jvm上健霹,要想生成一個類數(shù)組,需要用到的指令是anewarray,在使用這個指令之前瓶蚂,我們需要在堆棧上壓入一個數(shù)值糖埋,用于表示要生成的數(shù)組長度。假定有一行java代碼如下:

String[] ss = new String[5];

要把上面的代碼轉(zhuǎn)換成虛擬機(jī)字節(jié)碼時扬跋,我們需要這么做阶捆,首先把數(shù)組的元素個數(shù)5壓入到堆棧凌节,然后使用anewarray 指令在堆棧上創(chuàng)建一個String數(shù)組對象钦听,代碼如下:

sipush 5
anewarray java/lang/String

anewarray 指令后面跟著類的類型,上面的指令在堆棧上生成了一個字符串?dāng)?shù)組對象倍奢,字符串含有5個元素朴上,每個元素是一個指向heap上String類型實例的引用,由于我們只是生成了一個含有5個元素的數(shù)組卒煞,jvm會自動把數(shù)組中的五個元素初始化為null,上面字節(jié)碼執(zhí)行后痪宰,虛擬機(jī)的堆棧情況如下:


這里寫圖片描述

注意到,anewarray 指令在堆棧頂部生成了一個引用畔裕,這個引用指向了一個存在heap里的一個含有5個字符串類型的數(shù)組實例衣撬,并且實例中的每個元素都指向null.

要想使用字符串?dāng)?shù)組,我們就必須使得數(shù)組中的元素都指向一個字符串實例扮饶,這就需要使用到指令aastore, 和 aaload, 例如我們想讓字符串?dāng)?shù)組的第0和第1個元素分別指向字符串"hello"和"world", 那么我們需要確保anewarray生成的字符串?dāng)?shù)組對象在堆棧頂部具练,接著把數(shù)組元素的下標(biāo)壓入堆棧,最后再把具體字符串壓入堆棧頂部甜无,然后執(zhí)行一次aastore指令扛点,于是要想讓ss[0]指向字符串"hello",相應(yīng)的指令如下(承接上面代碼岂丘,于是字符串?dāng)?shù)組對象已經(jīng)存在堆棧頂部):

astore 0
aload  0
sipush 0
ldc "hello"
aastore

由于ss[0]對象已經(jīng)存在堆棧上陵究,所以指令astore 0先把它存儲到局部變量隊列的第0個位置,然后用aload 0再次把它從局部隊列加載到堆棧頂部奥帘,這么做看似多此一舉铜邮,但這對后面的指令有作用。然后把要賦值的數(shù)組元素下標(biāo)放到堆棧頂部寨蹋,也就對應(yīng)sipush 0松蒜, 最后把數(shù)組元素0要引用的字符串"hello"壓到堆棧頂部,因此在執(zhí)行指令aastore前钥庇,堆棧情況如下:
stack: ss[0] "hello" 0
執(zhí)行指令aastore后牍鞠,元素ss[0]就不再是null了,它會指向字符串"hello"评姨。指令aastore執(zhí)行后难述,堆棧上所有元素會被清空萤晴。為了把字符串?dāng)?shù)組下標(biāo)為1的元素指向字符串"world",我們需要把字符串?dāng)?shù)組對象重新加載到堆棧上,所以需要執(zhí)行指令aload 0, 接著把下標(biāo)1壓入堆棧胁后,最后把字符串"world"壓入堆棧店读,然后再次執(zhí)行aastore指令,相關(guān)代碼如下:

aload 0
sipush 0
ldc "world"
aastore

上面兩段代碼執(zhí)行后攀芯,堆棧情況如下:


這里寫圖片描述

既然數(shù)組的元素0和1都已經(jīng)指向了兩個有效的字符串屯断,如果程序想要通過這兩個數(shù)組元素獲取他們指向的字符串,那么就需要使用指令aaload,例如要想訪問ss[0]執(zhí)行的字符串"hello"侣诺,那么我們需要把字符串?dāng)?shù)組對象加載到堆棧頂部殖演,燃爆把元素下標(biāo)壓入堆棧,然后執(zhí)行aaload指令年鸳,代碼如下:

aload 0
sipush 0
aaload

上面代碼執(zhí)行后趴久,堆棧頂部存儲的是一個String類型的引用,這個引用指向存儲在Heap里面的字符串實例"hello"搔确”斯鳎基于這些原理,我們就可以把C語言中含有結(jié)構(gòu)體數(shù)組的代碼編譯成java字節(jié)碼了膳算,我們完全可以照貓畫虎座硕,把上面的String對象換成結(jié)構(gòu)體對象就可以了。

回到前面的C語言代碼涕蜂,對照上面的原理华匾,我們看看如何把含有結(jié)構(gòu)體數(shù)組的C語言代碼編譯成字節(jié)碼。編譯器解讀代碼時宇葱,當(dāng)解讀到這一句:struct CTag myTag[5];瘦真,它會創(chuàng)建一個Symbol對象,該對象對應(yīng)的變量名稱為myTag,并記錄下黍瞧,它是一個含有5個元素的數(shù)組诸尽。

當(dāng)編譯器讀取語句:myTag[2].x = 1;時,它會使用anewarray指令生成一個CTag類的數(shù)組印颤,按照前面講過的指令用法您机,我們的編譯器會產(chǎn)生如下指令:
(代碼片段1)

sipush  5
anewarray   CTag
astore  0

同時編譯器此時知道,代碼想對數(shù)組中的第二個對象中的x成員賦值為1年局,前面講過际看,anewarray 指令只是生成了數(shù)組對象,數(shù)組中的每個元素會被初始化為null,此時要對第二個元素指向的對象進(jìn)行賦值矢否,那么就必須為第二個元素生成一個CTag類的實例仲闽,于是編譯器要接著生成如下指令,(代碼片段二):

    aload   0
    sipush  2
    new CTag
    dup
    invokespecial   CTag/<init>()V
    aastore

上面指令把CTag類型數(shù)組對象加載到堆棧上后僵朗,把數(shù)組元素的下標(biāo)2壓入堆棧赖欣,然后創(chuàng)建一個CTag實例對象屑彻,初始化后,通過aastore指令把該對象的引用存入到數(shù)組對象的第二個元素顶吮。

接著代碼需要對第二個元素指向的實例所包含的成員x賦值社牲,于是編譯器需要生成指令,把數(shù)組第二個元素引用的實例加載到堆棧上悴了,然后使用上一節(jié)講過的方法對類成員賦值搏恤,于是它會產(chǎn)生如下指令,(代碼片段3):

    aload   0
    sipush  2
    aaload
    sipush  1
    putfield    CTag/x I

aload 0 把數(shù)組對象加載到堆棧后湃交,壓入要訪問的元素下標(biāo)2熟空,通過aaload指令把元素二引用的類實例加載到堆棧上,使用上節(jié)講過的指令修改類實例的成員變量巡揍。

最后編譯器解讀到語句:printf("the x value of second struct object is :%d", myTag[2].x); 痛阻,這條語句中,我們需要讀取數(shù)組第二個元素指向?qū)嵗念惓蓡Tx,那么編譯器需要生成指令腮敌,把它加載到堆棧上,然后使用上一節(jié)的辦法俏扩,讀取類實例的成員變量值糜工,于是會生成如下代碼(指令片段4):

    aload   0
    sipush  2
    aaload
    getfield    CTag/x I

上面指令先把數(shù)組對象加載到堆棧上,然后把要訪問的數(shù)值元素下標(biāo)2壓到堆棧录淡,接著使用aaload指令把元素2指向的類實例加載到堆棧上捌木,然后使用指令getfield 獲取該實例成員變量x 的值。

按照上面描述的指令生成步驟嫉戚,我們要對編譯器做相應(yīng)代碼修改刨裆。在解析語句myTag[2].x = 1;時,代碼會進(jìn)入到UnaryNodeExecutor.java中彬檀,于是我們做如下代碼修改:

case CGrammarInitializer.Unary_LB_Expr_RB_TO_Unary:
            child = root.getChildren().get(0);
            symbol = (Symbol)child.getAttribute(ICodeKey.SYMBOL);
            
            child = root.getChildren().get(1);
            int index = (Integer)child.getAttribute(ICodeKey.VALUE);
            
            try {
                Declarator declarator = symbol.getDeclarator(Declarator.ARRAY);
                if (declarator != null) {
                    
                    Object val = declarator.getElement(index);
                    root.setAttribute(ICodeKey.VALUE, val);
                    ArrayValueSetter setter = new ArrayValueSetter(symbol, index);
                    root.setAttribute(ICodeKey.SYMBOL, setter);
                    root.setAttribute(ICodeKey.TEXT, symbol.getName()); 
                    
                    //create array object on jvm
                    if (symbol.getSpecifierByType(Specifier.STRUCTURE) == null) {
                        //如果是結(jié)構(gòu)體數(shù)組帆啃,這里不做處理,留給下一步處理
                        ProgramGenerator.getInstance().createArray(symbol);
                        ProgramGenerator.getInstance().readArrayElement(symbol, index);
                    }
                }
                ....

當(dāng)編譯器解析到數(shù)組讀取時會執(zhí)行上面代碼窍帝,同時生成與數(shù)組讀取相關(guān)的jvm指令努潘,這里我們先判斷讀取的數(shù)組是否是結(jié)構(gòu)體,如果是坤学,那么就不能直接生成數(shù)組讀取指令疯坤。執(zhí)行完上面代碼后,又會進(jìn)入下面的代碼進(jìn)行執(zhí)行:

 case CGrammarInitializer.Unary_StructOP_Name_TO_Unary:
            /*
             * 當(dāng)編譯器讀取到myTag.x 這種類型的語句時深浮,會走入到這里
             */
            child = root.getChildren().get(0);
            String fieldName = (String)root.getAttribute(ICodeKey.TEXT);
            Object object = child.getAttribute(ICodeKey.SYMBOL);
            boolean isStructArray = false;
        
            if (object instanceof ArrayValueSetter) {
                //這里表示正在讀取結(jié)構(gòu)體數(shù)組,先構(gòu)造結(jié)構(gòu)體數(shù)組
                symbol = getStructSymbolFromStructArray(object);
                symbol.addValueSetter(object);
                isStructArray = true;
            } else {
                symbol = (Symbol)child.getAttribute(ICodeKey.SYMBOL);
            }
        ....

當(dāng)編譯器讀取到類似myTag.x這樣類型的代碼時压怠,它知道此時程序正在對結(jié)構(gòu)體進(jìn)行處理,于是會進(jìn)入到上面的代碼飞苇,由于當(dāng)代碼需要讀取數(shù)組時菌瘫,編譯器都會為數(shù)組的讀寫構(gòu)造一個ArrayValueSetter對象洋闽,上面代碼處理的是結(jié)構(gòu)體的讀寫,同時編譯器又發(fā)現(xiàn)傳過來的對象是一個ArrayValueSetter實例突梦,于是編譯器就會明白诫舅,當(dāng)前處理的是一個結(jié)構(gòu)體數(shù)組對象,然后代碼會調(diào)用getStructSymbolFromStructArray,這個函數(shù)的作用是為結(jié)構(gòu)體數(shù)組下標(biāo)為2的元素生成一個結(jié)構(gòu)體對象實例宫患,并且生成前面講過的指令片段1刊懈,我們看看該函數(shù)的實現(xiàn):

private Symbol getStructSymbolFromStructArray(Object object) {
        ArrayValueSetter vs = (ArrayValueSetter)object;
        Symbol symbol = vs.getSymbol();
        int idx = vs.getIndex();
        Declarator declarator = symbol.getDeclarator(Declarator.ARRAY);
        if (declarator == null) {
            return null;
        }
        
        ProgramGenerator.getInstance().createStructArray(symbol);
        
        Symbol struct = null;
        try {
            struct = (Symbol)declarator.getElement(idx);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        if (struct == null) {
            struct = symbol.copy();
            try {
                declarator.addElement(idx, (Object)struct);
                //通過指令為數(shù)組中的某個下標(biāo)處創(chuàng)建結(jié)構(gòu)體實例
                ProgramGenerator.getInstance().createInstanceForStructArray(symbol, idx);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        return struct;
    }

該函數(shù)先為myTag生成一個同類型的結(jié)構(gòu)體Symbol對象,其中代碼執(zhí)行了語句
ProgramGenerator.getInstance().createStructArray(symbol);
這條語句的作用是娃闲,生成前面說過的指令片段1虚汛,同時上面代碼還執(zhí)行了語句:
ProgramGenerator.getInstance().createInstanceForStructArray(symbol, idx);
它的作用是生成指令片段2.這兩個函數(shù)的具體實現(xiàn),在后面我們才羅列出來皇帮,代碼執(zhí)行完后卷哩,回到getStructSymbolFromStructArray函數(shù)的調(diào)用處繼續(xù)往下執(zhí)行:

....
//先把結(jié)構(gòu)體變量的作用范圍設(shè)置為定義它的函數(shù)名
            if (isStructArray == true) {
                //把結(jié)構(gòu)體數(shù)組symbol對象的作用域范圍設(shè)置為包含它的函數(shù)
                ArrayValueSetter vs = (ArrayValueSetter)object;
                Symbol structArray = vs.getSymbol();
                structArray.addScope(ProgramGenerator.getInstance().getCurrentFuncName());
            } else {
                symbol.addScope(ProgramGenerator.getInstance().getCurrentFuncName());
            }
            //如果是第一次訪問結(jié)構(gòu)體成員變量,那么將結(jié)構(gòu)體聲明成一個類
            ProgramGenerator.getInstance().putStructToClassDeclaration(symbol);
  ....
  /*
             * 假設(shè)當(dāng)前解析的語句是myTag.x, 那么args對應(yīng)的就是變量x
             * 通過調(diào)用setStructParent 把a(bǔ)rgs對應(yīng)的變量x 跟包含它的結(jié)構(gòu)體變量myTag
             * 關(guān)聯(lián)起來
             */
            Symbol args = symbol.getArgList();
            while (args != null) {
                if (args.getName().equals(fieldName)) {
                    args.setStructParent(symbol);
                    break;
                }
                
                args = args.getNextSymbol();
            }
            
            if (args == null) {
                System.err.println("access a filed not in struct object!");
                System.exit(1);
            }
            /*
             * 把讀取結(jié)構(gòu)體成員變量轉(zhuǎn)換成對應(yīng)的java匯編代碼,也就是使用getfield指令把對應(yīng)的成員變量的值讀取出來属拾,然后壓入堆棧頂部
             */
            //TODO 需要區(qū)分結(jié)構(gòu)體是否來自于結(jié)構(gòu)體數(shù)組
            if (args.getValue() != null) {
                ProgramGenerator.getInstance().readValueFromStructMember(symbol, args);
            }
        ....        

代碼先把變量myTag的作用域設(shè)置成包含它的函數(shù)将谊,也就是main函數(shù),然后調(diào)用ProgramGenerator.getInstance().putStructToClassDeclaration(symbol);
將結(jié)構(gòu)體聲明成一個java的類聲明渐白。當(dāng)要讀取結(jié)構(gòu)體數(shù)組第二個元素的成員變量x的時候尊浓,ProgramGenerator.getInstance().readValueFromStructMember(symbol, args);
會被執(zhí)行,它的作用是生成前面講過的指令片段4.

C語言代碼中myTag[2].x = 1; 這條語句的目標(biāo)是對結(jié)構(gòu)體數(shù)組第二個元素的成員變量x賦值1纯衍,編譯器在解釋執(zhí)行這個賦值操作時栋齿,會進(jìn)入到Symbol.java,執(zhí)行如下代碼:

@Override
    public void setValue(Object obj) {
        if (obj != null) {
            System.out.println("Assign Value of " + obj.toString() + " to Variable " + name);   
        }
        
        this.value = obj;
        
        if (this.value != null) {
            /*
             * 先判斷該變量是否是一個結(jié)構(gòu)體的成員變量襟诸,如果是瓦堵,那么需要通過assignValueToStructMember來實現(xiàn)成員變量
             * 的賦值,如果不是歌亲,那么就直接通過IStore語句直接賦值
             */
            ProgramGenerator generator = ProgramGenerator.getInstance();
            if (this.isStructMember() == false) {
                int idx = generator.getLocalVariableIndex(this);
                if (generator.isPassingArguments() == false) {
                    generator.emit(Instruction.ISTORE, "" + idx);   
                }   
            } else {
                
                if (this.getStructSymbol().getValueSetter() != null) {
                    generator.assignValueToStructMemberFromArray(this.getStructSymbol().getValueSetter(), this, this.value);
                } else {
                    generator.assignValueToStructMember(this.getStructSymbol(), this, this.value);  
                }
                
            }
            
        }
        
    }

代碼先判斷菇用,當(dāng)前的賦值操作是否是針對結(jié)構(gòu)體數(shù)組中的某個元素進(jìn)行的,如果是应结,那么generator.assignValueToStructMemberFromArray(this.getStructSymbol().getValueSetter(), this, this.value);
這條語句就會執(zhí)行刨疼,它的作用是生成代碼片段3.

由于篇幅原因,generator生成java指令的實現(xiàn)將會在視頻中再做解析鹅龄,更詳細(xì)的講解和代碼調(diào)試演示過程揩慕,請參看視頻
用java開發(fā)C語言編譯器

上面代碼完成后,編譯器會把給定的C語言代碼編譯成如下java匯編語言:

.class public CSourceToJava
.super java/lang/Object

.method public static main([Ljava/lang/String;)V
    sipush  2
    sipush  5
    anewarray   CTag
    astore  0
    aload   0
    sipush  2
    new CTag
    dup
    invokespecial   CTag/<init>()V
    aastore
    sipush  1
    aload   0
    sipush  2
    aaload
    sipush  1
    putfield    CTag/x I
    sipush  2
    aload   0
    sipush  2
    aaload
    getfield    CTag/x I
    istore  1
    getstatic   java/lang/System/out Ljava/io/PrintStream;
    ldc "the x value of second struct object is :"
    invokevirtual   java/io/PrintStream/print(Ljava/lang/String;)V
    getstatic   java/lang/System/out Ljava/io/PrintStream;
    iload   1
    invokevirtual   java/io/PrintStream/print(I)V
    getstatic   java/lang/System/out Ljava/io/PrintStream;
    ldc "
"
    invokevirtual   java/io/PrintStream/print(Ljava/lang/String;)V
    return
.end method
.end class
.class public CTag
.super java/lang/Object
.field public c C
.field public x I
.method public <init>()V
    aload   0
    invokespecial   java/lang/Object/<init>()V
    aload   0
    sipush  0
    putfield    CTag/c C
    aload   0
    sipush  0
    putfield    CTag/x I
    return
.end method
.end class


把這些匯編語言編譯成字節(jié)碼扮休,在虛擬機(jī)上運(yùn)行后迎卤,結(jié)果如下:


這里寫圖片描述

通過運(yùn)行結(jié)果可以得知,編譯器生成的字節(jié)碼在虛擬機(jī)上運(yùn)行的結(jié)果跟C語言代碼想要的結(jié)果是完全一樣的玷坠。由于本節(jié)代碼邏輯有點(diǎn)復(fù)雜蜗搔,請參看視頻用java開發(fā)C語言編譯器 以便加深理解和掌握劲藐。

更多技術(shù)信息,包括操作系統(tǒng)樟凄,編譯器聘芜,面試算法,機(jī)器學(xué)習(xí)缝龄,人工智能汰现,請關(guān)照我的公眾號:


這里寫圖片描述
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市叔壤,隨后出現(xiàn)的幾起案子瞎饲,更是在濱河造成了極大的恐慌,老刑警劉巖炼绘,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嗅战,死亡現(xiàn)場離奇詭異,居然都是意外死亡俺亮,警方通過查閱死者的電腦和手機(jī)驮捍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來铅辞,“玉大人厌漂,你說我怎么就攤上這事≌迳海” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵富纸,是天一觀的道長囤踩。 經(jīng)常有香客問我,道長晓褪,這世上最難降的妖魔是什么堵漱? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮涣仿,結(jié)果婚禮上勤庐,老公的妹妹穿的比我還像新娘。我一直安慰自己好港,他們只是感情好愉镰,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著钧汹,像睡著了一般丈探。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拔莱,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天碗降,我揣著相機(jī)與錄音隘竭,去河邊找鬼滩报。 笑死集侯,一個胖子當(dāng)著我的面吹牛叠纷,可吹牛的內(nèi)容都是我干的条舔。 我是一名探鬼主播飞崖,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼栈幸,長吁一口氣:“原來是場噩夢啊……” “哼山宾!你這毒婦竟也來了拢蛋?” 一聲冷哼從身側(cè)響起笔咽,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤搔预,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后叶组,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拯田,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年甩十,在試婚紗的時候發(fā)現(xiàn)自己被綠了船庇。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡侣监,死狀恐怖鸭轮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情橄霉,我是刑警寧澤窃爷,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站姓蜂,受9級特大地震影響按厘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜钱慢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一逮京、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧束莫,春花似錦懒棉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至挟裂,卻和暖如春享钞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工栗竖, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留暑脆,地道東北人。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓狐肢,卻偏偏與公主長得像添吗,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子份名,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容