using llvm RecursiveASTVisitor for Objective C and iOS - ios

I would like to use clang to preprocess objective C files from an iOS app. I looked over the source code and am trying to implement a pre-processor based on the RecursiveASTVisitor class. However, I seem to be running into many issues that I cannot resolve. I developed a preprocessor to add a "Enter" call at the beginning of each method and an "Exit" call at the end. I also added an "Exit" call before each return statement. I am using the following code to do the instrumentation:
class ExampleVisitor : public RecursiveASTVisitor<ExampleVisitor> {
private:
ASTContext *astContext; // used for getting additional AST info
std::string funcName;
public:
explicit ExampleVisitor(CompilerInstance *CI)
: astContext(&(CI->getASTContext())) // initialize private members
{
rewriter.setSourceMgr(astContext->getSourceManager(), astContext->getLangOpts());
}
virtual bool VisitObjCMethodDecl(ObjCMethodDecl *ND) {
funcName = ND->getDeclName().getAsString();
errs() << "Testing function: " << funcName << "\n";
if (ND->hasBody()) {
rewriter.InsertText(ND->getBody()->getSourceRange().getBegin().getLocWithOffset(1), "\nEnter(\""+funcName+"\");\n");
rewriter.InsertText(ND->getBody()->getSourceRange().getEnd(),"Exit(\""+funcName+"\");\n");
}
return true;
}
virtual bool VisitReturnStmt(ReturnStmt *ret) {
rewriter.InsertText(ret->getSourceRange().getBegin(), "\nExit(\""+funcName+"\");\n");
errs() << "** Rewrote ReturnStmt\n";
return true;
}
virtual ~ExampleVisitor() {}
};
class ExampleASTConsumer : public ASTConsumer {
private:
ExampleVisitor *visitor; // doesn't have to be private
public:
// override the constructor in order to pass CI
explicit ExampleASTConsumer(CompilerInstance *CI)
: visitor(new ExampleVisitor(CI)) // initialize the visitor
{ }
// override this to call our ExampleVisitor on the entire source file
virtual void HandleTranslationUnit(ASTContext &Context) {
/* we can use ASTContext to get the TranslationUnitDecl, which is
a single Decl that collectively represents the entire source file */
visitor->TraverseDecl(Context.getTranslationUnitDecl());
}
};
The code compiles. I created a command line executable "instrument". I then used the following command to run this on a simple Objective C program generated by Xcode:
instrument AppDelegate.m --
I run into two problems. First, I get the error: 'UIKit/UIKit.h' file not found. This is one of the includes generated by Xcode. Second, I'm seeing only some of the return statements being processed in the file. Can someone give me some insights into what is happening?
I'm using the 3.7.0 version of llvm.

Related

Why is it an error in Dart to try to set a static variable in the global namespace?

Why is it an error in Dart to try to set a class's static variable in the global space?
Example:
class Name {
static String? firstName;
}
Name.firstName = 'Mike'; // Error
void main() {
Name.firstName = 'Mike'; // Ok
}
It's not a big deal. I just came across this and then couldn't find an explanation for why it is. Where in the documentation does it describe the nuance here?
[UPDATE]
The actual error thrown is, among others: "Variables must be declared using the keywords 'const', 'final', 'var', or a type name."
You actually can execute statements outside of a function, but they have to be statements that declare scoped variables. Maybe these aren't technically statements, but just variable instantiations.
class Name {
static String? staticName;
String? lastName;
}
final me = Name(); // Ok
me.lastName = 'Jones'; // Error
void main() {
Name.staticName = 'Mike'; // Ok
final you = Name(); // Ok
you.lastName = 'Smith'; // Ok
}
Without the variable scoping, the compiler thinks I must be defining a function and it gets confused when there is no parameter list or function body.
It makes sense that statements are restricted to variable instantiations of function definitions only, so that there won't be side effects related to execution order to other importers of the file, as per #jamesdlin answer.
Name.firstName = 'Mike'; is a statement. You can't execute arbitrary statements in the global namespace. In what order would they execute? Suppose you had:
name.dart:
class Name {
static String? firstName;
}
and mike.dart:
import 'name.dart';
Name.firstName = 'Mike';
and spike.dart:
import 'name.dart';
Name.firstName = 'Spike';
and finally:
import 'name.dart';
import 'mike.dart';
import 'spike.dart';
void main() {
print(Name.firstName);
}
What should happen? Should it be illegal for multiple libraries to assign to Name.firstName? Should the last one imported win? If so, then suddenly importing a library would have side-effects, and order would matter. What would happen if an imported library imports other libraries with side-effects?
It's a huge headache that is completely unnecessary since you could have just done:
class Name {
static String? firstName = 'Mike';
}
in the first place.

Dart: how to run static function/code "automatically"?

In Dart (Flutter) I would like to have some static code run without being explicitly invoked.
I tried this:
// File 1
class MyClass {
static int member = 42;
}
int dummy = 42;
and file 2:
// File 2
void main() {
int tmp = MyClass.member;
}
I put a breakpoint on the dummy = 2; line but it seemed to never be invoked.
I also tried:
// File 1
class MyClass {
static int member1 = 42;
static int member2 = SomeOtherClass.someFunc();
}
and file 2:
// File 2
void main() {
int tmp1 = MyClass.member1;
int tmp2 = MyClass.member2;
}
With this, SomeOtherClass.someFunc() was invoked when the int tmp2 = ... line was invoked.
I would like SomeOtherClass.someFunc() to be invoked without explicitly accessing MyClass.member2. I would like it invoked on any of the following triggers:
When the program starts (before main() is called).
OR, when code in a file in which MyClass is imported is invoked for the first time.
Is either of these possible in Dart?
This behavior is intentional and cannot be changed. As jamesdlin also explain, all static variables (class and global) in Dart are lazy evaluated and will first get a value with first attempt to access the value.
This is design is described in the Dart specification followed up with a reason for that design choice:
Static variable declarations with an initializing expression are initializedlazily.
The lazy semantics are given because we do not want a language where one tends to define expensive initialization computations, causing long application startup times. This is especially crucial for Dart, which must support the coding of client applications.
https://dart.dev/guides/language/specifications/DartLangSpec-v2.2.pdf

Breaking a library into parts and privacy in Dart

I'm trying to break down a library into parts, and having trouble accessing private elements of the library from a part.
For example, say I have a file named stack.dart with the following content:
library stack;
final _stack = [];
get isEmpty => _stack.isEmpty;
get top => isEmpty ? throw "Empty stack!" : _stack.last;
get pop => isEmpty ? throw 'Empty stack!' : _stack.removeLast();
push(elt){
_stack.add(elt);
return elt;
}
I also have another file with the following contents:
part of stack;
display(){
print(_stack); // can't access _stack from here!
}
Is this to be expected or am I doing something wrong?
Is _stack private to the library or the file?
Your problem is you have forgotten to include you file into the library by using the part keyword:
lib.dart:
library test_lib;
part 'part.dart';
final _private = 'This is private';
part.dart:
part of test_lib;
void test() {
print(_private); // I have access to the _private variable defined in lib.dart
}

Usage of go/parser across packages

I have used go/parser to parse a golang file and examine it's AST. I have a specific problem for which I want to use go/parser but I hit a roadblock.
Consider that the following files are present in GOPATH/src
$GOPATH/src/
example.go
example_package/
example_package.go
The following are the contents of the files above
example.go
package main
import (
"example_package"
)
type MyObject struct {
base *example_package.BaseObject
}
func DoMyThing(arg *example_package.FirstArg) {
arg.Write(10)
}
func DoMyAnotherThing() {
}
func main() {
example_package.GetItStarted(&MyObject{})
}
example_package.go
package example_package
func GetItStarted(obj interface{}) {
}
type FirstArg interface {
Read() int
Write(x int)
}
type BaseObject struct {
}
func (p *BaseObject) DoSomething(arg *FirstArg, a int) {
arg.Write(arg.Read() + a)
}
My intention is to write a go program called gen_structure that is used like this
$ gen_structure example.go
The output would be
> MyObject
- DoMyThing(arg)
- base
- DoSomething(arg, a)
What did gen_structure do?
It parses example.go and
Extracts "MyObject" from the line example_package.GetItStarted(&MyObject{}) from inside the main() function.
Looks for methods on MyObject that have atleast one argument with the first one being of type *package_example.FirstArg. It finds DoMyThing (and ignored DoMyAnotherThing).
Identifies the member base and peeks inside (by opening the example_package).
Applies the same process to find methods as above and finds DoSomething
Using the collected information, it prints the required output.
I understand I can parse a single file or a bunch of files in the same directory using the functionality within go/parser. However, I am unable to figure out how to resolve symbols across packages (In this case, example_package).
How do I do this?
Call ast.NewPackage to resolve a package names. You will need to supply an importer that returns an *ast.Object for the given import path. If all you want to do is resolve the name to a path, the importer can simply return an *ast.Object with the Kind set to ast.Pkg and the Name set to name of the package. Most of the heavy lifting in the importer can be done with the go/build package. If want to resolve do the AST for the target package, you will need to parse the package and return the ast.Object for the package. To prevent loading the same package multiple times, use the map argument to the importer as a cache of previously loaded packages.
Here's some untested code for finding the resolved package path from the *ast.SelectorExpr se:
if x, _ := se.X.(*ast.Ident); x != nil {
if obj := x.Obj; obj != nil && obj.Kind == ast.Pkg {
if spec, _ := obj.Decl.(*ast.ImportSpec); spec != nil {
if path, err := strconv.Unquote(spec.Path.Value); err == nil {
// path is resolved path for selector expression se.
}
}
}
}
The go/types package can also be used to get this information and more. I recommend using go/types instead of using go/ast directly.

What is the correct way to define MQL4 "#import of static class methods"?

What I'm trying to achieve is define classes (using MQL4) in separate files and use the methods from those classes in the main code. Essentially importing static class member functions.
class example{ // ____ in example.mq4
public:
static void myfunction(void) export { .. do something .. }
}
class example{ // ____ in example.mqh
public:
static void myfunction(void);
}
#include <example.mqh> // ____ in main.mq4:
#import "example.ex4"
void example::myfunction(void);
#import
Results in a compile error when using the function as follows:
void OnInit(){
example::myfunction();
}
compiler error:
myfunction: function must have a body
(note example.mq4 is compiled to example.ex4 and can be imported ok)
"new"-MQL4 syntax is evolving
for the indicated purpose,
the class definition syntax shall be enough, upon instantiation of a class, its public methods are possible to be invoked on instance-objects.
Compile-time syntax:
Exporting a function without it's class-inheritance ( taking together the whole class container ) does not fit the OOP concept. This is clearly visible in the OnInit() call, where your code attempts to call a function, which is in fact a class-based object-method at a moment, where there has not yet been instantiated any object, on which the method ought to be performed anObjectINSTANCE.aClassOrInstanceMETHOD().
So, just #include
class example{ // ____ in example.mqh
public:
static void myfunction() { .. do something; }
}
// ---------------------------- // ____ in main.mq4
#property strict // "new"-MQL4
#include <example.mqh> // include
example anObject; // create a globally visible instance
// ----------------------------
void OnInit(){
anObject.myfunction(); // request <anObject> to perform <myfunction> method on self
return( EMPTY ); // "new"-MQL4 insists on return even on void fun()
}

Resources