Skip to content

Runtime Performance Optimization #207

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Aug 18, 2022
Merged
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
8d17f5c
Allocate function call argument buffer on stack
kateinoigakukun Aug 16, 2022
d684978
Revert "Allocate function call argument buffer on stack"
kateinoigakukun Aug 16, 2022
7f6e3d8
Reduce memory store for returned value kind
kateinoigakukun Aug 16, 2022
50fd47b
Add NODEJS_FLAGS to perform profiling by passing --prof
kateinoigakukun Aug 16, 2022
4f850a0
Revert "Revert "Allocate function call argument buffer on stack""
kateinoigakukun Aug 16, 2022
e66296f
Revert "Revert "Revert "Allocate function call argument buffer on sta…
kateinoigakukun Aug 16, 2022
0f53ca5
Reduce retain/release dance caused by Optional<this>
kateinoigakukun Aug 16, 2022
adfe1e8
Don't escape JSFunction self
kateinoigakukun Aug 16, 2022
78b442d
Add fast path for empty JSValue array
kateinoigakukun Aug 16, 2022
bffe009
Skip re-creating DataView in decodeArray
kateinoigakukun Aug 16, 2022
2a8146b
make regenerate_swiftpm_resources
kateinoigakukun Aug 16, 2022
43d9a2a
Apply the same techniques to call families
kateinoigakukun Aug 17, 2022
2b86b41
Reuse DataView as much as possible
kateinoigakukun Aug 17, 2022
5a3ecf2
npm run format
kateinoigakukun Aug 17, 2022
8184119
Optimize swjs_get_prop to reduce memory store
kateinoigakukun Aug 18, 2022
a1b690e
Optimize _get_subscript to reduce memory store
kateinoigakukun Aug 18, 2022
4110f7f
Rename writeV2 -> writeAndReturnKindBits
kateinoigakukun Aug 18, 2022
d4c45b4
Improve doc comment style
kateinoigakukun Aug 18, 2022
7532e6a
Use writeAndReturnKindBits in write
kateinoigakukun Aug 18, 2022
a9843ee
Add rationale comments for write
kateinoigakukun Aug 18, 2022
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
3 changes: 2 additions & 1 deletion IntegrationTests/Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
CONFIGURATION ?= debug
SWIFT_BUILD_FLAGS ?=
NODEJS_FLAGS ?=

NODEJS = node --experimental-wasi-unstable-preview1
NODEJS = node --experimental-wasi-unstable-preview1 $(NODEJS_FLAGS)

FORCE:
TestSuites/.build/$(CONFIGURATION)/%.wasm: FORCE
190 changes: 77 additions & 113 deletions Runtime/src/index.ts
Original file line number Diff line number Diff line change
@@ -84,21 +84,15 @@ export class SwiftRuntime {
) {
const argc = args.length;
const argv = this.exports.swjs_prepare_host_function_call(argc);
const memory = this.memory;
for (let index = 0; index < args.length; index++) {
const argument = args[index];
const base = argv + 16 * index;
JSValue.write(
argument,
base,
base + 4,
base + 8,
false,
this.memory
);
JSValue.write(argument, base, base + 4, base + 8, false, memory);
}
let output: any;
// This ref is released by the swjs_call_host_function implementation
const callback_func_ref = this.memory.retain((result: any) => {
const callback_func_ref = memory.retain((result: any) => {
output = result;
});
const alreadyReleased = this.exports.swjs_call_host_function(
@@ -127,28 +121,28 @@ export class SwiftRuntime {
payload1: number,
payload2: number
) => {
const obj = this.memory.getObject(ref);
const key = this.memory.getObject(name);
const value = JSValue.decode(kind, payload1, payload2, this.memory);
const memory = this.memory;
const obj = memory.getObject(ref);
const key = memory.getObject(name);
const value = JSValue.decode(kind, payload1, payload2, memory);
obj[key] = value;
},
swjs_get_prop: (
ref: ref,
name: ref,
kind_ptr: pointer,
payload1_ptr: pointer,
payload2_ptr: pointer
) => {
const obj = this.memory.getObject(ref);
const key = this.memory.getObject(name);
const memory = this.memory;
const obj = memory.getObject(ref);
const key = memory.getObject(name);
const result = obj[key];
JSValue.write(
return JSValue.writeAndReturnKindBits(
result,
kind_ptr,
payload1_ptr,
payload2_ptr,
false,
this.memory
memory
);
},

@@ -159,22 +153,21 @@ export class SwiftRuntime {
payload1: number,
payload2: number
) => {
const obj = this.memory.getObject(ref);
const value = JSValue.decode(kind, payload1, payload2, this.memory);
const memory = this.memory;
const obj = memory.getObject(ref);
const value = JSValue.decode(kind, payload1, payload2, memory);
obj[index] = value;
},
swjs_get_subscript: (
ref: ref,
index: number,
kind_ptr: pointer,
payload1_ptr: pointer,
payload2_ptr: pointer
) => {
const obj = this.memory.getObject(ref);
const result = obj[index];
JSValue.write(
return JSValue.writeAndReturnKindBits(
result,
kind_ptr,
payload1_ptr,
payload2_ptr,
false,
@@ -183,50 +176,50 @@ export class SwiftRuntime {
},

swjs_encode_string: (ref: ref, bytes_ptr_result: pointer) => {
const bytes = this.textEncoder.encode(this.memory.getObject(ref));
const bytes_ptr = this.memory.retain(bytes);
this.memory.writeUint32(bytes_ptr_result, bytes_ptr);
const memory = this.memory;
const bytes = this.textEncoder.encode(memory.getObject(ref));
const bytes_ptr = memory.retain(bytes);
memory.writeUint32(bytes_ptr_result, bytes_ptr);
return bytes.length;
},
swjs_decode_string: (bytes_ptr: pointer, length: number) => {
const bytes = this.memory
const memory = this.memory;
const bytes = memory
.bytes()
.subarray(bytes_ptr, bytes_ptr + length);
const string = this.textDecoder.decode(bytes);
return this.memory.retain(string);
return memory.retain(string);
},
swjs_load_string: (ref: ref, buffer: pointer) => {
const bytes = this.memory.getObject(ref);
this.memory.writeBytes(buffer, bytes);
const memory = this.memory;
const bytes = memory.getObject(ref);
memory.writeBytes(buffer, bytes);
},

swjs_call_function: (
ref: ref,
argv: pointer,
argc: number,
kind_ptr: pointer,
payload1_ptr: pointer,
payload2_ptr: pointer
) => {
const func = this.memory.getObject(ref);
let result: any;
const memory = this.memory;
const func = memory.getObject(ref);
let result = undefined;
try {
const args = JSValue.decodeArray(argv, argc, this.memory);
const args = JSValue.decodeArray(argv, argc, memory);
result = func(...args);
} catch (error) {
JSValue.write(
return JSValue.writeAndReturnKindBits(
error,
kind_ptr,
payload1_ptr,
payload2_ptr,
true,
this.memory
);
return;
}
JSValue.write(
return JSValue.writeAndReturnKindBits(
result,
kind_ptr,
payload1_ptr,
payload2_ptr,
false,
@@ -237,67 +230,48 @@ export class SwiftRuntime {
ref: ref,
argv: pointer,
argc: number,
kind_ptr: pointer,
payload1_ptr: pointer,
payload2_ptr: pointer
) => {
const func = this.memory.getObject(ref);
let isException = true;
try {
const args = JSValue.decodeArray(argv, argc, this.memory);
const result = func(...args);
JSValue.write(
result,
kind_ptr,
payload1_ptr,
payload2_ptr,
false,
this.memory
);
isException = false;
} finally {
if (isException) {
JSValue.write(
undefined,
kind_ptr,
payload1_ptr,
payload2_ptr,
true,
this.memory
);
}
}
const memory = this.memory;
const func = memory.getObject(ref);
const args = JSValue.decodeArray(argv, argc, memory);
const result = func(...args);
return JSValue.writeAndReturnKindBits(
result,
payload1_ptr,
payload2_ptr,
false,
this.memory
);
},

swjs_call_function_with_this: (
obj_ref: ref,
func_ref: ref,
argv: pointer,
argc: number,
kind_ptr: pointer,
payload1_ptr: pointer,
payload2_ptr: pointer
) => {
const obj = this.memory.getObject(obj_ref);
const func = this.memory.getObject(func_ref);
const memory = this.memory;
const obj = memory.getObject(obj_ref);
const func = memory.getObject(func_ref);
let result: any;
try {
const args = JSValue.decodeArray(argv, argc, this.memory);
const args = JSValue.decodeArray(argv, argc, memory);
result = func.apply(obj, args);
} catch (error) {
JSValue.write(
return JSValue.writeAndReturnKindBits(
error,
kind_ptr,
payload1_ptr,
payload2_ptr,
true,
this.memory
);
return;
}
JSValue.write(
return JSValue.writeAndReturnKindBits(
result,
kind_ptr,
payload1_ptr,
payload2_ptr,
false,
@@ -309,42 +283,28 @@ export class SwiftRuntime {
func_ref: ref,
argv: pointer,
argc: number,
kind_ptr: pointer,
payload1_ptr: pointer,
payload2_ptr: pointer
) => {
const obj = this.memory.getObject(obj_ref);
const func = this.memory.getObject(func_ref);
let isException = true;
try {
const args = JSValue.decodeArray(argv, argc, this.memory);
const result = func.apply(obj, args);
JSValue.write(
result,
kind_ptr,
payload1_ptr,
payload2_ptr,
false,
this.memory
);
isException = false;
} finally {
if (isException) {
JSValue.write(
undefined,
kind_ptr,
payload1_ptr,
payload2_ptr,
true,
this.memory
);
}
}
const memory = this.memory;
const obj = memory.getObject(obj_ref);
const func = memory.getObject(func_ref);
let result = undefined;
const args = JSValue.decodeArray(argv, argc, memory);
result = func.apply(obj, args);
return JSValue.writeAndReturnKindBits(
result,
payload1_ptr,
payload2_ptr,
false,
this.memory
);
},

swjs_call_new: (ref: ref, argv: pointer, argc: number) => {
const constructor = this.memory.getObject(ref);
const args = JSValue.decodeArray(argv, argc, this.memory);
const memory = this.memory;
const constructor = memory.getObject(ref);
const args = JSValue.decodeArray(argv, argc, memory);
const instance = new constructor(...args);
return this.memory.retain(instance);
},
@@ -356,10 +316,11 @@ export class SwiftRuntime {
exception_payload1_ptr: pointer,
exception_payload2_ptr: pointer
) => {
const constructor = this.memory.getObject(ref);
let memory = this.memory;
const constructor = memory.getObject(ref);
let result: any;
try {
const args = JSValue.decodeArray(argv, argc, this.memory);
const args = JSValue.decodeArray(argv, argc, memory);
result = new constructor(...args);
} catch (error) {
JSValue.write(
@@ -372,20 +333,22 @@ export class SwiftRuntime {
);
return -1;
}
memory = this.memory;
JSValue.write(
null,
exception_kind_ptr,
exception_payload1_ptr,
exception_payload2_ptr,
false,
this.memory
memory
);
return this.memory.retain(result);
return memory.retain(result);
},

swjs_instanceof: (obj_ref: ref, constructor_ref: ref) => {
const obj = this.memory.getObject(obj_ref);
const constructor = this.memory.getObject(constructor_ref);
const memory = this.memory;
const obj = memory.getObject(obj_ref);
const constructor = memory.getObject(constructor_ref);
return obj instanceof constructor;
},

@@ -419,9 +382,10 @@ export class SwiftRuntime {
},

swjs_load_typed_array: (ref: ref, buffer: pointer) => {
const typedArray = this.memory.getObject(ref);
const memory = this.memory;
const typedArray = memory.getObject(ref);
const bytes = new Uint8Array(typedArray.buffer);
this.memory.writeBytes(buffer, bytes);
memory.writeBytes(buffer, bytes);
},

swjs_release: (ref: ref) => {
61 changes: 38 additions & 23 deletions Runtime/src/js-value.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Memory } from "./memory.js";
import { assertNever, pointer } from "./types.js";
import { assertNever, JavaScriptValueKindAndFlags, pointer } from "./types.js";

export const enum Kind {
Boolean = 0,
@@ -51,17 +51,29 @@ export const decode = (
// Note:
// `decodeValues` assumes that the size of RawJSValue is 16.
export const decodeArray = (ptr: pointer, length: number, memory: Memory) => {
// fast path for empty array
if (length === 0) {
return [];
}

let result = [];
// It's safe to hold DataView here because WebAssembly.Memory.buffer won't
// change within this function.
const view = memory.dataView();
for (let index = 0; index < length; index++) {
const base = ptr + 16 * index;
const kind = memory.readUint32(base);
const payload1 = memory.readUint32(base + 4);
const payload2 = memory.readFloat64(base + 8);
const kind = view.getUint32(base, true);
const payload1 = view.getUint32(base + 4, true);
const payload2 = view.getFloat64(base + 8, true);
result.push(decode(kind, payload1, payload2, memory));
}
return result;
};

// A helper function to encode a RawJSValue into a pointers.
// Please prefer to use `writeAndReturnKindBits` to avoid unnecessary
// memory stores.
// This function should be used only when kind flag is stored in memory.
export const write = (
value: any,
kind_ptr: pointer,
@@ -70,54 +82,57 @@ export const write = (
is_exception: boolean,
memory: Memory
) => {
const kind = writeAndReturnKindBits(value, payload1_ptr, payload2_ptr, is_exception, memory);
memory.writeUint32(kind_ptr, kind);
};

export const writeAndReturnKindBits = (
value: any,
payload1_ptr: pointer,
payload2_ptr: pointer,
is_exception: boolean,
memory: Memory
): JavaScriptValueKindAndFlags => {
const exceptionBit = (is_exception ? 1 : 0) << 31;
if (value === null) {
memory.writeUint32(kind_ptr, exceptionBit | Kind.Null);
return;
return exceptionBit | Kind.Null;
}

const writeRef = (kind: Kind) => {
memory.writeUint32(kind_ptr, exceptionBit | kind);
memory.writeUint32(payload1_ptr, memory.retain(value));
return exceptionBit | kind;
};

const type = typeof value;
switch (type) {
case "boolean": {
memory.writeUint32(kind_ptr, exceptionBit | Kind.Boolean);
memory.writeUint32(payload1_ptr, value ? 1 : 0);
break;
return exceptionBit | Kind.Boolean;
}
case "number": {
memory.writeUint32(kind_ptr, exceptionBit | Kind.Number);
memory.writeFloat64(payload2_ptr, value);
break;
return exceptionBit | Kind.Number;
}
case "string": {
writeRef(Kind.String);
break;
return writeRef(Kind.String);
}
case "undefined": {
memory.writeUint32(kind_ptr, exceptionBit | Kind.Undefined);
break;
return exceptionBit | Kind.Undefined;
}
case "object": {
writeRef(Kind.Object);
break;
return writeRef(Kind.Object);
}
case "function": {
writeRef(Kind.Function);
break;
return writeRef(Kind.Function);
}
case "symbol": {
writeRef(Kind.Symbol);
break;
return writeRef(Kind.Symbol);
}
case "bigint": {
writeRef(Kind.BigInt);
break;
return writeRef(Kind.BigInt);
}
default:
assertNever(type, `Type "${type}" is not supported yet`);
}
throw new Error("Unreachable");
};
20 changes: 8 additions & 12 deletions Runtime/src/types.ts
Original file line number Diff line number Diff line change
@@ -3,6 +3,8 @@ import * as JSValue from "./js-value.js";
export type ref = number;
export type pointer = number;
export type bool = number;
export type JavaScriptValueKind = number;
export type JavaScriptValueKindAndFlags = number;

export interface ExportedFunctions {
swjs_library_version(): number;
@@ -30,10 +32,9 @@ export interface ImportedFunctions {
swjs_get_prop(
ref: number,
name: number,
kind_ptr: pointer,
payload1_ptr: pointer,
payload2_ptr: pointer
): void;
): JavaScriptValueKind;
swjs_set_subscript(
ref: number,
index: number,
@@ -44,47 +45,42 @@ export interface ImportedFunctions {
swjs_get_subscript(
ref: number,
index: number,
kind_ptr: pointer,
payload1_ptr: pointer,
payload2_ptr: pointer
): void;
): JavaScriptValueKind;
swjs_encode_string(ref: number, bytes_ptr_result: pointer): number;
swjs_decode_string(bytes_ptr: pointer, length: number): number;
swjs_load_string(ref: number, buffer: pointer): void;
swjs_call_function(
ref: number,
argv: pointer,
argc: number,
kind_ptr: pointer,
payload1_ptr: pointer,
payload2_ptr: pointer
): void;
): JavaScriptValueKindAndFlags;
swjs_call_function_no_catch(
ref: number,
argv: pointer,
argc: number,
kind_ptr: pointer,
payload1_ptr: pointer,
payload2_ptr: pointer
): void;
): JavaScriptValueKindAndFlags;
swjs_call_function_with_this(
obj_ref: ref,
func_ref: ref,
argv: pointer,
argc: number,
kind_ptr: pointer,
payload1_ptr: pointer,
payload2_ptr: pointer
): void;
): JavaScriptValueKindAndFlags;
swjs_call_function_with_this_no_catch(
obj_ref: ref,
func_ref: ref,
argv: pointer,
argc: number,
kind_ptr: pointer,
payload1_ptr: pointer,
payload2_ptr: pointer
): void;
): JavaScriptValueKindAndFlags;
swjs_call_new(ref: number, argv: pointer, argc: number): number;
swjs_call_throwing_new(
ref: number,
3 changes: 3 additions & 0 deletions Sources/JavaScriptKit/ConvertibleToJSValue.swift
Original file line number Diff line number Diff line change
@@ -253,6 +253,9 @@ extension JSValue {

extension Array where Element == ConvertibleToJSValue {
func withRawJSValues<T>(_ body: ([RawJSValue]) -> T) -> T {
// fast path for empty array
guard self.count != 0 else { return body([]) }

func _withRawJSValues<T>(
_ values: [ConvertibleToJSValue], _ index: Int,
_ results: inout [RawJSValue], _ body: ([RawJSValue]) -> T
76 changes: 53 additions & 23 deletions Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift
Original file line number Diff line number Diff line change
@@ -17,16 +17,31 @@ public class JSFunction: JSObject {
/// - arguments: Arguments to be passed to this function.
/// - Returns: The result of this call.
@discardableResult
public func callAsFunction(this: JSObject? = nil, arguments: [ConvertibleToJSValue]) -> JSValue {
invokeNonThrowingJSFunction(self, arguments: arguments, this: this).jsValue
public func callAsFunction(this: JSObject, arguments: [ConvertibleToJSValue]) -> JSValue {
invokeNonThrowingJSFunction(arguments: arguments, this: this).jsValue
}

/// Call this function with given `arguments`.
/// - Parameters:
/// - arguments: Arguments to be passed to this function.
/// - Returns: The result of this call.
@discardableResult
public func callAsFunction(arguments: [ConvertibleToJSValue]) -> JSValue {
invokeNonThrowingJSFunction(arguments: arguments).jsValue
}

/// A variadic arguments version of `callAsFunction`.
@discardableResult
public func callAsFunction(this: JSObject? = nil, _ arguments: ConvertibleToJSValue...) -> JSValue {
public func callAsFunction(this: JSObject, _ arguments: ConvertibleToJSValue...) -> JSValue {
self(this: this, arguments: arguments)
}

/// A variadic arguments version of `callAsFunction`.
@discardableResult
public func callAsFunction(_ arguments: ConvertibleToJSValue...) -> JSValue {
self(arguments: arguments)
}

/// Instantiate an object from this function as a constructor.
///
/// Guaranteed to return an object because either:
@@ -81,29 +96,44 @@ public class JSFunction: JSObject {
override public var jsValue: JSValue {
.function(self)
}
}

func invokeNonThrowingJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleToJSValue], this: JSObject?) -> RawJSValue {
arguments.withRawJSValues { rawValues in
rawValues.withUnsafeBufferPointer { bufferPointer in
let argv = bufferPointer.baseAddress
let argc = bufferPointer.count
var kindAndFlags = JavaScriptValueKindAndFlags()
var payload1 = JavaScriptPayload1()
var payload2 = JavaScriptPayload2()
if let thisId = this?.id {
_call_function_with_this_no_catch(thisId,
jsFunc.id, argv, Int32(argc),
&kindAndFlags, &payload1, &payload2)
} else {
_call_function_no_catch(
jsFunc.id, argv, Int32(argc),
&kindAndFlags, &payload1, &payload2
final func invokeNonThrowingJSFunction(arguments: [ConvertibleToJSValue]) -> RawJSValue {
let id = self.id
return arguments.withRawJSValues { rawValues in
rawValues.withUnsafeBufferPointer { bufferPointer in
let argv = bufferPointer.baseAddress
let argc = bufferPointer.count
var payload1 = JavaScriptPayload1()
var payload2 = JavaScriptPayload2()
let resultBitPattern = _call_function_no_catch(
id, argv, Int32(argc),
&payload1, &payload2
)
let kindAndFlags = unsafeBitCast(resultBitPattern, to: JavaScriptValueKindAndFlags.self)
assert(!kindAndFlags.isException)
let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2)
return result
}
}
}

final func invokeNonThrowingJSFunction(arguments: [ConvertibleToJSValue], this: JSObject) -> RawJSValue {
let id = self.id
return arguments.withRawJSValues { rawValues in
rawValues.withUnsafeBufferPointer { bufferPointer in
let argv = bufferPointer.baseAddress
let argc = bufferPointer.count
var payload1 = JavaScriptPayload1()
var payload2 = JavaScriptPayload2()
let resultBitPattern = _call_function_with_this_no_catch(this.id,
id, argv, Int32(argc),
&payload1, &payload2
)
let kindAndFlags = unsafeBitCast(resultBitPattern, to: JavaScriptValueKindAndFlags.self)
assert(!kindAndFlags.isException)
let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2)
return result
}
assert(!kindAndFlags.isException)
let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2)
return result
}
}
}
2 changes: 1 addition & 1 deletion Sources/JavaScriptKit/FundamentalObjects/JSSymbol.swift
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ public class JSSymbol: JSObject {

public init(_ description: JSString) {
// can’t do `self =` so we have to get the ID manually
let result = invokeNonThrowingJSFunction(Symbol, arguments: [description], this: nil)
let result = Symbol.invokeNonThrowingJSFunction(arguments: [description])
precondition(result.kind == .symbol)
super.init(id: UInt32(result.payload1))
}
16 changes: 10 additions & 6 deletions Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift
Original file line number Diff line number Diff line change
@@ -64,6 +64,7 @@ public class JSThrowingFunction {
}

private func invokeJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleToJSValue], this: JSObject?) throws -> JSValue {
let id = jsFunc.id
let (result, isException) = arguments.withRawJSValues { rawValues in
rawValues.withUnsafeBufferPointer { bufferPointer -> (JSValue, Bool) in
let argv = bufferPointer.baseAddress
@@ -72,14 +73,17 @@ private func invokeJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleToJSV
var payload1 = JavaScriptPayload1()
var payload2 = JavaScriptPayload2()
if let thisId = this?.id {
_call_function_with_this(thisId,
jsFunc.id, argv, Int32(argc),
&kindAndFlags, &payload1, &payload2)
let resultBitPattern = _call_function_with_this(
thisId, id, argv, Int32(argc),
&payload1, &payload2
)
kindAndFlags = unsafeBitCast(resultBitPattern, to: JavaScriptValueKindAndFlags.self)
} else {
_call_function(
jsFunc.id, argv, Int32(argc),
&kindAndFlags, &payload1, &payload2
let resultBitPattern = _call_function(
id, argv, Int32(argc),
&payload1, &payload2
)
kindAndFlags = unsafeBitCast(resultBitPattern, to: JavaScriptValueKindAndFlags.self)
}
let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2)
return (result.jsValue, kindAndFlags.isException)
24 changes: 15 additions & 9 deletions Sources/JavaScriptKit/JSValue.swift
Original file line number Diff line number Diff line change
@@ -196,9 +196,11 @@ extension JSValue: ExpressibleByNilLiteral {

public func getJSValue(this: JSObject, name: JSString) -> JSValue {
var rawValue = RawJSValue()
_get_prop(this.id, name.asInternalJSRef(),
&rawValue.kind,
&rawValue.payload1, &rawValue.payload2)
let rawBitPattern = _get_prop(
this.id, name.asInternalJSRef(),
&rawValue.payload1, &rawValue.payload2
)
rawValue.kind = unsafeBitCast(rawBitPattern, to: JavaScriptValueKind.self)
return rawValue.jsValue
}

@@ -210,9 +212,11 @@ public func setJSValue(this: JSObject, name: JSString, value: JSValue) {

public func getJSValue(this: JSObject, index: Int32) -> JSValue {
var rawValue = RawJSValue()
_get_subscript(this.id, index,
&rawValue.kind,
&rawValue.payload1, &rawValue.payload2)
let rawBitPattern = _get_subscript(
this.id, index,
&rawValue.payload1, &rawValue.payload2
)
rawValue.kind = unsafeBitCast(rawBitPattern, to: JavaScriptValueKind.self)
return rawValue.jsValue
}

@@ -226,9 +230,11 @@ public func setJSValue(this: JSObject, index: Int32, value: JSValue) {

public func getJSValue(this: JSObject, symbol: JSSymbol) -> JSValue {
var rawValue = RawJSValue()
_get_prop(this.id, symbol.id,
&rawValue.kind,
&rawValue.payload1, &rawValue.payload2)
let rawBitPattern = _get_prop(
this.id, symbol.id,
&rawValue.payload1, &rawValue.payload2
)
rawValue.kind = unsafeBitCast(rawBitPattern, to: JavaScriptValueKind.self)
return rawValue.jsValue
}

194 changes: 99 additions & 95 deletions Sources/JavaScriptKit/Runtime/index.js

Large diffs are not rendered by default.

194 changes: 99 additions & 95 deletions Sources/JavaScriptKit/Runtime/index.mjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 6 additions & 12 deletions Sources/JavaScriptKit/XcodeSupport.swift
Original file line number Diff line number Diff line change
@@ -16,10 +16,9 @@ import _CJavaScriptKit
func _get_prop(
_: JavaScriptObjectRef,
_: JavaScriptObjectRef,
_: UnsafeMutablePointer<JavaScriptValueKind>!,
_: UnsafeMutablePointer<JavaScriptPayload1>!,
_: UnsafeMutablePointer<JavaScriptPayload2>!
) { fatalError() }
) -> UInt32 { fatalError() }
func _set_subscript(
_: JavaScriptObjectRef,
_: Int32,
@@ -30,10 +29,9 @@ import _CJavaScriptKit
func _get_subscript(
_: JavaScriptObjectRef,
_: Int32,
_: UnsafeMutablePointer<JavaScriptValueKind>!,
_: UnsafeMutablePointer<JavaScriptPayload1>!,
_: UnsafeMutablePointer<JavaScriptPayload2>!
) { fatalError() }
) -> UInt32 { fatalError() }
func _encode_string(
_: JavaScriptObjectRef,
_: UnsafeMutablePointer<JavaScriptObjectRef>!
@@ -52,33 +50,29 @@ import _CJavaScriptKit
func _call_function(
_: JavaScriptObjectRef,
_: UnsafePointer<RawJSValue>!, _: Int32,
_: UnsafeMutablePointer<JavaScriptValueKindAndFlags>!,
_: UnsafeMutablePointer<JavaScriptPayload1>!,
_: UnsafeMutablePointer<JavaScriptPayload2>!
) { fatalError() }
) -> UInt32 { fatalError() }
func _call_function_no_catch(
_: JavaScriptObjectRef,
_: UnsafePointer<RawJSValue>!, _: Int32,
_: UnsafeMutablePointer<JavaScriptValueKindAndFlags>!,
_: UnsafeMutablePointer<JavaScriptPayload1>!,
_: UnsafeMutablePointer<JavaScriptPayload2>!
) { fatalError() }
) -> UInt32 { fatalError() }
func _call_function_with_this(
_: JavaScriptObjectRef,
_: JavaScriptObjectRef,
_: UnsafePointer<RawJSValue>!, _: Int32,
_: UnsafeMutablePointer<JavaScriptValueKindAndFlags>!,
_: UnsafeMutablePointer<JavaScriptPayload1>!,
_: UnsafeMutablePointer<JavaScriptPayload2>!
) { fatalError() }
) -> UInt32 { fatalError() }
func _call_function_with_this_no_catch(
_: JavaScriptObjectRef,
_: JavaScriptObjectRef,
_: UnsafePointer<RawJSValue>!, _: Int32,
_: UnsafeMutablePointer<JavaScriptValueKindAndFlags>!,
_: UnsafeMutablePointer<JavaScriptPayload1>!,
_: UnsafeMutablePointer<JavaScriptPayload2>!
) { fatalError() }
) -> UInt32 { fatalError() }
func _call_new(
_: JavaScriptObjectRef,
_: UnsafePointer<RawJSValue>!, _: Int32
81 changes: 45 additions & 36 deletions Sources/_CJavaScriptKit/include/_CJavaScriptKit.h
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@

#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>

/// `JavaScriptObjectRef` represents JavaScript object reference that is referenced by Swift side.
/// This value is an address of `SwiftRuntimeHeap`.
@@ -92,16 +93,17 @@ extern void _set_prop(const JavaScriptObjectRef _this,
///
/// @param _this The target JavaScript object to get its member value.
/// @param prop A JavaScript string object to reference a member of `_this` object.
/// @param kind A result pointer of JavaScript value kind to get.
/// @param payload1 A result pointer of first payload of JavaScript value to set the target object.
/// @param payload2 A result pointer of second payload of JavaScript value to set the target object.
/// @return A `JavaScriptValueKind` bits represented as 32bit integer for the returned value.
__attribute__((__import_module__("javascript_kit"),
__import_name__("swjs_get_prop")))
extern void _get_prop(const JavaScriptObjectRef _this,
const JavaScriptObjectRef prop,
JavaScriptValueKind *kind,
JavaScriptPayload1 *payload1,
JavaScriptPayload2 *payload2);
extern uint32_t _get_prop(
const JavaScriptObjectRef _this,
const JavaScriptObjectRef prop,
JavaScriptPayload1 *payload1,
JavaScriptPayload2 *payload2
);

/// `_set_subscript` sets a value of `_this` JavaScript object.
///
@@ -122,16 +124,17 @@ extern void _set_subscript(const JavaScriptObjectRef _this,
///
/// @param _this The target JavaScript object to get its member value.
/// @param index A subscript index to get value.
/// @param kind A result pointer of JavaScript value kind to get.
/// @param payload1 A result pointer of first payload of JavaScript value to get the target object.
/// @param payload2 A result pointer of second payload of JavaScript value to get the target object.
/// @return A `JavaScriptValueKind` bits represented as 32bit integer for the returned value.
__attribute__((__import_module__("javascript_kit"),
__import_name__("swjs_get_subscript")))
extern void _get_subscript(const JavaScriptObjectRef _this,
const int index,
JavaScriptValueKind *kind,
JavaScriptPayload1 *payload1,
JavaScriptPayload2 *payload2);
extern uint32_t _get_subscript(
const JavaScriptObjectRef _this,
const int index,
JavaScriptPayload1 *payload1,
JavaScriptPayload2 *payload2
);

/// `_encode_string` encodes the `str_obj` to bytes sequence and returns the length of bytes.
///
@@ -175,66 +178,72 @@ extern JavaScriptObjectRef _i64_to_bigint_slow(unsigned int lower, unsigned int
/// @param ref The target JavaScript function to call.
/// @param argv A list of `RawJSValue` arguments to apply.
/// @param argc The length of `argv``.
/// @param result_kind A result pointer of JavaScript value kind of returned result or thrown exception.
/// @param result_payload1 A result pointer of first payload of JavaScript value of returned result or thrown exception.
/// @param result_payload2 A result pointer of second payload of JavaScript value of returned result or thrown exception.
/// @return A `JavaScriptValueKindAndFlags` bits represented as 32bit integer for the returned value.
__attribute__((__import_module__("javascript_kit"),
__import_name__("swjs_call_function")))
extern void _call_function(const JavaScriptObjectRef ref, const RawJSValue *argv,
const int argc, JavaScriptValueKindAndFlags *result_kind,
JavaScriptPayload1 *result_payload1,
JavaScriptPayload2 *result_payload2);
extern uint32_t _call_function(
const JavaScriptObjectRef ref, const RawJSValue *argv,
const int argc,
JavaScriptPayload1 *result_payload1,
JavaScriptPayload2 *result_payload2
);

/// `_call_function` calls JavaScript function with given arguments list without capturing any exception
///
/// @param ref The target JavaScript function to call.
/// @param argv A list of `RawJSValue` arguments to apply.
/// @param argc The length of `argv``.
/// @param result_kind A result pointer of JavaScript value kind of returned result or thrown exception.
/// @param result_payload1 A result pointer of first payload of JavaScript value of returned result or thrown exception.
/// @param result_payload2 A result pointer of second payload of JavaScript value of returned result or thrown exception.
/// @return A `JavaScriptValueKindAndFlags` bits represented as 32bit integer for the returned value.
__attribute__((__import_module__("javascript_kit"),
__import_name__("swjs_call_function_no_catch")))
extern void _call_function_no_catch(const JavaScriptObjectRef ref, const RawJSValue *argv,
const int argc, JavaScriptValueKindAndFlags *result_kind,
JavaScriptPayload1 *result_payload1,
JavaScriptPayload2 *result_payload2);
extern uint32_t _call_function_no_catch(
const JavaScriptObjectRef ref, const RawJSValue *argv,
const int argc,
JavaScriptPayload1 *result_payload1,
JavaScriptPayload2 *result_payload2
);

/// `_call_function_with_this` calls JavaScript function with given arguments list and given `_this`.
///
/// @param _this The value of `this` provided for the call to `func_ref`.
/// @param func_ref The target JavaScript function to call.
/// @param argv A list of `RawJSValue` arguments to apply.
/// @param argc The length of `argv``.
/// @param result_kind A result pointer of JavaScript value kind of returned result or thrown exception.
/// @param result_payload1 A result pointer of first payload of JavaScript value of returned result or thrown exception.
/// @param result_payload2 A result pointer of second payload of JavaScript value of returned result or thrown exception.
/// @return A `JavaScriptValueKindAndFlags` bits represented as 32bit integer for the returned value.
__attribute__((__import_module__("javascript_kit"),
__import_name__("swjs_call_function_with_this")))
extern void _call_function_with_this(const JavaScriptObjectRef _this,
const JavaScriptObjectRef func_ref,
const RawJSValue *argv, const int argc,
JavaScriptValueKindAndFlags *result_kind,
JavaScriptPayload1 *result_payload1,
JavaScriptPayload2 *result_payload2);
extern uint32_t _call_function_with_this(
const JavaScriptObjectRef _this,
const JavaScriptObjectRef func_ref,
const RawJSValue *argv, const int argc,
JavaScriptPayload1 *result_payload1,
JavaScriptPayload2 *result_payload2
);

/// `_call_function_with_this` calls JavaScript function with given arguments list and given `_this` without capturing any exception.
///
/// @param _this The value of `this` provided for the call to `func_ref`.
/// @param func_ref The target JavaScript function to call.
/// @param argv A list of `RawJSValue` arguments to apply.
/// @param argc The length of `argv``.
/// @param result_kind A result pointer of JavaScript value kind of returned result or thrown exception.
/// @param result_payload1 A result pointer of first payload of JavaScript value of returned result or thrown exception.
/// @param result_payload2 A result pointer of second payload of JavaScript value of returned result or thrown exception.
/// @return A `JavaScriptValueKindAndFlags` bits represented as 32bit integer for the returned value.
__attribute__((__import_module__("javascript_kit"),
__import_name__("swjs_call_function_with_this_no_catch")))
extern void _call_function_with_this_no_catch(const JavaScriptObjectRef _this,
const JavaScriptObjectRef func_ref,
const RawJSValue *argv, const int argc,
JavaScriptValueKindAndFlags *result_kind,
JavaScriptPayload1 *result_payload1,
JavaScriptPayload2 *result_payload2);
extern uint32_t _call_function_with_this_no_catch(
const JavaScriptObjectRef _this,
const JavaScriptObjectRef func_ref,
const RawJSValue *argv, const int argc,
JavaScriptPayload1 *result_payload1,
JavaScriptPayload2 *result_payload2
);

/// `_call_new` calls JavaScript object constructor with given arguments list.
///