Permalink
Browse files

# Code cleanup.

  • Loading branch information...
1 parent 7e82e20 commit 743502afe50e9d86d53ad6afd2783a9446ef329f @manuelpichler committed Dec 13, 2010
Showing with 1,364 additions and 3,421 deletions.
  1. 0 .gitignore
  2. 0 components/aop/.gitignore
  3. 0 components/aop/build.properties
  4. 0 components/aop/build.xml
  5. 0 components/aop/docs/design.txt
  6. +119 −15 components/aop/play.php
  7. +0 −166 components/aop/source/Container.php
  8. +194 −0 components/aop/source/example/scanner.php
  9. 0 components/aop/source/example/scanner/aspects/MyAspect
  10. +36 −0 components/aop/source/example/scanner/aspects/MyAspect.php
  11. +38 −0 components/aop/source/example/scanner/aspects/MyAspectTwo.php
  12. +45 −0 components/aop/source/example/scanner/aspects/MyThirdAspect.php
  13. +10 −0 components/aop/source/example/scanner/domain/Customer.php
  14. +34 −0 components/aop/source/example/scanner/domain/Product.php
  15. +0 −388 components/aop/source/generator/AdviceCodeGenerator.php
  16. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/Aspect.php
  17. +102 −0 components/aop/source/main/php/de/buzz2ee/aop/Container.php
  18. +121 −0 components/aop/source/main/php/de/buzz2ee/aop/DefaultAdviceRegistry.php
  19. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/ReflectionJoinPoint.php
  20. +12 −2 components/aop/source/{ → main/php/de/buzz2ee/aop}/RuntimeJoinPoint.php
  21. +6 −8 components/aop/source/{ → main/php/de/buzz2ee/aop}/RuntimeProceedingJoinPoint.php
  22. +85 −0 components/aop/source/main/php/de/buzz2ee/aop/advice/Advices.php
  23. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/advice/AfterAdvice.php
  24. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/advice/AfterReturningAdvice.php
  25. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/advice/AfterThrowingAdvice.php
  26. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/advice/AroundAdvice.php
  27. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/advice/BaseAdvice.php
  28. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/advice/BeforeAdvice.php
  29. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/exceptions/Exception.php
  30. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/exceptions/InvalidPointcutExpressionException.php
  31. +472 −0 components/aop/source/main/php/de/buzz2ee/aop/generator/AdviceCodeGenerator.php
  32. +12 −22 components/aop/source/{ → main/php/de/buzz2ee/aop}/generator/ProxyClassGenerator.php
  33. +44 −0 components/aop/source/main/php/de/buzz2ee/aop/generator/ProxyGeneratorFactory.php
  34. +17 −4 components/aop/source/{ → main/php/de/buzz2ee/aop}/generator/ProxyMethodGenerator.php
  35. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/generator/ProxyParameterGenerator.php
  36. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/interfaces/Advice.php
  37. +7 −0 components/aop/source/main/php/de/buzz2ee/aop/interfaces/AdviceRegistry.php
  38. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/interfaces/ClassGenerator.php
  39. +7 −0 components/aop/source/main/php/de/buzz2ee/aop/interfaces/GeneratorFactory.php
  40. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/interfaces/JoinPoint.php
  41. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/interfaces/Pointcut.php
  42. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/interfaces/PointcutMatcher.php
  43. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/interfaces/PointcutRegistry.php
  44. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/interfaces/ProceedingJoinPoint.php
  45. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/interfaces/Proxy.php
  46. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/pointcut/DefaultPointcut.php
  47. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/pointcut/PointcutAndMatcher.php
  48. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/pointcut/PointcutBinaryMatcher.php
  49. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/pointcut/PointcutExecutionMatcher.php
  50. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/pointcut/PointcutExpressionParser.php
  51. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/pointcut/PointcutMatcherFactory.php
  52. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/pointcut/PointcutNamedMatcher.php
  53. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/pointcut/PointcutNotMatcher.php
  54. 0 components/aop/source/{ → main/php/de/buzz2ee/aop}/pointcut/PointcutOrMatcher.php
  55. +1 −1 components/aop/{ → source}/test/AllTests.php
  56. 0 components/aop/{ → source}/test/BaseTest.php
  57. 0 components/aop/{ → source}/test/generator/AdviceCodeGeneratorTest.php
  58. +1 −1 components/aop/{ → source}/test/generator/AllTests.php
  59. 0 components/aop/{ → source}/test/generator/ProxyParameterGeneratorTest.php
  60. +1 −1 components/aop/{ → source}/test/pointcut/AllTests.php
  61. 0 components/aop/{ → source}/test/pointcut/PointcutAndMatcherTest.php
  62. 0 components/aop/{ → source}/test/pointcut/PointcutExecutionMatcherTest.php
  63. 0 components/aop/{ → source}/test/pointcut/PointcutExpressionParserTest.php
  64. 0 components/aop/{ → source}/test/pointcut/PointcutMatcherFactoryTest.php
  65. 0 components/aop/{ → source}/test/pointcut/PointcutNotMatcherTest.php
  66. 0 components/aop/{ → source}/test/pointcut/PointcutOrMatcherTest.php
  67. +0 −14 components/ioc/build.xml
  68. +0 −8 components/ioc/sample/Movie.php
  69. +0 −10 components/ioc/sample/SienceFictionMovie.php
  70. +0 −52 components/ioc/sample/foo.php
  71. +0 −51 components/ioc/source/ArgumentValidatorFactory.php
  72. +0 −45 components/ioc/source/ConstructorReferenceArgument.php
  73. +0 −44 components/ioc/source/ConstructorValueArgument.php
  74. +0 −132 components/ioc/source/DefaultContainer.php
  75. +0 −57 components/ioc/source/ObjectBuilderFactory.php
  76. +0 −69 components/ioc/source/ObjectFactoryFactory.php
  77. +0 −47 components/ioc/source/PropertyValueArgument.php
  78. +0 −32 components/ioc/source/PrototypeObjectFactory.php
  79. +0 −135 components/ioc/source/ReflectionArgumentValidator.php
  80. +0 −39 components/ioc/source/ReflectionInjectionFactory.php
  81. +0 −118 components/ioc/source/ReflectionObjectBuilder.php
  82. +0 −43 components/ioc/source/ReflectionPropertyInjection.php
  83. +0 −38 components/ioc/source/ReflectionSetterInjection.php
  84. +0 −41 components/ioc/source/SingletonObjectFactory.php
  85. +0 −32 components/ioc/source/SourceLoaderAutoload.php
  86. +0 −28 components/ioc/source/exceptions/ArgumentNotFoundException.php
  87. +0 −33 components/ioc/source/exceptions/ArgumentTypeException.php
  88. +0 −28 components/ioc/source/exceptions/ClassNotFoundException.php
  89. +0 −23 components/ioc/source/exceptions/CyclicDependencyException.php
  90. +0 −20 components/ioc/source/exceptions/Exception.php
  91. +0 −29 components/ioc/source/exceptions/PropertyNotFoundException.php
  92. +0 −22 components/ioc/source/interfaces/Argument.php
  93. +0 −28 components/ioc/source/interfaces/ArgumentValidator.php
  94. +0 −28 components/ioc/source/interfaces/BaseConstructorArgument.php
  95. +0 −54 components/ioc/source/interfaces/BaseInjection.php
  96. +0 −82 components/ioc/source/interfaces/BaseObjectFactory.php
  97. +0 −49 components/ioc/source/interfaces/BasePropertyArgument.php
  98. +0 −24 components/ioc/source/interfaces/Container.php
  99. +0 −27 components/ioc/source/interfaces/Injection.php
  100. +0 −41 components/ioc/source/interfaces/ObjectBuilder.php
  101. +0 −39 components/ioc/source/interfaces/ObjectFactory.php
  102. +0 −23 components/ioc/source/interfaces/PropertyArgument.php
  103. +0 −27 components/ioc/source/interfaces/SourceLoader.php
  104. +0 −58 components/ioc/test/AllTests.php
  105. +0 −50 components/ioc/test/ArgumentValidatorFactoryTest.php
  106. +0 −308 components/ioc/test/BaseContainerTest.php
  107. +0 −43 components/ioc/test/BaseTest.php
  108. +0 −28 components/ioc/test/DefaultContainerTest.php
  109. +0 −50 components/ioc/test/ObjectBuilderFactoryTest.php
  110. +0 −40 components/ioc/test/ObjectFactoryFactoryTest.php
  111. +0 −241 components/ioc/test/ReflectionArgumentValidatorTest.php
  112. +0 −58 components/ioc/test/ReflectionPropertyInjectionTest.php
  113. +0 −85 components/ioc/test/ReflectionSetterInjectionTest.php
  114. +0 −61 components/ioc/test/_source/Author.php
  115. +0 −38 components/ioc/test/_source/Book.php
  116. +0 −116 components/ioc/test/_source/Chapter.php
  117. +0 −25 components/ioc/test/_source/CtorCycle.php
View
0 .gitignore 100644 → 100755
No changes.
View
0 components/aop/.gitignore 100644 → 100755
No changes.
View
0 components/aop/build.properties 100644 → 100755
No changes.
View
0 components/aop/build.xml 100644 → 100755
No changes.
View
0 components/aop/docs/design.txt 100644 → 100755
No changes.
View
134 components/aop/play.php 100644 → 100755
@@ -5,12 +5,14 @@
spl_autoload_register(
function($className) {
if (strpos($className, __NAMESPACE__) === 0) {
- include __DIR__ . '/source/' . strtr( substr( $className, strlen( __NAMESPACE__ ) + 1 ), '\\', '/' ) . '.php';
+ include __DIR__ . '/source/main/php/' . strtr( $className, '\\', '/' ) . '.php';
}
}
);
+use de\buzz2ee\aop\generator\ProxyGeneratorFactory;
use de\buzz2ee\aop\interfaces\JoinPoint;
+use de\buzz2ee\aop\interfaces\ProceedingJoinPoint;
/**
* @Aspect
@@ -32,15 +34,15 @@ function myPointcut() {}
* @Before("\de\buzz2ee\aop\MyAspect::myPointcut()")
*/
function myBeforeAdvice( JoinPoint $joinPoint ) {
- echo __METHOD__ . '(' . $joinPoint->getClassName() . '::' . $joinPoint->getMethodName() . ')' . PHP_EOL;
+ // echo __METHOD__ . '(' . $joinPoint->getClassName() . '::' . $joinPoint->getMethodName() . ')' . PHP_EOL;
}
/**
* @After("\de\buzz2ee\aop\MyAspect::myPointcut()")
*/
function myAfterAdvice( JoinPoint $joinPoint )
{
- echo __METHOD__ . '(' . $joinPoint->getClassName() . '::' . $joinPoint->getMethodName() . ')' . PHP_EOL;
+ // echo __METHOD__ . '(' . $joinPoint->getClassName() . '::' . $joinPoint->getMethodName() . ')' . PHP_EOL;
}
}
@@ -56,49 +58,151 @@ class MyAspectTwo
*/
public function myAfterReturningAdvice( JoinPoint $joinPoint )
{
- echo __METHOD__ . '(' . $joinPoint->getClassName() . '::' . $joinPoint->getMethodName() . ')' . PHP_EOL;
+ // echo __METHOD__ . '(' . $joinPoint->getClassName() . '::' . $joinPoint->getMethodName() . ')' . PHP_EOL;
}
/**
* @AfterThrowing("execution(* *MyClass::bar())")
*/
public function myAfterThrowingAdvice( JoinPoint $joinPoint )
{
- echo __METHOD__ . '(' . $joinPoint->getClassName() . '::' . $joinPoint->getMethodName() . ')' . PHP_EOL;
+ // echo __METHOD__ . '(' . $joinPoint->getClassName() . '::' . $joinPoint->getMethodName() . ')' . PHP_EOL;
}
/**
* @Around("execution(* *MyClass::bar())")
*/
- public function myAroundAdvice( interfaces\ProceedingJoinPoint $joinPoint )
+ public function myAroundAdvice( ProceedingJoinPoint $joinPoint )
{
- echo __METHOD__ . '(' . $joinPoint->getClassName() . '::' . $joinPoint->getMethodName() . ')' . PHP_EOL;
+ // echo __METHOD__ . '(' . $joinPoint->getClassName() . '::' . $joinPoint->getMethodName() . ')' . PHP_EOL;
$joinPoint->proceed();
}
}
+/**
+ * @Aspect
+ */
+class MyThirdAspect
+{
+ const TYPE = __CLASS__;
+
+ /**
+ * @Pointcut("
+ * execution(
+ * *MyClass::baz()
+ * )
+ * ")
+ */
+ function myPointcut() {}
+
+ /**
+ * @Around("\de\buzz2ee\aop\MyThirdAspect::myPointcut()")
+ */
+ public function myFirstAroundAdvice( ProceedingJoinPoint $joinPoint )
+ {
+ return 1 + $joinPoint->proceed();
+ }
+
+ /**
+ * @Around("\de\buzz2ee\aop\MyThirdAspect::myPointcut()")
+ */
+ public function mySecondAroundAdvice( ProceedingJoinPoint $joinPoint )
+ {
+ return 1 + $joinPoint->proceed();
+ }
+
+ /**
+ * @Around("\de\buzz2ee\aop\MyThirdAspect::myPointcut()")
+ */
+ public function myThirdAroundAdvice( ProceedingJoinPoint $joinPoint )
+ {
+ return 1 + $joinPoint->proceed();
+ }
+}
+
class MyClass extends \stdClass
{
const TYPE = __CLASS__;
function foo()
{
- echo __METHOD__, PHP_EOL;
+ // echo __METHOD__, PHP_EOL;
}
function bar( MyAspect $aspect )
{
throw new \Exception( __METHOD__ );
}
+
+ function baz( \stdClass $class )
+ {
+ return 42;
+ }
}
+// Just for this benchmark, pre include stuff
+class_exists('\de\buzz2ee\aop\RuntimeProceedingJoinPoint', true);
+class_exists('\de\buzz2ee\aop\RuntimeJoinPoint', true);
+
+//sleep(2);
+
+$adviceRegistry = new DefaultAdviceRegistry();
+$adviceRegistry->registerAspect( MyAspect::TYPE );
+$adviceRegistry->registerAspect( MyAspectTwo::TYPE );
+$adviceRegistry->registerAspect( MyThirdAspect::TYPE );
+
$container = new Container();
-$container->registerAspect( MyAspect::TYPE );
-$container->registerAspect( MyAspectTwo::TYPE );
+$container->setGeneratorFactory( new ProxyGeneratorFactory( $adviceRegistry ) );
+$container->setAdviceRegistry( $adviceRegistry );
+
+$count = 50000;
+
+$object0 = new MyClass();
+
+// START: ======================================================================
+$start = microtime(true);
+
+for ($i = 0; $i < $count; ++$i) {
+ $object0->foo();
+
+// try {
+// $object->bar( new MyAspect() );
+// } catch ( \Exception $e ) {
+// // echo $e->getFile(), ' +', $e->getLine(), PHP_EOL;
+// }
+ $object0->baz( new \stdClass() );
+}
+
+printf("===========================\nTotal time: %.8f\n===========================\n", ( ( $v0 = microtime( true ) - $start ) / $count ) );
+// END: ========================================================================
+
+$object1 = $container->createObject( MyClass::TYPE );
+
+// START: ======================================================================
+$start = microtime(true);
+//echo "foo()\n===========================\n";
+
+for ($i = 0; $i < $count; ++$i) {
+// $s0 = microtime(true);
+ $object1->foo();
+// printf( "Outer benchmark: %.8f\n", (microtime(true) - $s0));
+// try {
+// $object->bar( new MyAspect() );
+// } catch ( \Exception $e ) {
+// // echo $e->getFile(), ' +', $e->getLine(), PHP_EOL;
+// }
+//
+// echo "===========================\nbaz()\n===========================\n";
+// $s0 = microtime(true);
+ $object1->baz( new \stdClass() );
+// printf( "Outer benchmark: %.8f\n", (microtime(true) - $s0));
+}
+
+printf("===========================\nTotal time: %.8f\n===========================\n", ( ( $v1 = microtime( true ) - $start ) / $count ) );
+// END: ========================================================================
+
-$object = $container->createObject( MyClass::TYPE );
-$object->foo();
+printf ( "Proxy vs. POPO %.2f%%\n", ( $v1 / ( $v0 / 100 ) ) );
-try {
- $object->bar( new MyAspect() );
-} catch ( \Exception $e ) {}
+$x = ( $object0 === $object1 ? 1 : 0);
+exit( $x );
@@ -1,166 +0,0 @@
-<?php
-/**
- * This file is part of the aspect oriented programming component.
- *
- * PHP Version 5
- *
- * Copyright (c) 2009-2010, Manuel Pichler <mapi@pdepend.org>.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * * Neither the name of Manuel Pichler nor the names of his
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * @category Components
- * @package de\buzz2ee\aop
- * @author Manuel Pichler <mapi@buzz2ee.de>
- * @copyright 2009-2010 Manuel Pichler. All rights reserved.
- * @license http://www.opensource.org/licenses/bsd-license.php BSD License
- * @version SVN: $Id$
- * @link http://buzz2ee.de/
- */
-
-namespace de\buzz2ee\aop;
-
-use de\buzz2ee\aop\interfaces\Pointcut;
-use de\buzz2ee\aop\interfaces\PointcutRegistry;
-
-use de\buzz2ee\aop\pointcut\PointcutExpressionParser;
-use de\buzz2ee\aop\pointcut\PointcutMatcherFactory;
-
-use de\buzz2ee\aop\generator\AdviceCodeGenerator;
-
-/**
- *
- *
- * @category Components
- * @package de\buzz2ee\aop
- * @author Manuel Pichler <mapi@buzz2ee.de>
- * @copyright 2009-2010 Manuel Pichler. All rights reserved.
- * @license http://www.opensource.org/licenses/bsd-license.php BSD License
- * @version Release: @package_version@
- * @link http://buzz2ee.de/
- */
-class Container implements PointcutRegistry
-{
- private $_aspects = array();
-
- public function __construct()
- {
- PointcutMatcherFactory::set( new PointcutMatcherFactory() );
- }
-
- public function registerAspect( $aspectClassName )
- {
- $aspect = new Aspect( $aspectClassName );
-
- $reflection = new \ReflectionClass( $aspectClassName );
- foreach ( $reflection->getMethods() as $method )
- {
- if ( preg_match( '(\*\s*@(Pointcut|After|AfterReturning|AfterThrowing|Before|Around)\(\s*"(.*)"\s*\)\s*\*(\r|\n|/))s', $method->getDocComment(), $match ) === 0 )
- {
- continue;
- }
-
- $pointcutName = $reflection->getName() . '::' . $method->getName() . '()';
-
- $tagName = $match[1];
- $expression = $match[2];
-
- $expression = trim( preg_replace( '(\s*\*\s+)', '', $expression ) );
-
- $parser = new PointcutExpressionParser();
- $pointcut = new \de\buzz2ee\aop\pointcut\DefaultPointcut(
- $pointcutName,
- $parser->parse( $expression )
- );
-
- switch ( $tagName )
- {
- case 'Pointcut':
- $aspect->addPointcut( $pointcut );
- break;
-
- case 'After':
- $aspect->addAdvice( new \de\buzz2ee\aop\advice\AfterAdvice( $pointcut, $method->getName(), $reflection->getName() ) );
- break;
-
- case 'AfterReturning':
- $aspect->addAdvice( new \de\buzz2ee\aop\advice\AfterReturningAdvice( $pointcut, $method->getName(), $reflection->getName() ) );
- break;
-
- case 'AfterThrowing':
- $aspect->addAdvice( new \de\buzz2ee\aop\advice\AfterThrowingAdvice( $pointcut, $method->getName(), $reflection->getName() ) );
- break;
-
- case 'Around':
- $aspect->addAdvice( new \de\buzz2ee\aop\advice\AroundAdvice( $pointcut, $method->getName(), $reflection->getName() ) );
- break;
-
- case 'Before':
- $aspect->addAdvice( new \de\buzz2ee\aop\advice\BeforeAdvice( $pointcut, $method->getName(), $reflection->getName() ) );
- break;
- }
- }
-
- $this->_aspects[] = $aspect;
- }
-
- public function createObject( $className )
- {
- $adviceGenerator = new AdviceCodeGenerator( $this, $this->_aspects );
- $methodGenerator = new generator\ProxyMethodGenerator( $adviceGenerator );
-
- $creator = new generator\ProxyClassGenerator( $adviceGenerator, $methodGenerator );
- $proxyClass = $creator->create( $className );
-
- $proxyInstance = new $proxyClass( new $className() );
- foreach ( $proxyInstance->____aop_get_interceptor_configuration() as $name )
- {
- $proxyInstance->____aop_add_interceptor_instance( $name, new $name() );
- }
-
- return $proxyInstance;
- }
-
- public function getPointcutByName( $pointcutName )
- {
- $name = ltrim( $pointcutName, '\\' );
- foreach ( $this->_aspects as $aspect )
- {
- foreach ( $aspect->getPointcuts() as $pointcut )
- {
- if ( $pointcut->getName() === $name )
- {
- return $pointcut;
- }
- }
- }
- throw new \InvalidArgumentException( 'Unknown pointcut name: ' . $pointcutName );
- }
-}
Oops, something went wrong.

0 comments on commit 743502a

Please sign in to comment.