github.com/igggame/nebulas-go@v2.1.0+incompatible/nf/nvm/module.go (about)

     1  // Copyright (C) 2017 go-nebulas authors
     2  //
     3  // This file is part of the go-nebulas library.
     4  //
     5  // the go-nebulas library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // the go-nebulas library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU General Public License
    16  // along with the go-nebulas library.  If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  
    19  package nvm
    20  
    21  import "C"
    22  
    23  import (
    24  	"fmt"
    25  	"regexp"
    26  	"strings"
    27  	"unsafe"
    28  
    29  	"github.com/nebulasio/go-nebulas/core"
    30  	"github.com/nebulasio/go-nebulas/util/logging"
    31  	"github.com/sirupsen/logrus"
    32  )
    33  
    34  // const
    35  const (
    36  	JSLibRootName    = "lib/"
    37  	JSLibRootNameLen = 4
    38  )
    39  
    40  var (
    41  	pathRe = regexp.MustCompile("^\\.{0,2}/")
    42  )
    43  
    44  // Module module structure.
    45  type Module struct {
    46  	id         string
    47  	source     string
    48  	lineOffset int
    49  }
    50  
    51  // Modules module maps.
    52  type Modules map[string]*Module
    53  
    54  // NewModules create new modules and return it.
    55  func NewModules() Modules {
    56  	return make(Modules, 1)
    57  }
    58  
    59  // NewModule create new module and return it.
    60  func NewModule(id, source string, lineOffset int) *Module {
    61  	if !pathRe.MatchString(id) {
    62  		id = fmt.Sprintf("lib/%s", id)
    63  	}
    64  	id = reformatModuleID(id)
    65  
    66  	return &Module{
    67  		id:         id,
    68  		source:     source,
    69  		lineOffset: lineOffset,
    70  	}
    71  }
    72  
    73  // Add add source to module.
    74  func (ms Modules) Add(m *Module) {
    75  	ms[m.id] = m
    76  }
    77  
    78  // Get get module from Modules by id.
    79  func (ms Modules) Get(id string) *Module {
    80  	return ms[id]
    81  }
    82  
    83  // RequireDelegateFunc delegate func for require.
    84  //export RequireDelegateFunc
    85  func RequireDelegateFunc(handler unsafe.Pointer, filename *C.char, lineOffset *C.size_t) *C.char {
    86  	id := C.GoString(filename)
    87  
    88  	e := getEngineByEngineHandler(handler)
    89  	if e == nil {
    90  		logging.VLog().WithFields(logrus.Fields{
    91  			"filename": id,
    92  		}).Error("require delegate handler does not found.")
    93  		return nil
    94  	}
    95  
    96  	module := e.modules.Get(id)
    97  	if module == nil {
    98  		return nil
    99  	}
   100  
   101  	*lineOffset = C.size_t(module.lineOffset)
   102  	cSource := C.CString(module.source)
   103  	return cSource
   104  }
   105  
   106  // AttachLibVersionDelegateFunc delegate func for lib version choose
   107  //export AttachLibVersionDelegateFunc
   108  func AttachLibVersionDelegateFunc(handler unsafe.Pointer, require *C.char) *C.char {
   109  	libname := C.GoString(require)
   110  	e := getEngineByEngineHandler(handler)
   111  	if e == nil {
   112  		logging.VLog().WithFields(logrus.Fields{
   113  			"libname": libname,
   114  		}).Error("delegate handler does not found.")
   115  		return nil
   116  	}
   117  	if len(libname) == 0 {
   118  		logging.VLog().Error("libname is empty.")
   119  		return nil
   120  	}
   121  
   122  	if e.ctx == nil {
   123  		logging.VLog().WithFields(logrus.Fields{
   124  			"libname": libname,
   125  		}).Error("e.context is nil.")
   126  		return nil
   127  	}
   128  	if e.ctx.block == nil {
   129  		logging.VLog().WithFields(logrus.Fields{
   130  			"libname": libname,
   131  		}).Error("e.context.block is nil.")
   132  		return nil
   133  	}
   134  
   135  	// for instruction_counter.js
   136  	if strings.HasSuffix(libname, "instruction_counter.js") {
   137  		v := core.GetNearestInstructionCounterVersionAtHeight(e.ctx.block.Height())
   138  		if len(v) == 0 {
   139  			logging.VLog().WithFields(logrus.Fields{
   140  				"libname":     libname,
   141  				"blockHeight": e.ctx.block.Height(),
   142  			}).Error("instruction_counter version not found.")
   143  			return nil
   144  		}
   145  		return C.CString(JSLibRootName + v + libname[JSLibRootNameLen-1:])
   146  	}
   147  
   148  	// block after core.V8JSLibVersionControlHeight, inclusive
   149  	if core.V8JSLibVersionControlAtHeight(e.ctx.block.Height()) {
   150  		if e.ctx.contract == nil {
   151  			logging.VLog().WithFields(logrus.Fields{
   152  				"libname": libname,
   153  				"height":  e.ctx.block.Height(),
   154  			}).Error("e.context.contract is nil.")
   155  			return nil
   156  		}
   157  		if e.ctx.contract.ContractMeta() == nil {
   158  			/*
   159  				logging.VLog().WithFields(logrus.Fields{
   160  					"libname": libname,
   161  					"height":  e.ctx.block.Height(),
   162  				}).Debug("e.context.contract.ContractMeta is nil.")
   163  			*/
   164  			return attachDefaultVersionLib(libname)
   165  		}
   166  		cv := e.ctx.contract.ContractMeta().Version
   167  
   168  		if len(cv) == 0 {
   169  			logging.VLog().WithFields(logrus.Fields{
   170  				"libname": libname,
   171  				"height":  e.ctx.block.Height(),
   172  			}).Error("contract deploy lib version is empty.")
   173  			return nil
   174  		}
   175  
   176  		if !strings.HasPrefix(libname, JSLibRootName) || strings.Contains(libname, "../") {
   177  			logging.VLog().WithFields(logrus.Fields{
   178  				"libname":       libname,
   179  				"height":        e.ctx.block.Height(),
   180  				"deployVersion": cv,
   181  			}).Error("invalid require path.")
   182  			return nil
   183  		}
   184  
   185  		ver := core.FindLastNearestLibVersion(cv, libname[JSLibRootNameLen:])
   186  		if len(ver) == 0 {
   187  			logging.VLog().WithFields(logrus.Fields{
   188  				"libname":      libname,
   189  				"deployLibVer": cv,
   190  			}).Error("lib version not found.")
   191  			return nil
   192  		}
   193  
   194  		return C.CString(JSLibRootName + ver + libname[JSLibRootNameLen-1:])
   195  	}
   196  
   197  	return attachDefaultVersionLib(libname)
   198  }
   199  
   200  func attachDefaultVersionLib(libname string) *C.char {
   201  	// block created before core.V8JSLibVersionControlHeight, default lib version: 1.0.0
   202  	if !strings.HasPrefix(libname, JSLibRootName) {
   203  		if strings.HasPrefix(libname, "/") {
   204  			libname = "lib" + libname
   205  		} else {
   206  			libname = JSLibRootName + libname
   207  		}
   208  	}
   209  	return C.CString(JSLibRootName + core.DefaultV8JSLibVersion + libname[JSLibRootNameLen-1:])
   210  }
   211  
   212  func reformatModuleID(id string) string {
   213  	paths := make([]string, 0)
   214  	for _, p := range strings.Split(id, "/") {
   215  		if len(p) == 0 || strings.Compare(".", p) == 0 {
   216  			continue
   217  		}
   218  		if strings.Compare("..", p) == 0 {
   219  			if len(paths) > 0 {
   220  				paths = paths[:len(paths)-1]
   221  				continue
   222  			}
   223  		}
   224  		paths = append(paths, p)
   225  	}
   226  
   227  	return strings.Join(paths, "/")
   228  }