Skip to content

Commit

Permalink
[GR-58597] Cache and share Wasm-to-JS function adapters.
Browse files Browse the repository at this point in the history
PullRequest: js/3269
  • Loading branch information
woess committed Oct 2, 2024
2 parents 13e52ab + a3a3aa6 commit ca38edf
Show file tree
Hide file tree
Showing 20 changed files with 438 additions and 289 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@
import com.oracle.truffle.js.runtime.builtins.wasm.JSWebAssemblyModule;
import com.oracle.truffle.js.runtime.builtins.wasm.JSWebAssemblyModuleObject;
import com.oracle.truffle.js.runtime.builtins.wasm.JSWebAssemblyTable;
import com.oracle.truffle.js.runtime.builtins.wasm.JSWebAssemblyValueTypes;
import com.oracle.truffle.js.runtime.builtins.wasm.WebAssemblyValueType;
import com.oracle.truffle.js.runtime.java.JavaImporter;
import com.oracle.truffle.js.runtime.java.JavaPackage;
import com.oracle.truffle.js.runtime.objects.IteratorRecord;
Expand Down Expand Up @@ -3345,12 +3345,14 @@ public ConstructWebAssemblyTableNode(JSContext context, JSBuiltin builtin, boole
@Specialization
protected JSObject constructTable(JSDynamicObject newTarget, Object descriptor, Object[] args,
@Cached JSToStringNode toStringNode,
@Cached TruffleString.ToJavaStringNode toJavaString,
@Cached ToWebAssemblyValueNode toWebAssemblyValueNode) {
if (!isObjectNode.executeBoolean(descriptor)) {
throw Errors.createTypeError("WebAssembly.Table(): Argument 0 must be a table descriptor", this);
}
TruffleString elementKind = toStringNode.executeString(getElementNode.getValue(descriptor));
if (!JSWebAssemblyValueTypes.isReferenceType(elementKind)) {
String elementKindStr = toJavaString.execute(toStringNode.executeString(getElementNode.getValue(descriptor)));
WebAssemblyValueType elementKind = WebAssemblyValueType.lookupType(elementKindStr);
if (elementKind == null || !elementKind.isReference()) {
throw Errors.createTypeError("WebAssembly.Table(): Descriptor property 'element' must be 'anyfunc' or 'externref'", this);
}
Object initial = getInitialNode.getValue(descriptor);
Expand All @@ -3377,14 +3379,14 @@ protected JSObject constructTable(JSDynamicObject newTarget, Object descriptor,
final JSRealm realm = getRealm();
Object wasmValue;
if (args.length == 0) {
wasmValue = JSWebAssemblyValueTypes.getDefaultValue(realm, elementKind);
wasmValue = elementKind.getDefaultValue(realm);
} else {
wasmValue = toWebAssemblyValueNode.execute(args[0], elementKind);
}
Object wasmTable;
try {
Object createTable = realm.getWASMTableAlloc();
wasmTable = tableAllocLib.execute(createTable, initialInt, maximumInt, elementKind, wasmValue);
wasmTable = tableAllocLib.execute(createTable, initialInt, maximumInt, elementKindStr, wasmValue);
} catch (InteropException ex) {
throw Errors.shouldNotReachHere(ex);
}
Expand Down Expand Up @@ -3416,34 +3418,36 @@ protected JSObject constructGlobal(JSDynamicObject newTarget, Object descriptor,
@Cached IsObjectNode isObjectNode,
@Cached(inline = true) JSToBooleanNode toBooleanNode,
@Cached JSToStringNode toStringNode,
@Cached TruffleString.ToJavaStringNode toJavaString,
@Cached ToWebAssemblyValueNode toWebAssemblyValueNode) {
if (!isObjectNode.executeBoolean(descriptor)) {
throw Errors.createTypeError("WebAssembly.Global(): Argument 0 must be a global descriptor", this);
}
boolean mutable = toBooleanNode.executeBoolean(this, getMutableNode.getValue(descriptor));
TruffleString valueType = toStringNode.executeString(getValueNode.getValue(descriptor));
if (!JSWebAssemblyValueTypes.isValueType(valueType)) {
String valueTypeStr = toJavaString.execute(toStringNode.executeString(getValueNode.getValue(descriptor)));
WebAssemblyValueType valueType = WebAssemblyValueType.lookupType(valueTypeStr);
if (valueType == null) {
throw Errors.createTypeError("WebAssembly.Global(): Descriptor property 'value' must be a WebAssembly type (i32, i64, f32, f64, anyfunc, externref)", this);
}
if (JSWebAssemblyValueTypes.isV128(valueType)) {
if (valueType == WebAssemblyValueType.v128) {
throw Errors.createTypeError("WebAssembly.Global(): Descriptor property 'value' must not be v128", this);
}
final JSRealm realm = getRealm();
Object webAssemblyValue;
// According to the spec only missing values should produce a default value.
// According to the tests also undefined should use the default value.
if (args.length == 0 || args[0] == Undefined.instance) {
webAssemblyValue = JSWebAssemblyValueTypes.getDefaultValue(realm, valueType);
webAssemblyValue = valueType.getDefaultValue(realm);
} else {
if (!getContext().getLanguageOptions().wasmBigInt() && JSWebAssemblyValueTypes.isI64(valueType)) {
if (!getContext().getLanguageOptions().wasmBigInt() && valueType == WebAssemblyValueType.i64) {
throw Errors.createTypeError("WebAssembly.Global(): Can't set the value of i64 WebAssembly.Global", this);
}
webAssemblyValue = toWebAssemblyValueNode.execute(args[0], valueType);
}
Object wasmGlobal;
try {
Object createGlobal = realm.getWASMGlobalAlloc();
wasmGlobal = globalAllocLib.execute(createGlobal, valueType, mutable, webAssemblyValue);
wasmGlobal = globalAllocLib.execute(createGlobal, valueTypeStr, mutable, webAssemblyValue);
} catch (InteropException ex) {
throw Errors.shouldNotReachHere(ex);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
Expand Down Expand Up @@ -63,7 +63,7 @@
import com.oracle.truffle.js.runtime.builtins.BuiltinEnum;
import com.oracle.truffle.js.runtime.builtins.wasm.JSWebAssemblyGlobal;
import com.oracle.truffle.js.runtime.builtins.wasm.JSWebAssemblyGlobalObject;
import com.oracle.truffle.js.runtime.builtins.wasm.JSWebAssemblyValueTypes;
import com.oracle.truffle.js.runtime.builtins.wasm.WebAssemblyValueType;
import com.oracle.truffle.js.runtime.objects.Undefined;

public class WebAssemblyGlobalPrototypeBuiltins extends JSBuiltinsContainer.SwitchEnum<WebAssemblyGlobalPrototypeBuiltins.WebAssemblyGlobalPrototype> {
Expand Down Expand Up @@ -136,7 +136,7 @@ protected Object getValue(JSWebAssemblyGlobalObject object,
@Cached InlinedBranchProfile errorBranch,
@Cached ToJSValueNode toJSValueNode,
@CachedLibrary(limit = "InteropLibraryLimit") InteropLibrary globalReadLib) {
if (JSWebAssemblyValueTypes.isV128(object.getValueType())) {
if (object.getValueType() == WebAssemblyValueType.v128) {
errorBranch.enter(this);
v128TypeError();
}
Expand Down Expand Up @@ -177,7 +177,7 @@ protected Object setValue(JSWebAssemblyGlobalObject global, Object[] args,
errorBranch.enter(this);
throw Errors.createTypeError("set WebAssembly.Global.value: Can't set the value of an immutable global");
}
if (JSWebAssemblyValueTypes.isV128(global.getValueType())) {
if (global.getValueType() == WebAssemblyValueType.v128) {
errorBranch.enter(this);
throw Errors.createTypeError("set WebAssembly.Global.value: cannot write value type v128", this);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
Expand Down Expand Up @@ -50,7 +50,6 @@
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.profiles.InlinedBranchProfile;
import com.oracle.truffle.api.strings.TruffleString;
import com.oracle.truffle.js.builtins.JSBuiltinsContainer;
import com.oracle.truffle.js.builtins.wasm.WebAssemblyTablePrototypeBuiltinsFactory.WebAssemblyTableGetLengthNodeGen;
import com.oracle.truffle.js.builtins.wasm.WebAssemblyTablePrototypeBuiltinsFactory.WebAssemblyTableGetNodeGen;
Expand All @@ -68,7 +67,7 @@
import com.oracle.truffle.js.runtime.builtins.BuiltinEnum;
import com.oracle.truffle.js.runtime.builtins.wasm.JSWebAssemblyTable;
import com.oracle.truffle.js.runtime.builtins.wasm.JSWebAssemblyTableObject;
import com.oracle.truffle.js.runtime.builtins.wasm.JSWebAssemblyValueTypes;
import com.oracle.truffle.js.runtime.builtins.wasm.WebAssemblyValueType;
import com.oracle.truffle.js.runtime.objects.Undefined;

public class WebAssemblyTablePrototypeBuiltins extends JSBuiltinsContainer.SwitchEnum<WebAssemblyTablePrototypeBuiltins.WebAssemblyTablePrototype> {
Expand Down Expand Up @@ -143,12 +142,12 @@ protected Object grow(Object thiz, Object delta, Object[] args,
JSWebAssemblyTableObject table = (JSWebAssemblyTableObject) thiz;
int deltaInt = toDeltaNode.executeInt(delta);
Object wasmTable = table.getWASMTable();
TruffleString elementKind = table.getElementKind();
WebAssemblyValueType elementKind = table.getElementKind();

final JSRealm realm = getRealm();
final Object wasmValue;
if (args.length == 0) {
wasmValue = JSWebAssemblyValueTypes.getDefaultValue(realm, elementKind);
wasmValue = elementKind.getDefaultValue(realm);
} else {
wasmValue = toWebAssemblyValueNode.execute(args[0], elementKind);
}
Expand Down Expand Up @@ -220,12 +219,12 @@ protected Object set(Object thiz, Object index, Object[] args,
JSWebAssemblyTableObject table = (JSWebAssemblyTableObject) thiz;
int indexInt = toIndexNode.executeInt(index);
Object wasmTable = table.getWASMTable();
TruffleString elementKind = table.getElementKind();
WebAssemblyValueType elementKind = table.getElementKind();
final JSRealm realm = getRealm();

final Object wasmValue;
if (args.length == 0) {
wasmValue = JSWebAssemblyValueTypes.getDefaultValue(realm, elementKind);
wasmValue = elementKind.getDefaultValue(realm);
} else {
wasmValue = toWebAssemblyValueNode.execute(args[0], elementKind);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,14 @@
*/
package com.oracle.truffle.js.nodes.wasm;

import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.profiles.InlinedBranchProfile;
import com.oracle.truffle.js.nodes.JavaScriptBaseNode;
import com.oracle.truffle.js.nodes.array.ArrayBufferViewGetByteLengthNode;
import com.oracle.truffle.js.nodes.array.GetViewByteLengthNode;
import com.oracle.truffle.js.runtime.Errors;
import com.oracle.truffle.js.runtime.JSContext;
import com.oracle.truffle.js.runtime.JSRealm;
Expand Down Expand Up @@ -77,38 +82,45 @@ public static ExportByteSourceNode create(JSContext context, String nonByteSourc
}

@Specialization
protected Object exportBuffer(JSArrayBufferObject arrayBuffer) {
protected Object exportBuffer(JSArrayBufferObject arrayBuffer,
@Cached @Shared InlinedBranchProfile errorBranch) {
int length;
if (!context.getTypedArrayNotDetachedAssumption().isValid() && JSArrayBuffer.isDetachedBuffer(arrayBuffer)) {
length = 0;
} else {
length = arrayBuffer.getByteLength();
}
return exportBuffer(arrayBuffer, 0, length);
return exportBuffer(arrayBuffer, 0, length, errorBranch);
}

@Specialization
protected Object exportTypedArray(JSTypedArrayObject typedArray) {
protected Object exportTypedArray(JSTypedArrayObject typedArray,
@Cached ArrayBufferViewGetByteLengthNode getByteLengthNode,
@Cached @Shared InlinedBranchProfile errorBranch) {
int offset = JSArrayBufferView.getByteOffset(typedArray, context);
int length = JSArrayBufferView.getByteLength(typedArray, context);
return exportBuffer(typedArray.getArrayBuffer(), offset, length);
int length = getByteLengthNode.executeInt(this, typedArray, context);
return exportBuffer(typedArray.getArrayBuffer(), offset, length, errorBranch);
}

@Specialization
protected Object exportDataView(JSDataViewObject dataView) {
protected Object exportDataView(JSDataViewObject dataView,
@Cached GetViewByteLengthNode getByteLengthNode,
@Cached @Shared InlinedBranchProfile errorBranch) {
int offset = JSDataView.dataViewGetByteOffset(dataView);
int length = JSDataView.dataViewGetByteLength(dataView);
return exportBuffer(dataView.getArrayBuffer(), offset, length);
int length = getByteLengthNode.execute(dataView, context);
return exportBuffer(dataView.getArrayBuffer(), offset, length, errorBranch);
}

@Fallback
protected Object exportOther(@SuppressWarnings("unused") Object other) {
throw Errors.createTypeError(nonByteSourceMessage, this);
}

private Object exportBuffer(JSArrayBufferObject arrayBuffer, int offset, int length) {
private Object exportBuffer(JSArrayBufferObject arrayBuffer, int offset, int length,
InlinedBranchProfile errorBranch) {
JSArrayBufferObject buffer = arrayBuffer;
if (emptyByteSourceMessage != null && length == 0) {
errorBranch.enter(this);
throw Errors.createCompileError(emptyByteSourceMessage, this);
}
JSRealm realm = getRealm();
Expand All @@ -127,7 +139,7 @@ private Object exportBuffer(JSArrayBufferObject arrayBuffer, int offset, int len
bufferType = TypedArray.BUFFER_TYPE_SHARED;
}
TypedArray arrayType = TypedArrayFactory.Uint8Array.createArrayType(bufferType, (offset != 0), true);
JSTypedArrayObject array = JSArrayBufferView.createArrayBufferView(context, realm, buffer, arrayType, offset, length);
JSTypedArrayObject array = JSArrayBufferView.createArrayBufferView(context, realm, buffer, TypedArrayFactory.Uint8Array, arrayType, offset, length);
return new InteropBufferView(buffer, offset, length, array);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.profiles.InlinedBranchProfile;
import com.oracle.truffle.api.strings.TruffleString;
import com.oracle.truffle.js.nodes.JavaScriptBaseNode;
import com.oracle.truffle.js.nodes.cast.JSToBigIntNode;
import com.oracle.truffle.js.nodes.cast.JSToInt32Node;
Expand All @@ -57,52 +56,52 @@
import com.oracle.truffle.js.runtime.JSException;
import com.oracle.truffle.js.runtime.JSRuntime;
import com.oracle.truffle.js.runtime.builtins.wasm.JSWebAssembly;
import com.oracle.truffle.js.runtime.builtins.wasm.JSWebAssemblyValueTypes;
import com.oracle.truffle.js.runtime.builtins.wasm.WebAssemblyValueType;
import com.oracle.truffle.js.runtime.objects.JSDynamicObject;
import com.oracle.truffle.js.runtime.objects.Null;

/**
* Implementation of ToWebAssemblyValue() operation. See
* <a href="https://www.w3.org/TR/wasm-js-api/#towebassemblyvalue">Wasm JS-API Spec</a>
*/
@ImportStatic(JSWebAssemblyValueTypes.class)
@ImportStatic(WebAssemblyValueType.class)
@GenerateUncached
public abstract class ToWebAssemblyValueNode extends JavaScriptBaseNode {

protected ToWebAssemblyValueNode() {
}

public abstract Object execute(Object value, TruffleString type);
public abstract Object execute(Object value, WebAssemblyValueType type);

@Specialization(guards = "isI32(type)")
static int i32(Object value, @SuppressWarnings("unused") TruffleString type,
@Specialization(guards = "type == i32")
static int i32(Object value, @SuppressWarnings("unused") WebAssemblyValueType type,
@Cached JSToInt32Node toInt32Node) {
return toInt32Node.executeInt(value);
}

@Specialization(guards = "isI64(type)")
static long i64(Object value, @SuppressWarnings("unused") TruffleString type,
@Specialization(guards = "type == i64")
static long i64(Object value, @SuppressWarnings("unused") WebAssemblyValueType type,
@Cached JSToBigIntNode toBigIntNode) {
return toBigIntNode.executeBigInteger(value).longValue();
}

@Specialization(guards = "isF32(type)")
static float f32(Object value, @SuppressWarnings("unused") TruffleString type,
@Specialization(guards = "type == f32")
static float f32(Object value, @SuppressWarnings("unused") WebAssemblyValueType type,
@Cached @Shared JSToNumberNode toNumberNode) {
Number numberValue = toNumberNode.executeNumber(value);
double doubleValue = JSRuntime.toDouble(numberValue);
return (float) doubleValue;
}

@Specialization(guards = "isF64(type)")
static double f64(Object value, @SuppressWarnings("unused") TruffleString type,
@Specialization(guards = "type == f64")
static double f64(Object value, @SuppressWarnings("unused") WebAssemblyValueType type,
@Cached @Shared JSToNumberNode toNumberNode) {
Number numberValue = toNumberNode.executeNumber(value);
return JSRuntime.toDouble(numberValue);
}

@Specialization(guards = "isAnyfunc(type)")
final Object anyfunc(Object value, @SuppressWarnings("unused") TruffleString type,
@Specialization(guards = "type == anyfunc")
final Object anyfunc(Object value, @SuppressWarnings("unused") WebAssemblyValueType type,
@Cached InlinedBranchProfile errorBranch) {
if (value == Null.instance) {
return getRealm().getWasmRefNull();
Expand All @@ -120,8 +119,8 @@ private static JSException notAnExportedFunctionError() {
throw Errors.createTypeError("value is not an exported function");
}

@Specialization(guards = "isExternref(type)")
final Object externref(Object value, @SuppressWarnings("unused") TruffleString type) {
@Specialization(guards = "type == externref")
final Object externref(Object value, @SuppressWarnings("unused") WebAssemblyValueType type) {
if (value == Null.instance) {
return getRealm().getWasmRefNull();
} else {
Expand All @@ -131,7 +130,7 @@ final Object externref(Object value, @SuppressWarnings("unused") TruffleString t

@Fallback
@TruffleBoundary
final Object fallback(@SuppressWarnings("unused") Object value, TruffleString type) {
final Object fallback(@SuppressWarnings("unused") Object value, WebAssemblyValueType type) {
throw Errors.createTypeError("Unknown type: " + type, this);
}
}
Loading

0 comments on commit ca38edf

Please sign in to comment.