將v8當(dāng)成工具使用

將v8變成工具

如何嵌入一個(gè)v8引擎到你的應(yīng)用中

v8除了可以作為一個(gè)獨(dú)立的js引擎之外瓷叫,還可以通過庫的方式嵌入到我們的應(yīng)用中综膀,它以V8 API的方式服務(wù)我們。

我們來看一個(gè)老一點(diǎn)的例子來看看v8 API是如何使用的:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "include/libplatform/libplatform.h"
#include "include/v8.h"
int main(int argc, char* argv[]) {
  v8::V8::InitializeICUDefaultLocation(argv[0]);
  v8::V8::InitializeExternalStartupData(argv[0]);
  std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
  v8::V8::InitializePlatform(platform.get());
  v8::V8::Initialize();
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator =
      v8::ArrayBuffer::Allocator::NewDefaultAllocator();
  v8::Isolate* isolate = v8::Isolate::New(create_params);
  {
    v8::Isolate::Scope isolate_scope(isolate);
    v8::HandleScope handle_scope(isolate);
    v8::Local<v8::Context> context = v8::Context::New(isolate);
    v8::Context::Scope context_scope(context);
    v8::Local<v8::String> source =
        v8::String::NewFromUtf8(isolate, "let a = 2 ** 8; a++;",
                                v8::NewStringType::kNormal)
            .ToLocalChecked();
    v8::Local<v8::Script> script =
        v8::Script::Compile(context, source).ToLocalChecked();
    v8::Local<v8::Value> result = script->Run(context).ToLocalChecked();
    v8::String::Utf8Value utf8(isolate, result);
    printf("%s\n", *utf8);
  }
  isolate->Dispose();
  v8::V8::Dispose();
  v8::V8::ShutdownPlatform();
  delete create_params.array_buffer_allocator;
  return 0;
}

官方的例子是輸出一個(gè)"Hello,World"字符串动知。我覺得這完全沒有展示出這個(gè)例子的強(qiáng)大之處咪鲜,因?yàn)槟芷唇幼址莻€(gè)非常常規(guī)的操作,而我們引進(jìn)來的是可以解析js代碼的引擎滋捶。不寫幾條js語句痛悯,真對不起這個(gè)例子。

我們將剛才的例子存為hello2.cpp, 編譯:

g++ -I. -Iinclude ./hello2.cc -o hello_world2 -lv8_monolith -Lout.gn/x64.release.sample/obj/ -lpthread -std=c++14 -DV8_COMPRESS_POINTERS

v8_monolith這個(gè)庫怎么編譯出來的重窟,我們后面講編譯v8源代碼的時(shí)候會講载萌。

然后運(yùn)行生成的hello_world2,輸出:

256

總體流程就是:

  • 初始化ICU
  • 初始化v8平臺
  • 創(chuàng)建分配器
  • 創(chuàng)建Isolate
  • 創(chuàng)建Scope
  • 創(chuàng)建Context
  • 創(chuàng)建腳本
  • 編譯腳本
  • 運(yùn)行腳本
  • 清理Isolate
  • 清理v8
  • 清理分配器

其中亲族,Isolate代表一個(gè)線程不安全的v8運(yùn)行實(shí)例炒考。其它概念大家基本都可以理解。

新一點(diǎn)的API結(jié)構(gòu)上有一些變化霎迫,我們來看下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "include/libplatform/libplatform.h"
#include "include/v8-context.h"
#include "include/v8-initialization.h"
#include "include/v8-isolate.h"
#include "include/v8-local-handle.h"
#include "include/v8-primitive.h"
#include "include/v8-script.h"

int main(int argc, char* argv[]) {
  v8::V8::InitializeICUDefaultLocation(argv[0]);
  v8::V8::InitializeExternalStartupData(argv[0]);
  std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
  v8::V8::InitializePlatform(platform.get());
  v8::V8::Initialize();
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator =
      v8::ArrayBuffer::Allocator::NewDefaultAllocator();
  v8::Isolate* isolate = v8::Isolate::New(create_params);
  {
    v8::Isolate::Scope isolate_scope(isolate);
    v8::HandleScope handle_scope(isolate);
    v8::Local<v8::Context> context = v8::Context::New(isolate);
    v8::Context::Scope context_scope(context);
    {
      v8::Local<v8::String> source =
          v8::String::NewFromUtf8Literal(isolate, "let f1 = (x) => x*x; f1(1.2);");
      v8::Local<v8::Script> script =
          v8::Script::Compile(context, source).ToLocalChecked();
      v8::Local<v8::Value> result = script->Run(context).ToLocalChecked();
      v8::String::Utf8Value utf8(isolate, result);
      printf("%s\n", *utf8);
    }
  }
  isolate->Dispose();
  v8::V8::Dispose();
  v8::V8::ShutdownPlatform();
  delete create_params.array_buffer_allocator;
  return 0;
}

換個(gè)文件名存斋枢,用剛才一樣的參數(shù)去編譯:

g++ -I. -Iinclude ./hello3.cc -o hello_world3 -lv8_monolith -Lout.gn/x64.release.sample/obj/ -lpthread -std=c++14 -DV8_COMPRESS_POINTERS

雖然API變化了,但是都還是被支持的知给。

總體上瓤帚,我們發(fā)現(xiàn)流程上基本沒有太大變化。只是API拆解得更細(xì)了涩赢。原來只要包含一個(gè)v8.h就夠了戈次,現(xiàn)在context, isolate, primitive, script等都拆分成獨(dú)立的API了。

上面的版本是經(jīng)過我加工過的筒扒,其實(shí)還有wasm的部分被我刪除掉了怯邪。在2021年末的這個(gè)時(shí)刻,wasm的知識對于很多前端同學(xué)還不是必備的基礎(chǔ)花墩。后面我們有專文討論wasm基礎(chǔ)加上v8的實(shí)現(xiàn)悬秉。

本地句柄

作為一個(gè)JavaScript引擎澄步,v8自然是擁有一個(gè)運(yùn)行時(shí)的垃圾回收器。垃圾回收器會回收一切沒有句柄持有的對象和泌。

最簡單的句柄叫做本地句柄村缸,它是跟棧綁定的。當(dāng)退出一個(gè)作用域時(shí)武氓,本地局柄所持有的對象也將都被釋放掉梯皿。

本地句柄的用法是v8::Local<類型>

上面例子中我們基本上使用的都是本地句柄县恕,代碼字符呂东羹、腳本、值都是保存在本地句柄中:

      v8::Local<v8::String> source =
          v8::String::NewFromUtf8Literal(isolate, "let f1 = (x) => x*x; f1(1.2);");
      v8::Local<v8::Script> script =
          v8::Script::Compile(context, source).ToLocalChecked();
      v8::Local<v8::Value> result = script->Run(context).ToLocalChecked();
      v8::String::Utf8Value utf8(isolate, result);

針對于可能失敗忠烛,為空或者出現(xiàn)異常的情況百姓,我們可以使用MaybeLocal,例如:v8::MaybeLocal<v8::String>.

從utf-8字符串構(gòu)造v8 String的v8::String::NewFromUtf8函數(shù)况木,默認(rèn)生成的就是v8::MaybeLocal<v8::String>類型垒拢。

從MaybeLocal轉(zhuǎn)換到Local,可以調(diào)用ToLocalChecked函數(shù)火惊。其原型為:

  V8_INLINE Local<T> ToLocalChecked() {
    if (V8_UNLIKELY(val_ == nullptr)) api_internal::ToLocalEmpty();
    return Local<T>(val_);
  }

v8字符串

v8::String所表示的就是JavaScript的字符串求类。

上節(jié)我們說了,要生成v8 String, 需要使用v8::String::NewFromUtf8方法屹耐。NewStringType就用kNormal就好:

      char *str1 = "hello,v8";
      v8::MaybeLocal<v8::String> v8str1 =
          v8::String::NewFromUtf8(isolate, str1, v8::NewStringType::kNormal, strlen(str1));

要將v8字符串轉(zhuǎn)換成C字符串尸疆,需要通過v8::String::Utf8Value類型來轉(zhuǎn)換。

      v8::String::Utf8Value v8str2(isolate, v8str1.ToLocalChecked());
      printf("%s\n", *v8str2);

我們來看個(gè)將文件讀到v8字符串的例子:

v8::MaybeLocal<v8::String> ReadFile(v8::Isolate* isolate, const char* name) {
  FILE* file = fopen(name, "rb");
  if (file == NULL) return v8::MaybeLocal<v8::String>();

  fseek(file, 0, SEEK_END);
  size_t size = ftell(file);
  rewind(file);

  char* chars = new char[size + 1];
  chars[size] = '\0';
  for (size_t i = 0; i < size;) {
    i += fread(&chars[i], 1, size - i, file);
    if (ferror(file)) {
      fclose(file);
      return v8::MaybeLocal<v8::String>();
    }
  }
  fclose(file);
  v8::MaybeLocal<v8::String> result = v8::String::NewFromUtf8(
      isolate, chars, v8::NewStringType::kNormal, static_cast<int>(size));
  delete[] chars;
  return result;
}

我們使用上面的ReadFile函數(shù)惶岭,就可以改成運(yùn)行文件上的js腳本了:

    {
      v8::Local<v8::String> source;
      if (!ReadFile(isolate, filename).ToLocal(&source)) {
        printf("Cannot read from %s\n", filename);
      }
      v8::Local<v8::Script> script =
          v8::Script::Compile(context, source).ToLocalChecked();
      v8::Local<v8::Value> result = script->Run(context).ToLocalChecked();
      v8::String::Utf8Value utf8(isolate, result);
      printf("%s\n", *utf8);
    }

不過要注意寿弱,非Javascript字符串就不要來湊熱鬧了。
比如要獲取v8的版本號按灶,沒有這么麻煩症革,直接就是const char*:

printf("%s\n", v8::V8::GetVersion());

Context

除了內(nèi)存管理之外,另外一個(gè)很重要的事情就是運(yùn)行上下文Context. 我們可以看到鸯旁,在代碼中噪矛,編譯需要上下文,運(yùn)行需要上下文铺罢。

我們知道艇挨,js默認(rèn)是提供很多全局變量和全局函數(shù),這些都存儲于上下文中韭赘。腳本中的對象缩滨、函數(shù)也都是跟上下文綁定在一起的。

我們來看一下官方的圖:


image

編譯v8

下面我們就再說下如何編譯v8.

下載v8源代碼

v8是個(gè)比較復(fù)雜的工程,只從github上下載 https://github.com/v8/v8 的代碼是沒法編譯的脉漏。
我們以mac和Linux為例蛋勺。

首先我們需要下載depot_tools工具包:

git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git

然后將這個(gè)工具包加到path中,后面下載所用的fetch, 編譯所用的gn等工具都在這里鸠删。

export PATH=/path/to/depot_tools:$PATH

第三步,我們都過fetch工具下載v8:

fetch v8

如果不想用fetch工具贼陶,想直接手動(dòng)下載的話刃泡,需要下載以下的庫到相應(yīng)的目錄下。
比如我把v8的路徑換成github上的了碉怔。

entries = {
  'v8': 'https://github.com/v8/v8.git',
  'v8/base/trace_event/common': 'https://chromium.googlesource.com/chromium/src/base/trace_event/common.git@68d816952258c9d817bba656ee2664b35507f01b',
  'v8/build': 'https://chromium.googlesource.com/chromium/src/build.git@f78b0bd09847b94e9ec9cb520855d6785fd082ab',
  'v8/buildtools': 'https://chromium.googlesource.com/chromium/src/buildtools.git@a9bc3e283182a586998338a665c7eae17406ec54',
  'v8/buildtools/clang_format/script': 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/clang/tools/clang-format.git@99876cacf78329e5f99c244dbe42ccd1654517a0',
  'v8/buildtools/mac:gn/gn/mac-${arch}': 'https://chrome-infra-packages.appspot.com/gn/gn/mac-${arch}@git_revision:693f9fb87e4febdd4299db9f73d8d2c958e63148',
  'v8/buildtools/third_party/libc++/trunk': 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libcxx.git@79a2e924d96e2fc1e4b937c42efd08898fa472d7',
  'v8/buildtools/third_party/libc++abi/trunk': 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libcxxabi.git@9eb0245224c2d7f6b20f76d4d24eab1d60a2b281',
  'v8/buildtools/third_party/libunwind/trunk': 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libunwind.git@557b51a0ccab9b3dbce61bdd57aa5f7d5c7c6206',
  'v8/test/benchmarks/data': 'https://chromium.googlesource.com/v8/deps/third_party/benchmarks.git@05d7188267b4560491ff9155c5ee13e207ecd65f',
  'v8/test/mozilla/data': 'https://chromium.googlesource.com/v8/deps/third_party/mozilla-tests.git@f6c578a10ea707b1a8ab0b88943fe5115ce2b9be',
  'v8/test/test262/data': 'https://chromium.googlesource.com/external/github.com/tc39/test262.git@8d420cef415f3501cb24d674b8c032d1f09402a0',
  'v8/test/test262/harness': 'https://chromium.googlesource.com/external/github.com/test262-utils/test262-harness-py.git@278bcfaed0dcaa13936831fb1769d15e7c1e3b2b',
  'v8/third_party/depot_tools': 'https://chromium.googlesource.com/chromium/tools/depot_tools.git@756e98f5aac7fb163e558a5a5cc5f3dc0098b1d7',
  'v8/third_party/google_benchmark/src': 'https://chromium.googlesource.com/external/github.com/google/benchmark.git@1e3ab7fa434d1b4aebdd22b760dbf99c498ae7cd',
  'v8/third_party/googletest/src': 'https://chromium.googlesource.com/external/github.com/google/googletest.git@075810f7a20405ea09a93f68847d6e963212fa62',
  'v8/third_party/icu': 'https://chromium.googlesource.com/chromium/deps/icu.git@4df07a2d158218b77369b82f9fe3190725beb815',
  'v8/third_party/instrumented_libraries': 'https://chromium.googlesource.com/chromium/src/third_party/instrumented_libraries.git@6527a4e98a746f5324e21e813a41af25419bfae7',
  'v8/third_party/jinja2': 'https://chromium.googlesource.com/chromium/src/third_party/jinja2.git@ee69aa00ee8536f61db6a451f3858745cf587de6',
  'v8/third_party/jsoncpp/source': 'https://chromium.googlesource.com/external/github.com/open-source-parsers/jsoncpp.git@9059f5cad030ba11d37818847443a53918c327b1',
  'v8/third_party/logdog/logdog': 'https://chromium.googlesource.com/infra/luci/luci-py/client/libs/logdog@17ec234f823f7bff6ada6584fdbbee9d54b8fc58',
  'v8/third_party/markupsafe': 'https://chromium.googlesource.com/chromium/src/third_party/markupsafe.git@1b882ef6372b58bfd55a3285f37ed801be9137cd',
  'v8/third_party/perfetto': 'https://android.googlesource.com/platform/external/perfetto.git@aa4385bc5997ecad4c633885e1b331b1115012fb',
  'v8/third_party/protobuf': 'https://chromium.googlesource.com/external/github.com/google/protobuf@6a59a2ad1f61d9696092f79b6d74368b4d7970a3',
  'v8/third_party/zlib': 'https://chromium.googlesource.com/chromium/src/third_party/zlib.git@6da1d53b97c89b07e47714d88cab61f1ce003c68',
  'v8/tools/clang': 'https://chromium.googlesource.com/chromium/src/tools/clang.git@c00aa10009548ad073810d810cc4a71d2965f75b',
  'v8/tools/clang/dsymutil:chromium/llvm-build-tools/dsymutil': 'https://chrome-infra-packages.appspot.com/chromium/llvm-build-tools/dsymutil@M56jPzDv1620Rnm__jTMYS62Zi8rxHVq7yw0qeBFEgkC',
  'v8/tools/luci-go:infra/tools/luci/isolate/${platform}': 'https://chrome-infra-packages.appspot.com/infra/tools/luci/isolate/${platform}@git_revision:d1c03082ecda0148d8096f1fd8bf5491eafc7323',
  'v8/tools/luci-go:infra/tools/luci/isolated/${platform}': 'https://chrome-infra-packages.appspot.com/infra/tools/luci/isolated/${platform}@git_revision:d1c03082ecda0148d8096f1fd8bf5491eafc7323',
  'v8/tools/luci-go:infra/tools/luci/swarming/${platform}': 'https://chrome-infra-packages.appspot.com/infra/tools/luci/swarming/${platform}@git_revision:d1c03082ecda0148d8096f1fd8bf5491eafc7323',
}

以后烘贴,就可以在v8的目錄下運(yùn)行gclient sync去同步這些工具。

編譯d8

我們是做工具用撮胧,所以我們需要的是d8和libv8_monolith.

d8在前面講字節(jié)碼的時(shí)候介紹過桨踪。
我們看下如何編譯d8.
以x64 release版為例:

python3 tools/dev/gm.py x64.release

如果要編譯debug版,就是

python3 tools/dev/gm.py x64.debug

編譯成功之后芹啥,就會在out/x64.release或者out/x64.debug下面出現(xiàn)d8.

我們就可以應(yīng)用./d8 --print-bytecode查看字節(jié)碼啦锻离。

將v8作為庫使用

第一步,我們先生成v8庫對應(yīng)的gn文件:

python3 ./tools/dev/v8gen.py x64.release.sample

在out.gn/x64.release.sample中會生成一堆ninja文件墓怀,比如下面是v8_monolith.ninja文件:

defines = -D_LIBCPP_HAS_NO_ALIGNED_ALLOCATION -DCR_XCODE_VERSION=1310 -DCR_CLANG_REVISION=\"llvmorg-14-init-6355-gb2217b36-2\" -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D_FORTIFY_SOURCE=2 -D__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES=0 -DNDEBUG -DNVALGRIND -DDYNAMIC_ANNOTATIONS_ENABLED=0 -DV8_TYPED_ARRAY_MAX_SIZE_IN_HEAP=64 -DENABLE_GDB_JIT_INTERFACE -DENABLE_MINOR_MC -DV8_INTL_SUPPORT -DENABLE_HANDLE_ZAPPING -DV8_ATOMIC_OBJECT_FIELD_WRITES -DV8_ATOMIC_MARKING_STATE -DV8_ENABLE_LAZY_SOURCE_POSITIONS -DV8_SHARED_RO_HEAP -DV8_WIN64_UNWINDING_INFO -DV8_ENABLE_REGEXP_INTERPRETER_THREADED_DISPATCH -DV8_SNAPSHOT_COMPRESSION -DV8_SHORT_BUILTIN_CALLS -DV8_ENABLE_SYSTEM_INSTRUMENTATION -DV8_ENABLE_WEBASSEMBLY -DV8_ALLOCATION_FOLDING -DV8_ALLOCATION_SITE_TRACKING -DV8_ADVANCED_BIGINT_ALGORITHMS -DV8_INCLUDE_RECEIVER_IN_ARGC -DV8_COMPRESS_POINTERS -DV8_COMPRESS_POINTERS_IN_SHARED_CAGE -DV8_31BIT_SMIS_ON_64BIT_ARCH -DV8_DEPRECATION_WARNINGS -DV8_IMMINENT_DEPRECATION_WARNINGS -DCPPGC_CAGED_HEAP -DV8_TARGET_ARCH_X64 -DV8_HAVE_TARGET_OS -DV8_TARGET_OS_MACOSX -DV8_RUNTIME_CALL_STATS -DU_USING_ICU_NAMESPACE=0 -DU_ENABLE_DYLOAD=0 -DUSE_CHROMIUM_ICU=1 -DU_ENABLE_TRACING=1 -DU_ENABLE_RESOURCE_TRACING=0 -DU_STATIC_IMPLEMENTATION -DICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_FILE
framework_dirs =
include_dirs = -I../.. -Igen -I../../include -Igen/include -I../../third_party/icu/source/common -I../../third_party/icu/source/i18n
label_name = v8_monolith
target_out_dir = obj
target_output_name = libv8_monolith

build obj/v8_monolith.inputdeps.stamp: stamp obj/generate_bytecode_builtins_list.stamp obj/run_gen-regexp-special-case.stamp obj/run_torque.stamp obj/src/inspector/protocol_generated_sources.stamp obj/third_party/icu/icudata.stamp

也可以直接使用gn命令達(dá)到和v8gen.py同樣的效果:

gn args out.gn/x64.release.sample

如果看v8gen.py的源碼的話我們會發(fā)現(xiàn)汽纠,基本上都是在操作生成gn的參數(shù)。

第二步傀履,我們就可以用ninja來編譯libv8_monolith了:

ninja -C out.gn/x64.release.sample v8_monolith

同樣虱朵,如果想要編譯d8,將目標(biāo)改成d8即可:

ninja -C out.gn/x64.release.sample d8

第三步钓账,編譯前面寫的使用V8 API的C++程序:

g++ -I. -Iinclude ./hello3.cc -o hello_world3 -lv8_monolith -Lout.gn/x64.release.sample/obj/ -lpthread -std=c++14 -DV8_COMPRESS_POINTERS

小結(jié)

本文中我們學(xué)習(xí)了編譯v8和將v8當(dāng)成庫引入到你自己的應(yīng)用程序中的方法碴犬。并且通過簡單的例子就實(shí)現(xiàn)了執(zhí)行字符串腳本和從文件中讀取js腳本的功能。
這為我們進(jìn)一步學(xué)習(xí)v8細(xì)節(jié)打好了堅(jiān)實(shí)的基礎(chǔ)梆暮。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末服协,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子啦粹,更是在濱河造成了極大的恐慌蚯涮,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卖陵,死亡現(xiàn)場離奇詭異遭顶,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)泪蔫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進(jìn)店門棒旗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事铣揉∪纳睿” “怎么了?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵逛拱,是天一觀的道長敌厘。 經(jīng)常有香客問我,道長朽合,這世上最難降的妖魔是什么俱两? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮曹步,結(jié)果婚禮上宪彩,老公的妹妹穿的比我還像新娘。我一直安慰自己讲婚,他們只是感情好尿孔,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著筹麸,像睡著了一般活合。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上物赶,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天芜辕,我揣著相機(jī)與錄音,去河邊找鬼块差。 笑死侵续,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的憨闰。 我是一名探鬼主播状蜗,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼鹉动!你這毒婦竟也來了轧坎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤泽示,失蹤者是張志新(化名)和其女友劉穎缸血,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體械筛,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡捎泻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了埋哟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片笆豁。...
    茶點(diǎn)故事閱讀 40,561評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出闯狱,到底是詐尸還是另有隱情煞赢,我是刑警寧澤,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布哄孤,位于F島的核電站照筑,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏瘦陈。R本人自食惡果不足惜凝危,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望双饥。 院中可真熱鬧,春花似錦弟断、人聲如沸咏花。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽昏翰。三九已至,卻和暖如春刘急,著一層夾襖步出監(jiān)牢的瞬間棚菊,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工叔汁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留统求,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓据块,卻偏偏與公主長得像码邻,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子另假,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評論 2 359

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