incubator-flex-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Frank Wienberg <fr...@jangaroo.net>
Subject Re: [FalconJx] New JavaScript runtime format prototype
Date Thu, 20 Dec 2012 22:23:49 GMT
On Thu, Dec 20, 2012 at 10:54 PM, Erik de Bruin <erik@ixsoftware.nl> wrote:

> Excellent points! I see that we are closer in our goals and methods,
> but there is still 'some' misunderstanding about the tools we are
> proposing. I'll address your points and add my opinion/understanding
> where I think some clarification is needed.
>

Great, I agree we are "converging" and it is now just a matter of choosing
the "right" tools...


> - *waste:* In one short scentence: the Closure Builder part of the
> Tools takes care of this. Any (really!) code that is not actually used
> is NOT included in the output. So, if we use 'goog.base()' from
> 'base.js', ONLY the code needed to execute that method in the context
> we're using it in is included. All the rest is discarded. I'm not sure
> about the optimiser(s) you suggest offer the same functionality?
>

I think I understand the way the Closure Builder works. I was talking about
the non-optimized code, which I guess we would use for debugging.
When debugging an application, performance is not such an issue, but
runtime complexity that you see during debugging is!
So for example goog.base() -- during debugging -- executes a whole bunch of
code to find and call the super function, which you would have to step
through or which you would see in stack traces.
My approach prevents this overhead right from the beginning, so there is
even no need for such optimization!
This the overall impression I have with the GCC approach: it introduced
complexity only to optimize it later on.
I think we better avoid complexity in the first place!


>
> - *JavaScript readiness:* there's nothing to compare here, I'm afraid.
> I'm still fleshing out (in the Wiki) what language features are and
> aren't easily translated. I haven't started filling the gaps yet, and
> I love to cooperate with you on that. What you call "goog" and what I
> call "goog" are apparently different things. I don't advocate using
> the Closure Library (which I think is what you refer to as "goog")
> exclusively. What I refer to as the "goog" approach is to use the
> Closure Tools (annotation, compiler and builder) to our advantage. As
> it plays well with those tools, I say let's use what the Closure
> Library has to offer and we miss (so far: goog.provide, goog.require,
> goog.inherits and goog.base).


I don't miss those.
For goog.provide and goog.require, RequireJS provides alternatives that
better fit our needs: asynchronous module loading much better reflects the
nature of script loading in the browser. Synchronous require calls were
made for Node.js or other environments that allow to load scripts
synchronously. RequireJS concentrates exactly on that task and does nothing
else.
goog.inherits and goog.base are IMHO not needed at all. In JS code
generated from ActionScript, what goog.base does can be expressed by a
direct function call (see my prototype). goog.inherits is replaced by my
defineClass() utility, which consists of just 60 lines of code and supports
many more AS3 language features than just inheritance. I also had an
alternative "VanillaJS" version that did not even use a helper method for
inheritance (since it is so easy using ES5 API), but adding the other
language feature, I found that this introduced too much repetitive, boiler
plate code.

One thing I'd like to know: can you use the Closure Tools *completely
without* using the goog library in your JS code and still achieve
ADVANCED_OPTIMIZATIONS? If not, I think that is exactly where the
vendor-lock-in comes into play.


> But I'm all for, as I often comment on
> in the Wiki, creating a small utility class or whatever, if and where
> needed. And again: see my point in *waste:* with regard to the 1500
> lines of code in "base.js". Your point there is missing the point, as
> it where :-)
>

See above. When talking about 1500 lines of code, I don't worry about code
size, but about complexity that shines through during debugging.


>
> *tool/vendor dependency:* The Google Closure Tools are Open Source,
> available under the Apache License 2.0.
>

I know that. Still, it is Google's own interpretation of modules,
inheritance, ... for which there are at least partially standards in
JavaScript (ECMAScript 5, AMD).


>
> *separation of concerns:*
> 1) a matter of choice and convenience, I'm not married to "goog" here,
> but I haven't seen a good reason NOT to use it, so far.
>

See above: too complex, too monolithic, tailored to another use case.


> 2) you are probably correct from a purely language translation point
> of view. But the "ultimate" goal here is not only a language
> translation, but also a framework translation. And I'm sure "some
> polyfills" are not enough to create a cross browser UI framework.
>

To make the UI framework run cross-browser, I have no objections against
using an existing JavaScript framework. But this is my point when talking
about "separations of concerns". I wouldn't hard-wire the pure language
translation to "goog" just because it is a good base for a cross browser UI
framework. And any UI framework should be compatible with VanillaJS!


> 3) I'm hearing "different", I'm not sure I hear "better". Why wouldn't
> we use the Closure Builder for this? I explain the virtue of this tool
> in an earlier email and you can see it in action in the Publisher tool
> I set up in the 'develop' branch of the "asjs" section of the SVN
> repo.
>

What makes a difference to me is that to link with RequireJS, you do not
have use a RequireJS-specific syntax, but an AMD-specific one. They
separate syntax and implementation, which GCC apparently doesn't.


> 4) the Closure Builder does a bit more than linking. Please see my
> previous remark and email, as well as the documentation available
> online.
>

Yes, that's my point. I argue that it does *too much* in one tool, so we
don't have fine-grained control over the code generation process.


> 5) GCC is also more than just an minifier. And we can run it straight
> from the cross compiler, as Mike suggested. That leaves us with the
> option of keeping the intermediate JS code as an supplement output.
>

Yes, but the code is still generated (even if only in-memory) and parsed,
which is an unnecessary overhead in any case.


>
> *testing:* never implied that GCC or anything would free us from
> testing any part of the code. And creating optimised/minified code
> straight from the cross compiler - without using an external tool as
> you seem to imply - would mean that we'd have to write our own
> optimiser/minifier into it. I'm not familiar with creating such a
> tool, but that sounds like a lot of work.
>
> *source maps:* I don't know what you mean by this, so I can't argue
> either direction :-)
>

I'm talking about a new feature available so far in Chrome only to let the
browser debugger map the JavaScript code to the original source code, see
for example
http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/
If this feature was present in all major browsers, there would no longer be
a need for a debuggable JavaScript code format if we generated source maps.
Firefox has published an implementation plan, but it may take a while. IE
10 will probably follow, now that Microsoft pushes TypeScript.



>
> Like I emphasised before, and I'll repeat it every time it comes up:
> there is, at least not at this point in the development, an absolutely
> right nor absolutely wrong approach. I think we should look for the
> best of both approaches. Our approaches are essentially similar (only
> the tools differ) and I think both have strengths and weaknesses.
> Let's work on this a bit and than revisit this discussion and decide
> on the merits which one fullfils which requirements best.
>

Fully agreed!


>
> Have a great holiday, and keep having fun!
>

Ditto! :-)

-Frank-


>
> EdB
>
>
> On Thu, Dec 20, 2012 at 9:59 PM, Frank Wienberg <frank@jangaroo.net>
> wrote:
> > Hi Erik,
> >
> > my proposal is not at all about UI frameworks, so I'll try to answer the
> > part about (not) using "goog" / Google Closure Compiler.
> >
> > I agree on the strengths of GCC as being "not only a JS optimiser, it's
> an
> > actual compiler" when you want to write class-oriented JavaScript code
> > manually and maybe use other parts of the "goog" library, and then
> optimize
> > your code for deployment.
> > But I see its strengths turned to weaknesses in our setting. Let me
> explain
> > why.
> >
> >    - *waste:* no need to parse and reverse-engineer JavaScript when we
> >    already have a detailed class-oriented model of what we want to
> generate.
> >    Optimizing JavaScript code directly from the ActionScript source
> gives us
> >    much more knowledge about context (thus we don't need any proprietary
> >    annotations!) and thus at least the same potential to optimize, and
> even
> >    faster because we save the time to generate and parse the intermediate
> >    format.
> >    - *JavaScript readiness:* I think my prototype shows how easy it is to
> >    simulate ActionScript language features in modern JavaScript. The
> central
> >    utility function that implements a small domain-specific language to
> >    declare a class (defineClass.js) has about 60 lines, and it
> implements more
> >    AS3 language features than your "goog"-approach currently does
> (interfaces
> >    with is/as, lazy class initialization for static code, real private
> >    members). A few polyfills are only required to support IE < 9. Goog's
> >    base.js file alone has over 1500 lines, and even if you leave out
> comments,
> >    it is still factor 10 larger. The minified complete application is
> only 5k.
> >    Isn't that small enough?
> >    - *tool/vendor dependency:* we add a dependency on a complex tool that
> >    will also have bugs which we cannot fix. I do not replace "goog" / GCC
> >    by "multiple libraries", but only use one other tool, namely
> RequireJS, as
> >    an AMD implementation that can invoke an optimizer (comes with Uglify
> 2,
> >    but can also use GCC or any other JS optimizer you want) after
> linking.
> >    Using AMD, we rely on an open standard and not something Google
> invented
> >    and maintains.
> >    - *separation of concerns:* "goog"/GCC does at least five things for
> >    FalconJS: 1. simulate classes / inheritance / super calls, 2. solve
> >    cross-browser issues, 3. manage dependencies (require / provide), 4.
> link
> >    all code needed for an application and 5. minimize/optimize JavaScript
> >    code. Instead of relying on one tool "to rule them all", I chose a
> "best of
> >    breed" approach, allowing a "make or buy", and if "buy", choose the
> best
> >    tool, *for each concern*. Currently, my answers are 1. too easy in
> >    generated ECMAScript 5 to bother, 2. use ES5 polyfills for IE < 9, 3.
> use
> >    AMD module layout (implemented by RequireJS), 4. use the RequireJS
> >    "optimizer" tool (rather a "linker"), 5. use any JavaScript minifier
> you
> >    like. The last answer will probably later change to 5. optimize JS
> code
> >    directly during generation (quick win examples: shortening variable
> names,
> >    simple function inlining).
> >    - *testing:* using GCC does not free us from testing optimized code,
> so
> >    this is no argument against generating optimized code directly in the
> >    cross-compiler. But testing both versions is no big deal, as once set
> up,
> >    we expect the exact same results/behavior when running the production
> code
> >    as when running the debug code, so we can run the same integration
> tests
> >    twice.
> >    - *source maps:* letting GCC generate a source map does not help, as
> it
> >    can only produce a source map from production JavaScript code back to
> the
> >    "human readable" JS code we fed in. In contrast, we want a direct (!)
> >    mapping from production JavaScript code back to ActionScript source
> code.
> >    While I wouldn't recommend using the "goog" library, I *do* recommend
> >    GCC as a JavaScript optimizer and as a Java library to generate
> source maps
> >    (I already used it successfully in the Jangaroo compiler to generate
> >    generated-JS->AS3 source maps!).
> >
> > I think we can only really compare runtime performance and generated code
> > size of the two approaches when FalconJx has made some progress and we
> can
> > cross-compile a larger code base for testing.
> > Independent from concrete performance results, my feeling is that in the
> > long term, we are better off relying only on AMD for dependencies /
> linking
> > and on polyfills to make old IE as ECMAScript-5-compatible as possible.
> > Like in Mikes argumentation about the JBurg / BURM approach, I think that
> > learning the goog API, GCC's annotations and how to generate JS code so
> > that GCC can really optimize it and chaining our compiler with GCC
> > introduces unnecessary complexity. Please have a look at my JS prototype
> > and convince yourself how simple it is and that such magic is not
> > necessary! The few things you have to know / learn to understand that
> > format are JavaScript with some ECMAScript 5 extensions and AMD in its
> > purest form (something.js: define([dep1, dep2], function(dep1, dep2) {
> code
> > using dep1, dep2; return something; })). If we could agree to drop IE < 9
> > support, that would even make all the shim.js and polyfill stuff
> > (Object.js, Array...js, Function.bind.js) go away. The few files to
> > simulate is, as, type casts, method binding and trace would also be
> needed
> > when using "goog".
> > This is also a call to Mike and everybody else who thinks that digging
> into
> > the prototype would take 2 weeks or more. There's really only a few lines
> > of code, and some even have comments! ;-) And everything's there to test
> > and debug live in the browser, without even downloading, installing or
> > anything...
> > Here are the links again: live
> > demo<http://fwienber.github.com/as-js-runtime-prototype/> (if
> > you had a look at it before, please clean cache for latest version),
> > source<https://github.com/fwienber/as-js-runtime-prototype>,
> > download <https://github.com/fwienber/as-js-runtime-prototype/tags>
> >
> > Greetings
> > -Frank-
> >
> >
> > On Thu, Dec 20, 2012 at 8:22 PM, Erik de Bruin <erik@ixsoftware.nl>
> wrote:
> >
> >> Hi,
> >>
> >> To complete the party, a few brief comments:
> >>
> >> - at the moment, I'm not sure what approach to the eventual UI
> >> frameworks (AS and JS side) will take. FlexJS is one but it doesn't
> >> (yet) have the feel of something that meets the standards that Flex
> >> users have come to expect.
> >>
> >> - I'm sure that Google's Closure Tools provide us with a lot of the
> >> language features that native JS is missing. The ones it doesn't
> >> (easily) provide, we can have the compiler "replace" as that's what
> >> compilers do, I understand ;-) After we've finished the unit tests for
> >> FalconJx, we can focus on "goog" compatible JS output and create an -
> >> I expect very small - utility class to make up for the difference.
> >>
> >> - As we are using "goog" and Closure annotations to match AS language
> >> features, using the rest of Google's Closure Tools is really low
> >> hanging fruit. The Closure Compiler is not only a JS optimiser, it's
> >> an actual compiler which provides much of the features usually only,
> >> if at all, available if you use multiple libraries. The final piece is
> >> the Closure Builder, which takes an entire "intermediate" (i.e. human
> >> readable) JavaScript project and, combining all the tools in the
> >> Closure set, outputs one JS file, culling out dead code and optimising
> >> the remaining code. I've done a lot of studying on the Closure Tools
> >> the last few weeks and have become convinced they will provide us with
> >> a solid language base to start building the UI frameworks on.
> >>
> >> Having said all that, I really welcome Frank's POC and I think that we
> >> will soon be at a point where we can converge all our efforts and
> >> figure out a common way forward, accepting input from all previous
> >> trails.
> >>
> >> Mike, a question: can the FalconJx compiler tell when a 'regular'
> >> public variable is being accessed and when a public property (with
> >> "get" and "set" methods) is being accessed? I'm asking this because if
> >> it can, it would make it a whole lot easier and would require a whole
> >> lot less workarounds to get that part of the language translation
> >> done...
> >>
> >> TL;DR: for now I'm moving ahead with "goog" as the basis for the
> >> language translation, but I look forward to combining the various
> >> efforts and starting the build of a suitable replacement for the Flex
> >> SDK :-)
> >>
> >> EdB
> >>
> >>
> >>
> >> On Thu, Dec 20, 2012 at 7:23 PM, Michael Schmalle
> >> <apache@teotigraphix.com> wrote:
> >> >
> >> > Quoting Frank Wienberg <frank@jangaroo.net>:
> >> >
> >> >> Hi folks,
> >> >>
> >> >> as mentioned in another thread, I have been working on a new
> JavaScript
> >> >> "runtime", which is what I call the way the generated JavaScript code
> >> >> looks
> >> >> and works like. While this thread's title has [FalconJx] in it,
> there is
> >> >> no
> >> >> reason the proposal could not be used for FalconJS, too, but I
> >> understood
> >> >> that for now, using Google Closure is a given.
> >> >
> >> >
> >> > Hi Frank,
> >> >
> >> > Just so you know I read this, I wanted to write a couple things.
> >> >
> >> > FalconJS uses the JBurg BURM which was donated by Adobe. I choose the
> >> route
> >> > of an AST visitor framework that is handcoded for the sake of
> development
> >> > ease and allowing myself to try something with the time I can a lot
> for
> >> this
> >> > development. As far as I see, FalconJS is not being developed by
> anybody
> >> > beyond the fact I read everywhere that it produces "js code". I guess
> if
> >> > FlaconJS is going to be considered an alternative, there has to be
> >> > developers that can work on it, I'm not one of them. I spent 1+ years
> >> > learning antlr grammar and not about to figure out all the other
> >> > prerequisites of the FalconJS code.
> >> >
> >> >
> >> > That being said, I have to be honest that I am in no position to
> either
> >> > agree or argue with what you have produced. Besides the years of
> hands on
> >> > experience you have with JavaScript and ActionScript issues. I would
> say
> >> you
> >> > have a thing or to to add here. :)
> >> >
> >> >
> >> > It seems that right now, there are a handful of developers/community
> >> members
> >> > involved in this topic of AS -> JS. I don't know what will change that
> >> but,
> >> > it seems like we will need to decide on something that works sooner
> than
> >> > later.
> >> >
> >> >
> >> > The truth is, for me to understand what you have done and have an
> >> opinion I
> >> > would probably need 2 or more weeks to understand what you have done.
> :)
> >> >
> >> > I'm probably in the position of, "Show me what you want to generate
> >> based on
> >> > the semantics involved and I will generate it" mode.
> >> >
> >> > For me, my goal as it stands is to use what I have written to generate
> >> fully
> >> > compilable ActionScript source code from Falcon's AST model. Once I
> have
> >> > unit tested this, I can focus on the JavaScript side without thinking
> >> about
> >> > what I have to implement to cover all the AS conversions.
> >> >
> >> > I wonder what Erik has to say... I would be interested since he has
> put
> >> time
> >> > into investigation as well.
> >> >
> >> > So in closing, I really have not developed javascript applications.
> I'm
> >> > interested in this project because having an HTML/JS target for Flex
> >> would
> >> > allow me to do some of my audio experiments with an HTML Flex
> framework
> >> in
> >> > the future for mobile applications.
> >> >
> >> > This allows me the excuse for myself to put as much time into it as I
> >> am. :)
> >> >
> >> > Mike
> >> >
> >> >
> >> >> I had the following design goals:
> >> >>
> >> >>    - Support ActionScript 3's "differentiator" features: private
> >> members,
> >> >>    interfaces + is/as, lazy class initialization, method binding,
> >> >>    getter/setter functions
> >> >>    - work in IE < 9 (except for getter/setter functions)
> >> >>    - Produce debuggable JavaScript code that can also be optimized
> for
> >> >>    deployment. The debuggable code should be very similar to the
> >> original
> >> >>    ActionScript source code for every line containing executable code
> >> >>    - Dependency management: load/bundle only the needed classes.
> Every
> >> >>    compilation unit to debug should load as a separate request
> >> >>    - Minimal to no runtime overhead, even in debug mode
> >> >>    - Support trace()
> >> >>
> >> >>
> >> >> I took the following design decisions:
> >> >>
> >> >>    - For private members, a mix of JavaScript lexical scope and field
> >> >>    renaming like in Jangaroo is used
> >> >>    - To implement an efficient "is" operator, all interfaces a class
> >> >>    implements are determined once for the class. I use a prototype
> chain
> >> >> to
> >> >>    inherit the interfaces implemented by the super class
> >> >>    - Lazy class initialization uses a very efficient technique
> refined
> >> by
> >> >>    Olaf Kummer: Wherever a constructor or non-private static member
> is
> >> >> called,
> >> >>    the "self-destructing" class initialization function is checked
> for
> >> >>    existence and, if still existing, called ("guarded" call)
> >> >>    - Method binding is again borrowed from the Jangaroo Runtime: the
> >> bound
> >> >>    function is "cached" at the object under a different but unique
> name
> >> so
> >> >>    that it keeps its identity. This is important e.g. to be able to
> >> remove
> >> >>    event listeners
> >> >>    - Getter/setter functions map to Object.defineProperty("...", {
> get:
> >> >>    ..., set: ...}), thus no support in IE < 9
> >> >>    - To make everything else work in IE < 9, I use polyfills for
> >> >> ECMAScript
> >> >>    5 Object, Array and Function API
> >> >>    - For keeping generated code in separate JS files, expressing
> >> >>    dependencies between these files, loading them separately at
> runtime
> >> >> and
> >> >>    linking them statically into a single JavaScript file, I chose to
> use
> >> >>    Asynchronous Module Definitions (AMD) and RequireJS (version
> 2.1.2)
> >> as
> >> >> the
> >> >>    concrete implementation of AMD. AMD is preferred over CommonJS
> >> because
> >> >> it
> >> >>    better fits web applications, as e.g. argued here:
> >> >>
> >> >>
> >>
> http://blog.millermedeiros.com/amd-is-better-for-the-web-than-commonjs-modules/To
> >> >> link and optimize the application, RequireJS provides an optimizer
> >> >> tool
> >> >>    simply called "r.js".
> >> >>    - To conditionally load polyfills (for IE < 9), I use the "shim"
> >> >>    RequireJS plugin
> >> >>    - To achieve minimal runtime overhead, I only use helper
> functions to
> >> >>    define the class structure once (defineClass and
> defineInterface). As
> >> >> soon
> >> >>    as the complete application structure is set up, all code
> executed is
> >> >>    inlined
> >> >>    - Since all modern browsers seem to support a "console" object
> with a
> >> >>    "log" method, trace() is simply implemented to check for the
> >> existence
> >> >> of
> >> >>    console.log() by trial-and-error (try... catch). It then
> >> >> String-converts
> >> >>    and concatenates all its arguments like its ActionScript sibling
> >> >>
> >> >>
> >> >> *About the Example*
> >> >>
> >> >> To specify how I think FalconJx-generated JavaScript output should
> look
> >> >> like, I created an ActionScript example, consisting of two classes
> and
> >> >> three interfaces. Every language feature supported by the new runtime
> >> >> format gives an appearance in the code (well, I didn't add a private
> >> >> static
> >> >> method, because it works like a private method, only simpler).
> >> >> The whole thing is a public GitHub project at
> >> >> https://github.com/fwienber/as-js-runtime-prototype.
> >> >> To download all files in one go as a zip or tar.gz, please use the
> tag
> >> >> page: https://github.com/fwienber/as-js-runtime-prototype/tags.
> >> >>
> >> >> A live demo is available on the corresponding GitHub pages at
> >> >> http://fwienber.github.com/as-js-runtime-prototype/index.html
> >> >> From there, you can reach all ActionScript example sources and the
> >> >> proposed
> >> >> JavaScript equivalent (so far, hand-coded :-)).
> >> >> To see the example in action, use the three bottom links which start
> the
> >> >> same application in three flavors:
> >> >>
> >> >>    - debug: Each JS file corresponding to an AS class or interface
> and
> >> the
> >> >>    main bootstrap file is loaded with a single request. This gives
> you a
> >> >> nice
> >> >>    1:1 mapping of source to output in the JavaScript debugger.
> >> >> Additionally,
> >> >>    there are some "infrastructure" files: require.js itself, the
> >> RequireJS
> >> >>    plugin "shim" (shim.js, shims.js), my AS3 runtime helpers
> >> >> defineClass.js
> >> >>    and defineInterface.js, and a couple of built-in AS3 functions
> (bind,
> >> >> as,
> >> >>    is, trace). If loading in IE < 9, the shim plugin loads several
> ES5
> >> >>    polyfills.
> >> >>    - linked: The whole application is merged into one JS file using
> the
> >> >>    RequireJS optimizer "r.js" without optimizations. Thus you only
> have
> >> >> two
> >> >>    requests: require.js and hello-worls-all.js are loaded. In IE <
9,
> >> the
> >> >>    polyfills are still loaded one-by-one (I didn't bother to optimize
> >> this
> >> >>    case, but of course, it can be done).
> >> >>    - minified: Same as "linked", only that require.js and
> >> >>    hellow-world-all.js are minified by Uglify2. This can easily be
> >> >> achieved
> >> >>    using "r.js" with the parameter optimize=uglify2.
> >> >>
> >> >>
> >> >> For the demo, I redirected trace() from the JavaScript console
> >> (backed-up
> >> >> in trace-console.js) to the DOM, so that you can see the log messages
> >> >> directly.
> >> >>
> >> >> Just look into the code and observe how it works in Firebug or any
> other
> >> >> JavaScript debugger of your choice, and let me know if anything is
> >> >> unclear,
> >> >> missing or if you need more reasoning for the design decisions taken.
> >> >> I know there are still several issues to be fleshed out (e.g.
> >> interaction
> >> >> with "native" JavaScript or package-scope variables, just to name
> two),
> >> >> but
> >> >> I think the current state is a solid core of how a modern, efficient,
> >> >> self-contained, extensive and still concise JavaScript runtime format
> >> for
> >> >> Apache Flex could look like.
> >> >> Please tell me what you think!
> >> >>
> >> >> Have fun,
> >> >> -Frank-
> >> >>
> >> >
> >> > --
> >> > Michael Schmalle - Teoti Graphix, LLC
> >> > http://www.teotigraphix.com
> >> > http://blog.teotigraphix.com
> >> >
> >>
> >>
> >>
> >> --
> >> Ix Multimedia Software
> >>
> >> Jan Luykenstraat 27
> >> 3521 VB Utrecht
> >>
> >> T. 06-51952295
> >> I. www.ixsoftware.nl
> >>
>
>
>
> --
> Ix Multimedia Software
>
> Jan Luykenstraat 27
> 3521 VB Utrecht
>
> T. 06-51952295
> I. www.ixsoftware.nl
>

Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message