Вам когда-нибудь приходилось писать на хорошо знакомом языке под никогда ранее невиданную платформу? Странное ощущение. Кодабра делится опытом, как максимально быстро разобраться с незнакомым окружением и начать жить. Все знают Minecraft — кубический феномен, за считанное время выросший из инди-проекта никому не известного шведского программиста в одну из главных франшиз самой Microsoft. Мы в Кодабре очень любим Minecraft, потому что его любят дети, а значит мы можем применять его в учебных целях, используя хорошо знакомый игровой мир, встроенные механики и привычную терминологию, открывая с их помощью удивительный мир программирования. Проект MinecraftEdu специально создан для объединения процесса игры и обучения, привнося в стандартную игру новые объекты и инструменты, например — программируемых черепашек-роботов, с помощью которых удобно объяснять введение в программирование и алгоритмы.  Хотя "под капотом" у черепашек настоящая Lua, но внешний API слишком ограничен и фактически не подходит ни для чего более сложного, чем автоматизация черепашки. Это быстро наскучивает, а после освоения азов, детям хочется двигаться дальше и делать что-то более сложное и интересное. Так мы пришли к мысли попробовать использовать JavaScript из мода ScriptCraft, предлагающий полный доступ к созданию своих модов. Язык имеет достаточно низкий порог вхождения и без лишних проблем воспринимается детьми. Труднее приходится взрослым преподавателям, которые до этого работали с Node.js или программировали под веб-браузер, так как ScriptCraft предоставляет не совсем привычную JavaScript-среду, с которой необходимо разобраться до того, как садиться писать курсы и нырять с головой в пучину разработки модов. Мы обобщили свой опыт начала общения с ScriptCraft так, чтобы его можно было применить к любой другой незнакомой или неизвестной платформе с JavaScript на борту, под которую вам может потребоваться писать код. Успешно установленный мод добавляет в игру две новые команды — /js [code] для выполнения JavaScript кода из консоли и /jsp [command] для выполнения команд, заданных в плагинах через функцию command() ("jsp" здесь не имеет ничего общего с JavaServer Pages). Давайте протестируем работу мода, введя предложенную в документации команду /js 1 + 1. Результат действительно будет 2, но это еще не доказывает почти ничего. Нельзя даже понять, JavaScript ли это выполнился на самом деле? Попробуем убедиться, выполнив что-нибудь более характерное для JS, например, самовызывающуюся функцию, возвращающую ответ на главный вопрос вселенной через специфичное приведение типов. /js (function () { var a = 4, b = 2; return 'The answer is ' + a + b; }())  Теперь нет никаких сомнений, это действительно JavaScript, но какой именно? Этот вопрос прозвучит странно для большинства программистов на других языках, но, например, опытные фронтенд-разработчики хорошо знают, что JS JS-у рознь и первое, что надо сделать начиная писать код — разобраться с каким именно движком и окружением имеешь дело. Обычно, самый простой способ узнать, какой движок используется в том или ином продукте, это прочитать документацию либо исходный код к нему. Но в реальном мире это не всегда возможно — документация может отсутствовать или просто опустить такой момент, а исходный код может быть закрыт или недоступен для быстрого понимания. В таких случаях все придется выяснять вручную. К нашему счастью, мод написан на Java и доступен свободно на Github, так что можно найти, где и как движок подключается — https://git.io/vHc3g. Если вы не знакомы с Java, то понятнее из этого кода мало что станет. Но мы знаем, что здесь подключается используемый по умолчанию движок JavaScript, которым в Java является либо Rhino (в старых JDK), либо Nashorn (c Java 8). В принципе, на этом можно было бы остановиться и далее идти в Oracle зачитываться формальной документаций, но делать мы этого конечно не будем :) Во-первых это скучно, а во-вторых это нам не даст полного понимания окружения, так как мы находимся внутри Minecraft-мода, написанного взрослым ирландским мужиком, и ожидать тут можно чего угодно. Но давайте пока предположим, что исходников у нас не было и технологию в основе мы тоже не знаем, как быть тогда?
Немного теории — JavaScript это прежде всего реализация стандарта ECMAScript той или иной версии и любой движок обязан реализовать его полностью или частично. На практике этого мало и, в зависимости от задач производителя, к движку добавляются специфичные расширения и интерфейсы. Назовем это "особенностями реализации" — именно по ним можно отличить один движок от другого. Например, одной из особенностей Nashorn, является наличие у объектов метода __noSuchProperty__, используемого для описания поведения при попытке чтения отсутствующего свойства объекта. В частности, этот метод всегда есть у глобального объекта и может служить хорошим косвенным признаком движка. /js __noSuchProperty__ Для других движков особенности будут своими, к счастью выбор вариантов не так уж велик согласно Википедии — List of ECMAScript engines, а встречающихся в диких условиях движков и того меньше. Обычно выбор всегда состоит из 2-3 вариантов, не больше. Еще один достаточно эффективный способ узнать больше о движке — бросить эксепшен. /js throw 'dusk'  Из вывода уже понятно, что мы в Java, а посмотрев полный Java-эксепшен, станет понятно и все остальное. Хорошо, теперь мы сразу несколькими разными способами выяснили, кто именно выполняет наш JavaScript-код и можно двигаться дальше, прямиком в темный мир runtime-окружения. Для начала давайте проверим, не находимся ли мы в "sctrict mode", так как это может быть важно для техник, с помощью которых мы будем познавать мир. /js (function () { return !this; }()) В строгом режиме this внутри анонимной функции определен не будет. Нам повезло, мы не там, и это значительно развязывает руки. Далее нужно обязательно найти глобальный объект. Если повезет, this уже окажется им, что легко проверить следующим способом. /js this === new Function('return this')() Трюк с конструктором Function универсальный и может быть использован для получения глобального объекта даже в случае, если его имя неизвестно и прямое обращение невозможно. Нам снова повезло и мы будем использовать this для удобства записи, а так же global в остальных случаях (так как именно global является глобальным объектом в Nashorn, чего мы не знали бы, не выясни мы сначала движок). Наконец-то пришло время заглянуть в самое сокровенное любого рантайма — в глобальный объект. Помимо отличного способа снять "отпечатки", это даст нам базовые знания о том, что здесь доступно в распоряжение. Воспользуемся методом Object.getOwnPropertyNames, который возвращает массив всех имен свойств объекта, причем вне зависимости, являются ли они перечислимыми (enumerable) или нет. Этот метод доступен начиная со стандарта ECMAScript 5.1, который и реализован в Nashorn. В случае, если бы этот метод был недоступен, пришлось бы использовать стандартный for..in и довольствоваться только перечислимыми свойствами. Объект console в Nashorn не реализован нативно, поэтому я буду использовать метод print() для вывода информации в консоль сервера, так как это немного удобнее, чем использовать echo() из мода для вывода прямо в консоль игры. Выводим нативные методы. /js print('Native methods:\n' + Object.getOwnPropertyNames(this).filter(function (name) { return (typeof global[name] === 'function' && global[name].toString().indexOf('native code') >= 0) }).join(', ')) Отдельно — пользовательские методы. /js print('User-defined methods:\n' + Object.getOwnPropertyNames(this).filter(function (name) { return (typeof global[name] === 'function' && global[name].toString().indexOf('native code') < 0) }).join(', ')) Если отделить нативные методы от пользовательских достаточно просто, то со свойствами глобального объекта такой номер уже не проделать. Можно разделить их на основе дескриптора (получив его с помощью Object.getOwnPropertyDescriptor) и флагов configurable / enumerable, но на мой взгляд это не слишком эффективно и гораздо проще выполнить ту же задачу глазами. /js print('Properties:\n' + Object.getOwnPropertyNames(this).filter(function (name) { return typeof global[name] !== 'function' }).join(', ')) Так как по результатам вывода методов мы уже понимаем, что большая часть API плагинов экспортируются в глобальную зону видимости, то можем легко разделить более или менее стандартные для любого JS окружения глобальные свойства arguments, NaN, Infinity, undefined, Math, JSON и специфичные для Nashorn Packages, com, edu, java, javafx, javax, org, __FILE__, __DIR__, __LINE__, javax.script.filename, classroom, а все, что останется, скорее всего было добавлено самим модом ScriptCraft. Что еще нам говорит вывод глобальных переменных? Набор нативных методов и свойств в принципе достаточно стандартен для ES 5.1 окружения. Привычных API из Node.js и тем более из браузера здесь конечно нет, зато в изобилии обертки для доступа к внутреннему миру Java. Сам мод без особых терзаний совести экспортирует как внешние так и внутренние интерфейсы в глобальный объект с произвольными именами, переопределяет некоторые нативные методы, такие как setTimeout(), setInterval(), clearTimeout(), clearInterval(), добавляет объект console и метод require(), работающий в стиле Node.js.  Вы можете использовать метод toString() у функции для получения строкового представления тела функции (кроме нативных), а так же свойства name и length для получения имени (полезно для функций за bind'ом) и количества принимаемых аргументов соответственно (полезно для неизвестных нативных функций). Хотя полученной информации уже достаточно для начала работы, наше микро-исследование было бы неполным без определения своего места в цепочке вызовов (call stack), это знание позволит упростить будущую отладку. В JavaScript проще всего получить stack trace из произвольного места не останавливая выполнение кода, путем создания нового экземпляра объекта Error и обращения к его динамически-генерируемому свойству stack. Выполнение команды /js print(new Error().stack) недвусмысленно говорит нам, что мы находимся в некотором REPL интерфейсе и все, что мы введем, попадет в ту или иную форму eval в движке. Оно и логично, все же мы пытаемся исполнять код из консоли. Примечательно, что __onEnable$__onCommand здесь скорее всего сгенирированный Java-класс. Выполнение того же кода из внешнего файла, ситуацию в корне не изменит по-видимому из-за реализации механизма загрузки модулей и особенностей самого движка. Зато теперь мы легко можем узнать точку входа в загрузчик, просто поискав вызов __onEnable в исходниках. Описанный выше эвристический алгоритм показывает себя хорошо в ситуациях, подобных нашей, когда не очень понятно, с чем вообще придется иметь дело и с какой стороны тут лучше подступиться. Все, что вам на самом деле нужно, чтобы быстро вникнуть в суть дел — поле, в которое можно вводить код. Подход несколько экстремальный, но зато весьма эффективный и может работать даже при полном отсутствии документации и инструментов отладки, правда в этом случае понадобится значительно больше трюков. Стоит еще сказать, что в этой статье мы никак не затронули API Nashron для доступа к Java, потому как напрямую к теме статьи это не относится и там можно погрязнуть надолго. Если коротко, весь Nashorn устроен таким образом, чтобы Java-программисты ничем не были ограничены при работе из JavaScript. Доступ практически неограничен – можно распараллеливать выполнение кода, создавая родные для Java треды, можно наследоваться от Java-классов, можно создавать несколько глобальных объектов, динамически подгружать код, читать файлы, выполнять команды ОС и многое, многое другое. Вот лишь несколько ссылок для интересующихся: