Skip to content

Commit

Permalink
feat: encoding polyfill
Browse files Browse the repository at this point in the history
  • Loading branch information
4e6 committed Apr 2, 2024
1 parent 80bcd19 commit d71e200
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 4 deletions.
3 changes: 0 additions & 3 deletions app/gui2/shared/util/detect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,4 @@
export const isNode =
typeof global !== 'undefined' && (global as any)[Symbol.toStringTag] === 'global'

// Java environment is set up to have a `jvm: 'graalvm'` property.
export const isJvm = typeof global !== 'undefined' && (global as any)['jvm'] === 'graalvm'

export const isDevMode = process.env.NODE_ENV === 'development'
2 changes: 2 additions & 0 deletions app/gui2/vite.ydoc-server.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export default defineConfig({
'import.meta.vitest': false,
// Single hardcoded usage of `global` in aws-amplify.
'global.TYPED_ARRAY_SUPPORT': true,
// One of the libraries refers to self in `self.fetch.bind(self)`
'self': 'globalThis'
},
build: {
minify: false, // For debugging
Expand Down
8 changes: 8 additions & 0 deletions app/gui2/ydoc-server/server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { setupGatewayClient } from './ydoc'

declare global {
class WebSocketServer {
constructor(config: any)
onconnect: ((socket: any, url: any) => any) | null;
start(): void;
}
}

const wss = new WebSocketServer({ host: 'localhost', port: 1234 })

wss.onconnect = (socket, url) => setupGatewayClient(socket, "ws://localhost:8080", url)
Expand Down
3 changes: 2 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -1125,7 +1125,8 @@ lazy val `polyglot-ydoc-server` = project
.settings(
crossPaths := false,
autoScalaLibrary := false,
run / fork := true,
Compile / run / fork := true,
Test / fork := true,
modulePath := {
JPMSUtils.filterModulesFromUpdate(
update.value,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.enso.polyfill.encoding;

import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

import org.enso.polyfill.Polyfill;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Source;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.proxy.ProxyExecutable;

public final class EncodingPolyfill implements ProxyExecutable, Polyfill {

private static final String TEXT_DECODER_DECODE = "text-decoder-decode";

private static final String ENCODING_POLYFILL_JS = "encoding-polyfill.js";

public EncodingPolyfill() {
}

@Override
public void initialize(Context ctx) {
Source encodingPolyfillJs = Source
.newBuilder("js", EncodingPolyfill.class.getResource(ENCODING_POLYFILL_JS))
.buildLiteral();

ctx.eval(encodingPolyfillJs).execute(this);
}

@Override
public Object execute(Value... arguments) {
var command = arguments[0].asString();
System.err.println(command + " " + Arrays.toString(arguments));

return switch (command) {
case TEXT_DECODER_DECODE -> {
var encoding = arguments[1].asString();
var data = arguments[2].as(int[].class);

var charset = encoding == null ? StandardCharsets.UTF_8 : Charset.forName(encoding);
// Convert unsigned Uint8Array to byte[]
var bytes = new byte[data.length];
for (int i = 0; i < data.length; i++) {
bytes[i] = (byte) data[i];
}

yield charset.decode(ByteBuffer.wrap(bytes)).toString();
}

default ->
throw new IllegalStateException(command);
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import org.enso.polyfill.Polyfill;
import org.enso.polyfill.crypto.CryptoPolyfill;
import org.enso.polyfill.encoding.EncodingPolyfill;
import org.enso.polyfill.timers.TimersPolyfill;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Source;
Expand Down Expand Up @@ -58,6 +59,9 @@ public void initialize(Context ctx) {
var crypto = new CryptoPolyfill();
crypto.initialize(ctx);

var encoding = new EncodingPolyfill();
encoding.initialize(ctx);

Source webSocketPolyfillJs = Source
.newBuilder("js", WebSocketPolyfill.class.getResource(WEBSOCKET_POLYFILL_JS))
.buildLiteral();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
(function (jvm) {

class TextDecoder {

constructor(encoding) {
if (typeof encoding === 'string') {
this._encoding = encoding;
}
}

decode(arr) {
return jvm('text-decoder-decode', this._encoding, arr);
}
}

globalThis.TextDecoder = TextDecoder;

})
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package org.enso.polyfill;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.enso.polyfill.encoding.EncodingPolyfill;
import org.graalvm.polyglot.Context;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class EncodingPolyfillTest {

private Context context;
private ExecutorService executor;

public EncodingPolyfillTest() {
}

@Before
public void setup() throws Exception {
executor = Executors.newSingleThreadExecutor();
var encoding = new EncodingPolyfill();
var b = Context.newBuilder("js");

var chromePort = Integer.getInteger("inspectPort", -1);
if (chromePort > 0) {
b.option("inspect", ":" + chromePort);
}

context = CompletableFuture
.supplyAsync(() -> {
var ctx = b.build();
encoding.initialize(ctx);
return ctx;
}, executor)
.get();
}

@After
public void tearDown() {
executor.close();
context.close();
}

@Test
public void textDecoderDecodeUtf8() throws Exception {
var code = """
let decoder = new TextDecoder();
var arr = new Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33]);
decoder.decode(arr);
""";

var result = CompletableFuture
.supplyAsync(() -> context.eval("js", code), executor)
.get();

Assert.assertEquals("Hello World!", result.as(String.class));
}

@Test
public void textDecoderDecodeWindows1251() throws Exception {
var code = """
let decoder = new TextDecoder('windows-1251');
var arr = new Uint8Array([207, 240, 232, 226, 229, 242, 44, 32, 236, 232, 240, 33]);
decoder.decode(arr);
""";

var result = CompletableFuture
.supplyAsync(() -> context.eval("js", code), executor)
.get();

Assert.assertEquals("Привет, мир!", result.as(String.class));
}

}

0 comments on commit d71e200

Please sign in to comment.