1# Copyright 2019 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import web_idl 6 7_CODE_GEN_EXPR_PASS_KEY = object() 8 9 10class CodeGenExpr(object): 11 """ 12 Represents an expression which is composable to produce another expression. 13 14 This is designed primarily to represent conditional expressions and basic 15 logical operators (expr_not, expr_and, expr_or) come along with. 16 """ 17 18 def __init__(self, expr, is_compound=False, pass_key=None): 19 assert isinstance(expr, (bool, str)) 20 assert isinstance(is_compound, bool) 21 assert pass_key is _CODE_GEN_EXPR_PASS_KEY 22 23 if isinstance(expr, bool): 24 self._text = "true" if expr else "false" 25 else: 26 self._text = expr 27 self._is_compound = is_compound 28 self._is_always_false = expr is False 29 self._is_always_true = expr is True 30 31 def __eq__(self, other): 32 if not isinstance(self, other.__class__): 33 return NotImplemented 34 # Assume that, as long as the two texts are the same, the two 35 # expressions must be the same, i.e. |_is_compound|, etc. must be the 36 # same or do not matter. 37 return self.to_text() == other.to_text() 38 39 def __ne__(self, other): 40 return not (self == other) 41 42 def __hash__(self): 43 return hash(self.to_text()) 44 45 def __str__(self): 46 """ 47 __str__ is designed to be used when composing another expression. If 48 you'd only like to have a string representation, |to_text| works better. 49 """ 50 if self._is_compound: 51 return "({})".format(self.to_text()) 52 return self.to_text() 53 54 def to_text(self): 55 return self._text 56 57 @property 58 def is_always_false(self): 59 """ 60 The expression is always False, and code generators have chances of 61 optimizations. 62 """ 63 return self._is_always_false 64 65 @property 66 def is_always_true(self): 67 """ 68 The expression is always True, and code generators have chances of 69 optimizations. 70 """ 71 return self._is_always_true 72 73 74def _Expr(*args, **kwargs): 75 return CodeGenExpr(*args, pass_key=_CODE_GEN_EXPR_PASS_KEY, **kwargs) 76 77 78def _unary_op(op, term): 79 assert isinstance(op, str) 80 assert isinstance(term, CodeGenExpr) 81 82 return _Expr("{}{}".format(op, term), is_compound=True) 83 84 85def _binary_op(op, terms): 86 assert isinstance(op, str) 87 assert isinstance(terms, (list, tuple)) 88 assert all(isinstance(term, CodeGenExpr) for term in terms) 89 assert all( 90 not (term.is_always_false or term.is_always_true) for term in terms) 91 92 return _Expr(op.join(map(str, terms)), is_compound=True) 93 94 95def expr_not(term): 96 assert isinstance(term, CodeGenExpr) 97 98 if term.is_always_false: 99 return _Expr(True) 100 if term.is_always_true: 101 return _Expr(False) 102 return _unary_op("!", term) 103 104 105def expr_and(terms): 106 assert isinstance(terms, (list, tuple)) 107 assert all(isinstance(term, CodeGenExpr) for term in terms) 108 assert terms 109 110 if any(term.is_always_false for term in terms): 111 return _Expr(False) 112 terms = filter(lambda x: not x.is_always_true, terms) 113 if not terms: 114 return _Expr(True) 115 if len(terms) == 1: 116 return terms[0] 117 return _binary_op(" && ", expr_uniq(terms)) 118 119 120def expr_or(terms): 121 assert isinstance(terms, (list, tuple)) 122 assert all(isinstance(term, CodeGenExpr) for term in terms) 123 assert terms 124 125 if any(term.is_always_true for term in terms): 126 return _Expr(True) 127 terms = filter(lambda x: not x.is_always_false, terms) 128 if not terms: 129 return _Expr(False) 130 if len(terms) == 1: 131 return terms[0] 132 return _binary_op(" || ", expr_uniq(terms)) 133 134 135def expr_uniq(terms): 136 assert isinstance(terms, (list, tuple)) 137 assert all(isinstance(term, CodeGenExpr) for term in terms) 138 139 uniq_terms = [] 140 for term in terms: 141 if term not in uniq_terms: 142 uniq_terms.append(term) 143 return uniq_terms 144 145 146def expr_from_exposure(exposure, 147 global_names=None, 148 may_use_feature_selector=False): 149 """ 150 Returns an expression to determine whether this property should be exposed 151 or not. 152 153 Args: 154 exposure: web_idl.Exposure of the target construct. 155 global_names: When specified, it's taken into account that the global 156 object implements |global_names|. 157 may_use_feature_selector: True enables use of ${feature_selector} iff 158 the exposure is context dependent. 159 """ 160 assert isinstance(exposure, web_idl.Exposure) 161 assert (global_names is None 162 or (isinstance(global_names, (list, tuple)) 163 and all(isinstance(name, str) for name in global_names))) 164 165 # The property exposures are categorized into three. 166 # - Unconditional: Always exposed. 167 # - Context-independent: Enabled per v8::Isolate. 168 # - Context-dependent: Enabled per v8::Context, e.g. origin trials. 169 # 170 # Context-dependent properties can be installed in two phases. 171 # - The first phase installs all the properties that are associated with the 172 # features enabled at the moment. This phase is represented by 173 # FeatureSelector as FeatureSelector.IsAll(). 174 # - The second phase installs the properties associated with the specified 175 # feature. This phase is represented as FeatureSelector.IsAny(feature). 176 # 177 # The exposure condition is represented as; 178 # (and feature_selector-independent-term 179 # (or 180 # feature_selector-1st-phase-term 181 # feature_selector-2nd-phase-term)) 182 # which can be represented in more details as: 183 # (and secure_context_term 184 # uncond_exposed_term 185 # (or 186 # (and feature_selector.IsAll() # 1st phase; all enabled 187 # cond_exposed_term 188 # (or feature_enabled_term 189 # context_enabled_term)) 190 # (or exposed_selector_term # 2nd phase; any selected 191 # feature_selector_term))) 192 # where 193 # secure_context_term represents [SecureContext=F1] 194 # uncond_exposed_term represents [Exposed=(G1, G2)] 195 # cond_exposed_term represents [Exposed(G1 F1, G2 F2)] 196 # feature_enabled_term represents [RuntimeEnabled=(F1, F2)] 197 # context_enabled_term represents [ContextEnabled=F1] 198 # exposed_selector_term represents [Exposed(G1 F1, G2 F2)] 199 # feature_selector_term represents [RuntimeEnabled=(F1, F2)] 200 uncond_exposed_terms = [] 201 cond_exposed_terms = [] 202 feature_enabled_terms = [] 203 context_enabled_terms = [] 204 exposed_selector_terms = [] 205 feature_selector_names = [] # Will turn into feature_selector.IsAnyOf(...) 206 207 def ref_enabled(feature): 208 arg = "${execution_context}" if feature.is_context_dependent else "" 209 return _Expr("RuntimeEnabledFeatures::{}Enabled({})".format( 210 feature, arg)) 211 212 def ref_selected(features): 213 feature_tokens = map( 214 lambda feature: "OriginTrialFeature::k{}".format(feature), 215 features) 216 return _Expr("${{feature_selector}}.IsAnyOf({})".format( 217 ", ".join(feature_tokens))) 218 219 # [SecureContext] 220 if exposure.only_in_secure_contexts is True: 221 secure_context_term = _Expr("${is_in_secure_context}") 222 elif exposure.only_in_secure_contexts is False: 223 secure_context_term = _Expr(True) 224 else: 225 terms = map(ref_enabled, exposure.only_in_secure_contexts) 226 secure_context_term = expr_or( 227 [_Expr("${is_in_secure_context}"), 228 expr_not(expr_and(terms))]) 229 230 # [Exposed] 231 GLOBAL_NAME_TO_EXECUTION_CONTEXT_TEST = { 232 "AnimationWorklet": "IsAnimationWorkletGlobalScope", 233 "AudioWorklet": "IsAudioWorkletGlobalScope", 234 "DedicatedWorker": "IsDedicatedWorkerGlobalScope", 235 "LayoutWorklet": "IsLayoutWorkletGlobalScope", 236 "PaintWorklet": "IsPaintWorkletGlobalScope", 237 "ServiceWorker": "IsServiceWorkerGlobalScope", 238 "SharedWorker": "IsSharedWorkerGlobalScope", 239 "Window": "IsWindow", 240 "Worker": "IsWorkerGlobalScope", 241 "Worklet": "IsWorkletGlobalScope", 242 } 243 if global_names: 244 matched_global_count = 0 245 for entry in exposure.global_names_and_features: 246 if entry.global_name not in global_names: 247 continue 248 matched_global_count += 1 249 if entry.feature: 250 cond_exposed_terms.append(ref_enabled(entry.feature)) 251 if entry.feature.is_context_dependent: 252 feature_selector_names.append(entry.feature) 253 assert (not exposure.global_names_and_features 254 or matched_global_count > 0) 255 else: 256 for entry in exposure.global_names_and_features: 257 pred_term = _Expr("${{execution_context}}->{}()".format( 258 GLOBAL_NAME_TO_EXECUTION_CONTEXT_TEST[entry.global_name])) 259 if not entry.feature: 260 uncond_exposed_terms.append(pred_term) 261 else: 262 cond_exposed_terms.append( 263 expr_and([pred_term, ref_enabled(entry.feature)])) 264 if entry.feature.is_context_dependent: 265 exposed_selector_terms.append( 266 expr_and([pred_term, 267 ref_selected([entry.feature])])) 268 269 # [RuntimeEnabled] 270 if exposure.runtime_enabled_features: 271 feature_enabled_terms.extend( 272 map(ref_enabled, exposure.runtime_enabled_features)) 273 feature_selector_names.extend( 274 exposure.context_dependent_runtime_enabled_features) 275 276 # [ContextEnabled] 277 if exposure.context_enabled_features: 278 terms = map( 279 lambda feature: _Expr( 280 "${{context_feature_settings}}->is{}Enabled()".format( 281 feature)), exposure.context_enabled_features) 282 context_enabled_terms.append( 283 expr_and([_Expr("${context_feature_settings}"), 284 expr_or(terms)])) 285 286 # Build an expression. 287 top_level_terms = [] 288 top_level_terms.append(secure_context_term) 289 if uncond_exposed_terms: 290 top_level_terms.append(expr_or(uncond_exposed_terms)) 291 292 if not (may_use_feature_selector 293 and exposure.is_context_dependent(global_names)): 294 if cond_exposed_terms: 295 top_level_terms.append(expr_or(cond_exposed_terms)) 296 if feature_enabled_terms: 297 top_level_terms.append(expr_and(feature_enabled_terms)) 298 return expr_and(top_level_terms) 299 300 all_enabled_terms = [_Expr("${feature_selector}.IsAll()")] 301 if cond_exposed_terms: 302 all_enabled_terms.append(expr_or(cond_exposed_terms)) 303 if feature_enabled_terms or context_enabled_terms: 304 terms = [] 305 if feature_enabled_terms: 306 terms.append(expr_and(feature_enabled_terms)) 307 if context_enabled_terms: 308 terms.append(expr_or(context_enabled_terms)) 309 all_enabled_terms.append(expr_or(terms)) 310 311 selector_terms = [] 312 if exposed_selector_terms: 313 selector_terms.append(expr_or(exposed_selector_terms)) 314 if feature_selector_names: 315 selector_terms.append(ref_selected(feature_selector_names)) 316 317 terms = [] 318 terms.append(expr_and(all_enabled_terms)) 319 if selector_terms: 320 terms.append(expr_or(selector_terms)) 321 top_level_terms.append(expr_or(terms)) 322 323 return expr_and(top_level_terms) 324