Upgrade to Pro — share decks privately, control downloads, hide ads and more …

JavaScriptCore.frameworkでできること

 JavaScriptCore.frameworkでできること

JavaScriptCore.frameworkの解説と、できることの限界を探ります。

Kishikawa Katsumi

February 02, 2014
Tweet

More Decks by Kishikawa Katsumi

Other Decks in Programming

Transcript

  1. ! JSContext *context = [[JSContext alloc] init]; ! JSValue *result

    = [context evaluateScript:@"2 + 8"]; NSLog(@"%d", result.toInt32);
  2. JSContext *context = [[JSContext alloc] init]; ! context[@"factorial"] = ^(int

    x) { int factorial = 1; for (; x > 1; x--) { factorial *= x; } return factorial; }; ! JSValue *result = [context evaluateScript:@"factorial(5);"]; NSLog(@"%d", result.toInt32);
  3. JSContext *context = [[JSContext alloc] init]; ! context[@"factorial"] = ^(int

    x) { int factorial = 1; for (; x > 1; x--) { factorial *= x; } return factorial; }; ! JSValue *function = context[@“factorial"]; ! JSValue *result = [function callWithArguments:@[@(5)]]; NSLog(@"%d", result.toInt32);
  4. JSContext *context = [[JSContext alloc] init]; [context evaluateScript: @"function sum(a,

    b) { return a + b; }" ]; ! JSValue *function = context[@“sum"]; ! JSValue *result = [function callWithArguments:@[@(2), @(3)]] NSLog(@"%d", result.toInt32);
  5. w ͸ͯͳه๏1BSTFS  UFYUIBUFOBKT  IUUQUFDIOJUPZPODPNKBWBTDSJQUBQQMJDBUJPOUFYUIBUFOBEPXOMPBEIUNM w 5FYUJMF1BSTFS  UFYUJMFKT

     IUUQTHJUIVCDPNCPSHBSUFYUJMFKT w +40/ͷѹॖ  +40/)  IUUQTHJUIVCDPN8FC3FqFDUJPO+40/) +BWB4DSJQUͰॻ͔ΕͨϥΠϒϥϦΛ࢖͏
  6. NSBundle *bundle = [NSBundle mainBundle]; NSString *path = [bundle pathForResource:@"text-hatena"

    ofType:@"js"]; NSString *script = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil]; ! JSContext *context = [[JSContext alloc] init]; [context evaluateScript:script]; [context evaluateScript:@"var parser = new TextHatena();"]; ! JSValue *parser = context[@"parser"]; ! NSString *text = [NSString stringWithContentsOfFile: [bundle pathForResource:@“sample” ofType:@“txt"] encoding:NSUTF8StringEncoding error:nil]; ! NSLog(@"%@", [parser invokeMethod:@"parse" withArguments:@[text]]); ! return YES;
  7. *1384756611*[Objective-C][iOS]iOSΞϓϦέʔγϣϯͰΩʔϘʔυγϣʔτΧοτʹରԠ͢Δ ↓ ΑΓஸೡͳهࣄ͸ͪ͜ΒͰެ։͍ͯ͠·͢ɻ - [http://mobiletou.ch/2013/11/ ios%E3%82%A2%E3%83%97%E3%83%AA%E3%81%A7%E5%A4%96%E9%83%A8%E3%82%AD%E3%83%BC%E3%83%9C%E3 %83%BC%E3%83%89%E3%81%8B%E3%82%89%E3%81%AE%E3%82%B7%E3%83%A7%E3%83%BC%E3%83%88%E3%82%AB %E3%83%83%E3%83%88:title=iOSΞϓϦͰ֎෦ΩʔϘʔυ͔ΒͷγϣʔτΧοτʹରԠ͢Δํ๏ - iOSΞϓϦ։

    ൃ͜΅Ε࿩] ! ! iOS 7ͷSafari΍ϝʔϧͰ͸֎෦ΩʔϘʔυΛ࢖༻ͨ͠ࡍʹར༻Ͱ͖ΔͰ͖ΔγϣʔτΧοτ͕[http:// www.appbank.net/2013/11/08/iphone-news/696998.php:title=ҎલΑΓॆ࣮ͨ͜͠ͱ͕࿩୊ʹͳΓ·͠ ͨɻ] ! ! iOS 7Ͱ͸ΩʔϘʔυγϣʔτΧοτΛ࣮૷͢ΔͨΊͷAPI͕௥Ճ͞Ε͍ͯΔͷͰɺαʔυύʔςΟͷΞϓϦέʔγϣ ϯ΋ΩʔϘʔυγϣʔτΧοτʹରԠ͢Δ͜ͱ͕Ͱ͖·͢ɻ ! ! ಛఆͷΩʔϘʔυγϣʔτΧοτʹԠ౴͢Δʹ͸ԼهͷϓϩύςΟΛ࣮૷͠·͢ɻ >|objc| @property(nonatomic, readonly) NSArray *keyCommands ||<
  8. <div class="section"> <h3><a href="#1384756611" name="1384756611"><span class="sanchor">o-</span></a> [<a class="sectioncategory" href="searchdiary?word=*[Objective-C]">Objective-C</a>][<a class="sectioncategory"

    href="searchdiary?word=*[iOS]">iOS</a>]iOSΞϓϦέʔγϣϯͰΩʔϘʔυγϣʔτ ΧοτʹରԠ͢Δ</h3> <p>↓ ΑΓஸೡͳهࣄ͸ͪ͜ΒͰެ։͍ͯ͠·͢ɻ</p> <ul> <li> [http://mobiletou.ch/2013/11/ ios%E3%82%A2%E3%83%97%E3%83%AA%E3%81%A7%E5%A4%96%E9%83%A8%E3%82%AD%E3%83%BC%E3%83%9C%E3%83%BC%E3%8 3%89%E3%81%8B%E3%82%89%E3%81%AE%E3%82%B7%E3%83%A7%E3%83%BC%E3%83%88%E3%82%AB%E3%83%83%E3%83%88:tit le=iOSΞϓϦͰ֎෦ΩʔϘʔυ͔ΒͷγϣʔτΧοτʹରԠ͢Δํ๏ - iOSΞϓϦ։ൃ͜΅Ε࿩]</li> </ul> <br> <p>iOS 7ͷSafari΍ϝʔϧͰ͸֎෦ΩʔϘʔυΛ࢖༻ͨ͠ࡍʹར༻Ͱ͖ΔͰ͖ΔγϣʔτΧοτ͕[http:// www.appbank.net/2013/11/08/iphone-news/696998.php:title=ҎલΑΓॆ࣮ͨ͜͠ͱ͕࿩୊ʹͳΓ·ͨ͠ɻ]</p> <br> <p>iOS 7Ͱ͸ΩʔϘʔυγϣʔτΧοτΛ࣮૷͢ΔͨΊͷAPI͕௥Ճ͞Ε͍ͯΔͷͰɺαʔυύʔςΟͷΞϓϦέʔγϣϯ΋ ΩʔϘʔυγϣʔτΧοτʹରԠ͢Δ͜ͱ͕Ͱ͖·͢ɻ</p> <br> <p>ಛఆͷΩʔϘʔυγϣʔτΧοτʹԠ౴͢Δʹ͸ԼهͷϓϩύςΟΛ࣮૷͠·͢ɻ</p> <pre class="syntax-highlight prettyprint lang-objc"> @property(nonatomic, readonly) NSArray *keyCommands </pre>
  9. NSBundle *bundle = [NSBundle mainBundle]; NSString *path = [bundle pathForResource:@"textile"

    ofType:@"js"]; NSString *script = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil]; ! JSContext *context = [[JSContext alloc] init]; [context evaluateScript:script]; ! JSValue *func = context[@"textile"]; ! NSString *text = [NSString stringWithContentsOfFile: [bundle pathForResource:@"sample" ofType:@“textile"] encoding:NSUTF8StringEncoding error:nil]; ! NSLog(@"%@", [func callWithArguments:@[text]]);
  10. ࠓճ͸ɺεϖγϟϧηογϣϯͱͯ͠Evernoteຊ͔ࣾΒMac൛EvernoteͷUIͷϦχϡʔΞϧͷࢦشΛ͞ Ε·ͨ͠["Jack Hirsch":https://twitter.com/Jackolicious]͞Μʹ͖͍͖ͯͨͩɺഎܠ΍։ ൃख๏ͳͲΛ͓࿩͍͖ͨͩ·͢ɻ ͢΂ͯͷ։ൃऀʹͱͬͯඇৗʹ༗ҙٛͳ࣌ؒʹͳΔͱࢥ͍·͢ͷͰͥͻ͓ӽ͍ͩ͘͠͞ɻ ! ! h3. ελοϑืू !

    ౰೔ɺձ৔ͷ४උ΍ड෇ͳͲΛख఻ͬͯͩ͘͞Δ͔ͨΛืू͍ͨ͠·͢ɻख఻ͬͯ΋͍͍Αɺͱ͍͏͔ͨ ͕͍Βͬ͠Ό͍·ͨ͠Β͝࿈བྷ͍ͩ͘͞·ͤɻ ! ["lesamoureuses":https://twitter.com/lesamoureuses] ["huin":https://twitter.com/huin] ["myb":https://twitter.com/myb] ["nun_":https://twitter.com/nun_] ["KohsakuNishida":https://twitter.com/KohsakuNishida] ["৿ౢେथ":https://www.facebook.com/hiroki.with.omnia] ! ! *։৔ 12:30* **৔ॴ** גࣜձࣾVOYAGE GROUP (౦ژ౎ौ୩۠ਆઘொ8-16 ौ୩ϑΝʔετϓϨΠε8F)
  11. <p>ࠓճ͸ɺεϖγϟϧηογϣϯͱͯ͠Evernoteຊ͔ࣾΒMac൛EvernoteͷUIͷϦχϡʔΞ ϧͷࢦشΛ͞Ε·ͨ͠<a href="https://twitter.com/Jackolicious">Jack Hirsch</a>͞Μʹ͖͍͖ͯͨͩɺഎܠ΍։ൃख๏ͳͲΛ͓࿩͍͖ͨͩ·͢ɻ<br /> ͢΂ͯͷ։ൃऀʹͱͬͯඇৗʹ༗ҙٛͳ࣌ؒʹͳΔͱࢥ͍·͢ͷͰͥͻ͓ӽ͍ͩ͘͠͞ɻ</p> <h3>ελοϑืू</h3> <p>౰೔ɺձ৔ͷ४උ΍ड෇ͳͲΛख఻ͬͯͩ͘͞Δ͔ͨΛืू͍ͨ͠·͢ɻख఻ͬͯ΋͍͍ Αɺͱ͍͏͔͕͍ͨΒͬ͠Ό͍·ͨ͠Β͝࿈བྷ͍ͩ͘͞·ͤɻ</p> <p><a

    href="https://twitter.com/lesamoureuses">lesamoureuses</a><br /> <a href="https://twitter.com/huin">huin</a><br /> <a href="https://twitter.com/myb">myb</a><br /> <a href="https://twitter.com/nun_">nun_</a><br /> <a href="https://twitter.com/KohsakuNishida">KohsakuNishida</a><br /> <a href="https://www.facebook.com/hiroki.with.omnia">৿ౢେथ</a> </p> <p><strong>։৔ 12:30</strong><br /> <b>৔ॴ</b><br /> גࣜձࣾ<span class="caps">VOYAGE</span> <span class="caps">GROUP</ span><br /> (౦ژ౎ौ୩۠ਆઘொ8-16 ौ୩ϑΝʔετϓϨΠε8F)</p>
  12. @protocol JSUIWindow <JSExport> ! @property (nonatomic) CGRect frame; @property (nonatomic)

    UIColor *backgroundColor; ! + (id)new; - (void)makeKeyAndVisible; ! @end
  13. JSContext *context = [[JSContext alloc] init]; context[@"JSUIWindow"] = [JSUIWindow class];

    JSContext *context = [[JSContext alloc] init]; context[@"JSUIWindow"] = [JSUIWindow class]; ! [context evaluateScript:@"var window = JSUIWindow.new();"]; ! JSValue *value = context[@"window"]; NSLog(@"%@", value.toObject);
  14. ! class_addProtocol([UIWindow class], @protocol(JSUIWindow)); JSContext *context = [[JSContext alloc] init];

    context[@"UIWindow"] = [UIWindow class]; [context evaluateScript:@"var window = UIWindow.new();"]; ! JSValue *value = context[@"window"]; NSLog(@"%@", value.toObject);
  15. static void setup(JSContext *context) { const char *prefix = "JSUI";

    unsigned int numberOfClasses; Class *classes = objc_copyClassList(&numberOfClasses); for (unsigned int i = 0; i < numberOfClasses; i++) { Class cls = classes[i]; const char *className = class_getName(cls); NSString *name = @(className); char protocolName[512]; snprintf(protocolName, sizeof(protocolName), "%s%s", prefix, className); Protocol *proto = objc_allocateProtocol(protocolName); traverce_class(proto, cls); protocol_addProtocol(proto, @protocol(JSExport)); objc_registerProtocol(proto); class_addProtocol(cls, proto); context[name] = cls; } free(classes); } 4*("#35
  16. @protocol JSBUIView; ! @protocol JSBUIAlertView <JSExport, JSBUIView> ! @property (nonatomic,

    readonly) NSInteger numberOfButtons; @property (nonatomic, readonly, getter = isVisible) BOOL visible; @property (nonatomic) NSInteger cancelButtonIndex; @property (nonatomic, copy) NSString *title; @property (nonatomic, copy) NSString *message; @property (nonatomic, assign) id delegate; @property (nonatomic, assign) UIAlertViewStyle alertViewStyle; @property (nonatomic, readonly) NSInteger firstOtherButtonIndex; ! - (id)initWithTitle:(NSString *)title message:(NSString *)message delegate:(id)delegate cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles , ...; - (NSInteger)addButtonWithTitle:(NSString *)title; - (NSString *)buttonTitleAtIndex:(NSInteger)buttonIndex; - (void)show; - (void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated: (BOOL)animated; - (UITextField *)textFieldAtIndex:(NSInteger)textFieldIndex; ! #pragma clang diagnostic pop ! @end
  17. @protocol JSBUIView <JSExport, JSBUIResponder> ! @property (nonatomic, getter = isOpaque)

    BOOL opaque; @property (nonatomic) CGRect frame; @property (nonatomic, getter = isHidden) BOOL hidden; @property (nonatomic) BOOL autoresizesSubviews; @property (nonatomic, readonly, copy) NSArray *subviews; ! ! + (Class)layerClass; + (void)beginAnimations:(NSString *)animationID context:(void *)context; + (void)commitAnimations;
  18. @protocol JSBNSObject <JSExport, NSObject> ! - (BOOL)isEqual:(id)object; - (NSUInteger)hash; !

    - (Class)superclass; - (Class)class; ! - (BOOL)isProxy; ! - (BOOL)isKindOfClass:(Class)aClass; - (BOOL)isMemberOfClass:(Class)aClass; - (BOOL)conformsToProtocol:(Protocol *)aProtocol; ! - (NSString *)description; - (NSString *)debugDescription; ! + (void)load; ! + (void)initialize; - (id)init; ! + (id)new; + (id)allocWithZone:(struct _NSZone *)zone;
  19. [context evaluateScript: @"var window = UIWindow.alloc().initWithFrame(UIScreen.mainScreen().bounds);" @"window.backgroundColor = UIColor.whiteColor();" @""

    @"var navigationController = UINavigationController.new();" @"" @"var tableViewController = UITableViewController.new();" @"tableViewController.navigationItem.title = 'JavaScriptBridge';" @"navigationController.viewControllers = [tableViewController];" @"" @"window.rootViewController = navigationController;" @"" @"window.makeKeyAndVisible();" @"" @"var alertView = UIAlertView.new();" @"alertView.message = 'Hello JavaScriptBridge!';" @"alertView.addButtonWithTitle('OK');" @"alertView.show();" ];
  20. UISlider *slider = [[UISlider alloc] initWithFrame:frame]; slider.backgroundColor = [UIColor clearColor];

    slider.minimumValue = 0.0; slider.maximumValue = 100.0; slider.continuous = YES; slider.value = 50.0; var slider = UISlider.alloc().initWithFrame(frame); slider.backgroundColor = UIColor.clearColor(); slider.minimumValue = 0.0; slider.maximumValue = 100.0; slider.continuous = true; slider.value = 50.0; ϓϩύςΟΞΫηε
  21. UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; var window

    = UIWindow.alloc().initWithFrame(UIScreen.mainScreen().bounds); ϝιουݺͼग़͠
  22. UIView *view = [UIView new]; view.frame = CGRectMake(20, 80, 280,

    80); ! CGFloat x = view.frame.origin.x; CGFloat width = view.frame.size.width; var view = UIView.new(); view.frame = {x: 20, y: 80, width: 280, height: 80}; ! var x = view.frame.x; var width = view.frame.width; ߏ଄ମ
  23. var MainViewController = JSB.defineClass('MainViewController : UITableViewController', { // Instance Method

    Definitions viewDidLoad: function() { self.navigationItem.title = 'UICatalog'; }, viewWillAppear: function(animated) { self.tableView.reloadData(); } }, { // Class Method Definitions attemptRotationToDeviceOrientation: function() { ... } });
  24. Class cls = objc_allocateClassPair(NSClassFromString(parentClassName), className.UTF8String, 0); objc_registerClassPair(cls); ! Class superClass

    = class_getSuperclass(cls); if (superClass) { setupForwardingImplementations(cls, superClass, instanceMembers, staticMembers); } ! NSString *types; BOOL result; ! Class metaClass = objc_getMetaClass(className.UTF8String); ! types = [NSString stringWithFormat: @"%s%s%s%s", @encode(NSMethodSignature), @encode(id), @encode(SEL), @encode(SEL)]; result = class_addMethod(cls, @selector(methodSignatureForSelector:), (IMP)methodSignatureForSelector, types.UTF8String); result = class_addMethod(metaClass, @selector(methodSignatureForSelector:), (IMP)methodSignatureForSelector, types.UTF8String); ! types = [NSString stringWithFormat: @"%s%s%s%s", @encode(void), @encode(id), @encode(SEL), @encode(NSInvocation)]; result = class_addMethod(cls, @selector(forwardInvocation:), (IMP)forwardInvocation, types.UTF8String); result = class_addMethod(metaClass, @selector(forwardInvocation:), (IMP)forwardInvocation, types.UTF8String); ! types = [NSString stringWithFormat: @"%s%s%s%s", @encode(BOOL), @encode(id), @encode(SEL), @encode(SEL)]; result = class_addMethod(cls, @selector(respondsToSelector:), (IMP)respondsToSelector, types.UTF8String); result = class_addMethod(metaClass, @selector(respondsToSelector:), (IMP)respondsToSelector, types.UTF8String); ! for (NSString *protocol in [protocols componentsSeparatedByString:@","]) { class_addProtocol(cls, NSProtocolFromString([protocol stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]])); } ! class_addProtocol(cls, @protocol(JSBNSObject)); ! NSString *key = mangledNameFromClass(cls); globalContext[key] = cls; globalContext[key][JSBInstanceMembersKey] = instanceMembers; globalContext[key][JSBStaticMembersKey] = staticMembers; ! return cls;
  25. NSString *types; BOOL result; ! types = [NSString stringWithFormat: @“%s%s%s%s",

    @encode(NSMethodSignature), @encode(id), @encode(SEL), @encode(SEL)]; ! result = class_addMethod(cls, @selector(methodSignatureForSelector:), (IMP)methodSignatureForSelector, types.UTF8String); ΫϥεʹϝιουΛ௥Ճ ʢΠϯελϯεϝιουʣ
  26. NSMethodSignature *methodSignatureForSelector(id self, SEL _cmd, SEL selector) { NSMethodSignature *methodSignature

    = nil; Class cls = object_getClass(self); if (class_isMetaClass(cls)) { methodSignature = [cls instanceMethodSignatureForSelector:selector]; if (methodSignature) { return methodSignature; } } else { methodSignature = [cls instanceMethodSignatureForSelector:selector]; if (methodSignature) { return methodSignature; } } NSUInteger numberOfArguments = [[NSStringFromSelector(selector) componentsSeparatedByString:@":"] count] - 1; return [NSMethodSignature signatureWithObjCTypes:[[@"@@:" stringByPaddingToLength:numberOfArguments + 3 withString:@"@" startingAtIndex:0] UTF8String]]; } ϝιουݺͼग़͠Λసૹ
  27. BOOL respondsToSelector(id self, SEL _cmd, SEL selector) { NSString *propertyName

    = propertyNameFromSelector(selector); JSValue *function = propertyForObject(self, propertyName); return !function.isUndefined; } ϝιουݺͼग़͠Λసૹ
  28. void forwardInvocation(id self, SEL _cmd, NSInvocation *invocation) { JSContext *context

    = [JSBScriptingSupport globalContext]; if ([[self superclass] instancesRespondToSelector:invocation.selector]) { invokeSuper(invocation); } id currentSelf = context[@"self"]; context[@"self"] = self; NSString *propertyName = propertyNameFromSelector(invocation.selector); JSValue *function = propertyForObject(self, propertyName); if (!function.isUndefined) { NSArray *arguments = extractArguments(invocation); JSValue *returnValue = [function callWithArguments:arguments]; setReturnValue(returnValue, invocation); } context[@"self"] = currentSelf; } ϝιουݺͼग़͠Λసૹ
  29. NSString *types; BOOL result; ! Class metaClass = objc_getMetaClass(className.UTF8String); !

    types = [NSString stringWithFormat: @"%s%s%s%s", @encode(NSMethodSignature), @encode(id), @encode(SEL), @encode(SEL)]; ! result = class_addMethod(metaClass, @selector(methodSignatureForSelector:), (IMP)methodSignatureForSelector, types.UTF8String); ΫϥεʹϝιουΛ௥Ճ ʢΫϥεϝιουʣ
  30. JSValue *propertyForObject(id obj, NSString *propertyName) { JSContext *context = [JSBScriptingSupport

    globalContext]; JSValue *properties = nil; Class cls = object_getClass(obj); if (class_isMetaClass(cls)) { properties = context[mangledNameFromClass(obj)][JSBStaticMembersKey]; } else { properties = context[mangledNameFromClass(cls)][JSBInstanceMembersKey]; } return properties[propertyName]; } JEܕͷΦϒδΣΫτ͕ ΫϥεΦϒδΣΫτ͔Ͳ͏͔
  31. unsigned int numberOfInstanceMethods = 0; Method *instanceMethods = class_copyMethodList(cls, &numberOfInstanceMethods);

    for (unsigned int i = 0; i < numberOfInstanceMethods; i++) { Method method = instanceMethods[i]; struct objc_method_description *description = method_getDescription(method); NSString *propertyName = propertyNameFromSelector(description->name); JSValue *function = instanceFunctions[propertyName]; if (!function.isUndefined) { class_addMethod(targetClass, description->name, _objc_msgForward, description->types); } } if (instanceMethods) { free(instanceMethods); } ΫϥεʹϝιουΛ௥Ճ ʢΦʔόʔϥΠυʣ
  32. void forwardInvocation(id self, SEL _cmd, NSInvocation *invocation) { JSContext *context

    = [JSBScriptingSupport globalContext]; if ([[self superclass] instancesRespondToSelector:invocation.selector]) { invokeSuper(invocation); } id currentSelf = context[@"self"]; context[@"self"] = self; NSString *propertyName = propertyNameFromSelector(invocation.selector); JSValue *function = propertyForObject(self, propertyName); if (!function.isUndefined) { NSArray *arguments = extractArguments(invocation); JSValue *returnValue = [function callWithArguments:arguments]; setReturnValue(returnValue, invocation); } context[@"self"] = currentSelf; } ʢͳΜͱͳ͘ʣ+4ͱ0CK$͕ର࿩Ͱ͖Δ
  33. var ButtonsViewController = JSB.require('buttonsViewController'); var ControlsViewController = JSB.require('controlsViewController'); var WebViewController

    = JSB.require('webViewController'); var MapViewController = JSB.require('mapViewController'); ! var MainViewController = JSB.defineClass('MainViewController : UITableViewController', { viewDidLoad: function() { self.navigationItem.title = 'UICatalog'; ... }); JSB.exports = MainViewController;
  34. w 8FCΤϯδχΞ w J04ΤϯδχΞ w σβΠφʔ ! w ϢϏϨδΛࢧ͑Δٕज़ w

    IUUQBLJTVUFDPNCMPHQPTUIUNM w ब׆೔ه  ϢϏϨδ๚໰ w IUUQMBJTPIBUFOBCMPHDPNFOUSZ&##&###&"&"@ @&"&&"$&#&"""&'