github.com/igggame/nebulas-go@v2.1.0+incompatible/nf/nvm/v8/engine.cc (about)

     1  // Copyright (C) 2017 go-nebulas authors
     2  //
     3  // This file is part of the go-nebulas library.
     4  //
     5  // the go-nebulas library is free software: you can redistribute it and/or
     6  // modify it under the terms of the GNU General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // the go-nebulas library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU General Public License
    16  // along with the go-nebulas library.  If not, see
    17  // <http://www.gnu.org/licenses/>.
    18  //
    19  
    20  #include "engine.h"
    21  #include "allocator.h"
    22  #include "engine_int.h"
    23  #include "lib/execution_env.h"
    24  #include "lib/global.h"
    25  #include "lib/instruction_counter.h"
    26  #include "lib/logger.h"
    27  #include "lib/tracing.h"
    28  #include "lib/typescript.h"
    29  #include "v8_data_inc.h"
    30  
    31  #include <libplatform/libplatform.h>
    32  
    33  
    34  #include <assert.h>
    35  #include <string.h>
    36  #include <thread>
    37  #include <stdlib.h>
    38  #include <stdio.h>
    39  
    40  using namespace v8;
    41  
    42  static Platform *platformPtr = NULL;
    43  
    44  void PrintException(Local<Context> context, TryCatch &trycatch);
    45  void PrintAndReturnException(char **exception, Local<Context> context,
    46                               TryCatch &trycatch);
    47  void EngineLimitsCheckDelegate(Isolate *isolate, size_t count,
    48                                 void *listenerContext);
    49  
    50  #define ExecuteTimeOut  5*1000*1000
    51  #define STRINGIZE2(s) #s
    52  #define STRINGIZE(s) STRINGIZE2(s)
    53  #define V8VERSION_STRING                                                       \
    54    STRINGIZE(V8_MAJOR_VERSION)                                                  \
    55    "." STRINGIZE(V8_MINOR_VERSION) "." STRINGIZE(                               \
    56        V8_BUILD_NUMBER) "." STRINGIZE(V8_PATCH_LEVEL)
    57  
    58  static char V8VERSION[] = V8VERSION_STRING;
    59  char *GetV8Version() { return V8VERSION; }
    60  
    61  void Initialize() {
    62    // Initialize V8.
    63    platformPtr = platform::CreateDefaultPlatform();
    64  
    65    V8::InitializeICU();
    66    V8::InitializePlatform(platformPtr);
    67  
    68    StartupData nativesData, snapshotData;
    69    nativesData.data = reinterpret_cast<char *>(natives_blob_bin);
    70    nativesData.raw_size = natives_blob_bin_len;
    71    snapshotData.data = reinterpret_cast<char *>(snapshot_blob_bin);
    72    snapshotData.raw_size = snapshot_blob_bin_len;
    73    V8::SetNativesDataBlob(&nativesData);
    74    V8::SetSnapshotDataBlob(&snapshotData);
    75  
    76    V8::Initialize();
    77  
    78    // Initialize V8Engine.
    79    SetInstructionCounterIncrListener(EngineLimitsCheckDelegate);
    80  }
    81  
    82  void Dispose() {
    83    V8::Dispose();
    84    V8::ShutdownPlatform();
    85    if (platformPtr) {
    86      delete platformPtr;
    87      platformPtr = NULL;
    88    }
    89  }
    90  
    91  V8Engine *CreateEngine() {
    92    ArrayBuffer::Allocator *allocator = new ArrayBufferAllocator();
    93  
    94    Isolate::CreateParams create_params;
    95    create_params.array_buffer_allocator = allocator;
    96  
    97    Isolate *isolate = Isolate::New(create_params);
    98  
    99    // fix bug: https://github.com/nebulasio/go-nebulas/issues/5
   100    // isolate->SetStackLimit(0x700000000000UL);
   101  
   102    V8Engine *e = (V8Engine *)calloc(1, sizeof(V8Engine));
   103    e->allocator = allocator;
   104    e->isolate = isolate;
   105    e->timeout = ExecuteTimeOut;
   106    e->ver = BUILD_DEFAULT_VER; //default load initial com
   107    return e;
   108  }
   109  void EnableInnerContract(V8Engine *e) {
   110    e->ver = BUILD_INNER_VER;
   111  }
   112  
   113  void DeleteEngine(V8Engine *e) {
   114    Isolate *isolate = static_cast<Isolate *>(e->isolate);
   115    isolate->Dispose();
   116  
   117    delete static_cast<ArrayBuffer::Allocator *>(e->allocator);
   118  
   119    free(e);
   120  }
   121  
   122  int ExecuteSourceDataDelegate(char **result, Isolate *isolate,
   123                                const char *source, int source_line_offset,
   124                                Local<Context> context, TryCatch &trycatch,
   125                                void *delegateContext) {
   126    // Create a string containing the JavaScript source code.
   127    Local<String> src =
   128        String::NewFromUtf8(isolate, source, NewStringType::kNormal)
   129            .ToLocalChecked();
   130  
   131    // Compile the source code.
   132    ScriptOrigin sourceSrcOrigin(
   133        String::NewFromUtf8(isolate, "_contract_runner.js"),
   134        Integer::New(isolate, source_line_offset));
   135    MaybeLocal<Script> script = Script::Compile(context, src, &sourceSrcOrigin);
   136  
   137    if (script.IsEmpty()) {
   138      PrintAndReturnException(result, context, trycatch);
   139      return NVM_EXCEPTION_ERR;
   140    }
   141    // Run the script to get the result.
   142    MaybeLocal<Value> ret = script.ToLocalChecked()->Run(context);
   143    if (ret.IsEmpty()) {
   144      PrintAndReturnException(result, context, trycatch);
   145      return NVM_EXCEPTION_ERR;
   146    }
   147    // set result.
   148    if (result != NULL) {
   149      Local<Object> obj = ret.ToLocalChecked().As<Object>();
   150      if (!obj->IsUndefined()) {
   151        MaybeLocal<String> json_result = v8::JSON::Stringify(context, obj);
   152        if (!json_result.IsEmpty()) {
   153          String::Utf8Value str(json_result.ToLocalChecked());
   154          *result = (char *)malloc(str.length() + 1);
   155          strcpy(*result, *str);
   156        }
   157      }
   158    }
   159  
   160    return NVM_SUCCESS;
   161  }
   162  
   163  char *InjectTracingInstructions(V8Engine *e, const char *source,
   164                                  int *source_line_offset,
   165                                  int strictDisallowUsage) {
   166    TracingContext tContext;
   167    tContext.source_line_offset = 0;
   168    tContext.tracable_source = NULL;
   169    tContext.strictDisallowUsage = strictDisallowUsage;
   170  
   171    Execute(NULL, e, source, 0, 0L, 0L, InjectTracingInstructionDelegate,
   172            (void *)&tContext);
   173  
   174    *source_line_offset = tContext.source_line_offset;
   175    return static_cast<char *>(tContext.tracable_source);
   176  }
   177  
   178  char *TranspileTypeScriptModule(V8Engine *e, const char *source,
   179                                  int *source_line_offset) {
   180    TypeScriptContext tContext;
   181    tContext.source_line_offset = 0;
   182    tContext.js_source = NULL;
   183  
   184    Execute(NULL, e, source, 0, 0L, 0L, TypeScriptTranspileDelegate,
   185            (void *)&tContext);
   186  
   187    *source_line_offset = tContext.source_line_offset;
   188    return static_cast<char *>(tContext.js_source);
   189  }
   190  
   191  int RunScriptSource(char **result, V8Engine *e, const char *source,
   192                      int source_line_offset, uintptr_t lcsHandler,
   193                      uintptr_t gcsHandler) {
   194    return Execute(result, e, source, source_line_offset, (void *)lcsHandler,
   195                   (void *)gcsHandler, ExecuteSourceDataDelegate, NULL);
   196  }
   197  
   198  int Execute(char **result, V8Engine *e, const char *source,
   199              int source_line_offset, void *lcsHandler, void *gcsHandler,
   200              ExecutionDelegate delegate, void *delegateContext) {
   201    Isolate *isolate = static_cast<Isolate *>(e->isolate);
   202    Locker locker(isolate);
   203    assert(isolate);
   204  
   205    Isolate::Scope isolate_scope(isolate);
   206    // Create a stack-allocated handle scope.
   207    HandleScope handle_scope(isolate);
   208  
   209    // Create global object template.
   210    Local<ObjectTemplate> globalTpl = CreateGlobalObjectTemplate(isolate);
   211  
   212    // Create a new context.
   213    Local<Context> context = Context::New(isolate, NULL, globalTpl);
   214  
   215    // disable eval().
   216    context->AllowCodeGenerationFromStrings(false);
   217  
   218    // Enter the context for compiling and running the script.
   219    Context::Scope context_scope(context);
   220  
   221    TryCatch trycatch(isolate);
   222  
   223    // Continue put objects to global object.
   224    SetGlobalObjectProperties(isolate, context, e, lcsHandler, gcsHandler);
   225  
   226    // Setup execution env.
   227    if (SetupExecutionEnv(isolate, context)) {
   228      PrintAndReturnException(result, context, trycatch);
   229      return NVM_EXCEPTION_ERR;
   230    }
   231  
   232    int retTmp = delegate(result, isolate, source, source_line_offset, context,
   233                    trycatch, delegateContext);
   234    
   235    if (e->is_unexpected_error_happen) {
   236      return NVM_UNEXPECTED_ERR;
   237    }
   238  
   239    return retTmp;
   240  }
   241  
   242  void PrintException(Local<Context> context, TryCatch &trycatch) {
   243    PrintAndReturnException(NULL, context, trycatch);
   244  }
   245  
   246  void PrintAndReturnException(char **exception, Local<Context> context,
   247                               TryCatch &trycatch) {
   248    static char SOURCE_INFO_PLACEHOLDER[] = "";
   249    char *source_info = NULL;
   250  
   251    // print source line.
   252    Local<Message> message = trycatch.Message();
   253    if (!message.IsEmpty()) {
   254      // Print (filename):(line number): (message).
   255      ScriptOrigin origin = message->GetScriptOrigin();
   256      String::Utf8Value filename(message->GetScriptResourceName());
   257      int linenum = message->GetLineNumber();
   258  
   259      // Print line of source code.
   260      String::Utf8Value sourceline(message->GetSourceLine());
   261      int script_start = (linenum - origin.ResourceLineOffset()->Value()) == 1
   262                             ? origin.ResourceColumnOffset()->Value()
   263                             : 0;
   264      int start = message->GetStartColumn(context).FromMaybe(0);
   265      int end = message->GetEndColumn(context).FromMaybe(0);
   266      if (start >= script_start) {
   267        start -= script_start;
   268        end -= script_start;
   269      }
   270  
   271      char arrow[start + 1];
   272      for (int i = 0; i < start; i++) {
   273        char c = (*sourceline)[i];
   274        if (c == '\t') {
   275          arrow[i] = c;
   276        } else {
   277          arrow[i] = ' ';
   278        }
   279      }
   280      arrow[start] = '^';
   281      arrow[start + 1] = '\0';
   282  
   283      asprintf(&source_info, "%s:%d\n%s\n%s\n", *filename, linenum, *sourceline,
   284               arrow);
   285    }
   286  
   287    if (source_info == NULL) {
   288      source_info = SOURCE_INFO_PLACEHOLDER;
   289    }
   290  
   291    // get stack trace.
   292    MaybeLocal<Value> stacktrace_ret = trycatch.StackTrace(context);
   293    if (!stacktrace_ret.IsEmpty()) {
   294      // print full stack trace.
   295      String::Utf8Value stack_str(stacktrace_ret.ToLocalChecked());
   296      LogErrorf("V8 Exception:\n%s%s", source_info, *stack_str);
   297    }
   298  
   299    // exception message.
   300    Local<Value> exceptionValue = trycatch.Exception();
   301    String::Utf8Value exception_str(exceptionValue);
   302    if (stacktrace_ret.IsEmpty()) {
   303      // print exception when stack trace is not available.
   304      LogErrorf("V8 Exception:\n%s%s", source_info, *exception_str);
   305    }
   306  
   307    if (source_info != NULL && source_info != SOURCE_INFO_PLACEHOLDER) {
   308      free(source_info);
   309    }
   310  
   311    // return exception message.
   312    if (exception != NULL) {
   313      *exception = (char *)malloc(exception_str.length() + 1);
   314      //TODO: Branch code detected “(v8::String::Utf8Value) $0 = (str_ = <no value available>, length_ = 0)”
   315      if (exception_str.length() == 0) {
   316        strcpy(*exception, "");
   317      } else {
   318        strcpy(*exception, *exception_str);
   319      }
   320    }
   321  }
   322  
   323  void ReadMemoryStatistics(V8Engine *e) {
   324    Isolate *isolate = static_cast<Isolate *>(e->isolate);
   325    ArrayBufferAllocator *allocator =
   326        static_cast<ArrayBufferAllocator *>(e->allocator);
   327  
   328    HeapStatistics heap_stats;
   329    isolate->GetHeapStatistics(&heap_stats);
   330  
   331    V8EngineStats *stats = &(e->stats);
   332    stats->heap_size_limit = heap_stats.heap_size_limit();
   333    stats->malloced_memory = heap_stats.malloced_memory();
   334    stats->peak_malloced_memory = heap_stats.peak_malloced_memory();
   335    stats->total_available_size = heap_stats.total_available_size();
   336    stats->total_heap_size = heap_stats.total_heap_size();
   337    stats->total_heap_size_executable = heap_stats.total_heap_size_executable();
   338    stats->total_physical_size = heap_stats.total_physical_size();
   339    stats->used_heap_size = heap_stats.used_heap_size();
   340    stats->total_array_buffer_size = allocator->total_available_size();
   341    stats->peak_array_buffer_size = allocator->peak_allocated_size();
   342  
   343    stats->total_memory_size =
   344        stats->total_heap_size + stats->peak_array_buffer_size;
   345  }
   346  
   347  void TerminateExecution(V8Engine *e) {
   348    if (e->is_requested_terminate_execution) {
   349      return;
   350    }
   351    Isolate *isolate = static_cast<Isolate *>(e->isolate);
   352    isolate->TerminateExecution();
   353    e->is_requested_terminate_execution = true;
   354  }
   355  void SetInnerContractErrFlag(V8Engine *e) {
   356    e->is_inner_nvm_error_happen = true;
   357  }
   358  void EngineLimitsCheckDelegate(Isolate *isolate, size_t count,
   359                                 void *listenerContext) {
   360    V8Engine *e = static_cast<V8Engine *>(listenerContext);
   361  
   362    if (IsEngineLimitsExceeded(e)) {
   363      TerminateExecution(e);
   364    }
   365  }
   366  
   367  int IsEngineLimitsExceeded(V8Engine *e) {
   368    // TODO: read memory stats everytime may impact the performance.
   369    ReadMemoryStatistics(e);
   370    if (e->limits_of_executed_instructions > 0 &&
   371        e->limits_of_executed_instructions <
   372            e->stats.count_of_executed_instructions) {
   373      // Reach instruction limits.
   374      return NVM_GAS_LIMIT_ERR;
   375    } else if (e->limits_of_total_memory_size > 0 &&
   376               e->limits_of_total_memory_size < e->stats.total_memory_size) {
   377      // reach memory limits.
   378      return NVM_MEM_LIMIT_ERR;
   379    }
   380    // 
   381    
   382    return 0;
   383  }
   384  
   385  //loopExecute迁移文件