github.com/grumpyhome/grumpy@v0.3.1-0.20201208125205-7b775405bdf1/grumpy-runtime-src/runtime/module.go (about)

     1  // Copyright 2016 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package grumpy
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"reflect"
    21  	"runtime/pprof"
    22  	"strings"
    23  	"sync"
    24  )
    25  
    26  type moduleState int
    27  
    28  const (
    29  	moduleStateNew moduleState = iota
    30  	moduleStateInitializing
    31  	moduleStateReady
    32  )
    33  
    34  var (
    35  	importMutex    sync.Mutex
    36  	moduleRegistry = map[string]*Code{}
    37  	// ModuleType is the object representing the Python 'module' type.
    38  	ModuleType = newBasisType("module", reflect.TypeOf(Module{}), toModuleUnsafe, ObjectType)
    39  	// SysModules is the global dict of imported modules, aka sys.modules.
    40  	SysModules = NewDict()
    41  )
    42  
    43  // Module represents Python 'module' objects.
    44  type Module struct {
    45  	Object
    46  	mutex recursiveMutex
    47  	state moduleState
    48  	code  *Code
    49  }
    50  
    51  // ModuleInit functions are called when importing Grumpy modules to execute the
    52  // top level code for that module.
    53  type ModuleInit func(f *Frame, m *Module) *BaseException
    54  
    55  // RegisterModule adds the named module to the registry so that it can be
    56  // subsequently imported.
    57  func RegisterModule(name string, c *Code) {
    58  	err := ""
    59  	importMutex.Lock()
    60  	if moduleRegistry[name] == nil {
    61  		moduleRegistry[name] = c
    62  	} else {
    63  		err = "module already registered: " + name
    64  	}
    65  	importMutex.Unlock()
    66  	if err != "" {
    67  		logFatal(err)
    68  	}
    69  }
    70  
    71  // ImportModule takes a fully qualified module name (e.g. a.b.c) and a slice of
    72  // code objects where the name of the i'th module is the prefix of name
    73  // ending in the i'th dot. The number of dot delimited parts of name must be the
    74  // same as the number of code objects. For each successive prefix, ImportModule
    75  // looks in sys.modules for an existing module with that name and if not
    76  // present creates a new module object, adds it to sys.modules and initializes
    77  // it with the corresponding code object. If the module was already present in
    78  // sys.modules, it is not re-initialized. The returned slice contains each
    79  // package and module initialized in this way in order.
    80  //
    81  // For example, ImportModule(f, "a.b", []*Code{a.Code, b.Code})
    82  // causes the initialization and entry into sys.modules of Grumpy module a and
    83  // then Grumpy module b. The two initialized modules are returned.
    84  //
    85  // If ImportModule is called in two threads concurrently to import the same
    86  // module, both invocations will produce the same module object and the module
    87  // is guaranteed to only be initialized once. The second invocation will not
    88  // return the module until it is fully initialized.
    89  func ImportModule(f *Frame, name string) ([]*Object, *BaseException) {
    90  	if strings.Contains(name, "/") {
    91  		o, raised := importOne(f, name)
    92  		if raised != nil {
    93  			return nil, raised
    94  		}
    95  		return []*Object{o}, nil
    96  	}
    97  	parts := strings.Split(name, ".")
    98  	numParts := len(parts)
    99  	result := make([]*Object, numParts)
   100  	var prev *Object
   101  	for i := 0; i < numParts; i++ {
   102  		name := strings.Join(parts[:i+1], ".")
   103  		o, raised := importOne(f, name)
   104  		if raised != nil {
   105  			return nil, raised
   106  		}
   107  		if prev != nil {
   108  			if raised := SetAttr(f, prev, NewStr(parts[i]), o); raised != nil {
   109  				return nil, raised
   110  			}
   111  		}
   112  		result[i] = o
   113  		prev = o
   114  	}
   115  	return result, nil
   116  }
   117  
   118  func importOne(f *Frame, name string) (*Object, *BaseException) {
   119  	var c *Code
   120  	// We do very limited locking here resulting in some
   121  	// sys.modules consistency gotchas.
   122  	importMutex.Lock()
   123  	o, raised := SysModules.GetItemString(f, name)
   124  	if raised == nil && o == nil {
   125  		if c = moduleRegistry[name]; c == nil {
   126  			msg := fmt.Sprintf("No module named %s", name)
   127  			raised = f.RaiseType(ImportErrorType, msg)
   128  		} else {
   129  			o = newModule(name, c.filename).ToObject()
   130  			raised = SysModules.SetItemString(f, name, o)
   131  		}
   132  	}
   133  	importMutex.Unlock()
   134  	if raised != nil {
   135  		return nil, raised
   136  	}
   137  	if o.isInstance(ModuleType) {
   138  		var raised *BaseException
   139  		m := toModuleUnsafe(o)
   140  		m.mutex.Lock(f)
   141  		if m.state == moduleStateNew {
   142  			m.state = moduleStateInitializing
   143  			if _, raised = c.Eval(f, m.Dict(), nil, nil); raised == nil {
   144  				m.state = moduleStateReady
   145  			} else {
   146  				// If the module failed to initialize
   147  				// then before we relinquish the module
   148  				// lock, remove it from sys.modules.
   149  				// Threads waiting on this module will
   150  				// fail when they don't find it in
   151  				// sys.modules below.
   152  				e, tb := f.ExcInfo()
   153  				if _, raised := SysModules.DelItemString(f, name); raised != nil {
   154  					f.RestoreExc(e, tb)
   155  				}
   156  			}
   157  		}
   158  		m.mutex.Unlock(f)
   159  		if raised != nil {
   160  			return nil, raised
   161  		}
   162  		// The result should be what's in sys.modules, not
   163  		// necessarily the originally created module since this
   164  		// is CPython's behavior.
   165  		o, raised = SysModules.GetItemString(f, name)
   166  		if raised != nil {
   167  			return nil, raised
   168  		}
   169  		if o == nil {
   170  			// This can happen in the pathological case
   171  			// where the module clears itself from
   172  			// sys.modules during execution and is handled
   173  			// by CPython in PyImport_ExecCodeModuleEx in
   174  			// import.c.
   175  			format := "Loaded module %s not found in sys.modules"
   176  			return nil, f.RaiseType(ImportErrorType, fmt.Sprintf(format, name))
   177  		}
   178  	}
   179  	return o, nil
   180  }
   181  
   182  // LoadMembers scans over all the members in module
   183  // and populates globals with them, taking __all__ into
   184  // account.
   185  func LoadMembers(f *Frame, module *Object) *BaseException {
   186  	allAttr, raised := GetAttr(f, module, NewStr("__all__"), nil)
   187  	if raised != nil && !raised.isInstance(AttributeErrorType) {
   188  		return raised
   189  	}
   190  	f.RestoreExc(nil, nil)
   191  
   192  	if raised == nil {
   193  		raised = loadMembersFromIterable(f, module, allAttr, nil)
   194  		if raised != nil {
   195  			return raised
   196  		}
   197  		return nil
   198  	}
   199  
   200  	// Fall back on __dict__
   201  	dictAttr := module.dict.ToObject()
   202  	raised = loadMembersFromIterable(f, module, dictAttr, func(key *Object) bool {
   203  		return strings.HasPrefix(toStrUnsafe(key).value, "_")
   204  	})
   205  	if raised != nil {
   206  		return raised
   207  	}
   208  	return nil
   209  }
   210  
   211  func loadMembersFromIterable(f *Frame, module, iterable *Object, filterF func(*Object) bool) *BaseException {
   212  	globals := f.Globals()
   213  	raised := seqForEach(f, iterable, func(memberName *Object) *BaseException {
   214  		if !memberName.isInstance(StrType) {
   215  			errorMessage := fmt.Sprintf("attribute name must be string, not '%v'", memberName.typ.Name())
   216  			return f.RaiseType(AttributeErrorType, errorMessage)
   217  		}
   218  		member, raised := GetAttr(f, module, toStrUnsafe(memberName), nil)
   219  		if raised != nil {
   220  			return raised
   221  		}
   222  		if filterF != nil && filterF(memberName) {
   223  			return nil
   224  		}
   225  		raised = globals.SetItem(f, memberName, member)
   226  		if raised != nil {
   227  			return raised
   228  		}
   229  		return nil
   230  	})
   231  	return raised
   232  }
   233  
   234  // newModule creates a new Module object with the given fully qualified name
   235  // (e.g a.b.c) and its corresponding Python filename and package.
   236  func newModule(name, filename string) *Module {
   237  	pkgName := ""
   238  	if strings.Contains(name, ".") {
   239  		pkgParts := strings.Split(name, ".")
   240  		pkgName = strings.Join(pkgParts[:len(pkgParts)-1], ".")
   241  	}
   242  
   243  	d := newStringDict(map[string]*Object{
   244  		"__file__":    NewStr(filename).ToObject(),
   245  		"__name__":    NewStr(name).ToObject(),
   246  		"__package__": NewStr(pkgName).ToObject(),
   247  		"__doc__":     None,
   248  	})
   249  	return &Module{Object: Object{typ: ModuleType, dict: d}}
   250  }
   251  
   252  func toModuleUnsafe(o *Object) *Module {
   253  	return (*Module)(o.toPointer())
   254  }
   255  
   256  // GetFilename returns the __file__ attribute of m, raising SystemError if it
   257  // does not exist.
   258  func (m *Module) GetFilename(f *Frame) (*Str, *BaseException) {
   259  	fileAttr, raised := GetAttr(f, m.ToObject(), NewStr("__file__"), None)
   260  	if raised != nil {
   261  		return nil, raised
   262  	}
   263  	if !fileAttr.isInstance(StrType) {
   264  		return nil, f.RaiseType(SystemErrorType, "module filename missing")
   265  	}
   266  	return toStrUnsafe(fileAttr), nil
   267  }
   268  
   269  // GetName returns the __name__ attribute of m, raising SystemError if it does
   270  // not exist.
   271  func (m *Module) GetName(f *Frame) (*Str, *BaseException) {
   272  	nameAttr, raised := GetAttr(f, m.ToObject(), internedName, None)
   273  	if raised != nil {
   274  		return nil, raised
   275  	}
   276  	if !nameAttr.isInstance(StrType) {
   277  		return nil, f.RaiseType(SystemErrorType, "nameless module")
   278  	}
   279  	return toStrUnsafe(nameAttr), nil
   280  }
   281  
   282  // ToObject upcasts m to an Object.
   283  func (m *Module) ToObject() *Object {
   284  	return &m.Object
   285  }
   286  
   287  func moduleInit(f *Frame, o *Object, args Args, _ KWArgs) (*Object, *BaseException) {
   288  	expectedTypes := []*Type{StrType, ObjectType}
   289  	argc := len(args)
   290  	if argc == 1 {
   291  		expectedTypes = expectedTypes[:1]
   292  	}
   293  	if raised := checkFunctionArgs(f, "__init__", args, expectedTypes...); raised != nil {
   294  		return nil, raised
   295  	}
   296  	if raised := SetAttr(f, o, internedName, args[0]); raised != nil {
   297  		return nil, raised
   298  	}
   299  	if argc > 1 {
   300  		if raised := SetAttr(f, o, NewStr("__doc__"), args[1]); raised != nil {
   301  			return nil, raised
   302  		}
   303  	}
   304  	return None, nil
   305  }
   306  
   307  func moduleRepr(f *Frame, o *Object) (*Object, *BaseException) {
   308  	m := toModuleUnsafe(o)
   309  	name := "?"
   310  	nameAttr, raised := m.GetName(f)
   311  	if raised == nil {
   312  		name = nameAttr.Value()
   313  	} else {
   314  		f.RestoreExc(nil, nil)
   315  	}
   316  	file := "(built-in)"
   317  	fileAttr, raised := m.GetFilename(f)
   318  	if raised == nil {
   319  		file = fmt.Sprintf("from '%s'", fileAttr.Value())
   320  	} else {
   321  		f.RestoreExc(nil, nil)
   322  	}
   323  	return NewStr(fmt.Sprintf("<module '%s' %s>", name, file)).ToObject(), nil
   324  }
   325  
   326  func initModuleType(map[string]*Object) {
   327  	ModuleType.slots.Init = &initSlot{moduleInit}
   328  	ModuleType.slots.Repr = &unaryOpSlot{moduleRepr}
   329  }
   330  
   331  // RunMain execs the given code object as a module under the name "__main__".
   332  // It handles any exceptions raised during module execution. If no exceptions
   333  // were raised then the return value is zero. If a SystemExit was raised then
   334  // the return value depends on its code attribute: None -> zero, integer values
   335  // are returned as-is. Other code values and exception types produce a return
   336  // value of 1.
   337  func RunMain(code *Code) int {
   338  	if file := os.Getenv("GRUMPY_PROFILE"); file != "" {
   339  		f, err := os.Create(file)
   340  		if err != nil {
   341  			logFatal(err.Error())
   342  		}
   343  		if err := pprof.StartCPUProfile(f); err != nil {
   344  			logFatal(err.Error())
   345  		}
   346  		defer pprof.StopCPUProfile()
   347  	}
   348  	m := newModule("__main__", code.filename)
   349  	m.state = moduleStateInitializing
   350  	f := NewRootFrame()
   351  	f.code = code
   352  	f.globals = m.Dict()
   353  	if raised := SysModules.SetItemString(f, "__main__", m.ToObject()); raised != nil {
   354  		Stderr.writeString(raised.String())
   355  	}
   356  	_, e := code.fn(f, nil)
   357  	if e == nil {
   358  		return 0
   359  	}
   360  	if !e.isInstance(SystemExitType) {
   361  		Stderr.writeString(FormatExc(f))
   362  		return 1
   363  	}
   364  	f.RestoreExc(nil, nil)
   365  	o, raised := GetAttr(f, e.ToObject(), NewStr("code"), nil)
   366  	if raised != nil {
   367  		return 1
   368  	}
   369  	if o.isInstance(IntType) {
   370  		return toIntUnsafe(o).Value()
   371  	}
   372  	if o == None {
   373  		return 0
   374  	}
   375  	if s, raised := ToStr(f, o); raised == nil {
   376  		Stderr.writeString(s.Value() + "\n")
   377  	}
   378  	return 1
   379  }