Skip to content

Commit 551bf74

Browse files
authored
Initial work adding a JavaScript backend to ParaparVM (#4628)
The first step of #4643
1 parent 46958db commit 551bf74

51 files changed

Lines changed: 6539 additions & 33 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeClass.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,13 @@ public void setIsAnnotation(boolean isAnnotation) {
9393
private boolean finalClass;
9494
private boolean isEnum;
9595
private static Set<String> writableFields = new HashSet<String>();
96+
97+
static void cleanup() {
98+
arrayTypes.clear();
99+
writableFields.clear();
100+
mainClass = null;
101+
saveUnitTests = false;
102+
}
96103

97104
/**
98105
*
@@ -170,6 +177,10 @@ public void addField(ByteCodeField m) {
170177
public String generateCSharpCode() {
171178
return "";
172179
}
180+
181+
public String generateJavascriptCode(List<ByteCodeClass> allClasses) {
182+
return JavascriptMethodGenerator.generateClassJavascript(this, allClasses);
183+
}
173184

174185
public void addWritableField(String field) {
175186
writableFields.add(field);

vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeTranslator.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,13 @@ public String extension() {
6464
return "c";
6565
}
6666

67+
},
68+
OUTPUT_TYPE_JAVASCRIPT {
69+
@Override
70+
public String extension() {
71+
return "js";
72+
}
73+
6774
};
6875

6976
public abstract String extension();
@@ -144,7 +151,7 @@ public static void main(String[] args) throws Exception {
144151
}
145152

146153
if(args.length != 9) {
147-
System.out.println("We accept 9 arguments output type (ios, csharp, clean), input directory, output directory, app name, package name, app dispaly name, version, type (ios/iphone/ipad) and additional frameworks");
154+
System.out.println("We accept 9 arguments output type (ios, csharp, clean, javascript), input directory, output directory, app name, package name, app dispaly name, version, type (ios/iphone/ipad) and additional frameworks");
148155
System.exit(1);
149156
return;
150157
}
@@ -159,10 +166,14 @@ public static void main(String[] args) throws Exception {
159166
System.out.println("Generating Unit Tests");
160167
ByteCodeClass.setSaveUnitTests(true);
161168
}
162-
if(args[0].equalsIgnoreCase("csharp")) {
169+
if(args[0].equalsIgnoreCase("ios")) {
170+
output = OutputType.OUTPUT_TYPE_IOS;
171+
} else if(args[0].equalsIgnoreCase("csharp")) {
163172
output = OutputType.OUTPUT_TYPE_CSHARP;
164173
} else if(args[0].equalsIgnoreCase("clean")) {
165174
output = OutputType.OUTPUT_TYPE_CLEAN;
175+
} else if(args[0].equalsIgnoreCase("javascript")) {
176+
output = OutputType.OUTPUT_TYPE_JAVASCRIPT;
166177
}
167178
String[] sourceDirectories = args[1].split(";");
168179
File[] sources = new File[sourceDirectories.length];
@@ -189,6 +200,9 @@ public static void main(String[] args) throws Exception {
189200
case OUTPUT_TYPE_CLEAN:
190201
handleCleanOutput(b, sources, dest, appName);
191202
break;
203+
case OUTPUT_TYPE_JAVASCRIPT:
204+
handleJavascriptOutput(b, sources, dest, appName);
205+
break;
192206
default:
193207
handleDefaultOutput(b, sources, dest);
194208
}
@@ -250,6 +264,18 @@ private static void handleCleanOutput(ByteCodeTranslator b, File[] sources, File
250264
writeCmakeProject(root, srcRoot, appName);
251265
}
252266

267+
private static void handleJavascriptOutput(ByteCodeTranslator b, File[] sources, File dest, String appName) throws Exception {
268+
File root = new File(dest, "dist");
269+
root.mkdirs();
270+
if(verbose) {
271+
System.out.println("Root is: " + root.getAbsolutePath());
272+
}
273+
File srcRoot = new File(root, appName + "-js");
274+
srcRoot.mkdirs();
275+
b.execute(sources, srcRoot);
276+
Parser.writeOutput(srcRoot);
277+
}
278+
253279
private static void handleIosOutput(ByteCodeTranslator b, File[] sources, File dest, String appName, String appPackageName, String appDisplayName, String appVersion, String appType, String addFrameworks) throws Exception {
254280
File root = new File(dest, "dist");
255281
root.mkdirs();

vm/ByteCodeTranslator/src/com/codename1/tools/translator/BytecodeMethod.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,44 @@ public void addToConstantPool() {
747747
public boolean isSynchronizedMethod() {
748748
return synchronizedMethod;
749749
}
750+
751+
public List<Instruction> getInstructions() {
752+
return instructions;
753+
}
754+
755+
public List<ByteCodeMethodArg> getArguments() {
756+
return arguments;
757+
}
758+
759+
public ByteCodeMethodArg getReturnType() {
760+
return returnType;
761+
}
762+
763+
public int getMaxStack() {
764+
return maxStack;
765+
}
766+
767+
public int getMaxLocals() {
768+
return maxLocals;
769+
}
770+
771+
public boolean isConstructor() {
772+
return constructor;
773+
}
774+
775+
public String getMethodIdentifier() {
776+
StringBuilder b = new StringBuilder();
777+
b.append(clsName).append("_");
778+
if(methodName.equals("<init>")) {
779+
b.append("__INIT__");
780+
} else if(methodName.equals("<clinit>")) {
781+
b.append("__CLINIT__");
782+
} else {
783+
b.append(getCMethodName());
784+
}
785+
appendMethodSignatureSuffixFromDesc(desc, b, new ArrayList<String>());
786+
return b.toString();
787+
}
750788

751789
private boolean hasLocalVariableWithIndex(char qualifier, int index) {
752790
for (LocalVariable lv : localVariables) {
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package com.codename1.tools.translator;
2+
3+
import java.io.File;
4+
import java.io.IOException;
5+
import java.io.InputStream;
6+
import java.nio.charset.StandardCharsets;
7+
import java.nio.file.Files;
8+
import java.util.ArrayList;
9+
import java.util.Collections;
10+
import java.util.Comparator;
11+
import java.util.List;
12+
13+
final class JavascriptBundleWriter {
14+
private static final String RESOURCE_ROOT = "/javascript/";
15+
16+
private JavascriptBundleWriter() {
17+
}
18+
19+
static void write(File outputDirectory, List<ByteCodeClass> classes) throws IOException {
20+
writeRuntime(outputDirectory);
21+
writeTranslatedClasses(outputDirectory, classes);
22+
writeWorker(outputDirectory);
23+
writeIndex(outputDirectory);
24+
writeProtocol(outputDirectory);
25+
}
26+
27+
private static void writeRuntime(File outputDirectory) throws IOException {
28+
writeResource(outputDirectory, "parparvm_runtime.js", "parparvm_runtime.js");
29+
}
30+
31+
private static void writeTranslatedClasses(File outputDirectory, List<ByteCodeClass> classes) throws IOException {
32+
StringBuilder out = new StringBuilder();
33+
List<ByteCodeClass> sorted = new ArrayList<ByteCodeClass>(classes);
34+
Collections.sort(sorted, new Comparator<ByteCodeClass>() {
35+
@Override
36+
public int compare(ByteCodeClass a, ByteCodeClass b) {
37+
int priorityDiff = bootstrapPriority(a) - bootstrapPriority(b);
38+
if (priorityDiff != 0) {
39+
return priorityDiff;
40+
}
41+
return a.getClsName().compareTo(b.getClsName());
42+
}
43+
});
44+
for (ByteCodeClass cls : sorted) {
45+
out.append(cls.generateJavascriptCode(classes)).append('\n');
46+
}
47+
ByteCodeClass mainClass = ByteCodeClass.getMainClass();
48+
if (mainClass != null) {
49+
out.append("jvm.setMain(\"").append(mainClass.getClsName()).append("\", \"")
50+
.append(JavascriptNameUtil.methodIdentifier(mainClass.getClsName(), "main", "([Ljava/lang/String;)V"))
51+
.append("\");\n");
52+
}
53+
Files.write(new File(outputDirectory, "translated_app.js").toPath(),
54+
out.toString().getBytes(StandardCharsets.UTF_8));
55+
}
56+
57+
private static int bootstrapPriority(ByteCodeClass cls) {
58+
String name = cls.getClsName();
59+
if ("java_lang_Object".equals(name)) {
60+
return 0;
61+
}
62+
if ("java_lang_Class".equals(name)) {
63+
return 1;
64+
}
65+
if ("java_lang_String".equals(name)) {
66+
return 2;
67+
}
68+
if ("java_lang_Throwable".equals(name)) {
69+
return 3;
70+
}
71+
if (name.startsWith("java_lang_String_")) {
72+
return 4;
73+
}
74+
if (name.startsWith("java_lang_")) {
75+
return 5;
76+
}
77+
return 10;
78+
}
79+
80+
private static void writeWorker(File outputDirectory) throws IOException {
81+
List<String> nativeScripts = new ArrayList<String>();
82+
File[] files = outputDirectory.listFiles();
83+
if (files != null) {
84+
for (File file : files) {
85+
String name = file.getName();
86+
if (!name.endsWith(".js")) {
87+
continue;
88+
}
89+
if ("parparvm_runtime.js".equals(name) || "translated_app.js".equals(name) || "worker.js".equals(name)) {
90+
continue;
91+
}
92+
nativeScripts.add(name);
93+
}
94+
}
95+
96+
StringBuilder imports = new StringBuilder();
97+
imports.append("importScripts('parparvm_runtime.js');\n");
98+
for (String script : nativeScripts) {
99+
imports.append("importScripts('").append(script).append("');\n");
100+
}
101+
imports.append("importScripts('translated_app.js');\n");
102+
103+
String worker = loadResource("worker.js").replace("/*__IMPORTS__*/", imports.toString().trim());
104+
Files.write(new File(outputDirectory, "worker.js").toPath(), worker.getBytes(StandardCharsets.UTF_8));
105+
}
106+
107+
private static void writeIndex(File outputDirectory) throws IOException {
108+
writeResource(outputDirectory, "index.html", "index.html");
109+
}
110+
111+
private static void writeProtocol(File outputDirectory) throws IOException {
112+
writeResource(outputDirectory, "vm_protocol.md", "vm_protocol.md");
113+
}
114+
115+
private static void writeResource(File outputDirectory, String targetName, String resourceName) throws IOException {
116+
Files.write(new File(outputDirectory, targetName).toPath(),
117+
loadResource(resourceName).getBytes(StandardCharsets.UTF_8));
118+
}
119+
120+
private static String loadResource(String resourceName) throws IOException {
121+
InputStream input = JavascriptBundleWriter.class.getResourceAsStream(RESOURCE_ROOT + resourceName);
122+
if (input == null) {
123+
throw new IOException("Missing javascript backend resource " + resourceName);
124+
}
125+
try {
126+
byte[] data = new byte[8192];
127+
StringBuilder out = new StringBuilder();
128+
int len;
129+
while ((len = input.read(data)) > -1) {
130+
out.append(new String(data, 0, len, StandardCharsets.UTF_8));
131+
}
132+
return out.toString();
133+
} finally {
134+
input.close();
135+
}
136+
}
137+
}

0 commit comments

Comments
 (0)