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迁移文件