Design: Запретить дублирующийся импорт?

Созданный на 3 мар. 2021  ·  44Комментарии  ·  Источник: WebAssembly/design

(В этом выпуске представлена ​​презентация на CG-03-02 , цель которой - получить больше отзывов при подготовке к опросу на каком-то будущем собрании CG.)

Предложение «Связывание модулей» предлагает преобразовать двухуровневый импорт в сахар для одноуровневого импорта экземпляров . Например:

(module
  (import "a" "foo" (func))
  (import "a" "bar" (func))
)

будет обезглавлен до того же AST, что и:

(module
  (import "a" (instance
    (export "foo" (func))
    (export "bar" (func)))
)

Одна из проблем заключается в том, что в настоящее время wasm позволяет дублировать импорт (с одинаковыми или разными подписями):

(module
  (import "a" "b" (func))
  (import "a" "b" (func))
)

в то время как дублирующий экспорт запрещен. Таким образом, обессахаривая вышеуказанное, чтобы:

(module
  (import "a" (instance
    (export "b" (func))
    (export "b" (func)))
)

не будет действительным. module-linking / # 7 обсуждает различные способы устранения этого несоответствия, но самым простым решением было бы задним числом запретить дублирующийся импорт.

Ошибка 1647791 добавляла телеметрию для измерения дублированного импорта в Firefox, а результаты для Firefox Beta 86 показывают, что 0,00014% загрузок страниц содержат дублирующийся импорт (которые могут быть просто модульными тестами в автоматизации или других синтетических источниках). Таким образом, похоже, что это решающее изменение, которое мы могли бы практически внести.

Еще одна принципиальная причина запретить дублирующий импорт заключается в том, что дублированный импорт с разными сигнатурами может быть реализован только хостом, что нарушает общую цель - позволять wasm всегда иметь возможность виртуализировать (полифил, макет и т. Д.) Импорт wasm.

По конкретному опыту известно, что в наборе тестов спецификации имелся дублированный импорт (для разных «перегрузок» функции печати), но это было удалено, потому что это создавало проблемы для людей, желающих запускать тесты.

Таким образом, можно утверждать, что это ограничение, как правило, хорошо для экосистемы wasm, даже если отбросить опасения, связанные с предложением о связывании модулей.

Самый полезный комментарий

Дублирующий импорт весьма полезен для вызова импорта из динамических языков, таких как JS, где одна и та же функция может иметь разные сигнатуры, и я бы ожидал, что она будет использоваться чаще, чем реже, когда система типов на границе станет богаче, скажем, можно вызвать та же функция с числовым или строковым аргументом, либо с одним или двумя аргументами. Мне кажется, что удаление этой функции было бы очень неудачным для взаимодействия с JS.

Все 44 Комментарий

Что плохого произошло бы в предложении о связывании модуля, если бы мы не внесли это изменение? Означает ли это, что такое удаление сахара не будет работать, и поэтому описание импорта будет более сложным? Или это больше повлияет на функциональность предложения по связыванию модуля? (Я вовсе не против этого изменения, я просто пытаюсь лучше понять мотивацию.)

Как я помню из презентации Люка, в настоящее время мы запрещаем дублирующий экспорт.

Что, если мы снимем это ограничение и разрешим дублирующийся экспорт? Это открывает пространство дизайна для связывания модулей, чтобы решить эту проблему другим способом.

В настоящее время движки должны проверять имена импорта / экспорта только по двум причинам: правильность формата UTF-8 и проверка дублирования экспорта. Мы говорили оффлайн о снятии ограничения UTF-8, но, возможно, мы могли бы снять и то, и другое.

Дублирование экспорта сделало бы связывание на основе имени неоднозначным.

@tlively Если мы не запрещаем дублирующийся импорт, то альтернативные решения, которые мы обсуждали в разделе « Связывание модулей / # 7», кажется, что они значительно усложнят реализацию проверки и связывания, особенно в отношении вопроса о том, является ли данный модуль A при создании экземпляра. время приемлемо для импорта из модуля B.

@titzer Я думаю, что добавление дублирующих экспортов приведет к возникновению множества сложных вопросов для каждой интеграции исходного языка. Например, в JS API: отображается ли дублирующийся экспорт как один объект функции JS, который выполняет динамическое разрешение перегрузки? Web IDL делает это, и это довольно сложно . Что произойдет, если я попытаюсь импортировать эту JS-функцию из wasm (через WebAssembly.instantiate )? Я не говорю, что это неразрешимая проблема дизайна, но если есть возможность вообще избежать этих сложных / запутанных вопросов для интеграции с N языком, это кажется предпочтительным.

Дублирующий импорт весьма полезен для вызова импорта из динамических языков, таких как JS, где одна и та же функция может иметь разные сигнатуры, и я бы ожидал, что она будет использоваться чаще, чем реже, когда система типов на границе станет богаче, скажем, можно вызвать та же функция с числовым или строковым аргументом, либо с одним или двумя аргументами. Мне кажется, что удаление этой функции было бы очень неудачным для взаимодействия с JS.

@lukewagner , это может быть плохой идеей из-за того, что я не знаком с предложением о связывании модулей, но разве другим вариантом не будет разделение импорта MVP и импорта экземпляров? Если бы они не были унифицированы, импорт MVP все равно можно было бы дублировать, и не было бы необходимости в сложных типах пересечений.

Дублирующий импорт весьма полезен для вызова импорта из динамических языков, таких как JS, где одна и та же функция может иметь разные сигнатуры, и я бы ожидал, что она будет использоваться чаще, чем реже, когда система типов на границе станет богаче, скажем, можно вызвать та же функция с числовым или строковым аргументом, либо с одним или двумя аргументами. Мне кажется, что удаление этой функции было бы очень неудачным для взаимодействия с JS.

Этот. В тысячу раз больше. Я являюсь основным разработчиком проекта, который предоставляет платформу тестирования для разработчиков сценариев сборки, и я считаю это, вероятно, одной из самых важных частей веб-сборки в настоящее время.

0,00014% включает as- pect , @jtenner , которое является важной частью растущей экосистемы AssemblyScript.

Дублирующий импорт весьма полезен для вызова импорта из динамических языков, таких как JS, где одна и та же функция может иметь разные подписи,

Если для этого будет хотя бы альтернатива, тогда это будет здорово, потому что WebAssembly берет начало в браузерах с JavaScript. Давайте упростим эту задачу.

@tlively Это один из возможных альтернативных вариантов, да, но у него есть досадное последствие, заключающееся в том, что у него есть два способа сделать примерно одно и то же, чего в целом хорошо избегать, а также создает больше случаев для реализации, о которых нужно заботиться. во время проверки, проверки подтипа и создания экземпляра.

@jtenner @trusktr Для ясности, вы все равно можете импортировать одну и ту же JS-функцию несколько раз, с той лишь разницей, что для каждой отдельной подписи, с которой вы ее импортировали, потребуется другое имя. Так, например, вместо того, чтобы испускать:

(module
  (import "env" "log" (func (param i32)))
  (import "env" "log" (func (param f32)))
  (import "env" "log" (func (param i64)))
  (import "env" "log" (func (param f64)))
  ...
)

создается через:

WebAssembly.instantiate(module, { env: { log:log } });

вы могли испустить:

(module
  (import "env" "log_i32" (func (param i32)))
  (import "env" "log_f32" (func (param f32)))
  (import "env" "log_i64" (func (param i64)))
  (import "env" "log_f64" (func (param f64)))
  ...
)

создан с помощью:

WebAssembly.instantiate(module, { env: { log_i32:log, log_f32:log, log_i64:log, log_f64:log } })

Это возможное изменение, которое вы могли бы внести, или оно будет каким-то образом запретительным?

Это возможное изменение, которое вы могли бы внести, или оно будет каким-то образом запретительным?

Запредельно? Нет. Если все, что мне нужно сделать, это изменить имя функции, с которой я связываюсь, это нормально. Раздражает, но нормально. Я просто хочу убедиться, что меня правильно слышат.

Давайте упростим эту задачу.

Это изменение больше всего коснется разработчиков JavaScript. И поскольку мы являемся «веб-сборкой», мы должны быть очень осторожны с тем, как относиться к веб-разработчикам в отношении будущих проблем, подобных этой.

Я за перемены, если они улучшат ситуацию. Итак, у меня есть следующие вопросы в порядке убывания важности:

  1. Делает ли это Интернет лучшим местом для разработки?

Насколько я вижу? Может быть. Это заставляет разработчиков более откровенно относиться к импорту и отговаривает разработчиков JavaScript делать меньше вещей, связанных с javascriptive. Возможно, большее количество ограничений приведет к большему творчеству. Разговор, который, кажется, никогда не велся, касается того, как это влияет на разработчиков C # и Java. Я считаю, что это может негативно повлиять на разработчиков C #, пишущих компиляторы веб-сборки, но, возможно, эта сложность не так уж и плоха.

  1. Упрощает ли это внедрение, поддержку и ускорение запуска WebAssembly?

Я не знаю ответа на этот вопрос. Разработчик компилятора будет лучше разбираться в подобных вещах. Думаю, разработчикам, пытающимся поддерживать wasm-движки, удобнее. И да, это будет быстрее или, по крайней мере, будет более явным, легче проверить состояние модуля.

  1. Повлияет ли это изменение негативно на разработчиков, которые полагаются на ранее неизменное поведение?

Насколько я могу судить, у тех немногих людей, которые используют эту функцию, их оборудование сломается, и они будут недовольны тем, что придется вернуться и попытаться выяснить, как исправить свое программное обеспечение.

После поддержки программного обеспечения фреймворка тестирования для AssemblyScript я пришел к выводу, что такие небольшие изменения не влияют на типичных конечных разработчиков. Эти изменения влияют на людей, обслуживающих программное обеспечение экосистемы, инструменты разработки и компиляторы. Незначительные изменения в спецификации, подобные этой, могут привести к очень большим запросам на вытягивание, выполнение которых может занять несколько дней или недель.

Мое последнее мнение: мне не нравится это изменение, но если вы собираетесь его внести, уже снимите пластырь. Начни цепную реакцию сейчас и заставь ее рассчитывать.

Я вижу в этом еще один недостаток:

(module
  (import "env" "log_i32_f32_f64" (func (param i32 f32 f64)))
  (import "env" "log_f32_i32_f64" (func (param f32 i32 f64)))
  ...
)

это может потенциально увеличить двоичный размер, если вы должны сохранить исходные имена импорта (для разборки) и не можете применить двоичный проход, который сжимает имена импорта.

Однако для общего случая это не такая уж большая проблема.

Основная проблема, которую я вижу, заключается в том, что пользователь должен заранее знать все арности и типы аргументов, чтобы вручную исказить объект импорта, в то время как в настоящее время компилятор может предположить, что, например, Math импортируется целиком и генерирует импорт с тем же именем, но с разной арностью или типами аргументов при обнаружении вызовов импорта во время компиляции. Простым примером здесь является Math.max , немного более сложными являются ctors типизированного массива с сильно различающимися сигнатурами , и он становится только более сложным, например, с API-интерфейсами Node.js, которые часто позволяют опускать аргументы посреди вещей. при этом также допускаются совершенно разные типы и, конечно, пользовательский код, о котором никто никогда не знает наверняка. И, конечно же, всякий раз, когда что-то, связанное с исходными текстами модуля Wasm, изменяется, вам также придется повторно посетить объект импорта. Все это было бы очень грустно.

Я думаю, что на собрании CG я не мог выразить то, что я думаю, что предложение о связывании модуля пытается оттеснить требования от связывания на основе имени в движок, и мне кажется естественным, что здесь есть слой, чтобы эти требования выполнялись. не давить. Например, когда мы ограничиваем двоичный формат ядра wasm, мы заявляем, что движки должны проверять и отклонять несоответствующие модули. Это имеет смысл с точки зрения безопасности типов, разумных ограничений размера и других важных для движка вещей.

Я бы сказал, что имена не имеют значения для двигателей; они имеют значение только для размещения встраиваемых файлов, и движкам действительно не нужно смотреть (а тем более отклонять) имена. Об этом свидетельствует ситуация с перегрузкой JS, о которой упоминали другие комментаторы.

Мой комментарий по поводу UTF-8 был в том же направлении; движку буквально не нужна обработка UTF-8, за исключением того, чтобы отвергать плохие имена; то же самое для дублированного экспорта. Пахло очень забавно, когда я делал Wizard, потому что мне внезапно пришлось реализовать валидатор UTF-8 с нуля ... хм. Кроме того, проверка дублирования экспорта - единственное место, где мастер использует хеш-таблицу. Не то чтобы сотня или около того строк кода была кризисом, но это странная утка в упражнении с виртуальными машинами, ориентированным на вычисления.

Я понимаю, что цель предложения по связыванию модулей состоит в том, чтобы предоставить стандартное решение для связывания на основе имен для Wasm, но по ряду причин я думаю, что это имеет некоторый риск не работать в достаточно общем виде, поэтому мы должны прибегнуть к запрету вещей, которые отлично совершенствуются с точки зрения двигателя, на уровне двигателя.

Поэтому я поддерживаю свое предыдущее предложение о том, что мы должны быть более либеральными и, возможно, снять ограничение на экспорт дубликатов.

Если предложение по связыванию модулей прямо сейчас хочет отклонить модули, которые не могут быть связаны из-за проблем с именованием, то это кажется совершенно нормальным в качестве ограничения уровня выше основного wasm. Но, как я уже сказал, существует риск, что предложение по связыванию модулей не решит проблемы связывания для очень многих языков программирования, и вместо этого нам понадобится более программируемое решение, включающее API для первоклассных модулей. Но это, вероятно, более продолжительный разговор, и это довольно узкая проблема. Это контекст моих мыслей.

Ах, интересно @titzer. У меня были похожие мысли. Короче говоря, компиляция и компоновка связаны, но все же различны, и проблема заключается в том, что они слишком тесно связаны.

Мы могли бы представить себе, что все аспекты WebAssembly, связанные со ссылками, вынесены в отдельный раздел. Раздел «компиляции» все равно будет иметь импорт, но он безымянный (за пределами своего индекса) - просто дыры, которые раздел «связывание» позаботится о заполнении. Раздел «компиляция» может по-прежнему иметь экспорт, но опять же, он не имеет имени, а раздел «связывание» отвечает за предоставление имен.

Таким образом, связывающие разделы могли содержать специфический для встраивающего устройства контент. Например, секция связывания для типов интерфейса может предоставлять adapter_func s, тогда как секция связывания для JS может устанавливать словари rtt как в @tebbi WebAssembly / gc # 132 - в обоих случаях, этот дополнительный контент имеет смысл только для этого конкретного устройства для внедрения и чрезвычайно полезен для этого конкретного устройства для внедрения. Предложение типов модулей - это, по сути, еще один связывающий модуль для встраивания, и отчасти напряженность заключается в том, что то, что имеет наибольший смысл (например, отдельные имена для простого разрешения ссылок), не имеет наибольшего смысла в JS (например, перегруженные имена).

Приложение: Это может быть полезно для поддержки джиттинга. Модули хотят, чтобы их функции можно было напрямую вызывать из дополнительных модулей, которые они динамически генерируют, без необходимости предоставлять эти функции встроенной системе связывания.

Как сказал @titzer , это тоже выходит за рамки этого обсуждения, но я также хотел

@dcodeIO Во всех сценариях, которые я видел, JS-код, который создает экземпляр модуля wasm, генерируется той же цепочкой инструментов, которая генерирует модуль wasm (создание экземпляра b / c обычно требует кучи связующего кода), поэтому я Я не знаю, что эта подразумеваемая деталь всплывет до пользователя. Но видели ли вы иначе?

Одна вещь, которую я хотел бы прояснить и подчеркнуть: из точки обзора чисто JS-экосистемы дублированный импорт представляет опасность, потому что и JS API, и ESM-интеграция не имеют возможности соединить два экземпляра wasm A и B, где B имеет дублированный импорт. что мы хотим удовлетворить с помощью A. A может иметь возможность экспортировать две функции с подходящими подписями, но B сможет импортировать только 1 из них (с одной подписью, которая, как правило, не сможет удовлетворить оба импорта).

@titzer Я понимаю желание

(module
  (import "other" (module
    (import "foo" (func))
    (export "bar" (func))
  ))
)

вместо этого нам пришлось бы удалить "foo" и "bar" из типа импорта, оставив что-то чисто позиционное:

(module
  (import "other" (module
    (import (func))
    (export (func))
  ))
)

Если мы с нетерпением ждем сценариев раздельной компиляции (например, разделение разделяемых библиотек на модули, которые широко используются повторно), то это приведет к wasm-версии старой проблемы хрупкой базы C ++, поскольку мы будем полностью зависеть от позиционного порядка; это то, что исправляют имена импорта / экспорта. Это то, что вы предлагаете? (Обратите внимание, что ссылки на первоклассные модули и / или первоклассные экземпляры / экземпляры имеют одну и ту же проблему; она ортогональна.)

Но если это не то, что вы предлагаете, то я не понимаю, как мы можем избежать того, чтобы имена были неотъемлемой частью связывания модулей и, следовательно, двусмысленностей, вызванных дублированием импорта / экспорта.

Нет, я не предлагаю позиционную привязку. Я согласен, что это было бы действительно очень хрупко.

Но, размышляя о сценарии использования Jawa, а также о типах интерфейсов, выделяется то, что необходимо на самом деле анализировать и применять логику к импорту. В Jawa имена импорта - это не строки, а фактически выражения на языке, и с аргументами импорта они могут принимать типы и функции в качестве импорта. Нет чистого wasm-решения для позднего связывания с Java или любым языком высокого уровня IMHO. Низкоуровневое решение для связывания на основе имен может служить нишевой экосистеме wasm, построенной с очень ограниченным набором языков, но в долгосрочной перспективе оно не решает вариант использования языков более высокого уровня. Итак, ИМО создает механизм, позволяющий создать новую, очень ограниченную экосистему, в отличие от добавления достаточно общего механизма для создания ссылок на более высоком уровне.

Итак, мои комментарии о первоклассных модулях. В конечном итоге нам понадобятся рефлексия и JITing API, которые позволят полностью программируемое связывание, а очень жесткая система сопоставления на основе имен на самом деле не решает реальных проблем какого-либо языка и не дает им достаточного количества веревки.

И сопоставление на основе имен тоже очень хрупко. Поскольку у нас еще нет никакого механизма параметризации для импорта, языки вынуждены прибегать к искажению имен, что одновременно уродливо и хрупко и не может поддерживать даже такие простые вещи, как выделение подтипов и перегрузка.

Извините за расширенные комментарии; Сейчас мы действительно обсуждаем более крупный вопрос, и это, вероятно, не должно касаться узкого вопроса, как здесь.

Во всех сценариях, которые я видел, код JS, который создает экземпляр модуля wasm, генерируется той же цепочкой инструментов, которая генерирует модуль wasm (создание экземпляра b / c в целом обычно требует кучи связующего кода)

Например, AssemblyScript не генерирует связующий код для конкретного модуля, но пытается этого избежать. Он только генерирует определения типов (для целей разработки) для использования с крошечной сопутствующей JS-библиотекой общего назначения (может извлекать имена экспорта более высокого уровня для более удобного использования из JS), которую мы просто называем «загрузчиком», но использовать его часто необязательно. , особенно при использовании AS в основном в качестве немного более удобного синтаксиса для создания минимального модуля Wasm, скажем, для алгоритма сжатия (например, только экспорт C-подобных функций) или при компиляции для WASI. Я надеюсь, что в конечном итоге нам больше не понадобится искажение имен, например MyClass#myMethod , но я не уверен, связано ли это с этой проблемой.

дублирующийся импорт представляет собой опасность, потому что и JS API, и ESM-интеграция не имеют возможности соединить два экземпляра wasm A и B, где B имеет дублированный импорт, который мы хотим удовлетворить с помощью A

Я ожидал, что этот случай приведет к ошибке где-нибудь в будущем, да, вероятно, во время связывания экспорта Wasm с импортом Wasm с несоответствием подписи, которое не может быть удовлетворено, хотя это приведет к большому удовольствию во время выполнения при использовании несоответствующая подпись при импорте JS или импорте из любого другого динамического языка.

Мне непонятно, почему необходимо использовать одно и то же имя для импорта нескольких функций Wasm с разными сигнатурами, поскольку функции Wasm имеют фиксированные сигнатуры (в отличие от хост-функций JS, которые приводят все аргументы и типы возвращаемых данных к данной сигнатуре). Я неправильно понял предпосылку?

Похоже, что для этого сценария было бы совершенно нормально выдать ошибку, если данный импорт является функцией Wasm, если только стандарт не был расширен, чтобы разрешить определение функций с разными сигнатурами, также вызывающих автоматическое приведение.

Первоначально я осторожно подходил к удалению повторяющегося импорта, но теперь я решительно поддерживаю это изменение.

Ключевое наблюдение заключается в том, что дублирующийся импорт не может обрабатываться в различных средах, как указывает @lukewagner . И, по его словам, это проблематично даже для сценариев использования JS.

Что наиболее важно, это нарушает базовое модульное свойство, а именно то, что весь импорт может быть в равной степени реализован с помощью Wasm или хост-модулей. Это важно для таких случаев, как виртуализация.

Дублированный импорт фактически вызывает на стороне клиента механизм перегрузки или динамической типизации, что плохо, потому что не у каждого клиента есть средства для его поддержки (включая другие модули Wasm).

И это реальная проблема на практике, как показала история набора тестов.

@titzer , боюсь, я не понимаю. Однозначное именование кажется ортогональной проблемой параметризации. Даже если бы у нас было последнее, мы все равно хотели бы убедиться, что имена однозначно определяют (параметризованную) сущность. Возможно, даже в большей степени, поскольку разрешение перегруженных имен импорта другими способами становится еще более сложным, когда эти другие способы становятся более сложными?

Как я уже сказал, имена не имеют значения для двигателей, поэтому их уникальность не имеет значения и для двигателей. Они имеют значение для _linkers_, поэтому это выбор конкретного решения для связывания, если оно хочет обеспечить уникальность имен импорта (или имен экспорта, если на то пошло). А компоновщикам в пределе в конечном итоге потребуется полностью программируемое решение, которое будет включать не только первоклассные модули и экземпляры, но и JIT-технологию и возможность их постепенного создания.

Я думаю, что все проблемы связаны с попытками превратить модули в статически типизированные функторы [*] от импорта до экспорта на основе имен. В результате имена становятся частью типа, и введение этого типа в язык wasm требует проверки типов и, следовательно, правил сопоставления имен. Но проблема в том, что эти имена являются низкоуровневыми именами, сродни искаженным именам C / C ++. Конечным результатом этого являются три плохие вещи:

  1. мы уже видели: wasm не может даже подключиться к хост-средам, которые имеют перегрузку
  2. связывание постоянно ограничивается тем, что может и не может быть выражено с использованием одних только низкоуровневых имен, что приводит к постоянной проблеме дизайна с каждой новой функцией wasm
  3. она не служит языков , которые не могут использовать какую -

Эти проблемы вытекают непосредственно из желания ввести статический тип модуля, потому что это мотивировано тем, что движки выполняют сопоставление на основе имени для простого решения связывания. Но на самом деле двигатели никогда не должны смотреть на имена; другое дело - компоновщик - должен смотреть на имена и разрешать ли они их для движка. И да, один из способов решения этой проблемы состоит в том, что этот API предоставляет векторы импорта компоновщику и возвращает вектор экспорта - это то, что я сделал в Wizard, и так работает API встраивания C.

[ ] Я тоже так думал, что модули wasm - это функции от импорта до экспорта. Но здесь отсутствует кое-что важное: логика связывания, исходящая от языка более высокого уровня.

@titzer , я согласен с тем, что предложение по связыванию модулей не обеспечивает достаточно мощную модель связывания для многих случаев использования (например, он не сможет заменить объектные файлы C), но я не думаю, что предоставление ссылок общего назначения модель - цель предложения. Я рассматриваю его как расширение существующей системы импорта / экспорта на основе строк, которое является достаточным и полезным для связывания в стиле сборщика, распространенного в Интернете и в некоторой степени уже встроенного в структуру модуля Wasm. Компоновщики более высокого уровня, зависящие от языка, могут продолжать делать все, что захотят, в отношении именования, потому что их модульность в любом случае должна быть наложена поверх WebAssembly.

@titzer Если имена импорта и экспорта не являются частью типа модуля, то как вы предлагаете избежать проблемы хрупкости, которую, как вы сказали, вы также хотели избежать?

Я также не понимаю, как вы решаете проблему виртуализации. Читая комментарий @rossberg , я понимаю, что виртуализация - это более фундаментальная существующая проблема, которую нужно решить здесь, и на самом деле она не имеет ничего общего с предложением о связывании модулей.

Я с пониманием отношусь к изменениям, но, глядя на WebAssembly / module-linking # 7, кажется, что здесь не так много вариантов рассматривается. Существует много обсуждений типов пересечений, но в этом обсуждении не упоминается, что преобразования с использованием типов пересечений не работают для систем принудительного связывания, таких как JS API, на что полагаются некоторые из упомянутых здесь вариантов использования. Все рассмотренные варианты, похоже, основаны на конструкциях модулей на поверхностном языке (в основном на основе знаний Андреаса о модулях для конкретных функциональных языков), но, похоже, не уделяется особого внимания конструкциям модулей низкого уровня. Низкоуровневая модульная система легко могла бы сделать здесь спорным, но при этом также служила бы целям предложения по связыванию модулей (включая разрешение на основе имен). В то же время было бы легче использовать ряд важных расширений.

@RossTate , я не уверен, что вы здесь называете «низкоуровневой модульной системой» или чем она отличается от того, что уже обсуждалось. Если у вас есть идеи для различных решений, возможно, их можно обсудить на https://github.com/WebAssembly/module-linking/issues/7, чтобы эта проблема была сосредоточена на конкретном предлагаемом решении по запрету дублированного импорта.

Я оставил заметку на https://github.com/WebAssembly/module-linking/issues/7#issuecomment -791063260, спрашивая, может ли это просто быть частью проверки связи модуля, не вызывая проблем с обратной совместимостью для существующего кода, который может быть в дикой природе.

Это как раз та проблема, с которой мне пришлось так долго сталкиваться. Модули не имеют типов без учета обработчика импорта, который придает смысл именам и ограничениям, связанным с импортом. Это все равно, что спросить, какой тип текстового файла. Конечно, текстовый файл может содержать текст, написанный на любом языке, поэтому этот вопрос не имеет смысла без контекста. Когда мы исправим язык, мы можем даже начать говорить о типах, если таковые имеются, в файле. То же самое и с модулями. Модули не являются функторами, т.е. чистыми функциями от импорта (с именами) до их экспорта. Для придания значения импорту им требуется система ссылок. Пока мы не пройдем эту точку, все остальное из того, что я говорю, не имеет смысла. Я считаю, что это очень базовое предположение в предложении о связывании модуля, которое даже не было вынесено на обсуждение. И я чувствую, что на самом деле мы это не ставим под сомнение. Кажется, я повторяюсь, потому что на самом деле сомневаюсь в вещах глубже, чем может показаться вежливым. Я, конечно, не хочу показаться невежливым, но я хочу оспорить то, что модули должны выглядеть как функторы. И мы даже не сомневаемся в этом.

Я могу приводить пример за примером, почему модули явно не являются функторами.

  1. Связывание модулей wasm с JavaScript осуществляется через объект импорта - объект импорта, который на самом деле может быть прокси-сервером или иным образом иметь поведение для применения полной логики Тьюринга к именам (например, их декодирования) и даже имеет состояние, которое делает невозможным приведен как функтор. Оперативно он буквально определяется как алгоритм, который опрашивает импорт объекта JavaScript путем импорта. Вся эта связывающая экосистема не может быть выражена посредством связывания модулей.

  2. API встраивания C работает таким же образом. Он предоставляет модуль для внедрения в качестве полностью проверяемого объекта с именами, видами и подписями для проверки. Чтобы создать экземпляр модуля, устройство для внедрения может применять любую логику, которая им нравится, при условии, что они просто предоставляют привязки к движку (да, позиционным способом) для создания экземпляра. Кроме того, это полностью программируемая экосистема, не связанная с привязкой модулей.

  3. И Джава, и Пива тоже такие. Jawa полностью статически типизирован, но его импорт кодируется на языке импорта таким образом, что простое сопоставление имен неадекватно; среда выполнения Jawa должна синтезировать (JIT) новые функции, типы и другие привязки для предоставления механизму. Pywa полностью динамически типизирован, и его реализация в JS аналогичным образом использует перегрузку.

Модули и их импорт имеют смысл только в контексте схемы связывания, языка, процессора импорта, как бы вы это ни называли. Опять же, они не функторы. У них нет типов, лишенных контекста, в котором они находятся. Импорт модулей - это описания того, что им нужно из внешнего мира, написанное на иностранном языке, который не понимается и не может быть понят движком. У них есть только типы Wasm, так что сам модуль может быть отдельно проверен типом независимо от его импорта в соответствии с системой типов Wasm, и что импорт может быть проверен на соответствие предположениям, изложенным в ограничениях импорта. Опять же, здесь ничего об именах не появляется, пока мы не начнем пытаться превратить модули в типизированный функтор wasm-y. Но эти имена фиолетовые! Вот где выживает весь материал исходного языка. Конечно, возиться со сравнением строк не получится.

Имея это в виду, желание сделать решение для связывания "без излишеств" неплохое. Это имеет смысл, если имена не слишком сумасшедшие. Это похоже на создание небольшой экосистемы, в которой имена очень простые, и достаточно простого сопоставления. Просто он не должен быть примитивным, потому что это неправильный примитив. Это должно быть ясно, потому что он вносит свои предположения непосредственно в ядро ​​wasm таким образом, что рискует стать камнем преткновения или странной бородавкой, когда мы добираемся до того, что действительно необходимо, а именно полностью программируемого связывающего API. Если бы у нас был программируемый API для связывания, тогда предложение по связыванию модуля могло бы быть просто библиотекой. Поэтому нам лучше подумать о том, как превратить API встраивания C в более стандартный wasm API, а затем написать связывание модулей в качестве библиотеки для этого API.

@lukewagner Проблема "хрупкости": обратите внимание, я не сторонник отказа от имен. Имена, очевидно, необходимы системе ссылок. Но двигателю они не нужны. Что касается API между движком и компоновщиком, я просто имею в виду, что движок может передавать импорт компоновщику в виде упорядоченного вектора, компоновщик может разрешать (потенциально их подмножество) и возвращать их позиционно. Сами модули не нуждаются в позициях.

И да, для этой проблемы есть отличный вариант использования дублированного импорта, о котором я думал некоторое время, и да, дублирующий импорт в соответствии с их аргументами: совместное использование (или отсутствие) встроенных кешей. Если бы мы должны были, например, импортировать функцию, которая выполняет доступ к свойству JavaScript, мы могли бы дать ей имя «GetProperty [foo]». Если мы импортируем его только один раз, какой встроенный кеш должен использовать движок? Должен ли он проверять код wasm, чтобы создать для них новый сайт вызова? Что ж, если модуль может импортировать одну и ту же функцию несколько раз с одним и тем же именем, но каждый раз получать новую IC, тогда у модуля есть некоторая степень контроля над тем, как движок адаптируется к его различным полиморфизмам. (Конечно, мы всегда можем наклеить какой-нибудь дурацкий уникальный мусор на конец строки, но зачем беспокоиться? Это просто работа, позволяющая обойти ограничение из совершенно другой вещи, которую мы не используем в wasm.

@titzer Представление, которое вы

Независимо от того, что, как я узнал, является основным вопросом, остается здесь: должны ли модули wasm иметь возможность виртуализировать экспорт wasm? Если полностью оставить в стороне вопрос «как модули wasm соединяются вместе?», Это кажется основным принципиальным вопросом, который следует выдвинуть CG. Интересно, может быть, стоит отдельная проблема дизайна, чтобы сосредоточиться только на этом вопросе, независимо от вопросов о связывании?

Виртуализация важна, и программируемое связывание обязательно должно поддерживать это.

На самом деле я не думаю, что то, о чем я говорю, сильно отличается от сегодняшнего wasm, и то, о чем я говорю, ориентировано не только на Jawa. То же самое возникает, когда думаешь о связывании .NET или других языков. Выше я привел три примера, два из которых представляют собой существующие вложения, которые, будучи встроенными в другой язык, обеспечивают полную программируемость.

Я совершенно готов сделать презентацию для CG.

Fwiw, насколько я понимаю, предложения Титцера сделать Wasm жизнеспособным для большего количества вариантов использования, а не для меньшего, гораздо больше соответствуют моим ожиданиям, чем альтернатива. Я бы даже пошел дальше, чтобы предположить, что может потребоваться более общее обсуждение направления Wasm, учитывая все разногласия, которые уже имели место по аналогичным вопросам по аналогичным причинам в других местах.

Например, я нахожу такое утверждение, как

Представление, которое вы излагаете, является довольно существенным отклонением от того, как wasm определяется, реализуется и широко обсуждается сегодня, поэтому я думаю, что вы должны предложить это изменение точки зрения CG и получить некоторую поддержку - я не Не думаю, что это то, что можно утверждать как природу wasm.

глубоко обеспокоен в контексте. Здесь может быть более широкая проблема в том, как указывается wasm, и мне не ясно, с какой стороны уходит.

@titzer Я в основном имел в виду эту часть того, что вы говорили (и все следствия):

Модули не имеют типов без учета обработчика импорта, который придает смысл именам и ограничениям, связанным с импортом. Это все равно, что спросить, какой тип текстового файла.

что было бы существенным отклонением от текущей спецификации (например, этой части ).

@lukewagner Спасибо, что указали мне на это.

В этой формулировке (как я думаю, обсуждение связывания модулей указывалось), импорт и экспорт являются векторами, следовательно, позиционными. Чтобы связать (позиционный) экспорт одного модуля с (позиционным) импортом другого модуля, вам нужна функция сопоставления. Я просто указываю, что функция точного сопоставления имен «без излишеств» - это всего лишь один вариант, и у нас уже есть два других внедрения, где эта функция сопоставления полностью программируется на языке хоста.

@titzer Это правда, но обратите внимание: несмотря на то, что JS API полностью программируется, если я дам вам модуль wasm с дублированным импортом (разных типов), вы не сможете использовать JS API для реализации этого импорта с помощью экспорта wasm. из-за использования поиска по имени в WebAssembly.instantiate() . То же самое и с ESM-интеграцией . Хотя я могу представить себе теоретические расширения этих двух схем связывания для поддержки виртуализации дублированного импорта, мне труднее представить, чтобы каждый был достаточно мотивирован, чтобы на самом деле указать и реализовать дополнительную сложность.

Таким образом, я думаю, что мы должны задать вопрос: «Будет ли множество схем связывания wasm, которые неизбежно появятся в экосистеме wasm, в конечном итоге поддерживать виртуализацию дублированного импорта?» и, судя по нашему текущему набору выборок, это маловероятно.

Представьте, что импорт и экспорт WebAssembly не имеют связанных с ними строк. JS API будет использовать чисто позиционные индексы для доступа к импорту и экспорту. Насколько я могу судить, было бы легко (в относительном смысле) заставить существующие цепочки инструментов работать с такой настройкой. (В некоторых случаях это может быть проще, потому что такие вещи, как emscripten, не должны генерировать произвольные имена для подключения «встроенного» JS-кода (а-ля EM_ASM ).)

В таком мире не было бы проблем с виртуализацией. То есть проблема виртуализации вызвана выбором использования имен, а не позиционных индексов. Можно даже решить эту проблему с обратной совместимостью, сделав их доступными в JS API.

Дело в том, что непозиционный импорт / экспорт - это на самом деле декорации, специфичные для встраивающего устройства, которые используются для облегчения интеграции модуля wasm в экосистему. То, как эти украшения указаны, действительно должно быть адаптировано к (существующим или ожидаемым) нормам экосистемы.

Например, при интеграции ESM значения JS, экспортируемые другими модулями, могут быть перегружены, но WebAssembly не поддерживает перегрузку, поэтому дизайн оформления импорта-экспорта ESM должен восполнить этот пробел, чтобы модули WebAssembly могли заменить столько JS. модули по возможности. Это можно сделать, используя дублирующий импорт и приведение типов для извлечения различных перегрузок функции, импортированной из другого модуля JS. Можно даже разрешить дублирование экспорта, если все экспортируемые типы являются функциями с достаточно непересекающимися / различимыми параметрами, чтобы их можно было объединить в одну перегруженную функцию JS. И, конечно же, есть и другие варианты дизайна, у каждого из которых есть свои плюсы и минусы.

В качестве противоположного примера предположим, что основной язык внедрения для WebAssembly в основном использовал именованные параметры для функций, а не позиционные параметры. Тогда оформление импорта-экспорта выглядело бы иначе, с экспортом функций, назначающих имена каждому параметру. Но на низком уровне модуль WebAssembly по-прежнему будет просто позиционным импортом и экспортом.

Вот почему я предлагал решить проблему, сделав шаг ниже. Связывание «базового» модуля WebAssembly, то есть то, что можно использовать для связывания вместе модулей WebAssembly, определенных в одном файле (например, для связывания модуля адаптера типов интерфейса с модулем, который он адаптирует), должно быть позиционным. Связывание «встроенных» модулей WebAssembly, то есть то, что можно было бы использовать для связывания вместе как WebAssembly, так и специфичных для встраивающего модуля модулей, определенных в отдельных файлах в конкретном устройстве для внедрения, было бы разделом для конкретного устройства для внедрения и в значительной степени отвечало бы за определение преобразования между устройством для внедрения. конкретные конструкции и конструкции WebAssembly, такие как функции принуждения, добавление украшений к экспортируемым функциям / экземплярам / модулям или связывание (одинарных / двойных / списков) имен с позиционным импортом. Тогда «ядро» WebAssembly никогда не будет беспокоиться ни о чем, кроме чисел и позиций, и такие проблемы, как дублирующийся импорт, становятся проблемами, специфичными для встраивающего устройства, а не «основными» проблемами.

С таким менталитетом этот вопрос можно было бы переформулировать как «Для JS API или для модулей ES, должны ли мы разрешить два импорта быть связаны с одной и той же строкой?» причем ответ не имеет значения для предложения о связывании модулей. Это полезный вопрос, но на данный момент трудно дать хороший ответ, поскольку основной аргумент основан на связи, которой (я думаю) не должно быть.

Я согласен с тем, что позиционный импорт / экспорт позволяет избежать дублирования всего вопроса. Проблема, которую я бы хотел избежать, заключается в том, что когда у вас есть отдельная компиляция в разное время, так что вам нужно начать заботиться о версиях, позиционный импорт / экспорт имеет классическую проблему хрупкого базового класса, которая конкретно ощущается в компонентных системах. как COM, которые основаны на (позиционных) макетах C ++ vtable.

Теперь, если я понимаю, что предлагают @RossTate и @titzer , это проблема, которая должна быть решена на уровне хоста, позволяя хосту вводить имена, соответствующие этому хосту . Проблема, которую я вижу, остается с этим ответом, заключается в том, что он по своей сути разделяет экосистему инструментов wasm по хостам. Например, теперь у меня не может быть просто «clang targeting wasm», если clang хочет выполнить какую-либо форму связывания (например, чтобы исключить общий libc (что является важной оптимизацией в этой развивающейся категории высокоэффективных). многомодульные платформы выполнения wasm)) - мне нужно научить clang обо всех различных конкретных хостах, на которые он нацелен, и сделать так, чтобы хост был параметром для clang. Более того, теперь созданный вывод может быть запущен только на этом хосте, что предотвращает возможность создания модулей .wasm которые могут быть повторно использованы на разных хостах. Чтобы получить общие инструменты и повторно используемые модули, нам нужен какой-то тип общей абстрактной цели компиляции, которая говорит о связывании (особенно такая, которая не предполагает ассоциации 1: 1 между именами и экземплярами, что недостаточно для ряда случаев использования )) - это именно то, что надеялось установить в предложении о связывании модулей.

При этом я могу представить гибридное решение, в котором связывание модулей добавляется как уровень спецификации, не зависящий от хоста, над базовым wasm и под JS API. Это позволит создавать независимые от хоста инструменты и модули (нацеленные на этот независимый от хоста уровень спецификации), в то же время позволяя встраиванию хоста полностью обходить этот уровень, создавая экземпляры основных модулей напрямую со специализированными схемами связывания.

Звучит ли это как разумный способ решения этих противоречивых целей?

Спасибо, @lukewagner , за

Я думаю, что гибридное / двухуровневое решение - это то, что я пытался предложить, где два уровня соответствуют «внутреннему» и «внешнему» связыванию. Внутреннее связывание будет позиционным и указывать инструкции для создания / подключения / создания экземпляров модулей - это то, что должен реализовать каждый движок WebAssembly (со связыванием модулей). Внешнее связывание будет зависеть от устройства внедрения и указывать, как создавать (позиционные) модули из других файлов и т.п., например, связывать имена файлов и / или строковые имена с импортом / экспортом - это то, что должны были реализовать встраивающие устройства WebAssembly. Такое разделение задач упростит повторное использование «основных» аспектов программы WebAssembly и «основных» аспектов механизма WebAssembly в нескольких экосистемах. (Я также думаю, что это разделение может очистить аспекты внутренней компоновки модулей.)

Но, как вы упомянули, наличие двух «внешних» настроек может привести к серьезным трещинам. Итак, хотя я считаю, что «внутренние» и «внешние» ссылки должны быть разделены, я также считаю, что полезно иметь «общий» стандарт внешних ссылок. Некоторые могут разработать более специализированные системы внешних ссылок, чтобы лучше вписаться в специфику конкретной экосистемы или воспользоваться ее преимуществами (используя для этого более специализированные инструменты), но потребности многих программ будут соответствовать простому общему стандарту.

Итак, подытоживая, я думаю, что должна быть одна «внутренняя» система ссылок (это низкоуровневая, например, позиционная), и должна быть возможность для нескольких «внешних» систем ссылок, которые располагаются поверх нее, но с одной » общая "стандартизированная такая система на месте". Имеет ли это смысл? (К сожалению, ничто из этого не говорит о текущей проблеме дублированного импорта, но, по крайней мере, пытается охарактеризовать, где в этом пространстве находится эта проблема, если это имеет смысл.)

Хорошо, поэтому размышления о том, что такого рода изменения будут конкретно означать в краткосрочной перспективе: преобразование модуля Linking в «общий связывающий» уровень выше базовой спецификации (и, если бы мы хотели, в соответствии со спецификациями JS / Web), может означать, что мы вообще не нужно ничего добавлять в основную спецификацию. В частности, импорт / экспорт основных модулей может оставаться позиционным, так что уровень связывания модулей полностью игнорирует эти имена при создании экземпляра. Исходя из этого, мы могли бы закрыть эту проблему, ответив «Нет», и в этом случае я подхожу к пункту @titzer выше, о котором для симметрии мы также должны разрешить дублирование экспорта, чтобы, например, основные модули которые создаются исключительно уровнем связывания модулей, могут просто предоставлять строки нулевой длины для всех строк импорта / экспорта - мы не будем заставлять основные модули включать бессмысленные "" , "a" , "b" , "c" ... экспортируют имена только для облегчения проверки.

(В будущем мы могли бы поговорить о расширении ядра wasm с помощью позиционных версий модулей / экземпляров, но я не думаю, что это будет дополнительно необходимо для начального набора сценариев использования и требований, на которых сосредоточено предложение о связывании модулей.)

@rossberg @titzer @tlively Мысли?

Я обеспокоен тем, что кодирование того, что основной импорт и экспорт Wasm должен быть строго позиционным, закроет дверь в https://github.com/WebAssembly/gc/issues/148 , что предполагает введение нового механизма importexport для решения проблемы импорта и экспорта обязательно асимметричны. Предлагаемый механизм, по сути, вводит слабое связывание типов на базовом уровне Wasm, и я не уверен, как он может работать позиционно без строковых идентификаторов.

Очевидно, это гипотетически, потому что нет консенсуса по продвижению предложения GC в этом направлении, но я лично считаю, что это достаточно привлекательный вариант для предложения GC, и мне было бы грустно, если бы он стал менее осуществимым из-за несвязанных разработок, подобных этому. Сейчас. Я не понимаю, как это предложенное изменение работает позиционно, или я неправильно понимаю эту проблему?

Ах, интересная связь, @tlively! Это заставляет меня думать о паре идей.

Одна идея актуальна для статических ссылок. Так же, как именованный импорт помогает с возможностью изменения внешних связанных модулей, так и импорт по умолчанию (точно так же, как с именованными параметрами и параметрами по умолчанию для функций). Импорт по умолчанию позволяет добавить в модуль новую возможность настройки без нарушения обратной совместимости. В двухуровневом подходе к связыванию на внутреннем уровне я бы выразил это как два модуля (модуль A имеет импорт, а модуль B имеет экспорт), а на внешнем уровне семантика будет такой: "если задан аргумент для этого импорта , свяжите этот аргумент с A, иначе свяжите B с A ", чтобы B, который определяет значение по умолчанию, использовался только при необходимости.

Другая идея имеет отношение к динамическому связыванию. Здесь строки используются как глобальные ключи. importexport существу говорит: «импортируйте значение, связанное с этим ключом, и если это значение еще не определено, то глобально зарегистрируйте следующее как значение, соответствующее этому ключу». (Опять же, это все на уровне внешних ссылок.)

Хотя WebAssemby / gc # 148 касается номинальных типов, я отмечу, что обе вышеупомянутые идеи применимы и к исключениям. Для статического связывания я ожидаю, что общий шаблон для модуля будет заключаться в импорте события исключения, которое по умолчанию только что сгенерировано, но которое может быть создано для использования того же события, что и другие модули (экземпляры?) Для совместного использования нелокального управления с ними. Для динамического связывания, если кто-то действительно хочет, чтобы не было центрального модуля времени выполнения (о котором люди уже знают мои мысли), тогда любой язык с исключениями должен будет координировать событие исключения между динамически связанными модулями и importexport Семантика

Аккуратный! Если я правильно понимаю, механизм importexport можно рассматривать как часть уровня связывания модулей, который обессахаривает позиционный импорт и экспорт, неявно создавая модуль экспорта "по умолчанию", из которого можно импортировать определенный тип. Мне это кажется разумным, поэтому я больше не беспокоюсь об этом направлении.

Я думаю, что аргумент наслоения противоречит обоим. Вы также можете утверждать, что более высокие уровни должны быть ответственны за отображение любой схемы пар (высокоуровневое имя, дополнительная информация), которую они используют для устранения неоднозначности импорта / экспорта, в однозначную схему низкоуровневых имен.

Для нижнего уровня всегда кажется предпочтительным быть как можно более ясным и недвусмысленным. Это упрощает систему и взаимодействие с ней - и возлагает бремя работы с более сложными схемами на экосистемы, которые этого хотят, а не на всех.

Что касается предложения о связывании в качестве отдельного слоя, это звучит хорошо абстрактно, но я немного скептически отношусь к тому, что мы могли бы заставить это работать гладко. Например, предложение в настоящее время позволяет внутренним модулям ссылаться на внешние пространства имен для статических объектов, таких как модули или типы, что кажется довольно центральным и не может быть сопоставлено с основной спецификацией (за исключением преобразования, эквивалентного лямбда-лифтингу, что нарушит предполагаемые шаблоны композиции, если мы также не введем частичное приложение / создание экземпляров импорта, т. е. форму «закрытия модуля»).

Например, предложение в настоящее время позволяет внутренним модулям ссылаться на внешние пространства имен для статических объектов, таких как модули или типы.

Это был один из аспектов, который, как мне казалось, требует некоторого упрощения.

если мы также не введем частичное применение / создание экземпляров импорта, т. е. форму «закрытия модуля»

Я думал, что это один из приемов, который поможет добиться такого упрощения.

Я обычно поддерживаю идею переноса спецификации связывания на уровень выше; это в основном то, за что я спорил. Двигателям не нужно знать имена.

@rossberg Я в целом считаю, что «ясный и недвусмысленный» отлично подходит для нижнего уровня, но этот пример больше касается того, должно ли быть предписывающим или нет, а нижние уровни должны хорошо себя вести, чтобы не предписывать вещи, которые их не касаются, ИМО.

Связанное с этим наблюдение есть в https://github.com/WebAssembly/design/issues/1399#issuecomment -808401005, где проблема описывает проблему с неудачными вызовами при вызове экспорта Wasm с пропущенными аргументами i64 в что разрешение дублирования экспорта и выбор одного с соответствующей арностью при вызове из JS в Wasm было бы предпочтительным решением в контексте.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги

Смежные вопросы

mfateev picture mfateev  ·  5Комментарии

jfbastien picture jfbastien  ·  6Комментарии

konsoletyper picture konsoletyper  ·  6Комментарии

JimmyVV picture JimmyVV  ·  4Комментарии

chicoxyzzy picture chicoxyzzy  ·  5Комментарии