ソースを参照

Generate code that checks if globals exist in CFG construction logic

jonathanvdc 8 年 前
コミット
13cdd84064
1 ファイル変更54 行追加4 行削除
  1. 54 4
      kernel/modelverse_jit/bytecode_to_cfg.py

+ 54 - 4
kernel/modelverse_jit/bytecode_to_cfg.py

@@ -157,6 +157,48 @@ class AnalysisState(object):
 
     def analyze_resolve(self, instruction):
         """Analyzes a 'resolve' instruction."""
+        def __resolve_global_carefully():
+            # We might be resolving a global that does not exist. In that case, we
+            # need to throw. We want to create the following blocks:
+            #
+            # !current_block(...):
+            #     ...
+            #     $resolved_global = resolve-global global
+            #     $nothing = literal None
+            #     $condition = binary $resolved_global, 'is', $nothing
+            #     select $condition, !no_global_block(), !global_exists_block()
+            #
+            # !no_global_block():
+            #     $message = literal <GLOBAL_NOT_FOUND_MESSAGE_FORMAT % instruction.variable.name>
+            #     $exception = direct-call "simple-positional" Exception(message=$message)
+            #     throw $exception
+            #
+            # !global_exists_block():
+            #     ...
+            #
+            no_global_block = cfg_ir.BasicBlock(self.counter)
+            global_exists_block = cfg_ir.BasicBlock(self.counter)
+
+            resolved_global = self.current_block.append_definition(
+                cfg_ir.ResolveGlobal(instruction.variable))
+            nothing = self.current_block.append_definition(cfg_ir.Literal(None))
+            condition = self.current_block.append_definition(
+                cfg_ir.Binary(resolved_global, 'is', nothing))
+            self.current_block.flow = cfg_ir.SelectFlow(
+                condition, cfg_ir.Branch(no_global_block), cfg_ir.Branch(global_exists_block))
+
+            message = no_global_block.append_definition(
+                cfg_ir.Literal(
+                    jit_runtime.GLOBAL_NOT_FOUND_MESSAGE_FORMAT % instruction.variable.name))
+            exception = no_global_block.append_definition(
+                cfg_ir.DirectFunctionCall(
+                    'Exception',
+                    [('message', message)],
+                    cfg_ir.SIMPLE_POSITIONAL_CALLING_CONVENTION))
+            no_global_block.flow = cfg_ir.ThrowFlow(exception)
+
+            self.current_block = global_exists_block
+            return resolved_global
         return self.emit_select(
             lambda:
             self.current_block.append_definition(
@@ -164,9 +206,7 @@ class AnalysisState(object):
             lambda:
             self.current_block.append_definition(
                 cfg_ir.ResolveLocal(instruction.variable)),
-            lambda:
-            self.current_block.append_definition(
-                cfg_ir.ResolveGlobal(instruction.variable)))
+            __resolve_global_carefully)
 
     def analyze_declare(self, instruction):
         """Analyzes a 'declare' instruction."""
@@ -175,7 +215,17 @@ class AnalysisState(object):
 
     def analyze_global(self, instruction):
         """Analyzes a 'global' instruction."""
-        return self.current_block.append_definition(cfg_ir.DeclareGlobal(instruction.variable))
+        resolved_global = self.current_block.append_definition(
+            cfg_ir.ResolveGlobal(instruction.variable))
+        nothing = self.current_block.append_definition(cfg_ir.Literal(None))
+        return self.emit_select(
+            lambda:
+            self.current_block.append_definition(
+                cfg_ir.Binary(resolved_global, 'is', nothing)),
+            lambda:
+            self.current_block.append_definition(
+                cfg_ir.DeclareGlobal(instruction.variable)),
+            lambda: resolved_global)
 
     def analyze_assign(self, instruction):
         """Analyzes an 'assign' instruction."""