blob: 49b4a9000865d5870e11138ecae50022015b89b5 [file] [log] [blame]
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Prototype js util library for wasm.
library dart.js_util_wasm;
import 'dart:wasm';
/// [JSValue] is the root of the JS interop object hierarchy.
class JSValue {
final WasmAnyRef _ref;
JSValue(this._ref);
static JSValue? box(WasmAnyRef? ref) => ref == null ? null : JSValue(ref);
WasmAnyRef toAnyRef() => _ref;
String toString() => _jsStringToDartString(_ref);
List<Object?> toObjectList() => _jsArrayToDartList(_ref);
Object toObject() => _jsObjectToDartObject(_ref);
}
/// Raw private JS functions.
external WasmAnyRef _jsObjectFromDartObject(Object object);
external Object _jsObjectToDartObject(WasmAnyRef ref);
@pragma("wasm:import", "dart2wasm.arrayFromDartList")
external WasmAnyRef _jsArrayFromDartList(List<Object?> list);
@pragma("wasm:import", "dart2wasm.arrayToDartList")
external List<Object?> _jsArrayToDartList(WasmAnyRef list);
@pragma("wasm:import", "dart2wasm.stringFromDartString")
external WasmAnyRef _jsStringFromDartString(String string);
@pragma("wasm:import", "dart2wasm.stringToDartString")
external String _jsStringToDartString(WasmAnyRef string);
/// Raw public JS functions.
/// These are public temporarily to give performance conscious users an escape
/// hatch while we decide what this API will actually look like. They may
/// become private in the future, or disappear entirely. For descriptions of the
/// API, please see the corresponding non-raw functions.
@pragma("wasm:import", "dart2wasm.eval")
external void evalRaw(WasmAnyRef code);
@pragma("wasm:import", "dart2wasm.dartify")
external WasmAnyRef? dartifyRaw(WasmAnyRef? object);
@pragma("wasm:import", "dart2wasm.newObject")
external WasmAnyRef newObjectRaw();
@pragma("wasm:import", "dart2wasm.globalThis")
external WasmAnyRef globalThisRaw();
@pragma("wasm:import", "dart2wasm.callConstructorVarArgs")
external WasmAnyRef callConstructorVarArgsRaw(
WasmAnyRef o, WasmAnyRef name, WasmAnyRef args);
@pragma("wasm:import", "dart2wasm.hasProperty")
external bool hasPropertyRaw(WasmAnyRef o, WasmAnyRef name);
@pragma("wasm:import", "dart2wasm.getProperty")
external WasmAnyRef? getPropertyRaw(WasmAnyRef o, WasmAnyRef name);
@pragma("wasm:import", "dart2wasm.setProperty")
external WasmAnyRef? setPropertyRaw(
WasmAnyRef o, WasmAnyRef name, WasmAnyRef? value);
@pragma("wasm:import", "dart2wasm.callMethodVarArgs")
external WasmAnyRef? callMethodVarArgsRaw(
WasmAnyRef o, WasmAnyRef method, WasmAnyRef? args);
WasmAnyRef? jsifyRaw(Object? object) {
if (object == null) {
return null;
} else if (object is JSValue) {
return object.toAnyRef();
} else if (object is String) {
return _jsStringFromDartString(object);
} else if (object is List<Object?>) {
return _jsArrayFromDartList(object);
} else {
return _jsObjectFromDartObject(object);
}
}
/// Conversion functions.
/// TODO(joshualitt): Only a small set of types currently work:
/// JS -> Dart:
/// null
/// strings
/// arrays
/// opaque Dart objects passed to JS
/// Dart -> JS:
/// null
/// boolean
/// doubles
/// strings
/// lists
/// opaque JS objects passed to Dart
/// In the future we would like to support more types, at least maps,
/// and to fix some of the issues returning some types from JS.
/// Extension methods for conversions.
extension StringToJS on String {
JSValue toJS() => JSValue(_jsStringFromDartString(this));
}
extension ListOfObjectToJS on List<Object?> {
JSValue toJS() => JSValue(_jsArrayFromDartList(this));
}
extension ObjectToJS on Object {
JSValue toJS() => JSValue(_jsObjectFromDartObject(this));
}
/// Recursively converts objects from Dart to JS.
JSValue? jsify(Object? object) => JSValue.box(jsifyRaw(object));
/// Recursively converts objects from JS to Dart.
Object? dartify(JSValue? object) => object == null
? null
: _jsObjectToDartObject(dartifyRaw(object.toAnyRef())!);
/// js util methods.
/// These are low level calls into JS, and require care to use correctly.
/// Evals a snippet of JS code in a Dart string.
void eval(String code) => evalRaw(code.toJS().toAnyRef());
/// Creates a new JS object literal and returns it.
JSValue newObject() => JSValue(newObjectRaw());
/// Returns a reference to `globalThis`.
JSValue globalThis() => JSValue(globalThisRaw());
/// Gets a [String] name property off of a JS object [o], invokes it as
/// a constructor with a JS array of arguments [args], and returns the
/// constructed JS object.
JSValue callConstructorVarArgs(JSValue o, String name, List<JSValue?> args) =>
JSValue(callConstructorVarArgsRaw(
o.toAnyRef(), name.toJS().toAnyRef(), args.toJS().toAnyRef()));
/// Checks for a [String] name on a JS object [o].
bool hasProperty(JSValue o, String name) =>
hasPropertyRaw(o.toAnyRef(), name.toJS().toAnyRef());
/// Gets a JS property with [String] name off of a JS object [o].
JSValue? getProperty(JSValue o, String name) =>
JSValue.box(getPropertyRaw(o.toAnyRef(), name.toJS().toAnyRef()));
/// Sets a JS property with [String] name on JS object [o] to the JS value
/// [value], then returns [value].
JSValue? setProperty(JSValue o, String name, JSValue? value) => JSValue.box(
setPropertyRaw(o.toAnyRef(), name.toJS().toAnyRef(), value?.toAnyRef()));
/// Calls a JS method with a [String] name on JS object [o] with a JS array
/// of arguments [args] and returns the resulting JS value.
JSValue? callMethodVarArgs(JSValue o, String method, List<JSValue?> args) =>
JSValue.box(callMethodVarArgsRaw(
o.toAnyRef(), method.toJS().toAnyRef(), args.toJS().toAnyRef()));