Пример #1
0
func TranslatePackage(importPath string, files []*ast.File, fileSet *token.FileSet, config *types.Config) ([]byte, error) {
	info := &types.Info{
		Types:      make(map[ast.Expr]types.Type),
		Values:     make(map[ast.Expr]exact.Value),
		Objects:    make(map[*ast.Ident]types.Object),
		Implicits:  make(map[ast.Node]types.Object),
		Selections: make(map[*ast.SelectorExpr]*types.Selection),
	}

	var errList ErrorList
	var previousErr error
	config.Error = func(err error) {
		if previousErr != nil && previousErr.Error() == err.Error() {
			return
		}
		errList = append(errList, err)
		previousErr = err
	}
	config.Sizes = sizes32
	typesPkg, err := config.Check(importPath, fileSet, files, info)
	if errList != nil {
		return nil, errList
	}
	if err != nil {
		return nil, err
	}
	config.Packages[importPath] = typesPkg

	c := &PkgContext{
		pkg:          typesPkg,
		info:         info,
		pkgVars:      make(map[string]string),
		objectVars:   make(map[types.Object]string),
		allVarNames:  make(map[string]int),
		postLoopStmt: make(map[string]ast.Stmt),
		positions:    make(map[int]token.Pos),
	}
	for _, name := range ReservedKeywords {
		c.allVarNames[name] = 1
	}

	functionsByType := make(map[types.Type][]*ast.FuncDecl)
	functionsByObject := make(map[types.Object]*ast.FuncDecl)
	var initStmts []ast.Stmt
	var typeSpecs []*ast.TypeSpec
	var constSpecs []*ast.ValueSpec
	var varSpecs []*ast.ValueSpec
	for _, file := range files {
		for _, decl := range file.Decls {
			switch d := decl.(type) {
			case *ast.FuncDecl:
				sig := c.info.Objects[d.Name].(*types.Func).Type().(*types.Signature)
				var recvType types.Type
				if sig.Recv() != nil {
					recvType = sig.Recv().Type()
					if ptr, isPtr := recvType.(*types.Pointer); isPtr {
						recvType = ptr.Elem()
					}
				}
				if sig.Recv() == nil && d.Name.Name == "init" {
					initStmts = append(initStmts, d.Body.List...)
					continue
				}
				functionsByType[recvType] = append(functionsByType[recvType], d)
				o := c.info.Objects[d.Name]
				functionsByObject[o] = d
				if sig.Recv() == nil {
					c.objectName(o) // register toplevel name
				}
			case *ast.GenDecl:
				switch d.Tok {
				case token.TYPE:
					for _, spec := range d.Specs {
						s := spec.(*ast.TypeSpec)
						typeSpecs = append(typeSpecs, s)
						c.objectName(c.info.Objects[s.Name]) // register toplevel name
					}
				case token.CONST:
					for _, spec := range d.Specs {
						s := spec.(*ast.ValueSpec)
						constSpecs = append(constSpecs, s)
						for _, name := range s.Names {
							if !isBlank(name) {
								c.objectName(c.info.Objects[name]) // register toplevel name
							}
						}
					}
				case token.VAR:
					for _, spec := range d.Specs {
						s := spec.(*ast.ValueSpec)
						varSpecs = append(varSpecs, s)
						for _, name := range s.Names {
							if !isBlank(name) {
								c.objectName(c.info.Objects[name]) // register toplevel name
							}
						}
					}
				}
			}
		}
	}

	// resolve var dependencies
	var unorderedSingleVarSpecs []*ast.ValueSpec
	pendingObjects := make(map[types.Object]bool)
	for _, spec := range varSpecs {
		for _, singleSpec := range c.splitValueSpec(spec) {
			if singleSpec.Values[0] == nil {
				continue
			}
			unorderedSingleVarSpecs = append(unorderedSingleVarSpecs, singleSpec)
			for _, name := range singleSpec.Names {
				pendingObjects[c.info.Objects[name]] = true
			}
		}
	}
	complete := false
	var intVarStmts []ast.Stmt
	for !complete {
		complete = true
		for i, spec := range unorderedSingleVarSpecs {
			if spec == nil {
				continue
			}
			v := DependencyCollector{info: c.info, functions: functionsByObject}
			ast.Walk(&v, spec.Values[0])
			currentObjs := make(map[types.Object]bool)
			for _, name := range spec.Names {
				currentObjs[c.info.Objects[name]] = true
			}
			ready := true
			for _, dep := range v.dependencies {
				if currentObjs[dep] {
					return nil, fmt.Errorf("%s: initialization loop", fileSet.Position(dep.Pos()).String())
				}
				ready = ready && !pendingObjects[dep]
			}
			if !ready {
				complete = false
				continue
			}
			lhs := make([]ast.Expr, len(spec.Names))
			for i, name := range spec.Names {
				lhs[i] = name
				delete(pendingObjects, c.info.Objects[name])
			}
			intVarStmts = append(intVarStmts, &ast.AssignStmt{
				Lhs: lhs,
				Tok: token.DEFINE,
				Rhs: spec.Values,
			})
			unorderedSingleVarSpecs[i] = nil
		}
	}

	c.Indent(func() {
		for _, importedPkg := range typesPkg.Imports() {
			varName := c.newVariable(importedPkg.Name())
			c.Printf(`var %s = Go$packages["%s"];`, varName, importedPkg.Path())
			c.pkgVars[importedPkg.Path()] = varName
		}

		// types and their functions
		for _, spec := range typeSpecs {
			obj := c.info.Objects[spec.Name]
			typeName := c.objectName(obj)
			c.Printf("var %s;", typeName)
			c.translateTypeSpec(spec)
			for _, fun := range functionsByType[obj.Type()] {
				_, isStruct := obj.Type().Underlying().(*types.Struct)
				c.translateMethod(typeName, isStruct, fun)
			}
			c.Printf("Go$pkg.%s = %s;", typeName, typeName)
		}

		// package functions
		for _, fun := range functionsByType[nil] {
			if isBlank(fun.Name) {
				continue
			}
			c.newScope(func() {
				name := c.objectName(c.info.Objects[fun.Name])
				params := c.translateParams(fun.Type)
				c.Printf("var %s = function(%s) {", name, strings.Join(params, ", "))
				c.Indent(func() {
					jsCode, _ := typesPkg.Scope().Lookup("js_" + name).(*types.Const)
					if jsCode != nil {
						c.Write([]byte(exact.StringVal(jsCode.Val())))
						c.Write([]byte{'\n'})
						return
					}
					if fun.Body == nil {
						c.Printf(`throw new Go$Panic("Native function not implemented: %s");`, name)
						return
					}

					c.translateFunctionBody(fun.Body.List, c.info.Objects[fun.Name].Type().(*types.Signature))
				})
				c.Printf("};")
			})
		}

		// constants
		for _, spec := range constSpecs {
			for _, name := range spec.Names {
				if isBlank(name) || strings.HasPrefix(name.Name, "js_") {
					continue
				}
				o := c.info.Objects[name].(*types.Const)
				c.info.Types[name] = o.Type()
				c.info.Values[name] = o.Val()
				c.Printf("%s = %s;", c.objectName(o), c.translateExpr(name))
			}
		}

		// variables
		for _, spec := range varSpecs {
			for _, name := range spec.Names {
				o := c.info.Objects[name].(*types.Var)
				c.Printf("%s = %s;", c.objectName(o), c.zeroValue(o.Type()))
			}
		}

		// native implementations
		if native, hasNative := natives[importPath]; hasNative {
			c.Write([]byte(strings.TrimSpace(native)))
			c.Write([]byte{'\n'})
		}

		// exports for package functions
		for _, fun := range functionsByType[nil] {
			name := fun.Name.Name
			if fun.Name.IsExported() || name == "main" {
				c.Printf("Go$pkg.%s = %s;", name, name)
			}
		}

		// init function
		c.Printf("Go$pkg.init = function() {")
		c.Indent(func() {
			c.translateFunctionBody(append(intVarStmts, initStmts...), nil)
		})
		c.Printf("};")
	})

	return c.output, nil
}