/** * Sanity checks code generation by performing it once, parsing the result, then generating code * from the second parse tree to verify that it matches the code generated from the first parse * tree. * * @return The regenerated parse tree. Null on error. */ private Node sanityCheckCodeGeneration(Node root) { if (compiler.hasHaltingErrors()) { // Don't even bother checking code generation if we already know the // the code is bad. return null; } String source = compiler.toSource(root); Node root2 = compiler.parseSyntheticCode(source); if (compiler.hasHaltingErrors()) { compiler.report( JSError.make( CANNOT_PARSE_GENERATED_CODE, Strings.truncateAtMaxLength(source, 100, true))); // Throw an exception, so that the infrastructure will tell us // which pass violated the sanity check. throw new IllegalStateException("Sanity Check failed"); } String source2 = compiler.toSource(root2); if (!source.equals(source2)) { compiler.report( JSError.make( GENERATED_BAD_CODE, Strings.truncateAtMaxLength(source, 1000, true), Strings.truncateAtMaxLength(source2, 1000, true))); // Throw an exception, so that the infrastructure will tell us // which pass violated the sanity check. throw new IllegalStateException("Sanity Check failed"); } return root2; }
public void onRedeclaration( Scope s, String name, Node n, Node parent, Node gramps, Node nodeWithLineNumber) { // Don't allow multiple variables to be declared at the top level scope if (scope.isGlobal()) { Scope.Var origVar = scope.getVar(name); Node origParent = origVar.getParentNode(); if (origParent.getType() == Token.CATCH && parent.getType() == Token.CATCH) { // Okay, both are 'catch(x)' variables. return; } boolean allowDupe = false; JSDocInfo info = n.getJSDocInfo(); if (info == null) { info = parent.getJSDocInfo(); } allowDupe = info != null && info.getSuppressions().contains("duplicate"); if (!allowDupe) { compiler.report( JSError.make( sourceName, nodeWithLineNumber, VAR_MULTIPLY_DECLARED_ERROR, name, (origVar.input != null ? origVar.input.getName() : "??"))); } } else if (name.equals(ARGUMENTS) && !NodeUtil.isVarDeclaration(n)) { // Disallow shadowing "arguments" as we can't handle with our current // scope modeling. compiler.report(JSError.make(sourceName, nodeWithLineNumber, VAR_ARGUMENTS_SHADOWED_ERROR)); } }
/** * Emits a warning for each default value parameter that has the wrong type and for each getter * function that was used for the wrong type of tweak. */ void emitAllTypeWarnings() { for (TweakFunctionCall call : functionCalls) { Node valueNode = call.valueNode; TweakFunction tweakFunc = call.tweakFunc; TweakFunction registerFunc = registerCall.tweakFunc; if (valueNode != null) { // For register* and overrideDefaultValue calls, ensure the default // value is a literal of the correct type. if (!registerFunc.isValidNodeType(valueNode.getType())) { compiler.report( JSError.make( valueNode, INVALID_TWEAK_DEFAULT_VALUE_WARNING, tweakId, registerFunc.getName(), registerFunc.getExpectedTypeName())); } } else if (tweakFunc.isGetterFunction()) { // For getter calls, ensure the correct getter was used. if (!tweakFunc.isCorrectRegisterFunction(registerFunc)) { compiler.report( JSError.make( call.callNode, TWEAK_WRONG_GETTER_TYPE_WARNING, tweakFunc.getName(), registerFunc.getName())); } } } }
public void testOrderingCharno2() throws Exception { JSError e1 = JSError.make(NULL_SOURCE, 8, 7, FOO_TYPE); JSError e2 = JSError.make(NULL_SOURCE, 8, 5, FOO_TYPE); assertSmaller(error(e2), error(e1)); // CheckLevel preempts charno comparison assertSmaller(warning(e2), error(e1)); }
/** Processes a OBJECTLIT node. */ private void handleObjectLit(NodeTraversal t, Node n) { Node child = n.getFirstChild(); while (child != null) { // Maybe STRING, NUMBER, GET, SET if (child.getType() != Token.NUMBER) { // We should never see a mix of numbers and strings. String name = child.getString(); T type = typeSystem.getType(getScope(), n, name); Property prop = getProperty(name); if (!prop.scheduleRenaming(child, processProperty(t, prop, type, null))) { if (showInvalidationWarnings) { compiler.report( JSError.make( t.getSourceName(), child, INVALIDATION, name, (type == null ? "null" : type.toString()), n.toString())); } } } child = child.getNext(); } }
private void visitScriptNode() { for (Map.Entry<String, Node> ctorEntry : ctors.entrySet()) { String ctor = ctorEntry.getKey(); int index = -1; boolean found = false; if (ctor.startsWith("$jscomp.")) { continue; } do { index = ctor.indexOf('.', index + 1); String provideKey = index == -1 ? ctor : ctor.substring(0, index); if (provides.containsKey(provideKey)) { found = true; break; } } while (index != -1); if (!found) { Node n = ctorEntry.getValue(); compiler.report(JSError.make(n, checkLevel, MISSING_PROVIDE_WARNING, ctorEntry.getKey())); } } provides.clear(); ctors.clear(); }
/** Add an @this annotation to all functions in the objLit. */ private void addTypesToFunctions(Node objLit, String thisType) { Preconditions.checkState(objLit.isObjectLit()); for (Node keyNode : objLit.children()) { Node value = keyNode.getLastChild(); if (value != null && value.isFunction()) { JSDocInfoBuilder fnDoc = JSDocInfoBuilder.maybeCopyFrom(keyNode.getJSDocInfo()); fnDoc.recordThisType( new JSTypeExpression(new Node(Token.BANG, IR.string(thisType)), VIRTUAL_FILE)); keyNode.setJSDocInfo(fnDoc.build()); } } // Add @this and @return to default property values. for (MemberDefinition property : extractProperties(objLit)) { if (!property.value.isObjectLit()) { continue; } if (hasShorthandAssignment(property.value)) { compiler.report(JSError.make(property.value, POLYMER_SHORTHAND_NOT_SUPPORTED)); return; } Node defaultValue = NodeUtil.getFirstPropMatchingKey(property.value, "value"); if (defaultValue == null || !defaultValue.isFunction()) { continue; } Node defaultValueKey = defaultValue.getParent(); JSDocInfoBuilder fnDoc = JSDocInfoBuilder.maybeCopyFrom(defaultValueKey.getJSDocInfo()); fnDoc.recordThisType( new JSTypeExpression(new Node(Token.BANG, IR.string(thisType)), VIRTUAL_FILE)); fnDoc.recordReturnType(getTypeFromProperty(property)); defaultValueKey.setJSDocInfo(fnDoc.build()); } }
private void checkClassSuperReferences(Node classNode) { Node className = classNode.getFirstChild(); Node superClassName = className.getNext(); if (NodeUtil.referencesSuper(classNode) && !superClassName.isQualifiedName()) { compiler.report(JSError.make(classNode, NO_SUPERTYPE)); } }
@Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isAssign() || n.getFirstChild() == className) { return; } if (className.matchesQualifiedName(n.getFirstChild())) { compiler.report(JSError.make(n, CLASS_REASSIGNMENT)); } }
@Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isCall() && isGoogDefineClass(n)) { if (!validateUsage(n)) { compiler.report(JSError.make(n, GOOG_CLASS_TARGET_INVALID)); } } maybeRewriteClassDefinition(n); }
@Override public boolean isModule() { checkErrorManager(); try { regenerateDependencyInfoIfNecessary(); return isModuleFile; } catch (IOException e) { compiler .getErrorManager() .report(CheckLevel.ERROR, JSError.make(AbstractCompiler.READ_ERROR, getName())); return false; } }
/** Sets the default values of tweaks based on compiler options. */ private void applyCompilerDefaultValueOverrides(Map<String, TweakInfo> tweakInfos) { for (Entry<String, Node> entry : compilerDefaultValueOverrides.entrySet()) { String tweakId = entry.getKey(); TweakInfo tweakInfo = tweakInfos.get(tweakId); if (tweakInfo == null) { compiler.report(JSError.make(UNKNOWN_TWEAK_WARNING, tweakId)); } else { TweakFunction registerFunc = tweakInfo.registerCall.tweakFunc; Node value = entry.getValue(); if (!registerFunc.isValidNodeType(value.getType())) { compiler.report( JSError.make( INVALID_TWEAK_DEFAULT_VALUE_WARNING, tweakId, registerFunc.getName(), registerFunc.getExpectedTypeName())); } else { tweakInfo.defaultValueNode = value; } } } }
private void maybeRewriteClassDefinition(Node n, Node target, Node value) { if (isGoogDefineClass(value)) { if (!target.isQualifiedName()) { compiler.report(JSError.make(n, GOOG_CLASS_TARGET_INVALID)); } ClassDefinition def = extractClassDefinition(target, value); if (def != null) { value.detachFromParent(); target.detachFromParent(); rewriteGoogDefineClass(n, def); } } }
/** Gets a list of types provided by this input. */ @Override public Collection<String> getProvides() { checkErrorManager(); try { regenerateDependencyInfoIfNecessary(); return Collections.unmodifiableSet(provides); } catch (IOException e) { compiler .getErrorManager() .report(CheckLevel.ERROR, JSError.make(AbstractCompiler.READ_ERROR, getName())); return ImmutableList.of(); } }
private void reportExtraRequireWarning(Node call, String require) { if (DEFAULT_EXTRA_NAMESPACES.contains(require)) { return; } JSDocInfo jsDoc = call.getJSDocInfo(); if (jsDoc != null && jsDoc.getSuppressions().contains("extraRequire")) { // There is a @suppress {extraRequire} on the call node. Even though the compiler generally // doesn't understand @suppress in that position, respect it in this case, // since lots of people put it there to suppress the closure-linter's extraRequire check. return; } compiler.report(JSError.make(call, EXTRA_REQUIRE_WARNING, require)); }
void inferScope(Node n, Scope scope) { TypeInference typeInference = new TypeInference( compiler, computeCfg(n), reverseInterpreter, scope, assertionFunctionsMap); try { typeInference.analyze(); // Resolve any new type names found during the inference. compiler.getTypeRegistry().resolveTypesInScope(scope); } catch (DataFlowAnalysis.MaxIterationsExceededException e) { compiler.report(JSError.make(n, DATAFLOW_ERROR)); } }
private void rewriteClassDefinition(Node n, Node parent, NodeTraversal t) { if (parent.getParent().isConst() || parent.getParent().isLet()) { compiler.report(JSError.make(n, POLYMER_INVALID_DECLARATION)); return; } ClassDefinition def = extractClassDefinition(n); if (def != null) { if (NodeUtil.isNameDeclaration(parent.getParent()) || parent.isAssign()) { rewritePolymerClass(parent.getParent(), def, t); } else { rewritePolymerClass(parent, def, t); } } }
/** * Gets the JSTypeExpression for a given property using its "type" key. * * @see https://github.com/Polymer/polymer/blob/0.8-preview/PRIMER.md#configuring-properties */ private JSTypeExpression getTypeFromProperty(MemberDefinition property) { if (property.info != null && property.info.hasType()) { return property.info.getType(); } String typeString = ""; if (property.value.isObjectLit()) { Node typeValue = NodeUtil.getFirstPropMatchingKey(property.value, "type"); if (typeValue == null || !typeValue.isName()) { compiler.report(JSError.make(property.name, POLYMER_INVALID_PROPERTY)); return null; } typeString = typeValue.getString(); } else if (property.value.isName()) { typeString = property.value.getString(); } Node typeNode = null; switch (typeString) { case "Boolean": case "String": case "Number": typeNode = IR.string(typeString.toLowerCase()); break; case "Array": case "Function": case "Object": case "Date": typeNode = new Node(Token.BANG, IR.string(typeString)); break; default: compiler.report(JSError.make(property.name, POLYMER_INVALID_PROPERTY)); return null; } return new JSTypeExpression(typeNode, VIRTUAL_FILE); }
@Override public void process(Node externs, Node root) { // Find and validate the markers. NodeTraversal.traverseEs6(compiler, root, new Callback()); // Complain about any unmatched markers. for (Node node : markerStack) { compiler.report(JSError.make(node, UNMATCHED_START_MARKER, startMarkerName)); } // Add the block for the valid marker sets. for (Marker marker : validMarkers) { addBlocks(marker); } }
/** Strip property type annotations and add suppress checkTypes and globalThis on functions. */ private void suppressBehavior(Node behaviorValue) { if (behaviorValue == null) { compiler.report(JSError.make(behaviorValue, POLYMER_UNQUALIFIED_BEHAVIOR)); return; } if (behaviorValue.isArrayLit()) { for (Node child : behaviorValue.children()) { suppressBehavior(child); } } else if (behaviorValue.isObjectLit()) { stripPropertyTypes(behaviorValue); addBehaviorSuppressions(behaviorValue); } }
@Override public void process(Node externs, Node root) { FindPolymerExterns externsCallback = new FindPolymerExterns(); NodeTraversal.traverse(compiler, externs, externsCallback); polymerElementExterns = externsCallback.polymerElementExterns; polymerElementProps = externsCallback.getpolymerElementProps(); if (polymerElementExterns == null) { compiler.report(JSError.make(externs, POLYMER_MISSING_EXTERNS)); return; } globalNames = new GlobalNamespace(compiler, externs, root); hotSwapScript(root, null); }
/** Processes a GETPROP node. */ private void handleGetProp(NodeTraversal t, Node n) { String name = n.getLastChild().getString(); T type = typeSystem.getType(getScope(), n.getFirstChild(), name); Property prop = getProperty(name); if (!prop.scheduleRenaming(n.getLastChild(), processProperty(t, prop, type, null))) { if (showInvalidationWarnings) { compiler.report( JSError.make( t.getSourceName(), n, INVALIDATION, name, (type == null ? "null" : type.toString()), n.toString())); } } }
/** * Creates an intrument functions compiler pass. * * @param compiler The JSCompiler * @param functionNames Assigned function identifiers. * @param templateFilename Template filename; for use during error reporting only. * @param appNameStr String to pass to appNameSetter. * @param readable Instrumentation template protobuf text. */ InstrumentFunctions( AbstractCompiler compiler, FunctionNames functionNames, String templateFilename, String appNameStr, Readable readable) { this.compiler = compiler; this.functionNames = functionNames; this.templateFilename = templateFilename; this.appNameStr = appNameStr; Instrumentation.Builder builder = Instrumentation.newBuilder(); try { TextFormat.merge(readable, builder); } catch (IOException e) { compiler.report( JSError.make( RhinoErrorReporter.PARSE_ERROR, "Error reading instrumentation template protobuf at " + templateFilename)); this.initCodeSource = ""; this.definedFunctionName = ""; this.reportFunctionName = ""; this.reportFunctionExitName = ""; this.appNameSetter = ""; this.declarationsToRemove = Lists.newArrayList(); return; } Instrumentation template = builder.build(); StringBuilder initCodeSourceBuilder = new StringBuilder(); for (String line : template.getInitList()) { initCodeSourceBuilder.append(line).append("\n"); } this.initCodeSource = initCodeSourceBuilder.toString(); this.definedFunctionName = template.getReportDefined(); this.reportFunctionName = template.getReportCall(); this.reportFunctionExitName = template.getReportExit(); this.appNameSetter = template.getAppNameSetter(); this.declarationsToRemove = ImmutableList.copyOf(template.getDeclarationToRemoveList()); }
@Override public void visit(NodeTraversal t, Node n, Node parent) { if (isBehavior(n)) { if (!n.isVar() && !n.isAssign()) { compiler.report(JSError.make(n, POLYMER_UNQUALIFIED_BEHAVIOR)); return; } // Add @nocollapse. JSDocInfoBuilder newDocs = JSDocInfoBuilder.maybeCopyFrom(n.getJSDocInfo()); newDocs.recordNoCollapse(); n.setJSDocInfo(newDocs.build()); Node behaviorValue = n.getChildAtIndex(1); if (n.isVar()) { behaviorValue = n.getFirstChild().getFirstChild(); } suppressBehavior(behaviorValue); } }
private JSDocInfo mergeJsDocFor(ClassDefinition cls, Node associatedNode) { // avoid null checks JSDocInfo classInfo = (cls.classInfo != null) ? cls.classInfo : new JSDocInfo(true); JSDocInfo ctorInfo = (cls.constructor.info != null) ? cls.constructor.info : new JSDocInfo(true); Node superNode = cls.superClass; // Start with a clone of the constructor info if there is one. JSDocInfoBuilder mergedInfo = cls.constructor.info != null ? JSDocInfoBuilder.copyFrom(ctorInfo) : new JSDocInfoBuilder(true); // merge block description String blockDescription = Joiner.on("\n") .skipNulls() .join(classInfo.getBlockDescription(), ctorInfo.getBlockDescription()); if (!blockDescription.isEmpty()) { mergedInfo.recordBlockDescription(blockDescription); } // merge suppressions Set<String> suppressions = Sets.newHashSet(); suppressions.addAll(classInfo.getSuppressions()); suppressions.addAll(ctorInfo.getSuppressions()); if (!suppressions.isEmpty()) { mergedInfo.recordSuppressions(suppressions); } // Use class deprecation if set. if (classInfo.isDeprecated()) { mergedInfo.recordDeprecated(); } String deprecationReason = null; if (classInfo.getDeprecationReason() != null) { deprecationReason = classInfo.getDeprecationReason(); mergedInfo.recordDeprecationReason(deprecationReason); } // Use class visibility if specifically set Visibility visibility = classInfo.getVisibility(); if (visibility != null && visibility != JSDocInfo.Visibility.INHERITED) { mergedInfo.recordVisibility(classInfo.getVisibility()); } if (classInfo.isConstant()) { mergedInfo.recordConstancy(); } if (classInfo.isExport()) { mergedInfo.recordExport(); } // If @ngInject is on the ctor, it's already been copied above. if (classInfo.isNgInject()) { compiler.report(JSError.make(associatedNode, GOOG_CLASS_NG_INJECT_ON_CLASS)); mergedInfo.recordNgInject(true); } // @constructor is implied, @interface must be explicit boolean isInterface = classInfo.isInterface() || ctorInfo.isInterface(); if (isInterface) { mergedInfo.recordInterface(); List<JSTypeExpression> extendedInterfaces = null; if (classInfo.getExtendedInterfacesCount() > 0) { extendedInterfaces = classInfo.getExtendedInterfaces(); } else if (ctorInfo.getExtendedInterfacesCount() == 0 && superNode != null) { extendedInterfaces = ImmutableList.of( new JSTypeExpression( new Node(Token.BANG, IR.string(superNode.getQualifiedName())), VIRTUAL_FILE)); } if (extendedInterfaces != null) { for (JSTypeExpression extend : extendedInterfaces) { mergedInfo.recordExtendedInterface(extend); } } } else { // @constructor by default mergedInfo.recordConstructor(); if (classInfo.makesUnrestricted() || ctorInfo.makesUnrestricted()) { mergedInfo.recordUnrestricted(); } else if (classInfo.makesDicts() || ctorInfo.makesDicts()) { mergedInfo.recordDict(); } else { // @struct by default mergedInfo.recordStruct(); } if (classInfo.getBaseType() != null) { mergedInfo.recordBaseType(classInfo.getBaseType()); } else if (superNode != null) { // a "super" implies @extends, build a default. JSTypeExpression baseType = new JSTypeExpression( new Node(Token.BANG, IR.string(superNode.getQualifiedName())), VIRTUAL_FILE); mergedInfo.recordBaseType(baseType); } // @implements from the class if they exist List<JSTypeExpression> interfaces = classInfo.getImplementedInterfaces(); for (JSTypeExpression implemented : interfaces) { mergedInfo.recordImplementedInterface(implemented); } } // merge @template types if they exist List<String> templateNames = new ArrayList<>(); templateNames.addAll(classInfo.getTemplateTypeNames()); templateNames.addAll(ctorInfo.getTemplateTypeNames()); for (String typeName : templateNames) { mergedInfo.recordTemplateTypeName(typeName); } return mergedInfo.build(associatedNode); }
/** * Validates the class definition and if valid, destructively extracts the class definition from * the AST. */ private ClassDefinition extractClassDefinition(Node targetName, Node callNode) { JSDocInfo classInfo = NodeUtil.getBestJSDocInfo(targetName); // name = goog.defineClass(superClass, {...}, [modifier, ...]) Node superClass = NodeUtil.getArgumentForCallOrNew(callNode, 0); if (superClass == null || (!superClass.isNull() && !superClass.isQualifiedName())) { compiler.report(JSError.make(callNode, GOOG_CLASS_SUPER_CLASS_NOT_VALID)); return null; } if (NodeUtil.isNullOrUndefined(superClass) || superClass.matchesQualifiedName("Object")) { superClass = null; } Node description = NodeUtil.getArgumentForCallOrNew(callNode, 1); if (description == null || !description.isObjectLit() || !validateObjLit(description)) { // report bad class definition compiler.report(JSError.make(callNode, GOOG_CLASS_DESCRIPTOR_NOT_VALID)); return null; } int paramCount = callNode.getChildCount() - 1; if (paramCount > 2) { compiler.report(JSError.make(callNode, GOOG_CLASS_UNEXPECTED_PARAMS)); return null; } Node constructor = extractProperty(description, "constructor"); if (classInfo != null && classInfo.isInterface()) { if (constructor != null) { compiler.report(JSError.make(description, GOOG_CLASS_CONSTRUCTOR_ON_INTERFACE)); return null; } } else if (constructor == null) { // report missing constructor compiler.report(JSError.make(description, GOOG_CLASS_CONSTRUCTOR_MISSING)); return null; } if (constructor == null) { constructor = IR.function( IR.name("").srcref(callNode), IR.paramList().srcref(callNode), IR.block().srcref(callNode)); constructor.srcref(callNode); } JSDocInfo info = NodeUtil.getBestJSDocInfo(constructor); Node classModifier = null; Node statics = null; Node staticsProp = extractProperty(description, "statics"); if (staticsProp != null) { if (staticsProp.isObjectLit() && validateObjLit(staticsProp)) { statics = staticsProp; } else if (staticsProp.isFunction()) { classModifier = staticsProp; } else { compiler.report(JSError.make(staticsProp, GOOG_CLASS_STATICS_NOT_VALID)); return null; } } if (statics == null) { statics = IR.objectlit(); } // Ok, now rip apart the definition into its component pieces. // Remove the "special" property key nodes. maybeDetach(constructor.getParent()); maybeDetach(statics.getParent()); if (classModifier != null) { maybeDetach(classModifier.getParent()); } ClassDefinition def = new ClassDefinition( targetName, classInfo, maybeDetach(superClass), new MemberDefinition(info, null, maybeDetach(constructor)), objectLitToList(maybeDetach(statics)), objectLitToList(description), maybeDetach(classModifier)); return def; }
/** @param requirement */ private static void reportInvalidRequirement( AbstractCompiler compiler, Requirement requirement, String reason) { compiler.report( JSError.make(INVALID_REQUIREMENT_SPEC, reason, TextFormat.printToString(requirement))); }
public void testOrderingDescription() throws Exception { JSError e1 = JSError.make(NULL_SOURCE, -1, -1, FOO_TYPE); JSError e2 = JSError.make(NULL_SOURCE, -1, -1, JOO_TYPE); assertSmaller(error(e1), error(e2)); }
public void testOrderingCheckLevel() throws Exception { JSError e1 = JSError.make(NULL_SOURCE, -1, -1, FOO_TYPE); JSError e2 = JSError.make(NULL_SOURCE, -1, -1, FOO_TYPE); assertSmaller(warning(e1), error(e2)); }
public void testOrderingLineno2() throws Exception { JSError e1 = JSError.make(NULL_SOURCE, 8, -1, FOO_TYPE); JSError e2 = JSError.make(NULL_SOURCE, 56, -1, FOO_TYPE); assertSmaller(error(e1), error(e2)); }