Вот люди спрашивали, как St может использовать софт на Java. Как оказалось, есть несколько пакетов. Для VW это JavaConnect и JNIPort for VW. Для Dolphin это исходный вариант всё того же JNIPort.
Иногда, при разработке приложения можно столкнуться с тем, что UI среды перестаёт отвечать. Добиться этого достаточно просто. Откройте Workspace и выполните такой код:
[true] whileTrue: [].Система заблокирована и может быть не понятно, что делать в такой ситуации. Выходов может быть несколько.
Во-первых, можно нажать комбинацию <Ctrl>-\. При этом откроется "Process Monitor" и все пользовательские процессы будут приостановлены.
Во-вторых, если я правильно помню, то монитор процессов, это относительно новая штука. В VW с давних времён существовала возможность открыть отладчик на текущем процессе. Вызывается отладчик комбинацией <Ctrl>-Y. В зависимости от того, какое окно было активно в момент нажатия <Ctrl>-Y, на том процессе и будет активирован отладчик.
Эти две комбинации позволяют легко разобраться с простыми ситуациями. Например, с циклическим распространением событий между слушателями. И монитор и отладчик особенно полезны, если учесть, что из-за особенностей устройства VW ждать переполнения стека (и, как следствие, прерывания процесса) можно очень долго.
Кстати, в ситуации с зацикливанием так же очень помогает инструмент StackOverflow.
Если же отладчик/монитор не помогают или не доступны, то есть последний довод программиста - "Emergency Evaluator". Вызывается он по комбинации <Ctrl>-<Shift>-Y. В открывшемся окне можно выполнять ST-код. Набираете выражение и по нажатию <Esc> оно выполняется. Например, выражение
ObjectMemory quitпросто завершит работу системы. Если же вы хотите сохранить образ перед выходом, то можно воспользоваться выражением
ObjectMemory saveAs: 'filename' thenQuit: true.
Ярлыки: vw
Доступна для загрузки свежая некоммерческая версия Cincom Smalltalk. В релиз, как обычно, входят 2 диалекта - VisualWorks 7.6 и ObjectStudio 7.1.3. ObjectStudio 8.1 - win-only диалект на виртуальной машине (ВМ) от VW задерживается, так как проходит сертификацию под Vista.
Из нововведений: Seaside для VW теперь полностью поддерживаемый Cincom, поддержка Vista (например, исполняемые файлы ВМ подписаны цифровой подписью), улучшения в поддержке Mac OS X, улучшения в библиотеке классов (например, прерывание процесса по #terminate вызывает раскрутку стека и корректное выполнение блоков #ensure:, новые примитивы - 1700, 1701, 1702, 1747 - для расчета хеш-кода у стандартных объектов, у блоков теперь есть новый набор методов - #cull:, а так же добавилась возможность ограничить время выполнения блока при помощи #valueWithinSecond:orDo: и пр.), улучшения в GUI (помимо расширения API, уменьшено раздражающее мерцание), улучшения в инструментарии разработчика и, естественно, исправления ошибок.
Ярлыки: new version, vw
Г-н Holger Kleinsorgen создал on-line приложение - Storelight - для поиска по публичному StORE-репозиторию.
Написано на Seaside. Поисковый движок Apache Lucene.
Сырцы Storelight можно найти в публичном репозитории используя сам Storelight.
Ярлыки: vw
Оказывается, WithStyle свернул деятельность и открыл исходники одноименного продукта под лицензией MIT.
WithStyle это визуальный XML редактор, плюс, благодаря поддержке CSS, компонент для создания GUI. Используется в BottomFeeder для отрисовки html сообщений, но делает это "так себе" из-за того, что ему нужен правильный xhtml, а с этим в Интернете плохо.
Можно добавить, что описывать интерфейсы на XML можно и в Squeak, используя язык XUL.
Ярлыки: vw
Доступна для загрузки некоммерческая версия Cincom Smalltalk Весна 2007. В релиз, как обычно, входят 2 диалекта - VisualWorks 7.5 и ObjectStudio 7.1.2.
Из нововведений:
Фреймворк Widgetry (раннее называемый Pollock) по прежнему разрабатывается - в дистрибутиве находится Feature Set 3.
Предполагается, что ObjectStudio 7 - это последняя версия OS со своей собственной ВМ. В будущем, этот win-only диалект будет поставляться как плагин к VW. Бета версия OS 8 доступна для загрузки отдельно (40 Mb).
Ярлыки: vw
Перечень фиксов в 7.4d:
ВМ доступна для следующих платформ: AIX 4.x; HPUX 11; Linux x86 (64), PPC, SPARC; MacOS (x86); IRIX; Solaris 2.x, 3.x; Solaris 64bit; WinCE ARM, x86; Win32.
Ярлыки: vw
Выпущены пофикшенные версии виртуальной машины - 7.4c. ВМ 7.4c совместима с образами 7.х и рекомендуется к использованию.
ВМ доступна для следующих платформ: AIX 4.x; HPUX 11; Linux x86 (64), PPC, SPARC; MacOS (x86); IRIX; Solaris 2.x, 3.x; Solaris 64bit; WinCE ARM, x86; Win32.
Ярлыки: vw
Несколько раз в форумах посвященным разным языкам я видел вопросы о наличии метода "super super", который позволил бы вызвать метод не в родительском классе, а у родителя родителя - "дедушки". Не будем рассказывать о том, что такое желание свидетельствует о необходимости рефакторинга, а остановимся на интересном техническом моменте озвученном Элиотом Мирандой в c.l.s.
Оказывается, что в VW есть 2 способа добится желаемого эфекта. Они, естественно "не документированные" и не должны использоваться в "обычном" коде. Первый способ - использование специализированной версии #perform::
perform: selector withArguments: anArray startingAbove: aBehavior "Отправляет получателю сообщение, начинает поиск метода с суперкласса aBehavior. selector - селектор сообщения, anArray - аргументы сообщения. Если такой селектор не найден, то вызывает messageNotUnderstood:. aBehavior. Если aBehavior не (супер)класс объекта, или размер anArray не соответсвует количеству параметров искомого метода, то происходит ошибка примитива" <primitive: 515 errorCode: ec> ^self primitiveFailed
Второй способ потребует изменения синтаксиса и, естественно, компилятора. Байткод VW для 'super' принимает в качестве параметра класс, с которого починается поиск метода. Например код
super printOn: aStreamобычно компилируется в
1 |44| push self 2 |10| push local 0 3 |1C| push {TheClassInWhichThisMethodIsDefined} 4 |F2 21| super send printOn:Таким образом, используя этот байткод можно ввести, например, такой синтаксис:
self.ClassName fooОбратите внимание, что, в отличии от примитива, байткод не проверяет, что такой класс, является суперклассом для текущего класса. Это обычно не проблема, так как в обычной ситуации в байткод проставляется класс, в котором метод и находится. Однако, если вы сами будете генерировать эти байт-коды и не обеспечите необходимой проверки. то ВМ упадёт. Для изменения компилятора можете воспользоваться подсказками из статьи "Путеводитель хич-хайкера по компилятору в Smalltalk-е".
Ярлыки: vw
Запущен (VW) Smalltalk Cookbook. Надеюсь содержимого будет становиться всё больше и больше.
Ярлыки: vw
В публичном репозитории выложен пакет "Show C like Source". Этот пакет позволяет отображать уже написанный ST-код в синтаксисе С. Например, код:
cstDelete "delete Item on server" | destination | destination := self getDeleteURL. self validatePost ifFalse: [^self message: (UserMessage defaultString: 'You did not enter a username and/or password' key: #postingToolUserValidationMessage2) asString]. ^self postDeleteTo: destinationбудет отображен как:
ANY cstDelete (void) /*delete Item on server*/ { ANY destination; destination = self -> getDeleteURL(); self -> validatePost() -> ifFalse:(((void) { return self -> message:(UserMessage -> defaultString:key:("You did not enter a username and/or password" ,S"postingToolUserValidationMessage2") -> asString())}); return self -> postDeleteTo:(destination)}
Хотя применимость отображения в С-код может вызывать сомнения, но в общем случае отображение кода в некотором другом виде может быть полезно. Так в Squeak уже давно существует возможность менять вид ST-кода. Там помимо традиционного синтаксиса, можно смотреть код в некоем "новом" синтаксисе и в полуграфическом виде.
Ярлыки: vw
Некоммерческие версии VisualWorks Smalltalk 7.4 и ObjectStudio 7.1 доступны для скачивания. Напомню, что от коммерческого варианта некоммерческий отличается только лицензией и уровнем поддержки.
Среди новшеств в VW 7.4:
Ярлыки: vw
Если в MS Windows стоит русский язык ввода по умолчанию, то в VW поначалу будут отражаться только чёрные квадратики вместо букв. Если же переключится на любой другой язык, а потом назад на русский, то всё начнёт работать просто прекрасно. Что бы всё работало без проблем с самого начала, нужно исправить в классе WindowsInputManager метод стороны класса #initializeEncoder, как указано ниже:
initializeEncoder Encoder := CharacterEncoderPool.MS_CP_1251
Ярлыки: vw
Пакет "Levenshtein Distance" содержит реализацию алгоритма вычисления расстояния Левенштейна для VW7. Данная реализация работает с SequencableCollection и позволяет:
Пример использования:
'йцукен' distanceTo: 'йцкн' 2
Реализацию вычисления расстояния Левенштейна для Squeak можно найти в HTTP репозитории Monticello. Последняя, на момент написания заметки, версия - Fuzz-avi.9.mcz.
Подробнее об алгоритмах нечеткого поиска можно почитать на сайте itman.narod.ru.
Ярлыки: vw
В своей презентации на Smalltalk Solutions 2003 Дэвид Бак сравнил скорость работы программы ElastoLab на C++ и Smalltalk. ElastoLab это программа для симуляции физики для детей. Изначально она полностью была написана на C++, затем на Smalltalk был переписан пользовательский интерфейс, а затем на Smalltalk (VW) переползла и физика.
То что физика была как на C++ так и на VW ST даёт прекрасную возможность сравнить быстродействие на более менее реальной вычислительной задаче. Время симуляции (в милисекундах):
C++ 6 6 6 17 17 11 11 11 11 11 11 11 11 11 11 11 VWST 45 20 22 63 64 42 45 42 41 42 42 42 42 42 45 42
Итого: на вычислениях в среднем VW медленнее С++ в 3.9 раза.
Ярлыки: vw
Доступна для загрузки некоммерческая версия Cincom Smalltalk 2005 Summer Edition. В поставку входят VisualWorks 7.3.1 и ObjectStudio 7.0.1.
VisualWorks 7.3.1 включает в себя ряд исправлений и мелких дополнений к VW 7.3. Среди них:
Ярлыки: vw
При работе с внешними, по отношению к системе, ресурсами (например, сокеты) освобождать эти ресурсы приходится вручную. Однако, даже при работе с памятью могут существовать ситуации, когда потребуется самостоятельно очищать некоторые ссылки, чтобы избежать утечек памяти. Например, рассмотрим задачи, которые требуют присоединения произвольных атрибутов к произвольным объектам без добавления переменных объекта.
Самый распространённый пример такой задачи - хранение подписчиков на события объектов. Первоначально в ST использовалась очень простая схема, когда для добавления подписчиков используется сообщение #addDependent: с аргументом - подписчиком на события. Сейчас схема усложнилась - появилась возможность подписываться не на все события подряд, а только на те, которые интересуют, но принципиально ничего не поменялось - есть объект публикующий события и объекты-подписчики обрабатывающие события. Первое, что приходит в голову - завести переменную для хранения коллекции объектов-подписчиков. С этой схемой особых проблем нет. Коллекция объектов умрёт вместе с объектом публикующим события, а значит объекты-подписчики не будут удерживаться дольше чем нужно. Именно так и устроен класс Model. Однако, в ST подписаться на некие события можно не только у модели, а у любого объекта (простейший пример - WeakArray уведомляет своих подписчиков о смерти хранимых в нём объектов). Тут можно либо попробовать заводить переменную объекта для подписчиков в каждом классе, который может публиковать события, либо разрешить проблему сразу для всей системы задействовав класс Object. Однако, заводить переменную объекта для хранения подписчиков в классе Object не только нецелесообразно, но и невозможно в текущих реализациях виртуальных машин (ВМ).
Раз нельзя завести переменную, то прийдётся идти другим путём. А именно, заведём глобальный словарь где ключом будет объект публикующий события, а связанным с ключом значением будет коллекция подписчиков. Такие словари, где ключ это некий объект, а значение это присоединённое к объекту свойство, зачастую называют реестрами. Решение это вроде бы универсальное, но реестр то глобальный.
Для борьбы с такими ситуациями и придумали слабые ссылки (weak references). Попробуем задействовать слабые ссылки для реализации нашего реестра. Что же получится, если для хранения ключей словаря использовать слабые ссылки?
Попытка использовать для слабые ссылки для хранения значений еще более нелепа.
Получается, что нужен некий симбиоз двух подходов.
Эфемероны это разновидность ассоциации, то есть пары ключ-значение, и слабой ссылки. Поведение эфемерона такое: если есть сильные ссылки и на сам эфемерон и есть ссылки на ключ, ведущие не из самого эфемерона, то значение эфемерона удерживается. Если есть сильные ссылки на эфемерон, но нет сильных ссылок на ключ, исключая ссылки, ведущие из самого эфемерона (например от "значения" на "ключ"), то эфемерон уведомляется об этом и может обработать эту ситуацию, например, просто освободить значение или выполнить его финализацию. Обратите внимание, что эфемерон уведомляется только в том случае, если на него самого есть сильные ссылки.
Эфемероны были придуманы Георгом Босвортом (George Bosworth) из Digitalk для Visual Smalltalk Enterpraise и описаны Барри Хэесом (см. "Ephemerons: a new finalization mechanism; Barry Hayes; Proceedings of the 1997 ACM SIGPLAN conference on Object-oriented programming systems, languages and applications, 1997, Pages 176 - 183"). В VW эфемероны реализованы Барри Хэесом и Элиотом Мирэндой.
Помимо VW существует реализация эфемеронов для Squeak (упрощенный вариант, когда все ссылки просто об-nil-яются при исчезновении ссылок на ключ); эфемероны есть в GNU Smalltalk начиная с версии 2; эфемероны используются в XEmacs; очень похожий механизм, называемый "key/value weak pointers", существует в Haskell.
Для начала напомню, что в VW могут существовать классы, имеющие различную природу. Тип класса задаётся при его создании параметром indexedType:. Допустимые значения параметра: #none, #objects, #bytes, #immediate, #weak и #ephemeron. По-умолчанию тип проставляется в #none, и, поскольку прикладным программистам нет нужды создавать классы прочих типов, то описание значения этих типов будет дано другим разом. Сейчас же остановимся на типе #ephemeron.
Класс, создаваемый с indexedType: выставленным в #ephemeron, должен содержать минимум одну переменную экземпляра. Допустимо, что это переменная унаследованная от какого либо родительского класса. Такой класс должен быть унаследован от класса с типом #none и подклассы должны быть только типа #ephemeron.
Самая первая переменная экземпляра по-особому обрабатывается сборщиком мусора. Именно она и считается ключем из описания выше. То есть, если на объект в первой переменной экземпляра нет ссылок, то все прочие переменные эфемерона сборщик мусора не трассирует. Это позволяет обнаружить изолированные графы объектов. То есть графы объектов, в которых корнями являются только эфемероны. После нахождения всех таких эфемеронов сборщик мусора ложит их все в очередь для финализации, а затем трассирует все поля эфемеронов, что бы объекты в эфемеронах оставались живы.
До введения эфемеронов финализация в VW была основана на WeakArray. После очистки хоть одной ячейки в WeakArray этот массив получал уведомление и имел возможность обнаружить индексы удалённых объектов. Так как уведомление отсылалось после очистки ссылки, то есть после фактического уничтожения объектов сборщиком мусора, то данные для финализаци, такие как, файловые дескрипторы, нужно было сохранить отдельно. Подобная схема была введена, чтобы исключить "оживление" объекта во время финализации.
Эфемероны в VW поддерживают пообъектную финализацию. После того, как сборщик мусора находит живые эфемероны у которых на ключ (первую по порядку переменную) нет больше ссылок, этим эфемеронам посылается сообщение #mourn. Если на эфемерон нет ссылок, то сообщение #mourn ему не посылается. В VW уже существуют стандартные реализации классов с "indexedType: #ephemeron", которые содержат ряд методов управляющих финализацией. Это WeakKeyAssociation и его потомок класс Ephemeron.
Объекты класса WeakKeyAssociation после выполнения метода #mourn начинают возвращать false на сообщение #isActiveEphemeron. Если такому объекту послать сообщение #isActiveEphemeron: true, то процедура финализации может быть запущена повторно. Метод #mourn в классе WeakKeyAssociation просто проставляет nil в полях "ключ" и "значение".
Поведение потомка WeakKeyAssociation, класса Ephemeron, более сложное. Оно позволяет, кроме выполнения действий по очистке полей эфемерона, освободить так же и сам эфемерон. Одна из переменных экземпляра в классе Ephemeron - manager. По умолчанию, метод #mourn просто посылает менеджеру сообщение #mournKeyOf: self. Класс EphemeronDictionary это словарь, в котором в качестве ассоциаций используются эфемероны. При создании ассоциации в EphemeronDictionary, словарь создаёт эфемерон в котором менеджер это сам словарь. Таким образом, словарь может получить уведомление о том, что его эфемерон готов "умереть" через сообщение #mournKeyOf:. Сам словарь так же позволяет изменить менеджер у всех своих эфемеронов через сообщение #manager:. По-умолчанию, словарь просто удаляет ссылку на "умерший" эфемерон, позволяя сборщику мусора уничтожить граф объектов с эфемероном во главе. И так, чтобы обработать уведомление о том, что на ключ эфемерона больше нет ссылок есть несколько вариантов:
Ярлыки: vw
StorePlugins добавляет возможность получать уведомление о фактах загрузки/публикации пакетов в Store или выполнять определённые действия перед загрузкой/публикацией.
Сконфигурировать плагины можно через закладку 'Store Plugins', которая появляется если выбрать пакет в RB. Обратите внимание, что использовать это расширение можно только с той версией VW, которая указана в заголовке StorePlugins. На момент написания заметки в репозитории находилась версия предназначенная для работы с VW 7.2.1.
В состав StorePlugins уже включены несколько плагинов:
StorePlugins доступен в открытом репозитории.
Ярлыки: vw
Как известно, в Smalltalk объекты могут обрабатывать "нестандартые" сообщения - при получении сообщения для обработки которого нет метода будет вызван метод #doesNotUnderstand: (кратко - DNU). Эта способность часто используется для создания разного рода "прозрачных" прокси - для пересылки сообщений удалённым объектам, для ленивой загрузки объектов из БД, или для организации делегирования.
Еще одним вариантом использования перехвата неизвестных сообщений является создание "обучаемых" объектов. Например, объект может перехватывать сообщения с одним параметром, сохранять параметр и, затем, возвращать значение параметра в ответ на соответсвующее унарное сообщение. То есть, при получении сообщения parametr: anObject этот самый anObject будет сохранён в словаре в объекте и будет возвращен в ответ на сообщение parametr.
Класс Teachable является более продвинутой реализацией идеи обучаемых объектов. Его можно обучить отвечать на любое сообщение.
Пример обучения:
|teachable| teachable := Teachable new. teachable whenSend: #help return: 'ok'; whenSend: #doit evaluate: [1 inspect]; acceptSend: #noDebugger; whenSend: #negate: evaluate: [:num | num negated].После обучения объект готов к использованию:
teachable help. вернёт строку 'ok' teachable doit. откроет в инспекторе число 1 teachable noDebugger. Сообщение #noDebugger является допустимым, а его результат - сам обучаемый объект teachable negate: 120 вернёт число -120
Обучаемые объекты можно применять для создания моков для тестов.
Для реализации такого поведения нужно всего 5 методов. Три обучающих:
whenSend: aSymbol evaluate: aBlock При получении сообщения с данным селектором выполнить блок с параметрами полученного сообщения self learnings at: aSymbol put: aBlock whenSend: aSymbol return: anObject В ответ на сообщение с данным селектором вернуть указанный объект self learnings at: aSymbol put: (#return -> anObject) acceptSend: aSymbol Сообщение с данным селектором является допустимым self whenSend: aSymbol return: selfДля ответа на "обученные" сообщения используется DNU:
learnings Возвращает словарь для хранения данных об обучении ^learnings ifNil: [learnings := Dictionary new] doesNotUnderstand: aMessage Если объект не обучен такому сообщению, то будет вызван стандартный метод DNU | learning | learning := self learnings at: aMessage selector ifAbsent:[ ^super doesNotUnderstand: aMessage ]. ^learning class == Association ifTrue: [learning value] ifFalse: [learning valueWithArguments: aMessage arguments]Всё!
Версия для VW находится в публичном Store-репозитории, и состоит из двух пакетов Teachable и Teachable Tests. Версия для Squeak находится в SqueakMap.
Ярлыки: vw
При разработке фреймворков зачастую требуется отобрать определённые методы для обработки или использования. Простейщий пример это SUnit. SUnit считает методы начинающиеся с "test" методами-тестами, которые нужно выполнить при тестировании. Smalltalk обладает огромными возможностями рефлексии. По этому, получая информацию о программе, можно решить данную задачу, кодируя метаинформацию в именах методов, как это делает SUnit, или более продвинутыми способами. Однако, при наличии потребности, более удобно иметь обобщенное средство, а не изобретать каждый раз очередной "велосипед". В VisualWorks таким средством является механизм прагм.
Прагмы (pragmas) служат для добавления дополнительной информации к методу. API позволяет запросить объекты представляющие собой прагмы определённого метода, найти методы содержащие прагмы с некоторым именем - селектором, отслеживать добавление/удаление таких методов. Подобные средства вы можете найти и в других языках/платформах, например, в Java (начиная с релиза 5) и в .Net. В Java это аннотации [методов], в .Net - атрибуты [методов]. Не обделены и другие диалекты ST. Для подобных целей существует механизм, называемый так-же - прагмы, в IBM Smalltalk/Visual Age. Василий Байков (Vassili Bykov) так же реализовал прагмы а-ля VW для Squeak (реализация доступна по запросу). Аннотации с синтаксисом а-ля VW широко используются в Tweak для объявления обработчиков событий. Аналог прагм - "аннотации" - существует и в Gnu Smalltalk начиная с версии 2.1.
Историю возникновения прагм можно начать со Smalltalk-80 (а может и с более ранних времён). Тогда существовала специальная конструкция для вызова примитивов - методов реализованных в виртуальной машине (ВМ). Конструкция имела вид:
<primitive: N>N - номер примитива в ВМ. В ObjectWorks 2.5 этот синтаксис был расширен:
<primitive: N errorCode: errorCodeTempVar>Называлась эта конструкция именно прагма, но к обсуждаемым в статье прагмам не имеет прямого отношения. Компилятор просто генерировал специальный байт-код для вызова примитива, но ни о какой метаинформации речи не шло.
В конце 1989 г. вышел Smalltalk-80 version 2.5, содержавший среди нововведений C programmer's Object Kit (CPOK, сейчас называется DLLCC). Для определения функций и типов данных на С там использовался (и сейчас используется) прагмаподобный синтаксис:
<C: RETCODE SQLAllocConnect(HENV henv, HDBC * phdbc)>Для компиляции подобных выражений требуется компилятор, содержащийся в парселе DLLCC.
В VisualWorks 1.0 (1992г.) появилась еще одна прагма для пометки методов, в которых хранились ресурсы - спецификации GUI и меню:
<resource: #symbol>Где #symbol это #canvas, #image и т.д. В данное время это "обычная" прагма.
С необходимостью искать определённые методы столкнулись программисты и при разработке исключений. Исключения появились в начале 1989г. в Smalltalk-80 version 2.4. При возникновении исключения необходимо найти контекст активации содержащий обработчик. В далёком 1989г. проблема была решена просто - при добавлении метода в словарь методов класса вызывался хук #validateMethod:forSelector: (существующий в VW и до ныне). Если добавлялся метод с определённым именем, например, #valueNowOrOnUnwindDo:, то он помечался соответсвующей пометкой. Чтобы не привязыватся к именам селекторов Элиот Миренда (Eliot Miranda) предложил использовать для этих целей новую прагму:
<exception: #unwind>А Стив Дэхл (Steve Dahl) обобщил этот механизм. Теперь любые сообщения с аргументами-литералами сохраняются в методах, а подобные методы можно найти. Если вам интересно как это устроено "внизу", то посмотрите классы AnnotatedMethod и его подкласс MarkedMethod.
Уже в VisualWorks 3.0 (начало 1998г.) прагмы использовались во всю. Например, меню там компоновались "на лету" из методов содержащих специальную прагму. При добавлении или удалениии метода с прагмой соответсвующий пункт тут же появлялся или исчезал в меню.
Для того, что бы использовать прагмы список допустимых прагм желательно объявить явно. Для этого используется метод на стороне класса, возвращающий коллекцию допустимых селекторов прагм. Этот метод должен быть аннотирован прагмой #pragmas:, которая указывает область применения данных прагм - на стороне класса или на стороне экземпляра. Для указания, что объявляемые прагмы используются на стороне класса служит аргумент #class, на стороне экземпляра - #instance. Пример:
BlockClosure class>>exceptionPragmas <pragmas: #instance> ^#(#exception:)Если прагмы могут использоваться и в коде на стороне класса и на стороне экземпляра, то нужно аннотировать метод двумя прагмами. Например:
Object class>>resourceMethodPragmas <pragmas: #instance> <pragmas: #class> ^#(#resource:)При компиляции метода с необъявленной прагмой будет выведено предупреждение (которое можно проигнорировать).
Прагмы, используемые в методах, представлены экземплярами класса Pragma. Для поиска экземпляров прагм используются методы на стороне класса Pragma в протоколе 'finding'. Метод с селектором #allInMethod: возвращает коллекцию экземпляров прагм определённых в данном методе (объекте класса CompiledMethod). Существует и ряд методов позволяющих найти аннотации в иерархиях классов. Например, метод с селектором allNamed: aSymbol in: aClass возвращает все экземпляры прагм с указанным селектором в одном определённом классе. Метод с селектором allNamed: aSymbol from: subClass to: superClass возвращает коллекцию экземпляров прагм найденных в методах, которые определены в иерархии классов от подкласса subClass до базового класса superClass.
Для работы с экземплярами прагм используется ряд сообщений. Для получения колличества аргументов используется сообщение #numArgs; argumentAt: anInteger возвращает аргумент прагмы с порядковым номером anInteger; в ответ на сообщение #arguments экземпляр прагмы возвращает коллекцию аргументов; сообщение #keyword служит для получения селектора прагмы; сообщение #message, посланное экземпляру прагмы, вернёт объект класса Message с селектором прагмы и аргументом прагмы.
Экземпляр прагмы также позволяет получить аргументы не через индекс, а напрямую. Для этого служит сообщение #withArgumentsDo:. Это сообщение нужно послать экземпляру прагмы с одним параметром - блоком. Этот блок будет вызван с аргументами прагмы, соответсвенно, колличество параметров блока и агрументов прагмы должны совпадать. Пример:
VisualLauncherToolDock class>>componentSpecCollection | specs | specs := OrderedCollection new. (Pragma allNamed: #component:class:spec: in: self sortedByArgument: 1) do: [:pragma | | spec | spec := SubCanvasSpec new. pragma withArgumentsDo: [:order :classBinding :specSelector | spec clientKey: pragma selector; majorKey: classBinding; minorKey: specSelector]. specs add: spec]. specs isEmpty ifFalse: [self setLayoutsIn: specs]. ^SpecCollection new collection: specsВначале отбираются экземпляры прагмы с селектором #component:class:spec: (три параметра) при помощи сообщения #allNamed:in:, а, затем, аргументы найденных прагм передаются в блок, имеющий три параметра:
[:order :classBinding :specSelector | spec clientKey: pragma selector; majorKey: classBinding; minorKey: specSelector].
Сейчас прагмы применяются в VW очень широко - начиная от банальных определений меню и до довольно оригинальных примеров, позволяющих тестировать методы в момент компиляции. В нашей более ранней статье, посвящённой фреймворку для пользовательских установок, уже рассматривалось одно применение прагм, сегодня же мы остановимся на другом примере.
Василий Байков (Vassili Bykov) разработал набор переиспользуемых инструментов. При компоновке различных модулей, что бы избежать написания большого числа методов, которые просто делегируют выполнение другим объектам, используется механизм делегирования построенный на прагмах. Этот механизм определён в классе CompositeToolModule от которого наследуются композитные инструменты.
Механизм очень простой. Для задания делегируемых сообщений используется ряд прагм: #delegate:, #delegate:as:, #relay, #relay:as:. Первым аргументом прагм явлется селектор пересылаемого сообщения. Второй аргумент as: используется если пересылаемое сообщение нужно "переименовать". Этими прагмами могут быть помечены унарные методы (методы без параметров). Сообщения, попадающие в #doesNotUnderstend: пересылаются объекту, который будет возвращён помеченным унарным методом. Таким образом можно пересылать различные сообщения разным объектам. Прагмы #forward:* отличаются от прагм #relay:* только тем, какой результат будет возвращен после пересылки. Так, использование прагмы #forward: приведёт к пересылке сообщения новому получателю и к возврату результата "нового" сообщения, а при использовании прагмы #relay: после пересылки сообщения новому получателю результатом возврата будет исходный (делегирующий) объект.
Примеры таких объявлений можно посмотреть в подклассах класса CompositeToolModule. Вот одно из объявлений:
itemModule <relay: #itemDisplayStringSelector: as: #displayStringSelector:> <relay: #itemDisplayStringBlock: as: #displayStringBlock:> <relay: #itemIconSelector: as: #iconSelector:> <relay: #itemIconBlock: as: #iconBlock:> <delegate: #selection> <delegate: #selectionHolder> ^itemModule
Ярлыки: vw
Для получения локального времени из GMT, по умолчанию, VW использует не средства операционной системы, а собственный класс TimeZone. Как результат - необходимость установки временной зоны в образе VW.
Для установки нужной временной зоны нужно зайти в пункт меню "System->Settings->Time Zones", выбрать код, задающий нужную зону, и выполнить его. После этого вы получите образ, корректно выдающий время для той временной зоны, которую вы установили. Но, если сменится временная зона в операционной системе, то VW это "проигнорирует". То есть, если программа распространяется в нескольких временных зонах, вам прийдётся вделать свой интерфейс для установки нужной зоны.
Для автоматизации установки временной зоны и был создан пакет OSTimeZone. Первоначально он просто создавал объект класса TimeZone по информации из операционной системы. В последних же версиях пакета содержится более правильное решение этой проблемы. Там добавленна временная зона, которая обращается к ОС для получения локального времени. То есть, после установки этой временной зоны в качестве текущей, в большинстве случаев, не нужно заботится о том, совпадает ли зона в VW с зоной установленной в ОС.
Пакет OSTimeZone поставляется в дистрибутиве с VW. Там, однако, содержится устаревшая версия пакета. Загрузить последнюю версию пакета необходимо из публичного репозитория. Перед загрузкой пакета OSTimeZone не забудьте загрузить парсел DLLCC. После загрузки необходимо выполнить в Workspace команду 'OSSystemSupportTimeZone use' и проверить результат с помощью 'Time now'.
OSTimeZone работает, как минимум, в Windows и Linux.
Ярлыки: vw
Ярлыки: vw
Для VisualWorks существует открытый Store-репозиторий, содержащий ряд полезных пакетов. Некоторые из них мы рассматривали раннее. Сегодня же остановимся еще на двух пакетах.
Первоначально пакет первоначально был разработан для Squeak. И позже портрован на VW.
После загрузки пакета порождается процес с высоким приоритетом. Этот процес постоянно отслеживает глубину стека каждого процесса и если превышена определённая глубина (вероятно зацикливание), то процесс останавливается и открывается в отладчике, что позволяет обнаружить причину роста стека.
По умолчанию, порог, после которого открывается отладчик, установлен в 100'000 вызовов. См. метод класса Process class>>stackOverflowLimit
Порог для срабатывания можно установить для каждого просесса отдельно. Для этого нужно послать процессу сообщение #stackOverflowLimit: с аргументом - порогом срабатывания.
Что-бы проверить работу утилиты создайте класс Test с одним методом:
rec self rec
И затем в workspace выполните команду Test new rec
Первоначально это расширение так же было разработано для Squeak.
После загрузки этого пакета у вас появится возможность запускать операции, ограничивая время их выполнения.
Для того, что-бы ограничить время работы блока, нужно послать этому блоку сообщение #valueWithin:onTimeout:. Параметр valueWithin: задаёт время работы блока-получателя сообщения. Это объект класса Squeak.Duration (этот класс определён в пакете Squeak-Chronos). Параметр onTimeout: это блок, который запускается на выполнение при достижении тайм-аута. Если тайм-аут не достигнут, то метод возвращает результат работы блока-получателя, если достигнут - результат работы блока onTimeout:.
Рассмотрим примеры:
[ 50000 factorial ] valueWithin: 1 second onTimeout: [ 666 ]. возвращает 666 [ 3 + 4 ] valueWithin: 1 second onTimeout: [ 666 ]. возвращает 7
Советую обратить внимание на эти пакеты, так как каждый из них состоит всего из пары методов, отлично демонстрируя возможности расширения Smalltalk-а
Ярлыки: vw
Ярлыки: vw