Skip to content

Commit

Permalink
Refactored lambda runtime class generators. Added creation references.
Browse files Browse the repository at this point in the history
	Change on 2015/07/22 by kirbs <kirbs@google.com>
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=98846613
  • Loading branch information
sjkirby authored and kstanger committed Jul 30, 2015
1 parent 184e7f5 commit d05d92d
Show file tree
Hide file tree
Showing 10 changed files with 339 additions and 86 deletions.
4 changes: 2 additions & 2 deletions jre_emul/Classes/J2ObjC_source.h
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ FOUNDATION_EXPORT void JreRelease(id obj);
// Defined in JreEmulation.m // Defined in JreEmulation.m
FOUNDATION_EXPORT id GetNonCapturingLambda(Class baseClass, Protocol *protocol, FOUNDATION_EXPORT id GetNonCapturingLambda(Class baseClass, Protocol *protocol,
NSString *blockClassName, SEL methodSelector, id block); NSString *blockClassName, SEL methodSelector, id block);
FOUNDATION_EXPORT id GetCapturingLambda(int argumentCount, Class baseClass, Protocol *protocol, FOUNDATION_EXPORT id GetCapturingLambda(Class baseClass, Protocol *protocol,
NSString *blockClassName, SEL methodSelector, id block); NSString *blockClassName, SEL methodSelector, id wrapperBlock, id block);


/*! /*!
* Returns correct result when casting a double to an integral type. In C, a * Returns correct result when casting a double to an integral type. In C, a
Expand Down
103 changes: 34 additions & 69 deletions jre_emul/Classes/JreEmulation.m
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -91,6 +91,32 @@ id JreRetainVolatile(volatile_id *pVar) {
return [value retain]; return [value retain];
} }


// Block flag position for copy dispose, (1 << 25).
#define COPY_DISPOSE_FLAG 0x02000000

// Modified from clang block implementation http://clang.llvm.org/docs/Block-ABI-Apple.html
typedef struct Block_literal_1 {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor_1 {
unsigned long int reserved;
unsigned long int size;
// There will be 2 function pointers at the beginning of the signature if the copy_dispose flag
// is set.
void *signature[1];
} *descriptor;
} Block_literal;

// Returns a type string from a block.
const char *blockTypeSignature(id block) {
Block_literal *blockLiteral = (__bridge void *) block;
// Offset for optional function pointers.
int i = (blockLiteral->flags & COPY_DISPOSE_FLAG) ? 2 : 0;
return (const char *) blockLiteral->descriptor->signature[i];
}

typedef struct { typedef struct {
void *id; void *id;
} LambdaHolder; } LambdaHolder;
Expand Down Expand Up @@ -119,10 +145,9 @@ id GetNonCapturingLambda(Class baseClass, Protocol *protocol, NSString *blockCla
@throw AUTORELEASE([[JavaLangAssertionError alloc] @throw AUTORELEASE([[JavaLangAssertionError alloc]
initWithNSString:@"Unable to add protocol to non-capturing lambda class."]); initWithNSString:@"Unable to add protocol to non-capturing lambda class."]);
} }
Method method = class_getInstanceMethod(baseClass, methodSelector);
const char *types = method_getTypeEncoding(method);
IMP block_implementation = imp_implementationWithBlock(block); IMP block_implementation = imp_implementationWithBlock(block);
if (!class_addMethod(blockClass, methodSelector, block_implementation, types)) { if (!class_addMethod(blockClass, methodSelector, block_implementation,
blockTypeSignature(block))) {
@throw AUTORELEASE([[JavaLangAssertionError alloc] @throw AUTORELEASE([[JavaLangAssertionError alloc]
initWithNSString:@"Unable to add method to non-capturing lambda class."]); initWithNSString:@"Unable to add method to non-capturing lambda class."]);
} }
Expand All @@ -133,82 +158,22 @@ id GetNonCapturingLambda(Class baseClass, Protocol *protocol, NSString *blockCla
return (__bridge id) lambdaHolder->id; return (__bridge id) lambdaHolder->id;
} }


// Having this hardcoded is definitely not ideal, and I would love a dynamic solution to generated
// capturingLambdaBlockCallers that doesn't rely on packing and unpacking arrays for each lambda.
id capturingLambdaBlockCallers[10];
char *capturingLambdaBlockTypes[10];

// Method to handle dynamic creation of class wrappers surrounding blocks from lambdas requiring // Method to handle dynamic creation of class wrappers surrounding blocks from lambdas requiring
// a capture. // a capture.
id GetCapturingLambda(int argumentCount, Class baseClass, Protocol *protocol, id GetCapturingLambda(Class baseClass, Protocol *protocol, NSString *blockClassName,
NSString *blockClassName, SEL methodSelector, id block) { SEL methodSelector, id blockWrapper, id block) {
static dispatch_once_t once;
dispatch_once(&once, ^{
char typeHolder[10];
for(int i = 0; i < 10; i++) {
if (i) {
typeHolder[i - 1] = '@';
}
typeHolder[i] = 0;
// strcpy returns the address of the destination string, so we can use one call for writing
// and assignment.
capturingLambdaBlockTypes[i] = strcpy(malloc(i + 1), typeHolder);
}

capturingLambdaBlockCallers[0] = ^id(id _self){
id (^block)() = objc_getAssociatedObject(_self, (void *) 'b');
return block(_self);
};
capturingLambdaBlockCallers[1] = ^id(id _self, id a){
id (^block)() = objc_getAssociatedObject(_self, (void *) 'b');
return block(_self, a);
};
capturingLambdaBlockCallers[2] = ^id(id _self, id a, id b){
id (^block)() = objc_getAssociatedObject(_self, (void *) 'b');
return block(_self, a, b);
};
capturingLambdaBlockCallers[3] = ^id(id _self, id a, id b, id c){
id (^block)() = objc_getAssociatedObject(_self, (void *) 'b');
return block(_self, a, b, c);
};
capturingLambdaBlockCallers[4] = ^id(id _self, id a, id b, id c, id d){
id (^block)() = objc_getAssociatedObject(_self, (void *) 'b');
return block(_self, a, b, c, d);
};
capturingLambdaBlockCallers[5] = ^id(id _self, id a, id b, id c, id d, id e){
id (^block)() = objc_getAssociatedObject(_self, (void *) 'b');
return block(_self, a, b, c, d, e);
};
capturingLambdaBlockCallers[6] = ^id(id _self, id a, id b, id c, id d, id e, id f){
id (^block)() = objc_getAssociatedObject(_self, (void *) 'b');
return block(_self, a, b, c, d, e, f);
};
capturingLambdaBlockCallers[7] = ^id(id _self, id a, id b, id c, id d, id e, id f, id g){
id (^block)() = objc_getAssociatedObject(_self, (void *) 'b');
return block(_self, a, b, c, d, e, f, g);
};
capturingLambdaBlockCallers[8] = ^id(id _self, id a, id b, id c, id d, id e, id f, id g, id h, id i){
id (^block)() = objc_getAssociatedObject(_self, (void *) 'b');
return block(_self, a, b, c, d, e, f, g, h);
};
capturingLambdaBlockCallers[9] = ^id(id _self, id a, id b, id c, id d, id e, id f, id g, id h, id i){
id (^block)() = objc_getAssociatedObject(_self, (void *) 'b');
return block(_self, a, b, c, d, e, f, g, h, i);
};
});

LambdaHolder *lambdaHolder = FastPointerLookup(&lambdaLookup, (__bridge void*) blockClassName); LambdaHolder *lambdaHolder = FastPointerLookup(&lambdaLookup, (__bridge void*) blockClassName);
@synchronized(baseClass) { @synchronized(baseClass) {
if (lambdaHolder->id == nil) { if (lambdaHolder->id == nil) {
Class lambdaClass = objc_allocateClassPair(baseClass, [blockClassName UTF8String], sizeof(id)); Class lambdaClass = objc_allocateClassPair(baseClass, [blockClassName UTF8String], 0);
// Fail quickly if we can't create the runtime class. // Fail quickly if we can't create the runtime class.
if (!class_addProtocol(lambdaClass, protocol)) { if (!class_addProtocol(lambdaClass, protocol)) {
@throw AUTORELEASE([[JavaLangAssertionError alloc] @throw AUTORELEASE([[JavaLangAssertionError alloc]
initWithNSString:@"Unable to add protocol to capturing lambda class."]); initWithNSString:@"Unable to add protocol to capturing lambda class."]);
} }
IMP block_implementation = imp_implementationWithBlock(capturingLambdaBlockCallers[argumentCount]); IMP block_implementation = imp_implementationWithBlock(blockWrapper);
if (!class_addMethod([lambdaClass class], methodSelector, block_implementation, if (!class_addMethod([lambdaClass class], methodSelector, block_implementation,
capturingLambdaBlockTypes[argumentCount])) { blockTypeSignature(blockWrapper))) {
@throw AUTORELEASE([[JavaLangAssertionError alloc] @throw AUTORELEASE([[JavaLangAssertionError alloc]
initWithNSString:@"Unable to add method to capturing lambda class."]); initWithNSString:@"Unable to add method to capturing lambda class."]);
} }
Expand All @@ -217,7 +182,7 @@ id GetCapturingLambda(int argumentCount, Class baseClass, Protocol *protocol,
} }
} }
id instance = [[(id) lambdaHolder->id alloc] init]; id instance = [[(id) lambdaHolder->id alloc] init];
objc_setAssociatedObject(instance, (void*) 'b', [block copy], OBJC_ASSOCIATION_ASSIGN); objc_setAssociatedObject(instance, (void*) 0, block, OBJC_ASSOCIATION_COPY_NONATOMIC);
return instance; return instance;
} }


Expand Down
100 changes: 100 additions & 0 deletions jre_emul/Tests/com/google/j2objc/java8/CreationReferenceTest.java
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.j2objc.java8;

import junit.framework.TestCase;

interface FunInt<T> {
T apply(int x);
}

interface FunInt4<T> {
T apply(int x, I j, String s, Object o);
}

interface Call<T> {
T call();
}

class I {
String s = "...";
I() { }
I(int x) {
s = "" + x;
}
I(int x, I j, String s, Object o) {
this.s = s;
}
static String hello() {
return "Hello";
}
String world() {
return "World";
}
String getS() {
return s;
}
}

final class J {
int x = 41;
J() { }
J(int x) {
this.x = x;
}
J(int x, I j, String s, Object o) {
this.x = x;
}
static String hello() {
return "Hello";
}
String world() {
return "World";
}
int getX() {
return x;
}
}

/**
* Command-line tests for creation references.
*
* @author Seth Kirby
*/
public class CreationReferenceTest extends TestCase {
public CreationReferenceTest() {}

public void testBasicReferences() throws Exception {
Call<I> iInit = I::new;
FunInt<I> iInit2 = I::new;
FunInt4<I> iInit3 = I::new;
I myI = iInit.call();
I myI2 = iInit2.apply(42);
I myI3 = iInit3.apply(0, myI, "43", "");
assertEquals("World", myI.world());
assertEquals("...", myI.getS());
assertEquals("42", myI2.getS());
assertEquals("43", myI3.getS());
Call<J> jInit = J::new;
FunInt<J> jInit2 = J::new;
FunInt4<J> jInit3 = J::new;
J myJ = jInit.call();
J myJ2 = jInit2.apply(42);
J myJ3 = jInit3.apply(43, myI, "", "");
assertEquals("World", myJ.world());
assertEquals(41, myJ.getX());
assertEquals(42, myJ2.getX());
assertEquals(43, myJ3.getX());
}
}
1 change: 1 addition & 0 deletions jre_emul/Tests/com/google/j2objc/java8/SmallTests.java
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
*/ */
public class SmallTests { public class SmallTests {
private static final Class<?>[] smallTestClasses = new Class[] { private static final Class<?>[] smallTestClasses = new Class[] {
CreationReferenceTest.class,
LambdaTest.class LambdaTest.class
}; };


Expand Down
1 change: 1 addition & 0 deletions jre_emul/tests.mk
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -517,6 +517,7 @@ SUITE_SOURCES = \
org/json/SmallTests.java \ org/json/SmallTests.java \


JAVA8_TEST_SOURCES := \ JAVA8_TEST_SOURCES := \
com/google/j2objc/java8/CreationReferenceTest.java \
com/google/j2objc/java8/LambdaTest.java \ com/google/j2objc/java8/LambdaTest.java \


JAVA8_SUITE_SOURCES = \ JAVA8_SUITE_SOURCES = \
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*/ */
package com.google.devtools.j2objc.ast; package com.google.devtools.j2objc.ast;


import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.ITypeBinding;


import java.util.List; import java.util.List;
Expand All @@ -32,11 +33,13 @@
public abstract class MethodReference extends Expression { public abstract class MethodReference extends Expression {


protected ITypeBinding typeBinding; protected ITypeBinding typeBinding;
protected IMethodBinding methodBinding;
protected ChildList<Type> typeArguments = ChildList.create(Type.class, this); protected ChildList<Type> typeArguments = ChildList.create(Type.class, this);


public MethodReference(org.eclipse.jdt.core.dom.MethodReference jdtNode) { public MethodReference(org.eclipse.jdt.core.dom.MethodReference jdtNode) {
super(jdtNode); super(jdtNode);
typeBinding = jdtNode.resolveTypeBinding(); typeBinding = jdtNode.resolveTypeBinding();
methodBinding = jdtNode.resolveMethodBinding();
for (Object x : jdtNode.typeArguments()) { for (Object x : jdtNode.typeArguments()) {
typeArguments.add((Type) TreeConverter.convert(x)); typeArguments.add((Type) TreeConverter.convert(x));
} }
Expand All @@ -45,6 +48,7 @@ public MethodReference(org.eclipse.jdt.core.dom.MethodReference jdtNode) {
public MethodReference(MethodReference other) { public MethodReference(MethodReference other) {
super(other); super(other);
typeBinding = other.getTypeBinding(); typeBinding = other.getTypeBinding();
methodBinding = other.getMethodBinding();
typeArguments.copyFrom(other.typeArguments()); typeArguments.copyFrom(other.typeArguments());
} }


Expand All @@ -53,6 +57,10 @@ public ITypeBinding getTypeBinding() {
return typeBinding; return typeBinding;
} }


public IMethodBinding getMethodBinding() {
return methodBinding;
}

public List<Type> typeArguments() { public List<Type> typeArguments() {
return typeArguments; return typeArguments;
} }
Expand Down
Loading

0 comments on commit d05d92d

Please sign in to comment.