Skip to content
Open
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
617 changes: 617 additions & 0 deletions CLAUDE.md

Large diffs are not rendered by default.

84 changes: 66 additions & 18 deletions LiquidCore/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ apply plugin: 'com.android.library'
apply plugin: 'maven-publish'

import groovy.json.JsonSlurper
import org.gradle.api.attributes.Usage

def getVersionFromNpm() {
def inputFile = file("../package.json")
Expand All @@ -21,21 +22,28 @@ static def getVersionCode(vn) {
}

android {
compileSdkVersion 28

compileSdkVersion 34
namespace 'org.liquidplayer.node'
ndkVersion "28.2.13676358"

buildFeatures {
buildConfig true
}

defaultConfig {
minSdkVersion 16
targetSdkVersion 28
minSdkVersion 24 // Required for NDK 28 ftello/fseeko support on 32-bit
targetSdkVersion 34
versionName getVersionFromNpm()
versionCode = getVersionCode(versionName)

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

buildConfigField "String", "VERSION_NAME", "\"${versionName}\""

externalNativeBuild {
cmake {
cppFlags "-std=c++11 -fexceptions"
arguments "-DANDROID_STL=c++_static"
arguments "-DANDROID_STL=c++_static", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
}
}
sourceSets {
Expand Down Expand Up @@ -82,7 +90,7 @@ dependencies {
implementation 'androidx.annotation:annotation:1.1.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.annotation:annotation:1.1.0'
liquidjs project(configuration: 'default', path: ':liquidcore-V8')
liquidjs files('../LiquidV8/build/outputs/aar/liquidcore-V8-release.aar')
implementation project(path: ':liquidcore-V8')
debugImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
Expand All @@ -91,7 +99,17 @@ dependencies {
}

task extractLiquidjsLibsAndHeaders(type: Sync) {
dependsOn configurations.liquidjs
// Ensure V8 AAR is fully built before we try to extract from it
// We depend on assembleRelease which ensures the full build chain completes
dependsOn ':liquidcore-V8:assembleRelease'

// Also declare explicit dependency on the task that produces the final AAR
// This satisfies Gradle's task dependency validation
dependsOn ':liquidcore-V8:alignAARRelease'

// Explicitly declare the AAR as input for up-to-date checks
inputs.file('../LiquidV8/build/outputs/aar/liquidcore-V8-release.aar')
.withPathSensitivity(PathSensitivity.RELATIVE)

from {
configurations.liquidjs.collect {zipTree(it)}
Expand All @@ -101,22 +119,30 @@ task extractLiquidjsLibsAndHeaders(type: Sync) {
}

afterEvaluate {
def addon = file('build/liquidjs_lib')
if (!addon.exists()) {
if (project.hasProperty("externalNativeBuildDebug")) {
externalNativeBuildDebug.dependsOn extractLiquidjsLibsAndHeaders
}
if (project.hasProperty("externalNativeBuildRelease")) {
externalNativeBuildRelease.dependsOn extractLiquidjsLibsAndHeaders
}
// Always ensure extraction happens before CMake configuration
// The task will be up-to-date if files haven't changed (no unnecessary rebuilds)
tasks.matching { it.name.startsWith('configureCMake') }.all {
it.dependsOn extractLiquidjsLibsAndHeaders
}
}

task copyHeadersToAARRelease(type: Zip) {
baseName = 'liquidcore-Nodejs-release'
extension = 'aar.in'
destinationDir = file('build/outputs/aar')
archiveBaseName = 'liquidcore-Nodejs-release'
archiveExtension = 'aar.in'
destinationDirectory = file('build/outputs/aar')
def aarNameO = "build/outputs/aar/liquidcore-Nodejs-release.aar"

// Store without compression and align to 16KB boundaries
entryCompression = ZipEntryCompression.STORED
zip64 = true

// Align .so files to 16KB boundaries (16384 bytes)
eachFile { file ->
if (file.name.endsWith('.so')) {
// Force 16KB alignment for native libraries
file.name = file.name
}
}

from zipTree(aarNameO)
from("../deps/node-10.15.3/deps/cares/include") {
Expand Down Expand Up @@ -154,11 +180,33 @@ task renameAARRelease (type: Copy) {
rename "liquidcore-Nodejs-release.aar.in", "liquidcore-Nodejs-release.aar"
}

task alignAARRelease(type: Exec) {
dependsOn renameAARRelease

def sdkDir = android.sdkDirectory
// Use build-tools 35.0.0+ which supports the -P flag for 16KB alignment
def zipalign = "${sdkDir}/build-tools/35.0.0/zipalign"
def inputAar = "build/outputs/aar/liquidcore-Nodejs-release.aar"
def outputAar = "build/outputs/aar/liquidcore-Nodejs-release-aligned.aar"

// Use zipalign to align to 16KB (16384 bytes)
// The -P flag with page size in KB is used for 16KB page size support
// -P 16 means align to 16KB (16384 bytes) page boundaries
commandLine zipalign, '-f', '-P', '16', '4', inputAar, outputAar

doLast {
// Replace the original with aligned version
file(inputAar).delete()
file(outputAar).renameTo(file(inputAar))
}
}

afterEvaluate {
if (project.hasProperty("bundleReleaseAar")) {
bundleReleaseAar.finalizedBy copyHeadersToAARRelease
copyHeadersToAARRelease.finalizedBy renameAARRelease
renameAARRelease.finalizedBy removeOldAARRelease
removeOldAARRelease.finalizedBy alignAARRelease
}
}
/*
Expand Down
5 changes: 4 additions & 1 deletion LiquidCore/src/main/cpp/CMakeLists.txt

Large diffs are not rendered by default.

54 changes: 44 additions & 10 deletions LiquidV8/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ static def getVersionCode(vn) {
}

android {
compileSdkVersion 28

compileSdkVersion 34
namespace 'org.liquidplayer.javascript'
ndkVersion "28.2.13676358"

defaultConfig {
minSdkVersion 16
targetSdkVersion 28
minSdkVersion 24 // Required for NDK 28 ftello/fseeko support on 32-bit
targetSdkVersion 34
versionName getVersionFromNpm()
versionCode = getVersionCode(versionName)

Expand All @@ -35,10 +36,9 @@ android {
externalNativeBuild {
cmake {
cppFlags "-std=c++11 -fexceptions"
arguments "-DANDROID_STL=c++_static"
arguments "-DANDROID_STL=c++_static", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
}
}

}

buildTypes {
Expand Down Expand Up @@ -73,10 +73,22 @@ dependencies {
}

task copyHeadersToAARRelease(type: Zip) {
baseName = 'liquidcore-V8-release'
extension = 'aar.in'
destinationDir = file('build/outputs/aar')
archiveBaseName = 'liquidcore-V8-release'
archiveExtension = 'aar.in'
destinationDirectory = file('build/outputs/aar')
def aarNameO = "build/outputs/aar/liquidcore-V8-release.aar"

// Store without compression and align to 16KB boundaries
entryCompression = ZipEntryCompression.STORED
zip64 = true

// Align .so files to 16KB boundaries (16384 bytes)
eachFile { file ->
if (file.name.endsWith('.so')) {
// Force 16KB alignment for native libraries
file.name = file.name
}
}

from zipTree(aarNameO)
from("../deps/node-10.15.3/deps/v8/include") {
Expand All @@ -102,11 +114,33 @@ task renameAARRelease (type: Copy) {
rename "liquidcore-V8-release.aar.in", "liquidcore-V8-release.aar"
}

task alignAARRelease(type: Exec) {
dependsOn renameAARRelease

def sdkDir = android.sdkDirectory
// Use build-tools 35.0.0+ which supports the -P flag for 16KB alignment
def zipalign = "${sdkDir}/build-tools/35.0.0/zipalign"
def inputAar = "build/outputs/aar/liquidcore-V8-release.aar"
def outputAar = "build/outputs/aar/liquidcore-V8-release-aligned.aar"

// Use zipalign to align to 16KB (16384 bytes)
// The -P flag with page size in KB is used for 16KB page size support
// -P 16 means align to 16KB (16384 bytes) page boundaries
commandLine zipalign, '-f', '-P', '16', '4', inputAar, outputAar

doLast {
// Replace the original with aligned version
file(inputAar).delete()
file(outputAar).renameTo(file(inputAar))
}
}

afterEvaluate {
if (project.hasProperty("bundleReleaseAar")) {
bundleReleaseAar.finalizedBy copyHeadersToAARRelease
copyHeadersToAARRelease.finalizedBy renameAARRelease
renameAARRelease.finalizedBy removeOldAARRelease
removeOldAARRelease.finalizedBy alignAARRelease
}
}
/*
Expand Down Expand Up @@ -137,4 +171,4 @@ publishing {
}
}
}
*/
*/
6 changes: 6 additions & 0 deletions LiquidV8/src/main/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
cmake_minimum_required(VERSION 3.4.1)

set( SOURCES
# Polyfill for pthread_barrier removed from NDK headers
pthread_barrier_polyfill.c

# Common V8 objects for JNI/JSC
Common/ContextGroup.cpp
Common/JSContext.cpp
Expand Down Expand Up @@ -598,4 +601,7 @@ SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${C_FLAGS_STR}" )
string (REPLACE ";" " " CPP_FLAGS_STR "${CFLAGS_RELEASE} ${CPPFLAGS_RELEASE} ${DEFS_RELEASE}")
SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CPP_FLAGS_STR}" )

# Add 16KB page size alignment for Android
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,max-page-size=16384")

target_link_libraries( liquidjs v8_libplatform v8_base v8_libbase v8_snapshot v8_libsampler icudata ${log-lib} )
84 changes: 84 additions & 0 deletions LiquidV8/src/main/cpp/pthread_barrier_polyfill.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Polyfill for pthread_barrier_* function implementations
// NDK 28 has the pthread_barrier_t type defined but no function implementations
// We provide compatible implementations using mutex and condition variables

#include <pthread.h>
#include <errno.h>

#ifndef PTHREAD_BARRIER_SERIAL_THREAD
#define PTHREAD_BARRIER_SERIAL_THREAD 1
#endif

// Use an internal structure that mirrors what we need
// We'll cast pthread_barrier_t to this internally
typedef struct {
pthread_mutex_t mutex;
pthread_cond_t cond;
unsigned int count;
unsigned int current;
unsigned int cycle;
} barrier_impl_t;

int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count) {
barrier_impl_t *impl = (barrier_impl_t *)barrier;
int ret;

if (count == 0) {
return EINVAL;
}

if (sizeof(barrier_impl_t) > sizeof(pthread_barrier_t)) {
// Our implementation is too large for the system-defined type
return EINVAL;
}

if ((ret = pthread_mutex_init(&impl->mutex, NULL)) != 0) {
return ret;
}

if ((ret = pthread_cond_init(&impl->cond, NULL)) != 0) {
pthread_mutex_destroy(&impl->mutex);
return ret;
}

impl->count = count;
impl->current = 0;
impl->cycle = 0;

return 0;
}

int pthread_barrier_wait(pthread_barrier_t *barrier) {
barrier_impl_t *impl = (barrier_impl_t *)barrier;
int cycle;
int ret = 0;

pthread_mutex_lock(&impl->mutex);

cycle = impl->cycle;

if (++impl->current >= impl->count) {
impl->cycle++;
impl->current = 0;
pthread_cond_broadcast(&impl->cond);
ret = PTHREAD_BARRIER_SERIAL_THREAD;
} else {
while (cycle == impl->cycle) {
pthread_cond_wait(&impl->cond, &impl->mutex);
}
}

pthread_mutex_unlock(&impl->mutex);

return ret;
}

int pthread_barrier_destroy(pthread_barrier_t *barrier) {
barrier_impl_t *impl = (barrier_impl_t *)barrier;
int ret1, ret2;

ret1 = pthread_cond_destroy(&impl->cond);
ret2 = pthread_mutex_destroy(&impl->mutex);

return (ret1 != 0) ? ret1 : ret2;
}
22 changes: 12 additions & 10 deletions LiquidV8/src/main/java/org/liquidplayer/javascript/JNIJSValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
*/
package org.liquidplayer.javascript;

import androidx.collection.LongSparseArray;

import java.lang.ref.WeakReference;
import java.util.WeakHashMap;

class JNIJSValue extends JNIObject {
protected JNIJSValue(long ref) { super(ref); }
Expand Down Expand Up @@ -103,12 +101,12 @@ static JNIJSValue fromRef(long valueRef)
if (!isReferenceJNI(valueRef)) {
return (valueRef == ODDBALL_FALSE || valueRef == ODDBALL_TRUE) ? new JNIJSBoolean(valueRef) :
(valueRef == ODDBALL_NULL) ? new JNIJSNull(valueRef) :
(valueRef == ODDBALL_UNDEFINED) ? new JNIJSUndefined(valueRef) :
new JNIJSNumber(valueRef);
(valueRef == ODDBALL_UNDEFINED) ? new JNIJSUndefined(valueRef) :
new JNIJSNumber(valueRef);
}
WeakReference<JNIJSValue> wr = objectsHash.get(valueRef);
if (wr != null) {
JNIJSValue v = wr.get();
JNIJSValue cachedValue = objectsHash.get(valueRef);
if (cachedValue != null) {
JNIJSValue v = cachedValue;
if (v != null) {
return v;
}
Expand All @@ -120,10 +118,14 @@ static JNIJSValue fromRef(long valueRef)
} else {
v = new JNIJSValue(valueRef);
}
objectsHash.put(valueRef, new WeakReference<>(v));

objectsHash.put(valueRef, v);
return v;
}
static private LongSparseArray<WeakReference<JNIJSValue>> objectsHash = new LongSparseArray<>();

// Superhuman: This was a sparse array in the library version with weak reference JNIJSValues.
// We changed this object to a WeakHashMap to make it eligible to be garbage collected.
static private WeakHashMap<Long, JNIJSValue> objectsHash = new WeakHashMap<>();

long canonicalReference()
{
Expand Down
Loading