Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 #include "config.h" | |
| 6 | |
| 7 #include "bindings/dart/DartService.h" | |
| 8 | |
| 9 #include "DartApplicationLoader.h" | |
| 10 #include "DartController.h" | |
| 11 #include "DartDocument.h" | |
| 12 #include "DartServiceInternal.h" | |
| 13 #include "DartUtilities.h" | |
| 14 #include "DartWindow.h" | |
| 15 | |
| 16 | |
| 17 // These must be kept in sync with vmservice/constants.dart | |
| 18 // TODO(johnmccutchan): Put these constants in one place. | |
| 19 #define VM_SERVICE_ISOLATE_STARTUP_MESSAGE_ID 1 | |
| 20 #define VM_SERVICE_ISOLATE_SHUTDOWN_MESSAGE_ID 2 | |
| 21 | |
| 22 // The following Resources class is used in combination with a generated | |
| 23 // source file that expects underscores in names. The NOLINT tags are | |
| 24 // used to suppress the errors. | |
| 25 // TODO(johnmccutchan): Move VM service source into runtime and out of bin. | |
| 26 namespace dart { | |
| 27 namespace bin { | |
| 28 class Resources { | |
| 29 public: | |
| 30 static const int kNoSuchInstance = -1; | |
| 31 | |
| 32 static int ResourceLookup(const char* path, const char** resource) | |
| 33 { | |
| 34 for (int i = 0; i < get_resource_count(); i++) { | |
| 35 resource_map_entry* entry = get_resource(i); | |
| 36 if (!strcmp(path, entry->path_)) { | |
| 37 *resource = entry->resource_; | |
| 38 ASSERT(entry->length_ > 0); | |
| 39 return entry->length_; | |
| 40 } | |
| 41 } | |
| 42 return kNoSuchInstance; | |
| 43 } | |
| 44 | |
| 45 static intptr_t get_resource_count() // NOLINT. | |
| 46 { | |
| 47 return builtin_resources_count_; | |
| 48 } | |
| 49 | |
| 50 static const char* get_resource_path(intptr_t i) // NOLINT. | |
| 51 { | |
| 52 return get_resource(i)->path_; | |
| 53 } | |
| 54 | |
| 55 private: | |
| 56 struct resource_map_entry { // NOLINT. | |
| 57 const char* path_; // NOLINT. | |
| 58 const char* resource_; // NOLINT. | |
| 59 intptr_t length_; // NOLINT. | |
| 60 }; | |
| 61 | |
| 62 // These fields are generated by resources_gen.cc. | |
| 63 static resource_map_entry builtin_resources_[]; // NOLINT. | |
| 64 static const intptr_t builtin_resources_count_; // NOLINT. | |
| 65 | |
| 66 static resource_map_entry* get_resource(int i) // NOLINT. | |
| 67 { | |
| 68 ASSERT(i >= 0 && i < builtin_resources_count_); | |
| 69 return &builtin_resources_[i]; | |
| 70 } | |
| 71 | |
| 72 DISALLOW_IMPLICIT_CONSTRUCTORS(Resources); | |
| 73 }; | |
| 74 | |
| 75 } | |
| 76 } | |
| 77 namespace WebCore { | |
| 78 | |
| 79 #define SHUTDOWN_ON_ERROR(handle) \ | |
| 80 if (Dart_IsError(handle)) { \ | |
| 81 m_errorMsg = strdup(Dart_GetError(handle)); \ | |
| 82 goto error; \ | |
| 83 } | |
| 84 | |
| 85 static const char* kScriptUri = "vmservice:"; | |
| 86 #define kLibraryResourceNamePrefix "/vmservice" | |
| 87 static const char* kVMServiceDartiumLibraryScriptResourceName = | |
| 88 kLibraryResourceNamePrefix "/vmservice_dartium.dart"; | |
| 89 static const char* kVMServiceLibraryName = | |
| 90 kLibraryResourceNamePrefix "/vmservice.dart"; | |
| 91 | |
| 92 | |
| 93 Dart_Isolate DartService::m_isolate = 0; | |
| 94 Dart_Port DartService::m_port = ILLEGAL_PORT; | |
| 95 Dart_Port DartService::m_requestPort = ILLEGAL_PORT; | |
| 96 Dart_Port DartService::m_nativePort = ILLEGAL_PORT; | |
| 97 const char* DartService::m_errorMsg = 0; | |
| 98 | |
| 99 | |
| 100 bool DartService::Start(Document* document) | |
| 101 { | |
| 102 if (m_isolate) { | |
| 103 // Already running. | |
| 104 return true; | |
| 105 } | |
| 106 { | |
| 107 char* error = 0; | |
| 108 | |
| 109 m_isolate = DartController::createIsolate(kScriptUri, "main", document, true, &error); | |
| 110 if (!m_isolate) { | |
| 111 m_errorMsg = error; | |
| 112 return false; | |
|
Jacob
2013/12/05 19:31:31
Use Scope auto close opbjects here and elsewhere t
Cutch
2013/12/05 22:48:15
Everywhere but the startup and shutdown of the VM
| |
| 113 } | |
| 114 | |
| 115 Dart_EnterScope(); | |
| 116 // Set up the library tag handler for this isolate. | |
| 117 Dart_Handle result = Dart_SetLibraryTagHandler(LibraryTagHandler); | |
| 118 SHUTDOWN_ON_ERROR(result); | |
| 119 | |
| 120 { | |
| 121 // Load source into service isolate. | |
| 122 Dart_Handle library = | |
| 123 LoadScript(kVMServiceDartiumLibraryScriptResourceName); | |
| 124 SHUTDOWN_ON_ERROR(library); | |
| 125 } | |
| 126 // Make the isolate runnable so that it is ready to handle messages. | |
| 127 Dart_ExitScope(); | |
| 128 Dart_ExitIsolate(); | |
| 129 | |
| 130 bool retval = Dart_IsolateMakeRunnable(m_isolate); | |
| 131 if (!retval) { | |
| 132 Dart_EnterIsolate(m_isolate); | |
| 133 Dart_ShutdownIsolate(); | |
| 134 m_errorMsg = "Invalid isolate state - Unable to make it runnable."; | |
| 135 return false; | |
| 136 } | |
| 137 | |
| 138 Dart_EnterIsolate(m_isolate); | |
| 139 Dart_EnterScope(); | |
| 140 | |
| 141 // Invoke main. | |
| 142 Dart_Handle library = Dart_RootLibrary(); | |
| 143 Dart_Handle entryFunctioName = Dart_NewStringFromCString("main"); | |
| 144 SHUTDOWN_ON_ERROR(entryFunctioName); | |
| 145 result = Dart_Invoke(library, entryFunctioName, 0, 0); | |
| 146 SHUTDOWN_ON_ERROR(result); | |
| 147 | |
| 148 // Retrieve the ReceivePort that the service is waiting on. The _receive Port | |
| 149 // variable is setup in the call to main. | |
| 150 Dart_Handle portFieldName = Dart_NewStringFromCString("_receivePort"); | |
| 151 SHUTDOWN_ON_ERROR(portFieldName); | |
| 152 Dart_Handle receivePort = Dart_GetField(library, portFieldName); | |
| 153 SHUTDOWN_ON_ERROR(receivePort); | |
| 154 | |
| 155 m_port = DartServiceInternal::GetPortIdFromPort(receivePort); | |
| 156 if (m_port == ILLEGAL_PORT) { | |
| 157 Dart_ExitScope(); | |
| 158 Dart_ShutdownIsolate(); | |
| 159 m_errorMsg = "Invalid isolate state - Unable to get receivePort"; | |
| 160 return false; | |
| 161 } | |
| 162 | |
| 163 { | |
| 164 // Retrieve the ReceivePort that the service is waiting on. The _rec eivePort | |
| 165 // variable is setup in the call to main. | |
| 166 Dart_Handle portFieldName = Dart_NewStringFromCString("_requestPort" ); | |
| 167 SHUTDOWN_ON_ERROR(portFieldName); | |
| 168 Dart_Handle receivePort = Dart_GetField(library, portFieldName); | |
| 169 SHUTDOWN_ON_ERROR(receivePort); | |
| 170 m_requestPort = DartServiceInternal::GetPortIdFromPort(receivePort); | |
| 171 ASSERT(m_requestPort != ILLEGAL_PORT); | |
| 172 } | |
| 173 | |
| 174 Dart_ExitScope(); | |
| 175 Dart_ExitIsolate(); | |
| 176 SetupNativePort(); | |
| 177 return true; | |
| 178 } | |
| 179 error: | |
| 180 Dart_ExitScope(); | |
| 181 Dart_ShutdownIsolate(); | |
| 182 m_isolate = 0; | |
| 183 m_port = ILLEGAL_PORT; | |
| 184 m_requestPort = ILLEGAL_PORT; | |
| 185 return false; | |
| 186 } | |
| 187 | |
| 188 | |
| 189 bool DartService::Stop() | |
| 190 { | |
|
Jacob
2013/12/05 19:31:31
remove return values that provide no value.
| |
| 191 if (!m_isolate) { | |
| 192 // Already shutdown. | |
| 193 return true; | |
| 194 } | |
| 195 ShutdownNativePort(); | |
| 196 m_port = ILLEGAL_PORT; | |
| 197 m_requestPort = ILLEGAL_PORT; | |
| 198 Dart_Isolate isolate = m_isolate; | |
| 199 m_isolate = 0; | |
| 200 Dart_EnterIsolate(isolate); | |
| 201 DartController::shutdownIsolate(isolate); | |
| 202 return true; | |
| 203 } | |
| 204 | |
| 205 | |
| 206 void DartService::SetupNativePort() | |
| 207 { | |
| 208 if (m_nativePort != ILLEGAL_PORT) { | |
| 209 // Already running. | |
| 210 return; | |
| 211 } | |
| 212 const char* nativePortName = "VMServiceNativePort"; | |
| 213 m_nativePort = Dart_NewNativePort(nativePortName, NativePortMessageHandler, false); | |
| 214 ASSERT(m_nativePort != ILLEGAL_PORT); | |
| 215 } | |
| 216 | |
| 217 | |
| 218 void DartService::ShutdownNativePort() | |
| 219 { | |
| 220 if (m_nativePort == ILLEGAL_PORT) { | |
| 221 return; | |
| 222 } | |
| 223 Dart_CloseNativePort(m_nativePort); | |
| 224 m_nativePort = ILLEGAL_PORT; | |
| 225 } | |
| 226 | |
| 227 | |
| 228 const char* DartService::GetErrorMessage() | |
| 229 { | |
| 230 return m_errorMsg ? m_errorMsg : "No error."; | |
| 231 } | |
| 232 | |
| 233 | |
| 234 Dart_Port DartService::port() | |
| 235 { | |
| 236 return m_port; | |
| 237 } | |
| 238 | |
| 239 | |
| 240 bool DartService::IsRunning() | |
| 241 { | |
| 242 return m_port != ILLEGAL_PORT; | |
| 243 } | |
| 244 | |
| 245 | |
| 246 static Dart_Handle MakeServiceControlMessage(Dart_Port portId, intptr_t code, Da rt_Handle name) | |
| 247 { | |
| 248 Dart_Handle result; | |
| 249 Dart_Handle list = Dart_NewList(4); | |
| 250 ASSERT(!Dart_IsError(list)); | |
| 251 Dart_Handle codeHandle = Dart_NewInteger(code); | |
| 252 ASSERT(!Dart_IsError(codeHandle)); | |
| 253 result = Dart_ListSetAt(list, 0, codeHandle); | |
| 254 ASSERT(!Dart_IsError(result)); | |
| 255 Dart_Handle portIdHandle = Dart_NewInteger(portId); | |
| 256 ASSERT(!Dart_IsError(portIdHandle)); | |
| 257 result = Dart_ListSetAt(list, 1, portIdHandle); | |
| 258 ASSERT(!Dart_IsError(result)); | |
| 259 Dart_Handle sendPort = Dart_NewSendPort(portId); | |
| 260 ASSERT(!Dart_IsError(sendPort)); | |
| 261 result = Dart_ListSetAt(list, 2, sendPort); | |
| 262 ASSERT(!Dart_IsError(result)); | |
| 263 result = Dart_ListSetAt(list, 3, name); | |
| 264 ASSERT(!Dart_IsError(result)); | |
| 265 return list; | |
| 266 } | |
| 267 | |
| 268 | |
| 269 bool DartService::SendIsolateStartupMessage() | |
| 270 { | |
| 271 if (!IsRunning()) { | |
| 272 return false; | |
| 273 } | |
| 274 Dart_Handle name = Dart_DebugName(); | |
| 275 ASSERT(!Dart_IsError(name)); | |
| 276 Dart_Handle list = MakeServiceControlMessage(Dart_GetMainPortId(), VM_SERVIC E_ISOLATE_STARTUP_MESSAGE_ID, name); | |
| 277 ASSERT(!Dart_IsError(list)); | |
| 278 return Dart_Post(m_port, list); | |
| 279 } | |
| 280 | |
| 281 | |
| 282 bool DartService::SendIsolateShutdownMessage() | |
| 283 { | |
| 284 if (!IsRunning()) | |
| 285 return false; | |
| 286 Dart_Handle list = MakeServiceControlMessage(Dart_GetMainPortId(), VM_SERVIC E_ISOLATE_SHUTDOWN_MESSAGE_ID, Dart_Null()); | |
| 287 ASSERT(!Dart_IsError(list)); | |
| 288 return Dart_Post(m_port, list); | |
| 289 } | |
| 290 | |
| 291 | |
| 292 DartServiceRequest::DartServiceRequest(const char* request) | |
| 293 { | |
| 294 ASSERT(request); | |
| 295 m_request = strdup(request); | |
| 296 ASSERT(m_request); | |
| 297 } | |
| 298 | |
| 299 DartServiceRequest::~DartServiceRequest() | |
| 300 { | |
| 301 free(m_request); | |
| 302 } | |
| 303 | |
| 304 | |
| 305 | |
| 306 // The format of the message is: | |
| 307 // [send port, request string, address of DartServiceRequest]. | |
| 308 static Dart_Handle MakeServiceRequestMessage(Dart_Port port, DartServiceRequest* request) | |
| 309 { | |
| 310 intptr_t requestAddress = reinterpret_cast<intptr_t>(request); | |
| 311 int64_t requestAddress64 = static_cast<int64_t>(requestAddress); | |
| 312 Dart_Handle result; | |
| 313 Dart_Handle list = Dart_NewList(3); | |
| 314 ASSERT(!Dart_IsError(list)); | |
| 315 Dart_Handle portHandle = Dart_NewSendPort(port); | |
| 316 ASSERT(!Dart_IsError(portHandle)); | |
| 317 Dart_Handle requestHandle = Dart_NewStringFromCString(request->GetRequestStr ing()); | |
| 318 ASSERT(!Dart_IsError(requestHandle)); | |
| 319 Dart_Handle addressHandle = Dart_NewInteger(requestAddress64); | |
| 320 ASSERT(!Dart_IsError(addressHandle)); | |
| 321 result = Dart_ListSetAt(list, 0, portHandle); | |
| 322 ASSERT(!Dart_IsError(result)); | |
| 323 result = Dart_ListSetAt(list, 1, requestHandle); | |
| 324 ASSERT(!Dart_IsError(result)); | |
| 325 result = Dart_ListSetAt(list, 2, addressHandle); | |
| 326 ASSERT(!Dart_IsError(result)); | |
| 327 return list; | |
| 328 } | |
| 329 | |
| 330 | |
| 331 void DartService::MakeServiceRequest(DartServiceRequest* request) | |
| 332 { | |
| 333 // TODO(johnmccutchan): Once the VM service is no longer a DOM isolate, | |
| 334 // we must be careful about entering the isolate. | |
| 335 DartIsolateScope isolateScope(m_isolate); | |
| 336 { | |
|
Jacob
2013/12/05 19:31:31
no need for the extra {
}
here.
Cutch
2013/12/05 22:48:15
Done.
| |
| 337 DartApiScope apiScope; | |
| 338 Dart_Handle message = MakeServiceRequestMessage(m_nativePort, request); | |
| 339 Dart_Post(m_requestPort, message); | |
| 340 } | |
| 341 } | |
| 342 | |
| 343 | |
| 344 void DartService::NativePortMessageHandler(Dart_Port destPortId, Dart_CObject* m essage) | |
| 345 { | |
| 346 // The format of the reply message is: | |
| 347 // [response string, address of DartServiceRequest]. | |
| 348 ASSERT(destPortId == m_nativePort); | |
| 349 ASSERT(message->type == Dart_CObject_kArray); | |
| 350 ASSERT(message->value.as_array.length == 2); | |
| 351 ASSERT(message->value.as_array.values[0]->type == Dart_CObject_kString); | |
| 352 intptr_t requestAddress = 0; | |
| 353 if (message->value.as_array.values[1]->type == Dart_CObject_kInt32) { | |
| 354 requestAddress = static_cast<intptr_t>(message->value.as_array.values[1] ->value.as_int32); | |
| 355 } else { | |
| 356 ASSERT(message->value.as_array.values[1]->type == Dart_CObject_kInt64); | |
| 357 requestAddress = static_cast<intptr_t>(message->value.as_array.values[1] ->value.as_int64); | |
| 358 } | |
| 359 ASSERT(request_address); | |
| 360 DartServiceRequest* request = reinterpret_cast<DartServiceRequest*>(requestA ddress); | |
| 361 request->ResponseReady(message->value.as_array.values[0]->value.as_string); | |
| 362 } | |
| 363 | |
| 364 | |
| 365 Dart_Handle DartService::GetSource(const char* name) | |
| 366 { | |
| 367 const char* vmserviceSource = 0; | |
| 368 int r = dart::bin::Resources::ResourceLookup(name, &vmserviceSource); | |
| 369 ASSERT(r != dart::bin::Resources::kNoSuchInstance); | |
| 370 return Dart_NewStringFromCString(vmserviceSource); | |
| 371 } | |
| 372 | |
| 373 | |
| 374 Dart_Handle DartService::LoadScript(const char* name) | |
| 375 { | |
| 376 Dart_Handle url = Dart_NewStringFromCString(name); | |
| 377 Dart_Handle source = GetSource(name); | |
| 378 return Dart_LoadScript(url, source, 0, 0); | |
| 379 } | |
| 380 | |
| 381 | |
| 382 Dart_Handle DartService::LoadSource(Dart_Handle library, const char* name) | |
| 383 { | |
| 384 Dart_Handle url = Dart_NewStringFromCString(name); | |
| 385 Dart_Handle source = GetSource(name); | |
| 386 return Dart_LoadSource(library, url, source); | |
| 387 } | |
| 388 | |
| 389 | |
| 390 Dart_Handle DartService::LoadSources(Dart_Handle library, const char* names[]) | |
| 391 { | |
| 392 Dart_Handle result = Dart_Null(); | |
| 393 for (int i = 0; names[i]; i++) { | |
| 394 result = LoadSource(library, names[i]); | |
| 395 if (Dart_IsError(result)) | |
| 396 break; | |
| 397 } | |
| 398 return result; | |
| 399 } | |
| 400 | |
| 401 | |
| 402 static bool IsVMServiceURL(const char* url) | |
| 403 { | |
| 404 static const intptr_t kLibraryResourceNamePrefixLen = strlen(kLibraryResourc eNamePrefix); | |
| 405 return !strncmp(kLibraryResourceNamePrefix, url, kLibraryResourceNamePrefixL en); | |
| 406 } | |
| 407 | |
| 408 | |
| 409 static bool IsVMServiceLibrary(const char* url) | |
| 410 { | |
| 411 return !strcmp(kVMServiceLibraryName, url); | |
| 412 } | |
| 413 | |
| 414 static bool IsDartLibrary(const char* url) | |
| 415 { | |
| 416 static const char* kDartPrefix = "dart:"; | |
| 417 static const intptr_t kDartPrefixLen = strlen(kDartPrefix); | |
| 418 return !strncmp(kDartPrefix, url, kDartPrefixLen); | |
| 419 } | |
| 420 | |
| 421 static Dart_Handle Canonicalize(const char* url) | |
| 422 { | |
| 423 if (IsVMServiceURL(url)) { | |
| 424 // Already canonicalized. | |
| 425 return Dart_NewStringFromCString(url); | |
| 426 } | |
| 427 // FIXME(johnmccutchan): Remove hard coded 1024 character limit. | |
| 428 char buffer[1024]; | |
| 429 snprintf(&buffer[0], sizeof(buffer), "%s/%s", kLibraryResourceNamePrefix, ur l); | |
| 430 return Dart_NewStringFromCString(buffer); | |
| 431 } | |
| 432 | |
| 433 | |
| 434 Dart_Handle DartService::LibraryTagHandler(Dart_LibraryTag tag, Dart_Handle libr ary, Dart_Handle url) | |
| 435 { | |
| 436 if (!Dart_IsLibrary(library)) | |
| 437 return Dart_NewApiError("not a library"); | |
| 438 if (!Dart_IsString(url)) | |
| 439 return Dart_NewApiError("url is not a string"); | |
| 440 const char* urlString = 0; | |
| 441 Dart_Handle result = Dart_StringToCString(url, &urlString); | |
| 442 if (Dart_IsError(result)) | |
| 443 return result; | |
| 444 Dart_Handle libraryUrl = Dart_LibraryUrl(library); | |
| 445 const char* libraryUrlString = 0; | |
| 446 result = Dart_StringToCString(libraryUrl, &libraryUrlString); | |
| 447 if (Dart_IsError(result)) | |
| 448 return result; | |
| 449 if (IsDartLibrary(urlString)) | |
| 450 return DartApplicationLoader::libraryTagHandlerCallback(tag, library, ur l); | |
| 451 switch (tag) { | |
| 452 case Dart_kCanonicalizeUrl: | |
| 453 return Canonicalize(urlString); | |
| 454 break; | |
| 455 case Dart_kImportTag: { | |
| 456 Dart_Handle source = GetSource(urlString); | |
| 457 if (Dart_IsError(source)) | |
| 458 return source; | |
| 459 Dart_Handle lib = Dart_LoadLibrary(url, source); | |
| 460 if (Dart_IsError(lib)) | |
| 461 return lib; | |
| 462 if (IsVMServiceLibrary(urlString)) { | |
| 463 // Install native resolver for this library. | |
| 464 result = Dart_SetNativeResolver(lib, DartService::NativeResolver); | |
| 465 if (Dart_IsError(result)) | |
| 466 return result; | |
| 467 } | |
| 468 return lib; | |
| 469 } | |
| 470 break; | |
| 471 case Dart_kSourceTag: { | |
| 472 Dart_Handle source = GetSource(urlString); | |
| 473 if (Dart_IsError(source)) | |
| 474 return source; | |
| 475 return Dart_LoadSource(library, url, source); | |
| 476 } | |
| 477 break; | |
| 478 default: | |
| 479 DART_UNIMPLEMENTED(); | |
| 480 break; | |
| 481 } | |
| 482 ASSERT_NOT_REACHED(); | |
| 483 return result; | |
| 484 } | |
| 485 | |
| 486 | |
| 487 void DartService::VmServiceShutdownCallback(void* callbackData) | |
| 488 { | |
| 489 ASSERT(Dart_CurrentIsolate()); | |
| 490 Dart_EnterScope(); | |
| 491 SendIsolateShutdownMessage(); | |
| 492 Dart_ExitScope(); | |
| 493 } | |
| 494 | |
| 495 static void SendServiceMessage(Dart_NativeArguments args) | |
| 496 { | |
| 497 Dart_Handle sp = Dart_GetNativeArgument(args, 0); | |
| 498 Dart_Handle rp = Dart_GetNativeArgument(args, 1); | |
| 499 Dart_Handle message = Dart_GetNativeArgument(args, 2); | |
| 500 DartServiceInternal::PostOOB(sp, rp, message); | |
| 501 } | |
| 502 | |
| 503 | |
| 504 struct VmServiceNativeEntry { | |
| 505 const char* name; | |
| 506 int numArguments; | |
| 507 Dart_NativeFunction function; | |
| 508 }; | |
| 509 | |
| 510 | |
| 511 static VmServiceNativeEntry VmServiceNativeEntries[] = { | |
| 512 {"SendServiceMessage", 3, SendServiceMessage} | |
| 513 }; | |
| 514 | |
| 515 | |
| 516 Dart_NativeFunction DartService::NativeResolver(Dart_Handle name, int numArgumen ts) | |
| 517 { | |
| 518 const char* functionName = 0; | |
| 519 Dart_Handle result = Dart_StringToCString(name, &functionName); | |
| 520 ASSERT(!Dart_IsError(result)); | |
| 521 ASSERT(functionName); | |
| 522 intptr_t n = sizeof(VmServiceNativeEntries) / sizeof(VmServiceNativeEntries[ 0]); | |
| 523 for (intptr_t i = 0; i < n; i++) { | |
| 524 VmServiceNativeEntry entry = VmServiceNativeEntries[i]; | |
| 525 if (!strcmp(functionName, entry.name) && (numArguments == entry.numArgum ents)) { | |
| 526 return entry.function; | |
| 527 } | |
| 528 } | |
| 529 return 0; | |
| 530 } | |
| 531 | |
| 532 } | |
| OLD | NEW |