| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2015, the Dartino 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 library old_servicec.plugins.cc; | |
| 6 | |
| 7 import 'dart:core' hide Type; | |
| 8 import 'dart:io' show Platform, File; | |
| 9 | |
| 10 import 'package:path/path.dart' show basenameWithoutExtension, join, dirname; | |
| 11 import 'package:servicec/util.dart' as strings; | |
| 12 | |
| 13 import 'shared.dart'; | |
| 14 | |
| 15 import '../emitter.dart'; | |
| 16 import '../primitives.dart' as primitives; | |
| 17 import '../struct_layout.dart'; | |
| 18 | |
| 19 const COPYRIGHT = """ | |
| 20 // Copyright (c) 2015, the Dartino project authors. Please see the AUTHORS file | |
| 21 // for details. All rights reserved. Use of this source code is governed by a | |
| 22 // BSD-style license that can be found in the LICENSE.md file. | |
| 23 """; | |
| 24 | |
| 25 const List<String> RESOURCES = const [ | |
| 26 "struct.h", | |
| 27 "struct.cc", | |
| 28 "unicode.h", | |
| 29 "unicode.cc", | |
| 30 ]; | |
| 31 | |
| 32 const int RESPONSE_HEADER_SIZE = 8; | |
| 33 | |
| 34 void generate(String path, | |
| 35 Unit unit, | |
| 36 String resourcesDirectory, | |
| 37 String outputDirectory) { | |
| 38 String directory = join(outputDirectory, "cc"); | |
| 39 _generateHeaderFile(path, unit, directory); | |
| 40 _generateImplementationFile(path, unit, directory); | |
| 41 | |
| 42 resourcesDirectory = join(resourcesDirectory, 'cc'); | |
| 43 for (String resource in RESOURCES) { | |
| 44 String resourcePath = join(resourcesDirectory, resource); | |
| 45 File file = new File(resourcePath); | |
| 46 String contents = file.readAsStringSync(); | |
| 47 writeToFile(directory, resource, contents); | |
| 48 } | |
| 49 } | |
| 50 | |
| 51 void _generateHeaderFile(String path, Unit unit, String directory) { | |
| 52 _HeaderVisitor visitor = new _HeaderVisitor(path); | |
| 53 visitor.visit(unit); | |
| 54 String contents = visitor.buffer.toString(); | |
| 55 writeToFile(directory, path, contents, extension: 'h'); | |
| 56 } | |
| 57 | |
| 58 void _generateImplementationFile(String path, Unit unit, String directory) { | |
| 59 _ImplementationVisitor visitor = new _ImplementationVisitor(path); | |
| 60 visitor.visit(unit); | |
| 61 String contents = visitor.buffer.toString(); | |
| 62 writeToFile(directory, path, contents, extension: 'cc'); | |
| 63 } | |
| 64 | |
| 65 abstract class CcVisitor extends CodeGenerationVisitor { | |
| 66 CcVisitor(String path) : super(path); | |
| 67 | |
| 68 static const int REQUEST_HEADER_SIZE = 56; | |
| 69 static const PRIMITIVE_TYPES = const <String, String> { | |
| 70 'void' : 'void', | |
| 71 'bool' : 'bool', | |
| 72 | |
| 73 'uint8' : 'uint8_t', | |
| 74 'uint16' : 'uint16_t', | |
| 75 | |
| 76 'int8' : 'int8_t', | |
| 77 'int16' : 'int16_t', | |
| 78 'int32' : 'int32_t', | |
| 79 'int64' : 'int64_t', | |
| 80 | |
| 81 'float32' : 'float', | |
| 82 'float64' : 'double', | |
| 83 }; | |
| 84 | |
| 85 static String cast(String type, bool cStyle) => cStyle | |
| 86 ? '($type)' | |
| 87 : 'reinterpret_cast<$type>'; | |
| 88 | |
| 89 visitUnion(Union node) { | |
| 90 throw "Unreachable"; | |
| 91 } | |
| 92 | |
| 93 visitFormal(Formal node) { | |
| 94 writeType(node.type); | |
| 95 write(' ${node.name}'); | |
| 96 } | |
| 97 | |
| 98 void writeType(Type node) { | |
| 99 Node resolved = node.resolved; | |
| 100 if (resolved != null) { | |
| 101 write('${node.identifier}Builder'); | |
| 102 } else { | |
| 103 String type = PRIMITIVE_TYPES[node.identifier]; | |
| 104 write(type); | |
| 105 } | |
| 106 } | |
| 107 | |
| 108 void writeReturnType(Type node) { | |
| 109 Node resolved = node.resolved; | |
| 110 if (resolved != null) { | |
| 111 write('${node.identifier}'); | |
| 112 } else { | |
| 113 String type = PRIMITIVE_TYPES[node.identifier]; | |
| 114 write(type); | |
| 115 } | |
| 116 } | |
| 117 | |
| 118 visitArguments(List<Formal> formals) { | |
| 119 visitNodes(formals, (first) => first ? '' : ', '); | |
| 120 } | |
| 121 | |
| 122 visitStructArgumentMethodBody(String id, | |
| 123 Method method, | |
| 124 {String callback}) { | |
| 125 bool async = callback != null; | |
| 126 String argumentName = method.arguments.single.name; | |
| 127 if (method.outputKind == OutputKind.STRUCT) { | |
| 128 if (async) { | |
| 129 write(' '); | |
| 130 writeln('$argumentName.InvokeMethodAsync(service_id_, $id,' | |
| 131 ' $callback, reinterpret_cast<void*>(callback), callback_data);'
); | |
| 132 } else { | |
| 133 writeln(' int64_t result = $argumentName.' | |
| 134 'InvokeMethod(service_id_, $id);'); | |
| 135 writeln(' char* memory = reinterpret_cast<char*>(result);'); | |
| 136 // TODO(ajohnsen): Do range-check between size and segment size. | |
| 137 writeln(' Segment* segment = MessageReader::GetRootSegment(memory);'); | |
| 138 writeln(' return ${method.returnType.identifier}' | |
| 139 '(segment, $RESPONSE_HEADER_SIZE);'); | |
| 140 } | |
| 141 } else { | |
| 142 write(' '); | |
| 143 if (!method.returnType.isVoid && !async) write('return '); | |
| 144 String suffix = async ? 'Async' : ''; | |
| 145 String cb = async ? ', $callback, reinterpret_cast<void*>(callback), callb
ack_data' : ''; | |
| 146 writeln('$argumentName.InvokeMethod$suffix(service_id_, $id$cb);'); | |
| 147 } | |
| 148 } | |
| 149 | |
| 150 visitMethodBody(String id, | |
| 151 Method method, | |
| 152 {bool cStyle: false, | |
| 153 List<String> extraArguments: const [], | |
| 154 String callback}) { | |
| 155 List<Formal> arguments = method.arguments; | |
| 156 assert(method.inputKind == InputKind.PRIMITIVES); | |
| 157 StructLayout layout = method.inputPrimitiveStructLayout; | |
| 158 final bool async = callback != null; | |
| 159 int size = REQUEST_HEADER_SIZE + layout.size; | |
| 160 if (async) { | |
| 161 write(' static const int kSize = '); | |
| 162 writeln('${size} + ${extraArguments.length} * sizeof(void*);'); | |
| 163 } else { | |
| 164 writeln(' static const int kSize = ${size};'); | |
| 165 } | |
| 166 | |
| 167 String cast(String type) => CcVisitor.cast(type, cStyle); | |
| 168 | |
| 169 String pointerToArgument(int offset, int pointers, String type) { | |
| 170 offset += REQUEST_HEADER_SIZE; | |
| 171 String prefix = cast('$type*'); | |
| 172 if (pointers == 0) return '$prefix(_buffer + $offset)'; | |
| 173 return '$prefix(_buffer + $offset + $pointers * sizeof(void*))'; | |
| 174 } | |
| 175 | |
| 176 if (async) { | |
| 177 writeln(' char* _buffer = ${cast("char*")}(malloc(kSize));'); | |
| 178 } else { | |
| 179 writeln(' char _bits[kSize];'); | |
| 180 writeln(' char* _buffer = _bits;'); | |
| 181 } | |
| 182 | |
| 183 // Mark the message as being non-segmented. | |
| 184 writeln(' *${pointerToArgument(-8, 0, "int64_t")} = 0;'); | |
| 185 | |
| 186 int arity = arguments.length; | |
| 187 for (int i = 0; i < arity; i++) { | |
| 188 String name = arguments[i].name; | |
| 189 int offset = layout[arguments[i]].offset; | |
| 190 String type = PRIMITIVE_TYPES[arguments[i].type.identifier]; | |
| 191 writeln(' *${pointerToArgument(offset, 0, type)} = $name;'); | |
| 192 } | |
| 193 | |
| 194 if (async) { | |
| 195 String callbackFunction = pointerToArgument(-16, 0, 'void*'); | |
| 196 String callbackData = pointerToArgument(-24, 0, 'void*'); | |
| 197 writeln(' *$callbackFunction = ${cast("void*")}(callback);'); | |
| 198 writeln(' *$callbackData = callback_data;'); | |
| 199 for (int i = 0; i < extraArguments.length; i++) { | |
| 200 String dataArgument = pointerToArgument(layout.size, i, 'void*'); | |
| 201 String arg = extraArguments[i]; | |
| 202 writeln(' *$dataArgument = ${cast("void*")}($arg);'); | |
| 203 } | |
| 204 write(' ServiceApiInvokeAsync(service_id_, $id, $callback, '); | |
| 205 writeln('_buffer, kSize);'); | |
| 206 } else { | |
| 207 writeln(' ServiceApiInvoke(service_id_, $id, _buffer, kSize);'); | |
| 208 if (method.outputKind == OutputKind.STRUCT) { | |
| 209 writeln(' int64_t result = *${pointerToArgument(0, 0, 'int64_t')};'); | |
| 210 writeln(' char* memory = reinterpret_cast<char*>(result);'); | |
| 211 // TODO(ajohnsen): Do range-check between size and segment size. | |
| 212 writeln(' Segment* segment = MessageReader::GetRootSegment(memory);'); | |
| 213 writeln(' return ${method.returnType.identifier}' | |
| 214 '(segment, $RESPONSE_HEADER_SIZE);'); | |
| 215 } else if (!method.returnType.isVoid) { | |
| 216 writeln(' return *${pointerToArgument(0, 0, 'int64_t')};'); | |
| 217 } | |
| 218 } | |
| 219 } | |
| 220 } | |
| 221 | |
| 222 class _HeaderVisitor extends CcVisitor { | |
| 223 _HeaderVisitor(String path) : super(path); | |
| 224 | |
| 225 String computeHeaderGuard() { | |
| 226 String base = basenameWithoutExtension(path).toUpperCase(); | |
| 227 return '${base}_H'; | |
| 228 } | |
| 229 | |
| 230 visitUnit(Unit node) { | |
| 231 String headerGuard = computeHeaderGuard(); | |
| 232 writeln(COPYRIGHT); | |
| 233 | |
| 234 writeln('// Generated file. Do not edit.'); | |
| 235 writeln(); | |
| 236 | |
| 237 writeln('#ifndef $headerGuard'); | |
| 238 writeln('#define $headerGuard'); | |
| 239 | |
| 240 writeln(); | |
| 241 writeln('#include <inttypes.h>'); | |
| 242 writeln('#include "struct.h"'); | |
| 243 | |
| 244 if (node.structs.isNotEmpty) writeln(); | |
| 245 for (Struct struct in node.structs) { | |
| 246 writeln('class ${struct.name};'); | |
| 247 writeln('class ${struct.name}Builder;'); | |
| 248 } | |
| 249 | |
| 250 node.services.forEach(visit); | |
| 251 node.structs.forEach(visit); | |
| 252 | |
| 253 writeln(); | |
| 254 writeln('#endif // $headerGuard'); | |
| 255 } | |
| 256 | |
| 257 visitService(Service node) { | |
| 258 writeln(); | |
| 259 writeln('class ${node.name} {'); | |
| 260 writeln(' public:'); | |
| 261 writeln(' static void setup();'); | |
| 262 writeln(' static void tearDown();'); | |
| 263 | |
| 264 node.methods.forEach(visit); | |
| 265 | |
| 266 writeln('};'); | |
| 267 } | |
| 268 | |
| 269 visitMethod(Method node) { | |
| 270 write(' static '); | |
| 271 writeReturnType(node.returnType); | |
| 272 write(' ${node.name}('); | |
| 273 visitArguments(node.arguments); | |
| 274 writeln(');'); | |
| 275 | |
| 276 write(' static void ${node.name}Async('); | |
| 277 visitArguments(node.arguments); | |
| 278 if (node.arguments.isNotEmpty) write(', '); | |
| 279 write('void (*callback)('); | |
| 280 if (!node.returnType.isVoid) { | |
| 281 writeReturnType(node.returnType); | |
| 282 write(', '); | |
| 283 } | |
| 284 writeln('void*), void* callback_data);'); | |
| 285 } | |
| 286 | |
| 287 visitStruct(Struct node) { | |
| 288 writeReader(node); | |
| 289 writeBuilder(node); | |
| 290 } | |
| 291 | |
| 292 void writeReader(Struct node) { | |
| 293 String name = node.name; | |
| 294 StructLayout layout = node.layout; | |
| 295 | |
| 296 writeln(); | |
| 297 writeln('class $name : public Reader {'); | |
| 298 writeln(' public:'); | |
| 299 writeln(' static const int kSize = ${layout.size};'); | |
| 300 | |
| 301 writeln(' $name(Segment* segment, int offset)'); | |
| 302 writeln(' : Reader(segment, offset) { }'); | |
| 303 writeln(); | |
| 304 | |
| 305 for (StructSlot slot in layout.slots) { | |
| 306 Type slotType = slot.slot.type; | |
| 307 String camel = camelize(slot.slot.name); | |
| 308 | |
| 309 if (slot.isUnionSlot) { | |
| 310 String tagName = camelize(slot.union.tag.name); | |
| 311 int tag = slot.unionTag; | |
| 312 writeln(' bool is$camel() const { return $tag == get$tagName(); }'); | |
| 313 } | |
| 314 | |
| 315 if (slotType.isList) { | |
| 316 write(' '); | |
| 317 write('List<'); | |
| 318 writeReturnType(slotType); | |
| 319 write('> get$camel() const { '); | |
| 320 write('return ReadList<'); | |
| 321 writeReturnType(slotType); | |
| 322 writeln('>(${slot.offset}); }'); | |
| 323 } else if (slotType.isVoid) { | |
| 324 // No getters for void slots. | |
| 325 } else if (slotType.isString) { | |
| 326 writeln(' char* get$camel() const ' | |
| 327 '{ return ReadString(${slot.offset}); }'); | |
| 328 writeln(' List<uint16_t> get${camel}Data() const ' | |
| 329 '{ return ReadList<uint16_t>(${slot.offset}); }'); | |
| 330 } else if (slotType.isPrimitive) { | |
| 331 write(' '); | |
| 332 writeType(slotType); | |
| 333 write(' get$camel() const { return *PointerTo<'); | |
| 334 if (slotType.isBool) { | |
| 335 writeln('uint8_t>(${slot.offset}) != 0; }'); | |
| 336 } else { | |
| 337 writeType(slotType); | |
| 338 writeln('>(${slot.offset}); }'); | |
| 339 } | |
| 340 } else { | |
| 341 write(' '); | |
| 342 writeReturnType(slotType); | |
| 343 writeln(' get$camel() const;'); | |
| 344 } | |
| 345 } | |
| 346 | |
| 347 writeln('};'); | |
| 348 } | |
| 349 | |
| 350 void writeBuilder(Struct node) { | |
| 351 String name = "${node.name}Builder"; | |
| 352 StructLayout layout = node.layout; | |
| 353 | |
| 354 writeln(); | |
| 355 writeln('class $name : public Builder {'); | |
| 356 writeln(' public:'); | |
| 357 writeln(' static const int kSize = ${layout.size};'); | |
| 358 writeln(); | |
| 359 | |
| 360 writeln(' explicit $name(const Builder& builder)'); | |
| 361 writeln(' : Builder(builder) { }'); | |
| 362 writeln(' $name(Segment* segment, int offset)'); | |
| 363 writeln(' : Builder(segment, offset) { }'); | |
| 364 writeln(); | |
| 365 | |
| 366 for (StructSlot slot in layout.slots) { | |
| 367 String slotName = slot.slot.name; | |
| 368 Type slotType = slot.slot.type; | |
| 369 | |
| 370 String updateTag = ''; | |
| 371 if (slot.isUnionSlot) { | |
| 372 String tagName = camelize(slot.union.tag.name); | |
| 373 int tag = slot.unionTag; | |
| 374 updateTag = 'set$tagName($tag); '; | |
| 375 } | |
| 376 | |
| 377 String camel = camelize(slotName); | |
| 378 if (slotType.isList) { | |
| 379 write(' List<'); | |
| 380 writeType(slotType); | |
| 381 writeln('> init$camel(int length);'); | |
| 382 } else if (slotType.isVoid) { | |
| 383 assert(slot.isUnionSlot); | |
| 384 String tagName = camelize(slot.union.tag.name); | |
| 385 int tag = slot.unionTag; | |
| 386 writeln(' void set$camel() { set$tagName($tag); }'); | |
| 387 } else if (slotType.isString) { | |
| 388 write(' void set$camel(const char* value) { '); | |
| 389 write(updateTag); | |
| 390 writeln('NewString(${slot.offset}, value); }'); | |
| 391 writeln(' List<uint16_t> init${camel}Data(int length);'); | |
| 392 } else if (slotType.isPrimitive) { | |
| 393 write(' void set$camel('); | |
| 394 writeType(slotType); | |
| 395 write(' value) { '); | |
| 396 write(updateTag); | |
| 397 write('*PointerTo<'); | |
| 398 if (slotType.isBool) { | |
| 399 writeln('uint8_t>(${slot.offset}) = value ? 1 : 0; }'); | |
| 400 } else { | |
| 401 writeType(slotType); | |
| 402 writeln('>(${slot.offset}) = value; }'); | |
| 403 } | |
| 404 } else { | |
| 405 write(' '); | |
| 406 writeType(slotType); | |
| 407 writeln(' init$camel();'); | |
| 408 } | |
| 409 } | |
| 410 | |
| 411 writeln('};'); | |
| 412 } | |
| 413 } | |
| 414 | |
| 415 class _ImplementationVisitor extends CcVisitor { | |
| 416 int methodId = 1; | |
| 417 String serviceName; | |
| 418 | |
| 419 _ImplementationVisitor(String path) : super(path); | |
| 420 | |
| 421 String computeHeaderFile() { | |
| 422 String base = basenameWithoutExtension(path); | |
| 423 return '$base.h'; | |
| 424 } | |
| 425 | |
| 426 visitUnit(Unit node) { | |
| 427 String headerFile = computeHeaderFile(); | |
| 428 writeln(COPYRIGHT); | |
| 429 | |
| 430 writeln('// Generated file. Do not edit.'); | |
| 431 writeln(); | |
| 432 | |
| 433 writeln('#include "$headerFile"'); | |
| 434 writeln('#include "include/service_api.h"'); | |
| 435 writeln('#include <stdlib.h>'); | |
| 436 | |
| 437 node.services.forEach(visit); | |
| 438 node.structs.forEach(visit); | |
| 439 } | |
| 440 | |
| 441 visitService(Service node) { | |
| 442 writeln(); | |
| 443 writeln('static ServiceId service_id_ = kNoServiceId;'); | |
| 444 | |
| 445 serviceName = node.name; | |
| 446 | |
| 447 writeln(); | |
| 448 writeln('void ${serviceName}::setup() {'); | |
| 449 writeln(' service_id_ = ServiceApiLookup("$serviceName");'); | |
| 450 writeln('}'); | |
| 451 | |
| 452 writeln(); | |
| 453 writeln('void ${serviceName}::tearDown() {'); | |
| 454 writeln(' ServiceApiTerminate(service_id_);'); | |
| 455 writeln(' service_id_ = kNoServiceId;'); | |
| 456 writeln('}'); | |
| 457 | |
| 458 node.methods.forEach(visit); | |
| 459 } | |
| 460 | |
| 461 visitStruct(Struct node) { | |
| 462 writeBuilder(node); | |
| 463 writeReader(node); | |
| 464 } | |
| 465 | |
| 466 void writeBuilder(Struct node) { | |
| 467 String name = "${node.name}Builder"; | |
| 468 StructLayout layout = node.layout; | |
| 469 | |
| 470 for (StructSlot slot in layout.slots) { | |
| 471 String slotName = slot.slot.name; | |
| 472 Type slotType = slot.slot.type; | |
| 473 | |
| 474 String updateTag = ''; | |
| 475 if (slot.isUnionSlot) { | |
| 476 String tagName = camelize(slot.union.tag.name); | |
| 477 int tag = slot.unionTag; | |
| 478 updateTag = ' set$tagName($tag);\n'; | |
| 479 } | |
| 480 | |
| 481 String camel = camelize(slotName); | |
| 482 if (slotType.isList) { | |
| 483 writeln(); | |
| 484 write('List<'); | |
| 485 writeType(slotType); | |
| 486 writeln('> $name::init$camel(int length) {'); | |
| 487 write(updateTag); | |
| 488 int size = 0; | |
| 489 if (slotType.isPrimitive) { | |
| 490 size = primitives.size(slotType.primitiveType); | |
| 491 } else { | |
| 492 Struct element = slot.slot.type.resolved; | |
| 493 StructLayout elementLayout = element.layout; | |
| 494 size = elementLayout.size; | |
| 495 } | |
| 496 writeln(' Reader result = NewList(${slot.offset}, length, $size);'); | |
| 497 write(' return List<'); | |
| 498 writeType(slotType); | |
| 499 writeln('>(result.segment(), result.offset(), length);'); | |
| 500 writeln('}'); | |
| 501 } else if (slotType.isString) { | |
| 502 writeln(); | |
| 503 writeln('List<uint16_t> $name::init${camel}Data(int length) {'); | |
| 504 write(updateTag); | |
| 505 writeln(' Reader result = NewList(${slot.offset}, length, 2);'); | |
| 506 writeln(' return List<uint16_t>(result.segment(), result.offset(),' | |
| 507 ' length);'); | |
| 508 writeln('}'); | |
| 509 } else if (!slotType.isPrimitive) { | |
| 510 writeln(); | |
| 511 writeType(slotType); | |
| 512 writeln(' $name::init$camel() {'); | |
| 513 Struct element = slot.slot.type.resolved; | |
| 514 StructLayout elementLayout = element.layout; | |
| 515 int size = elementLayout.size; | |
| 516 write(updateTag); | |
| 517 if (!slotType.isPointer) { | |
| 518 write(' return '); | |
| 519 writeType(slotType); | |
| 520 writeln('(segment(), offset() + ${slot.offset});'); | |
| 521 } else { | |
| 522 writeln(' Builder result = NewStruct(${slot.offset}, $size);'); | |
| 523 write(' return '); | |
| 524 writeType(slotType); | |
| 525 writeln('(result);'); | |
| 526 } | |
| 527 writeln('}'); | |
| 528 } | |
| 529 } | |
| 530 } | |
| 531 | |
| 532 void writeReader(Struct node) { | |
| 533 String name = "${node.name}"; | |
| 534 StructLayout layout = node.layout; | |
| 535 | |
| 536 for (StructSlot slot in layout.slots) { | |
| 537 String slotName = slot.slot.name; | |
| 538 Type slotType = slot.slot.type; | |
| 539 | |
| 540 String camel = camelize(slotName); | |
| 541 if (!slotType.isPrimitive && !slotType.isList && !slotType.isString) { | |
| 542 writeln(); | |
| 543 writeReturnType(slotType); | |
| 544 write(' $name::get$camel() const { return '); | |
| 545 if (!slotType.isPointer) { | |
| 546 writeReturnType(slotType); | |
| 547 writeln('(segment(), offset() + ${slot.offset}); }'); | |
| 548 } else { | |
| 549 write('ReadStruct<'); | |
| 550 writeReturnType(slotType); | |
| 551 writeln('>(${slot.offset}); }'); | |
| 552 } | |
| 553 } | |
| 554 } | |
| 555 } | |
| 556 | |
| 557 visitMethod(Method node) { | |
| 558 String name = node.name; | |
| 559 String id = 'k${camelize(name)}Id_'; | |
| 560 | |
| 561 writeln(); | |
| 562 write('static const MethodId $id = '); | |
| 563 writeln('reinterpret_cast<MethodId>(${methodId++});'); | |
| 564 | |
| 565 writeln(); | |
| 566 writeReturnType(node.returnType); | |
| 567 write(' $serviceName::${name}('); | |
| 568 visitArguments(node.arguments); | |
| 569 writeln(') {'); | |
| 570 | |
| 571 if (node.inputKind == InputKind.STRUCT) { | |
| 572 visitStructArgumentMethodBody(id, node); | |
| 573 } else { | |
| 574 assert(node.inputKind == InputKind.PRIMITIVES); | |
| 575 visitMethodBody(id, node); | |
| 576 } | |
| 577 | |
| 578 writeln('}'); | |
| 579 | |
| 580 String callback; | |
| 581 if (node.inputKind == InputKind.STRUCT) { | |
| 582 Struct struct = node.arguments.single.type.resolved; | |
| 583 StructLayout layout = struct.layout; | |
| 584 callback = ensureCallback(node.returnType, layout); | |
| 585 } else { | |
| 586 callback = | |
| 587 ensureCallback(node.returnType, node.inputPrimitiveStructLayout); | |
| 588 } | |
| 589 | |
| 590 writeln(); | |
| 591 write('void $serviceName::${name}Async('); | |
| 592 visitArguments(node.arguments); | |
| 593 if (node.arguments.isNotEmpty) write(', '); | |
| 594 write('void (*callback)('); | |
| 595 if (!node.returnType.isVoid) { | |
| 596 writeReturnType(node.returnType); | |
| 597 write(', '); | |
| 598 } | |
| 599 writeln('void*), void* callback_data) {'); | |
| 600 | |
| 601 if (node.inputKind == InputKind.STRUCT) { | |
| 602 visitStructArgumentMethodBody(id, node, callback: callback); | |
| 603 } else { | |
| 604 visitMethodBody(id, node, callback: callback); | |
| 605 } | |
| 606 | |
| 607 writeln('}'); | |
| 608 } | |
| 609 | |
| 610 final Map<String, String> callbacks = {}; | |
| 611 String ensureCallback(Type type, | |
| 612 StructLayout layout, | |
| 613 {bool cStyle: false}) { | |
| 614 String key = '${type.identifier}_${layout.size}'; | |
| 615 return callbacks.putIfAbsent(key, () { | |
| 616 String cast(String type) => CcVisitor.cast(type, cStyle); | |
| 617 String pointerToArgument(int offset, int pointers, String type) { | |
| 618 offset += CcVisitor.REQUEST_HEADER_SIZE; | |
| 619 String prefix = cast('$type*'); | |
| 620 if (pointers == 0) return '$prefix(buffer + $offset)'; | |
| 621 return '$prefix(buffer + $offset + $pointers * sizeof(void*))'; | |
| 622 } | |
| 623 String name = 'Unwrap_$key'; | |
| 624 writeln(); | |
| 625 writeln('static void $name(void* raw) {'); | |
| 626 if (type.isVoid) { | |
| 627 writeln(' typedef void (*cbt)(void*);'); | |
| 628 } else { | |
| 629 write(' typedef void (*cbt)('); | |
| 630 writeReturnType(type); | |
| 631 writeln(', void*);'); | |
| 632 } | |
| 633 writeln(' char* buffer = ${cast('char*')}(raw);'); | |
| 634 int offset = CcVisitor.REQUEST_HEADER_SIZE; | |
| 635 if (!type.isVoid) { | |
| 636 writeln(' int64_t result = *${cast('int64_t*')}(buffer + $offset);'); | |
| 637 if (!type.isPrimitive) { | |
| 638 writeln(' char* memory = reinterpret_cast<char*>(result);'); | |
| 639 writeln(' Segment* segment = ' | |
| 640 'MessageReader::GetRootSegment(memory);'); | |
| 641 } | |
| 642 } | |
| 643 String callbackFunction = pointerToArgument(-16, 0, 'cbt'); | |
| 644 String callbackData = pointerToArgument(-24, 0, 'void*'); | |
| 645 writeln(' cbt callback = *$callbackFunction;'); | |
| 646 writeln(' void* callback_data = *$callbackData;'); | |
| 647 writeln(' MessageBuilder::DeleteMessage(buffer);'); | |
| 648 if (type.isVoid) { | |
| 649 writeln(' callback(callback_data);'); | |
| 650 } else { | |
| 651 if (type.isPrimitive) { | |
| 652 writeln(' callback(result, callback_data);'); | |
| 653 } else { | |
| 654 write(' callback('); | |
| 655 writeReturnType(type); | |
| 656 writeln('(segment, 8), callback_data);'); | |
| 657 } | |
| 658 } | |
| 659 writeln('}'); | |
| 660 return name; | |
| 661 }); | |
| 662 } | |
| 663 } | |
| OLD | NEW |