Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for the Julia programming language. #23

Open
Ismael-VC opened this issue Feb 26, 2016 · 16 comments
Open

Add support for the Julia programming language. #23

Ismael-VC opened this issue Feb 26, 2016 · 16 comments

Comments

@Ismael-VC
Copy link

Continues discussion at Twitter:

There is a very active chat room for the Julia language, here!

@kuri65536
Copy link
Owner

OK, Thank you moving from twitter to here.

I think julia maybe work with SL4A,
Because SL4A just launch the language binary in terminal app
and you said
"I can run Julia from Arch Linux Linux Deploy app in my rooted cellphone" in your tweet.

Next, you need to package the deploy app and
register it to SL4A system as android application like py4a.

Please fork this project or SL4A language branch,
or copy them for julia.

@michaelrinderle
Copy link

@Ismael-VC
Are you familiar with the language? I can add the hook inside SL4A to recognize the language but I am pretty sure you will need a module on the Julia said to talk back to the rpc server as well as an interpreter for android. I can help you out with that but I don't know julia syntax...

p.s. good looking on running arch. i run arch too! :D

@Ismael-VC
Copy link
Author

@ainsophical yes definitely! Thank you so much, that would be awesome, please let me know what I can do from the Julia side in order to help you make this possible. 👍

The Julia syntax is easy! 😄

It's a little outdated (v0.3) but for that introductory tutorial, syntax has remain the same AFAIKT.

@michaelrinderle
Copy link

@Ismael-VC

not really interested in julia but i think it would be cool to get more interpreted languages hooked into SL4A for a broader audience of this really cool app...

we need to hook julia into the known language list in SL4A. (Took care of this.... easy)

We need to compile Julia into an arm executable so it can run on android, and add any modules that are not c based. Then package this all up in an apk. I can start working on this as I want to get the process down so I can get other languages working in the future...

We also need an android module for julia. If you want you can convert this python android module to julia. Once I get the template going we can test it and see how it works...

import collections
import json
import os
import socket
import sys

PORT = os.environ.get('AP_PORT')
HOST = os.environ.get('AP_HOST')
HANDSHAKE = os.environ.get('AP_HANDSHAKE')
Result = collections.namedtuple('Result', 'id,result,error')


class Android(object):

  def __init__(self, addr=None):
    if addr is None:
      addr = HOST, PORT
    self.conn = socket.create_connection(addr)
    self.client = self.conn.makefile()
    self.id = 0
    if HANDSHAKE is not None:
      self._authenticate(HANDSHAKE)

  def _rpc(self, method, *args):
    data = {'id': self.id,
            'method': method,
            'params': args}
    request = json.dumps(data)
    self.client.write(request+'\n')
    self.client.flush()
    response = self.client.readline()
    self.id += 1
    result = json.loads(response)
    if result['error'] is not None:
      print result['error']
    # namedtuple doesn't work with unicode keys.
    return Result(id=result['id'], result=result['result'],
                  error=result['error'], )

  def __getattr__(self, name):
    def rpc_call(*args):
      return self._rpc(name, *args)
    return rpc_call

@Ismael-VC
Copy link
Author

@ainsophical where does self._authenticate comes from? I think the code snippet above could be incomplete. Where does this snippet come from?

@Ismael-VC
Copy link
Author

Oh ok, I found it at:

If HANDSHAKE is not None, calling Android() would raise an: AttributeError: 'Android' object has no attribute '_authenticate'

Is this a 🪲?

@Ismael-VC
Copy link
Author

Julia ARM executable, the one I've tested on Android:

@Ismael-VC
Copy link
Author

Three Julia packages would be needed, all are 100% julia code:

@michaelrinderle
Copy link

@Ismael-VC

i see what you mean and it didn't make much sense to me either. it's not a bug but i can't honestly say i know exactly how it works with a bit of prying into the code yet.

i ran the android.py module with that conditional commented out and got this error in logcat.

V/sl4a.JsonRpcServer:117: Sent: {"id":0,"result":null,"error":"java.lang.SecurityException: Authentication failed!"} 

i found this in JsonRpcServer.java

private boolean checkHandshake(String method, JSONArray params) throws JSONException {
        return method.equals("_authenticate") && this.mHandshake.equals(params.getString(0));
    }

inherited through the socket connection somehow? not sure but yes that part is needed...

@Ismael-VC
Copy link
Author

@ainsophical based on the Python and Ruby versions, I've come up with this:

__precompile__()    # Allow for whole module precompilation.

module AndroidConnection

# It seems only this external package is needed (and it's dependeny `Compat.jl`).
using JSON: json

export Android

# Types don't own function methods.
type Android
    client::TCPSocket
    id::Int
end

# Type constructor
function Android()
    handshake = ENV["AP_HANDSHAKE"]
    _authenticate(handshake)    # WTF!?
    host = ENV["AP_HOST"]
    port = parse(Int, ENV["AP_PORT"])    
    client = connect(host, port)
    id = 0
    new(client, id)
end

function _rpc(a::Android, method::Symbol, args...)
    a.id += 1
    request = json(Dict("id" => a.id, "method" => method, "params" => args))
    println(a.client, request)
    flush(a.client)
    response = readline(a.client)
    JSON.parse(response)    # Last value is returned.
end

# In Julia `Base.getfield` (`foo.bar`) can't be extended "yet", instead
# `Base.getindex` (`foo[bar]`) is used.
Base.getindex(a::Android, method::Symbol) = (args...) -> _rpc(a, method, args...)

end    # End module.

Still I can't understand how does self._authenticate works, @kuri65536 could you give me a hint on this?

From all the versions found here, for several languages, none of them seems to be defininf their own _authenticate method, I don't understand:

@Ismael-VC
Copy link
Author

This is the Ruby version:

AP_PORT = ENV['AP_PORT']
AP_HOST = ENV['AP_HOST']
AP_HANDSHAKE = ENV['AP_HANDSHAKE']

require 'json/pure'
require 'socket'


def trap(*ignore)
  # Trap does not work on Android. 
end


class Android

  def initialize()
    @client = TCPSocket.new(AP_HOST, AP_PORT)
    @id = 0
    _authenticate(AP_HANDSHAKE)    # WTF!?
  end

  def rpc(method, *args)
    @id += 1
    request = {'id' => @id, 'method' => method, 'params' => args}.to_json()
    @client.puts request
    response = @client.gets()
    return JSON.parse(response)
  end

  def method_missing(method, *args)
    rpc(method, *args)
  end

end

@Ismael-VC
Copy link
Author

I would expect this to fail like this:

julia> ENV["AP_HOST"] = "google.com"
"google.com"

julia> ENV["AP_PORT"] = 80
80

julia> ENV["AP_HANDSHAKE"] = :whatever
:whatever

julia> using AndroidConnection

julia> Android()
ERROR: UndefVarError: _authenticate not defined
 in call at /home/ismaelvc/Android.jl:19

@Ismael-VC
Copy link
Author

I'm also unsure as to when to increment id since Python does it after the response line, but Ruby does it before the request line! 😟

  def _rpc(self, method, *args):
    data = {'id': self.id,
            'method': method,
            'params': args}
    request = json.dumps(data)
    self.client.write(request+'\n')
    self.client.flush()
    response = self.client.readline()
    self.id += 1    # WTF!?
    result = json.loads(response)
    if result['error'] is not None:
      print result['error']
    # namedtuple doesn't work with unicode keys.
    return Result(id=result['id'], result=result['result'],
                  error=result['error'], )
  def rpc(method, *args)
    @id += 1    # WTF!?
    request = {'id' => @id, 'method' => method, 'params' => args}.to_json()
    @client.puts request
    response = @client.gets()
    return JSON.parse(response)
  end

@michaelrinderle
Copy link

i dont have it figured out yet either but one thing you should know that there is a language class in SL4A that can be extended for each new language. Each one has to have something that looks for the import statement and the initialization of the droid object like so... it might be overriding the error as sl4a controls the interpreter as well and handles the statement itself. ive only been working on this project for a couple months now and its a bit to get a good handle on the internals so pardon not having a definite answer yet.

public class PythonLanguage extends Language {
    public PythonLanguage() {
    }

    protected String getImportStatement() {
        return "import android\n";
    }

    protected String getRpcReceiverDeclaration(String rpcReceiver) {
        return rpcReceiver + " = android.Android()\n";
    }

ill report back when i find out where this leads to get to that snippet i showed you before as it was the only line in the code that was _authenticate

as far as the increment, i dont think it matters much whether it starts at zero or one / before or after the request. it was probably just the preference of the person that coded the specific language module.. the rpc receiver loop doesnt look like it cares just so long as it has an id to pass as a param with each call to it..

@Ismael-VC
Copy link
Author

This file has tests for the handshake:

This lines are also relevant, inside JsonRpcServer.java:

@Ismael-VC
Copy link
Author

Ok, I think I got this:

  • SL4A.jl:
__precompile__()
module SL4A
import JSON
export Android

const HOST = ENV["AP_HOST"]
const PORT = parse(Int, ENV["AP_PORT"])
const HANDSHAKE = ENV["AP_HANDSHAKE"]

typealias String AbstractString

type Android
    client::TCPSocket
    id::Int
    function Android()
        droid = new(connect(HOST, PORT), 0)
        droid(:_authenticate, HANDSHAKE)
        return droid
    end
end

function Base.remotecall_fetch(droid::Android, method::String, params...)
    request = JSON.json(Dict("id" => droid.id, "method" => method, "params" => params))
    println(droid.client, request)
    flush(droid.client)
    response = readline(droid.client) 
    droid.id += 1
    JSON.parse(response)
end

Base.call(droid::Android, method::String, args...) = remotecall_fetch(droid, method, params...)
Base.call(droid::Android, method::Symbol, args...) = call(droid, string(method), params...)

end
  • JuliaLanguage.java:
package com.googlecode.android_scripting.language;

public class JuliaLanguage extends Language {
    @Override
    protected String getImportStatement() {
        return "using SL4A\n";
    }

    @Override
    protected String getRpcReceiverDeclaration(String rpcReceiver) {
        return rpcReceiver + " = Android()\n";
    }

    @Override
    protected String getNull() {
        return "nothing";
    }

    @Override
    protected String getApplyOperatorText() {
        return "(";
    }

    @Override
    protected String getLeftParametersText() {
        return ", ";
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants