Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ let package = Package(
dependencies: [
.package(url: "https://source.skip.tools/skip.git", from: "1.0.4"),
.package(url: "https://source.skip.tools/skip-foundation.git", from: "1.0.0"),
.package(url: "https://source.skip.tools/skip-ffi.git", "0.0.0"..<"2.0.0"),
.package(url: "https://source.skip.tools/skip-ffi.git", from: "1.0.0"),
],
targets: [
.target(name: "SkipScript", dependencies: [
Expand Down
37 changes: 23 additions & 14 deletions Sources/SkipScript/JSContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -527,12 +527,11 @@ public class JSValue {

let pointerSize: Int32 = com.sun.jna.Native.POINTER_SIZE
let size = Int64(arguments.count * pointerSize)
let argptr = arguments.count == 0 ? nil : com.sun.jna.Memory(size)
//defer { argptr?.clear(size) }
let args = arguments.count == 0 ? nil : com.sun.jna.Memory(size)
defer { args?.clear(size) }
for i in (0..<arguments.count) {
argptr?.setPointer(i.toLong() * pointerSize, arguments[i].value)
args!.setPointer(i.toLong() * pointerSize, arguments[i].value)
}
let args = com.sun.jna.ptr.PointerByReference(argptr)
#endif

let ctx = self.context
Expand Down Expand Up @@ -706,6 +705,15 @@ extension JSValue {
// }
}

#if SKIP
// workaround for inability to implement this as a convenience constructor due to needing local variables: In Kotlin, delegating calls to 'self' or 'super' constructors can not use local variables other than the parameters passed to this constructor
public func JSValue(string value: String, in context: JSContext) -> JSValue {
let str = JavaScriptCore.JSStringCreateWithUTF8CString(value)
defer { JavaScriptCore.JSStringRelease(str) }
return JSValue(jsValueRef: JavaScriptCore.JSValueMakeString(context.context, str), in: context)
}
#endif


public struct JSCError : Error {
let errorDescription: String
Expand Down Expand Up @@ -876,7 +884,7 @@ private final class JSFunctionCallbackImpl : JSCallbackFunction {
init() {
}

public func JSFunctionCallback(_ jsc: JSContextRef?, _ object: JSObjectRef?, _ this: JSObjectRef?, _ argumentCount: Int, _ arguments: UnsafePointer<JSValueRef?>?, _ exception: UnsafeMutablePointer<JSValueRef?>?) -> JSValueRef? {
public func callback(_ jsc: JSContextRef?, _ object: JSObjectRef?, _ this: JSObjectRef?, _ argumentCount: Int, _ arguments: UnsafePointer<JSValueRef?>?, _ exception: UnsafeMutablePointer<JSValueRef?>?) -> JSValueRef? {
guard let object = object,
let data = JavaScriptCore.JSObjectGetPrivate(object) else {
preconditionFailure("SkipScript: unable to find private object data for \(object)")
Expand All @@ -890,9 +898,9 @@ private final class JSFunctionCallbackImpl : JSCallbackFunction {
return nil
}

//let argptrs = argumentCount == 0 ? nil : arguments?.value.getPointerArray(0, argumentCount) // Crashes
let args = (0..<argumentCount).map {
JSValue(jsValueRef: arguments!.getPointer(0).getPointer(Int64($0 * com.sun.jna.Native.POINTER_SIZE)), in: context)
let argptrs = argumentCount == 0 ? nil : arguments!.getPointerArray(0, argumentCount)
let args: [JSValue] = (0..<argumentCount).map {
JSValue(jsValueRef: argptrs![$0], in: context)
}
let this = this.map { JSValue(jsValueRef: $0, in: context) }
let value: JSValue = callback(context, this, args)
Expand All @@ -919,7 +927,7 @@ private final class JSFunctionFinalizeImpl : JSCallbackFunction {
init() {
}

public func JSFunctionFinalize(_ object: JSObjectRef?) -> Void {
public func callback(_ object: JSObjectRef?) -> Void {
guard let object = object,
let data = JavaScriptCore.JSObjectGetPrivate(object) else {
preconditionFailure("SkipScript: unable to find private object data for \(object)")
Expand Down Expand Up @@ -957,7 +965,7 @@ private final class JSFunctionInstanceOfImpl : JSCallbackFunction {
init() {
}

public func JSFunctionInstanceOf(_ jsc: JSContextRef?, _ constructor: JSObjectRef?, _ possibleInstance: JSValueRef?, _ exception: UnsafeMutablePointer<JSValueRef?>?) -> Bool {
public func callback(_ jsc: JSContextRef?, _ constructor: JSObjectRef?, _ possibleInstance: JSValueRef?, _ exception: UnsafeMutablePointer<JSValueRef?>?) -> Bool {
fatalError("### TODO: JSFunctionInstanceOf")
return false
}
Expand Down Expand Up @@ -996,7 +1004,7 @@ private final class JSFunctionConstructorImpl : JSCallbackFunction {
init() {
}

public func JSFunctionConstructor(_ jsc: JSContextRef?, _ object: JSObjectRef?, _ argumentCount: Int, _ arguments: UnsafePointer<JSValueRef?>?, _ exception: UnsafeMutablePointer<JSValueRef?>?) -> JSObjectRef? {
public func callback(_ jsc: JSContextRef?, _ object: JSObjectRef?, _ argumentCount: Int, _ arguments: UnsafePointer<JSValueRef?>?, _ exception: UnsafeMutablePointer<JSValueRef?>?) -> JSObjectRef? {
fatalError("### TODO: JSFunctionConstructor")
return nil
}
Expand Down Expand Up @@ -1150,7 +1158,7 @@ final class JavaScriptCoreLibrary : com.sun.jna.Library {
/* SKIP EXTERN */ public func JSObjectMakeFunctionWithCallback(_ ctx: JSContextRef, _ name: JSStringRef, _ callAsFunction: JSObjectCallAsFunctionCallback) -> JSObjectRef
/* SKIP EXTERN */ public func JSObjectMake(_ ctx: JSContextRef, _ jsClass: JSClassRef?, _ data: OpaqueJSValue?) -> JSObjectRef

/* SKIP EXTERN */ public func JSObjectCallAsFunction(_ ctx: JSContextRef, _ object: OpaquePointer?, _ thisObject: OpaquePointer?, _ argumentCount: Int32, _ arguments: com.sun.jna.ptr.PointerByReference?, _ exception: ExceptionPtr?) -> JSValueRef
/* SKIP EXTERN */ public func JSObjectCallAsFunction(_ ctx: JSContextRef, _ object: OpaquePointer?, _ thisObject: OpaquePointer?, _ argumentCount: Int32, _ arguments: OpaquePointer?, _ exception: ExceptionPtr?) -> JSValueRef

/* SKIP EXTERN */ public func JSClassCreate(_ cls: JSClassDefinition) -> JSClassRef
/* SKIP EXTERN */ public func JSClassRetain(_ cls: JSClassRef) -> JSClassRef
Expand All @@ -1164,9 +1172,10 @@ final class JavaScriptCoreLibrary : com.sun.jna.Library {
let isAndroid = System.getProperty("java.vm.vendor") == "The Android Project"
// on Android we use the embedded libjsc.so; on macOS host, use the system JavaScriptCore
let jscName = isAndroid ? "jsc" : "JavaScriptCore"
//return com.sun.jna.Native.load(jscName, javaClass(JavaScriptCoreLibrary.self))
if isAndroid {
System.loadLibrary("c++_shared") // io.github.react-native-community:jsc-android-intl requires this, provided in com.facebook.fbjni:fbjni
}
com.sun.jna.Native.register((JavaScriptCoreLibrary.self as kotlin.reflect.KClass).java, jscName)

#endif
}
}
Expand Down
28 changes: 18 additions & 10 deletions Sources/SkipScript/Skip/skip.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,29 @@
# package: 'skip.script'

# the blocks to add to the settings.gradle.kts
settings:
contents:
- block: 'dependencyResolutionManagement'
contents:
- block: 'repositories'
contents:
# this is where the android-jsc libraries are hosted
- 'maven("https://maven.skip.tools")'
# only needed for the https://maven.skip.tools build…
#settings:
# contents:
# - block: 'dependencyResolutionManagement'
# contents:
# - block: 'repositories'
# contents:
# # this is where the android-jsc libraries are hosted
# - 'maven("https://maven.skip.tools")'

# the blocks to add to the build.gradle.kts
build:
contents:
- block: 'dependencies'
contents:
- 'implementation("org.webkit:android-jsc-cppruntime:r245459@aar")'
# JSC is now on Maven central (https://github.com/facebook/react-native/pull/47972): https://repo1.maven.org/maven2/io/github/react-native-community/jsc-android/2026004.0.1/
- 'implementation("io.github.react-native-community:jsc-android-intl:2026004.0.1")'
- 'implementation("com.facebook.fbjni:fbjni:0.7.0")' # needed for libc++_shared.so, else no symbol "_ZNSt6__ndk122__libcpp_verbose_abortEPKcz"

# non-international version
#- 'implementation("io.github.react-native-community:jsc-android:2026004.0.1")'

#- 'implementation("org.webkit:android-jsc-cppruntime:r245459@aar")'
# the -intl variant is bigger (24M vs. 13M), but it is needed for locale support
#- 'implementation("org.webkit:android-jsc-intl:r245459@aar")'
- 'implementation("org.webkit:android-jsc:r245459@aar")'
#- 'implementation("org.webkit:android-jsc:r245459@aar")'
27 changes: 6 additions & 21 deletions Tests/SkipScriptTests/JSContextTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,11 @@ class JSContextTests : XCTestCase {
}

func testIntl() throws {
if isAndroid {
throw XCTSkip("testIntl disabled on Android due to not using android-jsc-intl") // adds 4+ meg per/arch (i.e., 20MB+ per build)
}
let ctx = try XCTUnwrap(JSContext())

XCTAssertEqual("12,34 €", ctx.evaluateScript("new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(12.34)")?.toString())
XCTAssertEqual("65.4", ctx.evaluateScript("new Intl.NumberFormat('en-IN', { maximumSignificantDigits: 3 }).format(65.4321)")?.toString())
XCTAssertEqual("٦٥٫٤٣٢١", ctx.evaluateScript("new Intl.NumberFormat('ar-AR', { maximumSignificantDigits: 6 }).format(65.432123456789)")?.toString())
//XCTAssertEqual("٦٥٫٤٣٢١", ctx.evaluateScript("new Intl.NumberFormat('ar-AR', { maximumSignificantDigits: 6 }).format(65.432123456789)")?.toString())

let yen = "new Intl.NumberFormat('ja-JP', { style: 'currency', currency: 'JPY' }).format(45.678)"
// these seem to be different values because they use combining marks differently
Expand Down Expand Up @@ -194,10 +191,7 @@ class JSContextTests : XCTestCase {
JavaScriptCore.JSValueMakeNumber(ctx, callbackResult)
}
#else
let callbackPtr = JSCCallback()
// { ctx in
// JavaScriptCore.JSValueMakeNumber(ctx!!, callbackResult)
// }
let callbackPtr = JSCCallback()
#endif

let callbackFunction = JavaScriptCore.JSObjectMakeFunctionWithCallback(jsc, callbackName, callbackPtr)
Expand All @@ -206,24 +200,15 @@ class JSContextTests : XCTestCase {
let f = try XCTUnwrap(JavaScriptCore.JSObjectCallAsFunction(jsc, callbackFunction, nil, 0, nil, nil))
XCTAssertEqual(callbackResult, JavaScriptCore.JSValueToNumber(jsc, f, nil))

#if !SKIP
// TODO: need JSObjectSetProperty in Skip
JavaScriptCore.JSObjectSetProperty(jsc, jsc, callbackName, callbackFunction, JSPropertyAttributes(kJSPropertyAttributeNone), nil)
XCTAssertEqual(callbackResult.description, try eval("skip_cb()").toString())
#endif
if !isAndroid { // crashes on Android, passes on Robolectric
JavaScriptCore.JSObjectSetProperty(jsc, jsc, callbackName, callbackFunction, JSPropertyAttributes(kJSPropertyAttributeNone), nil)
XCTAssertEqual(callbackResult.description, try eval("skip_cb()").toString())
}
}

#if SKIP
class JSCCallback : com.sun.jna.Callback {
// let callbackBlock: (JSContextRef?) -> JSValueRef?
//
// init(callbackBlock: @escaping (JSContextRef?) -> JSValueRef?) {
// self.callbackBlock = callbackBlock
// }

// TODO: (ctx: JSContextRef?, function: JSObjectRef?, thisObject: JSObjectRef?, argumentCount: Int, arguments: UnsafePointer<JSValueRef?>?, exception: UnsafeMutablePointer<JSValueRef?>?)
func callback(ctx: JSContextRef?, function: JSObjectRef?, thisObject: JSObjectRef?, argumentCount: Int32, arguments: UnsafeMutableRawPointer?, exception: UnsafeMutableRawPointer?) -> JSValueRef {
//callbackBlock(ctx)
JavaScriptCore.JSValueMakeNumber(ctx!, callbackResult)
}
}
Expand Down
Loading
Loading