Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Support for assync (wip)

  • Loading branch information...
commit b46a8e1982226b89f17e2cb5be977361a0f93b62 1 parent 7a8b5f0
Florin authored
6 DOCUMENTATION.md
View
@@ -202,6 +202,12 @@ the native call. It can be used to place guards for argument values/ranges in he
The way the native method is invoked can also be customized, using the @call directive.
+
+Modifiers:
+@noexpose -> do not expose class to javascript
+@manual -> will declare function, but not generate implementation
+@async -> will generate asynchronous function call
+
@type
=====
3  src/beautils.coffee
View
@@ -137,6 +137,9 @@ class Argument
@type = new Type parsed.type, @ns
@value = parsed.value
if cast then @type.cast = expandCast cast, @type
+ #returns full argument declaration, without default argument value (eg. int k = 0)
+ orgNoDefault: ->
+ return @org.replace /\s*\=\s*.+$/, ''
parseDeclaration = (str, namespace) ->
152 src/classconvert.coffee
View
@@ -178,12 +178,25 @@ class ClassConverter
@exposed = false
return false
+ str = node.text
+
if /^@manual\s+/.test node.text
isManual = true
str = node.text.replace /^@manual\s+/, ''
- else
- str = node.text
+ if /^@async\s+/.test node.text
+ isAsync = true
+ str = node.text.replace /^@async\s+/,''
+
+ if /^@asyncWait\s+/.test node.text
+ isAsync = true
+ isAsyncWait = true
+ str = node.text.replace /^@asyncWait\s+/,''
+
+ if /@default\s+/.test str
+ generateDefaultImpl = true
+ str = str.replace /@default\s+/,''
+
#remove end comments
str = str.replace /\s*\/\/.*$/g, ''
@@ -232,6 +245,9 @@ class ClassConverter
fn.org = str
fn.manual = isManual
+ fn.isAsync = isAsync
+ fn.isAsyncWait = isAsyncWait
+ fn.generateDefaultImpl = generateDefaultImpl
fn.requiredArgs = @requiredArgs fn.args
fn.sublines = node.children
fn.node = node
@@ -239,6 +255,11 @@ class ClassConverter
if fn.virtual
@virtualCount++
+
+ if fn.isAsync and not fn.virtual
+ @warn "Async works with virtual functions only! Ignoring @async declaration.", node
+ fn.isAsync = false
+
#check sublines for @call directive
callNode = _.detect fn.sublines, (subline) -> /^\@call/.test subline.text
@@ -391,16 +412,19 @@ class ClassConverter
#native-called virtual functions go into the .cpp file
#this is because we may return specializations of Convert<> from these functions
#and this must not happen before the explicit specializations of Convert<> generated by TypeManager.
-
+ #implBlock -> implementation block which goes into the cpp file
implBlock = new CodeBlock.CodeBlock
+ #publicv -> functions which are called from Javascript
publicv = publicBlock.add new CodeBlock.CodeBlock "", false
publicv.add "//JS: These virtual functions will only be called from Javascript"
+ #publicd -> functions which are called from native code
publicd = publicBlock.add new CodeBlock.CodeBlock "", false
publicd.add "//Native: These virtual functions will only be called from native code"
+ #go through each virtual function in the class
_.each vfuncs, (vfunc) =>
- dargs = _.map vfunc.args, (arg) -> arg.org
+ dargs = _.map vfunc.args, (arg) -> arg.orgNoDefault()
cargs = _.map vfunc.args, (arg) -> arg.name
vfunc.callAs = "_d_" + vfunc.name
@@ -419,38 +443,117 @@ class ClassConverter
vfuncdecla = "#{vfunc.type.org} #{vfunc.name}(#{dargs.join ', '})"
publicd.add vfuncdecla + ';'
+
+ if vfunc.isAsync
+ ###
+ A function which is called by native code from a different thread.
+ Since javascript is single-threaded we must execute the call from the javascript thread. This is accomplished
+ by using uv_queue_work function (from libev). uv_queue_work takes a callback as parameter and calls our callback from the main
+ thread. Then we can actually make the call into javascript.
+
+ How it is implemented:
+ Suppose we have the following function in the C++ interface:
+ class TransportI{
+ virtual void SendPacket(unsigned char* packet, int numberOfBytes, int channel, int speechValue, int packetLoss) = 0;
+ };
+ and the following bea declaration:
+ @async virtual void SendPacket(unsigned char* packet, int numberOfBytes, int channel, int speechValue, int packetLoss) = 0;
+
+ The implementation goes like this:
+ struct SendPacketAsync : public bea::Async{
+ unsigned char* packet;
+ int numberOfBytes;
+ int channel;
+ int speechValue;
+ int packetLoss;
+
+ void saveArguments(unsigned char* packet, int numberOfBytes, int channel, int speechValue, int packetLoss);//no implementation
+ void callJS(); //no implementation
+ ~SendPacketAsync(); //no implementation
+ }
+
+ in the cpp file:
+ void _d_TransportI::SendPacket(...){
+ SendAsyncPacket* as = new SendAsyncPacket(...);
+ uv_queue_work(uv_default_loop(), as, bea::Async::empty, bea::Async::complete);
+ }
+
+ Implementation of the struct constructor, callJs() and destructor is left to the user.
+ This is because it is impossible (hard) to guess how the data should be copied automatically.
+ ####
+
+ asyncStructName = vfunc.name + 'Async'
+ asyncStruct = new CodeBlock.ClassBlock "struct #{asyncStructName} : public bea::Async"
+ #constructor
+
+ saveArgsFnDeclaration = 'saveArguments(' + _.map(vfunc.args, (arg) -> arg.type.org + ' ' + arg.name).join(', ') + ')'
+ asyncStruct.add vfunc.type.org + ' ' + saveArgsFnDeclaration + '; //must be implemented manually'
+ #destructor
+ asyncStruct.add '~' + asyncStructName + '(); //must be implemented manually'
+ asyncStruct.add 'void callJS(); //must be implemented manually'
+
+
+ #_d_TransportI* _this;
+ asyncStruct.add @nativeClassName + '* _this;'
+ asyncStruct.add asyncStructName + "(#{@nativeClassName}* that): _this(that){}"
+ #add all function arguments as struct members
+ _.each vfunc.args, (arg) => asyncStruct.add arg.type.org + ' m_' + arg.name + ';'
+
+ classBlock.add asyncStruct
+
fn = implBlock.add new CodeBlock.FunctionBlock "#{vfunc.type.org} #{@nativeClassName}::#{vfunc.name}(#{dargs.join ', '})"
- fn.add "v8::Locker v8locker;"
- #fn.add "v8::Context::Scope v8ctxScope(bea::Global::context);"
- fn.add "v8::HandleScope v8scope; v8::Handle<v8::Value> v8retVal;"
- cif = fn.add new CodeBlock.CodeBlock "if (bea_derived_hasOverride(\"#{vfunc.name}\"))"
- arglist = _.map vfunc.args, (arg) =>
- snippets.ToJS(@nativeType(arg.type), @castArgument(arg), '')
+ if not vfunc.isAsync
+ #fn.add "v8::Locker v8locker;"
+ #fn.add "v8::Context::Scope v8ctxScope(bea::Global::context);"
+ fn.add "v8::HandleScope v8scope; v8::Handle<v8::Value> v8retVal;"
+ cif = fn.add new CodeBlock.CodeBlock "if (bea_derived_hasOverride(\"#{vfunc.name}\"))"
+ cif.add @v8ArgsFromArgs(vfunc)
+ cif.add "v8retVal = bea_derived_callJS(\"#{vfunc.name}\", #{vfunc.args.length}, v8args);"
- if vfunc.args.length > 0
- cif.add "v8::Handle<v8::Value> v8args[#{vfunc.args.length}] = {#{arglist.join(', ')}};"
+ fn.add "if (v8retVal.IsEmpty()) #{ret} _d_#{vfunc.name}(#{cargs.join ', '});"
+
+ if vfunc.type.rawType != 'void'
+ nativeType = @nativeType(vfunc.type)
+ #shit, this is messy, but no time now
+ if nativeType.indexOf('*') != -1 && !vfunc.type.isPointer then castBack = '*' else castBack = ''
+ fn.add "return #{castBack}" + snippets.FromJS(nativeType, "v8retVal", 0)
else
- cif.add "v8::Handle<v8::Value> v8args[1];"
-
- cif.add "v8retVal = bea_derived_callJS(\"#{vfunc.name}\", #{vfunc.args.length}, v8args);"
-
- fn.add "if (v8retVal.IsEmpty()) #{ret} _d_#{vfunc.name}(#{cargs.join ', '});"
-
- if vfunc.type.rawType != 'void'
- nativeType = @nativeType(vfunc.type)
- #shit, this is messy, but no time now
- if nativeType.indexOf('*') != -1 && !vfunc.type.isPointer then castBack = '*' else castBack = ''
- fn.add "return #{castBack}" + snippets.FromJS(nativeType, "v8retVal", 0)
+ fn.add asyncStructName + '* as = new ' + asyncStructName + '(this);'
+ fn.add 'as->saveArguments(' + _.map(vfunc.args, (arg) -> arg.name).join(', ') + ');'
+ fn.add 'uv_queue_work(uv_default_loop(), as, bea::Async::empty, bea::Async::complete);'
+
+ if vfunc.generateDefaultImpl
+ saveArgsFn = new CodeBlock.FunctionBlock "void #{@nativeClassName}::#{asyncStructName}::#{saveArgsFnDeclaration}"
+ _.each vfunc.args, (arg) -> saveArgsFn.add 'm_' + arg.name + ' = ' + arg.name + ';'
+
+ destructorFn = new CodeBlock.FunctionBlock "#{@nativeClassName}::#{asyncStructName}::~#{asyncStructName}()"
+ implBlock.add saveArgsFn
+ implBlock.add destructorFn
+ callJSFn = new CodeBlock.FunctionBlock "void #{@nativeClassName}::#{asyncStructName}::callJS()"
+ callJSFn.add @v8ArgsFromArgs(vfunc, 'm_')
+ ifBlock = new CodeBlock.CodeBlock 'if (!_this->bea_derived_tryCall("' + vfunc.name + '", ' + vfunc.args.length + ', v8args))'
+ ifBlock.add "_this->#{vfunc.name}(" + _.map(vfunc.args, (arg) -> 'm_' + arg.name).join(', ') + ');'
+ callJSFn.add ifBlock
+ implBlock.add callJSFn
return {
decla: classBlock
impl: implBlock
}
-
+
+ v8ArgsFromArgs: (vfunc, argPrefix = '') ->
+ arglist = _.map vfunc.args, (arg) =>
+ snippets.ToJS(@nativeType(arg.type), argPrefix + @castArgument(arg), '')
+
+ if vfunc.args.length > 0
+ return "v8::Handle<v8::Value> v8args[#{vfunc.args.length}] = {#{arglist.join(', ')}};"
+ else
+ return "v8::Handle<v8::Value> v8args[1];"
+
#Create the InitJSObject function, to be added to the CPP file.
#This function will be called by the exposing function
@@ -528,7 +631,6 @@ class ClassConverter
nativeType = type.fullType()
if @typeManager.isWrapped(type) then return nativeType + '*'
-
nativeType
#returns the proper cast for an argument name
9 src/mgr.coffee
View
@@ -40,10 +40,19 @@ class TypeManager
#Check if a type is native or is in the list of declared types
knownType: (type) ->
type.isNative() || _.any @types, (wt) -> wt.rawType == type.rawType && wt.namespace == type.namespace
+
+ #Check if a type has been declared, ignoring namespaces
+ declaredType: (type) ->
+ type.isNative() || _.any @types, (wt) -> wt.rawType == type.rawType
+
#Attempts to see if the value looks like a known (user-defined) type constructor
+ #returns false if it's not a type constructor
+ #returns true type if not
typeFromValue: (value) ->
thatType = false
+ #eg void fn(Mat& arg = Mat()) --> Mat() is a type constructor
+
mret = value.match(/(\w+)\s*\(.*\)/)
if mret?.length > 1
probableType = mret[1]
2  src/typetest.coffee
View
@@ -3,7 +3,7 @@ _ = require 'underscore'
fs = require 'fs'
beautils = require('./beautils').u
utest = require './utest'
-debugIt = require('./debugIt').debugIt
+
inspectType = (typeStr, ns) ->
console.log 'TypeStr: ' + typeStr + '; ns = ' + ns
Please sign in to comment.
Something went wrong with that request. Please try again.