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.java; | |
6 | |
7 import 'dart:core' hide Type; | |
8 import 'dart:io'; | |
9 | |
10 import 'package:path/path.dart' show | |
11 basenameWithoutExtension, | |
12 join, | |
13 relative; | |
14 import 'package:servicec/util.dart' as strings; | |
15 | |
16 import 'shared.dart'; | |
17 import '../emitter.dart'; | |
18 import '../struct_layout.dart'; | |
19 import '../primitives.dart' as primitives; | |
20 | |
21 import 'cc.dart' show CcVisitor; | |
22 | |
23 const HEADER = """ | |
24 // Copyright (c) 2015, the Dartino project authors. Please see the AUTHORS file | |
25 // for details. All rights reserved. Use of this source code is governed by a | |
26 // BSD-style license that can be found in the LICENSE.md file. | |
27 | |
28 // Generated file. Do not edit. | |
29 """; | |
30 | |
31 const HEADER_MK = """ | |
32 # Copyright (c) 2015, the Dartino project authors. Please see the AUTHORS file | |
33 # for details. All rights reserved. Use of this source code is governed by a | |
34 # BSD-style license that can be found in the LICENSE.md file. | |
35 | |
36 # Generated file. Do not edit. | |
37 """; | |
38 | |
39 const READER_HEADER = """ | |
40 package dartino; | |
41 """; | |
42 | |
43 const DARTINO_API_JAVA = """ | |
44 package dartino; | |
45 | |
46 public class DartinoApi { | |
47 public static abstract class PrintInterceptor { | |
48 public abstract void Out(String message); | |
49 public abstract void Error(String message); | |
50 private long nativePtr = 0; | |
51 } | |
52 public static native void Setup(); | |
53 public static native void TearDown(); | |
54 public static native void RunSnapshot(byte[] snapshot); | |
55 public static native void WaitForDebuggerConnection(int port); | |
56 public static native void AddDefaultSharedLibrary(String library); | |
57 public static native void RegisterPrintInterceptor(PrintInterceptor intercepto
r); | |
58 public static native void UnregisterPrintInterceptor(PrintInterceptor intercep
tor); | |
59 } | |
60 """; | |
61 | |
62 const DARTINO_SERVICE_API_JAVA = """ | |
63 package dartino; | |
64 | |
65 public class DartinoServiceApi { | |
66 public static native void Setup(); | |
67 public static native void TearDown(); | |
68 } | |
69 """; | |
70 | |
71 const DARTINO_API_JAVA_IMPL = """ | |
72 #include <jni.h> | |
73 #include <stdlib.h> | |
74 | |
75 #include "dartino_api.h" | |
76 | |
77 #ifdef ANDROID | |
78 typedef JNIEnv* AttachEnvType; | |
79 #else | |
80 typedef void* AttachEnvType; | |
81 #endif | |
82 | |
83 static JNIEnv* AttachCurrentThreadAndGetEnv(JavaVM* vm) { | |
84 AttachEnvType result = NULL; | |
85 if (vm->AttachCurrentThread(&result, NULL) != JNI_OK) { | |
86 // TODO(zerny): Nicer error recovery? | |
87 exit(1); | |
88 } | |
89 return reinterpret_cast<JNIEnv*>(result); | |
90 } | |
91 | |
92 class JNIPrintInterceptorInfo { | |
93 public: | |
94 JNIPrintInterceptorInfo(JavaVM* vm, jobject obj) | |
95 : vm(vm), obj(obj), interceptor(NULL) {} | |
96 JavaVM* vm; | |
97 jobject obj; | |
98 DartinoPrintInterceptor interceptor; | |
99 }; | |
100 | |
101 static void JNIPrintInterceptorFunction( | |
102 const char* message, int out, void* raw) { | |
103 JNIPrintInterceptorInfo* info = | |
104 reinterpret_cast<JNIPrintInterceptorInfo*>(raw); | |
105 JNIEnv* env = AttachCurrentThreadAndGetEnv(info->vm); | |
106 jobject obj = info->obj; | |
107 jclass clazz = env->GetObjectClass(obj); | |
108 jmethodID method = NULL; | |
109 const char* methodName = (out == 2) ? "Out" : (out == 3) ? "Error" : NULL; | |
110 const char* methodSig = "(Ljava/lang/String;)V"; | |
111 if (methodName == NULL) exit(1); | |
112 method = env->GetMethodID(clazz, methodName, methodSig); | |
113 jstring argument = env->NewStringUTF(message); | |
114 env->CallVoidMethod(obj, method, argument); | |
115 info->vm->DetachCurrentThread(); | |
116 } | |
117 | |
118 static void RegisterPrintInterceptor(JNIEnv* env, jobject obj) { | |
119 // TODO(zerny): Associate the Java object and native object in a map. | |
120 jclass clazz = env->GetObjectClass(obj); | |
121 jfieldID nativePtr = env->GetFieldID(clazz, "nativePtr", "J"); | |
122 jlong nativePtrValue = env->GetLongField(obj, nativePtr); | |
123 if (nativePtrValue != 0) return; | |
124 obj = env->NewGlobalRef(obj); | |
125 JavaVM* vm = NULL; | |
126 env->GetJavaVM(&vm); | |
127 JNIPrintInterceptorInfo* info = new JNIPrintInterceptorInfo(vm, obj); | |
128 info->interceptor = DartinoRegisterPrintInterceptor( | |
129 JNIPrintInterceptorFunction, reinterpret_cast<void*>(info)); | |
130 env->SetLongField(obj, nativePtr, reinterpret_cast<jlong>(info)); | |
131 } | |
132 | |
133 static void UnregisterPrintInterceptor(JNIEnv* env, jobject obj) { | |
134 // TODO(zerny): Retrieve the native object from the Java object via a map. | |
135 jclass clazz = env->GetObjectClass(obj); | |
136 jfieldID nativePtr = env->GetFieldID(clazz, "nativePtr", "J"); | |
137 jlong nativePtrValue = env->GetLongField(obj, nativePtr); | |
138 if (nativePtrValue == 0) return; | |
139 env->SetLongField(obj, nativePtr, 0); | |
140 JNIPrintInterceptorInfo* info = | |
141 reinterpret_cast<JNIPrintInterceptorInfo*>(nativePtrValue); | |
142 DartinoUnregisterPrintInterceptor(info->interceptor); | |
143 env->DeleteGlobalRef(info->obj); | |
144 delete info; | |
145 } | |
146 | |
147 #ifdef __cplusplus | |
148 extern "C" { | |
149 #endif | |
150 | |
151 JNIEXPORT void JNICALL Java_dartino_DartinoApi_Setup(JNIEnv*, jclass) { | |
152 DartinoSetup(); | |
153 } | |
154 | |
155 JNIEXPORT void JNICALL Java_dartino_DartinoApi_TearDown(JNIEnv*, jclass) { | |
156 DartinoTearDown(); | |
157 } | |
158 | |
159 JNIEXPORT void JNICALL Java_dartino_DartinoApi_RunSnapshot(JNIEnv* env, | |
160 jclass, | |
161 jbyteArray snapshot) { | |
162 int len = env->GetArrayLength(snapshot); | |
163 unsigned char* copy = new unsigned char[len]; | |
164 env->GetByteArrayRegion(snapshot, 0, len, reinterpret_cast<jbyte*>(copy)); | |
165 DartinoProgram program = DartinoLoadSnapshot(copy, len); | |
166 delete[] copy; | |
167 DartinoRunMain(program, 0, NULL); | |
168 DartinoDeleteProgram(program); | |
169 } | |
170 | |
171 JNIEXPORT void JNICALL Java_dartino_DartinoApi_AddDefaultSharedLibrary( | |
172 JNIEnv* env, jclass, jstring str) { | |
173 const char* library = env->GetStringUTFChars(str, 0); | |
174 DartinoAddDefaultSharedLibrary(library); | |
175 env->ReleaseStringUTFChars(str, library); | |
176 } | |
177 | |
178 JNIEXPORT void JNICALL Java_dartino_DartinoApi_RegisterPrintInterceptor( | |
179 JNIEnv* env, jclass, jobject obj) { | |
180 RegisterPrintInterceptor(env, obj); | |
181 } | |
182 | |
183 JNIEXPORT void JNICALL Java_dartino_DartinoApi_UnregisterPrintInterceptor( | |
184 JNIEnv* env, jclass, jobject obj) { | |
185 UnregisterPrintInterceptor(env, obj); | |
186 } | |
187 | |
188 #ifdef __cplusplus | |
189 } | |
190 #endif | |
191 """; | |
192 | |
193 const DARTINO_SERVICE_API_JAVA_IMPL = """ | |
194 #include <jni.h> | |
195 | |
196 #include "service_api.h" | |
197 | |
198 #ifdef __cplusplus | |
199 extern "C" { | |
200 #endif | |
201 | |
202 JNIEXPORT void JNICALL Java_dartino_DartinoServiceApi_Setup(JNIEnv*, jclass) { | |
203 ServiceApiSetup(); | |
204 } | |
205 | |
206 JNIEXPORT void JNICALL Java_dartino_DartinoServiceApi_TearDown(JNIEnv*, jclass)
{ | |
207 ServiceApiTearDown(); | |
208 } | |
209 | |
210 #ifdef __cplusplus | |
211 } | |
212 #endif | |
213 """; | |
214 | |
215 String JNI_UTILS = """ | |
216 static JNIEnv* AttachCurrentThreadAndGetEnv(JavaVM* vm) { | |
217 AttachEnvType result = NULL; | |
218 if (vm->AttachCurrentThread(&result, NULL) != JNI_OK) { | |
219 // TODO(ager): Nicer error recovery? | |
220 exit(1); | |
221 } | |
222 return reinterpret_cast<JNIEnv*>(result); | |
223 } | |
224 | |
225 static void DetachCurrentThread(JavaVM* vm) { | |
226 if (vm->DetachCurrentThread() != JNI_OK) { | |
227 // TODO(ager): Nicer error recovery? | |
228 exit(1); | |
229 } | |
230 } | |
231 | |
232 static jobject CreateByteArray(JNIEnv* env, char* memory, int size) { | |
233 jbyteArray result = env->NewByteArray(size); | |
234 jbyte* contents = reinterpret_cast<jbyte*>(memory); | |
235 env->SetByteArrayRegion(result, 0, size, contents); | |
236 free(memory); | |
237 return result; | |
238 } | |
239 | |
240 static jobject CreateByteArrayArray(JNIEnv* env, char* memory, int size) { | |
241 jobjectArray array = env->NewObjectArray(size, env->FindClass("[B"), NULL); | |
242 for (int i = 0; i < size; i++) { | |
243 int64_t address = *reinterpret_cast<int64_t*>(memory + 8 + (i * 16)); | |
244 int size = *reinterpret_cast<int*>(memory + 16 + (i * 16)); | |
245 char* contents = reinterpret_cast<char*>(address); | |
246 env->SetObjectArrayElement(array, i, CreateByteArray(env, contents, size)); | |
247 } | |
248 free(memory); | |
249 return array; | |
250 } | |
251 | |
252 static jobject GetRootSegment(JNIEnv* env, char* memory) { | |
253 int32_t segments = *reinterpret_cast<int32_t*>(memory); | |
254 if (segments == 0) { | |
255 int32_t size = *reinterpret_cast<int32_t*>(memory + 4); | |
256 return CreateByteArray(env, memory, size); | |
257 } | |
258 return CreateByteArrayArray(env, memory, segments); | |
259 } | |
260 | |
261 class CallbackInfo { | |
262 public: | |
263 CallbackInfo(jobject jcallback, JavaVM* jvm) | |
264 : callback(jcallback), vm(jvm) { } | |
265 jobject callback; | |
266 JavaVM* vm; | |
267 }; | |
268 | |
269 static char* ExtractByteArrayData(JNIEnv* env, | |
270 jbyteArray segment, | |
271 jint segment_length) { | |
272 jbyte* data = env->GetByteArrayElements(segment, NULL); | |
273 char* segment_copy = reinterpret_cast<char*>(malloc(segment_length)); | |
274 memcpy(segment_copy, data, segment_length); | |
275 env->ReleaseByteArrayElements(segment, data, JNI_ABORT); | |
276 return segment_copy; | |
277 } | |
278 | |
279 static int ComputeMessage(JNIEnv* env, | |
280 jobject builder, | |
281 jobject callback, | |
282 JavaVM* vm, | |
283 char** buffer) { | |
284 CallbackInfo* info = NULL; | |
285 if (callback != NULL) { | |
286 info = new CallbackInfo(callback, vm); | |
287 } | |
288 | |
289 jclass clazz = env->GetObjectClass(builder); | |
290 jmethodID method_id = env->GetMethodID(clazz, "getSegments", "()[Ljava/lang/Ob
ject;"); | |
291 jobjectArray array = (jobjectArray)env->CallObjectMethod(builder, method_id); | |
292 jobjectArray segments = (jobjectArray)env->GetObjectArrayElement(array, 0); | |
293 jintArray sizes_array = (jintArray)env->GetObjectArrayElement(array, 1); | |
294 int* sizes = env->GetIntArrayElements(sizes_array, NULL); | |
295 int number_of_segments = env->GetArrayLength(segments); | |
296 | |
297 if (number_of_segments > 1) { | |
298 int size = $REQUEST_HEADER_SIZE + 8 + (number_of_segments * 16); | |
299 *buffer = reinterpret_cast<char*>(malloc(size)); | |
300 int offset = $REQUEST_HEADER_SIZE + 8; | |
301 for (int i = 0; i < number_of_segments; i++) { | |
302 jbyteArray segment = (jbyteArray)env->GetObjectArrayElement(segments, i); | |
303 jint segment_length = sizes[i]; | |
304 char* segment_copy = ExtractByteArrayData(env, segment, segment_length); | |
305 *reinterpret_cast<void**>(*buffer + offset) = segment_copy; | |
306 *reinterpret_cast<int*>(*buffer + offset + 8) = segment_length; | |
307 offset += 16; | |
308 } | |
309 | |
310 env->ReleaseIntArrayElements(sizes_array, sizes, JNI_ABORT); | |
311 // Mark the request as being segmented. | |
312 *${pointerToArgument('*buffer', -8, 'int64_t')} = number_of_segments; | |
313 // Set the callback information. | |
314 *${pointerToArgument('*buffer', -16, 'CallbackInfo*')} = info; | |
315 return size; | |
316 } | |
317 | |
318 jbyteArray segment = (jbyteArray)env->GetObjectArrayElement(segments, 0); | |
319 jint segment_length = sizes[0]; | |
320 *buffer = ExtractByteArrayData(env, segment, segment_length); | |
321 env->ReleaseIntArrayElements(sizes_array, sizes, JNI_ABORT); | |
322 // Mark the request as being non-segmented. | |
323 *${pointerToArgument('*buffer', -8, 'int64_t')} = 0; | |
324 // Set the callback information. | |
325 *${pointerToArgument('*buffer', -16, 'CallbackInfo*')} = info; | |
326 return segment_length; | |
327 } | |
328 | |
329 static void DeleteMessage(char* message) { | |
330 int32_t segments = *${pointerToArgument('message', -8, 'int32_t')}; | |
331 for (int i = 0; i < segments; i++) { | |
332 int64_t address = *reinterpret_cast<int64_t*>(message + ${REQUEST_HEADER_SIZ
E + 8} + (i * 16)); | |
333 char* memory = reinterpret_cast<char*>(address); | |
334 free(memory); | |
335 } | |
336 free(message); | |
337 }"""; | |
338 | |
339 const int REQUEST_HEADER_SIZE = 56; | |
340 | |
341 String pointerToArgument(String buffer, int offset, String type) { | |
342 offset += REQUEST_HEADER_SIZE; | |
343 return 'reinterpret_cast<$type*>($buffer + $offset)'; | |
344 } | |
345 | |
346 const List<String> JAVA_RESOURCES = const [ | |
347 "Builder.java", | |
348 "BuilderSegment.java", | |
349 "ListBuilder.java", | |
350 "ListReader.java", | |
351 "MessageBuilder.java", | |
352 "MessageReader.java", | |
353 "Reader.java", | |
354 "Segment.java" | |
355 ]; | |
356 | |
357 void generate(String path, | |
358 Unit unit, | |
359 String resourcesDirectory, | |
360 String outputDirectory) { | |
361 _generateDartinoApis(outputDirectory); | |
362 _generateServiceJava(path, unit, outputDirectory); | |
363 _generateServiceJni(path, unit, outputDirectory); | |
364 _generateServiceJniMakeFiles(path, unit, resourcesDirectory, outputDirectory); | |
365 | |
366 resourcesDirectory = join(resourcesDirectory, 'java', 'dartino'); | |
367 String dartinoDirectory = join(outputDirectory, 'java', 'dartino'); | |
368 for (String resource in JAVA_RESOURCES) { | |
369 String resourcePath = join(resourcesDirectory, resource); | |
370 File file = new File(resourcePath); | |
371 String contents = file.readAsStringSync(); | |
372 writeToFile(dartinoDirectory, resource, contents); | |
373 } | |
374 } | |
375 | |
376 void _generateDartinoApis(String outputDirectory) { | |
377 String dartinoDirectory = join(outputDirectory, 'java', 'dartino'); | |
378 String jniDirectory = join(outputDirectory, 'java', 'jni'); | |
379 | |
380 StringBuffer buffer = new StringBuffer(HEADER); | |
381 buffer.writeln(); | |
382 buffer.write(DARTINO_API_JAVA); | |
383 writeToFile(dartinoDirectory, 'DartinoApi', buffer.toString(), | |
384 extension: 'java'); | |
385 | |
386 buffer = new StringBuffer(HEADER); | |
387 buffer.writeln(); | |
388 buffer.write(DARTINO_SERVICE_API_JAVA); | |
389 writeToFile(dartinoDirectory, 'DartinoServiceApi', buffer.toString(), | |
390 extension: 'java'); | |
391 | |
392 buffer = new StringBuffer(HEADER); | |
393 buffer.writeln(); | |
394 buffer.write(DARTINO_API_JAVA_IMPL); | |
395 writeToFile(jniDirectory, 'dartino_api_wrapper', buffer.toString(), | |
396 extension: 'cc'); | |
397 | |
398 buffer = new StringBuffer(HEADER); | |
399 buffer.writeln(); | |
400 buffer.write(DARTINO_SERVICE_API_JAVA_IMPL); | |
401 writeToFile(jniDirectory, 'dartino_service_api_wrapper', | |
402 buffer.toString(), extension: 'cc'); | |
403 } | |
404 | |
405 void _generateServiceJava(String path, Unit unit, String outputDirectory) { | |
406 _JavaVisitor visitor = new _JavaVisitor(path, outputDirectory); | |
407 visitor.visit(unit); | |
408 String contents = visitor.buffer.toString(); | |
409 String directory = join(outputDirectory, 'java', 'dartino'); | |
410 // TODO(ager): We should generate a file per service here. | |
411 if (unit.services.length > 1) { | |
412 print('Java plugin: multiple services in one file is not supported.'); | |
413 } | |
414 String serviceName = unit.services.first.name; | |
415 writeToFile(directory, serviceName, contents, extension: 'java'); | |
416 } | |
417 | |
418 void _generateServiceJni(String path, Unit unit, String outputDirectory) { | |
419 _JniVisitor visitor = new _JniVisitor(path); | |
420 visitor.visit(unit); | |
421 String contents = visitor.buffer.toString(); | |
422 String directory = join(outputDirectory, 'java', 'jni'); | |
423 // TODO(ager): We should generate a file per service here. | |
424 if (unit.services.length > 1) { | |
425 print('Java plugin: multiple services in one file is not supported.'); | |
426 } | |
427 String projectName = basenameWithoutExtension(path); | |
428 String file = '${projectName}_wrapper'; | |
429 writeToFile(directory, file, contents, extension: 'cc'); | |
430 } | |
431 | |
432 class _JavaVisitor extends CodeGenerationVisitor { | |
433 final Set<Type> neededListTypes; | |
434 final String outputDirectory; | |
435 | |
436 static Map<String, String> _GETTERS = const { | |
437 'bool' : 'segment.getBoolean', | |
438 | |
439 'uint8' : 'segment.getUnsigned', | |
440 'uint16' : 'segment.getUnsignedChar', | |
441 | |
442 'int8' : 'segment.buffer().get', | |
443 'int16' : 'segment.buffer().getShort', | |
444 'int32' : 'segment.buffer().getInt', | |
445 'int64' : 'segment.buffer().getLong', | |
446 | |
447 'float32' : 'segment.buffer().getFloat', | |
448 'float64' : 'segment.buffer().getDouble', | |
449 }; | |
450 | |
451 static Map<String, String> _SETTERS = const { | |
452 'bool' : 'segment.buffer().put', | |
453 | |
454 'uint8' : 'segment.buffer().put', | |
455 'uint16' : 'segment.buffer().putChar', | |
456 | |
457 'int8' : 'segment.buffer().put', | |
458 'int16' : 'segment.buffer().putShort', | |
459 'int32' : 'segment.buffer().putInt', | |
460 'int64' : 'segment.buffer().putLong', | |
461 | |
462 'float32' : 'segment.buffer().putFloat', | |
463 'float64' : 'segment.buffer().pubDouble', | |
464 }; | |
465 | |
466 static Map<String, String> _SETTER_TYPES = const { | |
467 'bool' : 'byte', | |
468 | |
469 'uint8' : 'byte', | |
470 'uint16' : 'char', | |
471 | |
472 'int8' : 'byte', | |
473 'int16' : 'char', | |
474 'int32' : 'int', | |
475 'int64' : 'long', | |
476 | |
477 'float32' : 'float', | |
478 'float64' : 'double', | |
479 }; | |
480 | |
481 _JavaVisitor(String path, String this.outputDirectory) | |
482 : neededListTypes = new Set<Type>(), | |
483 super(path); | |
484 | |
485 static const PRIMITIVE_TYPES = const <String, String> { | |
486 'void' : 'void', | |
487 'bool' : 'boolean', | |
488 | |
489 'uint8' : 'int', | |
490 'uint16' : 'int', | |
491 | |
492 'int8' : 'int', | |
493 'int16' : 'int', | |
494 'int32' : 'int', | |
495 'int64' : 'long', | |
496 | |
497 'float32' : 'float', | |
498 'float64' : 'double', | |
499 }; | |
500 | |
501 static const PRIMITIVE_LIST_TYPES = const <String, String> { | |
502 'bool' : 'boolean', | |
503 | |
504 'uint8' : 'int', | |
505 'uint16' : 'int', | |
506 | |
507 'int8' : 'int', | |
508 'int16' : 'int', | |
509 'int32' : 'int', | |
510 'int64' : 'long', | |
511 | |
512 'float32' : 'float', | |
513 'float64' : 'double', | |
514 }; | |
515 | |
516 String getType(Type node) { | |
517 Node resolved = node.resolved; | |
518 if (resolved != null) { | |
519 return '${node.identifier}Builder'; | |
520 } else { | |
521 String type = PRIMITIVE_TYPES[node.identifier]; | |
522 return type; | |
523 } | |
524 } | |
525 | |
526 String getReturnType(Type node) { | |
527 Node resolved = node.resolved; | |
528 if (resolved != null) { | |
529 return '${node.identifier}'; | |
530 } else { | |
531 String type = PRIMITIVE_TYPES[node.identifier]; | |
532 return type; | |
533 } | |
534 } | |
535 | |
536 String getListType(Type node) { | |
537 Node resolved = node.resolved; | |
538 if (resolved != null) { | |
539 return '${node.identifier}'; | |
540 } else { | |
541 String type = PRIMITIVE_LIST_TYPES[node.identifier]; | |
542 return type; | |
543 } | |
544 } | |
545 | |
546 void writeType(Type node) => write(getType(node)); | |
547 void writeTypeToBuffer(Type node, StringBuffer buffer) { | |
548 buffer.write(getType(node)); | |
549 } | |
550 | |
551 void writeReturnType(Type node) => write(getReturnType(node)); | |
552 void writeReturnTypeToBuffer(Type node, StringBuffer buffer) { | |
553 buffer.write(getReturnType(node)); | |
554 } | |
555 | |
556 void writeListTypeToBuffer(Type node, StringBuffer buffer) { | |
557 buffer.write(getListType(node)); | |
558 } | |
559 | |
560 visitUnit(Unit node) { | |
561 writeln(HEADER); | |
562 writeln('package dartino;'); | |
563 writeln(); | |
564 node.structs.forEach(visit); | |
565 node.services.forEach(visit); | |
566 neededListTypes.forEach(writeListReaderImplementation); | |
567 neededListTypes.forEach(writeListBuilderImplementation); | |
568 } | |
569 | |
570 visitService(Service node) { | |
571 writeln('public class ${node.name} {'); | |
572 writeln(' public static native void Setup();'); | |
573 writeln(' public static native void TearDown();'); | |
574 node.methods.forEach(visit); | |
575 writeln('}'); | |
576 } | |
577 | |
578 visitMethod(Method node) { | |
579 String name = node.name; | |
580 String camelName = name.substring(0, 1).toUpperCase() + name.substring(1); | |
581 writeln(); | |
582 writeln(' public static abstract class ${camelName}Callback {'); | |
583 if (!node.returnType.isVoid && !node.returnType.isPrimitive) { | |
584 write(' public final java.lang.Class returnType = '); | |
585 writeReturnType(node.returnType); | |
586 writeln('.class;'); | |
587 } | |
588 write(' public abstract void handle('); | |
589 if (!node.returnType.isVoid) { | |
590 writeReturnType(node.returnType); | |
591 write(' result'); | |
592 } | |
593 writeln(');'); | |
594 writeln(' }'); | |
595 | |
596 writeln(); | |
597 write(' public static native '); | |
598 writeReturnType(node.returnType); | |
599 write(' $name('); | |
600 visitArguments(node.arguments); | |
601 writeln(');'); | |
602 write(' public static native void ${name}Async('); | |
603 visitArguments(node.arguments); | |
604 if (node.arguments.isNotEmpty) write(', '); | |
605 writeln('${camelName}Callback callback);'); | |
606 } | |
607 | |
608 visitArguments(List<Formal> formals) { | |
609 visitNodes(formals, (first) => first ? '' : ', '); | |
610 } | |
611 | |
612 visitFormal(Formal node) { | |
613 writeType(node.type); | |
614 write(' ${node.name}'); | |
615 } | |
616 | |
617 visitStruct(Struct node) { | |
618 writeReader(node); | |
619 writeBuilder(node); | |
620 } | |
621 | |
622 void writeReader(Struct node) { | |
623 String dartinoDirectory = join(outputDirectory, 'java', 'dartino'); | |
624 String name = node.name; | |
625 StructLayout layout = node.layout; | |
626 | |
627 StringBuffer buffer = new StringBuffer(HEADER); | |
628 buffer.writeln(); | |
629 buffer.writeln(READER_HEADER); | |
630 | |
631 buffer.writeln('import java.util.List;'); | |
632 buffer.writeln(); | |
633 | |
634 buffer.writeln('public class $name extends Reader {'); | |
635 buffer.writeln(' public $name() { }'); | |
636 buffer.writeln(); | |
637 buffer.writeln(' public $name(byte[] memory, int offset) {'); | |
638 buffer.writeln(' super(memory, offset);'); | |
639 buffer.writeln(' }'); | |
640 buffer.writeln(); | |
641 buffer.writeln(' public $name(Segment segment, int offset) {'); | |
642 buffer.writeln(' super(segment, offset);'); | |
643 buffer.writeln(' }'); | |
644 buffer.writeln(); | |
645 buffer.writeln(' public $name(byte[][] segments, int offset) {'); | |
646 buffer.writeln(' super(segments, offset);'); | |
647 buffer.writeln(' }'); | |
648 buffer.writeln(); | |
649 buffer.writeln(' public static $name create(Object rawData) {'); | |
650 buffer.writeln(' if (rawData instanceof byte[]) {'); | |
651 buffer.writeln(' return new $name((byte[])rawData, 8);'); | |
652 buffer.writeln(' }'); | |
653 buffer.writeln(' return new $name((byte[][])rawData, 8);'); | |
654 buffer.writeln(' }'); | |
655 | |
656 for (StructSlot slot in layout.slots) { | |
657 buffer.writeln(); | |
658 Type slotType = slot.slot.type; | |
659 String camel = camelize(slot.slot.name); | |
660 | |
661 if (slot.isUnionSlot) { | |
662 String tagName = camelize(slot.union.tag.name); | |
663 int tag = slot.unionTag; | |
664 buffer.writeln( | |
665 ' public boolean is$camel() { return $tag == get$tagName(); }'); | |
666 } | |
667 | |
668 if (slotType.isList) { | |
669 neededListTypes.add(slotType); | |
670 String list = '${camelize(slotType.identifier)}List'; | |
671 buffer.writeln(' public $list get$camel() {'); | |
672 buffer.writeln(' ListReader reader = new ListReader();'); | |
673 buffer.writeln(' readList(reader, ${slot.offset});'); | |
674 buffer.writeln(' return new $list(reader);'); | |
675 buffer.writeln(' }'); | |
676 } else if (slotType.isVoid) { | |
677 // No getters for void slots. | |
678 } else if (slotType.isString) { | |
679 buffer.write(' public String get$camel() { '); | |
680 buffer.writeln('return readString(${slot.offset}); }'); | |
681 // TODO(ager): This is nasty. Maybe inject this type earler in the | |
682 // pipeline? | |
683 Type uint16ListType = new ListType(new SimpleType("uint16", false)); | |
684 uint16ListType.primitiveType = primitives.lookup("uint16"); | |
685 neededListTypes.add(uint16ListType); | |
686 buffer.writeln(' public Uint16List get${camel}Data() {'); | |
687 buffer.writeln(' ListReader reader = new ListReader();'); | |
688 buffer.writeln(' readList(reader, ${slot.offset});'); | |
689 buffer.writeln(' return new Uint16List(reader);'); | |
690 buffer.writeln(' }'); | |
691 } else if (slotType.isPrimitive) { | |
692 // TODO(ager): Dealing with unsigned numbers in Java is annoying. | |
693 if (camel == 'Tag') { | |
694 String getter = 'getUnsigned'; | |
695 String offset = 'base + ${slot.offset}'; | |
696 buffer.writeln(' public int getTag() {'); | |
697 buffer.writeln(' short shortTag = segment.$getter($offset);'); | |
698 buffer.writeln(' int tag = (int)shortTag;'); | |
699 buffer.writeln(' return tag < 0 ? -tag : tag;'); | |
700 buffer.writeln(' }'); | |
701 } else { | |
702 String getter = _GETTERS[slotType.identifier]; | |
703 String offset = "base + ${slot.offset}"; | |
704 buffer.write(' public ${getType(slotType)} get$camel() { '); | |
705 buffer.writeln('return $getter($offset); }'); | |
706 } | |
707 } else { | |
708 String returnType = getReturnType(slotType); | |
709 buffer.write(' public $returnType get$camel() {'); | |
710 if (!slotType.isPointer) { | |
711 String offset = 'base + ${slot.offset}'; | |
712 buffer.writeln(' return new $returnType(segment, $offset); }'); | |
713 } else { | |
714 buffer.writeln(); | |
715 int offset = slot.offset; | |
716 buffer.writeln(' $returnType reader = new $returnType();'); | |
717 buffer.writeln(' return ($returnType)readStruct(reader, $offset);')
; | |
718 buffer.writeln(' }'); | |
719 } | |
720 } | |
721 } | |
722 | |
723 buffer.writeln('}'); | |
724 | |
725 writeToFile(dartinoDirectory, '$name', buffer.toString(), | |
726 extension: 'java'); | |
727 } | |
728 | |
729 void writeBuilder(Struct node) { | |
730 String dartinoDirectory = join(outputDirectory, 'java', 'dartino'); | |
731 String name = '${node.name}Builder'; | |
732 StructLayout layout = node.layout; | |
733 | |
734 StringBuffer buffer = new StringBuffer(HEADER); | |
735 buffer.writeln(); | |
736 buffer.writeln(READER_HEADER); | |
737 | |
738 buffer.write('import java.util.List;'); | |
739 buffer.writeln(); | |
740 buffer.writeln('public class $name extends Builder {'); | |
741 buffer.writeln(' public static int kSize = ${layout.size};'); | |
742 buffer.writeln(' public $name(BuilderSegment segment, int offset) {'); | |
743 buffer.writeln(' super(segment, offset);'); | |
744 buffer.writeln(' }'); | |
745 buffer.writeln(); | |
746 buffer.writeln(' public $name() {'); | |
747 buffer.writeln(' super();'); | |
748 buffer.writeln(' }'); | |
749 | |
750 for (StructSlot slot in layout.slots) { | |
751 buffer.writeln(); | |
752 String slotName = slot.slot.name; | |
753 String camel = camelize(slotName); | |
754 Type slotType = slot.slot.type; | |
755 | |
756 String updateTag = ''; | |
757 if (slot.isUnionSlot) { | |
758 String tagName = camelize(slot.union.tag.name); | |
759 int tag = slot.unionTag; | |
760 updateTag = ' set$tagName($tag);\n'; | |
761 } | |
762 | |
763 if (slotType.isList) { | |
764 String listBuilder = ''; | |
765 if (slotType.isPrimitive) { | |
766 listBuilder = '${camelize(slotType.identifier)}ListBuilder'; | |
767 } else { | |
768 listBuilder = '${getListType(slotType)}ListBuilder'; | |
769 } | |
770 buffer.writeln(' public $listBuilder init$camel(int length) {'); | |
771 buffer.write(updateTag); | |
772 int size = 0; | |
773 if (slotType.isPrimitive) { | |
774 size = primitives.size(slotType.primitiveType); | |
775 } else { | |
776 Struct element = slotType.resolved; | |
777 StructLayout elementLayout = element.layout; | |
778 size = elementLayout.size; | |
779 } | |
780 buffer.writeln(' ListBuilder builder = new ListBuilder();'); | |
781 buffer.writeln(' newList(builder, ${slot.offset}, length, $size);'); | |
782 buffer.writeln(' return new ${listBuilder}(builder);'); | |
783 buffer.writeln(' }'); | |
784 } else if (slotType.isVoid) { | |
785 assert(slot.isUnionSlot); | |
786 String tagName = camelize(slot.union.tag.name); | |
787 int tag = slot.unionTag; | |
788 buffer.writeln(' public void set$camel() {' | |
789 ' set$tagName($tag); }'); | |
790 } else if (slotType.isString) { | |
791 buffer.writeln(' public void set$camel(String value) {'); | |
792 buffer.write(updateTag); | |
793 buffer.writeln(' newString(${slot.offset}, value);'); | |
794 buffer.writeln(' }'); | |
795 buffer.writeln(); | |
796 buffer.writeln(' public Uint16ListBuilder init${camel}Data(int length)
{'); | |
797 buffer.write(updateTag); | |
798 buffer.writeln(' ListBuilder builder = new ListBuilder();'); | |
799 buffer.writeln(' newList(builder, ${slot.offset}, length, 2);'); | |
800 buffer.writeln(' return new Uint16ListBuilder(builder);'); | |
801 buffer.writeln(' }'); | |
802 } else if (slotType.isPrimitive) { | |
803 String setter = _SETTERS[slotType.identifier]; | |
804 String setterType = _SETTER_TYPES[slotType.identifier]; | |
805 String offset = 'base + ${slot.offset}'; | |
806 buffer.writeln(' public void set$camel(${getType(slotType)} value) {'); | |
807 buffer.write(updateTag); | |
808 if (slotType.isBool) { | |
809 buffer.writeln(' $setter($offset,' | |
810 ' (byte)(value ? 1 : 0));'); | |
811 } else { | |
812 buffer.writeln(' $setter($offset, (${setterType})value);'); | |
813 } | |
814 buffer.writeln(' }'); | |
815 } else { | |
816 buffer.writeln(' public ${getType(slotType)} init$camel() {'); | |
817 buffer.write(updateTag); | |
818 String builderType = getType(slotType); | |
819 if (!slotType.isPointer) { | |
820 buffer.writeln(' $builderType result = new $builderType();'); | |
821 buffer.writeln(' result.segment = segment;'); | |
822 buffer.writeln(' result.base = base + ${slot.offset};'); | |
823 buffer.writeln(' return result;'); | |
824 } else { | |
825 Struct element = slotType.resolved; | |
826 StructLayout elementLayout = element.layout; | |
827 int size = elementLayout.size; | |
828 buffer.writeln(' $builderType result = new $builderType();'); | |
829 buffer.writeln(' newStruct(result, ${slot.offset}, $size);'); | |
830 buffer.writeln(' return result;'); | |
831 } | |
832 buffer.writeln(' }'); | |
833 } | |
834 } | |
835 | |
836 buffer.writeln('}'); | |
837 | |
838 writeToFile(dartinoDirectory, '$name', buffer.toString(), | |
839 extension: 'java'); | |
840 } | |
841 | |
842 void writeListReaderImplementation(Type type) { | |
843 String dartinoDirectory = join(outputDirectory, 'java', 'dartino'); | |
844 String name = '${camelize(type.identifier)}List'; | |
845 String listType = getListType(type); | |
846 | |
847 StringBuffer buffer = new StringBuffer(HEADER); | |
848 buffer.writeln(); | |
849 buffer.writeln(READER_HEADER); | |
850 | |
851 buffer.writeln('public class $name {'); | |
852 if (type.isPrimitive) { | |
853 int elementSize = primitives.size(type.primitiveType); | |
854 String offset = 'reader.base + index * $elementSize'; | |
855 | |
856 buffer.writeln(' private ListReader reader;'); | |
857 buffer.writeln(); | |
858 buffer.writeln(' public $name(ListReader reader) {' | |
859 ' this.reader = reader; }'); | |
860 buffer.writeln(); | |
861 buffer.writeln(' public $listType get(int index) {'); | |
862 buffer.write(' return '); | |
863 buffer.writeln('reader.${_GETTERS[type.identifier]}($offset);'); | |
864 buffer.writeln(' }'); | |
865 } else { | |
866 Struct element = type.resolved; | |
867 StructLayout elementLayout = element.layout;; | |
868 int elementSize = elementLayout.size; | |
869 String returnType = getReturnType(type); | |
870 | |
871 buffer.writeln(' private ListReader reader;'); | |
872 buffer.writeln(); | |
873 buffer.writeln(' public $name(ListReader reader) {' | |
874 ' this.reader = reader; }'); | |
875 buffer.writeln(); | |
876 buffer.writeln(' public $returnType get(int index) {'); | |
877 buffer.writeln(' $returnType result = new $returnType();'); | |
878 buffer.writeln(' reader.readListElement(' | |
879 'result, index, $elementSize);'); | |
880 buffer.writeln(' return result;'); | |
881 buffer.writeln(' }'); | |
882 } | |
883 | |
884 buffer.writeln(); | |
885 buffer.writeln(' public int size() { return reader.length; }'); | |
886 | |
887 buffer.writeln('}'); | |
888 | |
889 writeToFile(dartinoDirectory, '$name', buffer.toString(), | |
890 extension: 'java'); | |
891 } | |
892 | |
893 void writeListBuilderImplementation(Type type) { | |
894 String dartinoDirectory = join(outputDirectory, 'java', 'dartino'); | |
895 String name = '${camelize(type.identifier)}ListBuilder'; | |
896 | |
897 StringBuffer buffer = new StringBuffer(HEADER); | |
898 buffer.writeln(); | |
899 buffer.writeln(READER_HEADER); | |
900 | |
901 buffer.writeln('public class $name {'); | |
902 buffer.writeln(' private ListBuilder builder;'); | |
903 buffer.writeln(); | |
904 buffer.writeln(' public $name(ListBuilder builder) {' | |
905 ' this.builder = builder; }'); | |
906 buffer.writeln(); | |
907 | |
908 if (type.isPrimitive) { | |
909 int elementSize = primitives.size(type.primitiveType); | |
910 String offset = 'builder.base + index * $elementSize'; | |
911 String listType = getListType(type); | |
912 String getter = _GETTERS[type.identifier]; | |
913 | |
914 buffer.writeln(' public $listType get(int index) {'); | |
915 buffer.writeln(' return builder.$getter($offset);'); | |
916 buffer.writeln(' }'); | |
917 | |
918 buffer.writeln(); | |
919 String setter = _SETTERS[type.identifier]; | |
920 String setterType = _SETTER_TYPES[type.identifier]; | |
921 buffer.writeln(' public $listType set(int index, $listType value) {'); | |
922 buffer.write(' builder.$setter($offset, (${setterType})value);'); | |
923 buffer.writeln(' return value;'); | |
924 buffer.writeln(' }'); | |
925 } else { | |
926 Struct element = type.resolved; | |
927 StructLayout elementLayout = element.layout;; | |
928 int elementSize = elementLayout.size; | |
929 String structType = getType(type); | |
930 | |
931 buffer.writeln(' public $structType get(int index) {'); | |
932 buffer.writeln(' $structType result = new $structType();'); | |
933 buffer.writeln(' builder.readListElement(' | |
934 'result, index, $elementSize);'); | |
935 buffer.writeln(' return result;'); | |
936 buffer.writeln(' }'); | |
937 } | |
938 | |
939 buffer.writeln(); | |
940 buffer.writeln(' public int size() { return builder.length; }'); | |
941 | |
942 buffer.writeln('}'); | |
943 | |
944 writeToFile(dartinoDirectory, '$name', buffer.toString(), | |
945 extension: 'java'); | |
946 } | |
947 } | |
948 | |
949 class _JniVisitor extends CcVisitor { | |
950 static const int REQUEST_HEADER_SIZE = 48 + 8; | |
951 static const int RESPONSE_HEADER_SIZE = 8; | |
952 | |
953 int methodId = 1; | |
954 String serviceName; | |
955 | |
956 _JniVisitor(String path) : super(path); | |
957 | |
958 visitUnit(Unit node) { | |
959 writeln(HEADER); | |
960 writeln('#include <jni.h>'); | |
961 writeln('#include <stdlib.h>'); | |
962 writeln('#include <string.h>'); | |
963 writeln(); | |
964 writeln('#include "service_api.h"'); | |
965 node.services.forEach(visit); | |
966 } | |
967 | |
968 visitService(Service node) { | |
969 serviceName = node.name; | |
970 | |
971 writeln(); | |
972 | |
973 writeln('#ifdef __cplusplus'); | |
974 writeln('extern "C" {'); | |
975 writeln('#endif'); | |
976 | |
977 // TODO(ager): Get rid of this if we can. For some reason | |
978 // the jni.h header that is used by the NDK differs. | |
979 writeln(); | |
980 writeln('#ifdef ANDROID'); | |
981 writeln(' typedef JNIEnv* AttachEnvType;'); | |
982 writeln('#else'); | |
983 writeln(' typedef void* AttachEnvType;'); | |
984 writeln('#endif'); | |
985 | |
986 writeln(); | |
987 writeln('static ServiceId service_id_ = kNoServiceId;'); | |
988 | |
989 writeln(); | |
990 write('JNIEXPORT void JNICALL Java_dartino_'); | |
991 writeln('${serviceName}_Setup(JNIEnv*, jclass) {'); | |
992 writeln(' service_id_ = ServiceApiLookup("$serviceName");'); | |
993 writeln('}'); | |
994 | |
995 writeln(); | |
996 write('JNIEXPORT void JNICALL Java_dartino_'); | |
997 writeln('${serviceName}_TearDown(JNIEnv*, jclass) {'); | |
998 writeln(' ServiceApiTerminate(service_id_);'); | |
999 writeln('}'); | |
1000 | |
1001 // TODO(ager): Put this in resources and copy as a file instead. | |
1002 writeln(); | |
1003 writeln(JNI_UTILS); | |
1004 | |
1005 node.methods.forEach(visit); | |
1006 | |
1007 writeln(); | |
1008 writeln('#ifdef __cplusplus'); | |
1009 writeln('}'); | |
1010 writeln('#endif'); | |
1011 } | |
1012 | |
1013 visitMethod(Method node) { | |
1014 String name = node.name; | |
1015 String id = '_k${name}Id'; | |
1016 | |
1017 writeln(); | |
1018 write('static const MethodId $id = '); | |
1019 writeln('reinterpret_cast<MethodId>(${methodId++});'); | |
1020 | |
1021 writeln(); | |
1022 write('JNIEXPORT '); | |
1023 writeReturnType(node.returnType); | |
1024 write(' JNICALL Java_dartino_${serviceName}_${name}('); | |
1025 write('JNIEnv* _env, jclass'); | |
1026 if (node.arguments.isNotEmpty) write(', '); | |
1027 if (node.inputKind != InputKind.PRIMITIVES) { | |
1028 write('jobject ${node.arguments.single.name}'); | |
1029 } else { | |
1030 visitArguments(node.arguments); | |
1031 } | |
1032 writeln(') {'); | |
1033 if (node.inputKind != InputKind.PRIMITIVES) { | |
1034 visitStructArgumentMethodBody(id, node); | |
1035 } else { | |
1036 visitMethodBody(id, node); | |
1037 } | |
1038 writeln('}'); | |
1039 | |
1040 String callback; | |
1041 if (node.inputKind == InputKind.STRUCT) { | |
1042 Struct struct = node.arguments.single.type.resolved; | |
1043 StructLayout layout = struct.layout; | |
1044 callback = ensureCallback(node.returnType, layout); | |
1045 } else { | |
1046 callback = | |
1047 ensureCallback(node.returnType, node.inputPrimitiveStructLayout); | |
1048 } | |
1049 | |
1050 writeln(); | |
1051 write('JNIEXPORT void JNICALL '); | |
1052 write('Java_dartino_${serviceName}_${name}Async('); | |
1053 write('JNIEnv* _env, jclass'); | |
1054 if (node.arguments.isNotEmpty) write(', '); | |
1055 if (node.inputKind != InputKind.PRIMITIVES) { | |
1056 write('jobject ${node.arguments.single.name}'); | |
1057 } else { | |
1058 visitArguments(node.arguments); | |
1059 } | |
1060 writeln(', jobject _callback) {'); | |
1061 writeln(' jobject callback = NULL;'); | |
1062 writeln(' JavaVM* vm = NULL;'); | |
1063 writeln(' if (_callback) {'); | |
1064 writeln(' callback = _env->NewGlobalRef(_callback);'); | |
1065 writeln(' _env->GetJavaVM(&vm);'); | |
1066 writeln(' }'); | |
1067 // TODO(zerny): Issue #45. Store VM pointer in 'callback data' header field. | |
1068 if (node.inputKind != InputKind.PRIMITIVES) { | |
1069 visitStructArgumentMethodBody(id, | |
1070 node, | |
1071 extraArguments: [ 'vm' ], | |
1072 callback: callback); | |
1073 } else { | |
1074 visitMethodBody(id, | |
1075 node, | |
1076 extraArguments: [ 'vm' ], | |
1077 callback: callback); | |
1078 } | |
1079 writeln('}'); | |
1080 } | |
1081 | |
1082 visitMethodBody(String id, | |
1083 Method method, | |
1084 {bool cStyle: false, | |
1085 List<String> extraArguments: const [], | |
1086 String callback}) { | |
1087 String cast(String type) => CcVisitor.cast(type, false); | |
1088 | |
1089 String pointerToArgument(int offset, int pointers, String type) { | |
1090 offset += REQUEST_HEADER_SIZE; | |
1091 String prefix = cast('$type*'); | |
1092 if (pointers == 0) return '$prefix(_buffer + $offset)'; | |
1093 return '$prefix(_buffer + $offset + $pointers * sizeof(void*))'; | |
1094 } | |
1095 | |
1096 List<Formal> arguments = method.arguments; | |
1097 assert(method.inputKind == InputKind.PRIMITIVES); | |
1098 StructLayout layout = method.inputPrimitiveStructLayout; | |
1099 final bool async = callback != null; | |
1100 int size = REQUEST_HEADER_SIZE + layout.size; | |
1101 if (async) { | |
1102 write(' static const int kSize = '); | |
1103 writeln('${size} + ${extraArguments.length} * sizeof(void*);'); | |
1104 } else { | |
1105 writeln(' static const int kSize = ${size};'); | |
1106 } | |
1107 | |
1108 if (async) { | |
1109 writeln(' char* _buffer = ${cast("char*")}(malloc(kSize));'); | |
1110 } else { | |
1111 writeln(' char _bits[kSize];'); | |
1112 writeln(' char* _buffer = _bits;'); | |
1113 } | |
1114 | |
1115 // Mark the message as being non-segmented. | |
1116 writeln(' *${pointerToArgument(-8, 0, "int64_t")} = 0;'); | |
1117 | |
1118 int arity = arguments.length; | |
1119 for (int i = 0; i < arity; i++) { | |
1120 String name = arguments[i].name; | |
1121 int offset = layout[arguments[i]].offset; | |
1122 String type = PRIMITIVE_TYPES[arguments[i].type.identifier]; | |
1123 writeln(' *${pointerToArgument(offset, 0, type)} = $name;'); | |
1124 } | |
1125 | |
1126 if (async) { | |
1127 writeln(' CallbackInfo* info = callback ? new CallbackInfo(callback, vm)
: NULL;'); | |
1128 writeln(' *${pointerToArgument(-16, 0, "CallbackInfo*")} = info;'); | |
1129 write(' ServiceApiInvokeAsync(service_id_, $id, $callback, '); | |
1130 writeln('_buffer, kSize);'); | |
1131 } else { | |
1132 writeln(' ServiceApiInvoke(service_id_, $id, _buffer, kSize);'); | |
1133 if (method.outputKind == OutputKind.STRUCT) { | |
1134 Type type = method.returnType; | |
1135 writeln(' int64_t result = *${pointerToArgument(0, 0, 'int64_t')};'); | |
1136 writeln(' char* memory = reinterpret_cast<char*>(result);'); | |
1137 writeln(' jobject rootSegment = GetRootSegment(_env, memory);'); | |
1138 writeln(' jclass resultClass = ' | |
1139 '_env->FindClass("dartino/${type.identifier}");'); | |
1140 writeln(' jmethodID create = _env->GetStaticMethodID(' | |
1141 'resultClass, "create", ' | |
1142 '"(Ljava/lang/Object;)Ldartino/${type.identifier};");'); | |
1143 writeln(' jobject resultObject = _env->CallStaticObjectMethod(' | |
1144 'resultClass, create, rootSegment);'); | |
1145 writeln(' return resultObject;'); | |
1146 } else if (!method.returnType.isVoid) { | |
1147 writeln(' return *${pointerToArgument(0, 0, 'int64_t')};'); | |
1148 } | |
1149 } | |
1150 } | |
1151 | |
1152 visitStructArgumentMethodBody(String id, | |
1153 Method method, | |
1154 {bool cStyle: false, | |
1155 List<String> extraArguments: const [], | |
1156 String callback}) { | |
1157 String cast(String type) => CcVisitor.cast(type, false); | |
1158 | |
1159 String pointerToArgument(int offset, int pointers, String type) { | |
1160 offset += REQUEST_HEADER_SIZE; | |
1161 String prefix = cast('$type*'); | |
1162 if (pointers == 0) return '$prefix(buffer + $offset)'; | |
1163 return '$prefix(buffer + $offset + $pointers * sizeof(void*))'; | |
1164 } | |
1165 | |
1166 String argumentName = method.arguments.single.name; | |
1167 | |
1168 bool async = callback != null; | |
1169 String javaCallback = async ? 'callback' : 'NULL'; | |
1170 String javaVM = async ? 'vm' : 'NULL'; | |
1171 | |
1172 writeln(' char* buffer = NULL;'); | |
1173 writeln(' int size = ComputeMessage(' | |
1174 '_env, $argumentName, $javaCallback, $javaVM, &buffer);'); | |
1175 | |
1176 if (async) { | |
1177 write(' ServiceApiInvokeAsync(service_id_, $id, $callback, '); | |
1178 writeln('buffer, size);'); | |
1179 } else { | |
1180 writeln(' ServiceApiInvoke(service_id_, $id, buffer, size);'); | |
1181 writeln(' int64_t result = *${pointerToArgument(0, 0, 'int64_t')};'); | |
1182 writeln(' DeleteMessage(buffer);'); | |
1183 if (method.outputKind == OutputKind.STRUCT) { | |
1184 Type type = method.returnType; | |
1185 writeln(' char* memory = reinterpret_cast<char*>(result);'); | |
1186 writeln(' jobject rootSegment = GetRootSegment(_env, memory);'); | |
1187 writeln(' jclass resultClass = ' | |
1188 '_env->FindClass("dartino/${type.identifier}");'); | |
1189 writeln(' jmethodID create = _env->GetStaticMethodID(' | |
1190 'resultClass, "create", ' | |
1191 '"(Ljava/lang/Object;)Ldartino/${type.identifier};");'); | |
1192 writeln(' jobject resultObject = _env->CallStaticObjectMethod(' | |
1193 'resultClass, create, rootSegment);'); | |
1194 writeln(' return resultObject;'); | |
1195 } else { | |
1196 if (!method.returnType.isVoid) writeln(' return result;'); | |
1197 } | |
1198 } | |
1199 } | |
1200 | |
1201 static const Map<String, String> PRIMITIVE_TYPES = const { | |
1202 'void' : 'void', | |
1203 'bool' : 'jboolean', | |
1204 | |
1205 'uint8' : 'jboolean', | |
1206 'uint16' : 'jchar', | |
1207 | |
1208 'int8' : 'jbyte', | |
1209 'int16' : 'jshort', | |
1210 'int32' : 'jint', | |
1211 'int64' : 'jlong', | |
1212 | |
1213 'float32' : 'jfloat', | |
1214 'float64' : 'jdouble', | |
1215 }; | |
1216 | |
1217 void writeType(Type node) { | |
1218 String type = PRIMITIVE_TYPES[node.identifier]; | |
1219 write(type); | |
1220 } | |
1221 | |
1222 void writeReturnType(Type node) { | |
1223 Node resolved = node.resolved; | |
1224 if (resolved != null) { | |
1225 write('jobject'); | |
1226 } else { | |
1227 String type = PRIMITIVE_TYPES[node.identifier]; | |
1228 write(type); | |
1229 } | |
1230 } | |
1231 | |
1232 static const Map<String, String> PRIMITIVE_JNI_SIG = const { | |
1233 'void' : '', | |
1234 'bool' : 'Z', | |
1235 | |
1236 'uint8' : 'Z', | |
1237 'uint16' : 'C', | |
1238 | |
1239 'int8' : 'B', | |
1240 'int16' : 'S', | |
1241 'int32' : 'I', | |
1242 'int64' : 'J', | |
1243 | |
1244 'float32' : 'F', | |
1245 'float64' : 'D', | |
1246 }; | |
1247 | |
1248 String getJNISignatureType(Type type) { | |
1249 String name = type.identifier; | |
1250 if (type.isPrimitive) return PRIMITIVE_JNI_SIG[name]; | |
1251 return 'Ldartino/$name;'; | |
1252 } | |
1253 | |
1254 final Map<String, String> callbacks = {}; | |
1255 String ensureCallback(Type type, | |
1256 StructLayout layout, | |
1257 {bool cStyle: false}) { | |
1258 String key = '${type.identifier}_${layout.size}'; | |
1259 return callbacks.putIfAbsent(key, () { | |
1260 String cast(String type) => CcVisitor.cast(type, cStyle); | |
1261 String pointerToArgument(int offset, String type) { | |
1262 offset += REQUEST_HEADER_SIZE; | |
1263 String prefix = cast('$type*'); | |
1264 return '$prefix(buffer + $offset)'; | |
1265 } | |
1266 String name = 'Unwrap_$key'; | |
1267 writeln(); | |
1268 writeln('static void $name(void* raw) {'); | |
1269 writeln(' char* buffer = ${cast('char*')}(raw);'); | |
1270 write(' CallbackInfo* info ='); | |
1271 writeln(' *${pointerToArgument(-16, "CallbackInfo*")};'); | |
1272 writeln(' if (info == NULL) return;'); | |
1273 writeln(' JNIEnv* env = AttachCurrentThreadAndGetEnv(info->vm);'); | |
1274 writeln(' jclass clazz = env->GetObjectClass(info->callback);'); | |
1275 if (!type.isVoid) { | |
1276 write(' int64_t result ='); | |
1277 writeln(' *${pointerToArgument(0, 'int64_t')};'); | |
1278 writeln(' DeleteMessage(buffer);'); | |
1279 if (!type.isPrimitive) { | |
1280 writeln(' char* memory = reinterpret_cast<char*>(result);'); | |
1281 writeln(' jobject rootSegment = GetRootSegment(env, memory);'); | |
1282 write(' jfieldID returnTypeField = env->GetFieldID('); | |
1283 writeln('clazz, "returnType", "Ljava/lang/Class;");'); | |
1284 write(' jclass resultClass = (jclass)'); | |
1285 writeln('env->GetObjectField(info->callback, returnTypeField);'); | |
1286 writeln(' jmethodID create = env->GetStaticMethodID(' | |
1287 'resultClass, "create", ' | |
1288 '"(Ljava/lang/Object;)Ldartino/${type.identifier};");'); | |
1289 writeln(' jobject resultObject = env->CallStaticObjectMethod(' | |
1290 'resultClass, create, rootSegment);'); | |
1291 } | |
1292 } else { | |
1293 writeln(' DeleteMessage(buffer);'); | |
1294 } | |
1295 write(' jmethodID methodId = env->GetMethodID'); | |
1296 write('(clazz, "handle", '); | |
1297 if (type.isVoid) { | |
1298 writeln('"()V");'); | |
1299 writeln(' env->CallVoidMethod(info->callback, methodId);'); | |
1300 } else { | |
1301 String signatureType = getJNISignatureType(type); | |
1302 writeln('"($signatureType)V");'); | |
1303 write(' env->CallVoidMethod(info->callback, methodId,'); | |
1304 if (!type.isPrimitive) { | |
1305 writeln(' resultObject);'); | |
1306 } else { | |
1307 writeln(' result);'); | |
1308 } | |
1309 } | |
1310 writeln(' env->DeleteGlobalRef(info->callback);'); | |
1311 writeln(' DetachCurrentThread(info->vm);'); | |
1312 writeln(' delete info;'); | |
1313 writeln('}'); | |
1314 return name; | |
1315 }); | |
1316 } | |
1317 } | |
1318 | |
1319 void _generateServiceJniMakeFiles(String path, | |
1320 Unit unit, | |
1321 String resourcesDirectory, | |
1322 String outputDirectory) { | |
1323 String out = join(outputDirectory, 'java'); | |
1324 // TODO(stanm): pass dartino root directly | |
1325 String dartinoRoot = join(resourcesDirectory, '..', '..', '..', '..', '..'); | |
1326 String dartinoLibraryBuildDir = join(dartinoRoot, | |
1327 'tools', | |
1328 'android_build', | |
1329 'jni'); | |
1330 | |
1331 String dartinoIncludeDir = join(dartinoRoot, 'include'); | |
1332 | |
1333 String modulePath = relative(dartinoLibraryBuildDir, from: out); | |
1334 String includePath = relative(dartinoIncludeDir, from: out); | |
1335 | |
1336 StringBuffer buffer = new StringBuffer(HEADER_MK); | |
1337 | |
1338 buffer.writeln(); | |
1339 buffer.writeln('LOCAL_PATH := \$(call my-dir)'); | |
1340 | |
1341 buffer.writeln(); | |
1342 buffer.writeln('include \$(CLEAR_VARS)'); | |
1343 buffer.writeln('LOCAL_MODULE := dartino'); | |
1344 buffer.writeln('LOCAL_CFLAGS := -DDARTINO32'); | |
1345 buffer.writeln('LOCAL_LDLIBS := -llog -ldl -rdynamic'); | |
1346 | |
1347 buffer.writeln(); | |
1348 buffer.writeln('LOCAL_SRC_FILES := \\'); | |
1349 buffer.writeln('\tdartino_api_wrapper.cc \\'); | |
1350 buffer.writeln('\tdartino_service_api_wrapper.cc \\'); | |
1351 | |
1352 if (unit.services.length > 1) { | |
1353 print('Java plugin: multiple services in one file is not supported.'); | |
1354 } | |
1355 String projectName = basenameWithoutExtension(path); | |
1356 String file = '${projectName}_wrapper'; | |
1357 | |
1358 buffer.writeln('\t${file}.cc'); | |
1359 | |
1360 buffer.writeln(); | |
1361 buffer.writeln('LOCAL_C_INCLUDES += \$(LOCAL_PATH)'); | |
1362 buffer.writeln('LOCAL_C_INCLUDES += ${includePath}'); | |
1363 buffer.writeln('LOCAL_STATIC_LIBRARIES := dartino-library'); | |
1364 | |
1365 buffer.writeln(); | |
1366 buffer.writeln('include \$(BUILD_SHARED_LIBRARY)'); | |
1367 | |
1368 buffer.writeln(); | |
1369 buffer.writeln('\$(call import-module, ${modulePath})'); | |
1370 | |
1371 writeToFile(join(out, 'jni'), 'Android', buffer.toString(), | |
1372 extension: 'mk'); | |
1373 | |
1374 buffer = new StringBuffer(HEADER_MK); | |
1375 buffer.writeln('APP_STL := gnustl_static'); | |
1376 buffer.writeln('APP_ABI := x86 armeabi-v7a'); | |
1377 // TODO(zerny): Is this the right place and way to ensure ABI >= 8? | |
1378 buffer.writeln('APP_PLATFORM := android-8'); | |
1379 writeToFile(join(out, 'jni'), 'Application', buffer.toString(), | |
1380 extension: 'mk'); | |
1381 } | |
OLD | NEW |