From 8d17f5c1c78a20f5814c2eb1280569c2289c7f59 Mon Sep 17 00:00:00 2001
From: Yuta Saito <kateinoigakukun@gmail.com>
Date: Tue, 16 Aug 2022 22:02:32 +0900
Subject: [PATCH 01/20] Allocate function call argument buffer on stack

---
 .../JavaScriptKit/ConvertibleToJSValue.swift  | 36 +++++-----
 .../FundamentalObjects/JSFunction.swift       | 44 ++++++-------
 .../FundamentalObjects/JSString.swift         |  5 +-
 .../JSThrowingFunction.swift                  | 66 +++++++++----------
 Sources/JavaScriptKit/JSValue.swift           | 19 +++---
 5 files changed, 79 insertions(+), 91 deletions(-)

diff --git a/Sources/JavaScriptKit/ConvertibleToJSValue.swift b/Sources/JavaScriptKit/ConvertibleToJSValue.swift
index 572e867b0..95f96a2f7 100644
--- a/Sources/JavaScriptKit/ConvertibleToJSValue.swift
+++ b/Sources/JavaScriptKit/ConvertibleToJSValue.swift
@@ -213,7 +213,7 @@ extension RawJSValue: ConvertibleToJSValue {
 }
 
 extension JSValue {
-    func withRawJSValue<T>(_ body: (RawJSValue) -> T) -> T {
+    func toRawJSValue() -> RawJSValue {
         let kind: JavaScriptValueKind
         let payload1: JavaScriptPayload1
         var payload2: JavaScriptPayload2 = 0
@@ -226,7 +226,7 @@ extension JSValue {
             payload1 = 0
             payload2 = numberValue
         case let .string(string):
-            return string.withRawJSValue(body)
+            return string.toRawJSValue()
         case let .object(ref):
             kind = .object
             payload1 = JavaScriptPayload1(ref.id)
@@ -246,30 +246,30 @@ extension JSValue {
             kind = .bigInt
             payload1 = JavaScriptPayload1(bigIntRef.id)
         }
-        let rawValue = RawJSValue(kind: kind, payload1: payload1, payload2: payload2)
-        return body(rawValue)
+        return RawJSValue(kind: kind, payload1: payload1, payload2: payload2)
     }
 }
 
-extension Array where Element == ConvertibleToJSValue {
-    func withRawJSValues<T>(_ body: ([RawJSValue]) -> T) -> T {
-        func _withRawJSValues<T>(
-            _ values: [ConvertibleToJSValue], _ index: Int,
-            _ results: inout [RawJSValue], _ body: ([RawJSValue]) -> T
-        ) -> T {
-            if index == values.count { return body(results) }
-            return values[index].jsValue.withRawJSValue { (rawValue) -> T in
-                results.append(rawValue)
-                return _withRawJSValues(values, index + 1, &results, body)
+extension Array where Element == JSValue {
+    func withRawJSValues<T>(_ body: (UnsafeBufferPointer<RawJSValue>) -> T) -> T {
+        withUnsafeTemporaryAllocation(of: RawJSValue.self, capacity: self.count) { buffer in
+            for (index, value) in self.enumerated() {
+                buffer[index] = value.toRawJSValue()
             }
+            return body(UnsafeBufferPointer(buffer))
         }
-        var _results = [RawJSValue]()
-        return _withRawJSValues(self, 0, &_results, body)
     }
 }
 
+extension Array where Element == ConvertibleToJSValue {
+    func withRawJSValues<T>(_ body: (UnsafeBufferPointer<RawJSValue>) -> T) -> T {
+        map { $0.jsValue }.withRawJSValues(body)
+    }
+}
+
+
 extension Array where Element: ConvertibleToJSValue {
-    func withRawJSValues<T>(_ body: ([RawJSValue]) -> T) -> T {
-        [ConvertibleToJSValue].withRawJSValues(self)(body)
+    func withRawJSValues<T>(_ body: (UnsafeBufferPointer<RawJSValue>) -> T) -> T {
+        map { $0.jsValue }.withRawJSValues(body)
     }
 }
diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift
index 9cec5dad0..e54eede9c 100644
--- a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift
+++ b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift
@@ -38,10 +38,8 @@ public class JSFunction: JSObject {
     /// - Parameter arguments: Arguments to be passed to this constructor function.
     /// - Returns: A new instance of this constructor.
     public func new(arguments: [ConvertibleToJSValue]) -> JSObject {
-        arguments.withRawJSValues { rawValues in
-            rawValues.withUnsafeBufferPointer { bufferPointer in
-                JSObject(id: _call_new(self.id, bufferPointer.baseAddress!, Int32(bufferPointer.count)))
-            }
+        arguments.withRawJSValues { bufferPointer in
+            JSObject(id: _call_new(self.id, bufferPointer.baseAddress!, Int32(bufferPointer.count)))
         }
     }
 
@@ -84,26 +82,24 @@ public class JSFunction: JSObject {
 }
 
 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
-                )
-            }
-            assert(!kindAndFlags.isException)
-            let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2)
-            return result
+    arguments.withRawJSValues { 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
+            )
         }
+        assert(!kindAndFlags.isException)
+        let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2)
+        return result
     }
 }
diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSString.swift b/Sources/JavaScriptKit/FundamentalObjects/JSString.swift
index 5621793d0..a10972cbe 100644
--- a/Sources/JavaScriptKit/FundamentalObjects/JSString.swift
+++ b/Sources/JavaScriptKit/FundamentalObjects/JSString.swift
@@ -95,10 +95,9 @@ extension JSString {
         guts.jsRef
     }
 
-    func withRawJSValue<T>(_ body: (RawJSValue) -> T) -> T {
-        let rawValue = RawJSValue(
+    func toRawJSValue() -> RawJSValue {
+        return RawJSValue(
             kind: .string, payload1: guts.jsRef, payload2: 0
         )
-        return body(rawValue)
     }
 }
diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift
index 3e21f0e1b..c605c6e7f 100644
--- a/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift
+++ b/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift
@@ -36,24 +36,22 @@ public class JSThrowingFunction {
     /// - Parameter arguments: Arguments to be passed to this constructor function.
     /// - Returns: A new instance of this constructor.
     public func new(arguments: [ConvertibleToJSValue]) throws -> JSObject {
-        try arguments.withRawJSValues { rawValues -> Result<JSObject, JSValue> in
-            rawValues.withUnsafeBufferPointer { bufferPointer in
-                let argv = bufferPointer.baseAddress
-                let argc = bufferPointer.count
+        try arguments.withRawJSValues { bufferPointer -> Result<JSObject, JSValue> in
+            let argv = bufferPointer.baseAddress
+            let argc = bufferPointer.count
 
-                var exceptionKind = JavaScriptValueKindAndFlags()
-                var exceptionPayload1 = JavaScriptPayload1()
-                var exceptionPayload2 = JavaScriptPayload2()
-                let resultObj = _call_throwing_new(
-                    self.base.id, argv, Int32(argc),
-                    &exceptionKind, &exceptionPayload1, &exceptionPayload2
-                )
-                if exceptionKind.isException {
-                    let exception = RawJSValue(kind: exceptionKind.kind, payload1: exceptionPayload1, payload2: exceptionPayload2)
-                    return .failure(exception.jsValue)
-                }
-                return .success(JSObject(id: resultObj))
+            var exceptionKind = JavaScriptValueKindAndFlags()
+            var exceptionPayload1 = JavaScriptPayload1()
+            var exceptionPayload2 = JavaScriptPayload2()
+            let resultObj = _call_throwing_new(
+                self.base.id, argv, Int32(argc),
+                &exceptionKind, &exceptionPayload1, &exceptionPayload2
+            )
+            if exceptionKind.isException {
+                let exception = RawJSValue(kind: exceptionKind.kind, payload1: exceptionPayload1, payload2: exceptionPayload2)
+                return .failure(exception.jsValue)
             }
+            return .success(JSObject(id: resultObj))
         }.get()
     }
 
@@ -64,26 +62,24 @@ public class JSThrowingFunction {
 }
 
 private func invokeJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleToJSValue], this: JSObject?) throws -> JSValue {
-    let (result, isException) = arguments.withRawJSValues { rawValues in
-        rawValues.withUnsafeBufferPointer { bufferPointer -> (JSValue, Bool) 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(thisId,
-                                         jsFunc.id, argv, Int32(argc),
-                                         &kindAndFlags, &payload1, &payload2)
-            } else {
-                _call_function(
-                    jsFunc.id, argv, Int32(argc),
-                    &kindAndFlags, &payload1, &payload2
-                )
-            }
-            let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2)
-            return (result.jsValue, kindAndFlags.isException)
+    let (result, isException): (JSValue, Bool) = arguments.withRawJSValues { 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(thisId,
+                                     jsFunc.id, argv, Int32(argc),
+                                     &kindAndFlags, &payload1, &payload2)
+        } else {
+            _call_function(
+                jsFunc.id, argv, Int32(argc),
+                &kindAndFlags, &payload1, &payload2
+            )
         }
+        let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2)
+        return (result.jsValue, kindAndFlags.isException)
     }
     if isException {
         throw result
diff --git a/Sources/JavaScriptKit/JSValue.swift b/Sources/JavaScriptKit/JSValue.swift
index 973dfcb5d..8b6cfbd92 100644
--- a/Sources/JavaScriptKit/JSValue.swift
+++ b/Sources/JavaScriptKit/JSValue.swift
@@ -203,9 +203,8 @@ public func getJSValue(this: JSObject, name: JSString) -> JSValue {
 }
 
 public func setJSValue(this: JSObject, name: JSString, value: JSValue) {
-    value.withRawJSValue { rawValue in
-        _set_prop(this.id, name.asInternalJSRef(), rawValue.kind, rawValue.payload1, rawValue.payload2)
-    }
+    let rawValue = value.toRawJSValue()
+    _set_prop(this.id, name.asInternalJSRef(), rawValue.kind, rawValue.payload1, rawValue.payload2)
 }
 
 public func getJSValue(this: JSObject, index: Int32) -> JSValue {
@@ -217,11 +216,10 @@ public func getJSValue(this: JSObject, index: Int32) -> JSValue {
 }
 
 public func setJSValue(this: JSObject, index: Int32, value: JSValue) {
-    value.withRawJSValue { rawValue in
-        _set_subscript(this.id, index,
-                       rawValue.kind,
-                       rawValue.payload1, rawValue.payload2)
-    }
+    let rawValue = value.toRawJSValue()
+    _set_subscript(this.id, index,
+                   rawValue.kind,
+                   rawValue.payload1, rawValue.payload2)
 }
 
 public func getJSValue(this: JSObject, symbol: JSSymbol) -> JSValue {
@@ -233,9 +231,8 @@ public func getJSValue(this: JSObject, symbol: JSSymbol) -> JSValue {
 }
 
 public func setJSValue(this: JSObject, symbol: JSSymbol, value: JSValue) {
-    value.withRawJSValue { rawValue in
-        _set_prop(this.id, symbol.id, rawValue.kind, rawValue.payload1, rawValue.payload2)
-    }
+    let rawValue = value.toRawJSValue()
+    _set_prop(this.id, symbol.id, rawValue.kind, rawValue.payload1, rawValue.payload2)
 }
 
 public extension JSValue {

From d68497860269c37b5e48aad03fead5f26f181cdb Mon Sep 17 00:00:00 2001
From: Yuta Saito <kateinoigakukun@gmail.com>
Date: Tue, 16 Aug 2022 23:57:30 +0900
Subject: [PATCH 02/20] Revert "Allocate function call argument buffer on
 stack"

This reverts commit 8d17f5c1c78a20f5814c2eb1280569c2289c7f59.
---
 .../JavaScriptKit/ConvertibleToJSValue.swift  | 36 +++++-----
 .../FundamentalObjects/JSFunction.swift       | 44 +++++++------
 .../FundamentalObjects/JSString.swift         |  5 +-
 .../JSThrowingFunction.swift                  | 66 ++++++++++---------
 Sources/JavaScriptKit/JSValue.swift           | 19 +++---
 5 files changed, 91 insertions(+), 79 deletions(-)

diff --git a/Sources/JavaScriptKit/ConvertibleToJSValue.swift b/Sources/JavaScriptKit/ConvertibleToJSValue.swift
index 95f96a2f7..572e867b0 100644
--- a/Sources/JavaScriptKit/ConvertibleToJSValue.swift
+++ b/Sources/JavaScriptKit/ConvertibleToJSValue.swift
@@ -213,7 +213,7 @@ extension RawJSValue: ConvertibleToJSValue {
 }
 
 extension JSValue {
-    func toRawJSValue() -> RawJSValue {
+    func withRawJSValue<T>(_ body: (RawJSValue) -> T) -> T {
         let kind: JavaScriptValueKind
         let payload1: JavaScriptPayload1
         var payload2: JavaScriptPayload2 = 0
@@ -226,7 +226,7 @@ extension JSValue {
             payload1 = 0
             payload2 = numberValue
         case let .string(string):
-            return string.toRawJSValue()
+            return string.withRawJSValue(body)
         case let .object(ref):
             kind = .object
             payload1 = JavaScriptPayload1(ref.id)
@@ -246,30 +246,30 @@ extension JSValue {
             kind = .bigInt
             payload1 = JavaScriptPayload1(bigIntRef.id)
         }
-        return RawJSValue(kind: kind, payload1: payload1, payload2: payload2)
+        let rawValue = RawJSValue(kind: kind, payload1: payload1, payload2: payload2)
+        return body(rawValue)
     }
 }
 
-extension Array where Element == JSValue {
-    func withRawJSValues<T>(_ body: (UnsafeBufferPointer<RawJSValue>) -> T) -> T {
-        withUnsafeTemporaryAllocation(of: RawJSValue.self, capacity: self.count) { buffer in
-            for (index, value) in self.enumerated() {
-                buffer[index] = value.toRawJSValue()
+extension Array where Element == ConvertibleToJSValue {
+    func withRawJSValues<T>(_ body: ([RawJSValue]) -> T) -> T {
+        func _withRawJSValues<T>(
+            _ values: [ConvertibleToJSValue], _ index: Int,
+            _ results: inout [RawJSValue], _ body: ([RawJSValue]) -> T
+        ) -> T {
+            if index == values.count { return body(results) }
+            return values[index].jsValue.withRawJSValue { (rawValue) -> T in
+                results.append(rawValue)
+                return _withRawJSValues(values, index + 1, &results, body)
             }
-            return body(UnsafeBufferPointer(buffer))
         }
+        var _results = [RawJSValue]()
+        return _withRawJSValues(self, 0, &_results, body)
     }
 }
 
-extension Array where Element == ConvertibleToJSValue {
-    func withRawJSValues<T>(_ body: (UnsafeBufferPointer<RawJSValue>) -> T) -> T {
-        map { $0.jsValue }.withRawJSValues(body)
-    }
-}
-
-
 extension Array where Element: ConvertibleToJSValue {
-    func withRawJSValues<T>(_ body: (UnsafeBufferPointer<RawJSValue>) -> T) -> T {
-        map { $0.jsValue }.withRawJSValues(body)
+    func withRawJSValues<T>(_ body: ([RawJSValue]) -> T) -> T {
+        [ConvertibleToJSValue].withRawJSValues(self)(body)
     }
 }
diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift
index e54eede9c..9cec5dad0 100644
--- a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift
+++ b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift
@@ -38,8 +38,10 @@ public class JSFunction: JSObject {
     /// - Parameter arguments: Arguments to be passed to this constructor function.
     /// - Returns: A new instance of this constructor.
     public func new(arguments: [ConvertibleToJSValue]) -> JSObject {
-        arguments.withRawJSValues { bufferPointer in
-            JSObject(id: _call_new(self.id, bufferPointer.baseAddress!, Int32(bufferPointer.count)))
+        arguments.withRawJSValues { rawValues in
+            rawValues.withUnsafeBufferPointer { bufferPointer in
+                JSObject(id: _call_new(self.id, bufferPointer.baseAddress!, Int32(bufferPointer.count)))
+            }
         }
     }
 
@@ -82,24 +84,26 @@ public class JSFunction: JSObject {
 }
 
 func invokeNonThrowingJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleToJSValue], this: JSObject?) -> RawJSValue {
-    arguments.withRawJSValues { 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
-            )
+    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
+                )
+            }
+            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
     }
 }
diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSString.swift b/Sources/JavaScriptKit/FundamentalObjects/JSString.swift
index a10972cbe..5621793d0 100644
--- a/Sources/JavaScriptKit/FundamentalObjects/JSString.swift
+++ b/Sources/JavaScriptKit/FundamentalObjects/JSString.swift
@@ -95,9 +95,10 @@ extension JSString {
         guts.jsRef
     }
 
-    func toRawJSValue() -> RawJSValue {
-        return RawJSValue(
+    func withRawJSValue<T>(_ body: (RawJSValue) -> T) -> T {
+        let rawValue = RawJSValue(
             kind: .string, payload1: guts.jsRef, payload2: 0
         )
+        return body(rawValue)
     }
 }
diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift
index c605c6e7f..3e21f0e1b 100644
--- a/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift
+++ b/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift
@@ -36,22 +36,24 @@ public class JSThrowingFunction {
     /// - Parameter arguments: Arguments to be passed to this constructor function.
     /// - Returns: A new instance of this constructor.
     public func new(arguments: [ConvertibleToJSValue]) throws -> JSObject {
-        try arguments.withRawJSValues { bufferPointer -> Result<JSObject, JSValue> in
-            let argv = bufferPointer.baseAddress
-            let argc = bufferPointer.count
+        try arguments.withRawJSValues { rawValues -> Result<JSObject, JSValue> in
+            rawValues.withUnsafeBufferPointer { bufferPointer in
+                let argv = bufferPointer.baseAddress
+                let argc = bufferPointer.count
 
-            var exceptionKind = JavaScriptValueKindAndFlags()
-            var exceptionPayload1 = JavaScriptPayload1()
-            var exceptionPayload2 = JavaScriptPayload2()
-            let resultObj = _call_throwing_new(
-                self.base.id, argv, Int32(argc),
-                &exceptionKind, &exceptionPayload1, &exceptionPayload2
-            )
-            if exceptionKind.isException {
-                let exception = RawJSValue(kind: exceptionKind.kind, payload1: exceptionPayload1, payload2: exceptionPayload2)
-                return .failure(exception.jsValue)
+                var exceptionKind = JavaScriptValueKindAndFlags()
+                var exceptionPayload1 = JavaScriptPayload1()
+                var exceptionPayload2 = JavaScriptPayload2()
+                let resultObj = _call_throwing_new(
+                    self.base.id, argv, Int32(argc),
+                    &exceptionKind, &exceptionPayload1, &exceptionPayload2
+                )
+                if exceptionKind.isException {
+                    let exception = RawJSValue(kind: exceptionKind.kind, payload1: exceptionPayload1, payload2: exceptionPayload2)
+                    return .failure(exception.jsValue)
+                }
+                return .success(JSObject(id: resultObj))
             }
-            return .success(JSObject(id: resultObj))
         }.get()
     }
 
@@ -62,24 +64,26 @@ public class JSThrowingFunction {
 }
 
 private func invokeJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleToJSValue], this: JSObject?) throws -> JSValue {
-    let (result, isException): (JSValue, Bool) = arguments.withRawJSValues { 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(thisId,
-                                     jsFunc.id, argv, Int32(argc),
-                                     &kindAndFlags, &payload1, &payload2)
-        } else {
-            _call_function(
-                jsFunc.id, argv, Int32(argc),
-                &kindAndFlags, &payload1, &payload2
-            )
+    let (result, isException) = arguments.withRawJSValues { rawValues in
+        rawValues.withUnsafeBufferPointer { bufferPointer -> (JSValue, Bool) 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(thisId,
+                                         jsFunc.id, argv, Int32(argc),
+                                         &kindAndFlags, &payload1, &payload2)
+            } else {
+                _call_function(
+                    jsFunc.id, argv, Int32(argc),
+                    &kindAndFlags, &payload1, &payload2
+                )
+            }
+            let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2)
+            return (result.jsValue, kindAndFlags.isException)
         }
-        let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2)
-        return (result.jsValue, kindAndFlags.isException)
     }
     if isException {
         throw result
diff --git a/Sources/JavaScriptKit/JSValue.swift b/Sources/JavaScriptKit/JSValue.swift
index 8b6cfbd92..973dfcb5d 100644
--- a/Sources/JavaScriptKit/JSValue.swift
+++ b/Sources/JavaScriptKit/JSValue.swift
@@ -203,8 +203,9 @@ public func getJSValue(this: JSObject, name: JSString) -> JSValue {
 }
 
 public func setJSValue(this: JSObject, name: JSString, value: JSValue) {
-    let rawValue = value.toRawJSValue()
-    _set_prop(this.id, name.asInternalJSRef(), rawValue.kind, rawValue.payload1, rawValue.payload2)
+    value.withRawJSValue { rawValue in
+        _set_prop(this.id, name.asInternalJSRef(), rawValue.kind, rawValue.payload1, rawValue.payload2)
+    }
 }
 
 public func getJSValue(this: JSObject, index: Int32) -> JSValue {
@@ -216,10 +217,11 @@ public func getJSValue(this: JSObject, index: Int32) -> JSValue {
 }
 
 public func setJSValue(this: JSObject, index: Int32, value: JSValue) {
-    let rawValue = value.toRawJSValue()
-    _set_subscript(this.id, index,
-                   rawValue.kind,
-                   rawValue.payload1, rawValue.payload2)
+    value.withRawJSValue { rawValue in
+        _set_subscript(this.id, index,
+                       rawValue.kind,
+                       rawValue.payload1, rawValue.payload2)
+    }
 }
 
 public func getJSValue(this: JSObject, symbol: JSSymbol) -> JSValue {
@@ -231,8 +233,9 @@ public func getJSValue(this: JSObject, symbol: JSSymbol) -> JSValue {
 }
 
 public func setJSValue(this: JSObject, symbol: JSSymbol, value: JSValue) {
-    let rawValue = value.toRawJSValue()
-    _set_prop(this.id, symbol.id, rawValue.kind, rawValue.payload1, rawValue.payload2)
+    value.withRawJSValue { rawValue in
+        _set_prop(this.id, symbol.id, rawValue.kind, rawValue.payload1, rawValue.payload2)
+    }
 }
 
 public extension JSValue {

From 7f6e3d88f9df4c8272506b8d298fd45161b991c2 Mon Sep 17 00:00:00 2001
From: Yuta Saito <kateinoigakukun@gmail.com>
Date: Tue, 16 Aug 2022 23:57:12 +0900
Subject: [PATCH 03/20] Reduce memory store for returned value kind

---
 Runtime/src/index.ts                          | 23 ++------
 Runtime/src/js-value.ts                       | 58 ++++++++++++++++++-
 Runtime/src/types.ts                          |  4 +-
 .../FundamentalObjects/JSFunction.swift       |  5 +-
 Sources/JavaScriptKit/XcodeSupport.swift      |  3 +-
 .../_CJavaScriptKit/include/_CJavaScriptKit.h | 12 ++--
 6 files changed, 77 insertions(+), 28 deletions(-)

diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts
index 8f90776f1..a60f0cf6e 100644
--- a/Runtime/src/index.ts
+++ b/Runtime/src/index.ts
@@ -237,35 +237,24 @@ 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 result = undefined;
             let isException = true;
             try {
                 const args = JSValue.decodeArray(argv, argc, this.memory);
-                const result = func(...args);
-                JSValue.write(
+                result = func(...args);
+                isException = false;
+            } finally {
+                return JSValue.writeV2(
                     result,
-                    kind_ptr,
                     payload1_ptr,
                     payload2_ptr,
-                    false,
+                    isException,
                     this.memory
                 );
-                isException = false;
-            } finally {
-                if (isException) {
-                    JSValue.write(
-                        undefined,
-                        kind_ptr,
-                        payload1_ptr,
-                        payload2_ptr,
-                        true,
-                        this.memory
-                    );
-                }
             }
         },
 
diff --git a/Runtime/src/js-value.ts b/Runtime/src/js-value.ts
index c3c24c3a9..0f86fa554 100644
--- a/Runtime/src/js-value.ts
+++ b/Runtime/src/js-value.ts
@@ -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,
@@ -121,3 +121,59 @@ export const write = (
             assertNever(type, `Type "${type}" is not supported yet`);
     }
 };
+
+
+export const writeV2 = (
+    value: any,
+    payload1_ptr: pointer,
+    payload2_ptr: pointer,
+    is_exception: boolean,
+    memory: Memory
+): JavaScriptValueKindAndFlags => {
+    if (value === undefined) {
+        return Kind.Undefined;
+    }
+
+    const exceptionBit = (is_exception ? 1 : 0) << 31;
+    if (value === null) {
+        return exceptionBit | Kind.Null;
+    }
+
+    const writeRef = (kind: Kind) => {
+        memory.writeUint32(payload1_ptr, memory.retain(value));
+        return exceptionBit | kind
+    };
+
+    const type = typeof value;
+    switch (type) {
+        case "boolean": {
+            memory.writeUint32(payload1_ptr, value ? 1 : 0);
+            return exceptionBit | Kind.Boolean;
+        }
+        case "number": {
+            memory.writeFloat64(payload2_ptr, value);
+            return exceptionBit | Kind.Number;
+        }
+        case "string": {
+            return writeRef(Kind.String);
+        }
+        case "undefined": {
+            return exceptionBit | Kind.Undefined;
+        }
+        case "object": {
+            return writeRef(Kind.Object);
+        }
+        case "function": {
+            return writeRef(Kind.Function);
+        }
+        case "symbol": {
+            return writeRef(Kind.Symbol);
+        }
+        case "bigint": {
+            return writeRef(Kind.BigInt);
+        }
+        default:
+            assertNever(type, `Type "${type}" is not supported yet`);
+    }
+    throw new Error("Unreachable");
+};
diff --git a/Runtime/src/types.ts b/Runtime/src/types.ts
index a6e3dd1d2..f46ee9cdd 100644
--- a/Runtime/src/types.ts
+++ b/Runtime/src/types.ts
@@ -3,6 +3,7 @@ import * as JSValue from "./js-value.js";
 export type ref = number;
 export type pointer = number;
 export type bool = number;
+export type JavaScriptValueKindAndFlags = number;
 
 export interface ExportedFunctions {
     swjs_library_version(): number;
@@ -63,10 +64,9 @@ export interface ImportedFunctions {
         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,
diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift
index 9cec5dad0..6d0dbeda6 100644
--- a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift
+++ b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift
@@ -96,10 +96,11 @@ func invokeNonThrowingJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleTo
                                                   jsFunc.id, argv, Int32(argc),
                                                   &kindAndFlags, &payload1, &payload2)
             } else {
-                _call_function_no_catch(
+                let result = _call_function_no_catch(
                     jsFunc.id, argv, Int32(argc),
-                    &kindAndFlags, &payload1, &payload2
+                    &payload1, &payload2
                 )
+                kindAndFlags = unsafeBitCast(result, to: JavaScriptValueKindAndFlags.self)
             }
             assert(!kindAndFlags.isException)
             let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2)
diff --git a/Sources/JavaScriptKit/XcodeSupport.swift b/Sources/JavaScriptKit/XcodeSupport.swift
index 5556cdba8..d231440d9 100644
--- a/Sources/JavaScriptKit/XcodeSupport.swift
+++ b/Sources/JavaScriptKit/XcodeSupport.swift
@@ -59,10 +59,9 @@ import _CJavaScriptKit
     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,
diff --git a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h
index 59923e02d..b1fa593f4 100644
--- a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h
+++ b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h
@@ -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`.
@@ -193,12 +194,15 @@ extern void _call_function(const JavaScriptObjectRef ref, const RawJSValue *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`.
 ///

From 50fd47b8e90b4d846225acc40c589f4e316f77db Mon Sep 17 00:00:00 2001
From: Yuta Saito <kateinoigakukun@gmail.com>
Date: Wed, 17 Aug 2022 00:03:37 +0900
Subject: [PATCH 04/20] Add NODEJS_FLAGS to perform profiling by passing --prof

---
 IntegrationTests/Makefile | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/IntegrationTests/Makefile b/IntegrationTests/Makefile
index 312977482..57b99c8da 100644
--- a/IntegrationTests/Makefile
+++ b/IntegrationTests/Makefile
@@ -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

From 4f850a032eb742af10900af47cb42e5a44c03d98 Mon Sep 17 00:00:00 2001
From: Yuta Saito <kateinoigakukun@gmail.com>
Date: Wed, 17 Aug 2022 00:06:40 +0900
Subject: [PATCH 05/20] Revert "Revert "Allocate function call argument buffer
 on stack""

This reverts commit d68497860269c37b5e48aad03fead5f26f181cdb.
---
 .../JavaScriptKit/ConvertibleToJSValue.swift  | 36 +++++-----
 .../FundamentalObjects/JSFunction.swift       | 46 ++++++-------
 .../FundamentalObjects/JSString.swift         |  5 +-
 .../JSThrowingFunction.swift                  | 66 +++++++++----------
 Sources/JavaScriptKit/JSValue.swift           | 19 +++---
 5 files changed, 80 insertions(+), 92 deletions(-)

diff --git a/Sources/JavaScriptKit/ConvertibleToJSValue.swift b/Sources/JavaScriptKit/ConvertibleToJSValue.swift
index 572e867b0..95f96a2f7 100644
--- a/Sources/JavaScriptKit/ConvertibleToJSValue.swift
+++ b/Sources/JavaScriptKit/ConvertibleToJSValue.swift
@@ -213,7 +213,7 @@ extension RawJSValue: ConvertibleToJSValue {
 }
 
 extension JSValue {
-    func withRawJSValue<T>(_ body: (RawJSValue) -> T) -> T {
+    func toRawJSValue() -> RawJSValue {
         let kind: JavaScriptValueKind
         let payload1: JavaScriptPayload1
         var payload2: JavaScriptPayload2 = 0
@@ -226,7 +226,7 @@ extension JSValue {
             payload1 = 0
             payload2 = numberValue
         case let .string(string):
-            return string.withRawJSValue(body)
+            return string.toRawJSValue()
         case let .object(ref):
             kind = .object
             payload1 = JavaScriptPayload1(ref.id)
@@ -246,30 +246,30 @@ extension JSValue {
             kind = .bigInt
             payload1 = JavaScriptPayload1(bigIntRef.id)
         }
-        let rawValue = RawJSValue(kind: kind, payload1: payload1, payload2: payload2)
-        return body(rawValue)
+        return RawJSValue(kind: kind, payload1: payload1, payload2: payload2)
     }
 }
 
-extension Array where Element == ConvertibleToJSValue {
-    func withRawJSValues<T>(_ body: ([RawJSValue]) -> T) -> T {
-        func _withRawJSValues<T>(
-            _ values: [ConvertibleToJSValue], _ index: Int,
-            _ results: inout [RawJSValue], _ body: ([RawJSValue]) -> T
-        ) -> T {
-            if index == values.count { return body(results) }
-            return values[index].jsValue.withRawJSValue { (rawValue) -> T in
-                results.append(rawValue)
-                return _withRawJSValues(values, index + 1, &results, body)
+extension Array where Element == JSValue {
+    func withRawJSValues<T>(_ body: (UnsafeBufferPointer<RawJSValue>) -> T) -> T {
+        withUnsafeTemporaryAllocation(of: RawJSValue.self, capacity: self.count) { buffer in
+            for (index, value) in self.enumerated() {
+                buffer[index] = value.toRawJSValue()
             }
+            return body(UnsafeBufferPointer(buffer))
         }
-        var _results = [RawJSValue]()
-        return _withRawJSValues(self, 0, &_results, body)
     }
 }
 
+extension Array where Element == ConvertibleToJSValue {
+    func withRawJSValues<T>(_ body: (UnsafeBufferPointer<RawJSValue>) -> T) -> T {
+        map { $0.jsValue }.withRawJSValues(body)
+    }
+}
+
+
 extension Array where Element: ConvertibleToJSValue {
-    func withRawJSValues<T>(_ body: ([RawJSValue]) -> T) -> T {
-        [ConvertibleToJSValue].withRawJSValues(self)(body)
+    func withRawJSValues<T>(_ body: (UnsafeBufferPointer<RawJSValue>) -> T) -> T {
+        map { $0.jsValue }.withRawJSValues(body)
     }
 }
diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift
index 6d0dbeda6..07645ac94 100644
--- a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift
+++ b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift
@@ -38,10 +38,8 @@ public class JSFunction: JSObject {
     /// - Parameter arguments: Arguments to be passed to this constructor function.
     /// - Returns: A new instance of this constructor.
     public func new(arguments: [ConvertibleToJSValue]) -> JSObject {
-        arguments.withRawJSValues { rawValues in
-            rawValues.withUnsafeBufferPointer { bufferPointer in
-                JSObject(id: _call_new(self.id, bufferPointer.baseAddress!, Int32(bufferPointer.count)))
-            }
+        arguments.withRawJSValues { bufferPointer in
+            JSObject(id: _call_new(self.id, bufferPointer.baseAddress!, Int32(bufferPointer.count)))
         }
     }
 
@@ -84,27 +82,25 @@ public class JSFunction: JSObject {
 }
 
 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 {
-                let result = _call_function_no_catch(
-                    jsFunc.id, argv, Int32(argc),
-                    &payload1, &payload2
-                )
-                kindAndFlags = unsafeBitCast(result, to: JavaScriptValueKindAndFlags.self)
-            }
-            assert(!kindAndFlags.isException)
-            let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2)
-            return result
+    arguments.withRawJSValues { 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 {
+            let result = _call_function_no_catch(
+                jsFunc.id, argv, Int32(argc),
+                &payload1, &payload2
+            )
+            kindAndFlags = unsafeBitCast(result, to: JavaScriptValueKindAndFlags.self)
         }
+        assert(!kindAndFlags.isException)
+        let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2)
+        return result
     }
 }
diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSString.swift b/Sources/JavaScriptKit/FundamentalObjects/JSString.swift
index 5621793d0..a10972cbe 100644
--- a/Sources/JavaScriptKit/FundamentalObjects/JSString.swift
+++ b/Sources/JavaScriptKit/FundamentalObjects/JSString.swift
@@ -95,10 +95,9 @@ extension JSString {
         guts.jsRef
     }
 
-    func withRawJSValue<T>(_ body: (RawJSValue) -> T) -> T {
-        let rawValue = RawJSValue(
+    func toRawJSValue() -> RawJSValue {
+        return RawJSValue(
             kind: .string, payload1: guts.jsRef, payload2: 0
         )
-        return body(rawValue)
     }
 }
diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift
index 3e21f0e1b..c605c6e7f 100644
--- a/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift
+++ b/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift
@@ -36,24 +36,22 @@ public class JSThrowingFunction {
     /// - Parameter arguments: Arguments to be passed to this constructor function.
     /// - Returns: A new instance of this constructor.
     public func new(arguments: [ConvertibleToJSValue]) throws -> JSObject {
-        try arguments.withRawJSValues { rawValues -> Result<JSObject, JSValue> in
-            rawValues.withUnsafeBufferPointer { bufferPointer in
-                let argv = bufferPointer.baseAddress
-                let argc = bufferPointer.count
+        try arguments.withRawJSValues { bufferPointer -> Result<JSObject, JSValue> in
+            let argv = bufferPointer.baseAddress
+            let argc = bufferPointer.count
 
-                var exceptionKind = JavaScriptValueKindAndFlags()
-                var exceptionPayload1 = JavaScriptPayload1()
-                var exceptionPayload2 = JavaScriptPayload2()
-                let resultObj = _call_throwing_new(
-                    self.base.id, argv, Int32(argc),
-                    &exceptionKind, &exceptionPayload1, &exceptionPayload2
-                )
-                if exceptionKind.isException {
-                    let exception = RawJSValue(kind: exceptionKind.kind, payload1: exceptionPayload1, payload2: exceptionPayload2)
-                    return .failure(exception.jsValue)
-                }
-                return .success(JSObject(id: resultObj))
+            var exceptionKind = JavaScriptValueKindAndFlags()
+            var exceptionPayload1 = JavaScriptPayload1()
+            var exceptionPayload2 = JavaScriptPayload2()
+            let resultObj = _call_throwing_new(
+                self.base.id, argv, Int32(argc),
+                &exceptionKind, &exceptionPayload1, &exceptionPayload2
+            )
+            if exceptionKind.isException {
+                let exception = RawJSValue(kind: exceptionKind.kind, payload1: exceptionPayload1, payload2: exceptionPayload2)
+                return .failure(exception.jsValue)
             }
+            return .success(JSObject(id: resultObj))
         }.get()
     }
 
@@ -64,26 +62,24 @@ public class JSThrowingFunction {
 }
 
 private func invokeJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleToJSValue], this: JSObject?) throws -> JSValue {
-    let (result, isException) = arguments.withRawJSValues { rawValues in
-        rawValues.withUnsafeBufferPointer { bufferPointer -> (JSValue, Bool) 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(thisId,
-                                         jsFunc.id, argv, Int32(argc),
-                                         &kindAndFlags, &payload1, &payload2)
-            } else {
-                _call_function(
-                    jsFunc.id, argv, Int32(argc),
-                    &kindAndFlags, &payload1, &payload2
-                )
-            }
-            let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2)
-            return (result.jsValue, kindAndFlags.isException)
+    let (result, isException): (JSValue, Bool) = arguments.withRawJSValues { 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(thisId,
+                                     jsFunc.id, argv, Int32(argc),
+                                     &kindAndFlags, &payload1, &payload2)
+        } else {
+            _call_function(
+                jsFunc.id, argv, Int32(argc),
+                &kindAndFlags, &payload1, &payload2
+            )
         }
+        let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2)
+        return (result.jsValue, kindAndFlags.isException)
     }
     if isException {
         throw result
diff --git a/Sources/JavaScriptKit/JSValue.swift b/Sources/JavaScriptKit/JSValue.swift
index 973dfcb5d..8b6cfbd92 100644
--- a/Sources/JavaScriptKit/JSValue.swift
+++ b/Sources/JavaScriptKit/JSValue.swift
@@ -203,9 +203,8 @@ public func getJSValue(this: JSObject, name: JSString) -> JSValue {
 }
 
 public func setJSValue(this: JSObject, name: JSString, value: JSValue) {
-    value.withRawJSValue { rawValue in
-        _set_prop(this.id, name.asInternalJSRef(), rawValue.kind, rawValue.payload1, rawValue.payload2)
-    }
+    let rawValue = value.toRawJSValue()
+    _set_prop(this.id, name.asInternalJSRef(), rawValue.kind, rawValue.payload1, rawValue.payload2)
 }
 
 public func getJSValue(this: JSObject, index: Int32) -> JSValue {
@@ -217,11 +216,10 @@ public func getJSValue(this: JSObject, index: Int32) -> JSValue {
 }
 
 public func setJSValue(this: JSObject, index: Int32, value: JSValue) {
-    value.withRawJSValue { rawValue in
-        _set_subscript(this.id, index,
-                       rawValue.kind,
-                       rawValue.payload1, rawValue.payload2)
-    }
+    let rawValue = value.toRawJSValue()
+    _set_subscript(this.id, index,
+                   rawValue.kind,
+                   rawValue.payload1, rawValue.payload2)
 }
 
 public func getJSValue(this: JSObject, symbol: JSSymbol) -> JSValue {
@@ -233,9 +231,8 @@ public func getJSValue(this: JSObject, symbol: JSSymbol) -> JSValue {
 }
 
 public func setJSValue(this: JSObject, symbol: JSSymbol, value: JSValue) {
-    value.withRawJSValue { rawValue in
-        _set_prop(this.id, symbol.id, rawValue.kind, rawValue.payload1, rawValue.payload2)
-    }
+    let rawValue = value.toRawJSValue()
+    _set_prop(this.id, symbol.id, rawValue.kind, rawValue.payload1, rawValue.payload2)
 }
 
 public extension JSValue {

From e66296fb8aeafb8a4ef0aee31f7dae60bd837bac Mon Sep 17 00:00:00 2001
From: Yuta Saito <kateinoigakukun@gmail.com>
Date: Wed, 17 Aug 2022 00:07:08 +0900
Subject: [PATCH 06/20] Revert "Revert "Revert "Allocate function call argument
 buffer on stack"""

This reverts commit 4f850a032eb742af10900af47cb42e5a44c03d98.
---
 .../JavaScriptKit/ConvertibleToJSValue.swift  | 36 +++++-----
 .../FundamentalObjects/JSFunction.swift       | 46 +++++++------
 .../FundamentalObjects/JSString.swift         |  5 +-
 .../JSThrowingFunction.swift                  | 66 ++++++++++---------
 Sources/JavaScriptKit/JSValue.swift           | 19 +++---
 5 files changed, 92 insertions(+), 80 deletions(-)

diff --git a/Sources/JavaScriptKit/ConvertibleToJSValue.swift b/Sources/JavaScriptKit/ConvertibleToJSValue.swift
index 95f96a2f7..572e867b0 100644
--- a/Sources/JavaScriptKit/ConvertibleToJSValue.swift
+++ b/Sources/JavaScriptKit/ConvertibleToJSValue.swift
@@ -213,7 +213,7 @@ extension RawJSValue: ConvertibleToJSValue {
 }
 
 extension JSValue {
-    func toRawJSValue() -> RawJSValue {
+    func withRawJSValue<T>(_ body: (RawJSValue) -> T) -> T {
         let kind: JavaScriptValueKind
         let payload1: JavaScriptPayload1
         var payload2: JavaScriptPayload2 = 0
@@ -226,7 +226,7 @@ extension JSValue {
             payload1 = 0
             payload2 = numberValue
         case let .string(string):
-            return string.toRawJSValue()
+            return string.withRawJSValue(body)
         case let .object(ref):
             kind = .object
             payload1 = JavaScriptPayload1(ref.id)
@@ -246,30 +246,30 @@ extension JSValue {
             kind = .bigInt
             payload1 = JavaScriptPayload1(bigIntRef.id)
         }
-        return RawJSValue(kind: kind, payload1: payload1, payload2: payload2)
+        let rawValue = RawJSValue(kind: kind, payload1: payload1, payload2: payload2)
+        return body(rawValue)
     }
 }
 
-extension Array where Element == JSValue {
-    func withRawJSValues<T>(_ body: (UnsafeBufferPointer<RawJSValue>) -> T) -> T {
-        withUnsafeTemporaryAllocation(of: RawJSValue.self, capacity: self.count) { buffer in
-            for (index, value) in self.enumerated() {
-                buffer[index] = value.toRawJSValue()
+extension Array where Element == ConvertibleToJSValue {
+    func withRawJSValues<T>(_ body: ([RawJSValue]) -> T) -> T {
+        func _withRawJSValues<T>(
+            _ values: [ConvertibleToJSValue], _ index: Int,
+            _ results: inout [RawJSValue], _ body: ([RawJSValue]) -> T
+        ) -> T {
+            if index == values.count { return body(results) }
+            return values[index].jsValue.withRawJSValue { (rawValue) -> T in
+                results.append(rawValue)
+                return _withRawJSValues(values, index + 1, &results, body)
             }
-            return body(UnsafeBufferPointer(buffer))
         }
+        var _results = [RawJSValue]()
+        return _withRawJSValues(self, 0, &_results, body)
     }
 }
 
-extension Array where Element == ConvertibleToJSValue {
-    func withRawJSValues<T>(_ body: (UnsafeBufferPointer<RawJSValue>) -> T) -> T {
-        map { $0.jsValue }.withRawJSValues(body)
-    }
-}
-
-
 extension Array where Element: ConvertibleToJSValue {
-    func withRawJSValues<T>(_ body: (UnsafeBufferPointer<RawJSValue>) -> T) -> T {
-        map { $0.jsValue }.withRawJSValues(body)
+    func withRawJSValues<T>(_ body: ([RawJSValue]) -> T) -> T {
+        [ConvertibleToJSValue].withRawJSValues(self)(body)
     }
 }
diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift
index 07645ac94..6d0dbeda6 100644
--- a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift
+++ b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift
@@ -38,8 +38,10 @@ public class JSFunction: JSObject {
     /// - Parameter arguments: Arguments to be passed to this constructor function.
     /// - Returns: A new instance of this constructor.
     public func new(arguments: [ConvertibleToJSValue]) -> JSObject {
-        arguments.withRawJSValues { bufferPointer in
-            JSObject(id: _call_new(self.id, bufferPointer.baseAddress!, Int32(bufferPointer.count)))
+        arguments.withRawJSValues { rawValues in
+            rawValues.withUnsafeBufferPointer { bufferPointer in
+                JSObject(id: _call_new(self.id, bufferPointer.baseAddress!, Int32(bufferPointer.count)))
+            }
         }
     }
 
@@ -82,25 +84,27 @@ public class JSFunction: JSObject {
 }
 
 func invokeNonThrowingJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleToJSValue], this: JSObject?) -> RawJSValue {
-    arguments.withRawJSValues { 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 {
-            let result = _call_function_no_catch(
-                jsFunc.id, argv, Int32(argc),
-                &payload1, &payload2
-            )
-            kindAndFlags = unsafeBitCast(result, to: JavaScriptValueKindAndFlags.self)
+    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 {
+                let result = _call_function_no_catch(
+                    jsFunc.id, argv, Int32(argc),
+                    &payload1, &payload2
+                )
+                kindAndFlags = unsafeBitCast(result, 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
     }
 }
diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSString.swift b/Sources/JavaScriptKit/FundamentalObjects/JSString.swift
index a10972cbe..5621793d0 100644
--- a/Sources/JavaScriptKit/FundamentalObjects/JSString.swift
+++ b/Sources/JavaScriptKit/FundamentalObjects/JSString.swift
@@ -95,9 +95,10 @@ extension JSString {
         guts.jsRef
     }
 
-    func toRawJSValue() -> RawJSValue {
-        return RawJSValue(
+    func withRawJSValue<T>(_ body: (RawJSValue) -> T) -> T {
+        let rawValue = RawJSValue(
             kind: .string, payload1: guts.jsRef, payload2: 0
         )
+        return body(rawValue)
     }
 }
diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift
index c605c6e7f..3e21f0e1b 100644
--- a/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift
+++ b/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift
@@ -36,22 +36,24 @@ public class JSThrowingFunction {
     /// - Parameter arguments: Arguments to be passed to this constructor function.
     /// - Returns: A new instance of this constructor.
     public func new(arguments: [ConvertibleToJSValue]) throws -> JSObject {
-        try arguments.withRawJSValues { bufferPointer -> Result<JSObject, JSValue> in
-            let argv = bufferPointer.baseAddress
-            let argc = bufferPointer.count
+        try arguments.withRawJSValues { rawValues -> Result<JSObject, JSValue> in
+            rawValues.withUnsafeBufferPointer { bufferPointer in
+                let argv = bufferPointer.baseAddress
+                let argc = bufferPointer.count
 
-            var exceptionKind = JavaScriptValueKindAndFlags()
-            var exceptionPayload1 = JavaScriptPayload1()
-            var exceptionPayload2 = JavaScriptPayload2()
-            let resultObj = _call_throwing_new(
-                self.base.id, argv, Int32(argc),
-                &exceptionKind, &exceptionPayload1, &exceptionPayload2
-            )
-            if exceptionKind.isException {
-                let exception = RawJSValue(kind: exceptionKind.kind, payload1: exceptionPayload1, payload2: exceptionPayload2)
-                return .failure(exception.jsValue)
+                var exceptionKind = JavaScriptValueKindAndFlags()
+                var exceptionPayload1 = JavaScriptPayload1()
+                var exceptionPayload2 = JavaScriptPayload2()
+                let resultObj = _call_throwing_new(
+                    self.base.id, argv, Int32(argc),
+                    &exceptionKind, &exceptionPayload1, &exceptionPayload2
+                )
+                if exceptionKind.isException {
+                    let exception = RawJSValue(kind: exceptionKind.kind, payload1: exceptionPayload1, payload2: exceptionPayload2)
+                    return .failure(exception.jsValue)
+                }
+                return .success(JSObject(id: resultObj))
             }
-            return .success(JSObject(id: resultObj))
         }.get()
     }
 
@@ -62,24 +64,26 @@ public class JSThrowingFunction {
 }
 
 private func invokeJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleToJSValue], this: JSObject?) throws -> JSValue {
-    let (result, isException): (JSValue, Bool) = arguments.withRawJSValues { 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(thisId,
-                                     jsFunc.id, argv, Int32(argc),
-                                     &kindAndFlags, &payload1, &payload2)
-        } else {
-            _call_function(
-                jsFunc.id, argv, Int32(argc),
-                &kindAndFlags, &payload1, &payload2
-            )
+    let (result, isException) = arguments.withRawJSValues { rawValues in
+        rawValues.withUnsafeBufferPointer { bufferPointer -> (JSValue, Bool) 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(thisId,
+                                         jsFunc.id, argv, Int32(argc),
+                                         &kindAndFlags, &payload1, &payload2)
+            } else {
+                _call_function(
+                    jsFunc.id, argv, Int32(argc),
+                    &kindAndFlags, &payload1, &payload2
+                )
+            }
+            let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2)
+            return (result.jsValue, kindAndFlags.isException)
         }
-        let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2)
-        return (result.jsValue, kindAndFlags.isException)
     }
     if isException {
         throw result
diff --git a/Sources/JavaScriptKit/JSValue.swift b/Sources/JavaScriptKit/JSValue.swift
index 8b6cfbd92..973dfcb5d 100644
--- a/Sources/JavaScriptKit/JSValue.swift
+++ b/Sources/JavaScriptKit/JSValue.swift
@@ -203,8 +203,9 @@ public func getJSValue(this: JSObject, name: JSString) -> JSValue {
 }
 
 public func setJSValue(this: JSObject, name: JSString, value: JSValue) {
-    let rawValue = value.toRawJSValue()
-    _set_prop(this.id, name.asInternalJSRef(), rawValue.kind, rawValue.payload1, rawValue.payload2)
+    value.withRawJSValue { rawValue in
+        _set_prop(this.id, name.asInternalJSRef(), rawValue.kind, rawValue.payload1, rawValue.payload2)
+    }
 }
 
 public func getJSValue(this: JSObject, index: Int32) -> JSValue {
@@ -216,10 +217,11 @@ public func getJSValue(this: JSObject, index: Int32) -> JSValue {
 }
 
 public func setJSValue(this: JSObject, index: Int32, value: JSValue) {
-    let rawValue = value.toRawJSValue()
-    _set_subscript(this.id, index,
-                   rawValue.kind,
-                   rawValue.payload1, rawValue.payload2)
+    value.withRawJSValue { rawValue in
+        _set_subscript(this.id, index,
+                       rawValue.kind,
+                       rawValue.payload1, rawValue.payload2)
+    }
 }
 
 public func getJSValue(this: JSObject, symbol: JSSymbol) -> JSValue {
@@ -231,8 +233,9 @@ public func getJSValue(this: JSObject, symbol: JSSymbol) -> JSValue {
 }
 
 public func setJSValue(this: JSObject, symbol: JSSymbol, value: JSValue) {
-    let rawValue = value.toRawJSValue()
-    _set_prop(this.id, symbol.id, rawValue.kind, rawValue.payload1, rawValue.payload2)
+    value.withRawJSValue { rawValue in
+        _set_prop(this.id, symbol.id, rawValue.kind, rawValue.payload1, rawValue.payload2)
+    }
 }
 
 public extension JSValue {

From 0f53ca55cf73cedbaa591c9f26231bedc12703f2 Mon Sep 17 00:00:00 2001
From: Yuta Saito <kateinoigakukun@gmail.com>
Date: Wed, 17 Aug 2022 00:28:35 +0900
Subject: [PATCH 07/20] Reduce retain/release dance caused by Optional<this>

---
 .../FundamentalObjects/JSFunction.swift       | 73 +++++++++++++------
 .../FundamentalObjects/JSSymbol.swift         |  2 +-
 2 files changed, 51 insertions(+), 24 deletions(-)

diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift
index 6d0dbeda6..046a70440 100644
--- a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift
+++ b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift
@@ -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,30 +96,42 @@ 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 {
-                let result = _call_function_no_catch(
-                    jsFunc.id, argv, Int32(argc),
+    final func invokeNonThrowingJSFunction(arguments: [ConvertibleToJSValue]) -> 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()
+                let resultBitPattern = _call_function_no_catch(
+                    id, argv, Int32(argc),
                     &payload1, &payload2
                 )
-                kindAndFlags = unsafeBitCast(result, to: JavaScriptValueKindAndFlags.self)
+                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 {
+        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()
+                _call_function_with_this_no_catch(this.id,
+                                                  id, argv, Int32(argc),
+                                                  &kindAndFlags, &payload1, &payload2)
+                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
         }
     }
 }
diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSSymbol.swift b/Sources/JavaScriptKit/FundamentalObjects/JSSymbol.swift
index a0dea3937..fbb932dd1 100644
--- a/Sources/JavaScriptKit/FundamentalObjects/JSSymbol.swift
+++ b/Sources/JavaScriptKit/FundamentalObjects/JSSymbol.swift
@@ -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))
     }

From adfe1e8b5233ad1f876e2cd91477aa4781141451 Mon Sep 17 00:00:00 2001
From: Yuta Saito <kateinoigakukun@gmail.com>
Date: Wed, 17 Aug 2022 01:10:11 +0900
Subject: [PATCH 08/20] Don't escape JSFunction self

---
 Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift
index 046a70440..3c32327fd 100644
--- a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift
+++ b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift
@@ -98,7 +98,8 @@ public class JSFunction: JSObject {
     }
 
     final func invokeNonThrowingJSFunction(arguments: [ConvertibleToJSValue]) -> RawJSValue {
-        arguments.withRawJSValues { rawValues in
+        let id = self.id
+        return arguments.withRawJSValues { rawValues in
             rawValues.withUnsafeBufferPointer { bufferPointer in
                 let argv = bufferPointer.baseAddress
                 let argc = bufferPointer.count
@@ -118,7 +119,8 @@ public class JSFunction: JSObject {
     }
 
     final func invokeNonThrowingJSFunction(arguments: [ConvertibleToJSValue], this: JSObject) -> RawJSValue {
-        arguments.withRawJSValues { rawValues in
+        let id = self.id
+        return arguments.withRawJSValues { rawValues in
             rawValues.withUnsafeBufferPointer { bufferPointer in
                 let argv = bufferPointer.baseAddress
                 let argc = bufferPointer.count

From 78b442d6d0de461bb737c7153edf4ae00cf46319 Mon Sep 17 00:00:00 2001
From: Yuta Saito <kateinoigakukun@gmail.com>
Date: Wed, 17 Aug 2022 01:28:51 +0900
Subject: [PATCH 09/20] Add fast path for empty JSValue array

---
 Sources/JavaScriptKit/ConvertibleToJSValue.swift | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/Sources/JavaScriptKit/ConvertibleToJSValue.swift b/Sources/JavaScriptKit/ConvertibleToJSValue.swift
index 572e867b0..638672ca5 100644
--- a/Sources/JavaScriptKit/ConvertibleToJSValue.swift
+++ b/Sources/JavaScriptKit/ConvertibleToJSValue.swift
@@ -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

From bffe009412c79060a6684f86cb5f23d59af3a836 Mon Sep 17 00:00:00 2001
From: Yuta Saito <kateinoigakukun@gmail.com>
Date: Wed, 17 Aug 2022 01:39:14 +0900
Subject: [PATCH 10/20] Skip re-creating DataView in decodeArray

---
 Runtime/src/js-value.ts | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/Runtime/src/js-value.ts b/Runtime/src/js-value.ts
index 0f86fa554..14394259b 100644
--- a/Runtime/src/js-value.ts
+++ b/Runtime/src/js-value.ts
@@ -51,12 +51,18 @@ 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;

From 2a8146bbd894eca9ef85531dd2a1c1c28be7f1bc Mon Sep 17 00:00:00 2001
From: Yuta Saito <kateinoigakukun@gmail.com>
Date: Wed, 17 Aug 2022 02:02:15 +0900
Subject: [PATCH 11/20] make regenerate_swiftpm_resources

---
 Sources/JavaScriptKit/Runtime/index.js  | 68 +++++++++++++++++++++----
 Sources/JavaScriptKit/Runtime/index.mjs | 68 +++++++++++++++++++++----
 2 files changed, 118 insertions(+), 18 deletions(-)

diff --git a/Sources/JavaScriptKit/Runtime/index.js b/Sources/JavaScriptKit/Runtime/index.js
index 43158fbab..b56db0d82 100644
--- a/Sources/JavaScriptKit/Runtime/index.js
+++ b/Sources/JavaScriptKit/Runtime/index.js
@@ -54,12 +54,19 @@
     // Note:
     // `decodeValues` assumes that the size of RawJSValue is 16.
     const decodeArray = (ptr, length, 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;
@@ -114,6 +121,51 @@
                 assertNever(type, `Type "${type}" is not supported yet`);
         }
     };
+    const writeV2 = (value, payload1_ptr, payload2_ptr, is_exception, memory) => {
+        if (value === undefined) {
+            return 5 /* Undefined */;
+        }
+        const exceptionBit = (is_exception ? 1 : 0) << 31;
+        if (value === null) {
+            return exceptionBit | 4 /* Null */;
+        }
+        const writeRef = (kind) => {
+            memory.writeUint32(payload1_ptr, memory.retain(value));
+            return exceptionBit | kind;
+        };
+        const type = typeof value;
+        switch (type) {
+            case "boolean": {
+                memory.writeUint32(payload1_ptr, value ? 1 : 0);
+                return exceptionBit | 0 /* Boolean */;
+            }
+            case "number": {
+                memory.writeFloat64(payload2_ptr, value);
+                return exceptionBit | 2 /* Number */;
+            }
+            case "string": {
+                return writeRef(1 /* String */);
+            }
+            case "undefined": {
+                return exceptionBit | 5 /* Undefined */;
+            }
+            case "object": {
+                return writeRef(3 /* Object */);
+            }
+            case "function": {
+                return writeRef(6 /* Function */);
+            }
+            case "symbol": {
+                return writeRef(7 /* Symbol */);
+            }
+            case "bigint": {
+                return writeRef(8 /* BigInt */);
+            }
+            default:
+                assertNever(type, `Type "${type}" is not supported yet`);
+        }
+        throw new Error("Unreachable");
+    };
 
     let globalVariable;
     if (typeof globalThis !== "undefined") {
@@ -248,19 +300,17 @@
                     }
                     write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory);
                 },
-                swjs_call_function_no_catch: (ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => {
+                swjs_call_function_no_catch: (ref, argv, argc, payload1_ptr, payload2_ptr) => {
                     const func = this.memory.getObject(ref);
+                    let result = undefined;
                     let isException = true;
                     try {
                         const args = decodeArray(argv, argc, this.memory);
-                        const result = func(...args);
-                        write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory);
+                        result = func(...args);
                         isException = false;
                     }
                     finally {
-                        if (isException) {
-                            write(undefined, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory);
-                        }
+                        return writeV2(result, payload1_ptr, payload2_ptr, isException, this.memory);
                     }
                 },
                 swjs_call_function_with_this: (obj_ref, func_ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => {
diff --git a/Sources/JavaScriptKit/Runtime/index.mjs b/Sources/JavaScriptKit/Runtime/index.mjs
index 299bafdb5..0e91431fd 100644
--- a/Sources/JavaScriptKit/Runtime/index.mjs
+++ b/Sources/JavaScriptKit/Runtime/index.mjs
@@ -48,12 +48,19 @@ const decode = (kind, payload1, payload2, memory) => {
 // Note:
 // `decodeValues` assumes that the size of RawJSValue is 16.
 const decodeArray = (ptr, length, 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;
@@ -108,6 +115,51 @@ const write = (value, kind_ptr, payload1_ptr, payload2_ptr, is_exception, memory
             assertNever(type, `Type "${type}" is not supported yet`);
     }
 };
+const writeV2 = (value, payload1_ptr, payload2_ptr, is_exception, memory) => {
+    if (value === undefined) {
+        return 5 /* Undefined */;
+    }
+    const exceptionBit = (is_exception ? 1 : 0) << 31;
+    if (value === null) {
+        return exceptionBit | 4 /* Null */;
+    }
+    const writeRef = (kind) => {
+        memory.writeUint32(payload1_ptr, memory.retain(value));
+        return exceptionBit | kind;
+    };
+    const type = typeof value;
+    switch (type) {
+        case "boolean": {
+            memory.writeUint32(payload1_ptr, value ? 1 : 0);
+            return exceptionBit | 0 /* Boolean */;
+        }
+        case "number": {
+            memory.writeFloat64(payload2_ptr, value);
+            return exceptionBit | 2 /* Number */;
+        }
+        case "string": {
+            return writeRef(1 /* String */);
+        }
+        case "undefined": {
+            return exceptionBit | 5 /* Undefined */;
+        }
+        case "object": {
+            return writeRef(3 /* Object */);
+        }
+        case "function": {
+            return writeRef(6 /* Function */);
+        }
+        case "symbol": {
+            return writeRef(7 /* Symbol */);
+        }
+        case "bigint": {
+            return writeRef(8 /* BigInt */);
+        }
+        default:
+            assertNever(type, `Type "${type}" is not supported yet`);
+    }
+    throw new Error("Unreachable");
+};
 
 let globalVariable;
 if (typeof globalThis !== "undefined") {
@@ -242,19 +294,17 @@ class SwiftRuntime {
                 }
                 write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory);
             },
-            swjs_call_function_no_catch: (ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => {
+            swjs_call_function_no_catch: (ref, argv, argc, payload1_ptr, payload2_ptr) => {
                 const func = this.memory.getObject(ref);
+                let result = undefined;
                 let isException = true;
                 try {
                     const args = decodeArray(argv, argc, this.memory);
-                    const result = func(...args);
-                    write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory);
+                    result = func(...args);
                     isException = false;
                 }
                 finally {
-                    if (isException) {
-                        write(undefined, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory);
-                    }
+                    return writeV2(result, payload1_ptr, payload2_ptr, isException, this.memory);
                 }
             },
             swjs_call_function_with_this: (obj_ref, func_ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => {

From 43d9a2ae113ffa559b2fecb4c38ff20e6bc82ce7 Mon Sep 17 00:00:00 2001
From: Yuta Saito <kateinoigakukun@gmail.com>
Date: Wed, 17 Aug 2022 20:48:05 +0900
Subject: [PATCH 12/20] Apply the same techniques to call families

---
 Runtime/src/index.ts                          | 76 ++++++-------------
 Runtime/src/js-value.ts                       |  4 -
 Runtime/src/types.ts                          |  9 +--
 .../FundamentalObjects/JSFunction.swift       | 12 +--
 .../JSThrowingFunction.swift                  | 16 ++--
 Sources/JavaScriptKit/Runtime/index.js        | 50 ++++--------
 Sources/JavaScriptKit/Runtime/index.mjs       | 50 ++++--------
 Sources/JavaScriptKit/XcodeSupport.swift      |  9 +--
 .../_CJavaScriptKit/include/_CJavaScriptKit.h | 43 ++++++-----
 9 files changed, 98 insertions(+), 171 deletions(-)

diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts
index a60f0cf6e..45e1fc5ca 100644
--- a/Runtime/src/index.ts
+++ b/Runtime/src/index.ts
@@ -204,29 +204,25 @@ 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 result: any;
+            let result = undefined;
             try {
                 const args = JSValue.decodeArray(argv, argc, this.memory);
                 result = func(...args);
             } catch (error) {
-                JSValue.write(
+                return JSValue.writeV2(
                     error,
-                    kind_ptr,
                     payload1_ptr,
                     payload2_ptr,
                     true,
                     this.memory
                 );
-                return;
             }
-            JSValue.write(
+            return JSValue.writeV2(
                 result,
-                kind_ptr,
                 payload1_ptr,
                 payload2_ptr,
                 false,
@@ -241,21 +237,15 @@ export class SwiftRuntime {
             payload2_ptr: pointer
         ) => {
             const func = this.memory.getObject(ref);
-            let result = undefined;
-            let isException = true;
-            try {
-                const args = JSValue.decodeArray(argv, argc, this.memory);
-                result = func(...args);
-                isException = false;
-            } finally {
-                return JSValue.writeV2(
-                    result,
-                    payload1_ptr,
-                    payload2_ptr,
-                    isException,
-                    this.memory
-                );
-            }
+            const args = JSValue.decodeArray(argv, argc, this.memory);
+            const result = func(...args);
+            return JSValue.writeV2(
+                result,
+                payload1_ptr,
+                payload2_ptr,
+                false,
+                this.memory
+            );
         },
 
         swjs_call_function_with_this: (
@@ -263,7 +253,6 @@ export class SwiftRuntime {
             func_ref: ref,
             argv: pointer,
             argc: number,
-            kind_ptr: pointer,
             payload1_ptr: pointer,
             payload2_ptr: pointer
         ) => {
@@ -274,19 +263,16 @@ export class SwiftRuntime {
                 const args = JSValue.decodeArray(argv, argc, this.memory);
                 result = func.apply(obj, args);
             } catch (error) {
-                JSValue.write(
+                return JSValue.writeV2(
                     error,
-                    kind_ptr,
                     payload1_ptr,
                     payload2_ptr,
                     true,
                     this.memory
                 );
-                return;
             }
-            JSValue.write(
+            return JSValue.writeV2(
                 result,
-                kind_ptr,
                 payload1_ptr,
                 payload2_ptr,
                 false,
@@ -298,37 +284,21 @@ 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 {
+            let result = undefined;
                 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
-                    );
-                }
-            }
+                result = func.apply(obj, args);
+            return JSValue.writeV2(
+                result,
+                payload1_ptr,
+                payload2_ptr,
+                false,
+                this.memory
+            );
         },
 
         swjs_call_new: (ref: ref, argv: pointer, argc: number) => {
diff --git a/Runtime/src/js-value.ts b/Runtime/src/js-value.ts
index 14394259b..7539ba9af 100644
--- a/Runtime/src/js-value.ts
+++ b/Runtime/src/js-value.ts
@@ -136,10 +136,6 @@ export const writeV2 = (
     is_exception: boolean,
     memory: Memory
 ): JavaScriptValueKindAndFlags => {
-    if (value === undefined) {
-        return Kind.Undefined;
-    }
-
     const exceptionBit = (is_exception ? 1 : 0) << 31;
     if (value === null) {
         return exceptionBit | Kind.Null;
diff --git a/Runtime/src/types.ts b/Runtime/src/types.ts
index f46ee9cdd..6dd7bb2d6 100644
--- a/Runtime/src/types.ts
+++ b/Runtime/src/types.ts
@@ -56,10 +56,9 @@ export interface ImportedFunctions {
         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,
@@ -72,19 +71,17 @@ export interface ImportedFunctions {
         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,
diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift
index 3c32327fd..66c613402 100644
--- a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift
+++ b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift
@@ -103,14 +103,13 @@ public class JSFunction: JSObject {
             rawValues.withUnsafeBufferPointer { bufferPointer in
                 let argv = bufferPointer.baseAddress
                 let argc = bufferPointer.count
-                var kindAndFlags = JavaScriptValueKindAndFlags()
                 var payload1 = JavaScriptPayload1()
                 var payload2 = JavaScriptPayload2()
                 let resultBitPattern = _call_function_no_catch(
                     id, argv, Int32(argc),
                     &payload1, &payload2
                 )
-                kindAndFlags = unsafeBitCast(resultBitPattern, to: JavaScriptValueKindAndFlags.self)
+                let kindAndFlags = unsafeBitCast(resultBitPattern, to: JavaScriptValueKindAndFlags.self)
                 assert(!kindAndFlags.isException)
                 let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2)
                 return result
@@ -124,12 +123,13 @@ public class JSFunction: JSObject {
             rawValues.withUnsafeBufferPointer { bufferPointer in
                 let argv = bufferPointer.baseAddress
                 let argc = bufferPointer.count
-                var kindAndFlags = JavaScriptValueKindAndFlags()
                 var payload1 = JavaScriptPayload1()
                 var payload2 = JavaScriptPayload2()
-                _call_function_with_this_no_catch(this.id,
-                                                  id, argv, Int32(argc),
-                                                  &kindAndFlags, &payload1, &payload2)
+                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
diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift
index 3e21f0e1b..705899000 100644
--- a/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift
+++ b/Sources/JavaScriptKit/FundamentalObjects/JSThrowingFunction.swift
@@ -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)
diff --git a/Sources/JavaScriptKit/Runtime/index.js b/Sources/JavaScriptKit/Runtime/index.js
index b56db0d82..c00154d4b 100644
--- a/Sources/JavaScriptKit/Runtime/index.js
+++ b/Sources/JavaScriptKit/Runtime/index.js
@@ -122,9 +122,6 @@
         }
     };
     const writeV2 = (value, payload1_ptr, payload2_ptr, is_exception, memory) => {
-        if (value === undefined) {
-            return 5 /* Undefined */;
-        }
         const exceptionBit = (is_exception ? 1 : 0) << 31;
         if (value === null) {
             return exceptionBit | 4 /* Null */;
@@ -287,33 +284,25 @@
                     const bytes = this.memory.getObject(ref);
                     this.memory.writeBytes(buffer, bytes);
                 },
-                swjs_call_function: (ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => {
+                swjs_call_function: (ref, argv, argc, payload1_ptr, payload2_ptr) => {
                     const func = this.memory.getObject(ref);
-                    let result;
+                    let result = undefined;
                     try {
                         const args = decodeArray(argv, argc, this.memory);
                         result = func(...args);
                     }
                     catch (error) {
-                        write(error, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory);
-                        return;
+                        return writeV2(error, payload1_ptr, payload2_ptr, true, this.memory);
                     }
-                    write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory);
+                    return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
                 },
                 swjs_call_function_no_catch: (ref, argv, argc, payload1_ptr, payload2_ptr) => {
                     const func = this.memory.getObject(ref);
-                    let result = undefined;
-                    let isException = true;
-                    try {
-                        const args = decodeArray(argv, argc, this.memory);
-                        result = func(...args);
-                        isException = false;
-                    }
-                    finally {
-                        return writeV2(result, payload1_ptr, payload2_ptr, isException, this.memory);
-                    }
+                    const args = decodeArray(argv, argc, this.memory);
+                    const result = func(...args);
+                    return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
                 },
-                swjs_call_function_with_this: (obj_ref, func_ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => {
+                swjs_call_function_with_this: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => {
                     const obj = this.memory.getObject(obj_ref);
                     const func = this.memory.getObject(func_ref);
                     let result;
@@ -322,26 +311,17 @@
                         result = func.apply(obj, args);
                     }
                     catch (error) {
-                        write(error, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory);
-                        return;
+                        return writeV2(error, payload1_ptr, payload2_ptr, true, this.memory);
                     }
-                    write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory);
+                    return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
                 },
-                swjs_call_function_with_this_no_catch: (obj_ref, func_ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => {
+                swjs_call_function_with_this_no_catch: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => {
                     const obj = this.memory.getObject(obj_ref);
                     const func = this.memory.getObject(func_ref);
-                    let isException = true;
-                    try {
-                        const args = decodeArray(argv, argc, this.memory);
-                        const result = func.apply(obj, args);
-                        write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory);
-                        isException = false;
-                    }
-                    finally {
-                        if (isException) {
-                            write(undefined, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory);
-                        }
-                    }
+                    let result = undefined;
+                    const args = decodeArray(argv, argc, this.memory);
+                    result = func.apply(obj, args);
+                    return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
                 },
                 swjs_call_new: (ref, argv, argc) => {
                     const constructor = this.memory.getObject(ref);
diff --git a/Sources/JavaScriptKit/Runtime/index.mjs b/Sources/JavaScriptKit/Runtime/index.mjs
index 0e91431fd..32db67134 100644
--- a/Sources/JavaScriptKit/Runtime/index.mjs
+++ b/Sources/JavaScriptKit/Runtime/index.mjs
@@ -116,9 +116,6 @@ const write = (value, kind_ptr, payload1_ptr, payload2_ptr, is_exception, memory
     }
 };
 const writeV2 = (value, payload1_ptr, payload2_ptr, is_exception, memory) => {
-    if (value === undefined) {
-        return 5 /* Undefined */;
-    }
     const exceptionBit = (is_exception ? 1 : 0) << 31;
     if (value === null) {
         return exceptionBit | 4 /* Null */;
@@ -281,33 +278,25 @@ class SwiftRuntime {
                 const bytes = this.memory.getObject(ref);
                 this.memory.writeBytes(buffer, bytes);
             },
-            swjs_call_function: (ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => {
+            swjs_call_function: (ref, argv, argc, payload1_ptr, payload2_ptr) => {
                 const func = this.memory.getObject(ref);
-                let result;
+                let result = undefined;
                 try {
                     const args = decodeArray(argv, argc, this.memory);
                     result = func(...args);
                 }
                 catch (error) {
-                    write(error, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory);
-                    return;
+                    return writeV2(error, payload1_ptr, payload2_ptr, true, this.memory);
                 }
-                write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory);
+                return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
             },
             swjs_call_function_no_catch: (ref, argv, argc, payload1_ptr, payload2_ptr) => {
                 const func = this.memory.getObject(ref);
-                let result = undefined;
-                let isException = true;
-                try {
-                    const args = decodeArray(argv, argc, this.memory);
-                    result = func(...args);
-                    isException = false;
-                }
-                finally {
-                    return writeV2(result, payload1_ptr, payload2_ptr, isException, this.memory);
-                }
+                const args = decodeArray(argv, argc, this.memory);
+                const result = func(...args);
+                return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
             },
-            swjs_call_function_with_this: (obj_ref, func_ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => {
+            swjs_call_function_with_this: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => {
                 const obj = this.memory.getObject(obj_ref);
                 const func = this.memory.getObject(func_ref);
                 let result;
@@ -316,26 +305,17 @@ class SwiftRuntime {
                     result = func.apply(obj, args);
                 }
                 catch (error) {
-                    write(error, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory);
-                    return;
+                    return writeV2(error, payload1_ptr, payload2_ptr, true, this.memory);
                 }
-                write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory);
+                return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
             },
-            swjs_call_function_with_this_no_catch: (obj_ref, func_ref, argv, argc, kind_ptr, payload1_ptr, payload2_ptr) => {
+            swjs_call_function_with_this_no_catch: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => {
                 const obj = this.memory.getObject(obj_ref);
                 const func = this.memory.getObject(func_ref);
-                let isException = true;
-                try {
-                    const args = decodeArray(argv, argc, this.memory);
-                    const result = func.apply(obj, args);
-                    write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory);
-                    isException = false;
-                }
-                finally {
-                    if (isException) {
-                        write(undefined, kind_ptr, payload1_ptr, payload2_ptr, true, this.memory);
-                    }
-                }
+                let result = undefined;
+                const args = decodeArray(argv, argc, this.memory);
+                result = func.apply(obj, args);
+                return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
             },
             swjs_call_new: (ref, argv, argc) => {
                 const constructor = this.memory.getObject(ref);
diff --git a/Sources/JavaScriptKit/XcodeSupport.swift b/Sources/JavaScriptKit/XcodeSupport.swift
index d231440d9..303b44a33 100644
--- a/Sources/JavaScriptKit/XcodeSupport.swift
+++ b/Sources/JavaScriptKit/XcodeSupport.swift
@@ -52,10 +52,9 @@ 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,
@@ -66,18 +65,16 @@ import _CJavaScriptKit
         _: 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
diff --git a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h
index b1fa593f4..846c834f2 100644
--- a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h
+++ b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h
@@ -176,22 +176,23 @@ 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.
@@ -210,17 +211,18 @@ extern uint32_t _call_function_no_catch(
 /// @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.
 ///
@@ -228,17 +230,18 @@ extern void _call_function_with_this(const JavaScriptObjectRef _this,
 /// @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.
 ///

From 2b86b418207ccc48f699a8534c6241c5a4777d7d Mon Sep 17 00:00:00 2001
From: Yuta Saito <kateinoigakukun@gmail.com>
Date: Thu, 18 Aug 2022 02:57:19 +0900
Subject: [PATCH 13/20] Reuse DataView as much as possible

---
 Runtime/src/index.ts                    | 90 +++++++++++++++----------
 Sources/JavaScriptKit/Runtime/index.js  | 90 +++++++++++++++----------
 Sources/JavaScriptKit/Runtime/index.mjs | 90 +++++++++++++++----------
 3 files changed, 159 insertions(+), 111 deletions(-)

diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts
index 45e1fc5ca..afc6c63b2 100644
--- a/Runtime/src/index.ts
+++ b/Runtime/src/index.ts
@@ -84,6 +84,7 @@ 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;
@@ -93,12 +94,12 @@ export class SwiftRuntime {
                 base + 4,
                 base + 8,
                 false,
-                this.memory
+                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,9 +128,10 @@ 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: (
@@ -139,8 +141,9 @@ export class SwiftRuntime {
             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(
                 result,
@@ -148,7 +151,7 @@ export class SwiftRuntime {
                 payload1_ptr,
                 payload2_ptr,
                 false,
-                this.memory
+                memory
             );
         },
 
@@ -159,8 +162,9 @@ 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: (
@@ -183,21 +187,24 @@ 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: (
@@ -207,10 +214,11 @@ export class SwiftRuntime {
             payload1_ptr: pointer,
             payload2_ptr: pointer
         ) => {
-            const func = this.memory.getObject(ref);
+            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) {
                 return JSValue.writeV2(
@@ -236,8 +244,9 @@ export class SwiftRuntime {
             payload1_ptr: pointer,
             payload2_ptr: pointer
         ) => {
-            const func = this.memory.getObject(ref);
-            const args = JSValue.decodeArray(argv, argc, this.memory);
+            const memory = this.memory;
+            const func = memory.getObject(ref);
+            const args = JSValue.decodeArray(argv, argc, memory);
             const result = func(...args);
             return JSValue.writeV2(
                 result,
@@ -256,11 +265,12 @@ export class SwiftRuntime {
             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) {
                 return JSValue.writeV2(
@@ -287,10 +297,11 @@ export class SwiftRuntime {
             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 = undefined;
-                const args = JSValue.decodeArray(argv, argc, this.memory);
+                const args = JSValue.decodeArray(argv, argc, memory);
                 result = func.apply(obj, args);
             return JSValue.writeV2(
                 result,
@@ -302,8 +313,9 @@ export class SwiftRuntime {
         },
 
         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);
         },
@@ -315,10 +327,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(
@@ -331,20 +344,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;
         },
 
@@ -378,9 +393,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) => {
diff --git a/Sources/JavaScriptKit/Runtime/index.js b/Sources/JavaScriptKit/Runtime/index.js
index c00154d4b..03affa6cf 100644
--- a/Sources/JavaScriptKit/Runtime/index.js
+++ b/Sources/JavaScriptKit/Runtime/index.js
@@ -246,20 +246,23 @@
             this.importObjects = () => this.wasmImports;
             this.wasmImports = {
                 swjs_set_prop: (ref, name, kind, payload1, payload2) => {
-                    const obj = this.memory.getObject(ref);
-                    const key = this.memory.getObject(name);
-                    const value = decode(kind, payload1, payload2, this.memory);
+                    const memory = this.memory;
+                    const obj = memory.getObject(ref);
+                    const key = memory.getObject(name);
+                    const value = decode(kind, payload1, payload2, memory);
                     obj[key] = value;
                 },
                 swjs_get_prop: (ref, name, kind_ptr, payload1_ptr, payload2_ptr) => {
-                    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];
-                    write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory);
+                    write(result, kind_ptr, payload1_ptr, payload2_ptr, false, memory);
                 },
                 swjs_set_subscript: (ref, index, kind, payload1, payload2) => {
-                    const obj = this.memory.getObject(ref);
-                    const value = decode(kind, payload1, payload2, this.memory);
+                    const memory = this.memory;
+                    const obj = memory.getObject(ref);
+                    const value = decode(kind, payload1, payload2, memory);
                     obj[index] = value;
                 },
                 swjs_get_subscript: (ref, index, kind_ptr, payload1_ptr, payload2_ptr) => {
@@ -268,27 +271,31 @@
                     write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory);
                 },
                 swjs_encode_string: (ref, bytes_ptr_result) => {
-                    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, length) => {
-                    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, buffer) => {
-                    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, argv, argc, payload1_ptr, payload2_ptr) => {
-                    const func = this.memory.getObject(ref);
+                    const memory = this.memory;
+                    const func = memory.getObject(ref);
                     let result = undefined;
                     try {
-                        const args = decodeArray(argv, argc, this.memory);
+                        const args = decodeArray(argv, argc, memory);
                         result = func(...args);
                     }
                     catch (error) {
@@ -297,17 +304,19 @@
                     return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
                 },
                 swjs_call_function_no_catch: (ref, argv, argc, payload1_ptr, payload2_ptr) => {
-                    const func = this.memory.getObject(ref);
-                    const args = decodeArray(argv, argc, this.memory);
+                    const memory = this.memory;
+                    const func = memory.getObject(ref);
+                    const args = decodeArray(argv, argc, memory);
                     const result = func(...args);
                     return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
                 },
                 swjs_call_function_with_this: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => {
-                    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;
                     try {
-                        const args = decodeArray(argv, argc, this.memory);
+                        const args = decodeArray(argv, argc, memory);
                         result = func.apply(obj, args);
                     }
                     catch (error) {
@@ -316,36 +325,41 @@
                     return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
                 },
                 swjs_call_function_with_this_no_catch: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => {
-                    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 = undefined;
-                    const args = decodeArray(argv, argc, this.memory);
+                    const args = decodeArray(argv, argc, memory);
                     result = func.apply(obj, args);
                     return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
                 },
                 swjs_call_new: (ref, argv, argc) => {
-                    const constructor = this.memory.getObject(ref);
-                    const args = decodeArray(argv, argc, this.memory);
+                    const memory = this.memory;
+                    const constructor = memory.getObject(ref);
+                    const args = decodeArray(argv, argc, memory);
                     const instance = new constructor(...args);
                     return this.memory.retain(instance);
                 },
                 swjs_call_throwing_new: (ref, argv, argc, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr) => {
-                    const constructor = this.memory.getObject(ref);
+                    let memory = this.memory;
+                    const constructor = memory.getObject(ref);
                     let result;
                     try {
-                        const args = decodeArray(argv, argc, this.memory);
+                        const args = decodeArray(argv, argc, memory);
                         result = new constructor(...args);
                     }
                     catch (error) {
                         write(error, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, true, this.memory);
                         return -1;
                     }
-                    write(null, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, false, this.memory);
-                    return this.memory.retain(result);
+                    memory = this.memory;
+                    write(null, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, false, memory);
+                    return memory.retain(result);
                 },
                 swjs_instanceof: (obj_ref, constructor_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;
                 },
                 swjs_create_function: (host_func_id, line, file) => {
@@ -363,9 +377,10 @@
                     return this.memory.retain(array.slice());
                 },
                 swjs_load_typed_array: (ref, buffer) => {
-                    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) => {
                     this.memory.release(ref);
@@ -438,14 +453,15 @@
         callHostFunction(host_func_id, line, file, args) {
             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;
-                write(argument, base, base + 4, base + 8, false, this.memory);
+                write(argument, base, base + 4, base + 8, false, memory);
             }
             let output;
             // This ref is released by the swjs_call_host_function implementation
-            const callback_func_ref = this.memory.retain((result) => {
+            const callback_func_ref = memory.retain((result) => {
                 output = result;
             });
             const alreadyReleased = this.exports.swjs_call_host_function(host_func_id, argv, argc, callback_func_ref);
diff --git a/Sources/JavaScriptKit/Runtime/index.mjs b/Sources/JavaScriptKit/Runtime/index.mjs
index 32db67134..8b2396efd 100644
--- a/Sources/JavaScriptKit/Runtime/index.mjs
+++ b/Sources/JavaScriptKit/Runtime/index.mjs
@@ -240,20 +240,23 @@ class SwiftRuntime {
         this.importObjects = () => this.wasmImports;
         this.wasmImports = {
             swjs_set_prop: (ref, name, kind, payload1, payload2) => {
-                const obj = this.memory.getObject(ref);
-                const key = this.memory.getObject(name);
-                const value = decode(kind, payload1, payload2, this.memory);
+                const memory = this.memory;
+                const obj = memory.getObject(ref);
+                const key = memory.getObject(name);
+                const value = decode(kind, payload1, payload2, memory);
                 obj[key] = value;
             },
             swjs_get_prop: (ref, name, kind_ptr, payload1_ptr, payload2_ptr) => {
-                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];
-                write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory);
+                write(result, kind_ptr, payload1_ptr, payload2_ptr, false, memory);
             },
             swjs_set_subscript: (ref, index, kind, payload1, payload2) => {
-                const obj = this.memory.getObject(ref);
-                const value = decode(kind, payload1, payload2, this.memory);
+                const memory = this.memory;
+                const obj = memory.getObject(ref);
+                const value = decode(kind, payload1, payload2, memory);
                 obj[index] = value;
             },
             swjs_get_subscript: (ref, index, kind_ptr, payload1_ptr, payload2_ptr) => {
@@ -262,27 +265,31 @@ class SwiftRuntime {
                 write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory);
             },
             swjs_encode_string: (ref, bytes_ptr_result) => {
-                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, length) => {
-                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, buffer) => {
-                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, argv, argc, payload1_ptr, payload2_ptr) => {
-                const func = this.memory.getObject(ref);
+                const memory = this.memory;
+                const func = memory.getObject(ref);
                 let result = undefined;
                 try {
-                    const args = decodeArray(argv, argc, this.memory);
+                    const args = decodeArray(argv, argc, memory);
                     result = func(...args);
                 }
                 catch (error) {
@@ -291,17 +298,19 @@ class SwiftRuntime {
                 return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
             },
             swjs_call_function_no_catch: (ref, argv, argc, payload1_ptr, payload2_ptr) => {
-                const func = this.memory.getObject(ref);
-                const args = decodeArray(argv, argc, this.memory);
+                const memory = this.memory;
+                const func = memory.getObject(ref);
+                const args = decodeArray(argv, argc, memory);
                 const result = func(...args);
                 return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
             },
             swjs_call_function_with_this: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => {
-                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;
                 try {
-                    const args = decodeArray(argv, argc, this.memory);
+                    const args = decodeArray(argv, argc, memory);
                     result = func.apply(obj, args);
                 }
                 catch (error) {
@@ -310,36 +319,41 @@ class SwiftRuntime {
                 return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
             },
             swjs_call_function_with_this_no_catch: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => {
-                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 = undefined;
-                const args = decodeArray(argv, argc, this.memory);
+                const args = decodeArray(argv, argc, memory);
                 result = func.apply(obj, args);
                 return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
             },
             swjs_call_new: (ref, argv, argc) => {
-                const constructor = this.memory.getObject(ref);
-                const args = decodeArray(argv, argc, this.memory);
+                const memory = this.memory;
+                const constructor = memory.getObject(ref);
+                const args = decodeArray(argv, argc, memory);
                 const instance = new constructor(...args);
                 return this.memory.retain(instance);
             },
             swjs_call_throwing_new: (ref, argv, argc, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr) => {
-                const constructor = this.memory.getObject(ref);
+                let memory = this.memory;
+                const constructor = memory.getObject(ref);
                 let result;
                 try {
-                    const args = decodeArray(argv, argc, this.memory);
+                    const args = decodeArray(argv, argc, memory);
                     result = new constructor(...args);
                 }
                 catch (error) {
                     write(error, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, true, this.memory);
                     return -1;
                 }
-                write(null, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, false, this.memory);
-                return this.memory.retain(result);
+                memory = this.memory;
+                write(null, exception_kind_ptr, exception_payload1_ptr, exception_payload2_ptr, false, memory);
+                return memory.retain(result);
             },
             swjs_instanceof: (obj_ref, constructor_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;
             },
             swjs_create_function: (host_func_id, line, file) => {
@@ -357,9 +371,10 @@ class SwiftRuntime {
                 return this.memory.retain(array.slice());
             },
             swjs_load_typed_array: (ref, buffer) => {
-                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) => {
                 this.memory.release(ref);
@@ -432,14 +447,15 @@ class SwiftRuntime {
     callHostFunction(host_func_id, line, file, args) {
         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;
-            write(argument, base, base + 4, base + 8, false, this.memory);
+            write(argument, base, base + 4, base + 8, false, memory);
         }
         let output;
         // This ref is released by the swjs_call_host_function implementation
-        const callback_func_ref = this.memory.retain((result) => {
+        const callback_func_ref = memory.retain((result) => {
             output = result;
         });
         const alreadyReleased = this.exports.swjs_call_host_function(host_func_id, argv, argc, callback_func_ref);

From 5a3ecf2799db828ca4607bd5f409059a6da8ca45 Mon Sep 17 00:00:00 2001
From: Yuta Saito <kateinoigakukun@gmail.com>
Date: Thu, 18 Aug 2022 02:58:09 +0900
Subject: [PATCH 14/20] npm run format

---
 Runtime/src/index.ts    | 13 +++----------
 Runtime/src/js-value.ts |  7 ++++---
 2 files changed, 7 insertions(+), 13 deletions(-)

diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts
index afc6c63b2..73a3672ae 100644
--- a/Runtime/src/index.ts
+++ b/Runtime/src/index.ts
@@ -88,14 +88,7 @@ export class SwiftRuntime {
         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,
-                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
@@ -301,8 +294,8 @@ export class SwiftRuntime {
             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);
+            const args = JSValue.decodeArray(argv, argc, memory);
+            result = func.apply(obj, args);
             return JSValue.writeV2(
                 result,
                 payload1_ptr,
diff --git a/Runtime/src/js-value.ts b/Runtime/src/js-value.ts
index 7539ba9af..b76d8b217 100644
--- a/Runtime/src/js-value.ts
+++ b/Runtime/src/js-value.ts
@@ -52,7 +52,9 @@ export const decode = (
 // `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 []; }
+    if (length === 0) {
+        return [];
+    }
 
     let result = [];
     // It's safe to hold DataView here because WebAssembly.Memory.buffer won't
@@ -128,7 +130,6 @@ export const write = (
     }
 };
 
-
 export const writeV2 = (
     value: any,
     payload1_ptr: pointer,
@@ -143,7 +144,7 @@ export const writeV2 = (
 
     const writeRef = (kind: Kind) => {
         memory.writeUint32(payload1_ptr, memory.retain(value));
-        return exceptionBit | kind
+        return exceptionBit | kind;
     };
 
     const type = typeof value;

From 8184119185d0df88e996b987f9a84a52e15f4eaf Mon Sep 17 00:00:00 2001
From: Yuta Saito <kateinoigakukun@gmail.com>
Date: Thu, 18 Aug 2022 14:54:35 +0900
Subject: [PATCH 15/20] Optimize swjs_get_prop to reduce memory store

---
 Runtime/src/index.ts                             |  4 +---
 Runtime/src/types.ts                             |  4 ++--
 Sources/JavaScriptKit/JSValue.swift              | 16 ++++++++++------
 Sources/JavaScriptKit/Runtime/index.js           |  4 ++--
 Sources/JavaScriptKit/Runtime/index.mjs          |  4 ++--
 Sources/JavaScriptKit/XcodeSupport.swift         |  3 +--
 .../_CJavaScriptKit/include/_CJavaScriptKit.h    | 13 +++++++------
 7 files changed, 25 insertions(+), 23 deletions(-)

diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts
index 73a3672ae..02b56c197 100644
--- a/Runtime/src/index.ts
+++ b/Runtime/src/index.ts
@@ -130,7 +130,6 @@ export class SwiftRuntime {
         swjs_get_prop: (
             ref: ref,
             name: ref,
-            kind_ptr: pointer,
             payload1_ptr: pointer,
             payload2_ptr: pointer
         ) => {
@@ -138,9 +137,8 @@ export class SwiftRuntime {
             const obj = memory.getObject(ref);
             const key = memory.getObject(name);
             const result = obj[key];
-            JSValue.write(
+            return JSValue.writeV2(
                 result,
-                kind_ptr,
                 payload1_ptr,
                 payload2_ptr,
                 false,
diff --git a/Runtime/src/types.ts b/Runtime/src/types.ts
index 6dd7bb2d6..3cd90ae2d 100644
--- a/Runtime/src/types.ts
+++ b/Runtime/src/types.ts
@@ -3,6 +3,7 @@ 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 {
@@ -31,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,
diff --git a/Sources/JavaScriptKit/JSValue.swift b/Sources/JavaScriptKit/JSValue.swift
index 973dfcb5d..99a051a54 100644
--- a/Sources/JavaScriptKit/JSValue.swift
+++ b/Sources/JavaScriptKit/JSValue.swift
@@ -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
 }
 
@@ -226,9 +228,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
 }
 
diff --git a/Sources/JavaScriptKit/Runtime/index.js b/Sources/JavaScriptKit/Runtime/index.js
index 03affa6cf..ce8b611eb 100644
--- a/Sources/JavaScriptKit/Runtime/index.js
+++ b/Sources/JavaScriptKit/Runtime/index.js
@@ -252,12 +252,12 @@
                     const value = decode(kind, payload1, payload2, memory);
                     obj[key] = value;
                 },
-                swjs_get_prop: (ref, name, kind_ptr, payload1_ptr, payload2_ptr) => {
+                swjs_get_prop: (ref, name, payload1_ptr, payload2_ptr) => {
                     const memory = this.memory;
                     const obj = memory.getObject(ref);
                     const key = memory.getObject(name);
                     const result = obj[key];
-                    write(result, kind_ptr, payload1_ptr, payload2_ptr, false, memory);
+                    return writeV2(result, payload1_ptr, payload2_ptr, false, memory);
                 },
                 swjs_set_subscript: (ref, index, kind, payload1, payload2) => {
                     const memory = this.memory;
diff --git a/Sources/JavaScriptKit/Runtime/index.mjs b/Sources/JavaScriptKit/Runtime/index.mjs
index 8b2396efd..2c40e6bf0 100644
--- a/Sources/JavaScriptKit/Runtime/index.mjs
+++ b/Sources/JavaScriptKit/Runtime/index.mjs
@@ -246,12 +246,12 @@ class SwiftRuntime {
                 const value = decode(kind, payload1, payload2, memory);
                 obj[key] = value;
             },
-            swjs_get_prop: (ref, name, kind_ptr, payload1_ptr, payload2_ptr) => {
+            swjs_get_prop: (ref, name, payload1_ptr, payload2_ptr) => {
                 const memory = this.memory;
                 const obj = memory.getObject(ref);
                 const key = memory.getObject(name);
                 const result = obj[key];
-                write(result, kind_ptr, payload1_ptr, payload2_ptr, false, memory);
+                return writeV2(result, payload1_ptr, payload2_ptr, false, memory);
             },
             swjs_set_subscript: (ref, index, kind, payload1, payload2) => {
                 const memory = this.memory;
diff --git a/Sources/JavaScriptKit/XcodeSupport.swift b/Sources/JavaScriptKit/XcodeSupport.swift
index 303b44a33..34eada81f 100644
--- a/Sources/JavaScriptKit/XcodeSupport.swift
+++ b/Sources/JavaScriptKit/XcodeSupport.swift
@@ -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,
diff --git a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h
index 846c834f2..00bde65c9 100644
--- a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h
+++ b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h
@@ -93,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.
 ///

From a1b690e5efdceef3db15a7ea12b3963ad1118352 Mon Sep 17 00:00:00 2001
From: Yuta Saito <kateinoigakukun@gmail.com>
Date: Thu, 18 Aug 2022 14:59:56 +0900
Subject: [PATCH 16/20] Optimize _get_subscript to reduce memory store

---
 Runtime/src/index.ts                              |  4 +---
 Runtime/src/types.ts                              |  3 +--
 Sources/JavaScriptKit/JSValue.swift               |  8 +++++---
 Sources/JavaScriptKit/Runtime/index.js            |  4 ++--
 Sources/JavaScriptKit/Runtime/index.mjs           |  4 ++--
 Sources/JavaScriptKit/XcodeSupport.swift          |  3 +--
 Sources/_CJavaScriptKit/include/_CJavaScriptKit.h | 13 +++++++------
 7 files changed, 19 insertions(+), 20 deletions(-)

diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts
index 02b56c197..bb9e2fe12 100644
--- a/Runtime/src/index.ts
+++ b/Runtime/src/index.ts
@@ -161,15 +161,13 @@ export class SwiftRuntime {
         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.writeV2(
                 result,
-                kind_ptr,
                 payload1_ptr,
                 payload2_ptr,
                 false,
diff --git a/Runtime/src/types.ts b/Runtime/src/types.ts
index 3cd90ae2d..ff20999ea 100644
--- a/Runtime/src/types.ts
+++ b/Runtime/src/types.ts
@@ -45,10 +45,9 @@ 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;
diff --git a/Sources/JavaScriptKit/JSValue.swift b/Sources/JavaScriptKit/JSValue.swift
index 99a051a54..58b28e079 100644
--- a/Sources/JavaScriptKit/JSValue.swift
+++ b/Sources/JavaScriptKit/JSValue.swift
@@ -212,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
 }
 
diff --git a/Sources/JavaScriptKit/Runtime/index.js b/Sources/JavaScriptKit/Runtime/index.js
index ce8b611eb..c383ab513 100644
--- a/Sources/JavaScriptKit/Runtime/index.js
+++ b/Sources/JavaScriptKit/Runtime/index.js
@@ -265,10 +265,10 @@
                     const value = decode(kind, payload1, payload2, memory);
                     obj[index] = value;
                 },
-                swjs_get_subscript: (ref, index, kind_ptr, payload1_ptr, payload2_ptr) => {
+                swjs_get_subscript: (ref, index, payload1_ptr, payload2_ptr) => {
                     const obj = this.memory.getObject(ref);
                     const result = obj[index];
-                    write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory);
+                    return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
                 },
                 swjs_encode_string: (ref, bytes_ptr_result) => {
                     const memory = this.memory;
diff --git a/Sources/JavaScriptKit/Runtime/index.mjs b/Sources/JavaScriptKit/Runtime/index.mjs
index 2c40e6bf0..73522ce95 100644
--- a/Sources/JavaScriptKit/Runtime/index.mjs
+++ b/Sources/JavaScriptKit/Runtime/index.mjs
@@ -259,10 +259,10 @@ class SwiftRuntime {
                 const value = decode(kind, payload1, payload2, memory);
                 obj[index] = value;
             },
-            swjs_get_subscript: (ref, index, kind_ptr, payload1_ptr, payload2_ptr) => {
+            swjs_get_subscript: (ref, index, payload1_ptr, payload2_ptr) => {
                 const obj = this.memory.getObject(ref);
                 const result = obj[index];
-                write(result, kind_ptr, payload1_ptr, payload2_ptr, false, this.memory);
+                return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
             },
             swjs_encode_string: (ref, bytes_ptr_result) => {
                 const memory = this.memory;
diff --git a/Sources/JavaScriptKit/XcodeSupport.swift b/Sources/JavaScriptKit/XcodeSupport.swift
index 34eada81f..9689cf3b0 100644
--- a/Sources/JavaScriptKit/XcodeSupport.swift
+++ b/Sources/JavaScriptKit/XcodeSupport.swift
@@ -29,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>!
diff --git a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h
index 00bde65c9..adf732bd4 100644
--- a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h
+++ b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h
@@ -124,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.
 ///

From 4110f7f1aa3b5372a67589a7c19873b553b55ee7 Mon Sep 17 00:00:00 2001
From: Yuta Saito <kateinoigakukun@gmail.com>
Date: Thu, 18 Aug 2022 15:18:41 +0900
Subject: [PATCH 17/20] Rename writeV2 -> writeAndReturnKindBits

---
 Runtime/src/index.ts                    | 16 ++++++++--------
 Runtime/src/js-value.ts                 |  4 +++-
 Sources/JavaScriptKit/Runtime/index.js  | 20 +++++++++++---------
 Sources/JavaScriptKit/Runtime/index.mjs | 20 +++++++++++---------
 4 files changed, 33 insertions(+), 27 deletions(-)

diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts
index bb9e2fe12..a9da3eb9f 100644
--- a/Runtime/src/index.ts
+++ b/Runtime/src/index.ts
@@ -137,7 +137,7 @@ export class SwiftRuntime {
             const obj = memory.getObject(ref);
             const key = memory.getObject(name);
             const result = obj[key];
-            return JSValue.writeV2(
+            return JSValue.writeAndReturnKindBits(
                 result,
                 payload1_ptr,
                 payload2_ptr,
@@ -166,7 +166,7 @@ export class SwiftRuntime {
         ) => {
             const obj = this.memory.getObject(ref);
             const result = obj[index];
-            return JSValue.writeV2(
+            return JSValue.writeAndReturnKindBits(
                 result,
                 payload1_ptr,
                 payload2_ptr,
@@ -210,7 +210,7 @@ export class SwiftRuntime {
                 const args = JSValue.decodeArray(argv, argc, memory);
                 result = func(...args);
             } catch (error) {
-                return JSValue.writeV2(
+                return JSValue.writeAndReturnKindBits(
                     error,
                     payload1_ptr,
                     payload2_ptr,
@@ -218,7 +218,7 @@ export class SwiftRuntime {
                     this.memory
                 );
             }
-            return JSValue.writeV2(
+            return JSValue.writeAndReturnKindBits(
                 result,
                 payload1_ptr,
                 payload2_ptr,
@@ -237,7 +237,7 @@ export class SwiftRuntime {
             const func = memory.getObject(ref);
             const args = JSValue.decodeArray(argv, argc, memory);
             const result = func(...args);
-            return JSValue.writeV2(
+            return JSValue.writeAndReturnKindBits(
                 result,
                 payload1_ptr,
                 payload2_ptr,
@@ -262,7 +262,7 @@ export class SwiftRuntime {
                 const args = JSValue.decodeArray(argv, argc, memory);
                 result = func.apply(obj, args);
             } catch (error) {
-                return JSValue.writeV2(
+                return JSValue.writeAndReturnKindBits(
                     error,
                     payload1_ptr,
                     payload2_ptr,
@@ -270,7 +270,7 @@ export class SwiftRuntime {
                     this.memory
                 );
             }
-            return JSValue.writeV2(
+            return JSValue.writeAndReturnKindBits(
                 result,
                 payload1_ptr,
                 payload2_ptr,
@@ -292,7 +292,7 @@ export class SwiftRuntime {
             let result = undefined;
             const args = JSValue.decodeArray(argv, argc, memory);
             result = func.apply(obj, args);
-            return JSValue.writeV2(
+            return JSValue.writeAndReturnKindBits(
                 result,
                 payload1_ptr,
                 payload2_ptr,
diff --git a/Runtime/src/js-value.ts b/Runtime/src/js-value.ts
index b76d8b217..76b554a28 100644
--- a/Runtime/src/js-value.ts
+++ b/Runtime/src/js-value.ts
@@ -130,7 +130,9 @@ export const write = (
     }
 };
 
-export const writeV2 = (
+/// This is a fast version of the above `write` function.
+/// Please synchronize with the above `write` function if you change either.
+export const writeAndReturnKindBits = (
     value: any,
     payload1_ptr: pointer,
     payload2_ptr: pointer,
diff --git a/Sources/JavaScriptKit/Runtime/index.js b/Sources/JavaScriptKit/Runtime/index.js
index c383ab513..f9e273d21 100644
--- a/Sources/JavaScriptKit/Runtime/index.js
+++ b/Sources/JavaScriptKit/Runtime/index.js
@@ -121,7 +121,9 @@
                 assertNever(type, `Type "${type}" is not supported yet`);
         }
     };
-    const writeV2 = (value, payload1_ptr, payload2_ptr, is_exception, memory) => {
+    /// This is a fast version of the above `write` function.
+    /// Please synchronize with the above `write` function if you change either.
+    const writeAndReturnKindBits = (value, payload1_ptr, payload2_ptr, is_exception, memory) => {
         const exceptionBit = (is_exception ? 1 : 0) << 31;
         if (value === null) {
             return exceptionBit | 4 /* Null */;
@@ -257,7 +259,7 @@
                     const obj = memory.getObject(ref);
                     const key = memory.getObject(name);
                     const result = obj[key];
-                    return writeV2(result, payload1_ptr, payload2_ptr, false, memory);
+                    return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, memory);
                 },
                 swjs_set_subscript: (ref, index, kind, payload1, payload2) => {
                     const memory = this.memory;
@@ -268,7 +270,7 @@
                 swjs_get_subscript: (ref, index, payload1_ptr, payload2_ptr) => {
                     const obj = this.memory.getObject(ref);
                     const result = obj[index];
-                    return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
+                    return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, this.memory);
                 },
                 swjs_encode_string: (ref, bytes_ptr_result) => {
                     const memory = this.memory;
@@ -299,16 +301,16 @@
                         result = func(...args);
                     }
                     catch (error) {
-                        return writeV2(error, payload1_ptr, payload2_ptr, true, this.memory);
+                        return writeAndReturnKindBits(error, payload1_ptr, payload2_ptr, true, this.memory);
                     }
-                    return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
+                    return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, this.memory);
                 },
                 swjs_call_function_no_catch: (ref, argv, argc, payload1_ptr, payload2_ptr) => {
                     const memory = this.memory;
                     const func = memory.getObject(ref);
                     const args = decodeArray(argv, argc, memory);
                     const result = func(...args);
-                    return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
+                    return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, this.memory);
                 },
                 swjs_call_function_with_this: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => {
                     const memory = this.memory;
@@ -320,9 +322,9 @@
                         result = func.apply(obj, args);
                     }
                     catch (error) {
-                        return writeV2(error, payload1_ptr, payload2_ptr, true, this.memory);
+                        return writeAndReturnKindBits(error, payload1_ptr, payload2_ptr, true, this.memory);
                     }
-                    return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
+                    return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, this.memory);
                 },
                 swjs_call_function_with_this_no_catch: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => {
                     const memory = this.memory;
@@ -331,7 +333,7 @@
                     let result = undefined;
                     const args = decodeArray(argv, argc, memory);
                     result = func.apply(obj, args);
-                    return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
+                    return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, this.memory);
                 },
                 swjs_call_new: (ref, argv, argc) => {
                     const memory = this.memory;
diff --git a/Sources/JavaScriptKit/Runtime/index.mjs b/Sources/JavaScriptKit/Runtime/index.mjs
index 73522ce95..32e90f739 100644
--- a/Sources/JavaScriptKit/Runtime/index.mjs
+++ b/Sources/JavaScriptKit/Runtime/index.mjs
@@ -115,7 +115,9 @@ const write = (value, kind_ptr, payload1_ptr, payload2_ptr, is_exception, memory
             assertNever(type, `Type "${type}" is not supported yet`);
     }
 };
-const writeV2 = (value, payload1_ptr, payload2_ptr, is_exception, memory) => {
+/// This is a fast version of the above `write` function.
+/// Please synchronize with the above `write` function if you change either.
+const writeAndReturnKindBits = (value, payload1_ptr, payload2_ptr, is_exception, memory) => {
     const exceptionBit = (is_exception ? 1 : 0) << 31;
     if (value === null) {
         return exceptionBit | 4 /* Null */;
@@ -251,7 +253,7 @@ class SwiftRuntime {
                 const obj = memory.getObject(ref);
                 const key = memory.getObject(name);
                 const result = obj[key];
-                return writeV2(result, payload1_ptr, payload2_ptr, false, memory);
+                return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, memory);
             },
             swjs_set_subscript: (ref, index, kind, payload1, payload2) => {
                 const memory = this.memory;
@@ -262,7 +264,7 @@ class SwiftRuntime {
             swjs_get_subscript: (ref, index, payload1_ptr, payload2_ptr) => {
                 const obj = this.memory.getObject(ref);
                 const result = obj[index];
-                return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
+                return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, this.memory);
             },
             swjs_encode_string: (ref, bytes_ptr_result) => {
                 const memory = this.memory;
@@ -293,16 +295,16 @@ class SwiftRuntime {
                     result = func(...args);
                 }
                 catch (error) {
-                    return writeV2(error, payload1_ptr, payload2_ptr, true, this.memory);
+                    return writeAndReturnKindBits(error, payload1_ptr, payload2_ptr, true, this.memory);
                 }
-                return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
+                return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, this.memory);
             },
             swjs_call_function_no_catch: (ref, argv, argc, payload1_ptr, payload2_ptr) => {
                 const memory = this.memory;
                 const func = memory.getObject(ref);
                 const args = decodeArray(argv, argc, memory);
                 const result = func(...args);
-                return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
+                return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, this.memory);
             },
             swjs_call_function_with_this: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => {
                 const memory = this.memory;
@@ -314,9 +316,9 @@ class SwiftRuntime {
                     result = func.apply(obj, args);
                 }
                 catch (error) {
-                    return writeV2(error, payload1_ptr, payload2_ptr, true, this.memory);
+                    return writeAndReturnKindBits(error, payload1_ptr, payload2_ptr, true, this.memory);
                 }
-                return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
+                return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, this.memory);
             },
             swjs_call_function_with_this_no_catch: (obj_ref, func_ref, argv, argc, payload1_ptr, payload2_ptr) => {
                 const memory = this.memory;
@@ -325,7 +327,7 @@ class SwiftRuntime {
                 let result = undefined;
                 const args = decodeArray(argv, argc, memory);
                 result = func.apply(obj, args);
-                return writeV2(result, payload1_ptr, payload2_ptr, false, this.memory);
+                return writeAndReturnKindBits(result, payload1_ptr, payload2_ptr, false, this.memory);
             },
             swjs_call_new: (ref, argv, argc) => {
                 const memory = this.memory;

From d4c45b478eedbd847cce69adce40e665599b9a01 Mon Sep 17 00:00:00 2001
From: Yuta Saito <kateinoigakukun@gmail.com>
Date: Fri, 19 Aug 2022 00:04:43 +0900
Subject: [PATCH 18/20] Improve doc comment style

---
 Sources/_CJavaScriptKit/include/_CJavaScriptKit.h | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h
index adf732bd4..3bac436f4 100644
--- a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h
+++ b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h
@@ -95,7 +95,7 @@ extern void _set_prop(const JavaScriptObjectRef _this,
 /// @param prop A JavaScript string object to reference a member of `_this` object.
 /// @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.
+/// @return A `JavaScriptValueKind` bits represented as 32bit integer for the returned value.
 __attribute__((__import_module__("javascript_kit"),
                __import_name__("swjs_get_prop")))
 extern uint32_t _get_prop(
@@ -126,7 +126,7 @@ extern void _set_subscript(const JavaScriptObjectRef _this,
 /// @param index A subscript index to get value.
 /// @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.
+/// @return A `JavaScriptValueKind` bits represented as 32bit integer for the returned value.
 __attribute__((__import_module__("javascript_kit"),
                __import_name__("swjs_get_subscript")))
 extern uint32_t _get_subscript(
@@ -180,7 +180,7 @@ extern JavaScriptObjectRef _i64_to_bigint_slow(unsigned int lower, unsigned int
 /// @param argc The length of `argv``.
 /// @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.
+/// @return A `JavaScriptValueKindAndFlags` bits represented as 32bit integer for the returned value.
 __attribute__((__import_module__("javascript_kit"),
                __import_name__("swjs_call_function")))
 extern uint32_t _call_function(
@@ -197,7 +197,7 @@ extern uint32_t _call_function(
 /// @param argc The length of `argv``.
 /// @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.
+/// @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 uint32_t _call_function_no_catch(
@@ -215,7 +215,7 @@ extern uint32_t _call_function_no_catch(
 /// @param argc The length of `argv``.
 /// @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.
+/// @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 uint32_t _call_function_with_this(
@@ -234,7 +234,7 @@ extern uint32_t _call_function_with_this(
 /// @param argc The length of `argv``.
 /// @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.
+/// @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 uint32_t _call_function_with_this_no_catch(

From 7532e6a3b9a1b56f6803ab17bcdb9660d5e17668 Mon Sep 17 00:00:00 2001
From: Yuta Saito <kateinoigakukun@gmail.com>
Date: Fri, 19 Aug 2022 00:09:55 +0900
Subject: [PATCH 19/20] Use writeAndReturnKindBits in write

---
 Runtime/src/js-value.ts                 | 54 +------------------------
 Sources/JavaScriptKit/Runtime/index.js  | 52 +-----------------------
 Sources/JavaScriptKit/Runtime/index.mjs | 52 +-----------------------
 3 files changed, 6 insertions(+), 152 deletions(-)

diff --git a/Runtime/src/js-value.ts b/Runtime/src/js-value.ts
index 76b554a28..e3dda96db 100644
--- a/Runtime/src/js-value.ts
+++ b/Runtime/src/js-value.ts
@@ -78,60 +78,10 @@ export const write = (
     is_exception: boolean,
     memory: Memory
 ) => {
-    const exceptionBit = (is_exception ? 1 : 0) << 31;
-    if (value === null) {
-        memory.writeUint32(kind_ptr, exceptionBit | Kind.Null);
-        return;
-    }
-
-    const writeRef = (kind: Kind) => {
-        memory.writeUint32(kind_ptr, exceptionBit | kind);
-        memory.writeUint32(payload1_ptr, memory.retain(value));
-    };
-
-    const type = typeof value;
-    switch (type) {
-        case "boolean": {
-            memory.writeUint32(kind_ptr, exceptionBit | Kind.Boolean);
-            memory.writeUint32(payload1_ptr, value ? 1 : 0);
-            break;
-        }
-        case "number": {
-            memory.writeUint32(kind_ptr, exceptionBit | Kind.Number);
-            memory.writeFloat64(payload2_ptr, value);
-            break;
-        }
-        case "string": {
-            writeRef(Kind.String);
-            break;
-        }
-        case "undefined": {
-            memory.writeUint32(kind_ptr, exceptionBit | Kind.Undefined);
-            break;
-        }
-        case "object": {
-            writeRef(Kind.Object);
-            break;
-        }
-        case "function": {
-            writeRef(Kind.Function);
-            break;
-        }
-        case "symbol": {
-            writeRef(Kind.Symbol);
-            break;
-        }
-        case "bigint": {
-            writeRef(Kind.BigInt);
-            break;
-        }
-        default:
-            assertNever(type, `Type "${type}" is not supported yet`);
-    }
+    const kind = writeAndReturnKindBits(value, payload1_ptr, payload2_ptr, is_exception, memory);
+    memory.writeUint32(kind_ptr, kind);
 };
 
-/// This is a fast version of the above `write` function.
-/// Please synchronize with the above `write` function if you change either.
 export const writeAndReturnKindBits = (
     value: any,
     payload1_ptr: pointer,
diff --git a/Sources/JavaScriptKit/Runtime/index.js b/Sources/JavaScriptKit/Runtime/index.js
index f9e273d21..ec517f3e3 100644
--- a/Sources/JavaScriptKit/Runtime/index.js
+++ b/Sources/JavaScriptKit/Runtime/index.js
@@ -72,57 +72,9 @@
         return result;
     };
     const write = (value, kind_ptr, payload1_ptr, payload2_ptr, is_exception, memory) => {
-        const exceptionBit = (is_exception ? 1 : 0) << 31;
-        if (value === null) {
-            memory.writeUint32(kind_ptr, exceptionBit | 4 /* Null */);
-            return;
-        }
-        const writeRef = (kind) => {
-            memory.writeUint32(kind_ptr, exceptionBit | kind);
-            memory.writeUint32(payload1_ptr, memory.retain(value));
-        };
-        const type = typeof value;
-        switch (type) {
-            case "boolean": {
-                memory.writeUint32(kind_ptr, exceptionBit | 0 /* Boolean */);
-                memory.writeUint32(payload1_ptr, value ? 1 : 0);
-                break;
-            }
-            case "number": {
-                memory.writeUint32(kind_ptr, exceptionBit | 2 /* Number */);
-                memory.writeFloat64(payload2_ptr, value);
-                break;
-            }
-            case "string": {
-                writeRef(1 /* String */);
-                break;
-            }
-            case "undefined": {
-                memory.writeUint32(kind_ptr, exceptionBit | 5 /* Undefined */);
-                break;
-            }
-            case "object": {
-                writeRef(3 /* Object */);
-                break;
-            }
-            case "function": {
-                writeRef(6 /* Function */);
-                break;
-            }
-            case "symbol": {
-                writeRef(7 /* Symbol */);
-                break;
-            }
-            case "bigint": {
-                writeRef(8 /* BigInt */);
-                break;
-            }
-            default:
-                assertNever(type, `Type "${type}" is not supported yet`);
-        }
+        const kind = writeAndReturnKindBits(value, payload1_ptr, payload2_ptr, is_exception, memory);
+        memory.writeUint32(kind_ptr, kind);
     };
-    /// This is a fast version of the above `write` function.
-    /// Please synchronize with the above `write` function if you change either.
     const writeAndReturnKindBits = (value, payload1_ptr, payload2_ptr, is_exception, memory) => {
         const exceptionBit = (is_exception ? 1 : 0) << 31;
         if (value === null) {
diff --git a/Sources/JavaScriptKit/Runtime/index.mjs b/Sources/JavaScriptKit/Runtime/index.mjs
index 32e90f739..2592c82be 100644
--- a/Sources/JavaScriptKit/Runtime/index.mjs
+++ b/Sources/JavaScriptKit/Runtime/index.mjs
@@ -66,57 +66,9 @@ const decodeArray = (ptr, length, memory) => {
     return result;
 };
 const write = (value, kind_ptr, payload1_ptr, payload2_ptr, is_exception, memory) => {
-    const exceptionBit = (is_exception ? 1 : 0) << 31;
-    if (value === null) {
-        memory.writeUint32(kind_ptr, exceptionBit | 4 /* Null */);
-        return;
-    }
-    const writeRef = (kind) => {
-        memory.writeUint32(kind_ptr, exceptionBit | kind);
-        memory.writeUint32(payload1_ptr, memory.retain(value));
-    };
-    const type = typeof value;
-    switch (type) {
-        case "boolean": {
-            memory.writeUint32(kind_ptr, exceptionBit | 0 /* Boolean */);
-            memory.writeUint32(payload1_ptr, value ? 1 : 0);
-            break;
-        }
-        case "number": {
-            memory.writeUint32(kind_ptr, exceptionBit | 2 /* Number */);
-            memory.writeFloat64(payload2_ptr, value);
-            break;
-        }
-        case "string": {
-            writeRef(1 /* String */);
-            break;
-        }
-        case "undefined": {
-            memory.writeUint32(kind_ptr, exceptionBit | 5 /* Undefined */);
-            break;
-        }
-        case "object": {
-            writeRef(3 /* Object */);
-            break;
-        }
-        case "function": {
-            writeRef(6 /* Function */);
-            break;
-        }
-        case "symbol": {
-            writeRef(7 /* Symbol */);
-            break;
-        }
-        case "bigint": {
-            writeRef(8 /* BigInt */);
-            break;
-        }
-        default:
-            assertNever(type, `Type "${type}" is not supported yet`);
-    }
+    const kind = writeAndReturnKindBits(value, payload1_ptr, payload2_ptr, is_exception, memory);
+    memory.writeUint32(kind_ptr, kind);
 };
-/// This is a fast version of the above `write` function.
-/// Please synchronize with the above `write` function if you change either.
 const writeAndReturnKindBits = (value, payload1_ptr, payload2_ptr, is_exception, memory) => {
     const exceptionBit = (is_exception ? 1 : 0) << 31;
     if (value === null) {

From a9843eeeed988ddf68f78d170e34858876a4cc6e Mon Sep 17 00:00:00 2001
From: Yuta Saito <kateinoigakukun@gmail.com>
Date: Fri, 19 Aug 2022 00:14:25 +0900
Subject: [PATCH 20/20] Add rationale comments for write

---
 Runtime/src/js-value.ts                 | 4 ++++
 Sources/JavaScriptKit/Runtime/index.js  | 4 ++++
 Sources/JavaScriptKit/Runtime/index.mjs | 4 ++++
 3 files changed, 12 insertions(+)

diff --git a/Runtime/src/js-value.ts b/Runtime/src/js-value.ts
index e3dda96db..9ff3d065e 100644
--- a/Runtime/src/js-value.ts
+++ b/Runtime/src/js-value.ts
@@ -70,6 +70,10 @@ export const decodeArray = (ptr: pointer, length: number, memory: 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,
diff --git a/Sources/JavaScriptKit/Runtime/index.js b/Sources/JavaScriptKit/Runtime/index.js
index ec517f3e3..02dc9382e 100644
--- a/Sources/JavaScriptKit/Runtime/index.js
+++ b/Sources/JavaScriptKit/Runtime/index.js
@@ -71,6 +71,10 @@
         }
         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.
     const write = (value, kind_ptr, payload1_ptr, payload2_ptr, is_exception, memory) => {
         const kind = writeAndReturnKindBits(value, payload1_ptr, payload2_ptr, is_exception, memory);
         memory.writeUint32(kind_ptr, kind);
diff --git a/Sources/JavaScriptKit/Runtime/index.mjs b/Sources/JavaScriptKit/Runtime/index.mjs
index 2592c82be..823ffca60 100644
--- a/Sources/JavaScriptKit/Runtime/index.mjs
+++ b/Sources/JavaScriptKit/Runtime/index.mjs
@@ -65,6 +65,10 @@ const decodeArray = (ptr, length, 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.
 const write = (value, kind_ptr, payload1_ptr, payload2_ptr, is_exception, memory) => {
     const kind = writeAndReturnKindBits(value, payload1_ptr, payload2_ptr, is_exception, memory);
     memory.writeUint32(kind_ptr, kind);