Skip to content

Commit

Permalink
Sync changes from internal repo
Browse files Browse the repository at this point in the history
  • Loading branch information
theolampert committed Nov 3, 2023
1 parent aabbce5 commit bf0bacf
Show file tree
Hide file tree
Showing 22 changed files with 876 additions and 510 deletions.
Original file line number Diff line number Diff line change
@@ -1,63 +1,68 @@
import Foundation
import JavaScriptCore

@objc public protocol BlobExports: JSExport {
@objc
protocol BlobExports: JSExport {
func text() -> JSValue?
func arrayBuffer() -> JSValue
}


@objc class Blob: NSObject, BlobExports {

/// This implmenets the `Blob` browser API.
///
/// Reference: [Blob Reference on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Blob)
final class Blob: NSObject, BlobExports {
var content: String

weak var context: JSContext?

init(content: String) {
self.content = content
}

func text() -> JSValue? {
guard let context = context else {
fatalError("JSContext is nil")
}
return JSValue(newPromiseIn: context) { (resolve, reject) in

return JSValue(newPromiseIn: context) { resolve, _ in
let blobObject = JSValue(object: self.content, in: context)!
resolve?.call(withArguments: [blobObject])
}
}

func arrayBuffer() -> JSValue {
return JSValue(newPromiseIn: context) { [weak self] (resolve, reject) in
return JSValue(newPromiseIn: context) { [weak self] resolve, reject in
guard let data = self?.content.data(using: .utf8) else {
let errorDescription = "Failed to convert blob content to ArrayBuffer"
reject?.call(withArguments: [errorDescription])
return
}

// Convert Data to [UInt8]
let byteArray = [UInt8](data)

// Convert [UInt8] to JavaScript ArrayBuffer
let jsArrayBufferConstructor = self?.context?.evaluateScript("ArrayBuffer")
let jsUint8ArrayConstructor = self?.context?.evaluateScript("Uint8Array")
guard let arrayBuffer = jsArrayBufferConstructor?.construct(withArguments: [byteArray.count]),
let uint8Array = jsUint8ArrayConstructor?.construct(withArguments: [arrayBuffer]) else {
let uint8Array = jsUint8ArrayConstructor?.construct(withArguments: [arrayBuffer])
else {
let errorDescription = "Failed to create ArrayBuffer"
reject?.call(withArguments: [errorDescription])
return
}

// Set bytes to ArrayBuffer
for (index, byte) in byteArray.enumerated() {
uint8Array.setValue(byte, at: index)
}

resolve?.call(withArguments: [arrayBuffer])
}
}
}

/// Helper to register the ``Blob`` API with a context.
struct BlobAPI {
func registerAPIInto(context: JSContext) {
let blobClass: @convention(block) (String) -> Blob = { text in
Expand Down
12 changes: 7 additions & 5 deletions Sources/ECMASwift/API/Console/Console.swift
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import JavaScriptCore
import os

// https://developer.mozilla.org/en-US/docs/Web/API/console

@objc protocol ConsoleExports: JSExport {
static func log(_ msg: String)
static func info(_ msg: String)
static func warn(_ msg: String)
static func error(_ msg: String)
}

class Console: NSObject, ConsoleExports {
static let logger = Logger(subsystem: "ECMASwift", category: "Console")

/// This implmenets the `Console` browser API.
///
/// Reference: [Console Reference on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Console)
final class Console: NSObject, ConsoleExports {
static let logger = Logger(subsystem: "JSRuntime", category: "Console")

public class func log(_ msg: String) {
logger.log("\(msg)")
}
Expand All @@ -30,6 +31,7 @@ class Console: NSObject, ConsoleExports {
}
}

/// Helper to register the ``Console`` API with a context.
public struct ConsoleAPI {
public func registerAPIInto(context: JSContext) {
context.setObject(
Expand Down
81 changes: 12 additions & 69 deletions Sources/ECMASwift/API/Crypto/Crypto.swift
Original file line number Diff line number Diff line change
@@ -1,85 +1,27 @@
import Foundation
import JavaScriptCore
import CommonCrypto

// https://developer.mozilla.org/en-US/docs/Web/API/Crypto

@objc protocol SubtleCryptoExports: JSExport {
func digest(_ algorithm: String, _ data: [UInt8]) -> [UInt8]?
func encrypt(_ algorithm: String, _ key: [UInt8], _ iv: [UInt8], _ data: [UInt8]) -> [UInt8]?
func decrypt(_ algorithm: String, _ key: [UInt8], _ iv: [UInt8], _ data: [UInt8]) -> [UInt8]?
}

@objc class SubtleCrypto: NSObject, SubtleCryptoExports {
func digest(_ algorithm: String, _ data: [UInt8]) -> [UInt8]? {
guard algorithm == "SHA-256" else { return nil }
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
_ = data.withUnsafeBytes {
CC_SHA256($0.baseAddress, CC_LONG(data.count), &hash)
}
return hash
}

func encrypt(_ algorithm: String, _ key: [UInt8], _ iv: [UInt8], _ data: [UInt8]) -> [UInt8]? {
guard algorithm == "AES-GCM", key.count == kCCKeySizeAES128 else { return nil }
var buffer = [UInt8](repeating: 0, count: data.count + kCCBlockSizeAES128)
var numBytesEncrypted: size_t = 0

let cryptStatus = CCCrypt(CCOperation(kCCEncrypt),
CCAlgorithm(kCCAlgorithmAES),
CCOptions(kCCOptionPKCS7Padding),
key, key.count,
iv,
data, data.count,
&buffer, buffer.count,
&numBytesEncrypted)

if cryptStatus == kCCSuccess {
return Array(buffer.prefix(numBytesEncrypted))
}
return nil
}

func decrypt(_ algorithm: String, _ key: [UInt8], _ iv: [UInt8], _ data: [UInt8]) -> [UInt8]? {
guard algorithm == "AES-GCM", key.count == kCCKeySizeAES128 else { return nil }
var buffer = [UInt8](repeating: 0, count: data.count + kCCBlockSizeAES128)
var numBytesDecrypted: size_t = 0

let cryptStatus = CCCrypt(CCOperation(kCCDecrypt),
CCAlgorithm(kCCAlgorithmAES),
CCOptions(kCCOptionPKCS7Padding),
key, key.count,
iv,
data, data.count,
&buffer, buffer.count,
&numBytesDecrypted)

if cryptStatus == kCCSuccess {
return Array(buffer.prefix(numBytesDecrypted))
}
return nil
}
}

import JavaScriptCore

@objc protocol CryptoExports: JSExport {
func getRandomValues(_ array: [UInt]) -> [UInt]
func randomUUID() -> String

var subtle: SubtleCryptoExports { get }
}

@objc class Crypto: NSObject, CryptoExports {
/// This implmenets the `Crypto` browser API.
///
/// Reference: [Crypto Reference on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Crypto)
@objc final class Crypto: NSObject, CryptoExports {
func getRandomValues(_ array: [UInt]) -> [UInt] {
let size = array.count * MemoryLayout<UInt>.size

var buffer = [UInt8](repeating: 0, count: size)

let result = SecRandomCopyBytes(kSecRandomDefault, size, &buffer)

if result == errSecSuccess {
return stride(from: 0, to: buffer.count, by: MemoryLayout<UInt>.size).map { i in
return buffer.withUnsafeBytes { ptr -> UInt in
buffer.withUnsafeBytes { ptr -> UInt in
let base = ptr.baseAddress!.assumingMemoryBound(to: UInt.self)
return base[i / MemoryLayout<UInt>.size]
}
Expand All @@ -88,14 +30,15 @@ import CommonCrypto
return []
}
}

func randomUUID() -> String {
return UUID().uuidString
}

lazy var subtle: SubtleCryptoExports = SubtleCrypto()
}

/// Helper to register the ``Crypto`` API with a context.
struct CryptoAPI {
public func registerAPIInto(context: JSContext) {
context.setObject(
Expand Down
72 changes: 72 additions & 0 deletions Sources/ECMASwift/API/Crypto/SublteCrypto.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import CommonCrypto
import JavaScriptCore

@objc protocol SubtleCryptoExports: JSExport {
func digest(_ algorithm: String, _ data: [UInt8]) -> [UInt8]?
func encrypt(_ algorithm: String, _ key: [UInt8], _ iv: [UInt8], _ data: [UInt8]) -> [UInt8]?
func decrypt(_ algorithm: String, _ key: [UInt8], _ iv: [UInt8], _ data: [UInt8]) -> [UInt8]?
}

/// This implmenets the `SubtleCrypto` browser API.
///
/// Reference: [SubtleCrypto Reference on MDN](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto)
@objc final class SubtleCrypto: NSObject, SubtleCryptoExports {
func digest(_ algorithm: String, _ data: [UInt8]) -> [UInt8]? {
guard algorithm == "SHA-256" else { return nil }
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
_ = data.withUnsafeBytes {
CC_SHA256($0.baseAddress, CC_LONG(data.count), &hash)
}
return hash
}

func encrypt(_ algorithm: String, _ key: [UInt8], _ iv: [UInt8], _ data: [UInt8]) -> [UInt8]? {
guard algorithm == "AES-GCM", key.count == kCCKeySizeAES128 else { return nil }
var buffer = [UInt8](repeating: 0, count: data.count + kCCBlockSizeAES128)
var numBytesEncrypted: size_t = 0

let cryptStatus = CCCrypt(
CCOperation(kCCEncrypt),
CCAlgorithm(kCCAlgorithmAES),
CCOptions(kCCOptionPKCS7Padding),
key,
key.count,
iv,
data,
data.count,
&buffer,
buffer.count,
&numBytesEncrypted
)

if cryptStatus == kCCSuccess {
return Array(buffer.prefix(numBytesEncrypted))
}
return nil
}

func decrypt(_ algorithm: String, _ key: [UInt8], _ iv: [UInt8], _ data: [UInt8]) -> [UInt8]? {
guard algorithm == "AES-GCM", key.count == kCCKeySizeAES128 else { return nil }
var buffer = [UInt8](repeating: 0, count: data.count + kCCBlockSizeAES128)
var numBytesDecrypted: size_t = 0

let cryptStatus = CCCrypt(
CCOperation(kCCDecrypt),
CCAlgorithm(kCCAlgorithmAES),
CCOptions(kCCOptionPKCS7Padding),
key,
key.count,
iv,
data,
data.count,
&buffer,
buffer.count,
&numBytesDecrypted
)

if cryptStatus == kCCSuccess {
return Array(buffer.prefix(numBytesDecrypted))
}
return nil
}
}
55 changes: 55 additions & 0 deletions Sources/ECMASwift/API/Fetch/AbortController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import JavaScriptCore

@objc
protocol AbortSignalExports: JSExport {
var aborted: Bool { get set }
}

class AbortSignal: NSObject, AbortSignalExports {
private var _aborted: Bool = false
var aborted: Bool {
get { return _aborted }
set {
_aborted = newValue
if newValue == true {
self.onAbort?()
}
}
}

var onAbort: (() -> Void)?
}

@objc
protocol AbortControllerExports: JSExport {
var signal: AbortSignal { get set }
func abort()
}

class AbortController: NSObject, AbortControllerExports {
var signal = AbortSignal()

func abort() {
signal.aborted = true
}
}

struct AbortControllerAPI {
func registerAPIInto(context: JSContext) {
let abortControllerClass: @convention(block) () -> AbortController = {
AbortController()
}
let abortSignalClass: @convention(block) () -> AbortSignal = {
AbortSignal()
}

context.setObject(
abortSignalClass,
forKeyedSubscript: "AbortSignal" as NSString
)
context.setObject(
abortControllerClass,
forKeyedSubscript: "AbortController" as NSString
)
}
}
Loading

0 comments on commit bf0bacf

Please sign in to comment.