Commit d493dead authored by Yuki Shiino's avatar Yuki Shiino Committed by Commit Bot

bind-gen: Implement expr to represent complicated conditionals

Supports expression objects, which are constructible from
web_idl.Exposure, and basic logical operations.

Bug: 839389
Change-Id: I8d402a41cdd0f3fc185c8302578605f560af1472
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1930760Reviewed-by: default avatarHitoshi Yoshida <peria@chromium.org>
Commit-Queue: Yuki Shiino <yukishiino@chromium.org>
Cr-Commit-Position: refs/heads/master@{#718548}
parent 01cbb042
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import web_idl
class _Expr(object):
"""
Represents an expression which is composable to produce another expression.
This is designed primarily to represent conditional expressions and basic
logical operators (expr_not, expr_and, expr_or) come along with.
"""
def __init__(self, expr, is_compound=False):
assert isinstance(expr, (bool, str))
assert isinstance(is_compound, bool)
if isinstance(expr, bool):
self._text = "true" if expr else "false"
else:
self._text = expr
self._is_compound = is_compound
self._is_always_false = expr is False
self._is_always_true = expr is True
def __str__(self):
"""
__str__ is designed to be used when composing another expression. If
you'd only like to have a string representation, |to_text| works better.
"""
if self._is_compound:
return "({})".format(self.to_text())
return self.to_text()
def to_text(self):
return self._text
@property
def is_always_false(self):
"""
The expression is always False, and code generators have chances of
optimizations.
"""
return self._is_always_false
@property
def is_always_true(self):
"""
The expression is always True, and code generators have chances of
optimizations.
"""
return self._is_always_true
def _unary_op(op, term):
assert isinstance(op, str)
assert isinstance(term, _Expr)
return _Expr("{}{}".format(op, term), is_compound=True)
def _binary_op(op, terms):
assert isinstance(op, str)
assert isinstance(terms, (list, tuple))
assert all(isinstance(term, _Expr) for term in terms)
assert all(
not (term.is_always_false or term.is_always_true) for term in terms)
return _Expr(op.join(map(str, terms)), is_compound=True)
def expr_not(term):
assert isinstance(term, _Expr)
if term.is_always_false:
return _Expr(True)
if term.is_always_true:
return _Expr(False)
return _unary_op("!", term)
def expr_and(terms):
assert isinstance(terms, (list, tuple))
assert all(isinstance(term, _Expr) for term in terms)
assert terms
if any(term.is_always_false for term in terms):
return _Expr(False)
terms = filter(lambda x: not x.is_always_true, terms)
if not terms:
return _Expr(True)
if len(terms) == 1:
return terms[0]
return _binary_op(" && ", terms)
def expr_or(terms):
assert isinstance(terms, (list, tuple))
assert all(isinstance(term, _Expr) for term in terms)
assert terms
if any(term.is_always_true for term in terms):
return _Expr(True)
terms = filter(lambda x: not x.is_always_false, terms)
if not terms:
return _Expr(False)
if len(terms) == 1:
return terms[0]
return _binary_op(" || ", terms)
def expr_from_exposure(exposure, in_global=None):
"""
Args:
exposure: web_idl.Exposure
in_global: A global name of [Exposed] that the ExecutionContext is
supposed to be / represent.
"""
assert isinstance(exposure, web_idl.Exposure)
assert in_global is None or isinstance(in_global, str)
def ref_enabled(feature):
return _Expr("RuntimeEnabledFeatures::{}Enabled()".format(feature))
top_terms = [_Expr(True)]
# [Exposed]
GLOBAL_NAME_TO_EXECUTION_CONTEXT_TEST = {
"AnimationWorklet": "IsAnimationWorkletGlobalScope",
"AudioWorklet": "IsAudioWorkletGlobalScope",
"DedicatedWorker": "IsDedicatedWorkerGlobalScope",
"LayoutWorklet": "IsLayoutWorkletGlobalScope",
"PaintWorklet": "IsPaintWorkletGlobalScope",
"ServiceWorker": "IsServiceWorkerGlobalScope",
"SharedWorker": "IsSharedWorkerGlobalScope",
"Window": "IsDocument",
"Worker": "IsWorkerGlobalScope",
"Worklet": "IsWorkletGlobalScope",
}
in_globals = set()
if in_global:
in_globals.add(in_global)
for category_name in ("Worker", "Worklet"):
if in_global.endswith(category_name):
in_globals.add(category_name)
exposed_terms = []
for entry in exposure.global_names_and_features:
terms = []
if entry.global_name not in in_globals:
pred = GLOBAL_NAME_TO_EXECUTION_CONTEXT_TEST[entry.global_name]
terms.append(_Expr("${{execution_context}}->{}()".format(pred)))
if entry.feature:
terms.append(ref_enabled(entry.feature))
if terms:
exposed_terms.append(expr_and(terms))
if exposed_terms:
top_terms.append(expr_or(exposed_terms))
# [RuntimeEnabled]
if exposure.runtime_enabled_features:
terms = map(ref_enabled, exposure.runtime_enabled_features)
top_terms.append(expr_or(terms))
# [SecureContext]
if exposure.only_in_secure_contexts is True:
top_terms.append(_Expr("${in_secure_context}"))
elif exposure.only_in_secure_contexts is False:
top_terms.append(_Expr(True))
else:
terms = map(ref_enabled, exposure.only_in_secure_contexts)
top_terms.append(
expr_or([_Expr("${in_secure_context}"),
expr_not(expr_and(terms))]))
return expr_and(top_terms)
...@@ -37,6 +37,7 @@ from .database import Database ...@@ -37,6 +37,7 @@ from .database import Database
from .database_builder import build_database from .database_builder import build_database
from .dictionary import Dictionary from .dictionary import Dictionary
from .enumeration import Enumeration from .enumeration import Enumeration
from .exposure import Exposure
from .idl_type import IdlType from .idl_type import IdlType
from .interface import Interface from .interface import Interface
from .namespace import Namespace from .namespace import Namespace
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment