LiveCode 9.0 DP 6 – Infinite LiveCode Preview

by Ali Lloyd on March 9, 2017 17 comments

LiveCode 9.0.0 DP-6 has just been released, and is the first developer preview containing the Java Foreign Function Interface (FFI). We’re thrilled to be bringing you this exciting first part of the Infinite LiveCode project. It’s been a longish road to get here and as is typical with such large and complex software projects, there have been a number of frustrating walls to be knocked down en route. I’m proud to say we have overcome these roadblocks, and without further ado, let me get stuck in to telling you how you can use it! Foreign handlers can be written in LiveCode Builder (LCB) which bind to calls on Java classes via the Java Native Interface (JNI). In particular this enables binding to classes in the Android API.

This release also contains a command line tool called lc-compile-ffi-java which can auto-generate LCB wrappers of Java classes, given the appropriate data.

Setup

The JNI is currently available on Mac, Linux and Android.

Android

No additional setup is required on Android.

Mac

In order to be able to create a Java Virtual Machine on Mac, you will need the JAVA_HOME environment variable set to the appropriate value. Usually if you have a Java Runtime Environment installed you can find the correct path by executing /usr/libexec/java_home on the command line.

Due to the way the environment works on Mac when the application is launched from the dock, it is not currently possible to call Java APIs on Mac when LiveCode is launched in this way. However the situation is better when you launch LiveCode from the command line:

/Applications/LiveCode Community 9.0.0 (dp 6).app/Contents/Contents/MacOS/LiveCode-Community

If you launch LiveCode like this you can modify the JAVA_HOME environment variable in the message box by doing (for example)

put "/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home" into $JAVA_HOME

Linux

In order to be able to create a Java Virtual Machine on Linux, you will need to set the LD_LIBRARY_PATH environment variable to the location of the libjvm.so library. This is often located at ${JAVA_HOME}/jre/lib/amd64/server or ${JAVA_HOME}/jre/lib/i386/server on 64-bit and 32-bit Linux respectively.

Examples

Android Logger

Here is an example of a library which exposes a public handler OutputToLog, that can be used for logging from LiveCode Script or Builder, so that the output can be seen for example using adb logcat.


library com.livecode.library.androidlog

use com.livecode.foreign
use com.livecode.java

metadata title is "AndroidLog"
metadata author is "LiveCode"
metadata version is "1.0.0"

foreign handler AndroidLog(in pTag as JString, in pLog as JString) returns CInt binds to "java:android.util.Log>d(Ljava/lang/String;Ljava/lang/String;)I!static"

public handler OutputToLog(in pTag as String, in pString as String)
	unsafe 
		AndroidLog(StringToJString(pTag), StringToJString(pString))
	end unsafe
end handler

end library

In the foreign handler declaration, the input parameters (what the actual handler is called with) are JStrings. The binding string breaks down as follows:

java:android.util.Log>d(Ljava/lang/String;Ljava/lang/String;)I!static”
This indicates the language of the binding, i.e. Java

“java:android.util.Log>d(Ljava/lang/String;Ljava/lang/String;)I!static”
This is the class containing the method being called

“java:android.util.Log>d(Ljava/lang/String;Ljava/lang/String;)I!static”
This specified which method defined in the class is being called (in this case, d)

“java:android.util.Log>d(Ljava/lang/String;Ljava/lang/String;)I!static”
This is the parameter string. In brackets are the input parameter codes, and after the brackets is the return parameter code. In this case the d method takes two instances of the java.lang.String class as parameters, and returns an int

“java:android.util.Log>d(Ljava/lang/String;Ljava/lang/String;)I!static
This is the calling convention. The default is instance. The android.util.Log class d method is static, i.e. no instance of the Log class is needed to call it (in Java you would call it using Log.d("tag", "msg")).

Android Battery Level

Here is an example of using the Java FFI to monitor the battery level of an Android device. We jump through a few hoops in order to fetch the application’s Context– in the future we plan to provide an API for some useful things like this.

The battery changed intent is known as a ‘sticky intent’ – this is why we
can access the relevant values at any time rather than registering a BroadcastReceiver to respond to changes in the battery state.

All such sticky intents can be obtained in exactly the same way, passing null as the BroadcastReceiver (in this case passing nothing as the first parameter in ApplicationRegisterReceiver).

Then various values can be accessed using the appropriate EXTRA_ constants. For more information see the Android Intent API


library com.livecode.library.androidbattery

use com.livecode.foreign
use com.livecode.java

metadata title is "Android Battery Library"
metadata author is "LiveCode"
metadata version is "1.0.0"

foreign handler ClassForName(in pClass as JString) returns JObject binds to "java:java.lang.Class>forName(Ljava/lang/String;)Ljava/lang/Class;!static"
foreign handler GetCurrentApplicationMethod(in pActivityThread as JObject, in pMethod as JString, in pParams as optional JObject) returns JObject binds to "java:java.lang.Class>getMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"
foreign handler MethodInvoke(in pMethod as JObject, in pInstance as optional JObject, in pParams as optional JObject) returns JObject binds to "java:java.lang.reflect.Method>invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"

foreign handler GetIntentFilter(in pAction as JString) returns JObject binds to "java:android.content.IntentFilter>new(Ljava/lang/String;)"
foreign handler ApplicationRegisterReceiver(in pContext as JObject, in pObj as optional JObject, in pFilter as JObject) returns JObject binds to "java:android.content.Context>registerReceiver(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;"
foreign handler GetIntentValueInt(in pIntent as JObject, in pWhich as JString, in pDefault as CInt) returns CInt binds to "java:android.content.Intent>getIntExtra(Ljava/lang/String;I)I"

public handler GetBatteryPercentage() returns Number
	unsafe 
	
		// Get the application context. We rely on an undocumented 
		// method to get this 'statically' at the moment.
		variable tClass as JObject
		put ClassForName(StringToJString("android.app.ActivityThread")) into tClass

		variable tMethod as JObject
		put GetCurrentApplicationMethod(tClass, StringToJString("currentApplication"), nothing) into tMethod
				
	 	variable tContext as JObject
	 	put MethodInvoke(tMethod, nothing, nothing) into tContext
	 	
	 	// Get an intent filter for the battery changed intent		
	 	variable tFilter as JObject
	 	put GetIntentFilter(StringToJString("android.intent.action.BATTERY_CHANGED")) into tFilter

		// Get the intent
	 	variable tIntent as JObject
	 	put ApplicationRegisterReceiver(tContext, nothing, tFilter) into tIntent
	 	    	
		// Get the relevant values from the intent.
		variable tLevel as Integer
		put GetIntentValueInt(tIntent, StringToJString("level"), -1) into tLevel

		variable tScale as Integer
		put GetIntentValueInt(tIntent, StringToJString("scale"), -1) into tScale

		return tLevel / tScale * 100	
	end unsafe
end handler

end library

Generating an LCB wrapper from a class specification

We have also provided a tool which eventually will be integrated into the extension builder to generate an LCB wrapper from a class specification.

Here’s an example of the spec file for the java.math.BigInteger class:


foreign package java.math

class Random
end class

class Object
end class

class BigInteger

constructor BigInteger(val as byte[]) named BigIntegerFromBytes
constructor BigInteger(val as String) named BigIntegerFromString
constructor BigInteger(val as String, radix as int) named BigIntegerFromStringWithRadix

method abs() returns BigInteger
method add(val as BigInteger) returns BigInteger
method and(val as BigInteger) returns BigInteger
method andNot(val as BigInteger) returns BigInteger
method bitCount() returns int
method bitLength() returns int
method clearBit(n as int) returns BigInteger
method compareTo(val as BigInteger) returns int
method divide(val as BigInteger) returns BigInteger
method divideAndRemainder(val as BigInteger) returns BigInteger[]
method doubleValue() returns double
method equals(x as Object) returns boolean
method flipBit(n as int) returns BigInteger
method floatValue() returns float
method gcd(val as BigInteger) returns BigInteger
method getLowestSetBit() returns int
method hashCode() returns int
method intValue() returns int
method isProbablePrime(certainty as int) returns boolean
method longValue() returns long
method max(val as BigInteger) returns BigInteger
method min(val as BigInteger) returns BigInteger
method mod(m as BigInteger) returns BigInteger
method modInverse(m as BigInteger) returns BigInteger
method modPow(exponent as BigInteger, m as BigInteger) returns BigInteger
method multiply(val as BigInteger) returns BigInteger
method negate() returns BigInteger
method nextProbablePrime() returns BigInteger
method not() returns BigInteger
method or(val as BigInteger) returns BigInteger
method pow(exponent as int) returns BigInteger
class method probablePrime(bitLength as int, rnd as Random) returns BigInteger
method remainder(val as BigInteger) returns BigInteger
method setBit(n as int) returns BigInteger
method shiftLeft(n as int) returns BigInteger
method shiftRight(n as int) returns BigInteger
method signum() returns int
method subtract(val as BigInteger) returns BigInteger
method testBit(n as int) returns boolean
method toByteArray() returns byte[]
method toString() returns String
method toString(radix as int) named toStringRadix returns String
class method valueOf(val as long) returns BigInteger
method xor(val as BigInteger) returns BigInteger

end class
end package

From this, an LCB wrapper can be generated using the lc-compile-ffi-java tool:

/Applications/LiveCode Business 9.0.0 (dp 6).app/Contents/Tools/Toolchain/lc-compile-ffi-java --modulename com.livecode.java.bigint --output /Users/alilloyd/Desktop/BigInteger.lcb /Users/alilloyd/Desktop/BigInteger.lcb-java

This results in a full wrapping of the class as a module, that can be used in LCB.


module com.livecode.java.bigint 

use com.livecode.foreign
use com.livecode.java
foreign handler _JNI_BigInteger_BigIntegerFromBytes(in pParam_val as JByteArray) returns JObject binds to "java:java.math.BigInteger>new([B)"
foreign handler _JNI_BigInteger_BigIntegerFromString(in pParam_val as JString) returns JObject binds to "java:java.math.BigInteger>new(Ljava/lang/String;)"
foreign handler _JNI_BigInteger_BigIntegerFromStringWithRadix(in pParam_val as JString, in pParam_radix as CInt) returns JObject binds to "java:java.math.BigInteger>new(Ljava/lang/String;I)"
foreign handler _JNI_BigInteger_abs(in pObj as JObject) returns JObject binds to "java:java.math.BigInteger>abs()Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_add(in pObj as JObject, in pParam_val as JObject) returns JObject binds to "java:java.math.BigInteger>add(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_and(in pObj as JObject, in pParam_val as JObject) returns JObject binds to "java:java.math.BigInteger>and(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_andNot(in pObj as JObject, in pParam_val as JObject) returns JObject binds to "java:java.math.BigInteger>andNot(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_bitCount(in pObj as JObject) returns CInt binds to "java:java.math.BigInteger>bitCount()I"
foreign handler _JNI_BigInteger_bitLength(in pObj as JObject) returns CInt binds to "java:java.math.BigInteger>bitLength()I"
foreign handler _JNI_BigInteger_clearBit(in pObj as JObject, in pParam_n as CInt) returns JObject binds to "java:java.math.BigInteger>clearBit(I)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_compareTo(in pObj as JObject, in pParam_val as JObject) returns CInt binds to "java:java.math.BigInteger>compareTo(Ljava/math/BigInteger;)I"
foreign handler _JNI_BigInteger_divide(in pObj as JObject, in pParam_val as JObject) returns JObject binds to "java:java.math.BigInteger>divide(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_divideAndRemainder(in pObj as JObject, in pParam_val as JObject) returns optional JObject binds to "java:java.math.BigInteger>divideAndRemainder(Ljava/math/BigInteger;)[Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_doubleValue(in pObj as JObject) returns CDouble binds to "java:java.math.BigInteger>doubleValue()D"
foreign handler _JNI_BigInteger_equals(in pObj as JObject, in pParam_x as JObject) returns CBool binds to "java:java.math.BigInteger>equals(Ljava/math/BigInteger/Object;)Z"
foreign handler _JNI_BigInteger_flipBit(in pObj as JObject, in pParam_n as CInt) returns JObject binds to "java:java.math.BigInteger>flipBit(I)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_floatValue(in pObj as JObject) returns CFloat binds to "java:java.math.BigInteger>floatValue()F"
foreign handler _JNI_BigInteger_gcd(in pObj as JObject, in pParam_val as JObject) returns JObject binds to "java:java.math.BigInteger>gcd(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_getLowestSetBit(in pObj as JObject) returns CInt binds to "java:java.math.BigInteger>getLowestSetBit()I"
foreign handler _JNI_BigInteger_hashCode(in pObj as JObject) returns CInt binds to "java:java.math.BigInteger>hashCode()I"
foreign handler _JNI_BigInteger_intValue(in pObj as JObject) returns CInt binds to "java:java.math.BigInteger>intValue()I"
foreign handler _JNI_BigInteger_isProbablePrime(in pObj as JObject, in pParam_certainty as CInt) returns CBool binds to "java:java.math.BigInteger>isProbablePrime(I)Z"
foreign handler _JNI_BigInteger_longValue(in pObj as JObject) returns CInt binds to "java:java.math.BigInteger>longValue()J"
foreign handler _JNI_BigInteger_max(in pObj as JObject, in pParam_val as JObject) returns JObject binds to "java:java.math.BigInteger>max(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_min(in pObj as JObject, in pParam_val as JObject) returns JObject binds to "java:java.math.BigInteger>min(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_mod(in pObj as JObject, in pParam_m as JObject) returns JObject binds to "java:java.math.BigInteger>mod(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_modInverse(in pObj as JObject, in pParam_m as JObject) returns JObject binds to "java:java.math.BigInteger>modInverse(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_modPow(in pObj as JObject, in pParam_exponent as JObject, in pParam_m as JObject) returns JObject binds to "java:java.math.BigInteger>modPow(Ljava/math/BigInteger;Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_multiply(in pObj as JObject, in pParam_val as JObject) returns JObject binds to "java:java.math.BigInteger>multiply(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_negate(in pObj as JObject) returns JObject binds to "java:java.math.BigInteger>negate()Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_nextProbablePrime(in pObj as JObject) returns JObject binds to "java:java.math.BigInteger>nextProbablePrime()Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_not(in pObj as JObject) returns JObject binds to "java:java.math.BigInteger>not()Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_or(in pObj as JObject, in pParam_val as JObject) returns JObject binds to "java:java.math.BigInteger>or(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_pow(in pObj as JObject, in pParam_exponent as CInt) returns JObject binds to "java:java.math.BigInteger>pow(I)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_probablePrime(in pParam_bitLength as CInt, in pParam_rnd as JObject) returns JObject binds to "java:java.math.BigInteger>probablePrime(ILjava/math/BigInteger/Random;)Ljava/math/BigInteger;!static"
foreign handler _JNI_BigInteger_remainder(in pObj as JObject, in pParam_val as JObject) returns JObject binds to "java:java.math.BigInteger>remainder(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_setBit(in pObj as JObject, in pParam_n as CInt) returns JObject binds to "java:java.math.BigInteger>setBit(I)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_shiftLeft(in pObj as JObject, in pParam_n as CInt) returns JObject binds to "java:java.math.BigInteger>shiftLeft(I)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_shiftRight(in pObj as JObject, in pParam_n as CInt) returns JObject binds to "java:java.math.BigInteger>shiftRight(I)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_signum(in pObj as JObject) returns CInt binds to "java:java.math.BigInteger>signum()I"
foreign handler _JNI_BigInteger_subtract(in pObj as JObject, in pParam_val as JObject) returns JObject binds to "java:java.math.BigInteger>subtract(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_testBit(in pObj as JObject, in pParam_n as CInt) returns CBool binds to "java:java.math.BigInteger>testBit(I)Z"
foreign handler _JNI_BigInteger_toByteArray(in pObj as JObject) returns JByteArray binds to "java:java.math.BigInteger>toByteArray()[B"
foreign handler _JNI_BigInteger_toString(in pObj as JObject) returns JString binds to "java:java.math.BigInteger>toString()Ljava/lang/String;"
foreign handler _JNI_BigInteger_toStringRadix(in pObj as JObject, in pParam_radix as CInt) returns JString binds to "java:java.math.BigInteger>toString(I)Ljava/lang/String;"
foreign handler _JNI_BigInteger_valueOf(in pParam_val as CInt) returns JObject binds to "java:java.math.BigInteger>valueOf(J)Ljava/math/BigInteger;!static"
foreign handler _JNI_BigInteger_xor(in pObj as JObject, in pParam_val as JObject) returns JObject binds to "java:java.math.BigInteger>xor(Ljava/math/BigInteger;)Ljava/math/BigInteger;"

public handler BigIntegerFromBytes_Constructor(in pParam_val as Data) returns JObject
	variable tParam_val as JByteArray
	put DataToJByteArray(pParam_val) into tParam_val

	unsafe
		return _JNI_BigInteger_BigIntegerFromBytes(tParam_val)
	end unsafe
end handler

public handler BigIntegerFromString_Constructor(in pParam_val as String) returns JObject
	variable tParam_val as JString
	put StringToJString(pParam_val) into tParam_val

	unsafe
		return _JNI_BigInteger_BigIntegerFromString(tParam_val)
	end unsafe
end handler

public handler BigIntegerFromStringWithRadix_Constructor(in pParam_val as String, in pParam_radix as Number) returns JObject
	variable tParam_val as JString
	put StringToJString(pParam_val) into tParam_val

	unsafe
		return _JNI_BigInteger_BigIntegerFromStringWithRadix(tParam_val, pParam_radix)
	end unsafe
end handler

public handler BigInteger_abs(in pObj as JObject) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_abs(pObj) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_add(in pObj as JObject, in pParam_val as JObject) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_add(pObj, pParam_val) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_and(in pObj as JObject, in pParam_val as JObject) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_and(pObj, pParam_val) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_andNot(in pObj as JObject, in pParam_val as JObject) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_andNot(pObj, pParam_val) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_bitCount(in pObj as JObject) returns Number
	variable tJNIResult as Number
	unsafe
		put _JNI_BigInteger_bitCount(pObj) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_bitLength(in pObj as JObject) returns Number
	variable tJNIResult as Number
	unsafe
		put _JNI_BigInteger_bitLength(pObj) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_clearBit(in pObj as JObject, in pParam_n as Number) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_clearBit(pObj, pParam_n) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_compareTo(in pObj as JObject, in pParam_val as JObject) returns Number
	variable tJNIResult as Number
	unsafe
		put _JNI_BigInteger_compareTo(pObj, pParam_val) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_divide(in pObj as JObject, in pParam_val as JObject) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_divide(pObj, pParam_val) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_divideAndRemainder(in pObj as JObject, in pParam_val as JObject) returns List
	variable tJNIResult as List
	unsafe
		put _JNI_BigInteger_divideAndRemainder(pObj, pParam_val) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_doubleValue(in pObj as JObject) returns Number
	variable tJNIResult as Number
	unsafe
		put _JNI_BigInteger_doubleValue(pObj) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_equals(in pObj as JObject, in pParam_x as JObject) returns Boolean
	variable tJNIResult as Boolean
	unsafe
		put _JNI_BigInteger_equals(pObj, pParam_x) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_flipBit(in pObj as JObject, in pParam_n as Number) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_flipBit(pObj, pParam_n) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_floatValue(in pObj as JObject) returns Number
	variable tJNIResult as Number
	unsafe
		put _JNI_BigInteger_floatValue(pObj) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_gcd(in pObj as JObject, in pParam_val as JObject) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_gcd(pObj, pParam_val) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_getLowestSetBit(in pObj as JObject) returns Number
	variable tJNIResult as Number
	unsafe
		put _JNI_BigInteger_getLowestSetBit(pObj) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_hashCode(in pObj as JObject) returns Number
	variable tJNIResult as Number
	unsafe
		put _JNI_BigInteger_hashCode(pObj) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_intValue(in pObj as JObject) returns Number
	variable tJNIResult as Number
	unsafe
		put _JNI_BigInteger_intValue(pObj) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_isProbablePrime(in pObj as JObject, in pParam_certainty as Number) returns Boolean
	variable tJNIResult as Boolean
	unsafe
		put _JNI_BigInteger_isProbablePrime(pObj, pParam_certainty) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_longValue(in pObj as JObject) returns Number
	variable tJNIResult as Number
	unsafe
		put _JNI_BigInteger_longValue(pObj) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_max(in pObj as JObject, in pParam_val as JObject) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_max(pObj, pParam_val) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_min(in pObj as JObject, in pParam_val as JObject) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_min(pObj, pParam_val) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_mod(in pObj as JObject, in pParam_m as JObject) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_mod(pObj, pParam_m) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_modInverse(in pObj as JObject, in pParam_m as JObject) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_modInverse(pObj, pParam_m) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_modPow(in pObj as JObject, in pParam_exponent as JObject, in pParam_m as JObject) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_modPow(pObj, pParam_exponent, pParam_m) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_multiply(in pObj as JObject, in pParam_val as JObject) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_multiply(pObj, pParam_val) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_negate(in pObj as JObject) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_negate(pObj) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_nextProbablePrime(in pObj as JObject) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_nextProbablePrime(pObj) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_not(in pObj as JObject) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_not(pObj) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_or(in pObj as JObject, in pParam_val as JObject) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_or(pObj, pParam_val) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_pow(in pObj as JObject, in pParam_exponent as Number) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_pow(pObj, pParam_exponent) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_probablePrime(in pParam_bitLength as Number, in pParam_rnd as JObject) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_probablePrime(pParam_bitLength, pParam_rnd) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_remainder(in pObj as JObject, in pParam_val as JObject) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_remainder(pObj, pParam_val) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_setBit(in pObj as JObject, in pParam_n as Number) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_setBit(pObj, pParam_n) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_shiftLeft(in pObj as JObject, in pParam_n as Number) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_shiftLeft(pObj, pParam_n) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_shiftRight(in pObj as JObject, in pParam_n as Number) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_shiftRight(pObj, pParam_n) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_signum(in pObj as JObject) returns Number
	variable tJNIResult as Number
	unsafe
		put _JNI_BigInteger_signum(pObj) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_subtract(in pObj as JObject, in pParam_val as JObject) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_subtract(pObj, pParam_val) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_testBit(in pObj as JObject, in pParam_n as Number) returns Boolean
	variable tJNIResult as Boolean
	unsafe
		put _JNI_BigInteger_testBit(pObj, pParam_n) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_toByteArray(in pObj as JObject) returns Data
	variable tJNIResult as JByteArray
	unsafe
		put _JNI_BigInteger_toByteArray(pObj) into tJNIResult
	end unsafe
	return DataFromJByteArray(tJNIResult)
end handler

public handler BigInteger_toString(in pObj as JObject) returns String
	variable tJNIResult as JString
	unsafe
		put _JNI_BigInteger_toString(pObj) into tJNIResult
	end unsafe
	return StringFromJString(tJNIResult)
end handler

public handler BigInteger_toStringRadix(in pObj as JObject, in pParam_radix as Number) returns String
	variable tJNIResult as JString
	unsafe
		put _JNI_BigInteger_toStringRadix(pObj, pParam_radix) into tJNIResult
	end unsafe
	return StringFromJString(tJNIResult)
end handler

public handler BigInteger_valueOf(in pParam_val as Number) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_valueOf(pParam_val) into tJNIResult
	end unsafe
	return tJNIResult
end handler

public handler BigInteger_xor(in pObj as JObject, in pParam_val as JObject) returns JObject
	variable tJNIResult as JObject
	unsafe
		put _JNI_BigInteger_xor(pObj, pParam_val) into tJNIResult
	end unsafe
	return tJNIResult
end handler
end module 

This module can then be used to implement a library for performing big integer arithmetic:


use com.livecode.java.bigint
public handler AddBigIntegers(in pLeft as String, in pRight as String) returns String
	unsafe
		variable tLeftBigInt as JObject
		variable tRightBigInt as JObject
		put BigIntegerFromString_Constructor(pLeft) into tLeftBigInt
		put BigIntegerFromString_Constructor(pRight) into tRightBigInt
		
		variable tSumBigInt as JObject
		put BigInteger_add(tLeftBigInt, tRightBigInt) into tSumBigInt
		return BigInteger_toString(tSumBigInt)
	end unsafe
end handler

I hope you find this new functionality for LiveCode useful. I can’t wait to see all the new Android widgets and addons that it enables starting to be created! I’m really looking forward to bringing you further parts of the Infinite LiveCode project in the future.

Ali LloydLiveCode 9.0 DP 6 – Infinite LiveCode Preview

Related Posts

Take a look at these posts

17 comments

Join the conversation
  • Paul Dupuis - March 9, 2017 reply

    I am delighted that LC9 continues to expand what can be done. I hope LiveCode someday publishes an idiot’s guide to Infinite Livecode, because what I don’t get is the steps to get from this blot post to command/functions usable in scripts.

    If I google Java BigInteger Library, I get https://docs.oracle.com/javase/7/docs/api/java/math/BigInteger.html

    When I look at this library I see a bunch of functions, and being barely familiar with Java (I took 1 class nearly 20 years ago), I guess, I could write something like

    put BigInteger(“1234567890″) into X — using the BigInteger(String val) constructor to translates the decimal String representation of a BigInteger into a BigInteger.
    put add(X,”9876543210”) into X — using add(BigInteger val)
    put toString(X) into fld “somefield” — using toString()

    and end up with “11111111100”?

    Could you perhaps expand your BigInteger example at some point to show how one can get from the blog post to command/functions usable in scripts?

    Ali Lloyd - March 9, 2017 reply

    Hi Paul,
    Essentially what you write is correct, although the foreign bindings are only available within LiveCode Builder (until you provide some sort of LCB library interface to use it in LiveCode Script). So for example, if you just wanted to be able to add two big integers in LCS, you could put some of the above together to write an LCB library as follows:

    
    library com.livecode.library.javabiginteger
    use com.livecode.foreign
    use com.livecode.java
    
    -- Foreign handler binding to BigInteger constructor
    foreign handler _JNI_BigInteger_BigIntegerFromString(in pParam_val as JString) returns JObject binds to "java:java.math.BigInteger>new(Ljava/lang/String;)"
    
    -- Wrap the foreign handler to take LCB strings and convert to BigIntegers
    handler BigIntegerFromString_Constructor(in pParam_val as String) returns JObject
    	variable tParam_val as JString
    	put StringToJString(pParam_val) into tParam_val
    
    	unsafe
    		return _JNI_BigInteger_BigIntegerFromString(tParam_val)
    	end unsafe
    end handler
    
    -- Foreign handler binding to BigInteger addition
    foreign handler _JNI_BigInteger_add(in pObj as JObject, in pParam_val as JObject) returns JObject binds to "java:java.math.BigInteger>add(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
    
    --Foreign handler binding to BigInteger to Java String conversion
    foreign handler _JNI_BigInteger_toString(in pObj as JObject) returns JString binds to "java:java.math.BigInteger>toString()Ljava/lang/String;"
    
    -- Wrap the foreign handler to take BigIntegers and convert to LCB strings
    handler BigInteger_toString(in pObj as JObject) returns String
    	variable tJNIResult as JString
    	unsafe
    		put _JNI_BigInteger_toString(pObj) into tJNIResult
    	end unsafe
    	return StringFromJString(tJNIResult)
    end handler
    
    -- Public handler - this will be in LCS message path when the library is loaded
    public handler AddBigIntegers(in pLeft as String, in pRight as String) returns String
    	unsafe
    		variable tLeftBigInt as JObject
    		variable tRightBigInt as JObject
    		put BigIntegerFromString_Constructor(pLeft) into tLeftBigInt
    		put BigIntegerFromString_Constructor(pRight) into tRightBigInt
    		
    		variable tSumBigInt as JObject
    		put BigInteger_add(tLeftBigInt, tRightBigInt) into tSumBigInt
    		return BigInteger_toString(tSumBigInt)
    	end unsafe
    end handler
    end library
    

    Once that library is loaded, you could then do

    put AddBigIntegers("1234567890", "9876543210")

    to obtain 11111111100 in the message box (for example).

    Does that make things a little clearer?

  • Michael T Walas - March 9, 2017 reply

    Will this new functionality eventually help with further HTML5 development like networking and databases?

    Ali Lloyd - March 13, 2017 reply

    Unfortunately not, no. As far as I know we have no way of being able to load the Java Runtime Environment from the HTML5 engine.

  • Paul McClernan - March 10, 2017 reply

    Sweet, I can’t wait to have time to delve into this more!
    https://developer.android.com/reference/android/os/BatteryManager.html
    So looking at the above page we can can add more to this lib like so:

    public handler GetBatteryPlugged() returns Number
    unsafe
    // Get the application context. We rely on an undocumented
    // method to get this ‘statically’ at the moment.
    variable tClass as JObject
    put ClassForName(StringToJString(“android.app.ActivityThread”)) into tClass

    variable tMethod as JObject
    put GetCurrentApplicationMethod(tClass, StringToJString(“currentApplication”), nothing) into tMethod

    variable tContext as JObject
    put MethodInvoke(tMethod, nothing, nothing) into tContext

    // Get an intent filter for the battery changed intent
    variable tFilter as JObject
    put GetIntentFilter(StringToJString(“android.intent.action.BATTERY_CHANGED”)) into tFilter

    // Get the intent
    variable tIntent as JObject
    put ApplicationRegisterReceiver(tContext, nothing, tFilter) into tIntent

    // Get the relevant values from the intent.
    variable tLevel as Integer
    put GetIntentValueInt(tIntent, StringToJString(“plugged”), -1) into tPlugBool

    return tPlugBool
    end unsafe
    end handler

    Paul McClernan - March 10, 2017 reply

    err
    forgot to edit :
    variable tLevel as Integer
    should read
    variable tPlugBool as Integer

    Paul McClernan - March 10, 2017 reply

    Probably be nicer if it was returning a string “Plugged” or “Unplugged”

    Ali Lloyd - March 13, 2017 reply

    Yep, that looks like it would work! For a library full of battery utilities you would probably want to store the application context in a variable so as not to have to fetch it each time.

  • MaxV - March 21, 2017 reply

    Any RSS of this blog? How can I advertise it with my friends?

    Ali Lloyd - March 21, 2017 reply

    Hi Max, https://livecode.com/blog/feed/ should work!

  • Paul McClernan - March 28, 2017 reply

    OK, I actually go around to resetting up to build for Android and tried to build the battery library. It seems to compile correctly but then fails building the Android apk package. It’s something to do with the manifest or something (not at home right now). I see there is a manifest file generated in the same folder as the compiled, is there something more that needs to be set so this file overrides the normal default manifest or something?
    T.I.A.

    Ali Lloyd - March 28, 2017 reply

    The manifest is not affected by the inclusion or otherwise of the LCB module, so if you are getting an error to do with manifests I suspect it will happen with a stack with nothing on it too…

    Paul McClernan - March 29, 2017 reply

    Sorry my bad, it was actually a missing provisioning file for iOS build (obviously not needed here). Thanks.

    Paul McClernan - March 29, 2017 reply

    Got things to compile, but not getting any functionality, installed apk phone but get no results from GetBatteryPercentage(), also tried the JAVA Big Integer code on Mac OS X but getting domain error when I try AddBigIntegers(“12345″,12345”). My environment is set up correctly and everything compiles installs as library extension fine. “Hint Runtime” isn’t very helpful, is there fully tested & working code on on git hub or somewhere? Should probably move this to a forum discussion.
    Thanks Ali

    Paul McClernan - March 29, 2017

    AddBigIntegers(“12345″,”12345”), that was a typo, not the problem.

    Ali Lloyd - March 29, 2017

    OK I have started a forum topic here: http://forums.livecode.com/viewtopic.php?f=93&t=29062

  • Paul McClernan - April 26, 2017 reply

    I set up a Git project with the Android Battery example with a few more functions added (I’m not sure if all of the functions work correctly though, I haven’t done much testing).
    https://github.com/PaulMcClernan/LCAndroidBatteryLib

Join the conversation

*