diff --git a/compiler/wasm-pack/Cargo.toml b/compiler/wasm-pack/Cargo.toml
new file mode 100644
index 000000000..8b27f0c79
--- /dev/null
+++ b/compiler/wasm-pack/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "hello_world"
+version = "0.1.0"
+authors = ["The wasm-bindgen Developers"]
+edition = "2018"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+wasm-bindgen = "^0.2"
+web-sys = "^0.3"
+js-sys = "^0.3"
+wasm-bindgen-futures = "^0.4"
+yew = "0.17"
+seed = "0.8.0"
+
+[package.metadata.wasm-pack.profile.release]
+wasm-opt = false
\ No newline at end of file
diff --git a/compiler/wasm-pack/Dockerfile b/compiler/wasm-pack/Dockerfile
new file mode 100644
index 000000000..ae2dd8a55
--- /dev/null
+++ b/compiler/wasm-pack/Dockerfile
@@ -0,0 +1,17 @@
+# syntax = docker/dockerfile:experimental
+
+# fetch dependencies to local
+FROM shepmaster/rust-nightly as sources
+RUN cargo install wasm-pack
+ADD --chown=playground src/lib.rs /playground/src/lib.rs
+# TODO support top 100 crates
+ADD --chown=playground Cargo.toml /playground/Cargo.toml
+RUN cargo fetch
+
+# build dependencies
+FROM sources
+RUN wasm-pack build --target web --out-name package --dev
+RUN rm src/*.rs
+
+ADD --chown=playground cargo-pack /playground/.cargo/bin/
+ENTRYPOINT ["/playground/tools/entrypoint.sh"]
diff --git a/compiler/wasm-pack/cargo-pack b/compiler/wasm-pack/cargo-pack
new file mode 100755
index 000000000..64f14c25e
--- /dev/null
+++ b/compiler/wasm-pack/cargo-pack
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+
+set -eu
+
+# Rewrite our arguments to be `cargo build` instead of `cargo wasm`;
+# this assumes that the command will always be `cargo wasm ...`. We
+# capture the output directory in order to place the result file.
+shift # Ignore "wasm"
+args=()
+while (( "$#" )); do
+ if [[ "$1" == "--" ]] ; then
+ : # Ignore
+ elif [[ "$1" == "-o" ]] ; then
+ shift
+ output="$1"
+ else
+ args+="$1"
+ fi
+
+ shift
+done
+# Greatly inspired from https://gitlab.com/strwrite/seed-playground
+# --dev flag disables the wasm-opt for optimization downloaded from networks
+wasm-pack build --target web --out-name package --dev
+
+cat pkg/package_bg.wasm | base64 > "${output}.wasm"
+cat pkg/package.js | base64 > "${output}.js"
+
diff --git a/compiler/wasm-pack/src/lib.rs b/compiler/wasm-pack/src/lib.rs
new file mode 100644
index 000000000..8f6dcb2f2
--- /dev/null
+++ b/compiler/wasm-pack/src/lib.rs
@@ -0,0 +1,19 @@
+use wasm_bindgen::prelude::*;
+
+// Called by our JS entry point to run the example
+#[wasm_bindgen(start)]
+pub fn run() -> Result<(), JsValue> {
+ // Use `web_sys`'s global `window` function to get a handle on the global
+ // window object.
+ let window = web_sys::window().expect("no global `window` exists");
+ let document = window.document().expect("should have a document on window");
+ let body = document.body().expect("document should have a body");
+
+ // Manufacture the element we're gonna append
+ let val = document.create_element("p")?;
+ val.set_text_content(Some("Hello from Rust!"));
+
+ body.append_child(&val)?;
+
+ Ok(())
+}
\ No newline at end of file
diff --git a/ui/Cargo.lock b/ui/Cargo.lock
index 4b8225a0e..c5c75ff2b 100644
--- a/ui/Cargo.lock
+++ b/ui/Cargo.lock
@@ -1,7 +1,5 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
-version = 3
-
[[package]]
name = "aho-corasick"
version = "0.7.15"
diff --git a/ui/frontend/BuildMenu.tsx b/ui/frontend/BuildMenu.tsx
index 9a4ea349a..c75985868 100644
--- a/ui/frontend/BuildMenu.tsx
+++ b/ui/frontend/BuildMenu.tsx
@@ -26,6 +26,7 @@ const useDispatchAndClose = (action: () => void, close: () => void) => {
const BuildMenu: React.SFC = props => {
const isHirAvailable = useSelector(selectors.isHirAvailable);
const isWasmAvailable = useSelector(selectors.isWasmAvailable);
+ const isWasmPackAvailable = useSelector(selectors.isWasmPackAvailable);
const compile = useDispatchAndClose(actions.performCompile, props.close);
const compileToAssembly = useDispatchAndClose(actions.performCompileToAssembly, props.close);
@@ -33,6 +34,7 @@ const BuildMenu: React.SFC = props => {
const compileToMir = useDispatchAndClose(actions.performCompileToMir, props.close);
const compileToHir = useDispatchAndClose(actions.performCompileToNightlyHir, props.close);
const compileToWasm = useDispatchAndClose(actions.performCompileToNightlyWasm, props.close);
+ const compileToWasmPack = useDispatchAndClose(actions.performCompileToNightlyWasmPack, props.close);
const execute = useDispatchAndClose(actions.performExecute, props.close);
const test = useDispatchAndClose(actions.performTest, props.close);
@@ -67,6 +69,10 @@ const BuildMenu: React.SFC = props => {
Build a WebAssembly module for web browsers, in the .WAT textual representation.
{!isWasmAvailable && }
+
+ Build a WebAssembly frontend for web browsers. Rendering in Iframe
+ {!isWasmPackAvailable && }
+
);
};
@@ -85,4 +91,10 @@ const WasmAside: React.SFC = () => (
);
+const WasmPackAside: React.SFC = () => (
+
+ Note: WASM PACK currently requires using the Nightly channel, selecting this
+ option will switch to Nightly.
+
+);
export default BuildMenu;
diff --git a/ui/frontend/Output.tsx b/ui/frontend/Output.tsx
index ae5535e2d..736dea9fa 100644
--- a/ui/frontend/Output.tsx
+++ b/ui/frontend/Output.tsx
@@ -10,6 +10,7 @@ import Gist from './Output/Gist';
import Section from './Output/Section';
import SimplePane, { SimplePaneProps } from './Output/SimplePane';
import PaneWithMir from './Output/PaneWithMir';
+import PaneWithWasmPack from './Output/PaneWithWasmPack';
import * as selectors from './selectors';
const Tab: React.SFC = ({ kind, focus, label, onClick, tabProps }) => {
@@ -46,7 +47,10 @@ interface PaneWithCodeProps extends SimplePaneProps {
const Output: React.SFC = () => {
const somethingToShow = useSelector(selectors.getSomethingToShow);
- const { meta: { focus }, execute, format, clippy, miri, macroExpansion, assembly, llvmIr, mir, hir, wasm, gist } =
+ const { meta: { focus },
+ execute, format, clippy, miri,
+ macroExpansion, assembly, llvmIr, mir,
+ hir, wasm, gist, wasmPack } =
useSelector((state: State) => state.output);
const dispatch = useDispatch();
@@ -62,6 +66,7 @@ const Output: React.SFC = () => {
const focusHir = useCallback(() => dispatch(actions.changeFocus(Focus.Hir)), [dispatch]);
const focusWasm = useCallback(() => dispatch(actions.changeFocus(Focus.Wasm)), [dispatch]);
const focusGist = useCallback(() => dispatch(actions.changeFocus(Focus.Gist)), [dispatch]);
+ const focusWasmPack = useCallback(() => dispatch(actions.changeFocus(Focus.WasmPack)), [dispatch]);
if (!somethingToShow) {
return null;
@@ -88,6 +93,7 @@ const Output: React.SFC = () => {
{focus === Focus.Hir && }
{focus === Focus.Wasm && }
{focus === Focus.Gist && }
+ {focus === Focus.WasmPack && }
);
}
@@ -139,6 +145,10 @@ const Output: React.SFC = () => {
label="Share"
onClick={focusGist}
tabProps={gist} />
+
{close}
{body}
diff --git a/ui/frontend/Output/Container.tsx b/ui/frontend/Output/Container.tsx
new file mode 100644
index 000000000..cad0f1a29
--- /dev/null
+++ b/ui/frontend/Output/Container.tsx
@@ -0,0 +1,35 @@
+import React, { useState, useEffect } from 'react'
+import { createPortal } from 'react-dom'
+
+export const FunctionalIFrameComponent = ({
+ children,
+ url,
+ ...props
+}) => {
+ const [contentRef, setContentRef] = useState(null)
+ const document =
+ contentRef?.contentWindow?.document;
+ const mountNode = document?.body;
+
+ useEffect(() => {
+ if (document) {
+ const script = document.createElement('script');
+ script.src = url;
+ script.type = 'module';
+ script.async = true;
+ mountNode && mountNode.appendChild(script);
+ }
+
+ return () => {
+ if (mountNode) {
+ mountNode.innerHTML = ''
+ }
+ };
+ }, [mountNode, document, url]);
+
+ return (
+
+ )
+}
diff --git a/ui/frontend/Output/PaneWithWasmPack.tsx b/ui/frontend/Output/PaneWithWasmPack.tsx
new file mode 100644
index 000000000..0353cd600
--- /dev/null
+++ b/ui/frontend/Output/PaneWithWasmPack.tsx
@@ -0,0 +1,52 @@
+import React from 'react';
+
+import Header from './Header';
+import SimplePane, { SimplePaneProps } from './SimplePane';
+import { FunctionalIFrameComponent } from './Container';
+
+interface PaneWithWasmPackProps extends SimplePaneProps {
+ success?: boolean;
+ wasm_js?: string;
+ wasm_bg?: string;
+}
+
+function base64ToByteArray(src: string) {
+ const decode = atob(src);
+ const byteNumbers = new Array(decode.length);
+ for (let i = 0; i < decode.length; i++) {
+ byteNumbers[i] = decode.charCodeAt(i);
+ }
+ return new Uint8Array(byteNumbers);
+}
+
+function createObjectURL(src: ArrayBuffer | string, mime: string) {
+ return URL.createObjectURL(new Blob([src], { type: mime }));
+}
+
+function createEntryJS(wasm_js: string, wasm_bg: string, success: boolean) {
+ if (!success) {
+ return '';
+ }
+ const wasmJS = atob(wasm_js);
+ const bgWasm = base64ToByteArray(wasm_bg);
+ const wasmJSBlob = createObjectURL(wasmJS, 'application/javascript');
+ const bgWasmBlob = createObjectURL(bgWasm, 'application/wasm');
+ const entryJS = `
+ import init from '${wasmJSBlob}';
+ await init('${bgWasmBlob}');
+ `;
+
+ return createObjectURL(entryJS, 'application/javascript');
+}
+
+const PaneWithWasmPack: React.SFC = ({ wasm_js, wasm_bg, success, ...rest }) => (
+
+
+
+
+
+
+
+);
+
+export default PaneWithWasmPack;
diff --git a/ui/frontend/actions.ts b/ui/frontend/actions.ts
index 8b2e30d2d..19be55ab2 100644
--- a/ui/frontend/actions.ts
+++ b/ui/frontend/actions.ts
@@ -38,6 +38,7 @@ const routes = {
clippy: { pathname: '/clippy' },
miri: { pathname: '/miri' },
macroExpansion: { pathname: '/macro-expansion' },
+ wasmPack: { pathname: '/wasm-pack' },
meta: {
crates: { pathname: '/meta/crates' },
version: {
@@ -92,6 +93,9 @@ export enum ActionType {
CompileWasmRequest = 'COMPILE_WASM_REQUEST',
CompileWasmSucceeded = 'COMPILE_WASM_SUCCEEDED',
CompileWasmFailed = 'COMPILE_WASM_FAILED',
+ CompileWasmPackRequest = 'COMPILE_WASM_PACK_REQUEST',
+ CompileWasmPackSucceeded = 'COMPILE_WASM_PACK_SUCCEEDED',
+ CompileWasmPackFailed = 'COMPILE_WASM_PACK_FAILED',
EditCode = 'EDIT_CODE',
AddMainFunction = 'ADD_MAIN_FUNCTION',
AddImport = 'ADD_IMPORT',
@@ -260,7 +264,7 @@ const performCommonExecute = (crateType, tests): ThunkAction => (dispatch, getSt
};
function performAutoOnly(): ThunkAction {
- return function(dispatch, getState) {
+ return function (dispatch, getState) {
const state = getState();
const crateType = getCrateType(state);
const tests = runAsTest(state);
@@ -282,7 +286,7 @@ interface CompileRequestBody extends ExecuteRequestBody {
function performCompileShow(target, { request, success, failure }): ThunkAction {
// TODO: Check a cache
- return function(dispatch, getState) {
+ return function (dispatch, getState) {
dispatch(request());
const state = getState();
@@ -406,7 +410,45 @@ const performCompileToNightlyWasmOnly = (): ThunkAction => dispatch => {
dispatch(changeChannel(Channel.Nightly));
dispatch(performCompileToWasm());
};
+interface WasmPackRequestBody {
+ code: string;
+}
+
+function performCompileToWasmPack({ request, success, failure }): ThunkAction {
+ // TODO: Check a cache
+ return function (dispatch, getState) {
+ dispatch(request());
+ const state = getState();
+ const { code } = state;
+ const body: WasmPackRequestBody = {
+ code,
+ };
+ return jsonPost(routes.wasmPack, body)
+ .then(json => dispatch(success(json)))
+ .catch(json => dispatch(failure(json)));
+ };
+}
+
+const requestCompileWasmPack = () =>
+ createAction(ActionType.CompileWasmPackRequest);
+
+const receiveCompileWasmPackSuccess = ({ wasm_js, wasm_bg, stdout, stderr, success }) =>
+ createAction(ActionType.CompileWasmPackSucceeded, { wasm_js, wasm_bg, stdout, stderr, success });
+
+const receiveCompileWasmPackFailure = ({ error }) =>
+ createAction(ActionType.CompileWasmPackFailed, { error });
+
+const performCompileToNightlyWasmPackOnly = (): ThunkAction => dispatch => {
+ dispatch(changeChannel(Channel.Nightly));
+ dispatch(performCompileToWasmPack(
+ {
+ request: requestCompileWasmPack,
+ success: receiveCompileWasmPackSuccess,
+ failure: receiveCompileWasmPackFailure,
+ }
+ ));
+};
const PRIMARY_ACTIONS: { [index in PrimaryAction]: () => ThunkAction } = {
[PrimaryActionCore.Asm]: performCompileToAssemblyOnly,
[PrimaryActionCore.Compile]: performCompileOnly,
@@ -417,6 +459,7 @@ const PRIMARY_ACTIONS: { [index in PrimaryAction]: () => ThunkAction } = {
[PrimaryActionCore.Hir]: performCompileToHirOnly,
[PrimaryActionCore.Mir]: performCompileToMirOnly,
[PrimaryActionCore.Wasm]: performCompileToNightlyWasmOnly,
+ [PrimaryActionCore.WasmPack]: performCompileToNightlyWasmPackOnly,
};
export const performPrimaryAction = (): ThunkAction => (dispatch, getState) => {
@@ -446,6 +489,8 @@ export const performCompileToNightlyHir =
performAndSwitchPrimaryAction(performCompileToNightlyHirOnly, PrimaryActionCore.Hir);
export const performCompileToNightlyWasm =
performAndSwitchPrimaryAction(performCompileToNightlyWasmOnly, PrimaryActionCore.Wasm);
+export const performCompileToNightlyWasmPack =
+ performAndSwitchPrimaryAction(performCompileToNightlyWasmPackOnly, PrimaryActionCore.WasmPack);
export const editCode = (code: string) =>
createAction(ActionType.EditCode, { code });
@@ -488,7 +533,7 @@ const receiveFormatFailure = (body: FormatResponseBody) =>
export function performFormat(): ThunkAction {
// TODO: Check a cache
- return function(dispatch, getState) {
+ return function (dispatch, getState) {
dispatch(requestFormat());
const body: FormatRequestBody = formatRequestSelector(getState());
@@ -522,7 +567,7 @@ const receiveClippyFailure = ({ error }) =>
export function performClippy(): ThunkAction {
// TODO: Check a cache
- return function(dispatch, getState) {
+ return function (dispatch, getState) {
dispatch(requestClippy());
const body: ClippyRequestBody = clippyRequestSelector(getState());
@@ -549,7 +594,7 @@ const receiveMiriFailure = ({ error }) =>
export function performMiri(): ThunkAction {
// TODO: Check a cache
- return function(dispatch, getState) {
+ return function (dispatch, getState) {
dispatch(requestMiri());
const { code, configuration: {
@@ -579,7 +624,7 @@ const receiveMacroExpansionFailure = ({ error }) =>
export function performMacroExpansion(): ThunkAction {
// TODO: Check a cache
- return function(dispatch, getState) {
+ return function (dispatch, getState) {
dispatch(requestMacroExpansion());
const { code, configuration: {
@@ -617,7 +662,7 @@ type PerformGistLoadProps =
Pick>;
export function performGistLoad({ id, channel, mode, edition }: PerformGistLoadProps): ThunkAction {
- return function(dispatch, _getState) {
+ return function (dispatch, _getState) {
dispatch(requestGistLoad());
const u = url.resolve(routes.meta.gist.pathname, id);
jsonGet(u)
@@ -636,7 +681,7 @@ const receiveGistSaveFailure = ({ error }) => // eslint-disable-line no-unused-v
createAction(ActionType.GistSaveFailed, { error });
export function performGistSave(): ThunkAction {
- return function(dispatch, getState) {
+ return function (dispatch, getState) {
dispatch(requestGistSave());
const { code, configuration: { channel, mode, edition }, output: { execute: { stdout, stderr } } } = getState();
@@ -654,7 +699,7 @@ const receiveCratesLoadSuccess = ({ crates }) =>
createAction(ActionType.CratesLoadSucceeded, { crates });
export function performCratesLoad(): ThunkAction {
- return function(dispatch) {
+ return function (dispatch) {
dispatch(requestCratesLoad());
return jsonGet(routes.meta.crates)
@@ -670,7 +715,7 @@ const receiveVersionsLoadSuccess = ({ stable, beta, nightly, rustfmt, clippy, mi
createAction(ActionType.VersionsLoadSucceeded, { stable, beta, nightly, rustfmt, clippy, miri });
export function performVersionsLoad(): ThunkAction {
- return function(dispatch) {
+ return function (dispatch) {
dispatch(requestVersionsLoad());
const stable = jsonGet(routes.meta.version.stable);
@@ -742,7 +787,7 @@ export function indexPageLoad({
mode: modeString = 'debug',
edition: editionString,
}): ThunkAction {
- return function(dispatch) {
+ return function (dispatch) {
const channel = parseChannel(version);
const mode = parseMode(modeString);
let edition = parseEdition(editionString);
@@ -783,7 +828,7 @@ export function helpPageLoad() {
}
export function showExample(code): ThunkAction {
- return function(dispatch) {
+ return function (dispatch) {
dispatch(navigateToIndex());
dispatch(editCode(code));
};
@@ -823,6 +868,9 @@ export type Action =
| ReturnType
| ReturnType
| ReturnType
+ | ReturnType
+ | ReturnType
+ | ReturnType
| ReturnType
| ReturnType
| ReturnType
diff --git a/ui/frontend/index.scss b/ui/frontend/index.scss
index e06a60f24..fed78bede 100644
--- a/ui/frontend/index.scss
+++ b/ui/frontend/index.scss
@@ -894,3 +894,12 @@ $header-transition: 0.2s ease-in-out;
cursor: pointer;
}
}
+
+.container {
+ height: 100%;
+ width: 100%;
+ margin: 0;
+ border: 0;
+ padding: 0;
+ overflow: hidden;
+}
\ No newline at end of file
diff --git a/ui/frontend/reducers/output/index.ts b/ui/frontend/reducers/output/index.ts
index 8942a967a..61ff8d4a7 100644
--- a/ui/frontend/reducers/output/index.ts
+++ b/ui/frontend/reducers/output/index.ts
@@ -12,6 +12,7 @@ import meta from './meta';
import mir from './mir';
import miri from './miri';
import wasm from './wasm';
+import wasmPack from './wasmPack';
const output = combineReducers({
meta,
@@ -26,6 +27,7 @@ const output = combineReducers({
wasm,
execute,
gist,
+ wasmPack,
});
export type State = ReturnType;
diff --git a/ui/frontend/reducers/output/meta.ts b/ui/frontend/reducers/output/meta.ts
index 37689c903..f0423871b 100644
--- a/ui/frontend/reducers/output/meta.ts
+++ b/ui/frontend/reducers/output/meta.ts
@@ -35,6 +35,9 @@ export default function meta(state = DEFAULT, action: Action) {
case ActionType.CompileWasmRequest:
return { ...state, focus: Focus.Wasm };
+ case ActionType.CompileWasmPackRequest:
+ return { ...state, focus: Focus.WasmPack };
+
case ActionType.CompileAssemblyRequest:
return { ...state, focus: Focus.Asm };
diff --git a/ui/frontend/reducers/output/wasmPack.ts b/ui/frontend/reducers/output/wasmPack.ts
new file mode 100644
index 000000000..db3221682
--- /dev/null
+++ b/ui/frontend/reducers/output/wasmPack.ts
@@ -0,0 +1,37 @@
+import { Action, ActionType } from '../../actions';
+import { finish, start } from './sharedStateManagement';
+
+const DEFAULT: State = {
+ requestsInProgress: 0,
+ success: false,
+ stdout: null,
+ stderr: null,
+ error: null,
+ wasm_js: null,
+ wasm_bg: null,
+};
+
+interface State {
+ requestsInProgress: number;
+ success?: boolean;
+ stdout?: string;
+ stderr?: string;
+ error?: string;
+ wasm_js?: string;
+ wasm_bg?: string;
+}
+
+export default function wasmPack(state = DEFAULT, action: Action) {
+ switch (action.type) {
+ case ActionType.CompileWasmPackRequest:
+ return start(DEFAULT, state);
+ case ActionType.CompileWasmPackSucceeded: {
+ const { stdout = '', stderr = '', wasm_js = '', wasm_bg = '', success = false } = action;
+ return finish(state, { stdout, stderr, wasm_js, wasm_bg, success });
+ }
+ case ActionType.CompileWasmPackFailed:
+ return finish(state, { error: action.error });
+ default:
+ return state;
+ }
+}
diff --git a/ui/frontend/selectors/index.ts b/ui/frontend/selectors/index.ts
index 90e3e52ef..370748896 100644
--- a/ui/frontend/selectors/index.ts
+++ b/ui/frontend/selectors/index.ts
@@ -75,6 +75,7 @@ const LABELS: { [index in PrimaryActionCore]: string } = {
[PrimaryActionCore.Mir]: 'Show MIR',
[PrimaryActionCore.Test]: 'Test',
[PrimaryActionCore.Wasm]: 'Show WASM',
+ [PrimaryActionCore.WasmPack]: 'Show WASM PACK',
};
export const getExecutionLabel = createSelector(primaryActionSelector, primaryAction => LABELS[primaryAction]);
@@ -108,6 +109,7 @@ export const isNightlyChannel = (state: State) => (
);
export const isWasmAvailable = isNightlyChannel;
export const isHirAvailable = isNightlyChannel;
+export const isWasmPackAvailable = isNightlyChannel;
export const getModeLabel = (state: State) => {
const { configuration: { mode } } = state;
@@ -149,6 +151,7 @@ const getOutputs = (state: State) => [
state.output.miri,
state.output.macroExpansion,
state.output.wasm,
+ state.output.wasmPack,
];
export const getSomethingToShow = createSelector(
diff --git a/ui/frontend/types.ts b/ui/frontend/types.ts
index a21518986..ac94847c5 100644
--- a/ui/frontend/types.ts
+++ b/ui/frontend/types.ts
@@ -78,6 +78,7 @@ export enum PrimaryActionCore {
Mir = 'mir',
Test = 'test',
Wasm = 'wasm',
+ WasmPack = 'wasm-pack',
}
export type PrimaryAction = PrimaryActionCore | PrimaryActionAuto;
@@ -115,6 +116,7 @@ export enum Focus {
Execute = 'execute',
Format = 'format',
Gist = 'gist',
+ WasmPack = 'wasm-pack',
}
export enum Notification {
diff --git a/ui/src/main.rs b/ui/src/main.rs
index 2421bb8f2..a337087b0 100644
--- a/ui/src/main.rs
+++ b/ui/src/main.rs
@@ -85,6 +85,7 @@ fn main() {
mount.mount("/meta/version/miri", meta_version_miri);
mount.mount("/meta/gist", gist_router);
mount.mount("/evaluate.json", evaluate);
+ mount.mount("/wasm-pack", wasm_pack);
let mut chain = Chain::new(mount);
let file_logger = FileLogger::new(logfile).expect("Unable to create file logger");
@@ -281,6 +282,16 @@ fn evaluate(req: &mut Request<'_, '_>) -> IronResult {
})
}
+fn wasm_pack(req: &mut Request<'_, '_>) -> IronResult{
+ with_sandbox(req, |sandbox, req: WasmPackRequest| {
+ let req = req.try_into()?;
+ sandbox
+ .wasm_pack(&req)
+ .map(WasmPackResponse::from)
+ .context(WasmPack)
+ })
+}
+
fn with_sandbox(req: &mut Request<'_, '_>, f: F) -> IronResult
where
F: FnOnce(Sandbox, Req) -> Result,
@@ -488,6 +499,8 @@ pub enum Error {
Execution { source: sandbox::Error },
#[snafu(display("Evaluation operation failed: {}", source))]
Evaluation { source: sandbox::Error },
+ #[snafu(display("wasm-pack operation failed: {}", source))]
+ WasmPack { source: sandbox::Error },
#[snafu(display("Linting operation failed: {}", source))]
Linting { source: sandbox::Error },
#[snafu(display("Expansion operation failed: {}", source))]
@@ -691,6 +704,20 @@ struct EvaluateResponse {
error: Option,
}
+#[derive(Debug, Clone, Deserialize)]
+struct WasmPackRequest {
+ code: String
+}
+
+#[derive(Debug, Clone, Serialize)]
+struct WasmPackResponse {
+ success: bool,
+ wasm_js: String,
+ wasm_bg: String,
+ stdout: String,
+ stderr: String,
+}
+
impl TryFrom for sandbox::CompileRequest {
type Error = Error;
@@ -927,6 +954,29 @@ impl From for EvaluateResponse {
}
}
+impl TryFrom for sandbox::WasmPackRequest {
+ type Error = Error;
+
+ fn try_from(me: WasmPackRequest) -> Result {
+ Ok(sandbox::WasmPackRequest {
+ code: me.code,
+ ..sandbox::WasmPackRequest::default()
+ })
+ }
+}
+
+impl From for WasmPackResponse {
+ fn from(me: sandbox::WasmPackResponse) -> Self {
+ WasmPackResponse {
+ success: me.success,
+ wasm_bg: me.wasm_bg,
+ wasm_js: me.wasm_js,
+ stdout: me.stdout,
+ stderr: me.stderr,
+ }
+ }
+}
+
fn parse_target(s: &str) -> Result {
Ok(match s {
"asm" => sandbox::CompileTarget::Assembly(sandbox::AssemblyFlavor::Att,
diff --git a/ui/src/sandbox.rs b/ui/src/sandbox.rs
index 2b529d088..fac6624ae 100644
--- a/ui/src/sandbox.rs
+++ b/ui/src/sandbox.rs
@@ -194,6 +194,43 @@ impl Sandbox {
})
}
+ // Greatly inspired from https://gitlab.com/strwrite/seed-playground
+ pub fn wasm_pack(&self, req: &WasmPackRequest) -> Result {
+ use CompileTarget::*;
+ use CrateType::*;
+
+ let compile_req = CompileRequest{
+ backtrace: false,
+ channel: Channel::WasmPack,
+ code: req.code.clone(),
+ crate_type: Library(LibraryType::Cdylib),
+ edition: Some(Edition::Rust2018),
+ mode: Mode::Debug,
+ target: WasmPack,
+ tests: false,
+ };
+ let res = self.compile(&compile_req)?;
+ let js_file =
+ fs::read_dir(&self.output_dir)
+ .context(UnableToReadOutput)?
+ .flat_map(|entry| entry)
+ .map(|entry| entry.path())
+ .find(|path| path.extension() == Some(OsStr::new("js")));
+
+ let js_code = match js_file {
+ Some(file) => read(&file)?.unwrap_or_else(String::new),
+ None => String::new() // TODO: return proper error?
+ };
+
+ Ok(WasmPackResponse {
+ success: res.success,
+ stdout: res.stdout,
+ stderr: res.stderr,
+ wasm_bg: res.code,
+ wasm_js: js_code
+ })
+ }
+
pub fn format(&self, req: &FormatRequest) -> Result {
self.write_source_code(&req.code)?;
let command = self.format_command(req);
@@ -419,10 +456,14 @@ impl Sandbox {
mount_output_dir.push(":");
mount_output_dir.push("/playground-result");
+ // let mut mount_output_wasm = self.scratch.as_path().join("pkg").as_os_str().to_os_string();
+ // mount_output_wasm.push(":");
+ // mount_output_wasm.push("/playground/pkg");
let mut cmd = basic_secure_docker_command();
cmd
.arg("--volume").arg(&mount_input_file)
+ // .arg("--volume").arg(&mount_output_wasm)
.arg("--volume").arg(&mount_output_dir);
cmd
@@ -469,6 +510,7 @@ fn build_execution_command(target: Option, channel: Channel, mode
let mut cmd = vec!["cargo"];
match (target, req.crate_type(), tests) {
+ (Some(WasmPack), _, _) => cmd.push("pack"),
(Some(Wasm), _, _) => cmd.push("wasm"),
(Some(_), _, _) => cmd.push("rustc"),
(_, _, true) => cmd.push("test"),
@@ -511,12 +553,14 @@ fn build_execution_command(target: Option, channel: Channel, mode
Mir => cmd.push("--emit=mir"),
Hir => cmd.push("-Zunpretty=hir"),
Wasm => { /* handled by cargo-wasm wrapper */ },
+ WasmPack => { /* handled by cargo-wasmpack wrapper */ },
}
- }
-
+ }
+ log::debug!("{:?}", &cmd);
cmd
}
+
fn set_execution_environment(cmd: &mut Command, target: Option, req: impl CrateTypeRequest + EditionRequest + BacktraceRequest) {
use self::CompileTarget::*;
@@ -623,6 +667,7 @@ pub enum CompileTarget {
Mir,
Hir,
Wasm,
+ WasmPack,
}
impl CompileTarget {
@@ -633,6 +678,7 @@ impl CompileTarget {
CompileTarget::Mir => "mir",
CompileTarget::Hir => "hir",
CompileTarget::Wasm => "wat",
+ CompileTarget::WasmPack => "wasm",
};
OsStr::new(ext)
}
@@ -648,6 +694,7 @@ impl fmt::Display for CompileTarget {
Mir => "Rust MIR".fmt(f),
Hir => "Rust HIR".fmt(f),
Wasm => "WebAssembly".fmt(f),
+ WasmPack => "WasmPack".fmt(f),
}
}
}
@@ -657,6 +704,7 @@ pub enum Channel {
Stable,
Beta,
Nightly,
+ WasmPack,
}
impl Channel {
@@ -667,6 +715,7 @@ impl Channel {
Stable => "rust-stable",
Beta => "rust-beta",
Nightly => "rust-nightly",
+ WasmPack => "rust-wasm-pack",
}
}
}
@@ -924,6 +973,35 @@ pub struct MacroExpansionResponse {
pub stderr: String,
}
+#[derive(Debug, Clone)]
+pub struct WasmPackRequest {
+ pub code: String,
+ pub crate_type: CrateType,
+ pub output_name: String,
+}
+
+impl Default for WasmPackRequest {
+ fn default() -> Self {
+ WasmPackRequest {
+ code: String::from(""),
+ crate_type: CrateType::Library(LibraryType::Rlib),
+ output_name: "wasm".to_string(),
+ }
+ }
+}
+
+impl CrateTypeRequest for WasmPackRequest {
+ fn crate_type(&self) -> CrateType { self.crate_type }
+}
+
+#[derive(Debug, Clone)]
+pub struct WasmPackResponse {
+ pub wasm_js: String,
+ pub wasm_bg: String,
+ pub success: bool,
+ pub stdout: String,
+ pub stderr: String,
+}
#[cfg(test)]
mod test {
use super::*;