Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,29 @@ private void uncancelScroll() {
private static native boolean supportsSrcdocAttribute();
private boolean supportsSrcdocAttribute;

@JSBody(params={"iframe"}, script="try{if(iframe.contentWindow.document){return false} else {return true}}catch(e){return true}")
private native static boolean isCORSRestricted(HTMLIFrameElement iframe);
// NOTE: this must NOT be an @JSBody. On the ParparVM worker model an
// @JSBody script runs in the worker, where the ``iframe`` argument is a
// host-ref proxy with no live DOM -- ``iframe.contentWindow`` is undefined,
// so the old inline probe always threw and reported EVERY BrowserComponent
// (even a same-origin one like the Playground editor) as CORS-restricted,
// which made execute()/executeAndReturnString() throw and the editor never
// bootstrapped. Probe through the JSO bridge instead so contentWindow /
// document access runs on the MAIN thread where it is meaningful: a
// same-origin iframe yields a non-null document; a genuinely cross-origin
// one throws on access (caught here) and is correctly reported restricted.
// (TeaVM ran everything on the main thread, so its @JSBody worked -- this
// only bit the worker-based ParparVM port.)
private static boolean isCORSRestricted(HTMLIFrameElement iframe) {
try {
Window cw = iframe.getContentWindow();
if (cw == null) {
return true;
}
return cw.getDocument() == null;
} catch (Throwable t) {
return true;
}
}

private boolean listenersInstalled;
private List<EventListener> frameListeners;
Expand Down
18 changes: 15 additions & 3 deletions Ports/JavaScriptPort/src/main/webapp/js/localforage-shim.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,18 @@
if (typeof window.createConfigOptions !== "function") {
window.createConfigOptions = function() { return {}; };
}
// If a real ``localforage`` library is loaded ahead of us, leave it
// alone. Otherwise install a localStorage-backed shim.
if (window.localforage && typeof window.localforage.setItem === "function") {
// This synchronous-callback shim MUST own ``window.localforage`` on the
// ParparVM port. The CN1 worker can't pump the async microtask/Promise loop
// a real localForage relies on, so its callbacks must fire inline (see the
// setItem note below). The catch: ``fontmetrics.js`` bundles a real
// localForage 1.7.3 and globally exposes it, and it loads BEFORE us -- left
// in place, CN1 Storage hits its Promise-based callback path and the
// worker-bridged callback blows up with "b is not a function". So install
// unconditionally, overriding any real localForage on the window. The only
// thing we skip is re-installing over OURSELVES (idempotent). fontmetrics
// keeps its own bundled instance for internal font-metric caching; it
// references that through its module closure, not ``window.localforage``.
if (window.localforage && window.localforage.__cn1ShimInstalled) {
return;
}
var STORE_PREFIX = "cn1lf:";
Expand Down Expand Up @@ -122,6 +131,9 @@
// callback fired. The empty-result case looked indistinguishable from
// a non-existent key.)
window.localforage = {
// Marker so a second load of this shim (or a re-check) doesn't reinstall
// over itself, while still letting us override a real localForage.
__cn1ShimInstalled: true,
INDEXEDDB: "indexeddb",
WEBSQL: "websql",
LOCALSTORAGE: "localstorage",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -720,20 +720,31 @@ private boolean runByteCodeTranslator(File compilerJar, File stageClasses, File
log("Running ByteCodeTranslator (javascript target) for " + mainClass);
java.util.List<String> cmd = new java.util.ArrayList<String>();
cmd.add("java");
cmd.add("-Xmx512m");
// Pass through extra translator JVM options (e.g. -Dparparvm.js.*
// size/diagnostic knobs and kill switches) from the
// size/diagnostic knobs and kill switches, or a larger -Xmx) from the
// CN1_TRANSLATOR_OPTS environment variable. The forked JVM does
// not inherit the Maven process's -D properties, so this is the
// only way to reach the translator for bisection / tuning.
String translatorOpts = System.getenv("CN1_TRANSLATOR_OPTS");
boolean heapOverridden = false;
java.util.List<String> extraOpts = new java.util.ArrayList<String>();
if (translatorOpts != null && !translatorOpts.trim().isEmpty()) {
for (String opt : translatorOpts.trim().split("\\s+")) {
if (!opt.isEmpty()) {
cmd.add(opt);
extraOpts.add(opt);
if (opt.startsWith("-Xmx")) {
heapOverridden = true;
}
}
}
}
// Default heap; a -Xmx in CN1_TRANSLATOR_OPTS takes precedence (apps
// that disable tree-shaking, e.g. the Playground, emit a much larger
// bundle and need a bigger heap to avoid OutOfMemoryError mid-emit).
if (!heapOverridden) {
cmd.add("-Xmx512m");
}
cmd.addAll(extraOpts);
cmd.add("-cp");
cmd.add(compilerJar.getAbsolutePath());
cmd.add("com.codename1.tools.translator.ByteCodeTranslator");
Expand Down Expand Up @@ -812,6 +823,55 @@ private void mergeTranslatorRootResources(File translatorOut, File distDir) thro
}
}
}
// BrowserComponent.setURLHierarchy(path) loads the app's bundled HTML
// hierarchy from ``assets/cn1html/<path>`` (HTML5Implementation
// .setBrowserPageInHierarchy). Those files ship packed in html.tar; the
// CN1 runtime only ever unpacks it into the in-app FileSystemStorage
// (installTar), which the browser can't fetch over HTTP for an iframe
// src. Unpack html.tar into assets/cn1html/ so the iframe URL resolves
// to a real served file (same-origin) -- without this the editor iframe
// 404s. (Leave html.tar at the root too; installTar still reads it.)
File htmlTar = new File(distDir, "html.tar");
if (htmlTar.isFile()) {
extractHtmlHierarchy(htmlTar, new File(assetsDir, "cn1html"));
}
}

/** Unpack html.tar into {@code destDir} (used to HTTP-serve setURLHierarchy content). */
private void extractHtmlHierarchy(File htmlTar, File destDir) throws IOException {
destDir.mkdirs();
org.xeustechnologies.jtar.TarInputStream tis =
new org.xeustechnologies.jtar.TarInputStream(new java.io.BufferedInputStream(new FileInputStream(htmlTar)));
try {
org.xeustechnologies.jtar.TarEntry entry;
byte[] buf = new byte[8192];
while ((entry = tis.getNextEntry()) != null) {
String name = entry.getName();
if (name == null || name.length() == 0 || name.contains("..")) {
continue;
}
File out = new File(destDir, name);
if (entry.isDirectory()) {
out.mkdirs();
continue;
}
File parent = out.getParentFile();
if (parent != null) {
parent.mkdirs();
}
FileOutputStream fos = new FileOutputStream(out);
try {
int n;
while ((n = tis.read(buf)) != -1) {
fos.write(buf, 0, n);
}
} finally {
fos.close();
}
}
} finally {
tis.close();
}
}

private static void copyTree(File src, File dst) throws IOException {
Expand Down
25 changes: 17 additions & 8 deletions scripts/cn1playground/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,21 +187,30 @@ The playground includes an **Inspector** tab that displays the component hierarc

Changes made in the property editor are immediately reflected in the preview. The component tree updates automatically when your script re-runs.

## JavaScript Port Tracking
## JavaScript Port

The current `javascript` module still represents the legacy JavaScript build
path. While the new ParparVM-backed JavaScript port is being integrated, you
can compare bundle size against a ParparVM artifact with:
The `javascript` module builds with the local ParparVM JavaScript target
(`codename1.buildTarget=local-javascript`), the same path the initializr uses.
The build translates the app's ParparVM bytecode to JavaScript locally instead
of routing through the legacy `javascript` cloud build target, so it tracks the
current Codename One sources directly — there is no longer an old-version pin or
a class-exclusion list working around the cloud TeaVM backend lagging the
release channel. The cloud `javascript` target remains available as a fallback.

The bean-shell access registry (`GeneratedCN1Access`) is generated against the
same sources the bundle is built from. When building against the local
workspace (`-Dcn1.localWorkspace=true`) it is generated from the repo's own CN1
sources; see [`tools/README.md`](tools/README.md).

To compare a bundle's size against another ParparVM artifact:

```bash
PLAYGROUND_PARPARVM_BUNDLE=/path/to/parparvm/dist ./build.sh javascript_compare
```

This uses
[`compare-javascript-bundles.sh`](/Users/shai/dev/cn1/scripts/cn1playground/tools/compare-javascript-bundles.sh)
to report total and JavaScript payload sizes. The long-term goal is to replace
the legacy `javascript` module build itself with the ParparVM-backed port once
the runtime and browser harness are complete.
[`compare-javascript-bundles.sh`](tools/compare-javascript-bundles.sh)
to report total and JavaScript payload sizes.

## BeanShell Interpreter Tradeoffs

Expand Down
8 changes: 7 additions & 1 deletion scripts/cn1playground/build.bat
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,13 @@ goto :EOF

goto :EOF
:javascript
!MVNW! package -DskipTests -Dcodename1.platform^=javascript -Dcodename1.buildTarget^=javascript -U -e
rem The Playground keeps nearly the whole Codename One API reachable via its
rem bean-shell registry, so the ParparVM JS RTA tree-shaking pass runs for over
rem an hour without pruning much. Disable it (parparvm.js.rta.off); the un-pruned
rem bundle is large, so raise the translator heap above the 512m default to avoid
rem an OutOfMemoryError mid-emit. See README.md "JavaScript Port".
if not defined CN1_TRANSLATOR_OPTS set CN1_TRANSLATOR_OPTS=-Dparparvm.js.rta.off -Xmx6g
!MVNW! package -DskipTests -Dcodename1.platform^=javascript -Dcodename1.buildTarget^=local-javascript -U -e

goto :EOF
:android
Expand Down
10 changes: 8 additions & 2 deletions scripts/cn1playground/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,14 @@ function linux_device {
"$MVNW" "package" "-DskipTests" "-Dcodename1.platform=linux" "-Dcodename1.buildTarget=linux-device" "-U" "-e"
}
function javascript {

"$MVNW" "package" "-DskipTests" "-Dcodename1.platform=javascript" "-Dcodename1.buildTarget=javascript" "-U" "-e"
# The Playground's bean-shell registry keeps nearly the whole Codename One
# API reachable, so the ParparVM JS Rapid Type Analysis (RTA) tree-shaking
# pass cannot prune much yet runs for well over an hour. Disable RTA
# (parparvm.js.rta.off); the resulting un-pruned bundle is large, so give
# the translator a bigger heap than the 512m default to avoid an
# OutOfMemoryError mid-emit. See README.md "JavaScript Port".
CN1_TRANSLATOR_OPTS="${CN1_TRANSLATOR_OPTS:--Dparparvm.js.rta.off -Xmx6g}" \
"$MVNW" "package" "-DskipTests" "-Dcodename1.platform=javascript" "-Dcodename1.buildTarget=local-javascript" "-U" "-e"
}
function javascript_compare {
"javascript"
Expand Down
3 changes: 3 additions & 0 deletions scripts/cn1playground/common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,9 @@
<configuration>
<executable>sh</executable>
<workingDirectory>${project.parent.basedir}</workingDirectory>
<environmentVariables>
<CN1_ACCESS_USE_LOCAL_SOURCES>${cn1.accessRegistry.useLocalSources}</CN1_ACCESS_USE_LOCAL_SOURCES>
</environmentVariables>
<arguments>
<argument>${project.parent.basedir}/tools/generate-cn1-access-registry.sh</argument>
</arguments>
Expand Down
Loading
Loading